connectbase-client 6.0.0 → 6.2.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.d.mts CHANGED
@@ -325,10 +325,26 @@ declare class HttpClient {
325
325
  updateConfig(config: Partial<HttpClientConfig>): void;
326
326
  setTokens(accessToken: string, refreshToken: string): void;
327
327
  clearTokens(): void;
328
+ /**
329
+ * Access Token 만 교체한다 (refresh token 은 건드리지 않는다).
330
+ *
331
+ * 조직 컨텍스트 발급(`POST /v1/public/organizations/:orgID/context`)처럼 **새 액세스
332
+ * 토큰만 돌려주는** 엔드포인트를 위한 것이다. 그 엔드포인트는 refresh token 을 회전시키지
333
+ * 않으므로 `setTokens()` 로 빈 refresh token 을 덮어쓰면 세션 복구가 깨진다.
334
+ *
335
+ * 세션이 새로 생기는 것이 아니므로 세션 힌트/쿠키 없음 마커는 건드리지 않는다 — 그 두
336
+ * 마커는 로그인/재발급이 소유한다.
337
+ */
338
+ setAccessToken(accessToken: string): void;
328
339
  private buildSessionHintKey;
329
340
  private markSessionHint;
330
341
  private clearSessionHint;
331
342
  private hasSessionHint;
343
+ private buildNoCookieSessionKey;
344
+ private markNoCookieSession;
345
+ private clearNoCookieSessionMark;
346
+ /** 쿠키 전용 refresh 를 지금 건너뛰어야 하는지 (최근에 401/403 을 받았는지). */
347
+ private isNoCookieSessionMarkFresh;
332
348
  /**
333
349
  * OAuth redirect callback 직후 호출되어 HttpOnly cookie 를 부트스트랩한다.
334
350
  *
@@ -1566,9 +1582,33 @@ declare class AppMembersAPI {
1566
1582
  }>;
1567
1583
  }
1568
1584
 
1569
- /** 앱 멤버 회원가입 요청 */
1585
+ /**
1586
+ * 앱 멤버 회원가입 요청.
1587
+ *
1588
+ * `login_id` 와 `email` 은 **둘 다 선택**이지만 최소 하나는 있어야 한다(둘 다 비면 400).
1589
+ * 어느 쪽을 주느냐에 따라 서버가 만드는 로그인 수단(identity)이 달라진다:
1590
+ *
1591
+ * | 보낸 필드 | identity | `app_members.email` | 비밀번호 재설정 |
1592
+ * |---|---|---|---|
1593
+ * | `login_id` 만 | USERNAME | 비어 있음 | **불가** (보낼 주소가 없다) |
1594
+ * | `login_id` + `email` | USERNAME (로그인 아이디는 그대로) | 저장됨 | 가능 |
1595
+ * | `email` 만 | EMAIL (소문자 정규화된 이메일) | 저장됨 | 가능 |
1596
+ *
1597
+ * 기존 `login_id` 단독 호출은 **동작이 완전히 그대로**다. 다만 그렇게 가입한 멤버는
1598
+ * 비밀번호를 잊으면 복구할 방법이 없으므로, 신규 앱은 `email` 을 함께 받는 것을 권장한다.
1599
+ */
1570
1600
  interface MemberSignUpRequest {
1571
- login_id: string;
1601
+ /** 로그인 아이디 (3~50자). 주면 USERNAME identity 가 만들어진다. */
1602
+ login_id?: string;
1603
+ /**
1604
+ * 이메일 (최대 254자). `login_id` 없이 단독으로 주면 EMAIL identity 가 되고,
1605
+ * `login_id` 와 함께 주면 로그인 수단은 USERNAME 그대로이되 복구용 주소로만 저장된다.
1606
+ *
1607
+ * 저장만 될 뿐 **인증 메일이 자동 발송되지는 않는다** — 필요하면 가입 직후
1608
+ * `sendEmailVerification()` 을 앱이 직접 호출한다.
1609
+ */
1610
+ email?: string;
1611
+ /** 비밀번호 (6~100자). */
1572
1612
  password: string;
1573
1613
  nickname?: string;
1574
1614
  }
@@ -1576,12 +1616,23 @@ interface MemberSignUpRequest {
1576
1616
  interface MemberSignUpResponse {
1577
1617
  member_id: string;
1578
1618
  nickname: string;
1619
+ /** 가입 시 이메일을 함께 보낸 경우에만 채워진다. 없으면 서버가 필드를 생략한다. */
1620
+ email?: string;
1579
1621
  access_token: string;
1580
1622
  refresh_token: string;
1581
1623
  }
1582
- /** 앱 멤버 로그인 요청 */
1624
+ /**
1625
+ * 앱 멤버 로그인 요청.
1626
+ *
1627
+ * `login_id` 가 1순위 조회 키이고, `email` 은 EMAIL 로 가입한 멤버를 위한 별칭이다.
1628
+ * 서버는 USERNAME 을 먼저 찾고 없을 때 EMAIL 로 폴백하므로 **기존 `login_id` 로그인 경로는
1629
+ * 완전히 그대로**다. 둘 다 비면 400.
1630
+ */
1583
1631
  interface MemberSignInRequest {
1584
- login_id: string;
1632
+ /** 로그인 아이디. USERNAME identity 로 먼저 조회된다. */
1633
+ login_id?: string;
1634
+ /** 이메일. `login_id` 가 비어 있을 때만 사용된다. */
1635
+ email?: string;
1585
1636
  password: string;
1586
1637
  }
1587
1638
  /** 앱 멤버 로그인 응답 */
@@ -1631,6 +1682,112 @@ interface AuthSettingsResponse {
1631
1682
  /** 활성화된 OAuth 프로바이더 목록 (GOOGLE, KAKAO, NAVER, APPLE, GITHUB, DISCORD) */
1632
1683
  enabled_oauth_providers: string[];
1633
1684
  }
1685
+ /**
1686
+ * 메일 링크가 열릴 페이지 주소를 지정하는 공통 옵션.
1687
+ *
1688
+ * # `redirect_url` 은 앱에 등록된 도메인이어야 한다
1689
+ *
1690
+ * 서버는 오픈 리다이렉트(= 재설정 메일 안의 링크가 공격자 사이트로 가는 피싱)를 막기 위해
1691
+ * 이 값을 **앱이 소유했다고 증명된 origin 목록**과 대조한다. 목록은 다음 셋뿐이다:
1692
+ *
1693
+ * 1. 검증 완료된 커스텀 도메인 (콘솔 → 도메인, status=active)
1694
+ * 2. 웹 스토리지에 연결된 커스텀 도메인
1695
+ * 3. 웹 스토리지의 `allowed_origins`
1696
+ *
1697
+ * 와일드카드(`*`)는 **인정되지 않는다**. `localhost` 는 앱 설정에서 로컬 개발 origin 을
1698
+ * opt-in(`allow_local_dev_origins`) 했을 때만 통과한다.
1699
+ *
1700
+ * 대조에 실패해도 에러가 나지 않는다 — 조용히 무시되고 플랫폼 기본 페이지로 링크가
1701
+ * 만들어진다. 즉 **도메인을 먼저 등록하지 않으면 앱의 자체 재설정 페이지로 갈 수 없다.**
1702
+ * 링크가 엉뚱한 곳으로 간다면 도메인 등록 상태부터 확인할 것.
1703
+ */
1704
+ interface MemberRedirectOption {
1705
+ /**
1706
+ * 토큰을 받을 앱 자체 페이지 주소. 최종 링크는 `<redirect_url>?token=<토큰>` 이 된다.
1707
+ * 생략하거나 등록되지 않은 origin 이면 플랫폼 기본 페이지로 폴백한다.
1708
+ */
1709
+ redirect_url?: string;
1710
+ }
1711
+ /** 비밀번호 재설정 메일 발송 요청. */
1712
+ interface MemberPasswordResetRequest extends MemberRedirectOption {
1713
+ /** 가입 시 저장된 이메일. */
1714
+ email: string;
1715
+ }
1716
+ /** 비밀번호 재설정 확정 요청. */
1717
+ interface MemberPasswordResetConfirmRequest {
1718
+ /** 메일 링크의 `token` 쿼리 값. 1시간 만료이고 1회만 쓸 수 있다. */
1719
+ token: string;
1720
+ /** 새 비밀번호 (6~100자). */
1721
+ new_password: string;
1722
+ }
1723
+ /** 로그인 상태에서의 비밀번호 변경 요청. */
1724
+ interface MemberChangePasswordRequest {
1725
+ current_password: string;
1726
+ /** 새 비밀번호 (6~100자). */
1727
+ new_password: string;
1728
+ }
1729
+ /** 이메일 인증 메일 발송 요청. 본문은 `redirect_url` 뿐이라 전부 선택이다. */
1730
+ type MemberEmailVerificationRequest = MemberRedirectOption;
1731
+ /** 이메일 인증 확정 요청. */
1732
+ interface MemberEmailVerificationConfirmRequest {
1733
+ /** 메일 링크의 `token` 쿼리 값. 24시간 만료이고 1회만 쓸 수 있다. */
1734
+ token: string;
1735
+ }
1736
+ /** 이메일 인증 확정 응답. */
1737
+ interface MemberEmailVerificationConfirmResponse {
1738
+ member_id: string;
1739
+ email: string;
1740
+ is_email_verified: boolean;
1741
+ }
1742
+ /** 자격증명 관련 엔드포인트가 공통으로 돌려주는 안내 메시지 응답. */
1743
+ interface MemberCredentialMessageResponse {
1744
+ message: string;
1745
+ }
1746
+ /** 활성 세션 하나 — 「한 기기에서의 한 번의 로그인」과 1:1 이다. */
1747
+ interface MemberSession {
1748
+ /**
1749
+ * 세션 식별자. 개별 로그아웃 시 이 값을 그대로 돌려보낸다.
1750
+ *
1751
+ * 서버에서는 refresh token 의 rotation family id 이며, 그 자체로는 인증에 쓸 수 없다 —
1752
+ * 유출돼도 세션을 탈취할 수 없고, 폐기 API 는 호출자 본인 스코프가 질의에 박혀 있어
1753
+ * 남의 값을 넣으면 404 가 된다.
1754
+ */
1755
+ session_id: string;
1756
+ /**
1757
+ * 지금 이 요청을 보낸 세션인지 여부. **화면에서 반드시 구분해 표시하라** — 그러지 않으면
1758
+ * 사용자가 자기가 쓰고 있는 기기를 끊고 영문도 모른 채 로그아웃된다.
1759
+ *
1760
+ * refresh 쿠키가 없는 환경(예: `persistence: 'none'`, 쿠키 차단)에서는 서버가 현재 세션을
1761
+ * 특정할 수 없어 **어느 세션에도 붙지 않는다.** 없을 수 있는 값으로 다뤄야 한다.
1762
+ */
1763
+ current?: boolean;
1764
+ /** User-Agent 에서 뽑은 표시용 요약. 판별 불가 시 빈 문자열. */
1765
+ browser: string;
1766
+ os: string;
1767
+ /** 원문 User-Agent — 요약이 애매할 때 사용자가 직접 확인할 수 있도록 함께 내려온다. */
1768
+ user_agent: string;
1769
+ /** GeoIP 라벨(예: "Seoul, South Korea"). 서버에 GeoIP DB 가 없으면 빈 문자열. */
1770
+ location: string;
1771
+ /** 네트워크 접두부만 남긴 IP (예: "203.0.113.x"). 평문 IP 는 저장도 전달도 하지 않는다. */
1772
+ ip_masked: string;
1773
+ /** 이 세션의 로그인 시각 (ISO 8601). */
1774
+ started_at: string;
1775
+ /** 마지막 토큰 회전 시각 = 마지막 사용 시각. */
1776
+ last_used_at: string;
1777
+ /** 이대로 두면 세션이 끊기는 시각 (sliding 만료). */
1778
+ expires_at: string;
1779
+ }
1780
+ /** `cb.auth.listSessions()` 반환값. 현재 세션이 맨 앞, 나머지는 최근 사용 순이다. */
1781
+ interface MemberSessionList {
1782
+ member_id: string;
1783
+ sessions: MemberSession[];
1784
+ }
1785
+ /** 세션 폐기 API 의 반환값. `revoked` 는 토큰 수가 아니라 **기기(세션) 수** 다. */
1786
+ interface MemberSessionRevokeResponse {
1787
+ member_id: string;
1788
+ revoked: number;
1789
+ message: string;
1790
+ }
1634
1791
 
1635
1792
  declare class AuthAPI {
1636
1793
  private http;
@@ -1658,34 +1815,176 @@ declare class AuthAPI {
1658
1815
  */
1659
1816
  getAuthSettings(): Promise<AuthSettingsResponse>;
1660
1817
  /**
1661
- * 앱 멤버 회원가입 (아이디/비밀번호 기반)
1818
+ * 앱 멤버 회원가입 (아이디/이메일 + 비밀번호 기반)
1662
1819
  * 앱에 새로운 멤버를 등록합니다.
1663
1820
  *
1821
+ * `login_id` 와 `email` 중 **최소 하나**는 있어야 합니다. 어느 쪽을 주느냐에 따라
1822
+ * 만들어지는 로그인 수단이 달라집니다 — 표는 {@link MemberSignUpRequest} 참고.
1823
+ *
1824
+ * `email` 을 함께 저장해야 나중에 {@link AuthAPI.requestPasswordReset} 로 비밀번호를
1825
+ * 복구할 수 있습니다. `login_id` 만으로 가입한 멤버는 복구 수단이 없습니다.
1826
+ *
1827
+ * 이메일을 넘겨도 **인증 메일이 자동 발송되지는 않습니다.** 발신 주소가 플랫폼 단일
1828
+ * 주소라 앱이 요청하지 않은 메일을 보내지 않는다는 정책입니다 — 필요하면 가입 직후
1829
+ * {@link AuthAPI.sendEmailVerification} 을 직접 호출하세요.
1830
+ *
1664
1831
  * @example
1665
1832
  * ```typescript
1833
+ * // 기존 방식 — 아이디만 (동작 그대로, 단 비밀번호 복구 불가)
1666
1834
  * const result = await client.auth.signUpMember({
1667
1835
  * login_id: 'myuser123',
1668
1836
  * password: 'password123',
1669
1837
  * nickname: 'John'
1670
1838
  * })
1671
- * console.log('가입 완료:', result.member_id)
1839
+ *
1840
+ * // 아이디 + 복구용 이메일 (로그인은 여전히 login_id 로)
1841
+ * await client.auth.signUpMember({
1842
+ * login_id: 'myuser123',
1843
+ * email: 'john@example.com',
1844
+ * password: 'password123'
1845
+ * })
1846
+ *
1847
+ * // 이메일만 — 이메일이 곧 로그인 아이디
1848
+ * await client.auth.signUpMember({
1849
+ * email: 'john@example.com',
1850
+ * password: 'password123'
1851
+ * })
1672
1852
  * ```
1673
1853
  */
1674
1854
  signUpMember(data: MemberSignUpRequest): Promise<MemberSignUpResponse>;
1675
1855
  /**
1676
- * 앱 멤버 로그인 (아이디/비밀번호 기반)
1856
+ * 앱 멤버 로그인 (아이디 또는 이메일 + 비밀번호)
1677
1857
  * 기존 멤버로 로그인합니다.
1678
1858
  *
1859
+ * `login_id` 가 1순위 조회 키이고, `email` 은 이메일로 가입한 멤버를 위한 별칭입니다.
1860
+ * 서버는 USERNAME 을 먼저 찾고 없을 때만 EMAIL 로 폴백하므로 **기존 `login_id` 호출은
1861
+ * 동작이 그대로**입니다. 둘 다 비어 있으면 400 입니다.
1862
+ *
1679
1863
  * @example
1680
1864
  * ```typescript
1865
+ * // 아이디로 로그인 (기존 방식)
1681
1866
  * const result = await client.auth.signInMember({
1682
1867
  * login_id: 'myuser123',
1683
1868
  * password: 'password123'
1684
1869
  * })
1685
- * console.log('로그인 성공:', result.member_id)
1870
+ *
1871
+ * // 이메일로 로그인
1872
+ * await client.auth.signInMember({
1873
+ * email: 'john@example.com',
1874
+ * password: 'password123'
1875
+ * })
1686
1876
  * ```
1687
1877
  */
1688
1878
  signInMember(data: MemberSignInRequest): Promise<MemberSignInResponse>;
1879
+ /**
1880
+ * 비밀번호 재설정 메일을 보냅니다. (로그인 불필요)
1881
+ *
1882
+ * # 계정 열거 방지 — 응답으로는 아무것도 알 수 없다
1883
+ *
1884
+ * 가입되지 않은 이메일이든, 소셜 전용 계정(비밀번호 없음)이든, 정지된 멤버든
1885
+ * **똑같은 200 과 똑같은 문구**가 돌아옵니다. 메일이 실제로 나갔는지 여부는 응답으로
1886
+ * 판별할 수 없습니다 — "가입 여부 확인" 용도로 쓸 수 없다는 뜻이며, 의도된 설계입니다.
1887
+ * UI 에서도 "가입된 계정이라면 메일을 보냈습니다" 처럼 안내하세요.
1888
+ *
1889
+ * 메일 링크의 토큰은 **1시간** 만료이고 **1회만** 쓸 수 있습니다.
1890
+ *
1891
+ * @example
1892
+ * ```typescript
1893
+ * // 앱이 자체 재설정 페이지를 운영하는 경우 (도메인 등록 선행 필수)
1894
+ * await client.auth.requestPasswordReset({
1895
+ * email: 'john@example.com',
1896
+ * redirect_url: 'https://myapp.com/reset-password'
1897
+ * })
1898
+ * toast('가입된 계정이라면 재설정 링크를 보냈습니다')
1899
+ * ```
1900
+ */
1901
+ requestPasswordReset(data: MemberPasswordResetRequest): Promise<MemberCredentialMessageResponse>;
1902
+ /**
1903
+ * 재설정 토큰으로 새 비밀번호를 확정합니다. (로그인 불필요)
1904
+ *
1905
+ * `redirect_url` 페이지의 `?token=` 쿼리 값을 그대로 넘기면 됩니다.
1906
+ *
1907
+ * 성공하면 **그 멤버의 모든 세션이 서버에서 폐기**됩니다. 다른 기기 로그인까지 전부
1908
+ * 끊기며, 이 SDK 인스턴스에 남아 있던 토큰도 함께 정리하므로 새 비밀번호로 다시
1909
+ * 로그인해야 합니다.
1910
+ *
1911
+ * 토큰이 만료(1시간)됐거나 이미 사용됐거나 위조/타앱 토큰이면 400 이 납니다. 사유는
1912
+ * 구분되지 않습니다 — 토큰을 탐색하는 단서를 주지 않기 위해서입니다.
1913
+ *
1914
+ * @example
1915
+ * ```typescript
1916
+ * const token = new URLSearchParams(location.search).get('token')
1917
+ * if (!token) return
1918
+ * try {
1919
+ * await client.auth.confirmPasswordReset({ token, new_password: 'newpass123' })
1920
+ * location.href = '/login'
1921
+ * } catch (e) {
1922
+ * // 만료, 이미 사용됨, 잘못된 링크가 모두 여기로 온다
1923
+ * alert('링크가 만료되었거나 이미 사용되었습니다. 재설정을 다시 요청해 주세요.')
1924
+ * }
1925
+ * ```
1926
+ */
1927
+ confirmPasswordReset(data: MemberPasswordResetConfirmRequest): Promise<MemberCredentialMessageResponse>;
1928
+ /**
1929
+ * 로그인한 멤버가 현재 비밀번호를 확인하고 새 비밀번호로 바꿉니다. (AppMember 토큰 필요)
1930
+ *
1931
+ * `confirmPasswordReset` 과 마찬가지로 성공 시 **전 세션이 폐기**되고 로컬 토큰도
1932
+ * 정리됩니다. 호출한 클라이언트도 재로그인이 필요합니다.
1933
+ *
1934
+ * @example
1935
+ * ```typescript
1936
+ * await client.auth.changePassword({
1937
+ * current_password: 'oldpass123',
1938
+ * new_password: 'newpass123'
1939
+ * })
1940
+ * // 세션이 끊겼으므로 로그인 화면으로
1941
+ * location.href = '/login'
1942
+ * ```
1943
+ */
1944
+ changePassword(data: MemberChangePasswordRequest): Promise<MemberCredentialMessageResponse>;
1945
+ /**
1946
+ * 로그인한 멤버에게 이메일 인증 메일을 보냅니다. (AppMember 토큰 필요)
1947
+ *
1948
+ * 가입 시 자동 발송되지 않으므로, 인증이 필요한 앱은 이 메서드를 **직접** 불러야 합니다
1949
+ * (예: 가입 직후, 또는 설정 화면의 "인증 메일 다시 보내기" 버튼).
1950
+ *
1951
+ * 메일 링크의 토큰은 **24시간** 만료이고 **1회만** 쓸 수 있습니다.
1952
+ *
1953
+ * 이메일 인증은 **로그인의 전제 조건이 아닙니다** — 인증하지 않아도 로그인은 됩니다.
1954
+ * 인증 여부로 기능을 제한할지는 앱이 `getMe()` 등으로 확인해 스스로 정합니다.
1955
+ *
1956
+ * @example
1957
+ * ```typescript
1958
+ * await client.auth.sendEmailVerification({
1959
+ * redirect_url: 'https://myapp.com/verify-email'
1960
+ * })
1961
+ *
1962
+ * // redirect_url 없이 — 플랫폼 기본 페이지 링크로 발송된다
1963
+ * await client.auth.sendEmailVerification()
1964
+ * ```
1965
+ */
1966
+ sendEmailVerification(data?: MemberEmailVerificationRequest): Promise<MemberCredentialMessageResponse>;
1967
+ /**
1968
+ * 인증 토큰으로 이메일 인증을 확정합니다. (로그인 불필요)
1969
+ *
1970
+ * 메일 링크가 연 페이지에서 `?token=` 을 꺼내 그대로 넘깁니다. 링크를 누른 사람이 그
1971
+ * 앱에 로그인해 있지 않아도(다른 기기의 메일 앱에서 열어도) 동작합니다.
1972
+ *
1973
+ * 만료(24시간), 이미 사용됨, 위조 토큰은 모두 400 이며 사유가 구분되지 않습니다.
1974
+ *
1975
+ * @example
1976
+ * ```typescript
1977
+ * const token = new URLSearchParams(location.search).get('token')
1978
+ * if (!token) return
1979
+ * try {
1980
+ * const r = await client.auth.confirmEmailVerification({ token })
1981
+ * console.log(r.email, r.is_email_verified) // true
1982
+ * } catch {
1983
+ * alert('인증 링크가 만료되었거나 이미 사용되었습니다.')
1984
+ * }
1985
+ * ```
1986
+ */
1987
+ confirmEmailVerification(data: MemberEmailVerificationConfirmRequest): Promise<MemberEmailVerificationConfirmResponse>;
1689
1988
  /**
1690
1989
  * 현재 로그인한 멤버 정보 조회
1691
1990
  * custom_data를 포함한 멤버 정보를 반환합니다.
@@ -1812,6 +2111,66 @@ declare class AuthAPI {
1812
2111
  * 로그아웃
1813
2112
  */
1814
2113
  signOut(): Promise<void>;
2114
+ /**
2115
+ * 로그인된 기기(활성 세션) 목록을 조회합니다.
2116
+ *
2117
+ * 현재 세션이 맨 앞에 오고, 나머지는 최근 사용 순입니다. 응답에는 토큰도 평문 IP 도
2118
+ * 담기지 않습니다.
2119
+ *
2120
+ * **`current` 를 화면에 반드시 표시하세요.** 구분이 없으면 사용자가 자기가 쓰고 있는
2121
+ * 기기를 끊고 영문도 모른 채 로그아웃됩니다. refresh 쿠키가 없는 환경에서는 서버가
2122
+ * 현재 세션을 특정하지 못해 `current` 가 어느 항목에도 붙지 않을 수 있습니다.
2123
+ *
2124
+ * @example
2125
+ * ```typescript
2126
+ * const { sessions } = await cb.auth.listSessions()
2127
+ * for (const s of sessions) {
2128
+ * console.log(s.browser, s.os, s.location, s.current ? '(이 기기)' : '')
2129
+ * }
2130
+ * ```
2131
+ */
2132
+ listSessions(): Promise<MemberSessionList>;
2133
+ /**
2134
+ * 기기 하나를 로그아웃시킵니다. `sessionId` 는 `listSessions()` 가 돌려준 `session_id`.
2135
+ *
2136
+ * 남의 세션 식별자를 넣으면 404 입니다 — 존재 여부조차 구분해 알려주지 않습니다.
2137
+ * 이미 끝난 세션도 같은 404 입니다.
2138
+ *
2139
+ * **현재 세션을 지목하면 그 자리에서 로그아웃됩니다.** 서버가 refresh 쿠키를 함께
2140
+ * 만료시키므로 로컬 토큰도 정리합니다 — 이 경우 앱은 로그인 화면으로 보내야 합니다.
2141
+ * 그렇지 않은 기기를 끊는 것은 현재 세션에 영향을 주지 않습니다.
2142
+ *
2143
+ * @example
2144
+ * ```typescript
2145
+ * const target = sessions.find((s) => !s.current)
2146
+ * if (target) await cb.auth.revokeSession(target.session_id)
2147
+ * ```
2148
+ */
2149
+ revokeSession(sessionId: string): Promise<MemberSessionRevokeResponse>;
2150
+ /**
2151
+ * **현재 기기만 남기고** 나머지 기기에서 모두 로그아웃합니다.
2152
+ *
2153
+ * 자기 세션까지 포함한 전체 로그아웃은 `signOut()` 입니다 — 자격증명 유출 대응처럼
2154
+ * 모든 기기를 내보내야 하는 상황에서는 그쪽을 쓰세요.
2155
+ *
2156
+ * refresh 쿠키가 없어 서버가 현재 세션을 특정하지 못하면 이 호출은 **실패합니다**
2157
+ * (400). 기준점 없이 진행하면 사용자가 의도하지 않은 자기 세션 종료가 함께 일어나기
2158
+ * 때문입니다. 그 경우 `signOut()` 후 재로그인을 안내하세요.
2159
+ *
2160
+ * 반환되는 `revoked` 는 토큰 수가 아니라 **끊긴 기기 수** 입니다.
2161
+ *
2162
+ * @example
2163
+ * ```typescript
2164
+ * const { revoked } = await cb.auth.revokeOtherSessions()
2165
+ * toast(`${revoked}개 기기에서 로그아웃했습니다`)
2166
+ * ```
2167
+ */
2168
+ revokeOtherSessions(): Promise<MemberSessionRevokeResponse>;
2169
+ /**
2170
+ * 폐기 대상이 현재 세션인지 미리 확인한다. 목록 조회가 실패하면 false 로 떨어져
2171
+ * 폐기 자체는 진행한다 — 판정 실패로 사용자의 로그아웃 요청을 막을 이유가 없다.
2172
+ */
2173
+ private isCurrentSession;
1815
2174
  }
1816
2175
 
1817
2176
  /**
@@ -4827,10 +5186,17 @@ type KnowledgeFileInput = File | Blob | {
4827
5186
  *
4828
5187
  * ## 사용자별 격리 (다중 사용자 RAG 시나리오)
4829
5188
  *
4830
- * `Authorization: Bearer <appmember-jwt>` 함께 보내면 서버가:
4831
- * - 검색 결과를 본인 문서 (metadata.user_id == member_id) 로 제한
4832
- * - addDocument metadata.user_id 자동 태깅
4833
- * - listDocuments / deleteDocument 본인 자료만 노출
5189
+ * 접근 범위는 축으로 판정됩니다 (2026-09-06 fail-closed 전환).
5190
+ *
5191
+ * 1. **익명 (퍼블릭 키만)** — `metadata.user_id` **없는** 문서만 조회/검색/수정/삭제.
5192
+ * FAQ 제품 문서 같은 공유 코퍼스는 종전과 동일하게 동작합니다.
5193
+ * 2. **회원 (`Authorization: Bearer <appmember-jwt>` 동봉)** — 본인 문서만. addDocument 시
5194
+ * `metadata.user_id` 가 자동 태깅되고, 클라이언트가 다른 값을 넣어도 서버가 덮어씁니다.
5195
+ * 3. **관리자 (`secretKey: 'cb_sk_*'`)** — 전체 접근. 서버사이드 동기화/대리 색인용.
5196
+ *
5197
+ * 퍼블릭 키만으로 `metadata.user_id` 를 직접 지정하면 403 `MEMBER_SCOPED_METADATA_DENIED`,
5198
+ * 회원 귀속 문서를 건드리면 403 `CROSS_USER_ACCESS_DENIED` 입니다. 퍼블릭 키는 브라우저 번들에
5199
+ * 실려 배포되는 공개 식별자라, 그것만으로 남의 개인 문서를 읽거나 주입할 수 없어야 합니다.
4834
5200
  *
4835
5201
  * @example
4836
5202
  * ```typescript
@@ -4854,6 +5220,15 @@ type KnowledgeFileInput = File | Blob | {
4854
5220
  * query: '내 메모',
4855
5221
  * where: { 'metadata.tag': 'work' },
4856
5222
  * })
5223
+ *
5224
+ * // 서버사이드 대리 색인 — 특정 회원 앞으로 문서를 넣으려면 secretKey 가 필요하다
5225
+ * const admin = new ConnectBase({ publicKey: 'your-public-key', secretKey: 'cb_sk_...' })
5226
+ * await admin.knowledge.addDocument('kb-id', {
5227
+ * name: '내 노트',
5228
+ * source_type: 'text',
5229
+ * content: '...',
5230
+ * metadata: { user_id: memberId },
5231
+ * })
4857
5232
  * ```
4858
5233
  */
4859
5234
  declare class KnowledgeAPI {
@@ -5972,6 +6347,299 @@ declare class OAuthAPI {
5972
6347
  } | null>;
5973
6348
  }
5974
6349
 
6350
+ /**
6351
+ * 조직 역할.
6352
+ *
6353
+ * 백엔드 `organization_service.AllRoles` 와 동일합니다. 스키마상 enum 이 아니라 문자열
6354
+ * 컬럼이므로 문자열 확장을 허용합니다.
6355
+ */
6356
+ type OrganizationRole = "owner" | "admin" | "member" | (string & {});
6357
+ /** 조직 하나. */
6358
+ interface Organization {
6359
+ /** 조직 ID (UUID) */
6360
+ id: string;
6361
+ /** 조직 이름 */
6362
+ name: string;
6363
+ /** 조직 식별자. **생성 후 변경할 수 없습니다** */
6364
+ slug: string;
6365
+ /** 조직 설명 */
6366
+ description?: string;
6367
+ /** 앱이 자유롭게 쓰는 메타데이터 */
6368
+ metadata?: Record<string, unknown>;
6369
+ /** 활성 여부. 비활성 조직에 대한 작업은 403 입니다 */
6370
+ is_active: boolean;
6371
+ created_at: string;
6372
+ updated_at: string;
6373
+ /**
6374
+ * **요청한 사람의** 이 조직 내 역할. 목록 화면이 조직마다 다시 물어보지 않아도 되도록
6375
+ * 함께 실립니다. 수정(`update`) 응답에는 실리지 않습니다.
6376
+ */
6377
+ my_role?: OrganizationRole;
6378
+ /**
6379
+ * 조직 멤버 수. **상세 조회(`get`)에서만** 채워집니다 — 목록에서 조직마다 COUNT 를
6380
+ * 돌면 N+1 이라 생략됩니다.
6381
+ */
6382
+ member_count?: number;
6383
+ }
6384
+ /** 조직 멤버 하나. */
6385
+ interface OrganizationMember {
6386
+ /** 앱 멤버 ID (UUID) */
6387
+ member_id: string;
6388
+ /** 조직 내 역할 */
6389
+ role: OrganizationRole;
6390
+ joined_at: string;
6391
+ /** 이 멤버를 초대한 사람의 앱 멤버 ID */
6392
+ invited_by_member_id?: string;
6393
+ }
6394
+ /**
6395
+ * 멤버 목록 응답.
6396
+ *
6397
+ * 닉네임/이메일 같은 **프로필은 실리지 않습니다** — 조직에 초대되기만 하면 다른 구성원의
6398
+ * 이메일을 전부 수집할 수 있게 되기 때문입니다. 프로필이 필요하면 `cb.appMembers.*` 를
6399
+ * 쓰고, 노출 범위는 그쪽 정책이 소유합니다.
6400
+ */
6401
+ interface OrganizationMemberList {
6402
+ total: number;
6403
+ members: OrganizationMember[];
6404
+ }
6405
+ /** 조직 초대 하나. **평문 토큰은 여기 없습니다** */
6406
+ interface OrganizationInvitation {
6407
+ id: string;
6408
+ email: string;
6409
+ role: OrganizationRole;
6410
+ status: string;
6411
+ expires_at: string;
6412
+ created_at: string;
6413
+ }
6414
+ interface CreateOrganizationRequest {
6415
+ name: string;
6416
+ /** 생략하면 `name` 에서 파생하고, 충돌하면 짧은 접미사를 붙입니다 */
6417
+ slug?: string;
6418
+ description?: string;
6419
+ metadata?: Record<string, unknown>;
6420
+ }
6421
+ /**
6422
+ * 조직 수정 요청.
6423
+ *
6424
+ * `slug` 는 없습니다 — 변경을 허용하면 조직 URL 과 외부 참조가 조용히 깨지고, 놓아준 slug 를
6425
+ * 다른 조직이 선점해 **다른 조직의 페이지가 열리는** 상황이 생깁니다.
6426
+ */
6427
+ interface UpdateOrganizationRequest {
6428
+ name?: string;
6429
+ description?: string;
6430
+ metadata?: Record<string, unknown>;
6431
+ }
6432
+ interface CreateInvitationRequest {
6433
+ email: string;
6434
+ /** 비우면 `member` */
6435
+ role?: OrganizationRole;
6436
+ }
6437
+ /**
6438
+ * 초대 생성 결과.
6439
+ *
6440
+ * `token` 은 평문 초대 토큰이며 **이 응답에서만** 볼 수 있습니다 (DB 에는 SHA-256 만 남습니다).
6441
+ * **초대 메일 발송은 플랫폼이 대신하지 않습니다** — 앱마다 문안과 발송 채널이 다르므로
6442
+ * 토큰을 받아 전달하는 것은 앱의 몫입니다.
6443
+ */
6444
+ interface CreateInvitationResponse {
6445
+ invitation: OrganizationInvitation;
6446
+ token: string;
6447
+ }
6448
+ /** 초대 수락 결과. */
6449
+ interface AcceptInvitationResponse {
6450
+ organization: Organization;
6451
+ role: OrganizationRole;
6452
+ }
6453
+ /**
6454
+ * 조직 컨텍스트 발급(= 조직 전환) 결과.
6455
+ *
6456
+ * `access_token` 은 조직 컨텍스트가 실린 **새 액세스 토큰**입니다.
6457
+ * `cb.organizations.switchTo()` 를 쓰면 SDK 가 이 토큰을 자동으로 적용합니다.
6458
+ *
6459
+ * **refresh token 은 바뀌지 않습니다.** 토큰 회전이 일어나면 조직 컨텍스트는 의도적으로
6460
+ * 사라지므로 전환을 다시 호출해야 합니다.
6461
+ */
6462
+ interface OrganizationContext {
6463
+ access_token: string;
6464
+ /** 액세스 토큰 자체의 남은 수명(초) */
6465
+ expires_in: number;
6466
+ /**
6467
+ * **조직 컨텍스트**의 남은 수명(초). 항상 `expires_in` 이하이며 기본 600초(10분)입니다.
6468
+ *
6469
+ * 이 시간이 지나면 토큰은 여전히 유효하지만 RLS 의 `auth.org_id` 는 사라집니다 —
6470
+ * 조직 규칙이 fail-closed 로 거부되어 "갑자기 전부 막힘" 으로 보입니다.
6471
+ * 이 값을 보고 만료 전에 `switchTo()` 를 다시 호출하세요.
6472
+ */
6473
+ org_context_expires_in: number;
6474
+ organization: Organization;
6475
+ role: OrganizationRole;
6476
+ }
6477
+
6478
+ /**
6479
+ * 조직(워크스페이스) API — `cb.organizations.*`
6480
+ *
6481
+ * 앱의 **엔드유저가 만드는 조직/팀**을 다룹니다. Connect Base 콘솔의 협업자 RBAC
6482
+ * (`cb.roles.*`)와는 완전히 별개 시스템입니다.
6483
+ *
6484
+ * ## 로그인이 반드시 필요합니다
6485
+ *
6486
+ * 모든 조직 API 는 **로그인한 회원의 액세스 토큰**을 요구합니다. 퍼블릭 키(`X-Public-Key`)만
6487
+ * 으로는 아무것도 할 수 없습니다 — 퍼블릭 키는 설계상 클라이언트 번들에 노출되는 값이라,
6488
+ * 그것만으로 목록을 열면 앱의 조직 구조가 통째로 덤프되기 때문입니다.
6489
+ *
6490
+ * ## 조직 컨텍스트는 10분마다 갱신해야 합니다 (가장 흔한 함정)
6491
+ *
6492
+ * 데이터베이스 보안 규칙(RLS)의 `auth.org_id` / `auth.org_role` 은 **토큰에 실린 조직
6493
+ * 컨텍스트**에서 옵니다. 이 컨텍스트의 수명은 액세스 토큰(1시간)과 **독립적인 10분**입니다.
6494
+ * 만료되어도 토큰 자체는 유효하므로 401 이 나지 않고, 대신 조직 규칙이 fail-closed 로
6495
+ * 거부되어 **"멀쩡하던 조회가 갑자기 전부 막히는"** 증상으로만 드러납니다.
6496
+ *
6497
+ * `switchTo()` 가 돌려주는 `org_context_expires_in`(초) 보다 먼저 다시 호출하세요.
6498
+ *
6499
+ * @example
6500
+ * ```typescript
6501
+ * // 조직 전환 + 만료 전 자동 갱신
6502
+ * let timer: ReturnType<typeof setTimeout> | undefined
6503
+ *
6504
+ * async function activate(orgId: string) {
6505
+ * const ctx = await cb.organizations.switchTo(orgId) // 액세스 토큰이 자동 적용된다
6506
+ * clearTimeout(timer)
6507
+ * // 만료 60초 전에 갱신 (최소 30초 간격)
6508
+ * const delayMs = Math.max(ctx.org_context_expires_in - 60, 30) * 1000
6509
+ * timer = setTimeout(() => activate(orgId), delayMs)
6510
+ * return ctx
6511
+ * }
6512
+ * ```
6513
+ *
6514
+ * 토큰 회전(refresh)이 일어나면 조직 컨텍스트는 **의도적으로 사라집니다** — 갱신 후에는
6515
+ * `switchTo()` 를 다시 호출해야 합니다.
6516
+ */
6517
+ declare class OrganizationsAPI {
6518
+ private http;
6519
+ constructor(http: HttpClient);
6520
+ /** 조직 라우트는 `/v1/public` 아래에만 있습니다. */
6521
+ private readonly prefix;
6522
+ /**
6523
+ * 조직을 만듭니다. **만든 사람이 자동으로 `owner`** 가 됩니다.
6524
+ *
6525
+ * @example
6526
+ * ```typescript
6527
+ * const org = await cb.organizations.create({ name: '우리 팀' })
6528
+ * // slug 를 생략하면 name 에서 파생되고, 충돌하면 짧은 접미사가 붙는다
6529
+ * ```
6530
+ */
6531
+ create(data: CreateOrganizationRequest): Promise<Organization>;
6532
+ /**
6533
+ * 내가 속한 조직 목록. 조직 전환 UI 를 그릴 때 씁니다.
6534
+ *
6535
+ * 각 항목의 `my_role` 이 채워집니다. `member_count` 는 목록에서는 실리지 않습니다
6536
+ * (조직마다 COUNT 를 돌면 N+1 이라 상세 조회에서만 채워집니다).
6537
+ */
6538
+ listMine(): Promise<Organization[]>;
6539
+ /**
6540
+ * 조직 상세. `my_role` 과 `member_count` 가 함께 실립니다.
6541
+ *
6542
+ * 내가 속하지 않은 조직은 **403 이 아니라 404** 입니다 — 403 을 주면 그것만으로 그 조직이
6543
+ * 실재한다는 사실이 새어 조직 ID 열거가 가능해지기 때문입니다.
6544
+ */
6545
+ get(organizationId: string): Promise<Organization>;
6546
+ /**
6547
+ * 조직 정보 수정.
6548
+ *
6549
+ * **`slug` 는 바꿀 수 없습니다** — 변경을 허용하면 조직 URL 과 외부 참조가 조용히 깨지고,
6550
+ * 놓아준 slug 를 다른 조직이 선점해 다른 조직의 페이지가 열립니다.
6551
+ */
6552
+ update(organizationId: string, data: UpdateOrganizationRequest): Promise<Organization>;
6553
+ /** 조직 삭제. */
6554
+ delete(organizationId: string): Promise<{
6555
+ success: boolean;
6556
+ }>;
6557
+ /**
6558
+ * 조직 멤버 목록.
6559
+ *
6560
+ * 닉네임/이메일 같은 **프로필은 실리지 않습니다** — 조직에 초대되기만 하면 다른 구성원의
6561
+ * 이메일을 전부 수집할 수 있게 되기 때문입니다. 프로필이 필요하면 `member_id` 로
6562
+ * `cb.appMembers.*` 를 조회하세요.
6563
+ */
6564
+ listMembers(organizationId: string, params?: {
6565
+ limit?: number;
6566
+ offset?: number;
6567
+ }): Promise<OrganizationMemberList>;
6568
+ /**
6569
+ * 멤버의 조직 역할 변경.
6570
+ *
6571
+ * 소유자는 별도 컬럼이 아니라 `role === 'owner'` 인 행에서 파생되므로, 마지막 owner 의
6572
+ * 역할을 내리는 것은 거부됩니다 (403).
6573
+ */
6574
+ updateMemberRole(organizationId: string, memberId: string, role: OrganizationRole): Promise<OrganizationMember>;
6575
+ /**
6576
+ * 멤버 제거. **본인 ID 를 넘기면 조직 탈퇴**입니다.
6577
+ *
6578
+ * 마지막 owner 는 제거할 수 없습니다 (403) — 소유자 없는 조직은 아무도 삭제할 수 없게
6579
+ * 되기 때문입니다.
6580
+ */
6581
+ removeMember(organizationId: string, memberId: string): Promise<{
6582
+ success: boolean;
6583
+ }>;
6584
+ /**
6585
+ * 초대 생성.
6586
+ *
6587
+ * **평문 토큰은 이 응답에서만 볼 수 있습니다** (DB 에는 SHA-256 만 남습니다).
6588
+ * **초대 메일 발송은 플랫폼이 대신하지 않습니다** — 앱마다 문안과 발송 채널이 다르므로,
6589
+ * 받은 토큰을 초대 대상에게 전달하는 것은 앱의 몫입니다.
6590
+ *
6591
+ * @example
6592
+ * ```typescript
6593
+ * const { invitation, token } = await cb.organizations.createInvitation(orgId, {
6594
+ * email: 'teammate@example.com',
6595
+ * role: 'member',
6596
+ * })
6597
+ * // token 을 담은 초대 링크를 앱이 직접 발송한다
6598
+ * await sendMyInviteMail(invitation.email, `https://myapp.com/join?token=${token}`)
6599
+ * ```
6600
+ */
6601
+ createInvitation(organizationId: string, data: CreateInvitationRequest): Promise<CreateInvitationResponse>;
6602
+ /** 조직의 초대 목록. 평문 토큰은 실리지 않습니다. */
6603
+ listInvitations(organizationId: string): Promise<OrganizationInvitation[]>;
6604
+ /** 초대 취소. */
6605
+ revokeInvitation(organizationId: string, invitationId: string): Promise<{
6606
+ success: boolean;
6607
+ }>;
6608
+ /**
6609
+ * 초대 토큰으로 조직에 합류합니다. 조직 ID 를 몰라도 됩니다.
6610
+ *
6611
+ * 수락자의 이메일은 **로그인한 회원 토큰의 클레임**에서 가져오므로, 초대 대상과 다른
6612
+ * 계정으로 로그인한 상태면 거부됩니다 (403).
6613
+ * 없는 토큰/만료/이미 사용됨/취소됨은 **구분하지 않고 404** 입니다 (토큰 추측 방지).
6614
+ */
6615
+ acceptInvitation(token: string): Promise<AcceptInvitationResponse>;
6616
+ /**
6617
+ * 조직을 전환합니다 — 활성 조직이 실린 새 액세스 토큰을 받아 **SDK 에 자동 적용**합니다.
6618
+ *
6619
+ * 데이터베이스 보안 규칙(RLS)의 `auth.org_id` / `auth.org_role` 을 채우는 **유일한
6620
+ * 경로**이며, 호출할 때마다 소속과 역할을 DB 에서 다시 확인합니다.
6621
+ *
6622
+ * ## 반드시 주기적으로 다시 호출하세요
6623
+ *
6624
+ * 조직 컨텍스트의 수명은 반환값의 `org_context_expires_in`(기본 600초) 입니다. 액세스
6625
+ * 토큰(1시간)과 독립적이라, 만료되어도 401 이 나지 않고 조직 규칙만 조용히 거부됩니다.
6626
+ *
6627
+ * ## 세션을 연장하지 않습니다
6628
+ *
6629
+ * 새 토큰의 수명은 `min(1시간, 지금 토큰의 남은 수명)` 입니다. 전환으로 세션을 무한
6630
+ * 연장할 수 없게 하기 위한 제약이며, refresh token 은 건드리지 않습니다. 토큰 회전이
6631
+ * 일어나면 조직 컨텍스트는 사라지므로 다시 호출해야 합니다.
6632
+ *
6633
+ * @example
6634
+ * ```typescript
6635
+ * const ctx = await cb.organizations.switchTo(orgId)
6636
+ * console.log(ctx.role, ctx.org_context_expires_in)
6637
+ * // 이 시점부터 DB 요청은 auth.org_id 가 채워진 상태로 나간다
6638
+ * ```
6639
+ */
6640
+ switchTo(organizationId: string): Promise<OrganizationContext>;
6641
+ }
6642
+
5975
6643
  type PaymentProvider = "toss" | "stripe" | "payapp" | "paypal" | "paddle" | "dodo";
5976
6644
  /**
5977
6645
  * 자격증명 모드 — 콘솔에 등록한 테스트 키/라이브 키 중 어느 쪽으로 결제할지.
@@ -6438,6 +7106,22 @@ interface PublicKeyItem {
6438
7106
  expires_at?: string;
6439
7107
  /** 생성일 */
6440
7108
  created_at: string;
7109
+ /**
7110
+ * 이 키의 **최소권한 스코프**. **비어 있거나 없으면 전권 키**입니다 (하위호환 — 이 기능
7111
+ * 이전에 발급된 키가 전부 그렇습니다).
7112
+ *
7113
+ * 어휘는 새로 만든 것이 아니라 **콘솔 RBAC 권한 이름**(`database:read`, `storage:write` 등)과
7114
+ * **service_role `management_scopes`** 의 합집합입니다. 두 어휘가 AND 로 결합됩니다.
7115
+ */
7116
+ scopes?: string[];
7117
+ /** 발급 출처 (감사). 이 기능 도입 전 발급된 키에는 없습니다 */
7118
+ created_by_kind?: "user" | "app_member" | "service";
7119
+ /** 발급한 사람의 사용자 ID. 사람이 아닌 주체면 없습니다 */
7120
+ created_by_user_id?: string;
7121
+ /** 발급 시점 IP 의 **마스킹된** 값 (평문 IP 가 아닙니다) */
7122
+ created_ip_masked?: string;
7123
+ /** 발급 시점 User-Agent */
7124
+ created_user_agent?: string;
6441
7125
  }
6442
7126
  /**
6443
7127
  * Public Key 생성 요청
@@ -6449,6 +7133,14 @@ interface CreatePublicKeyRequest {
6449
7133
  payment_mode?: PublicKeyPaymentMode;
6450
7134
  /** 만료일 (옵션) */
6451
7135
  expires_at?: string;
7136
+ /**
7137
+ * 이 키의 **최소권한 스코프**. **비어 있거나 없으면 전권 키**입니다 (하위호환 — 이 기능
7138
+ * 이전에 발급된 키가 전부 그렇습니다).
7139
+ *
7140
+ * 어휘는 새로 만든 것이 아니라 **콘솔 RBAC 권한 이름**(`database:read`, `storage:write` 등)과
7141
+ * **service_role `management_scopes`** 의 합집합입니다. 두 어휘가 AND 로 결합됩니다.
7142
+ */
7143
+ scopes?: string[];
6452
7144
  }
6453
7145
  /**
6454
7146
  * Public Key 생성 응답 (전체 키는 이때만 반환됨)
@@ -6470,6 +7162,14 @@ interface CreatePublicKeyResponse {
6470
7162
  expires_at?: string;
6471
7163
  /** 생성일 */
6472
7164
  created_at: string;
7165
+ /**
7166
+ * 이 키의 **최소권한 스코프**. **비어 있거나 없으면 전권 키**입니다 (하위호환 — 이 기능
7167
+ * 이전에 발급된 키가 전부 그렇습니다).
7168
+ *
7169
+ * 어휘는 새로 만든 것이 아니라 **콘솔 RBAC 권한 이름**(`database:read`, `storage:write` 등)과
7170
+ * **service_role `management_scopes`** 의 합집합입니다. 두 어휘가 AND 로 결합됩니다.
7171
+ */
7172
+ scopes?: string[];
6473
7173
  }
6474
7174
  /**
6475
7175
  * Public Key 목록 조회 응답
@@ -6499,6 +7199,43 @@ interface UpdatePublicKeyResponse {
6499
7199
  payment_mode: PublicKeyPaymentMode;
6500
7200
  expires_at?: string;
6501
7201
  created_at: string;
7202
+ /**
7203
+ * 이 키의 **최소권한 스코프**. **비어 있거나 없으면 전권 키**입니다 (하위호환 — 이 기능
7204
+ * 이전에 발급된 키가 전부 그렇습니다).
7205
+ *
7206
+ * 어휘는 새로 만든 것이 아니라 **콘솔 RBAC 권한 이름**(`database:read`, `storage:write` 등)과
7207
+ * **service_role `management_scopes`** 의 합집합입니다. 두 어휘가 AND 로 결합됩니다.
7208
+ */
7209
+ scopes?: string[];
7210
+ }
7211
+ /**
7212
+ * Public Key 회전 요청.
7213
+ *
7214
+ * 회전은 "새로 만들고 옛것 지우기" 가 아닙니다 — 새 키를 발급하고 **옛 키는 유예 기간 동안
7215
+ * 함께 유효하게** 둔 뒤 만료시킵니다. 그래야 클라이언트 배포가 끝나기 전에 키가 죽지 않습니다.
7216
+ */
7217
+ interface RotatePublicKeyRequest {
7218
+ /**
7219
+ * 옛 키를 얼마나 더 살려 둘지(시간).
7220
+ *
7221
+ * 미지정이면 **24시간**, `0` 이면 즉시 폐기, 상한은 **720시간(30일)** 입니다.
7222
+ * 옛 키에 더 이른 만료가 이미 걸려 있으면 그 값이 유지됩니다 — 유출 대응으로 당겨 둔
7223
+ * 만료가 회전으로 되살아나면 안 되기 때문입니다.
7224
+ */
7225
+ grace_period_hours?: number;
7226
+ /** 새 키의 이름. 미지정이면 옛 키의 이름을 그대로 물려받습니다 */
7227
+ name?: string;
7228
+ }
7229
+ /**
7230
+ * Public Key 회전 결과. **새 키의 평문은 이때만 반환됩니다.**
7231
+ */
7232
+ interface RotatePublicKeyResponse {
7233
+ /** 새로 발급된 키 (평문 `key` 포함). 스코프와 `payment_mode` 는 옛 키에서 물려받습니다 */
7234
+ new_key: CreatePublicKeyResponse;
7235
+ /** 유예 기간 뒤 만료될 옛 키의 ID */
7236
+ previous_key_id: string;
7237
+ /** 옛 키가 거부되기 시작하는 시각. **클라이언트 배포는 이 시각 전에 끝나야 합니다** */
7238
+ previous_key_expires_at: string;
6502
7239
  }
6503
7240
 
6504
7241
  /**
@@ -6513,6 +7250,12 @@ interface UpdatePublicKeyResponse {
6513
7250
  * `payment_mode` 로 키마다 결제 자격증명 모드를 고정할 수 있다 — QA 빌드에 `test` 키를 심으면
6514
7251
  * 앱 설정과 무관하게 그 키의 결제만 테스트로 처리된다(platform-issue 019f9961).
6515
7252
  *
7253
+ * `scopes` 로 키의 권한을 좁힐 수 있다. **비우면 전권**이고(기존 키가 전부 그렇다), 어휘는 콘솔
7254
+ * RBAC 권한 이름과 service_role `management_scopes` 를 그대로 재사용한다.
7255
+ *
7256
+ * `rotatePublicKey()` 는 새 키를 발급하고 옛 키를 유예 기간(기본 24시간) 뒤에 만료시킨다 —
7257
+ * 배포가 끝나기 전에 키가 죽지 않게 하는 무중단 회전 경로다.
7258
+ *
6516
7259
  * @example
6517
7260
  * ```typescript
6518
7261
  * // Public Key 목록 조회
@@ -6530,6 +7273,19 @@ interface UpdatePublicKeyResponse {
6530
7273
  * await cb.publicKey.updatePublicKey('app-id', 'key-id', { payment_mode: 'live' })
6531
7274
  * await cb.publicKey.updatePublicKey('app-id', 'key-id', { is_active: false })
6532
7275
  *
7276
+ * // 최소권한 키 (읽기 전용)
7277
+ * await cb.publicKey.createPublicKey('app-id', {
7278
+ * name: 'Analytics reader',
7279
+ * scopes: ['database:read', 'storage:read'],
7280
+ * })
7281
+ *
7282
+ * // 무중단 회전 — 옛 키는 72시간 뒤 만료
7283
+ * const rotated = await cb.publicKey.rotatePublicKey('app-id', 'key-id', {
7284
+ * grace_period_hours: 72,
7285
+ * })
7286
+ * console.log(rotated.new_key.key) // 새 키 (이때만 볼 수 있음!)
7287
+ * console.log(rotated.previous_key_expires_at) // 이 시각 전에 배포를 끝내야 한다
7288
+ *
6533
7289
  * // Public Key 삭제
6534
7290
  * await cb.publicKey.deletePublicKey('app-id', 'key-id')
6535
7291
  * ```
@@ -6570,6 +7326,29 @@ declare class PublicKeyAPI {
6570
7326
  * @param keyId Public Key ID
6571
7327
  */
6572
7328
  deletePublicKey(appId: string, keyId: string): Promise<void>;
7329
+ /**
7330
+ * Public Key 를 **무중단 회전**합니다 (management_scope: `publickey:manage`)
7331
+ *
7332
+ * 새 키를 발급하고 옛 키는 유예 기간 동안 함께 유효하게 둔 뒤 만료시킵니다. 옛 키의
7333
+ * 스코프와 `payment_mode` 는 새 키가 물려받습니다 — 리셋되면 읽기 전용 키가 회전 한 번에
7334
+ * 전권이 되기 때문입니다.
7335
+ *
7336
+ * **반환되는 `new_key.key` 는 이 응답에서만 볼 수 있습니다.**
7337
+ *
7338
+ * @param appId 앱 ID
7339
+ * @param keyId 회전할 Public Key ID
7340
+ * @param data 유예 기간(기본 24시간, 상한 720시간)과 새 키 이름
7341
+ *
7342
+ * @example
7343
+ * ```typescript
7344
+ * const rotated = await cb.publicKey.rotatePublicKey('app-id', 'key-id', {
7345
+ * grace_period_hours: 72,
7346
+ * })
7347
+ * // 1. rotated.new_key.key 를 배포 파이프라인에 반영
7348
+ * // 2. rotated.previous_key_expires_at 전에 모든 클라이언트 배포를 끝낸다
7349
+ * ```
7350
+ */
7351
+ rotatePublicKey(appId: string, keyId: string, data?: RotatePublicKeyRequest): Promise<RotatePublicKeyResponse>;
6573
7352
  }
6574
7353
 
6575
7354
  /**
@@ -6978,20 +7757,27 @@ interface QueueInfoResponse {
6978
7757
  * NATS JetStream 기반 고신뢰 메시지 큐 API.
6979
7758
  * 메시지 발행, 소비, 확인(Ack), 재시도(Nack)를 지원합니다.
6980
7759
  *
7760
+ * 자격증명 경계 (2026-09-06):
7761
+ * - `publish` / `publishBatch` — 엔드유저 동작. 퍼블릭 키만으로 동작합니다.
7762
+ * - `consume` / `ack` / `nack` / `getInfo` — **워커 동작. Secret Key(`cb_sk_*`) 필수.**
7763
+ * 퍼블릭 키는 브라우저 번들에 실려 배포되는 공개 식별자라, 이것만으로 소비가 열려 있으면
7764
+ * 제3자가 메시지를 가져가 페이로드를 탈취하고 원래 워커는 그 메시지를 영영 받지 못합니다.
7765
+ * 누락 시 401 `SECRET_KEY_REQUIRED`.
7766
+ *
6981
7767
  * @example
6982
7768
  * ```typescript
7769
+ * // 브라우저/앱 — 발행만
6983
7770
  * const cb = new ConnectBase({ publicKey: 'your-public-key' })
6984
- *
6985
- * // 메시지 발행
6986
7771
  * await cb.queue.publish('queue-id', {
6987
7772
  * body: { to: 'user@example.com', subject: 'Welcome' }
6988
7773
  * })
6989
7774
  *
6990
- * // 메시지 소비
6991
- * const { messages } = await cb.queue.consume('queue-id', { max_messages: 5 })
7775
+ * // 서버사이드 워커 — secretKey 필수 (브라우저 번들에 넣지 말 것)
7776
+ * const worker = new ConnectBase({ publicKey: 'your-public-key', secretKey: 'cb_sk_...' })
7777
+ * const { messages, ack_token } = await worker.queue.consume('queue-id', { max_messages: 5 })
6992
7778
  * for (const msg of messages) {
6993
7779
  * await processMessage(msg.body)
6994
- * await cb.queue.ack('queue-id', [msg.message_id])
7780
+ * await worker.queue.ack('queue-id', [msg.message_id], ack_token)
6995
7781
  * }
6996
7782
  * ```
6997
7783
  */
@@ -6999,16 +7785,19 @@ declare class QueueAPI {
6999
7785
  private http;
7000
7786
  constructor(http: HttpClient);
7001
7787
  /**
7002
- * 메시지 발행
7788
+ * 메시지 발행 — 엔드유저 동작. 퍼블릭 키만으로 동작합니다.
7003
7789
  */
7004
7790
  publish(queueID: string, data: PublishMessageRequest): Promise<PublishMessageResponse>;
7005
7791
  /**
7006
- * 배치 메시지 발행 (최대 100개)
7792
+ * 배치 메시지 발행 (최대 100개) — 엔드유저 동작. 퍼블릭 키만으로 동작합니다.
7007
7793
  */
7008
7794
  publishBatch(queueID: string, data: PublishBatchRequest): Promise<PublishBatchResponse>;
7009
7795
  /**
7010
7796
  * 메시지 소비 (Pull 방식)
7011
7797
  *
7798
+ * **워커 동작 — Secret Key 필수.** `new ConnectBase({ publicKey, secretKey })` 로 만든
7799
+ * 서버사이드 클라이언트로 호출하세요. 퍼블릭 키만이면 401 `SECRET_KEY_REQUIRED`.
7800
+ *
7012
7801
  * auto_ack=false(기본값): 명시적 Ack 필요. 응답의 ack_token을 ack() 호출 시 전달.
7013
7802
  * auto_ack=true: 즉시 자동 Ack (at-most-once, 유실 가능)
7014
7803
  */
@@ -7016,15 +7805,21 @@ declare class QueueAPI {
7016
7805
  /**
7017
7806
  * 메시지 처리 완료 확인 (Ack)
7018
7807
  *
7808
+ * **워커 동작 — Secret Key 필수.** 퍼블릭 키만이면 401 `SECRET_KEY_REQUIRED`.
7809
+ *
7019
7810
  * ackToken: auto_ack=false 소비 시 반환된 ack_token. 생략 시 message_ids로만 확인.
7020
7811
  */
7021
7812
  ack(queueID: string, messageIds: string[], ackToken?: string): Promise<void>;
7022
7813
  /**
7023
7814
  * 메시지 재시도 요청 (Nack)
7815
+ *
7816
+ * **워커 동작 — Secret Key 필수.** 퍼블릭 키만이면 401 `SECRET_KEY_REQUIRED`.
7024
7817
  */
7025
7818
  nack(queueID: string, messageId: string, options?: NackMessageRequest): Promise<void>;
7026
7819
  /**
7027
- * 큐 정보 조회
7820
+ * 큐 정보 조회 (큐 목록/깊이 등 운영 정보)
7821
+ *
7822
+ * **워커 동작 — Secret Key 필수.** 퍼블릭 키만이면 401 `SECRET_KEY_REQUIRED`.
7028
7823
  */
7029
7824
  getInfo(queueID: string): Promise<QueueInfoResponse>;
7030
7825
  }
@@ -7951,6 +8746,35 @@ interface CompleteUploadResponse {
7951
8746
  created_at: string;
7952
8747
  }
7953
8748
 
8749
+ /**
8750
+ * 파일 스토리지 API.
8751
+ *
8752
+ * ## 소유권 모델 (2026-09 도입)
8753
+ *
8754
+ * 퍼블릭 키(`cb_pk_*`)는 브라우저 번들에 실려 배포되는 **공개값**이라 "어느 앱인가" 만 말할 뿐
8755
+ * "이 파일을 다룰 자격이 있는가" 를 증명하지 못한다. 그래서 파일 행에 **업로더(AppMember)** 를
8756
+ * 기록하고, 수정/삭제는 그 업로더 본인이나 앱 관리자만 할 수 있다.
8757
+ *
8758
+ * 요청자 신원은 세 가지다.
8759
+ *
8760
+ * | 보내는 것 | 신원 | 할 수 있는 것 |
8761
+ * |---|---|---|
8762
+ * | `X-Public-Key` 만 | 익명 | 접근 수준에 따름 (아래 표) |
8763
+ * | `+ Authorization: Bearer <AppMember 토큰>` | 그 멤버 | 자기가 올린 파일 |
8764
+ * | `+ Authorization: Bearer cb_sk_...` | 앱 관리자 | 전부 |
8765
+ *
8766
+ * ## 스토리지 접근 수준 (콘솔 → 스토리지 → 보안 설정)
8767
+ *
8768
+ * | 수준 | 목록/조회 | 업로드 | 수정/삭제 |
8769
+ * |---|---|---|---|
8770
+ * | `shared` (기존 스토리지) | 전체 | 누구나 | 업로더 본인 또는 관리자. **업로더가 없는 기존 파일은 종전대로 누구나** |
8771
+ * | `public_read` | 전체 | 누구나 | 업로더 본인 또는 관리자. 업로더가 없는 파일은 관리자만 |
8772
+ * | `private` (**새로 만드는 스토리지 기본값**) | 자기가 올린 것만 | 멤버 토큰 필수 | 업로더 본인 또는 관리자 |
8773
+ *
8774
+ * 이미 운영 중인 스토리지는 `shared` 라 동작이 바뀌지 않는다. 새로 만든 스토리지에서
8775
+ * `getFiles()` 가 401(`이 스토리지는 접근 수준이 private...`)을 던진다면 둘 중 하나를 하면 된다.
8776
+ * 멤버 토큰을 함께 보내거나, 공개 자산 갤러리라면 콘솔에서 접근 수준을 `public_read` 로 내린다.
8777
+ */
7954
8778
  declare class StorageAPI {
7955
8779
  private http;
7956
8780
  constructor(http: HttpClient);
@@ -7977,6 +8801,10 @@ declare class StorageAPI {
7977
8801
  /**
7978
8802
  * 파일 목록 조회
7979
8803
  *
8804
+ * 접근 수준이 `private` 인 스토리지에서는 **자기가 올린 파일만** 반환하며, 멤버 토큰 없이
8805
+ * 호출하면 401 이다 (빈 배열이 아니라 에러다 — 빈 목록은 "스토리지가 비었다" 와 구분되지
8806
+ * 않아 원인을 찾을 수 없기 때문). `shared` / `public_read` 는 종전대로 전체를 반환한다.
8807
+ *
7980
8808
  * @param storageId - 파일 스토리지 ID
7981
8809
  * @param parentId - 부모 폴더 ID. 지정 시 해당 폴더의 **직계 자식만** 반환한다.
7982
8810
  * 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
@@ -7996,6 +8824,12 @@ declare class StorageAPI {
7996
8824
  *
7997
8825
  * 서버를 거치지 않고 Object Storage에 직접 업로드합니다.
7998
8826
  *
8827
+ * `Authorization: Bearer <AppMember 토큰>` 을 함께 보내면 업로드한 파일이 **그 멤버에게
8828
+ * 귀속**되어, 이후 그 멤버(와 앱 관리자)만 지울 수 있다. 토큰 없이 올린 파일은 귀속이 없어
8829
+ * `shared` 스토리지에서는 누구나 지울 수 있으므로, 사용자 업로드를 받는 앱이라면 로그인
8830
+ * 세션과 함께 호출하는 것을 권장한다. 접근 수준이 `private` 인 스토리지는 멤버 토큰이 없으면
8831
+ * 401 이다.
8832
+ *
7999
8833
  * 3번째 인자는 부모 폴더 ID(문자열) 또는 옵션 객체를 받습니다. 옵션으로
8000
8834
  * `onProgress` 콜백을 넘기면 업로드 진행률(%)을 실시간으로 받을 수 있습니다
8001
8835
  * (브라우저 환경). 기존 `uploadFile(id, file, 'folder-id')` 호출과 하위 호환됩니다.
@@ -8026,10 +8860,21 @@ declare class StorageAPI {
8026
8860
  uploadFiles(storageId: string, files: File[], parentIdOrOptions?: string | UploadFileOptions): Promise<UploadFileResponse[]>;
8027
8861
  /**
8028
8862
  * 폴더 생성
8863
+ *
8864
+ * 업로드와 같은 귀속 규칙이 적용된다 — 멤버 토큰과 함께 만들면 그 멤버 소유가 된다.
8865
+ * 폴더 삭제는 CASCADE 라, 폴더 안에 **남이 올린 파일이 하나라도 있으면** 폴더 소유자라도
8866
+ * 지울 수 없다(403).
8029
8867
  */
8030
8868
  createFolder(storageId: string, data: CreateFolderRequest): Promise<CreateFolderResponse>;
8031
8869
  /**
8032
8870
  * 파일/폴더 삭제
8871
+ *
8872
+ * **업로더 본인 또는 앱 관리자만** 지울 수 있다. 남의 파일이면 403
8873
+ * (`이 파일의 업로더만 수정/삭제할 수 있습니다`), 애초에 볼 수 없는 파일이면 404 다.
8874
+ *
8875
+ * 운영 도구처럼 앱의 모든 파일을 지워야 하는 코드라면 `Authorization: Bearer cb_sk_...`
8876
+ * (Secret Key)를 함께 보낸다. Secret Key 는 브라우저 번들에 넣으면 안 되므로 서버 쪽에서만
8877
+ * 사용한다.
8033
8878
  */
8034
8879
  deleteFile(storageId: string, fileId: string): Promise<void>;
8035
8880
  /**
@@ -8058,6 +8903,10 @@ declare class StorageAPI {
8058
8903
  * 같은 경로에 파일이 이미 존재하면 덮어쓰기합니다.
8059
8904
  * URL이 변경되지 않아 고정 URL이 필요한 경우에 유용합니다.
8060
8905
  *
8906
+ * **덮어쓰기는 삭제와 같은 권한을 요구한다.** URL 은 그대로 둔 채 내용만 바뀌므로, 소유권
8907
+ * 검사가 없으면 사이트가 참조 중인 이미지를 임의 콘텐츠로 갈아치울 수 있기 때문이다.
8908
+ * 기존 파일의 업로더 본인이나 앱 관리자가 아니면 403 이다.
8909
+ *
8061
8910
  * @example
8062
8911
  * ```ts
8063
8912
  * // 프로필 이미지 업로드 (항상 같은 URL 유지)
@@ -8233,6 +9082,40 @@ interface UpdateBillingKeyRequest {
8233
9082
  interface ListBillingKeysResponse {
8234
9083
  billing_keys: BillingKeyResponse[];
8235
9084
  }
9085
+ /**
9086
+ * 빌링키 삭제 결과. `cb.subscription.deleteBillingKey()` 의 반환값.
9087
+ *
9088
+ * **PG 쪽 등록까지 지워졌는지는 프로바이더마다 다릅니다.** toss 만 빌링키 삭제 API 가 있어
9089
+ * PG 등록까지 해지되고, 나머지 프로바이더(payapp / paypal / paddle / stripe)의 `billing_key`
9090
+ * 행은 카드 토큰이 아니라 구독 제어 키이며 PG 에 "결제수단 삭제" 개념이 없어 Connect Base
9091
+ * 기록만 지워집니다. 이 차이를 무시하면 사용자는 "결제수단을 지웠다"고 믿는데 PG 청구는
9092
+ * 계속됩니다 — 삭제 후 반드시 `provider_revoked` 를 확인하세요.
9093
+ *
9094
+ * @example
9095
+ * ```typescript
9096
+ * const result = await client.subscription.deleteBillingKey(billingKeyId)
9097
+ * if (!result.provider_revoked) {
9098
+ * // PG 등록이 남아 있다. 사용자에게 알리고 PG 대시보드에서 직접 해지하도록 안내한다.
9099
+ * alert(result.provider_revoke_note)
9100
+ * }
9101
+ * ```
9102
+ */
9103
+ interface DeleteBillingKeyResponse {
9104
+ /** 사람이 읽는 결과 메시지 */
9105
+ message: string;
9106
+ /** 이 빌링키를 발급했던 PG (`toss` / `payapp` / `paypal` / `paddle` / `stripe`) */
9107
+ provider: string;
9108
+ /**
9109
+ * PG 측 등록까지 해지되었는지. `false` 면 Connect Base 기록만 지워졌고 PG 등록은
9110
+ * 그대로 남아 있을 수 있습니다.
9111
+ */
9112
+ provider_revoked: boolean;
9113
+ /**
9114
+ * `provider_revoked: false` 인 이유 (사람이 읽는 설명).
9115
+ * `provider_revoked: true` 이면 실리지 않습니다.
9116
+ */
9117
+ provider_revoke_note?: string;
9118
+ }
8236
9119
  type BillingCycle = "daily" | "weekly" | "monthly" | "yearly";
8237
9120
  type SubscriptionStatus = "active" | "paused" | "canceled" | "past_due" | "expired" | "trial";
8238
9121
  interface CreateSubscriptionRequest {
@@ -8740,9 +9623,31 @@ declare class SubscriptionAPI {
8740
9623
  /**
8741
9624
  * 빌링키 삭제
8742
9625
  *
9626
+ * **PG 쪽 등록까지 지워졌는지는 프로바이더마다 다릅니다.** 반환값의 `provider_revoked` 를
9627
+ * 반드시 확인하세요 — `false` 면 Connect Base 기록만 지워졌고 PG 등록은 남아 있습니다.
9628
+ *
9629
+ * | 프로바이더 | PG 해지 | 동작 |
9630
+ * |-----------|---------|------|
9631
+ * | `toss` | O | 토스 빌링키 삭제 API 호출. PG 삭제가 실패하면 DB 행도 지우지 않고 에러 |
9632
+ * | `payapp` / `paypal` / `paddle` / `stripe` | X | "결제수단 삭제" API 가 없어 기록만 삭제. `provider_revoked: false` + `provider_revoke_note` |
9633
+ *
9634
+ * 살아 있는 구독(`active` / `trial` / `paused` / `past_due`)이 그 빌링키를 물고 있으면
9635
+ * PG 해지 경로가 없는 프로바이더에서는 `409 billing_key_bound_to_subscription` 으로
9636
+ * 거절됩니다 (구독을 먼저 해지하세요). **toss 는 이 검사를 하지 않고 그대로 진행합니다** —
9637
+ * PG 등록이 실제로 사라지므로 청구도 함께 멈추기 때문입니다.
9638
+ *
8743
9639
  * @param billingKeyId - 빌링키 ID
9640
+ * @returns 삭제 결과 (PG 해지 여부 포함)
9641
+ *
9642
+ * @example
9643
+ * ```typescript
9644
+ * const result = await client.subscription.deleteBillingKey(billingKeyId)
9645
+ * if (!result.provider_revoked) {
9646
+ * alert(result.provider_revoke_note) // PG 등록이 남아 있음을 사용자에게 알린다
9647
+ * }
9648
+ * ```
8744
9649
  */
8745
- deleteBillingKey(billingKeyId: string): Promise<void>;
9650
+ deleteBillingKey(billingKeyId: string): Promise<DeleteBillingKeyResponse>;
8746
9651
  /**
8747
9652
  * 구독 생성
8748
9653
  * 정기결제 구독을 생성합니다.
@@ -9601,6 +10506,20 @@ interface ChannelMembership {
9601
10506
  started_at: string;
9602
10507
  expires_at?: string;
9603
10508
  }
10509
+ /**
10510
+ * Ownership enforcement level for a video storage container.
10511
+ *
10512
+ * Shares the vocabulary with file storage (`backend/pkg/ownership` is the source of truth).
10513
+ * On the video path it currently gates only `PATCH` / `DELETE` of individual videos, so
10514
+ * `public_read` and `private` behave the same until a read gate is added.
10515
+ *
10516
+ * - `shared` videos with a recorded uploader are editable only by that member; videos
10517
+ * without one stay editable by anyone (the value every pre-existing container has)
10518
+ * - `public_read` only the uploader may edit or delete; videos without an uploader need the
10519
+ * console or a Secret Key
10520
+ * - `private` same write rules as `public_read`, and declares the intent to gate reads
10521
+ */
10522
+ type VideoStorageAccessLevel = "shared" | "public_read" | "private";
9604
10523
  interface VideoStorage {
9605
10524
  id: string;
9606
10525
  app_id: string;
@@ -9612,6 +10531,7 @@ interface VideoStorage {
9612
10531
  allow_server_to_server: boolean;
9613
10532
  allowed_origins: string[];
9614
10533
  strict_referer_check: boolean;
10534
+ access_level: VideoStorageAccessLevel;
9615
10535
  cdn_provider: string;
9616
10536
  cdn_config: Record<string, unknown>;
9617
10537
  default_qualities: string[];
@@ -9640,6 +10560,8 @@ interface UpdateVideoStorageRequest {
9640
10560
  allow_server_to_server?: boolean;
9641
10561
  allowed_origins?: string[];
9642
10562
  strict_referer_check?: boolean;
10563
+ /** Omit to keep the current value. Anything outside the three values is a 400. */
10564
+ access_level?: VideoStorageAccessLevel;
9643
10565
  cdn_provider?: string;
9644
10566
  cdn_config?: Record<string, unknown>;
9645
10567
  default_qualities?: string[];
@@ -9758,10 +10680,25 @@ declare class VideoAPI {
9758
10680
  get(videoId: string): Promise<Video>;
9759
10681
  /**
9760
10682
  * Update video details
10683
+ *
10684
+ * ## 소유권 (2026-09 도입)
10685
+ *
10686
+ * **영상을 올린 멤버 본인 또는 앱 관리자만** 수정할 수 있다. 퍼블릭 키(`cb_pk_*`)는
10687
+ * 브라우저 번들에 실려 배포되는 공개값이라 그것만으로는 남의 영상을 다룰 수 없다.
10688
+ *
10689
+ * - `Authorization: Bearer <AppMember 토큰>` — 그 멤버가 올린 영상만
10690
+ * - `Authorization: Bearer cb_sk_...` (Secret Key, 서버 전용) — 앱의 모든 영상
10691
+ *
10692
+ * 남의 영상이면 403 (`NOT_RESOURCE_OWNER`) 이다.
10693
+ *
10694
+ * 업로더 정보가 없는 **기존 영상**(멤버 토큰 없이 업로드된 것)은 스토리지 접근 수준이
10695
+ * `shared` 일 때 종전대로 수정할 수 있다 — 운영 중인 앱의 동작을 바꾸지 않기 위한 하위호환.
9761
10696
  */
9762
10697
  update(videoId: string, data: UpdateVideoRequest): Promise<Video>;
9763
10698
  /**
9764
10699
  * Delete a video
10700
+ *
10701
+ * `update()` 와 동일한 소유권 규칙이 적용된다 — 업로더 본인 또는 앱 관리자만 삭제할 수 있다.
9765
10702
  */
9766
10703
  delete(videoId: string): Promise<void>;
9767
10704
  /**
@@ -10706,6 +11643,16 @@ declare class ConnectBase {
10706
11643
  * 정기결제/구독 API
10707
11644
  */
10708
11645
  readonly subscription: SubscriptionAPI;
11646
+ /**
11647
+ * 조직(워크스페이스) API — 앱 엔드유저가 만드는 조직/팀.
11648
+ *
11649
+ * **로그인한 회원 토큰이 필요합니다** (퍼블릭 키만으로는 401). 콘솔 협업자 RBAC
11650
+ * (`cb.roles.*`)와는 별개 시스템입니다.
11651
+ *
11652
+ * 조직 전환(`switchTo`)이 실어 주는 RLS 조직 컨텍스트는 **10분**만 유효하므로 만료 전에
11653
+ * 다시 호출해야 합니다 — 자세한 내용은 `OrganizationsAPI` 문서를 참고하세요.
11654
+ */
11655
+ readonly organizations: OrganizationsAPI;
10709
11656
  /**
10710
11657
  * 푸시 알림 API
10711
11658
  */
@@ -10810,4 +11757,4 @@ declare class ConnectBase {
10810
11757
  updateConfig(config: Partial<ConnectBaseConfig>): void;
10811
11758
  }
10812
11759
 
10813
- export { AIAPI, type AIChatOptions, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, AIError, type AIErrorCode, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppMemberDetail, type AppMemberIdentityDetail, type AppMemberIdentitySummary, type AppMemberList, type AppMemberListItem, type AppMemberProviderType, AppMembersAPI, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type ChangePlanPreview, type ChangePlanRequest, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListAppMembersOptions, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OnPaymentFailure, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentMode, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PostponeBillingRequest, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type ProrationMode, type PublicKeyItem, type PublicKeyPaymentMode, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptLogEntry, type ScriptLogsResponse, type ScriptMeta, type ScriptMetricsResponse, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SyncLostMessage, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toAIError, toCreateRoomWire };
11760
+ export { AIAPI, type AIChatOptions, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, AIError, type AIErrorCode, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AcceptInvitationResponse, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppMemberDetail, type AppMemberIdentityDetail, type AppMemberIdentitySummary, type AppMemberList, type AppMemberListItem, type AppMemberProviderType, AppMembersAPI, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type ChangePlanPreview, type ChangePlanRequest, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateInvitationRequest, type CreateInvitationResponse, type CreateLobbyRequest, type CreateOrganizationRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteBillingKeyResponse, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListAppMembersOptions, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberChangePasswordRequest, type MemberCredentialMessageResponse, type MemberEmailVerificationConfirmRequest, type MemberEmailVerificationConfirmResponse, type MemberEmailVerificationRequest, type MemberInfoResponse, type MemberPasswordResetConfirmRequest, type MemberPasswordResetRequest, type MemberRedirectOption, type MemberSession, type MemberSessionList, type MemberSessionRevokeResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OnPaymentFailure, type OpenDialogOptions, type OpenDialogResult, type Organization, type OrganizationContext, type OrganizationInvitation, type OrganizationMember, type OrganizationMemberList, type OrganizationRole, OrganizationsAPI, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentMode, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PostponeBillingRequest, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type ProrationMode, type PublicKeyItem, type PublicKeyPaymentMode, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type RotatePublicKeyRequest, type RotatePublicKeyResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptLogEntry, type ScriptLogsResponse, type ScriptMeta, type ScriptMetricsResponse, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SyncLostMessage, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdateOrganizationRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageAccessLevel, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toAIError, toCreateRoomWire };