@cp949/iframecall 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)을 따르며, 버전 관리는 [Semantic Versioning](https://semver.org/spec/v2.0.0.html)을 준수합니다. 0.x 버전대에서는 호환성이 깨지는 변경도 minor 버전 증가로 처리합니다.
6
6
 
7
+ ## [0.2.1] - 2026-08-25
8
+
9
+ ### Added
10
+
11
+ - host controller의 정식 command 실행 API로 `invoke(command, args, options?)`를 추가했습니다.
12
+
13
+ ### Deprecated
14
+
15
+ - `controller.call()`은 호환성을 위해 유지하지만 다음 major release에서 제거할 예정입니다. `controller.invoke()`를 사용하세요.
16
+
7
17
  ## [0.2.0] - 2026-04-28
8
18
 
9
19
  ### Added
package/README.md CHANGED
@@ -20,16 +20,16 @@ yarn add @cp949/iframecall
20
20
 
21
21
  `react`, `@types/react`는 peer dependency이다. 프로젝트에 이미 설치되어 있어야 한다.
22
22
 
23
- | peer | 지원 버전 |
24
- |------|-----------|
25
- | `react` | `^18.0.0 \|\| ^19.0.0` |
23
+ | peer | 지원 버전 |
24
+ | -------------- | ---------------------- |
25
+ | `react` | `^18.0.0 \|\| ^19.0.0` |
26
26
  | `@types/react` | `^18.0.0 \|\| ^19.0.0` |
27
27
 
28
28
  ## 진입점
29
29
 
30
- | import | 용도 |
31
- |--------|------|
32
- | `@cp949/iframecall/host` | 부모 페이지(host)에서 iframe을 제어할 때 |
30
+ | import | 용도 |
31
+ | -------------------------- | ------------------------------------------------- |
32
+ | `@cp949/iframecall/host` | 부모 페이지(host)에서 iframe을 제어할 때 |
33
33
  | `@cp949/iframecall/iframe` | 임베드된 페이지(iframe)에서 host의 호출을 받을 때 |
34
34
 
35
35
  호스트와 iframe은 서로 다른 origin에서 실행되며, 각 진입점은 그쪽에서만 필요한 API와 타입만 노출한다.
@@ -84,7 +84,7 @@ export function HostPage() {
84
84
 
85
85
  const handleGreet = async () => {
86
86
  if (!controller) return;
87
- const result = await controller.call("greet", ["World"]);
87
+ const result = await controller.invoke("greet", ["World"]);
88
88
  console.log(result); // "Hello, World!"
89
89
  };
90
90
 
@@ -94,11 +94,7 @@ export function HostPage() {
94
94
  <button type="button" onClick={handleGreet} disabled={status !== "ready"}>
95
95
  Greet
96
96
  </button>
97
- <iframe
98
- ref={iframeRef}
99
- src={IFRAME_ORIGIN}
100
- title="iframe demo"
101
- />
97
+ <iframe ref={iframeRef} src={IFRAME_ORIGIN} title="iframe demo" />
102
98
  </div>
103
99
  );
104
100
  }
@@ -110,11 +106,11 @@ export function HostPage() {
110
106
 
111
107
  prefix 컨벤션:
112
108
 
113
- | prefix | 의미 |
114
- |--------|------|
115
- | (없음) | host로 dispatch되는 remote command |
116
- | `_` | 사용자 local-only 메서드. dispatch 대상에서 제외된다. (예: `_sendLifecycleReady`, `_onStatusChange`) |
117
- | `$` | 라이브러리 점유 namespace. dispatch에서 제외되며, 라이브러리가 정의한 hook 이름만 의미가 있다. |
109
+ | prefix | 의미 |
110
+ | ------ | ---------------------------------------------------------------------------------------------------- |
111
+ | (없음) | host로 dispatch되는 remote command |
112
+ | `_` | 사용자 local-only 메서드. dispatch 대상에서 제외된다. (예: `_sendLifecycleReady`, `_onStatusChange`) |
113
+ | `$` | 라이브러리 점유 namespace. dispatch에서 제외되며, 라이브러리가 정의한 hook 이름만 의미가 있다. |
118
114
 
119
115
  현재 라이브러리가 인식하는 hook은 한 개:
120
116
 
@@ -222,7 +218,7 @@ host iframe
222
218
  │ ◀── ready ───────────────────────────│ (lifecycle 채널)
223
219
  │ controller.status = "ready" │
224
220
  │ │
225
- │ controller.call("greet", ["World"])
221
+ │ controller.invoke("greet", ["World"])│
226
222
  │ ── request ─────────────────────────▶│ $onCommandRun → AppCommands.greet("World")
227
223
  │ ◀── notify status-changed:processing │ (도메인 채널, refcount 0→1)
228
224
  │ ◀── response: "Hello, World!" ───────│
@@ -230,7 +226,7 @@ host iframe
230
226
  ```
231
227
 
232
228
  - iframe이 마운트되면 `commands._sendLifecycleReady()`가 `sendLifecycleReady()`를 통해 transport ready 신호를 보낸다.
233
- - host의 `controller.call`은 ready 시점까지 대기한 뒤 전송된다.
229
+ - host의 `controller.invoke`는 ready 시점까지 대기한 뒤 전송된다.
234
230
  - 응답은 Promise로 돌아오며, iframe 측 메서드가 throw하면 host 쪽 Promise는 reject된다.
235
231
  - iframe → host 단방향 알림은 `sendNotificationToHost`로 보내고, host 쪽에서 `controller.onNotificationFromIframe`으로 받는다.
236
232
  - **라이프사이클 채널과 도메인 채널은 책임이 다르다.** `ready`/`terminated`는 transport 신호 전용이고, 도메인 알림(`status-changed` 등)에는 `"ready"` 같은 lifecycle 의미를 담지 않는다.
@@ -240,16 +236,17 @@ host iframe
240
236
 
241
237
  ### host 진입점
242
238
 
243
- | export | 종류 | 설명 |
244
- |--------|------|------|
245
- | `useIframeCallController` | hook | host용 React 훅. `iframeRef`, `controller`, `status`를 반환한다. |
246
- | `createIframeCallController` | factory | 훅 없이 컨트롤러를 직접 만들 때 사용 |
247
- | `createIframeWindowTransport` | factory | 커스텀 트랜스포트 구성용 |
248
- | `consoleDebugLogger` | util | 디버그 이벤트를 콘솔에 출력하는 로거 |
239
+ | export | 종류 | 설명 |
240
+ | ----------------------------- | ------- | ---------------------------------------------------------------- |
241
+ | `useIframeCallController` | hook | host용 React 훅. `iframeRef`, `controller`, `status`를 반환한다. |
242
+ | `createIframeCallController` | factory | 훅 없이 컨트롤러를 직접 만들 때 사용 |
243
+ | `createIframeWindowTransport` | factory | 커스텀 트랜스포트 구성용 |
244
+ | `consoleDebugLogger` | util | 디버그 이벤트를 콘솔에 출력하는 로거 |
249
245
 
250
246
  훅이 반환하는 `controller`의 주요 멤버:
251
247
 
252
- - `controller.call(command, args, options?)` — iframe의 커맨드 호출, 결과를 Promise로 반환
248
+ - `controller.invoke(command, args, options?)` — iframe의 커맨드 호출, 결과를 Promise로 반환
249
+ - `controller.call(command, args, options?)` — deprecated 호환 alias. `invoke`를 사용하세요.
253
250
  - `controller.onNotificationFromIframe(event, handler)` — iframe이 보내는 알림 구독, unsubscribe 함수 반환
254
251
  - `controller.ready` — iframe ready 신호 대기용 Promise
255
252
  - `controller.terminated` — 종료 사유를 노출하는 Promise (정상 dispose면 `null`)
@@ -258,11 +255,11 @@ host iframe
258
255
 
259
256
  ### iframe 진입점
260
257
 
261
- | export | 종류 | 설명 |
262
- |--------|------|------|
263
- | `useIframeCallRunner` | hook | iframe용 React 훅. `commands`, `iframeHelper`, `isActive`를 반환한다. mount 전 `commands`/`iframeHelper`는 `null`. |
264
- | `createIframeCallRunner` | factory | 훅 없이 러너를 직접 만들 때 사용 |
265
- | `createParentWindowTransport` | factory | 커스텀 트랜스포트 구성용 |
258
+ | export | 종류 | 설명 |
259
+ | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
260
+ | `useIframeCallRunner` | hook | iframe용 React 훅. `commands`, `iframeHelper`, `isActive`를 반환한다. mount 전 `commands`/`iframeHelper`는 `null`. |
261
+ | `createIframeCallRunner` | factory | 훅 없이 러너를 직접 만들 때 사용 |
262
+ | `createParentWindowTransport` | factory | 커스텀 트랜스포트 구성용 |
266
263
 
267
264
  `iframeHelper`의 주요 멤버:
268
265
 
@@ -276,7 +273,7 @@ host iframe
276
273
  - `_` prefix → 사용자 local-only (dispatch 제외)
277
274
  - `$` prefix → 라이브러리 namespace (dispatch 제외, 인식되는 hook은 `$onCommandRun`)
278
275
 
279
- `onStatusChange`처럼 prefix가 없으면서 host에서 호출하면 안 되는 메서드는 반드시 `_` prefix를 붙여야 한다. 그렇지 않으면 host가 `controller.call("onStatusChange", [...])`로 직접 호출할 수 있다.
276
+ `onStatusChange`처럼 prefix가 없으면서 host에서 호출하면 안 되는 메서드는 반드시 `_` prefix를 붙여야 한다. 그렇지 않으면 host가 `controller.invoke("onStatusChange", [...])`로 직접 호출할 수 있다.
280
277
 
281
278
  > 알림은 iframe → host 단방향이다. host → iframe 알림은 라이브러리 외부에서 `postMessage`로 직접 처리하거나, host에서 커맨드를 호출해 처리한다.
282
279
 
@@ -288,9 +285,9 @@ host iframe
288
285
 
289
286
  `targetOrigin`과 `allowedOrigins`는 반드시 명시적으로 지정한다. 와일드카드(`*`)는 사용하지 않는다.
290
287
 
291
- | 옵션 | 의미 |
292
- |------|------|
293
- | `targetOrigin` | `postMessage` 전송 시 사용할 대상 origin |
288
+ | 옵션 | 의미 |
289
+ | ---------------- | ----------------------------------------- |
290
+ | `targetOrigin` | `postMessage` 전송 시 사용할 대상 origin |
294
291
  | `allowedOrigins` | 수신 시 허용할 origin 화이트리스트 (배열) |
295
292
 
296
293
  수신 메시지의 `event.origin`이 화이트리스트에 없으면 무시된다.
@@ -111,10 +111,12 @@ function isRecord(value) {
111
111
 
112
112
  // src/core/transport.ts
113
113
  function createIframeWindowTransport(iframe) {
114
+ var _a;
114
115
  return {
115
- expectedSource: iframe.contentWindow ?? void 0,
116
+ expectedSource: (_a = iframe.contentWindow) != null ? _a : void 0,
116
117
  post(message, targetOrigin, transfer) {
117
- iframe.contentWindow?.postMessage(
118
+ var _a2;
119
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage(
118
120
  message,
119
121
  targetOrigin,
120
122
  transfer
package/dist/host.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { H as HostDebugEvent, C as CommandMap, I as IframeCallControllerOptions, a as IframeCallController, S as SerializedIframeCallError } from './messages-BuRipoIx.js';
2
- export { b as CommandArgs, c as CommandHandler, d as CommandResult, e as CommandRunner, f as IframeCallCallOptions, g as IframeCallLogger, h as IframeCallNotify, i as IframeCallRequest, j as IframeCallResponse, k as IframeCallTransferable, l as IframeCallTransport, m as IframeCallTransportEvent, N as NotifyHandler, P as ParsedIframeCallMessage, R as ReadyPolicy, n as ReservedNotificationName, o as createIframeCallError, p as createIframeCallErrorResponse, q as createIframeCallNotify, r as createIframeCallRequest, s as createIframeCallSuccessResponse, t as createIframeWindowTransport, u as isSerializedIframeCallError, v as parseIframeCallMessage, w as serializeIframeCallError } from './messages-BuRipoIx.js';
1
+ import { H as HostDebugEvent, C as CommandMap, I as IframeCallControllerOptions, a as IframeCallController, S as SerializedIframeCallError } from './messages-uOmcU6eT.js';
2
+ export { b as CommandArgs, c as CommandHandler, d as CommandResult, e as CommandRunner, f as IframeCallCallOptions, g as IframeCallLogger, h as IframeCallNotify, i as IframeCallRequest, j as IframeCallResponse, k as IframeCallTransferable, l as IframeCallTransport, m as IframeCallTransportEvent, N as NotifyHandler, P as ParsedIframeCallMessage, R as ReadyPolicy, n as ReservedNotificationName, o as createIframeCallError, p as createIframeCallErrorResponse, q as createIframeCallNotify, r as createIframeCallRequest, s as createIframeCallSuccessResponse, t as createIframeWindowTransport, u as isSerializedIframeCallError, v as parseIframeCallMessage, w as serializeIframeCallError } from './messages-uOmcU6eT.js';
3
3
 
4
4
  /** consoleDebugLogger 옵션. prefix를 명시하지 않으면 [iframecall:host]가 기본값이다. */
5
5
  type ConsoleDebugLoggerOptions = {
package/dist/host.js CHANGED
@@ -8,11 +8,12 @@ import {
8
8
  isSerializedIframeCallError,
9
9
  parseIframeCallMessage,
10
10
  serializeIframeCallError
11
- } from "./chunk-UYZXYOI6.js";
11
+ } from "./chunk-RSUK22E2.js";
12
12
 
13
13
  // src/host/consoleDebugLogger.ts
14
14
  function consoleDebugLogger(options = {}) {
15
- const prefix = options.prefix ?? "[iframecall:host]";
15
+ var _a;
16
+ const prefix = (_a = options.prefix) != null ? _a : "[iframecall:host]";
16
17
  const head = prefix.length > 0 ? `${prefix} ` : "";
17
18
  return (event) => {
18
19
  switch (event.type) {
@@ -95,7 +96,7 @@ function createControllerLifecycle(options) {
95
96
  return;
96
97
  }
97
98
  cleanupDone = true;
98
- onCleanup?.();
99
+ onCleanup == null ? void 0 : onCleanup();
99
100
  }
100
101
  return {
101
102
  ready,
@@ -117,7 +118,8 @@ function createNotifyHandlerRegistry() {
117
118
  const handlers = /* @__PURE__ */ new Map();
118
119
  return {
119
120
  register(event, handler) {
120
- const set = handlers.get(event) ?? /* @__PURE__ */ new Set();
121
+ var _a;
122
+ const set = (_a = handlers.get(event)) != null ? _a : /* @__PURE__ */ new Set();
121
123
  set.add(handler);
122
124
  handlers.set(event, set);
123
125
  return () => {
@@ -203,7 +205,8 @@ function createPendingCallRegistry(transport, targetOrigin) {
203
205
  pending.clear();
204
206
  },
205
207
  getCommand(id) {
206
- return pending.get(id)?.command;
208
+ var _a;
209
+ return (_a = pending.get(id)) == null ? void 0 : _a.command;
207
210
  }
208
211
  };
209
212
  }
@@ -244,24 +247,26 @@ function createReadyQueue() {
244
247
 
245
248
  // src/host/controller.ts
246
249
  function createIframeCallController(options) {
250
+ var _a, _b, _c, _d, _e, _f, _g;
247
251
  const targetOrigin = requireTargetOrigin(options.targetOrigin);
248
- const allowedOrigins = new Set(options.allowedOrigins ?? [targetOrigin]);
249
- const transport = options.transport ?? createIframeWindowTransport(options.iframe);
250
- const generateId = options.generateId ?? crypto.randomUUID.bind(crypto);
251
- const defaultTimeoutMs = options.defaultTimeoutMs ?? 3e4;
252
- const readyTimeoutMs = options.readyTimeoutMs ?? defaultTimeoutMs;
253
- const readyPolicy = options.readyPolicy ?? "queue";
254
- const readyQueueLimit = options.readyQueueLimit ?? Number.POSITIVE_INFINITY;
252
+ const allowedOrigins = new Set((_a = options.allowedOrigins) != null ? _a : [targetOrigin]);
253
+ const transport = (_b = options.transport) != null ? _b : createIframeWindowTransport(options.iframe);
254
+ const generateId = (_c = options.generateId) != null ? _c : createDefaultRequestId;
255
+ const defaultTimeoutMs = (_d = options.defaultTimeoutMs) != null ? _d : 3e4;
256
+ const readyTimeoutMs = (_e = options.readyTimeoutMs) != null ? _e : defaultTimeoutMs;
257
+ const readyPolicy = (_f = options.readyPolicy) != null ? _f : "queue";
258
+ const readyQueueLimit = (_g = options.readyQueueLimit) != null ? _g : Number.POSITIVE_INFINITY;
255
259
  const pending = createPendingCallRegistry(transport, targetOrigin);
256
260
  const queue = createReadyQueue();
257
261
  const notifyRegistry = createNotifyHandlerRegistry();
258
262
  const debugSubscribers = /* @__PURE__ */ new Set();
259
263
  function emitDebug(event) {
264
+ var _a2;
260
265
  for (const handler of debugSubscribers) {
261
266
  try {
262
267
  handler(event);
263
268
  } catch (error) {
264
- options.logger?.warn("iframecall debug subscriber threw.", error);
269
+ (_a2 = options.logger) == null ? void 0 : _a2.warn("iframecall debug subscriber threw.", error);
265
270
  }
266
271
  }
267
272
  }
@@ -289,68 +294,67 @@ function createIframeCallController(options) {
289
294
  unsubscribeTransport();
290
295
  notifyRegistry.clear();
291
296
  });
292
- const controller = {
293
- ready: lifecycle.ready,
294
- terminated: lifecycle.terminated,
295
- call(cmd, args, callOptions) {
296
- const terminatedError = lifecycle.getTerminatedError();
297
- if (terminatedError !== null) {
298
- return Promise.reject(terminatedError);
299
- }
300
- if (!lifecycle.isReady() && readyPolicy === "reject") {
301
- return Promise.reject(
302
- createIframeCallError("not_ready", "Iframe is not ready.", {
303
- command: cmd
297
+ const invoke = (cmd, args, callOptions) => {
298
+ var _a2;
299
+ const terminatedError = lifecycle.getTerminatedError();
300
+ if (terminatedError !== null) {
301
+ return Promise.reject(terminatedError);
302
+ }
303
+ if (!lifecycle.isReady() && readyPolicy === "reject") {
304
+ return Promise.reject(
305
+ createIframeCallError("not_ready", "Iframe is not ready.", {
306
+ command: cmd
307
+ })
308
+ );
309
+ }
310
+ const id = generateId();
311
+ const timeoutMs = (_a2 = callOptions == null ? void 0 : callOptions.timeoutMs) != null ? _a2 : defaultTimeoutMs;
312
+ return new Promise((resolve, reject) => {
313
+ const timeoutId = timeoutMs === 0 || timeoutMs === Number.POSITIVE_INFINITY ? null : setTimeout(() => {
314
+ pending.delete(id);
315
+ queue.delete(id);
316
+ reject(
317
+ createIframeCallError("timeout", "Command timed out.", {
318
+ command: cmd,
319
+ details: { timeoutMs }
304
320
  })
305
321
  );
306
- }
307
- const id = generateId();
308
- const timeoutMs = callOptions?.timeoutMs ?? defaultTimeoutMs;
309
- return new Promise((resolve, reject) => {
310
- const timeoutId = timeoutMs === 0 || timeoutMs === Number.POSITIVE_INFINITY ? null : setTimeout(() => {
311
- pending.delete(id);
312
- queue.delete(id);
322
+ }, timeoutMs);
323
+ const call = {
324
+ command: cmd,
325
+ timeoutId,
326
+ resolve,
327
+ reject
328
+ };
329
+ if (!lifecycle.isReady()) {
330
+ if (queue.size() >= readyQueueLimit) {
331
+ if (timeoutId !== null) clearTimeout(timeoutId);
313
332
  reject(
314
- createIframeCallError("timeout", "Command timed out.", {
333
+ createIframeCallError("queue_overflow", "Ready queue overflow.", {
315
334
  command: cmd,
316
- details: { timeoutMs }
335
+ details: { readyQueueLimit }
317
336
  })
318
337
  );
319
- }, timeoutMs);
320
- const call = {
321
- command: cmd,
322
- timeoutId,
323
- resolve,
324
- reject
325
- };
326
- if (!lifecycle.isReady()) {
327
- if (queue.size() >= readyQueueLimit) {
328
- if (timeoutId !== null) {
329
- clearTimeout(timeoutId);
330
- }
331
- reject(
332
- createIframeCallError(
333
- "queue_overflow",
334
- "Ready queue overflow.",
335
- {
336
- command: cmd,
337
- details: { readyQueueLimit }
338
- }
339
- )
340
- );
341
- return;
342
- }
343
- queue.add(id, {
344
- ...call,
345
- args,
346
- transfer: callOptions?.transfer
347
- });
348
338
  return;
349
339
  }
350
- emitDebug({ type: "commandSentToIframe", command: cmd, args });
351
- pending.add(id, call);
352
- pending.post(id, cmd, args, callOptions?.transfer);
353
- });
340
+ queue.add(id, {
341
+ ...call,
342
+ args,
343
+ transfer: callOptions == null ? void 0 : callOptions.transfer
344
+ });
345
+ return;
346
+ }
347
+ emitDebug({ type: "commandSentToIframe", command: cmd, args });
348
+ pending.add(id, call);
349
+ pending.post(id, cmd, args, callOptions == null ? void 0 : callOptions.transfer);
350
+ });
351
+ };
352
+ const controller = {
353
+ ready: lifecycle.ready,
354
+ terminated: lifecycle.terminated,
355
+ invoke,
356
+ call(cmd, args, callOptions) {
357
+ return invoke(cmd, args, callOptions);
354
358
  },
355
359
  onNotificationFromIframe(event, handler) {
356
360
  return notifyRegistry.register(
@@ -367,6 +371,7 @@ function createIframeCallController(options) {
367
371
  }
368
372
  },
369
373
  async dispose(reason = "host_requested") {
374
+ var _a2;
370
375
  if (lifecycle.isTerminated()) {
371
376
  lifecycle.cleanup();
372
377
  return;
@@ -384,7 +389,7 @@ function createIframeCallController(options) {
384
389
  targetOrigin
385
390
  );
386
391
  } catch (error) {
387
- options.logger?.warn("iframecall dispose message failed.", error);
392
+ (_a2 = options.logger) == null ? void 0 : _a2.warn("iframecall dispose message failed.", error);
388
393
  } finally {
389
394
  lifecycle.terminate(lifecycleError);
390
395
  lifecycle.cleanup();
@@ -393,6 +398,14 @@ function createIframeCallController(options) {
393
398
  };
394
399
  return controller;
395
400
  }
401
+ function createDefaultRequestId() {
402
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
403
+ let id = "";
404
+ for (const byte of bytes) {
405
+ id += byte.toString(16).padStart(2, "0");
406
+ }
407
+ return id;
408
+ }
396
409
  function createTransportRouter(deps) {
397
410
  const {
398
411
  lifecycle,
@@ -411,7 +424,7 @@ function createTransportRouter(deps) {
411
424
  return;
412
425
  }
413
426
  const parsed = parseIframeCallMessage(event.data);
414
- if (parsed?.type === "response") {
427
+ if ((parsed == null ? void 0 : parsed.type) === "response") {
415
428
  const responseMessage = parsed.message;
416
429
  const command = pending.getCommand(responseMessage.id);
417
430
  pending.settle(responseMessage.id, responseMessage);
@@ -432,7 +445,7 @@ function createTransportRouter(deps) {
432
445
  }
433
446
  return;
434
447
  }
435
- if (parsed?.type !== "notify") return;
448
+ if ((parsed == null ? void 0 : parsed.type) !== "notify") return;
436
449
  const { event: notifyEvent, payload } = parsed.message;
437
450
  if (notifyEvent === "ready") {
438
451
  handleReadyNotify(payload, lifecycle, queue, pending, logger, emitDebug);
@@ -445,7 +458,7 @@ function createTransportRouter(deps) {
445
458
  emitDebug({
446
459
  type: "terminatedReceived",
447
460
  reason,
448
- error: cause ?? null
461
+ error: cause != null ? cause : null
449
462
  });
450
463
  lifecycle.terminate(
451
464
  createIframeCallError("terminated", "Iframe terminated.", {
@@ -465,7 +478,7 @@ function createTransportRouter(deps) {
465
478
  }
466
479
  function handleReadyNotify(payload, lifecycle, queue, pending, logger, emitDebug) {
467
480
  if (lifecycle.isReady()) {
468
- logger?.warn("iframecall duplicate ready ignored.", payload);
481
+ logger == null ? void 0 : logger.warn("iframecall duplicate ready ignored.", payload);
469
482
  return;
470
483
  }
471
484
  if (!isSupportedReadyPayload(payload)) {
package/dist/iframe.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { x as IframeDebugEvent, C as CommandMap, y as IframeCallRunnerOptions, z as IframeCallRunnerHandle, A as CommandsConstructor, B as IframeCallRunnerClassOptions, D as IframeHelper } from './messages-BuRipoIx.js';
2
- export { b as CommandArgs, c as CommandHandler, d as CommandResult, e as CommandRunner, E as DomainNotificationKey, f as IframeCallCallOptions, g as IframeCallLogger, h as IframeCallNotify, i as IframeCallRequest, j as IframeCallResponse, k as IframeCallTransferable, l as IframeCallTransport, m as IframeCallTransportEvent, N as NotifyHandler, P as ParsedIframeCallMessage, R as ReadyPolicy, n as ReservedNotificationName, S as SerializedIframeCallError, o as createIframeCallError, p as createIframeCallErrorResponse, q as createIframeCallNotify, r as createIframeCallRequest, s as createIframeCallSuccessResponse, F as createParentWindowTransport, u as isSerializedIframeCallError, v as parseIframeCallMessage, w as serializeIframeCallError } from './messages-BuRipoIx.js';
1
+ import { x as IframeDebugEvent, C as CommandMap, y as IframeCallRunnerOptions, z as IframeCallRunnerHandle, A as CommandsConstructor, B as IframeCallRunnerClassOptions, D as IframeHelper } from './messages-uOmcU6eT.js';
2
+ export { b as CommandArgs, c as CommandHandler, d as CommandResult, e as CommandRunner, E as DomainNotificationKey, f as IframeCallCallOptions, g as IframeCallLogger, h as IframeCallNotify, i as IframeCallRequest, j as IframeCallResponse, k as IframeCallTransferable, l as IframeCallTransport, m as IframeCallTransportEvent, N as NotifyHandler, P as ParsedIframeCallMessage, R as ReadyPolicy, n as ReservedNotificationName, S as SerializedIframeCallError, o as createIframeCallError, p as createIframeCallErrorResponse, q as createIframeCallNotify, r as createIframeCallRequest, s as createIframeCallSuccessResponse, F as createParentWindowTransport, u as isSerializedIframeCallError, v as parseIframeCallMessage, w as serializeIframeCallError } from './messages-uOmcU6eT.js';
3
3
 
4
4
  /** consoleDebugLogger 옵션. prefix를 명시하지 않으면 [iframecall:iframe]가 기본값이다. */
5
5
  type ConsoleDebugLoggerOptions = {
package/dist/iframe.js CHANGED
@@ -8,11 +8,12 @@ import {
8
8
  isSerializedIframeCallError,
9
9
  parseIframeCallMessage,
10
10
  serializeIframeCallError
11
- } from "./chunk-UYZXYOI6.js";
11
+ } from "./chunk-RSUK22E2.js";
12
12
 
13
13
  // src/iframe/consoleDebugLogger.ts
14
14
  function consoleDebugLogger(options = {}) {
15
- const prefix = options.prefix ?? "[iframecall:iframe]";
15
+ var _a;
16
+ const prefix = (_a = options.prefix) != null ? _a : "[iframecall:iframe]";
16
17
  const head = prefix.length > 0 ? `${prefix} ` : "";
17
18
  return (event) => {
18
19
  switch (event.type) {
@@ -38,18 +39,20 @@ function consoleDebugLogger(options = {}) {
38
39
  // src/iframe/runner.ts
39
40
  var RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set(["constructor", "host:dispose"]);
40
41
  function createIframeCallRunner(options) {
42
+ var _a, _b;
41
43
  const targetOrigin = requireTargetOrigin(options.targetOrigin);
42
- const allowedOrigins = new Set(options.allowedOrigins ?? [targetOrigin]);
43
- const transport = options.transport ?? createParentWindowTransport();
44
+ const allowedOrigins = new Set((_a = options.allowedOrigins) != null ? _a : [targetOrigin]);
45
+ const transport = (_b = options.transport) != null ? _b : createParentWindowTransport();
44
46
  let disposing = false;
45
47
  let disposed = false;
46
48
  const debugSubscribers = /* @__PURE__ */ new Set();
47
49
  function emitDebug(event) {
50
+ var _a2;
48
51
  for (const handler of debugSubscribers) {
49
52
  try {
50
53
  handler(event);
51
54
  } catch (error) {
52
- options.logger?.warn("iframecall debug subscriber threw.", error);
55
+ (_a2 = options.logger) == null ? void 0 : _a2.warn("iframecall debug subscriber threw.", error);
53
56
  }
54
57
  }
55
58
  }
@@ -83,7 +86,7 @@ function createIframeCallRunner(options) {
83
86
  return;
84
87
  }
85
88
  const parsed = parseIframeCallMessage(event.data);
86
- if (parsed?.type === "notify") {
89
+ if ((parsed == null ? void 0 : parsed.type) === "notify") {
87
90
  emitDebug({
88
91
  type: "notificationReceivedFromHost",
89
92
  event: parsed.message.event,
@@ -91,7 +94,7 @@ function createIframeCallRunner(options) {
91
94
  });
92
95
  return;
93
96
  }
94
- if (parsed?.type !== "request") {
97
+ if ((parsed == null ? void 0 : parsed.type) !== "request") {
95
98
  return;
96
99
  }
97
100
  if (parsed.message.cmd === "host:dispose") {
@@ -105,19 +108,21 @@ function createIframeCallRunner(options) {
105
108
  );
106
109
  });
107
110
  function safePost(message, transfer) {
111
+ var _a2;
108
112
  try {
109
113
  transport.post(message, targetOrigin, transfer);
110
114
  } catch (error) {
111
- options.logger?.warn("iframecall postMessage failed.", error);
115
+ (_a2 = options.logger) == null ? void 0 : _a2.warn("iframecall postMessage failed.", error);
112
116
  }
113
117
  }
114
118
  async function handleHostDispose(reason) {
119
+ var _a2, _b2;
115
120
  if (disposing || disposed) return;
116
121
  disposing = true;
117
122
  try {
118
- await options.onHostDispose?.(reason);
123
+ await ((_a2 = options.onHostDispose) == null ? void 0 : _a2.call(options, reason));
119
124
  } catch (error) {
120
- options.logger?.warn("iframecall host dispose handler failed.", error);
125
+ (_b2 = options.logger) == null ? void 0 : _b2.warn("iframecall host dispose handler failed.", error);
121
126
  } finally {
122
127
  safePost(createIframeCallNotify("terminated", { reason }));
123
128
  disposed = true;
@@ -260,6 +265,7 @@ function getDisposeReason(payload) {
260
265
  // src/iframe/useIframeCallRunner.tsx
261
266
  import { useEffect, useRef, useState } from "react";
262
267
  function useIframeCallRunner(options) {
268
+ var _a, _b;
263
269
  const optionsRef = useRef(options);
264
270
  optionsRef.current = options;
265
271
  const runnerRef = useRef(null);
@@ -293,8 +299,8 @@ function useIframeCallRunner(options) {
293
299
  }, []);
294
300
  const runner = runnerRef.current;
295
301
  return {
296
- commands: runner?.commands ?? null,
297
- iframeHelper: runner?.iframeHelper ?? null,
302
+ commands: (_a = runner == null ? void 0 : runner.commands) != null ? _a : null,
303
+ iframeHelper: (_b = runner == null ? void 0 : runner.iframeHelper) != null ? _b : null,
298
304
  isActive
299
305
  };
300
306
  }
@@ -107,7 +107,7 @@ type IframeCallControllerOptions<TCommands extends CommandMap<TCommands>> = {
107
107
  readonly readyPolicy?: ReadyPolicy;
108
108
  /** queue 정책일 때 대기열 최대 크기. 초과 호출은 queue_overflow 에러로 거부한다. */
109
109
  readonly readyQueueLimit?: number;
110
- /** call() 호출별 timeout 기본값(ms). 0 또는 Infinity면 timeout을 적용하지 않는다. */
110
+ /** invoke() 호출별 timeout 기본값(ms). 0 또는 Infinity면 timeout을 적용하지 않는다. */
111
111
  readonly defaultTimeoutMs?: number;
112
112
  /** ready 신호 대기 timeout(ms). 미지정이면 defaultTimeoutMs를 따른다. */
113
113
  readonly readyTimeoutMs?: number;
@@ -230,10 +230,12 @@ type IframeCallController<TCommands extends CommandMap<TCommands>, TNotification
230
230
  /** 종료 사유를 노출하는 promise. 정상 dispose면 null, 비정상 종료면 직렬화된 에러로 resolve된다. */
231
231
  readonly terminated: Promise<SerializedIframeCallError | null>;
232
232
  /**
233
- * iframe에 등록된 command를 호출한다.
234
- * ready 이전 호출은 readyPolicy에 따라 queue되거나 즉시 거부된다.
235
- * timeout/transfer는 호출별 options로 지정한다.
233
+ * iframe에 등록된 command를 실행한다.
234
+ * ready 이전 실행은 readyPolicy에 따라 queue되거나 즉시 거부된다.
235
+ * timeout/transfer는 실행별 options로 지정한다.
236
236
  */
237
+ invoke<K extends keyof TCommands & string>(cmd: K, args: CommandArgs<TCommands[K]>, options?: IframeCallCallOptions): Promise<CommandResult<TCommands[K]>>;
238
+ /** @deprecated `invoke()`를 사용한다. 다음 major release에서 제거될 예정이다. */
237
239
  call<K extends keyof TCommands & string>(cmd: K, args: CommandArgs<TCommands[K]>, options?: IframeCallCallOptions): Promise<CommandResult<TCommands[K]>>;
238
240
  /**
239
241
  * iframe이 host로 보낸 notification을 event 이름과 payload로 구독한다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cp949/iframecall",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "타입 안전한 host ↔ iframe postMessage 호출 라이브러리 (React 18/19 지원)",
5
5
  "keywords": [
6
6
  "iframe",
@@ -24,6 +24,10 @@
24
24
  },
25
25
  "type": "module",
26
26
  "sideEffects": false,
27
+ "browserslist": [
28
+ "chrome 75",
29
+ "firefox 67"
30
+ ],
27
31
  "files": [
28
32
  "dist",
29
33
  "README.md",
@@ -43,25 +47,29 @@
43
47
  "publishConfig": {
44
48
  "access": "public"
45
49
  },
46
- "peerDependencies": {
47
- "react": "^18.0.0 || ^19.0.0",
48
- "@types/react": "^18.0.0 || ^19.0.0"
49
- },
50
- "devDependencies": {
51
- "@testing-library/dom": "^10.4.0",
52
- "@testing-library/react": "^16.3.0",
53
- "@types/react": "19.2.2",
54
- "jsdom": "^25.0.1",
55
- "react": "^19.2.0",
56
- "typescript": "6.0.3",
57
- "tsup": "^8.0.0",
58
- "vitest": "^4.1.5",
59
- "@repo/typescript-config": "0.0.0"
60
- },
61
50
  "scripts": {
62
51
  "build": "tsup",
63
52
  "dev": "tsup --watch",
53
+ "lint": "eslint . --max-warnings 0",
64
54
  "check-types": "tsc --noEmit",
65
- "test": "vitest run tests/unit --passWithNoTests"
55
+ "test": "vitest run tests/unit --passWithNoTests",
56
+ "prepublishOnly": "pnpm run build"
57
+ },
58
+ "peerDependencies": {
59
+ "@types/react": "^18.0.0 || ^19.0.0",
60
+ "react": "^18.0.0 || ^19.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@repo/eslint-config": "workspace:*",
64
+ "@repo/typescript-config": "workspace:*",
65
+ "@testing-library/dom": "^10.4.1",
66
+ "@testing-library/react": "^16.3.2",
67
+ "@types/react": "19.2.18",
68
+ "eslint": "^9.39.5",
69
+ "jsdom": "^29.1.1",
70
+ "react": "^19.2.8",
71
+ "tsup": "^8.5.1",
72
+ "typescript": "6.0.3",
73
+ "vitest": "^4.1.11"
66
74
  }
67
- }
75
+ }