@bstage-sdk/bridge 0.0.0-stage → 0.9.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.
@@ -0,0 +1,262 @@
1
+ import { f as BridgeHostEventType, e as BridgeEventMap, s as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-9N0GFq5b.cjs';
2
+ export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, v as isBridgeSurface } from './messages-9N0GFq5b.cjs';
3
+ export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-bOoxttPz.cjs';
4
+
5
+ /** 호스트 fetch 정책. */
6
+ interface BridgePolicy {
7
+ /** 같은 origin에서 허용할 경로 접두사. 기본 `["/gw/"]`(`/gw` 정확 일치 포함). */
8
+ readonly gatewayPrefixes: readonly string[];
9
+ /**
10
+ * `gatewayPrefixes` 안에서도 막는 경로 접두사. 기본은 인증 경로 — 토큰 갱신
11
+ * (`/gw/account/v1/auth/token/refresh`)은 refresh 쿠키로 새 access token을 **응답 본문**에 돌려주므로
12
+ * 프레임이 닿으면 토큰이 프레임에 들어간다. 디코딩·슬래시 병합·소문자화한 경로로 비교한다.
13
+ */
14
+ readonly gatewayDenyPrefixes: readonly string[];
15
+ /**
16
+ * 위치와 무관하게 막는 경로 세그먼트(소문자). 서비스·버전·phase 접두사가 붙은 실제 인증 경로
17
+ * (`/gw/svc/account/api/v1/auth/token/refresh`·`/gw/svc/api/v1/auth/token/refresh`)도 잡기 위해
18
+ * 접두사가 아니라 세그먼트로 본다. 기본은 {@link DEFAULT_GATEWAY_DENY_SEGMENTS}
19
+ * (`/gw/svc/x/token`·`/gw/svc/x/oauth/callback`·`/gw/svc/x/sso/login`처럼 auth 밖에서 토큰을 내는 경로,
20
+ * 디버그 엔드포인트, 회원 탈퇴 포함). 호스트는 이 상수를 명시해
21
+ * 넘기면 정책이 코드에 드러난다(값은 이 패키지 버전의 기본값과 같다 — 고정이 필요하면 리터럴 목록이나
22
+ * `[...DEFAULT_GATEWAY_DENY_SEGMENTS, '추가']`처럼 넘긴다).
23
+ *
24
+ * 이 목록과 별개로 **`token`이 들어간 세그먼트**(`tokens`·`access-tokens`·`refreshToken`)는 항상
25
+ * 거절한다 — 정책으로 끌 수 없다.
26
+ */
27
+ readonly gatewayDenySegments: readonly string[];
28
+ /**
29
+ * 같은 origin `/gw`·파트너가 아닌 **그 밖의 https origin**을 `externalFetchImpl`로 보낼지. 기본 `false`
30
+ * (옵트인) — 켜면 프레임이 임의 https로 데이터를 보내고 응답을 읽는 양방향 채널이 생기므로, 패키지를
31
+ * 올리기만 해서 열리지 않게 한다. 외부 호출을 허용하는 호스트(web-base)는 `true`를 명시하고,
32
+ * 막아야 하는 호스트(포털 미리보기)는 생략하거나 `false`.
33
+ */
34
+ readonly allowExternal: boolean;
35
+ /** 요청 본문 상한(UTF-8 바이트). 기본 1MB. */
36
+ readonly maxBodyBytes: number;
37
+ /** 프레임별 분당 요청 상한. 기본 300. */
38
+ readonly maxRequestsPerMinute: number;
39
+ /**
40
+ * 응답 본문 상한(UTF-8 바이트). 기본 10MB. 프로토콜에 정해지지 않은 값(구현 선택) — 넘으면 `BODY_TOO_LARGE`.
41
+ * 큰 응답 여러 개를 붙잡아 호스트 메모리를 쓰게 하는 것을 막는다.
42
+ */
43
+ readonly maxResponseBytes: number;
44
+ }
45
+ /**
46
+ * 기본 거절 세그먼트. 인증·토큰 발급(`auth`·`logout`·`mfa`·`token`·`oauth`·`sso`), 디버그 엔드포인트
47
+ * (`debug`·`debugger`·`xdebug`), 회원 탈퇴(`withdraw`). 호스트가 `policy.gatewayDenySegments`로 그대로
48
+ * 넘길 수 있게 공개한다.
49
+ */
50
+ declare const DEFAULT_GATEWAY_DENY_SEGMENTS: readonly string[];
51
+ declare const DEFAULT_POLICY: BridgePolicy;
52
+ /**
53
+ * 통과시키는 요청 헤더(소문자). 나머지(authorization·cookie·`x-bmf-sdk-version` 밖의 x-bmf-* 포함)는 버린다.
54
+ * 목적지 갈래(gateway·partner·external)와 무관하게 같은 목록이다.
55
+ */
56
+ declare const ALLOWED_REQUEST_HEADERS: readonly string[];
57
+ /**
58
+ * 테넌시 헤더 — 프레임 값은 **절대 통과시키지 않는다**. 호스트가 `pinnedHeaders`로 준 값만 실린다.
59
+ * 프레임이 테넌트를 고르면 사용자 자격증명이 실린 요청을 다른 테넌트로 보내 응답을 읽을 수 있다.
60
+ */
61
+ declare const HOST_ONLY_HEADERS: readonly string[];
62
+ declare const ALLOWED_METHODS: readonly string[];
63
+ /** partnerOrigins 최대 개수. */
64
+ declare const MAX_PARTNER_ORIGINS = 10;
65
+ /** 사용자 정책을 기본값과 합치고 검증한다. */
66
+ declare function resolvePolicy(policy: Partial<BridgePolicy> | undefined): BridgePolicy;
67
+ /**
68
+ * partnerOrigins를 검증한다 — https 정확한 origin만, 중복 제거, `excludeOrigin`(hostOrigin) 제외,
69
+ * 최대 10개. 나머지는 버린다.
70
+ */
71
+ declare function normalizePartnerOrigins(origins: unknown, excludeOrigin?: string): readonly string[];
72
+ /**
73
+ * 같은 origin 경로가 게이트웨이로 보낼 수 있는지: 구조 검사(`checkPath`) 통과 + 원문이 허용 접두사 안 +
74
+ * 어느 디코딩 단계도 거절 접두사·거절 세그먼트에 걸리지 않을 것.
75
+ */
76
+ declare function isAllowedGatewayPath(pathname: string, policy: BridgePolicy): boolean;
77
+ interface DestinationContext {
78
+ readonly hostOrigin: string;
79
+ readonly partnerOrigins: readonly string[];
80
+ readonly policy: BridgePolicy;
81
+ /** 게이트웨이 요청에 호스트가 덮어 싣는 헤더(`normalizePinnedHeaders` 결과). 파트너·외부 요청에는 싣지 않는다. */
82
+ readonly pinnedHeaders?: Readonly<Record<string, string>>;
83
+ }
84
+ /**
85
+ * 목적지 종류.
86
+ * - `gateway`: 같은 origin `/gw` — 쿠키·인증 fetch(`fetchImpl`), 고정 헤더.
87
+ * - `partner`: `partnerOrigins` 정확 일치 — `partnerFetchImpl`(호스트가 팬 토큰을 붙일 수 있는 백엔드).
88
+ * - `external`: 그 밖의 https origin(hostOrigin 제외) — `externalFetchImpl`, 무인증. `policy.allowExternal`로 끈다.
89
+ */
90
+ type DestinationKind = 'gateway' | 'partner' | 'external';
91
+ interface Destination {
92
+ readonly url: string;
93
+ readonly kind: DestinationKind;
94
+ }
95
+ /**
96
+ * 목적지 판정. `new URL(url, hostOrigin)`으로 정규화한 뒤(`..`·`%2e`·역슬래시는 여기서 해소된다)
97
+ * (a) 같은 origin + 게이트웨이 접두사 + 모호하지 않은 경로 → gateway, (b) **hostOrigin이 아닌** 파트너
98
+ * origin 정확 일치 → partner, (c) 그 밖의 https origin(`allowExternal`일 때, IP 리터럴·로컬 이름·
99
+ * 호스트와 같은 호스트명 제외) → external.
100
+ * hostOrigin은 (b)·(c)로 통과하지 않는다 — 그러면 /gw 밖 같은 origin 경로(쿠키 포함)가 열린다.
101
+ * http·userinfo·비 http 스킴은 모두 거절한다.
102
+ *
103
+ * 돌려주는 `url`은 **판정한 그 URL**(`href`)이다 — 호출자는 이 값으로 보낸다(판정과 전송이 같은 URL).
104
+ */
105
+ declare function classifyDestination(rawUrl: unknown, ctx: DestinationContext): Destination;
106
+ /** `classifyDestination`의 URL만. */
107
+ declare function resolveDestination(rawUrl: unknown, ctx: DestinationContext): string;
108
+ /**
109
+ * 호스트가 고정하는 헤더를 검증·소문자화한다. 허용 헤더·테넌시 헤더만 받는다(그 밖은 TypeError —
110
+ * 이 경로로 authorization 같은 값을 실수로 넣지 못하게).
111
+ */
112
+ declare function normalizePinnedHeaders(pinned: unknown): Readonly<Record<string, string>>;
113
+ interface PreparedFetch {
114
+ readonly url: string;
115
+ readonly kind: DestinationKind;
116
+ readonly init: {
117
+ readonly method: string;
118
+ readonly headers: Readonly<Record<string, string>>;
119
+ readonly body?: string;
120
+ /** gateway는 `same-origin`(주입 인증 fetch), partner·external은 `omit`(쿠키 없음). */
121
+ readonly credentials: 'same-origin' | 'omit';
122
+ readonly redirect: 'error';
123
+ /** partner·external만 — 호스트 페이지 URL(경로·쿼리)을 다른 origin에 Referer로 흘리지 않는다. */
124
+ readonly referrerPolicy?: 'no-referrer';
125
+ };
126
+ }
127
+ /** 프레임의 fetch 요청을 정책에 따라 실제 호출 인자로 바꾼다. 위반이면 BridgeError. */
128
+ declare function prepareFetch(payload: unknown, ctx: DestinationContext): PreparedFetch;
129
+
130
+ /** 호스트 메시지 리스너를 다는 창의 최소 모양. */
131
+ interface HostWindow {
132
+ addEventListener(type: 'message', fn: (ev: MessageEventLike) => void): void;
133
+ removeEventListener(type: 'message', fn: (ev: MessageEventLike) => void): void;
134
+ open?(url: string, target: string, features: string): unknown;
135
+ requestAnimationFrame?(cb: () => void): number;
136
+ cancelAnimationFrame?(handle: number): void;
137
+ }
138
+ interface MessageEventLike {
139
+ readonly source: unknown;
140
+ readonly origin: string;
141
+ readonly data: unknown;
142
+ }
143
+ /** 프레임 창의 최소 모양. */
144
+ interface FrameWindow {
145
+ postMessage(message: unknown, targetOrigin: string): void;
146
+ }
147
+ /** iframe 요소의 최소 모양(`HTMLIFrameElement`가 만족한다). */
148
+ interface IframeLike {
149
+ readonly contentWindow: FrameWindow | null;
150
+ readonly ownerDocument?: {
151
+ readonly defaultView: HostWindow | null;
152
+ } | null;
153
+ }
154
+ /**
155
+ * 호스트가 넘기는 init 원본. `hostOrigin`·`gatewayBase`·`limits`는 호스트 옵션·정책에서 채운다.
156
+ */
157
+ interface BridgeInitInput {
158
+ /** 호스트가 만든 placement 스코프 키(예: `page:{경로}|{url}`, `slot:{id}|{url}`). storage 스코프에도 쓴다. */
159
+ readonly placementId: string;
160
+ /** 커스텀 페이지(`page`) 또는 슬롯 위젯(`slot`). 그 밖의 값은 생성 시 TypeError. */
161
+ readonly surface: BridgeSurface;
162
+ readonly phase: string;
163
+ readonly spaceId: string;
164
+ readonly locale: string;
165
+ readonly theme: ThemeState;
166
+ readonly slot?: SlotState;
167
+ readonly member: MemberState;
168
+ readonly partnerOrigins: readonly string[];
169
+ readonly translations?: BridgeTranslations;
170
+ }
171
+ /** 요청별 호스트 처리기. 없으면 해당 요청은 `UNSUPPORTED`(openExternal·log는 기본 동작이 있다). */
172
+ interface BridgeHostHandlers {
173
+ readonly navigate?: (path: string) => void;
174
+ readonly back?: () => void;
175
+ /** 없으면 `window.open(url, '_blank', 'noopener,noreferrer')`. */
176
+ readonly openExternal?: (url: string) => void;
177
+ readonly toast?: (message: string, kind?: ToastKind) => void;
178
+ /** rAF로 디바운스된 높이(px, 0~20000). */
179
+ readonly resize?: (height: number) => void;
180
+ readonly track?: (name: string, params?: Readonly<Record<string, unknown>>) => void;
181
+ /** real이 아닌 phase에서만 호출. 없으면 console. */
182
+ readonly log?: (level: LogLevel, message: string) => void;
183
+ }
184
+ /** 호스트가 주입하는 fetch. 플랫폼은 목적지 허용 목록·redirect 차단·인증을 적용하는 자체 fetch를 넘긴다. */
185
+ type HostFetch = (url: string, init: RequestInit) => Promise<Response>;
186
+ interface CreateBridgeHostOptions {
187
+ readonly iframe: IframeLike;
188
+ readonly session: string;
189
+ readonly hostOrigin: string;
190
+ readonly init: BridgeInitInput;
191
+ /** 같은 origin `/gw` 요청용 fetch(플랫폼 인증 fetch). `credentials: 'same-origin'`으로 호출된다. */
192
+ readonly fetchImpl: HostFetch;
193
+ /**
194
+ * 파트너 origin 요청용 fetch. 기본은 `globalThis.fetch`를 `credentials: 'omit'`·`redirect: 'error'`로,
195
+ * 인증 헤더 없이 호출한다. **인증을 붙이는 fetch를 주입하면** 파트너 백엔드가 그 자격증명을 응답에
196
+ * 되돌려 프레임으로 흘릴 수 있다는 위험을 주입하는 쪽이 받아들이는 것이다.
197
+ * `init.partnerOrigins`는 **팬 토큰을 보낼 vibe 백엔드 목록**이다 — 토큰 없이 부를 외부 API는 등록할
198
+ * 필요가 없다(external).
199
+ */
200
+ readonly partnerFetchImpl?: HostFetch;
201
+ /**
202
+ * 그 밖의 https origin(파트너 아님, hostOrigin 아님) 요청용 fetch. 기본은 `globalThis.fetch`를
203
+ * `credentials: 'omit'`·`redirect: 'error'`·`referrerPolicy: 'no-referrer'`로, 인증 헤더 없이 호출한다.
204
+ * 요청 헤더 허용 목록은 그대로 적용되고 `pinnedHeaders`(테넌시·앱)는 싣지 않는다.
205
+ * `policy.allowExternal: true`일 때만 호출된다(기본 false, 옵트인). 인증을 붙이는 fetch를 주입하지 말 것.
206
+ */
207
+ readonly externalFetchImpl?: HostFetch;
208
+ /**
209
+ * 게이트웨이 요청에 호스트가 덮어 싣는 헤더. 테넌시 헤더(`x-bstage-tenant-id`·`x-bstage-org-id`·
210
+ * `x-bstage-enterprise-id`)는 프레임 값이 버려지고 **여기 준 값만** 실린다. 앱 헤더
211
+ * (`x-bstage-app-id`·`x-bstage-app-key`)는 여기 주면 프레임 값을 덮는다. 그 밖의 키는 TypeError.
212
+ */
213
+ readonly pinnedHeaders?: Readonly<Record<string, string>>;
214
+ readonly handlers?: BridgeHostHandlers;
215
+ readonly policy?: Partial<BridgePolicy>;
216
+ }
217
+ interface BridgeHost {
218
+ /** 프레임에 이벤트를 보낸다. 이후 ready에 답하는 init에도 반영된다. */
219
+ send<E extends BridgeHostEventType>(type: E, payload: BridgeEventMap[E]): void;
220
+ /** 리스너·대기 중인 resize를 정리한다. 이후 메시지는 무시된다. */
221
+ dispose(): void;
222
+ }
223
+
224
+ /**
225
+ * 플랫폼 측 브리지 호스트.
226
+ *
227
+ * - 발신자 확인: `ev.source === iframe.contentWindow`, origin `"null"`(sandbox 프레임), 봉투 형식,
228
+ * `payload.session === session`이 모두 맞는 메시지만 처리한다.
229
+ * - `evt:ready`에 `res:init`으로 답한다(프레임이 재전송하면 매번 최신 init으로).
230
+ * - 요청은 fetch 정책(policy.ts)·요청별 상한(validators.ts)을 적용해 처리기 또는 주입 `fetchImpl`로 넘긴다.
231
+ *
232
+ * 토큰은 이 함수가 다루지 않는다 — 인증은 `fetchImpl`(플랫폼의 인증 fetch)이 호스트 쪽에서 붙이고,
233
+ * 프레임에는 응답 본문·상태·content-type만 돌아간다.
234
+ */
235
+ declare function createBridgeHost(options: CreateBridgeHostOptions): BridgeHost;
236
+
237
+ /** 디코딩 최대 횟수. 이 안에 고정점에 닿지 않으면(과도한 다중 인코딩) 거절한다. */
238
+ declare const MAX_DECODE_PASSES = 3;
239
+ interface PathCheckOptions {
240
+ /** `//`가 경로 어디에든(어느 단계에서든) 있으면 거절 — navigate용(라우터가 프로토콜 상대 URL로 읽는다). */
241
+ readonly forbidDoubleSlash?: boolean;
242
+ }
243
+ interface PathCheck {
244
+ readonly ok: boolean;
245
+ /** 원문과 디코딩 각 단계(ok일 때만 의미). 마지막 항목이 고정점. */
246
+ readonly forms: readonly string[];
247
+ }
248
+ /**
249
+ * 같은 origin 경로(게이트웨이 fetch·navigate 공용)의 구조 검사.
250
+ *
251
+ * 차단 목록 정규식을 늘리는 대신, 앞단(프록시·게이트웨이·라우터)이 몇 번을 더 디코딩하든 그 결과가 될 수
252
+ * 있는 **모든 단계**(원문 + `decodeURIComponent` 고정점까지)를 만들어 각각에 같은 규칙을 적용한다.
253
+ * - 디코딩 실패(overlong·잘못된 UTF-8)·3회 안에 고정점 미도달 → 거절
254
+ * - 역슬래시·`;`·NUL/제어문자 → 거절
255
+ * - 점 세그먼트(`/../`·`/./`·끝의 `/..`·`/.`) → 거절
256
+ * - `forbidDoubleSlash`면 `//` → 거절
257
+ *
258
+ * 세그먼트 안의 정상 인코딩 값(`/gw/x/a%2Fb`)은 풀어도 점 세그먼트가 생기지 않으므로 통과한다.
259
+ */
260
+ declare function checkPath(pathname: string, options?: PathCheckOptions): PathCheck;
261
+
262
+ export { ALLOWED_METHODS, ALLOWED_REQUEST_HEADERS, type BridgeHost, type BridgeHostHandlers, type BridgeInitInput, type BridgePolicy, BridgeSurface, type CreateBridgeHostOptions, DEFAULT_GATEWAY_DENY_SEGMENTS, DEFAULT_POLICY, type Destination, type DestinationContext, type DestinationKind, type FrameWindow, HOST_ONLY_HEADERS, type HostFetch, type HostWindow, type IframeLike, MAX_DECODE_PASSES, MAX_PARTNER_ORIGINS, type MessageEventLike, type PathCheck, type PathCheckOptions, type PreparedFetch, checkPath, classifyDestination, createBridgeHost, isAllowedGatewayPath, normalizePartnerOrigins, normalizePinnedHeaders, prepareFetch, resolveDestination, resolvePolicy };
package/dist/host.d.ts ADDED
@@ -0,0 +1,262 @@
1
+ import { f as BridgeHostEventType, e as BridgeEventMap, s as ToastKind, L as LogLevel, l as BridgeSurface, T as ThemeState, S as SlotState, M as MemberState, m as BridgeTranslations } from './messages-9N0GFq5b.js';
2
+ export { a as BRIDGE_SURFACES, I as IFRAME_ATTRIBUTES, v as isBridgeSurface } from './messages-9N0GFq5b.js';
3
+ export { C as CreateSrcdocOptions, c as createNonce, d as createSession, e as createSrcdoc, f as isValidSession } from './createSrcdoc-bOoxttPz.js';
4
+
5
+ /** 호스트 fetch 정책. */
6
+ interface BridgePolicy {
7
+ /** 같은 origin에서 허용할 경로 접두사. 기본 `["/gw/"]`(`/gw` 정확 일치 포함). */
8
+ readonly gatewayPrefixes: readonly string[];
9
+ /**
10
+ * `gatewayPrefixes` 안에서도 막는 경로 접두사. 기본은 인증 경로 — 토큰 갱신
11
+ * (`/gw/account/v1/auth/token/refresh`)은 refresh 쿠키로 새 access token을 **응답 본문**에 돌려주므로
12
+ * 프레임이 닿으면 토큰이 프레임에 들어간다. 디코딩·슬래시 병합·소문자화한 경로로 비교한다.
13
+ */
14
+ readonly gatewayDenyPrefixes: readonly string[];
15
+ /**
16
+ * 위치와 무관하게 막는 경로 세그먼트(소문자). 서비스·버전·phase 접두사가 붙은 실제 인증 경로
17
+ * (`/gw/svc/account/api/v1/auth/token/refresh`·`/gw/svc/api/v1/auth/token/refresh`)도 잡기 위해
18
+ * 접두사가 아니라 세그먼트로 본다. 기본은 {@link DEFAULT_GATEWAY_DENY_SEGMENTS}
19
+ * (`/gw/svc/x/token`·`/gw/svc/x/oauth/callback`·`/gw/svc/x/sso/login`처럼 auth 밖에서 토큰을 내는 경로,
20
+ * 디버그 엔드포인트, 회원 탈퇴 포함). 호스트는 이 상수를 명시해
21
+ * 넘기면 정책이 코드에 드러난다(값은 이 패키지 버전의 기본값과 같다 — 고정이 필요하면 리터럴 목록이나
22
+ * `[...DEFAULT_GATEWAY_DENY_SEGMENTS, '추가']`처럼 넘긴다).
23
+ *
24
+ * 이 목록과 별개로 **`token`이 들어간 세그먼트**(`tokens`·`access-tokens`·`refreshToken`)는 항상
25
+ * 거절한다 — 정책으로 끌 수 없다.
26
+ */
27
+ readonly gatewayDenySegments: readonly string[];
28
+ /**
29
+ * 같은 origin `/gw`·파트너가 아닌 **그 밖의 https origin**을 `externalFetchImpl`로 보낼지. 기본 `false`
30
+ * (옵트인) — 켜면 프레임이 임의 https로 데이터를 보내고 응답을 읽는 양방향 채널이 생기므로, 패키지를
31
+ * 올리기만 해서 열리지 않게 한다. 외부 호출을 허용하는 호스트(web-base)는 `true`를 명시하고,
32
+ * 막아야 하는 호스트(포털 미리보기)는 생략하거나 `false`.
33
+ */
34
+ readonly allowExternal: boolean;
35
+ /** 요청 본문 상한(UTF-8 바이트). 기본 1MB. */
36
+ readonly maxBodyBytes: number;
37
+ /** 프레임별 분당 요청 상한. 기본 300. */
38
+ readonly maxRequestsPerMinute: number;
39
+ /**
40
+ * 응답 본문 상한(UTF-8 바이트). 기본 10MB. 프로토콜에 정해지지 않은 값(구현 선택) — 넘으면 `BODY_TOO_LARGE`.
41
+ * 큰 응답 여러 개를 붙잡아 호스트 메모리를 쓰게 하는 것을 막는다.
42
+ */
43
+ readonly maxResponseBytes: number;
44
+ }
45
+ /**
46
+ * 기본 거절 세그먼트. 인증·토큰 발급(`auth`·`logout`·`mfa`·`token`·`oauth`·`sso`), 디버그 엔드포인트
47
+ * (`debug`·`debugger`·`xdebug`), 회원 탈퇴(`withdraw`). 호스트가 `policy.gatewayDenySegments`로 그대로
48
+ * 넘길 수 있게 공개한다.
49
+ */
50
+ declare const DEFAULT_GATEWAY_DENY_SEGMENTS: readonly string[];
51
+ declare const DEFAULT_POLICY: BridgePolicy;
52
+ /**
53
+ * 통과시키는 요청 헤더(소문자). 나머지(authorization·cookie·`x-bmf-sdk-version` 밖의 x-bmf-* 포함)는 버린다.
54
+ * 목적지 갈래(gateway·partner·external)와 무관하게 같은 목록이다.
55
+ */
56
+ declare const ALLOWED_REQUEST_HEADERS: readonly string[];
57
+ /**
58
+ * 테넌시 헤더 — 프레임 값은 **절대 통과시키지 않는다**. 호스트가 `pinnedHeaders`로 준 값만 실린다.
59
+ * 프레임이 테넌트를 고르면 사용자 자격증명이 실린 요청을 다른 테넌트로 보내 응답을 읽을 수 있다.
60
+ */
61
+ declare const HOST_ONLY_HEADERS: readonly string[];
62
+ declare const ALLOWED_METHODS: readonly string[];
63
+ /** partnerOrigins 최대 개수. */
64
+ declare const MAX_PARTNER_ORIGINS = 10;
65
+ /** 사용자 정책을 기본값과 합치고 검증한다. */
66
+ declare function resolvePolicy(policy: Partial<BridgePolicy> | undefined): BridgePolicy;
67
+ /**
68
+ * partnerOrigins를 검증한다 — https 정확한 origin만, 중복 제거, `excludeOrigin`(hostOrigin) 제외,
69
+ * 최대 10개. 나머지는 버린다.
70
+ */
71
+ declare function normalizePartnerOrigins(origins: unknown, excludeOrigin?: string): readonly string[];
72
+ /**
73
+ * 같은 origin 경로가 게이트웨이로 보낼 수 있는지: 구조 검사(`checkPath`) 통과 + 원문이 허용 접두사 안 +
74
+ * 어느 디코딩 단계도 거절 접두사·거절 세그먼트에 걸리지 않을 것.
75
+ */
76
+ declare function isAllowedGatewayPath(pathname: string, policy: BridgePolicy): boolean;
77
+ interface DestinationContext {
78
+ readonly hostOrigin: string;
79
+ readonly partnerOrigins: readonly string[];
80
+ readonly policy: BridgePolicy;
81
+ /** 게이트웨이 요청에 호스트가 덮어 싣는 헤더(`normalizePinnedHeaders` 결과). 파트너·외부 요청에는 싣지 않는다. */
82
+ readonly pinnedHeaders?: Readonly<Record<string, string>>;
83
+ }
84
+ /**
85
+ * 목적지 종류.
86
+ * - `gateway`: 같은 origin `/gw` — 쿠키·인증 fetch(`fetchImpl`), 고정 헤더.
87
+ * - `partner`: `partnerOrigins` 정확 일치 — `partnerFetchImpl`(호스트가 팬 토큰을 붙일 수 있는 백엔드).
88
+ * - `external`: 그 밖의 https origin(hostOrigin 제외) — `externalFetchImpl`, 무인증. `policy.allowExternal`로 끈다.
89
+ */
90
+ type DestinationKind = 'gateway' | 'partner' | 'external';
91
+ interface Destination {
92
+ readonly url: string;
93
+ readonly kind: DestinationKind;
94
+ }
95
+ /**
96
+ * 목적지 판정. `new URL(url, hostOrigin)`으로 정규화한 뒤(`..`·`%2e`·역슬래시는 여기서 해소된다)
97
+ * (a) 같은 origin + 게이트웨이 접두사 + 모호하지 않은 경로 → gateway, (b) **hostOrigin이 아닌** 파트너
98
+ * origin 정확 일치 → partner, (c) 그 밖의 https origin(`allowExternal`일 때, IP 리터럴·로컬 이름·
99
+ * 호스트와 같은 호스트명 제외) → external.
100
+ * hostOrigin은 (b)·(c)로 통과하지 않는다 — 그러면 /gw 밖 같은 origin 경로(쿠키 포함)가 열린다.
101
+ * http·userinfo·비 http 스킴은 모두 거절한다.
102
+ *
103
+ * 돌려주는 `url`은 **판정한 그 URL**(`href`)이다 — 호출자는 이 값으로 보낸다(판정과 전송이 같은 URL).
104
+ */
105
+ declare function classifyDestination(rawUrl: unknown, ctx: DestinationContext): Destination;
106
+ /** `classifyDestination`의 URL만. */
107
+ declare function resolveDestination(rawUrl: unknown, ctx: DestinationContext): string;
108
+ /**
109
+ * 호스트가 고정하는 헤더를 검증·소문자화한다. 허용 헤더·테넌시 헤더만 받는다(그 밖은 TypeError —
110
+ * 이 경로로 authorization 같은 값을 실수로 넣지 못하게).
111
+ */
112
+ declare function normalizePinnedHeaders(pinned: unknown): Readonly<Record<string, string>>;
113
+ interface PreparedFetch {
114
+ readonly url: string;
115
+ readonly kind: DestinationKind;
116
+ readonly init: {
117
+ readonly method: string;
118
+ readonly headers: Readonly<Record<string, string>>;
119
+ readonly body?: string;
120
+ /** gateway는 `same-origin`(주입 인증 fetch), partner·external은 `omit`(쿠키 없음). */
121
+ readonly credentials: 'same-origin' | 'omit';
122
+ readonly redirect: 'error';
123
+ /** partner·external만 — 호스트 페이지 URL(경로·쿼리)을 다른 origin에 Referer로 흘리지 않는다. */
124
+ readonly referrerPolicy?: 'no-referrer';
125
+ };
126
+ }
127
+ /** 프레임의 fetch 요청을 정책에 따라 실제 호출 인자로 바꾼다. 위반이면 BridgeError. */
128
+ declare function prepareFetch(payload: unknown, ctx: DestinationContext): PreparedFetch;
129
+
130
+ /** 호스트 메시지 리스너를 다는 창의 최소 모양. */
131
+ interface HostWindow {
132
+ addEventListener(type: 'message', fn: (ev: MessageEventLike) => void): void;
133
+ removeEventListener(type: 'message', fn: (ev: MessageEventLike) => void): void;
134
+ open?(url: string, target: string, features: string): unknown;
135
+ requestAnimationFrame?(cb: () => void): number;
136
+ cancelAnimationFrame?(handle: number): void;
137
+ }
138
+ interface MessageEventLike {
139
+ readonly source: unknown;
140
+ readonly origin: string;
141
+ readonly data: unknown;
142
+ }
143
+ /** 프레임 창의 최소 모양. */
144
+ interface FrameWindow {
145
+ postMessage(message: unknown, targetOrigin: string): void;
146
+ }
147
+ /** iframe 요소의 최소 모양(`HTMLIFrameElement`가 만족한다). */
148
+ interface IframeLike {
149
+ readonly contentWindow: FrameWindow | null;
150
+ readonly ownerDocument?: {
151
+ readonly defaultView: HostWindow | null;
152
+ } | null;
153
+ }
154
+ /**
155
+ * 호스트가 넘기는 init 원본. `hostOrigin`·`gatewayBase`·`limits`는 호스트 옵션·정책에서 채운다.
156
+ */
157
+ interface BridgeInitInput {
158
+ /** 호스트가 만든 placement 스코프 키(예: `page:{경로}|{url}`, `slot:{id}|{url}`). storage 스코프에도 쓴다. */
159
+ readonly placementId: string;
160
+ /** 커스텀 페이지(`page`) 또는 슬롯 위젯(`slot`). 그 밖의 값은 생성 시 TypeError. */
161
+ readonly surface: BridgeSurface;
162
+ readonly phase: string;
163
+ readonly spaceId: string;
164
+ readonly locale: string;
165
+ readonly theme: ThemeState;
166
+ readonly slot?: SlotState;
167
+ readonly member: MemberState;
168
+ readonly partnerOrigins: readonly string[];
169
+ readonly translations?: BridgeTranslations;
170
+ }
171
+ /** 요청별 호스트 처리기. 없으면 해당 요청은 `UNSUPPORTED`(openExternal·log는 기본 동작이 있다). */
172
+ interface BridgeHostHandlers {
173
+ readonly navigate?: (path: string) => void;
174
+ readonly back?: () => void;
175
+ /** 없으면 `window.open(url, '_blank', 'noopener,noreferrer')`. */
176
+ readonly openExternal?: (url: string) => void;
177
+ readonly toast?: (message: string, kind?: ToastKind) => void;
178
+ /** rAF로 디바운스된 높이(px, 0~20000). */
179
+ readonly resize?: (height: number) => void;
180
+ readonly track?: (name: string, params?: Readonly<Record<string, unknown>>) => void;
181
+ /** real이 아닌 phase에서만 호출. 없으면 console. */
182
+ readonly log?: (level: LogLevel, message: string) => void;
183
+ }
184
+ /** 호스트가 주입하는 fetch. 플랫폼은 목적지 허용 목록·redirect 차단·인증을 적용하는 자체 fetch를 넘긴다. */
185
+ type HostFetch = (url: string, init: RequestInit) => Promise<Response>;
186
+ interface CreateBridgeHostOptions {
187
+ readonly iframe: IframeLike;
188
+ readonly session: string;
189
+ readonly hostOrigin: string;
190
+ readonly init: BridgeInitInput;
191
+ /** 같은 origin `/gw` 요청용 fetch(플랫폼 인증 fetch). `credentials: 'same-origin'`으로 호출된다. */
192
+ readonly fetchImpl: HostFetch;
193
+ /**
194
+ * 파트너 origin 요청용 fetch. 기본은 `globalThis.fetch`를 `credentials: 'omit'`·`redirect: 'error'`로,
195
+ * 인증 헤더 없이 호출한다. **인증을 붙이는 fetch를 주입하면** 파트너 백엔드가 그 자격증명을 응답에
196
+ * 되돌려 프레임으로 흘릴 수 있다는 위험을 주입하는 쪽이 받아들이는 것이다.
197
+ * `init.partnerOrigins`는 **팬 토큰을 보낼 vibe 백엔드 목록**이다 — 토큰 없이 부를 외부 API는 등록할
198
+ * 필요가 없다(external).
199
+ */
200
+ readonly partnerFetchImpl?: HostFetch;
201
+ /**
202
+ * 그 밖의 https origin(파트너 아님, hostOrigin 아님) 요청용 fetch. 기본은 `globalThis.fetch`를
203
+ * `credentials: 'omit'`·`redirect: 'error'`·`referrerPolicy: 'no-referrer'`로, 인증 헤더 없이 호출한다.
204
+ * 요청 헤더 허용 목록은 그대로 적용되고 `pinnedHeaders`(테넌시·앱)는 싣지 않는다.
205
+ * `policy.allowExternal: true`일 때만 호출된다(기본 false, 옵트인). 인증을 붙이는 fetch를 주입하지 말 것.
206
+ */
207
+ readonly externalFetchImpl?: HostFetch;
208
+ /**
209
+ * 게이트웨이 요청에 호스트가 덮어 싣는 헤더. 테넌시 헤더(`x-bstage-tenant-id`·`x-bstage-org-id`·
210
+ * `x-bstage-enterprise-id`)는 프레임 값이 버려지고 **여기 준 값만** 실린다. 앱 헤더
211
+ * (`x-bstage-app-id`·`x-bstage-app-key`)는 여기 주면 프레임 값을 덮는다. 그 밖의 키는 TypeError.
212
+ */
213
+ readonly pinnedHeaders?: Readonly<Record<string, string>>;
214
+ readonly handlers?: BridgeHostHandlers;
215
+ readonly policy?: Partial<BridgePolicy>;
216
+ }
217
+ interface BridgeHost {
218
+ /** 프레임에 이벤트를 보낸다. 이후 ready에 답하는 init에도 반영된다. */
219
+ send<E extends BridgeHostEventType>(type: E, payload: BridgeEventMap[E]): void;
220
+ /** 리스너·대기 중인 resize를 정리한다. 이후 메시지는 무시된다. */
221
+ dispose(): void;
222
+ }
223
+
224
+ /**
225
+ * 플랫폼 측 브리지 호스트.
226
+ *
227
+ * - 발신자 확인: `ev.source === iframe.contentWindow`, origin `"null"`(sandbox 프레임), 봉투 형식,
228
+ * `payload.session === session`이 모두 맞는 메시지만 처리한다.
229
+ * - `evt:ready`에 `res:init`으로 답한다(프레임이 재전송하면 매번 최신 init으로).
230
+ * - 요청은 fetch 정책(policy.ts)·요청별 상한(validators.ts)을 적용해 처리기 또는 주입 `fetchImpl`로 넘긴다.
231
+ *
232
+ * 토큰은 이 함수가 다루지 않는다 — 인증은 `fetchImpl`(플랫폼의 인증 fetch)이 호스트 쪽에서 붙이고,
233
+ * 프레임에는 응답 본문·상태·content-type만 돌아간다.
234
+ */
235
+ declare function createBridgeHost(options: CreateBridgeHostOptions): BridgeHost;
236
+
237
+ /** 디코딩 최대 횟수. 이 안에 고정점에 닿지 않으면(과도한 다중 인코딩) 거절한다. */
238
+ declare const MAX_DECODE_PASSES = 3;
239
+ interface PathCheckOptions {
240
+ /** `//`가 경로 어디에든(어느 단계에서든) 있으면 거절 — navigate용(라우터가 프로토콜 상대 URL로 읽는다). */
241
+ readonly forbidDoubleSlash?: boolean;
242
+ }
243
+ interface PathCheck {
244
+ readonly ok: boolean;
245
+ /** 원문과 디코딩 각 단계(ok일 때만 의미). 마지막 항목이 고정점. */
246
+ readonly forms: readonly string[];
247
+ }
248
+ /**
249
+ * 같은 origin 경로(게이트웨이 fetch·navigate 공용)의 구조 검사.
250
+ *
251
+ * 차단 목록 정규식을 늘리는 대신, 앞단(프록시·게이트웨이·라우터)이 몇 번을 더 디코딩하든 그 결과가 될 수
252
+ * 있는 **모든 단계**(원문 + `decodeURIComponent` 고정점까지)를 만들어 각각에 같은 규칙을 적용한다.
253
+ * - 디코딩 실패(overlong·잘못된 UTF-8)·3회 안에 고정점 미도달 → 거절
254
+ * - 역슬래시·`;`·NUL/제어문자 → 거절
255
+ * - 점 세그먼트(`/../`·`/./`·끝의 `/..`·`/.`) → 거절
256
+ * - `forbidDoubleSlash`면 `//` → 거절
257
+ *
258
+ * 세그먼트 안의 정상 인코딩 값(`/gw/x/a%2Fb`)은 풀어도 점 세그먼트가 생기지 않으므로 통과한다.
259
+ */
260
+ declare function checkPath(pathname: string, options?: PathCheckOptions): PathCheck;
261
+
262
+ export { ALLOWED_METHODS, ALLOWED_REQUEST_HEADERS, type BridgeHost, type BridgeHostHandlers, type BridgeInitInput, type BridgePolicy, BridgeSurface, type CreateBridgeHostOptions, DEFAULT_GATEWAY_DENY_SEGMENTS, DEFAULT_POLICY, type Destination, type DestinationContext, type DestinationKind, type FrameWindow, HOST_ONLY_HEADERS, type HostFetch, type HostWindow, type IframeLike, MAX_DECODE_PASSES, MAX_PARTNER_ORIGINS, type MessageEventLike, type PathCheck, type PathCheckOptions, type PreparedFetch, checkPath, classifyDestination, createBridgeHost, isAllowedGatewayPath, normalizePartnerOrigins, normalizePinnedHeaders, prepareFetch, resolveDestination, resolvePolicy };