@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/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  BRIDGE_SURFACES: () => BRIDGE_SURFACES,
26
26
  BRIDGE_VERSION: () => BRIDGE_VERSION,
27
27
  BridgeError: () => BridgeError,
28
+ FRAME_UNLOAD_EVENT: () => FRAME_UNLOAD_EVENT,
28
29
  HOST_EVENT_TYPES: () => HOST_EVENT_TYPES,
29
30
  IFRAME_ATTRIBUTES: () => IFRAME_ATTRIBUTES,
30
31
  INIT_RESPONSE: () => INIT_RESPONSE,
@@ -114,6 +115,7 @@ var REQUEST_TYPES = [
114
115
  var HOST_EVENT_TYPES = ["slot.context", "locale", "member", "theme", "visibility"];
115
116
  var READY_EVENT = "ready";
116
117
  var INIT_RESPONSE = "init";
118
+ var FRAME_UNLOAD_EVENT = "frame-unload";
117
119
  var BRIDGE_SURFACES = ["page", "slot"];
118
120
  function isBridgeSurface(value) {
119
121
  return BRIDGE_SURFACES.includes(value);
@@ -165,6 +167,20 @@ function bridgeRuntime(g, makeFetch, config) {
165
167
  const session = config.session;
166
168
  const hostOrigin = config.hostOrigin;
167
169
  const parent = g.parent;
170
+ const sendToHost = parent ? parent.postMessage.bind(parent) : void 0;
171
+ const stopInterval = clearInterval;
172
+ const stopTimeout = clearTimeout;
173
+ let readPersisted;
174
+ const ownDescriptor = Object.getOwnPropertyDescriptor;
175
+ const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
176
+ try {
177
+ const pte = g.PageTransitionEvent;
178
+ const desc = pte ? Object.getOwnPropertyDescriptor(pte.prototype, "persisted") : void 0;
179
+ if (desc && typeof desc.get === "function")
180
+ readPersisted = Function.prototype.call.bind(desc.get);
181
+ } catch {
182
+ readPersisted = void 0;
183
+ }
168
184
  const pending = /* @__PURE__ */ new Map();
169
185
  const listeners = /* @__PURE__ */ new Map();
170
186
  let init;
@@ -181,6 +197,17 @@ function bridgeRuntime(g, makeFetch, config) {
181
197
  return out;
182
198
  }
183
199
  const idPrefix = randomPrefix();
200
+ function secureToken() {
201
+ if (!g.crypto || typeof g.crypto.getRandomValues !== "function") return "";
202
+ const bytes = new Uint8Array(16);
203
+ g.crypto.getRandomValues(bytes);
204
+ let out = "";
205
+ for (let i = 0; i < bytes.length; i += 1) out += (bytes[i] + 256).toString(16).slice(1);
206
+ return out;
207
+ }
208
+ const frameToken = secureToken();
209
+ let closed = frameToken === "";
210
+ let unloaded = false;
184
211
  function nextId() {
185
212
  seq += 1;
186
213
  return idPrefix + "-" + seq;
@@ -192,9 +219,12 @@ function bridgeRuntime(g, makeFetch, config) {
192
219
  return err;
193
220
  }
194
221
  function post(kind, type, id, payload) {
195
- if (!parent) return;
222
+ if (!sendToHost || closed) return;
196
223
  const body = Object.assign({}, payload, { session });
197
- parent.postMessage({ v: 1, ns: NS, id, kind, type, payload: body }, hostOrigin);
224
+ sendToHost(
225
+ { v: 1, ns: NS, id, kind, type, payload: body, frame: frameToken },
226
+ hostOrigin
227
+ );
198
228
  }
199
229
  function isEnvelope(m) {
200
230
  if (typeof m !== "object" || m === null) return false;
@@ -267,6 +297,54 @@ function bridgeRuntime(g, makeFetch, config) {
267
297
  }
268
298
  }
269
299
  g.addEventListener("message", onMessage);
300
+ function isPersisted(ev) {
301
+ try {
302
+ if (readPersisted) return readPersisted(ev) === true;
303
+ if (typeof ev !== "object" || ev === null) return false;
304
+ const desc = ownDescriptor(ev, "persisted");
305
+ return !!desc && hasOwn(desc, "value") && desc.value === true;
306
+ } catch {
307
+ return false;
308
+ }
309
+ }
310
+ function onPageHide(ev) {
311
+ if (unloaded || isPersisted(ev)) return;
312
+ unloaded = true;
313
+ closed = true;
314
+ if (sendToHost) {
315
+ try {
316
+ sendToHost(
317
+ {
318
+ v: 1,
319
+ ns: NS,
320
+ id: idPrefix + "-unload",
321
+ kind: "evt",
322
+ type: "frame-unload",
323
+ payload: { session },
324
+ frame: frameToken
325
+ },
326
+ hostOrigin
327
+ );
328
+ } catch {
329
+ }
330
+ }
331
+ try {
332
+ stopInterval(readyTimer);
333
+ } catch {
334
+ }
335
+ try {
336
+ pending.forEach(function(entry) {
337
+ try {
338
+ stopTimeout(entry.timer);
339
+ entry.reject(codeError("UNSUPPORTED", "bridge closed"));
340
+ } catch {
341
+ }
342
+ });
343
+ pending.clear();
344
+ } catch {
345
+ }
346
+ }
347
+ g.addEventListener("pagehide", onPageHide, true);
270
348
  let readyAttempts = 0;
271
349
  function sendReady() {
272
350
  readyAttempts += 1;
@@ -280,6 +358,10 @@ function bridgeRuntime(g, makeFetch, config) {
280
358
  sendReady();
281
359
  function request(type, payload) {
282
360
  return new Promise(function(resolve, reject) {
361
+ if (closed) {
362
+ reject(codeError("UNSUPPORTED", "bridge closed"));
363
+ return;
364
+ }
283
365
  const id = nextId();
284
366
  const timer = setTimeout(function() {
285
367
  pending.delete(id);
@@ -414,6 +496,126 @@ function createBridgeFetch(client) {
414
496
  return bridgeFetch;
415
497
  }
416
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
+
417
619
  // src/srcdoc/escape.ts
418
620
  var HTML_ESCAPES = {
419
621
  "&": "&amp;",
@@ -554,7 +756,7 @@ function buildFrameCsp(nonce, assetOrigin, mediaOrigins = []) {
554
756
  `img-src ${media} data: blob:`,
555
757
  `font-src ${assetOrigin} data:`,
556
758
  `media-src ${media} blob:`,
557
- "connect-src 'none'",
759
+ "connect-src https: wss:",
558
760
  "frame-src 'none'",
559
761
  "form-action 'none'",
560
762
  "base-uri 'none'"
@@ -571,6 +773,9 @@ function bootstrapScript(o) {
571
773
  `var bridgeRuntime = ${bridgeRuntime.toString()};`,
572
774
  `var createBridgeFetch = ${createBridgeFetch.toString()};`,
573
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 });",
574
779
  "var lastHeight = -1;",
575
780
  "var scheduled = false;",
576
781
  "function reportHeight() {",
@@ -646,6 +851,7 @@ ${script}
646
851
  BRIDGE_SURFACES,
647
852
  BRIDGE_VERSION,
648
853
  BridgeError,
854
+ FRAME_UNLOAD_EVENT,
649
855
  HOST_EVENT_TYPES,
650
856
  IFRAME_ATTRIBUTES,
651
857
  INIT_RESPONSE,
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 FetchRequest, n as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, o as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, p as REQUEST_TIMEOUT_MS, q as REQUEST_TYPES, S as SlotState, r as StorageOp, T as ThemeState, s as ToastKind, W as WithSession, t as isBridgeErrorCode, u as isBridgeErrorLike, v as isBridgeSurface, w as isHostEventType, x as isRequestType } from './messages-9N0GFq5b.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-bOoxttPz.cjs';
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;
@@ -19,6 +19,11 @@ interface BridgeEnvelope<P = unknown> {
19
19
  readonly kind: EnvelopeKind;
20
20
  readonly type: string;
21
21
  readonly payload: P;
22
+ /**
23
+ * 프레임 → 호스트 봉투에만 붙는 프레임 토큰(소문자 hex 32자). 프레임 런타임이 문서마다 새로 만들고
24
+ * 호스트는 처음 본 값에 세션을 묶는다. 호스트 → 프레임 봉투에는 없다.
25
+ */
26
+ readonly frame?: string;
22
27
  }
23
28
  /**
24
29
  * 값이 브리지 봉투인지 검사한다. 모양만 본다 — 발신자 확인(source·session)은 호출자 몫이다.
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 FetchRequest, n as FetchResult, H as HOST_EVENT_TYPES, I as IFRAME_ATTRIBUTES, o as INIT_RESPONSE, L as LogLevel, M as MemberState, R as READY_EVENT, p as REQUEST_TIMEOUT_MS, q as REQUEST_TYPES, S as SlotState, r as StorageOp, T as ThemeState, s as ToastKind, W as WithSession, t as isBridgeErrorCode, u as isBridgeErrorLike, v as isBridgeSurface, w as isHostEventType, x as isRequestType } from './messages-9N0GFq5b.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-bOoxttPz.js';
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;
@@ -19,6 +19,11 @@ interface BridgeEnvelope<P = unknown> {
19
19
  readonly kind: EnvelopeKind;
20
20
  readonly type: string;
21
21
  readonly payload: P;
22
+ /**
23
+ * 프레임 → 호스트 봉투에만 붙는 프레임 토큰(소문자 hex 32자). 프레임 런타임이 문서마다 새로 만들고
24
+ * 호스트는 처음 본 값에 세션을 묶는다. 호스트 → 프레임 봉투에는 없다.
25
+ */
26
+ readonly frame?: string;
22
27
  }
23
28
  /**
24
29
  * 값이 브리지 봉투인지 검사한다. 모양만 본다 — 발신자 확인(source·session)은 호출자 몫이다.
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  BRIDGE_SURFACES,
5
5
  BRIDGE_VERSION,
6
6
  BridgeError,
7
+ FRAME_UNLOAD_EVENT,
7
8
  HOST_EVENT_TYPES,
8
9
  IFRAME_ATTRIBUTES,
9
10
  INIT_RESPONSE,
@@ -28,14 +29,15 @@ import {
28
29
  isRequestType,
29
30
  isValidNonce,
30
31
  isValidSession
31
- } from "./chunk-6LV7HEN4.js";
32
- import "./chunk-KQMJY4ZH.js";
32
+ } from "./chunk-ND2HFYBP.js";
33
+ import "./chunk-CSAW3L7P.js";
33
34
  export {
34
35
  BRIDGE_ERROR_CODES,
35
36
  BRIDGE_NAMESPACE,
36
37
  BRIDGE_SURFACES,
37
38
  BRIDGE_VERSION,
38
39
  BridgeError,
40
+ FRAME_UNLOAD_EVENT,
39
41
  HOST_EVENT_TYPES,
40
42
  IFRAME_ATTRIBUTES,
41
43
  INIT_RESPONSE,
@@ -28,6 +28,11 @@ type BridgeHostEventType = (typeof HOST_EVENT_TYPES)[number];
28
28
  declare const READY_EVENT = "ready";
29
29
  /** 호스트 → 프레임 핸드셰이크 응답 type. */
30
30
  declare const INIT_RESPONSE = "init";
31
+ /**
32
+ * 프레임 → 호스트 종료 알림 이벤트. 부트스트랩이 `pagehide`(bfcache 제외)에서 동기로 보낸다.
33
+ * 호스트는 받는 즉시 세션을 끊는다(이후 메시지 무시).
34
+ */
35
+ declare const FRAME_UNLOAD_EVENT = "frame-unload";
31
36
  /** 프레임이 보내는 모든 payload에 붙는 세션. */
32
37
  type WithSession<T> = T & {
33
38
  readonly session: string;
@@ -65,6 +70,12 @@ interface BridgeInit {
65
70
  readonly spaceId: string;
66
71
  readonly hostOrigin: string;
67
72
  readonly gatewayBase: string;
73
+ /**
74
+ * 호스트 정책의 같은 origin 게이트웨이 경로 접두사(`policy.gatewayPrefixes`). 프레임
75
+ * 라우터(`__bstage_fetch__`)가 "브리지로 보낼 같은 origin 경로"를 가르는 데 쓴다. 판정의 정본은 여전히 호스트다.
76
+ * 이 필드가 없는 옛 호스트면 라우터는 hostOrigin 요청을 모두 브리지로 보낸다.
77
+ */
78
+ readonly gatewayPrefixes?: readonly string[];
68
79
  readonly locale: string;
69
80
  readonly theme: ThemeState;
70
81
  readonly slot?: SlotState;
@@ -183,4 +194,4 @@ declare const IFRAME_ATTRIBUTES: {
183
194
  declare function isRequestType(value: unknown): value is BridgeRequestType;
184
195
  declare function isHostEventType(value: unknown): value is BridgeHostEventType;
185
196
 
186
- export { BRIDGE_ERROR_CODES as B, type FetchRequest as F, HOST_EVENT_TYPES as H, IFRAME_ATTRIBUTES as I, type LogLevel as L, type MemberState as M, READY_EVENT as R, type SlotState as S, type ThemeState as T, type WithSession as W, BRIDGE_SURFACES as a, BridgeError as b, type BridgeErrorCode as c, type BridgeErrorLike as d, type BridgeEventMap as e, type BridgeHostEventType as f, type BridgeInit as g, type BridgeLimits as h, type BridgeRequestMap as i, type BridgeRequestType as j, type BridgeResult as k, type BridgeSurface as l, type BridgeTranslations as m, type FetchResult as n, INIT_RESPONSE as o, REQUEST_TIMEOUT_MS as p, REQUEST_TYPES as q, type StorageOp as r, type ToastKind as s, isBridgeErrorCode as t, isBridgeErrorLike as u, isBridgeSurface as v, isHostEventType as w, isRequestType as x };
197
+ export { BRIDGE_ERROR_CODES as B, FRAME_UNLOAD_EVENT as F, HOST_EVENT_TYPES as H, IFRAME_ATTRIBUTES as I, type LogLevel as L, type MemberState as M, READY_EVENT as R, type SlotState as S, type ThemeState as T, type WithSession as W, BRIDGE_SURFACES as a, BridgeError as b, type BridgeErrorCode as c, type BridgeErrorLike as d, type BridgeEventMap as e, type BridgeHostEventType as f, type BridgeInit as g, type BridgeLimits as h, type BridgeRequestMap as i, type BridgeRequestType as j, type BridgeResult as k, type BridgeSurface as l, type BridgeTranslations as m, type FetchRequest as n, type FetchResult as o, INIT_RESPONSE as p, REQUEST_TIMEOUT_MS as q, REQUEST_TYPES as r, type StorageOp as s, type ToastKind as t, isBridgeErrorCode as u, isBridgeErrorLike as v, isBridgeSurface as w, isHostEventType as x, isRequestType as y };
@@ -28,6 +28,11 @@ type BridgeHostEventType = (typeof HOST_EVENT_TYPES)[number];
28
28
  declare const READY_EVENT = "ready";
29
29
  /** 호스트 → 프레임 핸드셰이크 응답 type. */
30
30
  declare const INIT_RESPONSE = "init";
31
+ /**
32
+ * 프레임 → 호스트 종료 알림 이벤트. 부트스트랩이 `pagehide`(bfcache 제외)에서 동기로 보낸다.
33
+ * 호스트는 받는 즉시 세션을 끊는다(이후 메시지 무시).
34
+ */
35
+ declare const FRAME_UNLOAD_EVENT = "frame-unload";
31
36
  /** 프레임이 보내는 모든 payload에 붙는 세션. */
32
37
  type WithSession<T> = T & {
33
38
  readonly session: string;
@@ -65,6 +70,12 @@ interface BridgeInit {
65
70
  readonly spaceId: string;
66
71
  readonly hostOrigin: string;
67
72
  readonly gatewayBase: string;
73
+ /**
74
+ * 호스트 정책의 같은 origin 게이트웨이 경로 접두사(`policy.gatewayPrefixes`). 프레임
75
+ * 라우터(`__bstage_fetch__`)가 "브리지로 보낼 같은 origin 경로"를 가르는 데 쓴다. 판정의 정본은 여전히 호스트다.
76
+ * 이 필드가 없는 옛 호스트면 라우터는 hostOrigin 요청을 모두 브리지로 보낸다.
77
+ */
78
+ readonly gatewayPrefixes?: readonly string[];
68
79
  readonly locale: string;
69
80
  readonly theme: ThemeState;
70
81
  readonly slot?: SlotState;
@@ -183,4 +194,4 @@ declare const IFRAME_ATTRIBUTES: {
183
194
  declare function isRequestType(value: unknown): value is BridgeRequestType;
184
195
  declare function isHostEventType(value: unknown): value is BridgeHostEventType;
185
196
 
186
- export { BRIDGE_ERROR_CODES as B, type FetchRequest as F, HOST_EVENT_TYPES as H, IFRAME_ATTRIBUTES as I, type LogLevel as L, type MemberState as M, READY_EVENT as R, type SlotState as S, type ThemeState as T, type WithSession as W, BRIDGE_SURFACES as a, BridgeError as b, type BridgeErrorCode as c, type BridgeErrorLike as d, type BridgeEventMap as e, type BridgeHostEventType as f, type BridgeInit as g, type BridgeLimits as h, type BridgeRequestMap as i, type BridgeRequestType as j, type BridgeResult as k, type BridgeSurface as l, type BridgeTranslations as m, type FetchResult as n, INIT_RESPONSE as o, REQUEST_TIMEOUT_MS as p, REQUEST_TYPES as q, type StorageOp as r, type ToastKind as s, isBridgeErrorCode as t, isBridgeErrorLike as u, isBridgeSurface as v, isHostEventType as w, isRequestType as x };
197
+ export { BRIDGE_ERROR_CODES as B, FRAME_UNLOAD_EVENT as F, HOST_EVENT_TYPES as H, IFRAME_ATTRIBUTES as I, type LogLevel as L, type MemberState as M, READY_EVENT as R, type SlotState as S, type ThemeState as T, type WithSession as W, BRIDGE_SURFACES as a, BridgeError as b, type BridgeErrorCode as c, type BridgeErrorLike as d, type BridgeEventMap as e, type BridgeHostEventType as f, type BridgeInit as g, type BridgeLimits as h, type BridgeRequestMap as i, type BridgeRequestType as j, type BridgeResult as k, type BridgeSurface as l, type BridgeTranslations as m, type FetchRequest as n, type FetchResult as o, INIT_RESPONSE as p, REQUEST_TIMEOUT_MS as q, REQUEST_TYPES as r, type StorageOp as s, type ToastKind as t, isBridgeErrorCode as u, isBridgeErrorLike as v, isBridgeSurface as w, isHostEventType as x, isRequestType as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bstage-sdk/bridge",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "bstage SDK bridge — isolated template iframe protocol, host policy and frame client",
5
5
  "type": "module",
6
6
  "publishConfig": {