entity-client 1.0.7

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.
Files changed (82) hide show
  1. package/dist/EntityAppServerClient.d.ts +383 -0
  2. package/dist/EntityAppServerClient.js +2 -0
  3. package/dist/EntityAppServerClient.js.map +7 -0
  4. package/dist/EntityServerClient.d.ts +638 -0
  5. package/dist/EntityServerClient.js +2 -0
  6. package/dist/EntityServerClient.js.map +7 -0
  7. package/dist/client/base.d.ts +98 -0
  8. package/dist/client/base.js +2 -0
  9. package/dist/client/base.js.map +7 -0
  10. package/dist/client/hmac.d.ts +8 -0
  11. package/dist/client/hmac.js +2 -0
  12. package/dist/client/hmac.js.map +7 -0
  13. package/dist/client/packet.d.ts +24 -0
  14. package/dist/client/packet.js +2 -0
  15. package/dist/client/packet.js.map +7 -0
  16. package/dist/client/request.d.ts +20 -0
  17. package/dist/client/request.js +2 -0
  18. package/dist/client/request.js.map +7 -0
  19. package/dist/client/utils.d.ts +8 -0
  20. package/dist/client/utils.js +2 -0
  21. package/dist/client/utils.js.map +7 -0
  22. package/dist/hooks/useEntityServer.d.ts +104 -0
  23. package/dist/hooks/useEntityServer.js +2 -0
  24. package/dist/hooks/useEntityServer.js.map +7 -0
  25. package/dist/index.d.ts +10 -0
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +7 -0
  28. package/dist/mixins/app/account.d.ts +64 -0
  29. package/dist/mixins/app/account.js +2 -0
  30. package/dist/mixins/app/account.js.map +7 -0
  31. package/dist/mixins/app/board.d.ts +87 -0
  32. package/dist/mixins/app/board.js +2 -0
  33. package/dist/mixins/app/board.js.map +7 -0
  34. package/dist/mixins/app/email-verify.d.ts +59 -0
  35. package/dist/mixins/app/email-verify.js +2 -0
  36. package/dist/mixins/app/email-verify.js.map +7 -0
  37. package/dist/mixins/app/index.d.ts +6 -0
  38. package/dist/mixins/app/index.js +2 -0
  39. package/dist/mixins/app/index.js.map +7 -0
  40. package/dist/mixins/app/oauth.d.ts +60 -0
  41. package/dist/mixins/app/oauth.js +2 -0
  42. package/dist/mixins/app/oauth.js.map +7 -0
  43. package/dist/mixins/app/password-reset.d.ts +59 -0
  44. package/dist/mixins/app/password-reset.js +2 -0
  45. package/dist/mixins/app/password-reset.js.map +7 -0
  46. package/dist/mixins/app/two-factor.d.ts +61 -0
  47. package/dist/mixins/app/two-factor.js +2 -0
  48. package/dist/mixins/app/two-factor.js.map +7 -0
  49. package/dist/mixins/auth.d.ts +115 -0
  50. package/dist/mixins/auth.js +2 -0
  51. package/dist/mixins/auth.js.map +7 -0
  52. package/dist/mixins/entity.d.ts +147 -0
  53. package/dist/mixins/entity.js +2 -0
  54. package/dist/mixins/entity.js.map +7 -0
  55. package/dist/mixins/file.d.ts +103 -0
  56. package/dist/mixins/file.js +2 -0
  57. package/dist/mixins/file.js.map +7 -0
  58. package/dist/mixins/push.d.ts +115 -0
  59. package/dist/mixins/push.js +2 -0
  60. package/dist/mixins/push.js.map +7 -0
  61. package/dist/mixins/server/admin.d.ts +105 -0
  62. package/dist/mixins/server/admin.js +2 -0
  63. package/dist/mixins/server/admin.js.map +7 -0
  64. package/dist/mixins/server/index.d.ts +7 -0
  65. package/dist/mixins/server/index.js +2 -0
  66. package/dist/mixins/server/index.js.map +7 -0
  67. package/dist/mixins/smtp.d.ts +65 -0
  68. package/dist/mixins/smtp.js +2 -0
  69. package/dist/mixins/smtp.js.map +7 -0
  70. package/dist/mixins/utils.d.ts +116 -0
  71. package/dist/mixins/utils.js +2 -0
  72. package/dist/mixins/utils.js.map +7 -0
  73. package/dist/packet.d.ts +11 -0
  74. package/dist/packet.js +2 -0
  75. package/dist/packet.js.map +7 -0
  76. package/dist/react.d.ts +1 -0
  77. package/dist/react.js +2 -0
  78. package/dist/react.js.map +7 -0
  79. package/dist/types.d.ts +250 -0
  80. package/dist/types.js +1 -0
  81. package/dist/types.js.map +7 -0
  82. package/package.json +57 -0
@@ -0,0 +1,98 @@
1
+ import type { EntityServerClientHealthCsrf, EntityServerClientOptions } from "../types.js";
2
+ import { type RequestOptions } from "./request.js";
3
+ export type GConstructor<T = object> = new (...args: any[]) => T;
4
+ export declare class EntityServerClientBase {
5
+ baseUrl: string;
6
+ token: string;
7
+ anonymousPacketToken: string;
8
+ apiKey: string;
9
+ hmacSecret: string;
10
+ encryptRequests: boolean;
11
+ csrfEnabled: boolean;
12
+ csrfToken: string;
13
+ csrfHeaderName: string;
14
+ csrfRefreshPath: string;
15
+ csrfRefreshBuffer: number;
16
+ activeTxId: string | null;
17
+ keepSession: boolean;
18
+ refreshBuffer: number;
19
+ onTokenRefreshed?: (accessToken: string, expiresIn: number) => void;
20
+ onSessionExpired?: (error: Error) => void;
21
+ _sessionRefreshToken: string | null;
22
+ _refreshTimer: ReturnType<typeof setTimeout> | null;
23
+ _csrfRefreshTimer: ReturnType<typeof setTimeout> | null;
24
+ _csrfRefreshPromise: Promise<string> | null;
25
+ /**
26
+ * EntityServerClient 인스턴스를 생성합니다.
27
+ *
28
+ * 기본값:
29
+ * - `baseUrl`: `VITE_ENTITY_SERVER_URL` 또는 상대 경로(`""`)
30
+ */
31
+ constructor(options?: EntityServerClientOptions);
32
+ /** baseUrl, token, encryptRequests 값을 런타임에 갱신합니다. */
33
+ configure(options: Partial<EntityServerClientOptions>): void;
34
+ /** 인증 요청에 사용할 JWT Access Token을 설정합니다. */
35
+ setToken(token: string): void;
36
+ /** 익명 패킷 암호화용 토큰을 설정합니다. */
37
+ setAnonymousPacketToken(token: string): void;
38
+ /** HMAC 인증용 API Key를 설정합니다. */
39
+ setApiKey(apiKey: string): void;
40
+ /** HMAC 인증용 시크릿을 설정합니다. */
41
+ setHmacSecret(secret: string): void;
42
+ /** 암호화 요청 활성화 여부를 설정합니다. */
43
+ setEncryptRequests(value: boolean): void;
44
+ setCsrfToken(token: string): void;
45
+ setCsrfEnabled(enabled: boolean): void;
46
+ /** @internal 자동 토큰 갱신 타이머를 시작합니다. */
47
+ _scheduleKeepSession(refreshToken: string, expiresIn: number, refreshFn: (rt: string) => Promise<{
48
+ access_token: string;
49
+ expires_in: number;
50
+ }>): void;
51
+ /** @internal 자동 갱신 타이머를 정리합니다. */
52
+ _clearRefreshTimer(): void;
53
+ /**
54
+ * 세션 유지 타이머를 중지합니다.
55
+ * `logout()` 호출 시 자동으로 중지되며, 직접 호출이 필요한 경우는 드뭅니다.
56
+ */
57
+ stopKeepSession(): void;
58
+ _clearCsrfRefreshTimer(): void;
59
+ stopCsrfRefresh(): void;
60
+ _scheduleCsrfRefresh(expiresIn: number): void;
61
+ refreshCsrfToken(): Promise<string>;
62
+ _applyCsrfHealth(csrf?: EntityServerClientHealthCsrf | null): void;
63
+ /**
64
+ * 요청 바디를 파싱합니다.
65
+ * `application/octet-stream`이면 XChaCha20-Poly1305 복호화, 그 외는 JSON 파싱합니다.
66
+ *
67
+ * @param requireEncrypted `true`이면 암호화된 요청만 허용합니다.
68
+ */
69
+ readRequestBody<T = Record<string, unknown>>(body: ArrayBuffer | Uint8Array | string | T | null | undefined, contentType?: string, requireEncrypted?: boolean): T;
70
+ get _reqOpts(): RequestOptions;
71
+ /**
72
+ * 임의 경로에 JSON 요청을 보냅니다. 응답이 JSON이면 파싱, octet-stream이면 자동 복호화합니다.
73
+ * `ok` 필드를 강제하지 않아 go서버/앱서버 신규 라우트 등 자유 응답 포맷에 사용합니다.
74
+ * `encryptRequests: true`이면 요청 바디도 자동 암호화됩니다.
75
+ */
76
+ requestJson<T>(method: string, path: string, body?: unknown, withAuth?: boolean, extraHeaders?: Record<string, string>): Promise<T>;
77
+ /**
78
+ * 임의 경로에 요청을 보내고 바이너리(ArrayBuffer)를 반환합니다.
79
+ * 이미지, PDF, 압축 파일 등 바이너리 응답이 오는 엔드포인트에 사용합니다.
80
+ */
81
+ requestBinary(method: string, path: string, body?: unknown, withAuth?: boolean): Promise<ArrayBuffer>;
82
+ /**
83
+ * multipart/form-data 요청을 보냅니다. 파일 업로드 등에 사용합니다.
84
+ * 응답은 JSON으로 파싱하여 반환합니다.
85
+ */
86
+ requestForm<T>(method: string, path: string, form: FormData, withAuth?: boolean): Promise<T>;
87
+ /**
88
+ * multipart/form-data 요청을 보내고 바이너리(ArrayBuffer)를 반환합니다.
89
+ */
90
+ requestFormBinary(method: string, path: string, form: FormData, withAuth?: boolean): Promise<ArrayBuffer>;
91
+ _request<T>(method: string, path: string, body?: unknown, withAuth?: boolean, extraHeaders?: Record<string, string>): Promise<T>;
92
+ /** PNG/바이너리 응답을 ArrayBuffer로 반환합니다. (QR, 바코드 등) */
93
+ _requestBinary(method: string, path: string, body?: unknown, withAuth?: boolean): Promise<ArrayBuffer>;
94
+ /** multipart/form-data 요청을 보냅니다. (파일 업로드 등) */
95
+ _requestForm<T>(method: string, path: string, form: FormData, withAuth?: boolean): Promise<T>;
96
+ /** multipart/form-data 요청을 보내고 바이너리(ArrayBuffer)를 반환합니다. */
97
+ _requestFormBinary(method: string, path: string, form: FormData, withAuth?: boolean): Promise<ArrayBuffer>;
98
+ }
@@ -0,0 +1,2 @@
1
+ import{readEnv as o}from"./utils.js";import{derivePacketKey as c,parseRequestBody as u}from"./packet.js";import{entityRequest as h}from"./request.js";class d{baseUrl;token;anonymousPacketToken;apiKey;hmacSecret;encryptRequests;csrfEnabled;csrfToken;csrfHeaderName;csrfRefreshPath;csrfRefreshBuffer;activeTxId=null;keepSession;refreshBuffer;onTokenRefreshed;onSessionExpired;_sessionRefreshToken=null;_refreshTimer=null;_csrfRefreshTimer=null;_csrfRefreshPromise=null;constructor(e={}){const r=o("VITE_ENTITY_SERVER_URL");this.baseUrl=(e.baseUrl??r??"").replace(/\/$/,""),this.token=e.token??"",this.anonymousPacketToken=e.anonymousPacketToken??"",this.apiKey=e.apiKey??"",this.hmacSecret=e.hmacSecret??"",this.encryptRequests=e.encryptRequests??!1,this.csrfEnabled=e.csrfEnabled??!1,this.csrfToken=e.csrfToken??"",this.csrfHeaderName=e.csrfHeaderName??"x-csrf-token",this.csrfRefreshPath=e.csrfRefreshPath??"/v1/csrf-token",this.csrfRefreshBuffer=e.csrfRefreshBuffer??60,this.keepSession=e.keepSession??!1,this.refreshBuffer=e.refreshBuffer??60,this.onTokenRefreshed=e.onTokenRefreshed,this.onSessionExpired=e.onSessionExpired}configure(e){typeof e.baseUrl=="string"&&(this.baseUrl=e.baseUrl.replace(/\/$/,"")),typeof e.token=="string"&&(this.token=e.token),typeof e.anonymousPacketToken=="string"&&(this.anonymousPacketToken=e.anonymousPacketToken),typeof e.encryptRequests=="boolean"&&(this.encryptRequests=e.encryptRequests),typeof e.csrfEnabled=="boolean"&&(this.csrfEnabled=e.csrfEnabled,e.csrfEnabled||(this.csrfToken="",this.stopCsrfRefresh())),typeof e.csrfToken=="string"&&(this.csrfToken=e.csrfToken),typeof e.csrfHeaderName=="string"&&(this.csrfHeaderName=e.csrfHeaderName),typeof e.csrfRefreshPath=="string"&&(this.csrfRefreshPath=e.csrfRefreshPath),typeof e.csrfRefreshBuffer=="number"&&(this.csrfRefreshBuffer=e.csrfRefreshBuffer),typeof e.apiKey=="string"&&(this.apiKey=e.apiKey),typeof e.hmacSecret=="string"&&(this.hmacSecret=e.hmacSecret),typeof e.keepSession=="boolean"&&(this.keepSession=e.keepSession),typeof e.refreshBuffer=="number"&&(this.refreshBuffer=e.refreshBuffer),e.onTokenRefreshed&&(this.onTokenRefreshed=e.onTokenRefreshed),e.onSessionExpired&&(this.onSessionExpired=e.onSessionExpired)}setToken(e){this.token=e}setAnonymousPacketToken(e){this.anonymousPacketToken=e}setApiKey(e){this.apiKey=e}setHmacSecret(e){this.hmacSecret=e}setEncryptRequests(e){this.encryptRequests=e}setCsrfToken(e){this.csrfToken=e}setCsrfEnabled(e){this.csrfEnabled=e,e||(this.csrfToken="",this.stopCsrfRefresh())}_scheduleKeepSession(e,r,t){this._clearRefreshTimer(),this._sessionRefreshToken=e;const i=Math.max((r-this.refreshBuffer)*1e3,0);this._refreshTimer=setTimeout(async()=>{if(this._sessionRefreshToken)try{const s=await t(this._sessionRefreshToken);this.onTokenRefreshed?.(s.access_token,s.expires_in),this._scheduleKeepSession(this._sessionRefreshToken,s.expires_in,t)}catch(s){this._clearRefreshTimer(),this.onSessionExpired?.(s instanceof Error?s:new Error(String(s)))}},i)}_clearRefreshTimer(){this._refreshTimer!==null&&(clearTimeout(this._refreshTimer),this._refreshTimer=null)}stopKeepSession(){this._clearRefreshTimer(),this._sessionRefreshToken=null}_clearCsrfRefreshTimer(){this._csrfRefreshTimer!==null&&(clearTimeout(this._csrfRefreshTimer),this._csrfRefreshTimer=null)}stopCsrfRefresh(){this._clearCsrfRefreshTimer(),this._csrfRefreshPromise=null}_scheduleCsrfRefresh(e){if(this._clearCsrfRefreshTimer(),!this.csrfEnabled||!this.csrfRefreshPath)return;const r=Math.max((e-this.csrfRefreshBuffer)*1e3,0);this._csrfRefreshTimer=setTimeout(()=>{this.refreshCsrfToken().catch(()=>{this._clearCsrfRefreshTimer()})},r)}async refreshCsrfToken(){return!this.csrfEnabled||!this.csrfRefreshPath?"":(this._csrfRefreshPromise||(this._csrfRefreshPromise=(async()=>{const e=await fetch(`${this.baseUrl}${this.csrfRefreshPath}`,{method:"GET",credentials:"include"});if(!e.ok){const i=await e.text().catch(()=>""),s=new Error(i||`HTTP ${e.status}`);throw s.status=e.status,s}const r=await e.json().catch(()=>null),t=r&&typeof r=="object"&&"data"in r?r.data??null:r;if(!t?.enabled||typeof t.token!="string")throw new Error("CSRF token refresh failed");return this._applyCsrfHealth(t),this.csrfToken})().finally(()=>{this._csrfRefreshPromise=null})),this._csrfRefreshPromise)}_applyCsrfHealth(e){if(!e?.enabled){this.setCsrfEnabled(!1);return}this.csrfEnabled=!0,typeof e.token=="string"&&(this.csrfToken=e.token),typeof e.headerName=="string"&&(this.csrfHeaderName=e.headerName),typeof e.refreshPath=="string"&&(this.csrfRefreshPath=e.refreshPath),typeof e.expiresIn=="number"&&e.expiresIn>0&&this._scheduleCsrfRefresh(e.expiresIn)}readRequestBody(e,r="application/json",t=!1){const i=c(this.hmacSecret,this.token||this.anonymousPacketToken);return u(e,r,t,i)}get _reqOpts(){return{baseUrl:this.baseUrl,token:this.token,anonymousPacketToken:this.anonymousPacketToken,apiKey:this.apiKey,hmacSecret:this.hmacSecret,encryptRequests:this.encryptRequests,csrfEnabled:this.csrfEnabled,csrfToken:this.csrfToken,csrfHeaderName:this.csrfHeaderName,refreshCsrfToken:this.csrfEnabled?()=>this.refreshCsrfToken():null}}requestJson(e,r,t,i=!0,s){return h(this._reqOpts,e,r,t,i,s,!1)}requestBinary(e,r,t,i=!0){return this._requestBinary(e,r,t,i)}requestForm(e,r,t,i=!0){return this._requestForm(e,r,t,i)}requestFormBinary(e,r,t,i=!0){return this._requestFormBinary(e,r,t,i)}_request(e,r,t,i=!0,s){return h(this._reqOpts,e,r,t,i,s,!0)}async _requestBinary(e,r,t,i=!0){const s={"Content-Type":"application/json"};i&&this.token&&(s.Authorization=`Bearer ${this.token}`),this.apiKey&&(s["X-API-Key"]=this.apiKey);const n=await fetch(this.baseUrl+r,{method:e,headers:s,...t!=null?{body:JSON.stringify(t)}:{},credentials:"include"});if(!n.ok){const f=await n.text(),a=new Error(`HTTP ${n.status}: ${f}`);throw a.status=n.status,a}return n.arrayBuffer()}async _requestForm(e,r,t,i=!0){const s={};i&&this.token&&(s.Authorization=`Bearer ${this.token}`),this.apiKey&&(s["X-API-Key"]=this.apiKey);const n=await fetch(this.baseUrl+r,{method:e,headers:s,body:t,credentials:"include"}),f=await n.json();if(!f.ok){const a=new Error(f.message??`EntityServer error (HTTP ${n.status})`);throw a.status=n.status,a}return f}async _requestFormBinary(e,r,t,i=!0){const s={};i&&this.token&&(s.Authorization=`Bearer ${this.token}`),this.apiKey&&(s["X-API-Key"]=this.apiKey);const n=await fetch(this.baseUrl+r,{method:e,headers:s,body:t,credentials:"include"});if(!n.ok){const f=await n.text(),a=new Error(`HTTP ${n.status}: ${f}`);throw a.status=n.status,a}return n.arrayBuffer()}}export{d as EntityServerClientBase};
2
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/client/base.ts"],
4
+ "sourcesContent": ["import type {\n EntityServerClientHealthCsrf,\n EntityServerClientOptions,\n} from \"../types.js\";\nimport { readEnv } from \"./utils.js\";\nimport { derivePacketKey, parseRequestBody } from \"./packet.js\";\nimport { entityRequest, type RequestOptions } from \"./request.js\";\n\n// mixin \uD5EC\uD37C \uD0C0\uC785\nexport type GConstructor<T = object> = new (...args: any[]) => T;\n\nexport class EntityServerClientBase {\n baseUrl: string;\n token: string;\n anonymousPacketToken: string;\n apiKey: string;\n hmacSecret: string;\n encryptRequests: boolean;\n csrfEnabled: boolean;\n csrfToken: string;\n csrfHeaderName: string;\n csrfRefreshPath: string;\n csrfRefreshBuffer: number;\n activeTxId: string | null = null;\n\n // \uC138\uC158 \uC720\uC9C0 \uAD00\uB828\n keepSession: boolean;\n refreshBuffer: number;\n onTokenRefreshed?: (accessToken: string, expiresIn: number) => void;\n onSessionExpired?: (error: Error) => void;\n _sessionRefreshToken: string | null = null;\n _refreshTimer: ReturnType<typeof setTimeout> | null = null;\n _csrfRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n _csrfRefreshPromise: Promise<string> | null = null;\n\n // \u2500\u2500\u2500 \uCD08\uAE30\uD654 & \uC124\uC815 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * EntityServerClient \uC778\uC2A4\uD134\uC2A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4.\n *\n * \uAE30\uBCF8\uAC12:\n * - `baseUrl`: `VITE_ENTITY_SERVER_URL` \uB610\uB294 \uC0C1\uB300 \uACBD\uB85C(`\"\"`)\n */\n constructor(options: EntityServerClientOptions = {}) {\n const envBaseUrl = readEnv(\"VITE_ENTITY_SERVER_URL\");\n\n this.baseUrl = (options.baseUrl ?? envBaseUrl ?? \"\").replace(/\\/$/, \"\");\n this.token = options.token ?? \"\";\n this.anonymousPacketToken = options.anonymousPacketToken ?? \"\";\n this.apiKey = options.apiKey ?? \"\";\n this.hmacSecret = options.hmacSecret ?? \"\";\n this.encryptRequests = options.encryptRequests ?? false;\n this.csrfEnabled = options.csrfEnabled ?? false;\n this.csrfToken = options.csrfToken ?? \"\";\n this.csrfHeaderName = options.csrfHeaderName ?? \"x-csrf-token\";\n this.csrfRefreshPath = options.csrfRefreshPath ?? \"/v1/csrf-token\";\n this.csrfRefreshBuffer = options.csrfRefreshBuffer ?? 60;\n this.keepSession = options.keepSession ?? false;\n this.refreshBuffer = options.refreshBuffer ?? 60;\n this.onTokenRefreshed = options.onTokenRefreshed;\n this.onSessionExpired = options.onSessionExpired;\n }\n\n /** baseUrl, token, encryptRequests \uAC12\uC744 \uB7F0\uD0C0\uC784\uC5D0 \uAC31\uC2E0\uD569\uB2C8\uB2E4. */\n configure(options: Partial<EntityServerClientOptions>): void {\n if (typeof options.baseUrl === \"string\") {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n }\n if (typeof options.token === \"string\") this.token = options.token;\n if (typeof options.anonymousPacketToken === \"string\") {\n this.anonymousPacketToken = options.anonymousPacketToken;\n }\n if (typeof options.encryptRequests === \"boolean\")\n this.encryptRequests = options.encryptRequests;\n if (typeof options.csrfEnabled === \"boolean\") {\n this.csrfEnabled = options.csrfEnabled;\n if (!options.csrfEnabled) {\n this.csrfToken = \"\";\n this.stopCsrfRefresh();\n }\n }\n if (typeof options.csrfToken === \"string\")\n this.csrfToken = options.csrfToken;\n if (typeof options.csrfHeaderName === \"string\") {\n this.csrfHeaderName = options.csrfHeaderName;\n }\n if (typeof options.csrfRefreshPath === \"string\") {\n this.csrfRefreshPath = options.csrfRefreshPath;\n }\n if (typeof options.csrfRefreshBuffer === \"number\") {\n this.csrfRefreshBuffer = options.csrfRefreshBuffer;\n }\n if (typeof options.apiKey === \"string\") this.apiKey = options.apiKey;\n if (typeof options.hmacSecret === \"string\")\n this.hmacSecret = options.hmacSecret;\n if (typeof options.keepSession === \"boolean\")\n this.keepSession = options.keepSession;\n if (typeof options.refreshBuffer === \"number\")\n this.refreshBuffer = options.refreshBuffer;\n if (options.onTokenRefreshed)\n this.onTokenRefreshed = options.onTokenRefreshed;\n if (options.onSessionExpired)\n this.onSessionExpired = options.onSessionExpired;\n }\n\n /** \uC778\uC99D \uC694\uCCAD\uC5D0 \uC0AC\uC6A9\uD560 JWT Access Token\uC744 \uC124\uC815\uD569\uB2C8\uB2E4. */\n setToken(token: string): void {\n this.token = token;\n }\n\n /** \uC775\uBA85 \uD328\uD0B7 \uC554\uD638\uD654\uC6A9 \uD1A0\uD070\uC744 \uC124\uC815\uD569\uB2C8\uB2E4. */\n setAnonymousPacketToken(token: string): void {\n this.anonymousPacketToken = token;\n }\n\n /** HMAC \uC778\uC99D\uC6A9 API Key\uB97C \uC124\uC815\uD569\uB2C8\uB2E4. */\n setApiKey(apiKey: string): void {\n this.apiKey = apiKey;\n }\n\n /** HMAC \uC778\uC99D\uC6A9 \uC2DC\uD06C\uB9BF\uC744 \uC124\uC815\uD569\uB2C8\uB2E4. */\n setHmacSecret(secret: string): void {\n this.hmacSecret = secret;\n }\n\n /** \uC554\uD638\uD654 \uC694\uCCAD \uD65C\uC131\uD654 \uC5EC\uBD80\uB97C \uC124\uC815\uD569\uB2C8\uB2E4. */\n setEncryptRequests(value: boolean): void {\n this.encryptRequests = value;\n }\n\n setCsrfToken(token: string): void {\n this.csrfToken = token;\n }\n\n setCsrfEnabled(enabled: boolean): void {\n this.csrfEnabled = enabled;\n if (!enabled) {\n this.csrfToken = \"\";\n this.stopCsrfRefresh();\n }\n }\n\n // \u2500\u2500\u2500 \uC138\uC158 \uC720\uC9C0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** @internal \uC790\uB3D9 \uD1A0\uD070 \uAC31\uC2E0 \uD0C0\uC774\uBA38\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4. */\n _scheduleKeepSession(\n refreshToken: string,\n expiresIn: number,\n refreshFn: (\n rt: string,\n ) => Promise<{ access_token: string; expires_in: number }>,\n ): void {\n this._clearRefreshTimer();\n this._sessionRefreshToken = refreshToken;\n const delayMs = Math.max((expiresIn - this.refreshBuffer) * 1000, 0);\n this._refreshTimer = setTimeout(async () => {\n if (!this._sessionRefreshToken) return;\n try {\n const result = await refreshFn(this._sessionRefreshToken);\n this.onTokenRefreshed?.(result.access_token, result.expires_in);\n this._scheduleKeepSession(\n this._sessionRefreshToken,\n result.expires_in,\n refreshFn,\n );\n } catch (err) {\n this._clearRefreshTimer();\n this.onSessionExpired?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n }\n }, delayMs);\n }\n\n /** @internal \uC790\uB3D9 \uAC31\uC2E0 \uD0C0\uC774\uBA38\uB97C \uC815\uB9AC\uD569\uB2C8\uB2E4. */\n _clearRefreshTimer(): void {\n if (this._refreshTimer !== null) {\n clearTimeout(this._refreshTimer);\n this._refreshTimer = null;\n }\n }\n\n /**\n * \uC138\uC158 \uC720\uC9C0 \uD0C0\uC774\uBA38\uB97C \uC911\uC9C0\uD569\uB2C8\uB2E4.\n * `logout()` \uD638\uCD9C \uC2DC \uC790\uB3D9\uC73C\uB85C \uC911\uC9C0\uB418\uBA70, \uC9C1\uC811 \uD638\uCD9C\uC774 \uD544\uC694\uD55C \uACBD\uC6B0\uB294 \uB4DC\uBB45\uB2C8\uB2E4.\n */\n stopKeepSession(): void {\n this._clearRefreshTimer();\n this._sessionRefreshToken = null;\n }\n\n _clearCsrfRefreshTimer(): void {\n if (this._csrfRefreshTimer !== null) {\n clearTimeout(this._csrfRefreshTimer);\n this._csrfRefreshTimer = null;\n }\n }\n\n stopCsrfRefresh(): void {\n this._clearCsrfRefreshTimer();\n this._csrfRefreshPromise = null;\n }\n\n _scheduleCsrfRefresh(expiresIn: number): void {\n this._clearCsrfRefreshTimer();\n if (!this.csrfEnabled || !this.csrfRefreshPath) {\n return;\n }\n\n const delayMs = Math.max(\n (expiresIn - this.csrfRefreshBuffer) * 1000,\n 0,\n );\n this._csrfRefreshTimer = setTimeout(() => {\n void this.refreshCsrfToken().catch(() => {\n this._clearCsrfRefreshTimer();\n });\n }, delayMs);\n }\n\n async refreshCsrfToken(): Promise<string> {\n if (!this.csrfEnabled || !this.csrfRefreshPath) {\n return \"\";\n }\n\n if (!this._csrfRefreshPromise) {\n this._csrfRefreshPromise = (async () => {\n const res = await fetch(\n `${this.baseUrl}${this.csrfRefreshPath}`,\n {\n method: \"GET\",\n credentials: \"include\",\n },\n );\n\n if (!res.ok) {\n const message = await res.text().catch(() => \"\");\n const err = new Error(message || `HTTP ${res.status}`);\n (err as { status?: number }).status = res.status;\n throw err;\n }\n\n const payload = (await res.json().catch(() => null)) as\n | { data?: EntityServerClientHealthCsrf | null }\n | EntityServerClientHealthCsrf\n | null;\n const csrf = (\n payload && typeof payload === \"object\" && \"data\" in payload\n ? (payload.data ?? null)\n : payload\n ) as EntityServerClientHealthCsrf | null;\n\n if (!csrf?.enabled || typeof csrf.token !== \"string\") {\n throw new Error(\"CSRF token refresh failed\");\n }\n\n this._applyCsrfHealth(csrf);\n return this.csrfToken;\n })().finally(() => {\n this._csrfRefreshPromise = null;\n });\n }\n\n return this._csrfRefreshPromise;\n }\n\n _applyCsrfHealth(csrf?: EntityServerClientHealthCsrf | null): void {\n if (!csrf?.enabled) {\n this.setCsrfEnabled(false);\n return;\n }\n\n this.csrfEnabled = true;\n if (typeof csrf.token === \"string\") {\n this.csrfToken = csrf.token;\n }\n if (typeof csrf.headerName === \"string\") {\n this.csrfHeaderName = csrf.headerName;\n }\n if (typeof csrf.refreshPath === \"string\") {\n this.csrfRefreshPath = csrf.refreshPath;\n }\n if (typeof csrf.expiresIn === \"number\" && csrf.expiresIn > 0) {\n this._scheduleCsrfRefresh(csrf.expiresIn);\n }\n }\n\n // \u2500\u2500\u2500 \uC694\uCCAD \uBCF8\uBB38 \uD30C\uC2F1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * \uC694\uCCAD \uBC14\uB514\uB97C \uD30C\uC2F1\uD569\uB2C8\uB2E4.\n * `application/octet-stream`\uC774\uBA74 XChaCha20-Poly1305 \uBCF5\uD638\uD654, \uADF8 \uC678\uB294 JSON \uD30C\uC2F1\uD569\uB2C8\uB2E4.\n *\n * @param requireEncrypted `true`\uC774\uBA74 \uC554\uD638\uD654\uB41C \uC694\uCCAD\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4.\n */\n readRequestBody<T = Record<string, unknown>>(\n body: ArrayBuffer | Uint8Array | string | T | null | undefined,\n contentType = \"application/json\",\n requireEncrypted = false,\n ): T {\n const key = derivePacketKey(\n this.hmacSecret,\n this.token || this.anonymousPacketToken,\n );\n return parseRequestBody<T>(body, contentType, requireEncrypted, key);\n }\n\n // \u2500\u2500\u2500 \uB0B4\uBD80 \uD5EC\uD37C \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n get _reqOpts(): RequestOptions {\n return {\n baseUrl: this.baseUrl,\n token: this.token,\n anonymousPacketToken: this.anonymousPacketToken,\n apiKey: this.apiKey,\n hmacSecret: this.hmacSecret,\n encryptRequests: this.encryptRequests,\n csrfEnabled: this.csrfEnabled,\n csrfToken: this.csrfToken,\n csrfHeaderName: this.csrfHeaderName,\n refreshCsrfToken: this.csrfEnabled\n ? () => this.refreshCsrfToken()\n : null,\n };\n }\n\n /**\n * \uC784\uC758 \uACBD\uB85C\uC5D0 JSON \uC694\uCCAD\uC744 \uBCF4\uB0C5\uB2C8\uB2E4. \uC751\uB2F5\uC774 JSON\uC774\uBA74 \uD30C\uC2F1, octet-stream\uC774\uBA74 \uC790\uB3D9 \uBCF5\uD638\uD654\uD569\uB2C8\uB2E4.\n * `ok` \uD544\uB4DC\uB97C \uAC15\uC81C\uD558\uC9C0 \uC54A\uC544 go\uC11C\uBC84/\uC571\uC11C\uBC84 \uC2E0\uADDC \uB77C\uC6B0\uD2B8 \uB4F1 \uC790\uC720 \uC751\uB2F5 \uD3EC\uB9F7\uC5D0 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n * `encryptRequests: true`\uC774\uBA74 \uC694\uCCAD \uBC14\uB514\uB3C4 \uC790\uB3D9 \uC554\uD638\uD654\uB429\uB2C8\uB2E4.\n */\n requestJson<T>(\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n return entityRequest<T>(\n this._reqOpts,\n method,\n path,\n body,\n withAuth,\n extraHeaders,\n false,\n );\n }\n\n /**\n * \uC784\uC758 \uACBD\uB85C\uC5D0 \uC694\uCCAD\uC744 \uBCF4\uB0B4\uACE0 \uBC14\uC774\uB108\uB9AC(ArrayBuffer)\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4.\n * \uC774\uBBF8\uC9C0, PDF, \uC555\uCD95 \uD30C\uC77C \uB4F1 \uBC14\uC774\uB108\uB9AC \uC751\uB2F5\uC774 \uC624\uB294 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC5D0 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n */\n requestBinary(\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n ): Promise<ArrayBuffer> {\n return this._requestBinary(method, path, body, withAuth);\n }\n\n /**\n * multipart/form-data \uC694\uCCAD\uC744 \uBCF4\uB0C5\uB2C8\uB2E4. \uD30C\uC77C \uC5C5\uB85C\uB4DC \uB4F1\uC5D0 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n * \uC751\uB2F5\uC740 JSON\uC73C\uB85C \uD30C\uC2F1\uD558\uC5EC \uBC18\uD658\uD569\uB2C8\uB2E4.\n */\n requestForm<T>(\n method: string,\n path: string,\n form: FormData,\n withAuth = true,\n ): Promise<T> {\n return this._requestForm<T>(method, path, form, withAuth);\n }\n\n /**\n * multipart/form-data \uC694\uCCAD\uC744 \uBCF4\uB0B4\uACE0 \uBC14\uC774\uB108\uB9AC(ArrayBuffer)\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4.\n */\n requestFormBinary(\n method: string,\n path: string,\n form: FormData,\n withAuth = true,\n ): Promise<ArrayBuffer> {\n return this._requestFormBinary(method, path, form, withAuth);\n }\n\n _request<T>(\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n return entityRequest<T>(\n this._reqOpts,\n method,\n path,\n body,\n withAuth,\n extraHeaders,\n true,\n );\n }\n\n /** PNG/\uBC14\uC774\uB108\uB9AC \uC751\uB2F5\uC744 ArrayBuffer\uB85C \uBC18\uD658\uD569\uB2C8\uB2E4. (QR, \uBC14\uCF54\uB4DC \uB4F1) */\n async _requestBinary(\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n ): Promise<ArrayBuffer> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (withAuth && this.token)\n headers[\"Authorization\"] = `Bearer ${this.token}`;\n if (this.apiKey) headers[\"X-API-Key\"] = this.apiKey;\n\n const res = await fetch(this.baseUrl + path, {\n method,\n headers,\n ...(body != null ? { body: JSON.stringify(body) } : {}),\n credentials: \"include\",\n });\n\n if (!res.ok) {\n const text = await res.text();\n const err = new Error(`HTTP ${res.status}: ${text}`);\n (err as { status?: number }).status = res.status;\n throw err;\n }\n\n return res.arrayBuffer();\n }\n\n /** multipart/form-data \uC694\uCCAD\uC744 \uBCF4\uB0C5\uB2C8\uB2E4. (\uD30C\uC77C \uC5C5\uB85C\uB4DC \uB4F1) */\n async _requestForm<T>(\n method: string,\n path: string,\n form: FormData,\n withAuth = true,\n ): Promise<T> {\n const headers: Record<string, string> = {};\n if (withAuth && this.token)\n headers[\"Authorization\"] = `Bearer ${this.token}`;\n if (this.apiKey) headers[\"X-API-Key\"] = this.apiKey;\n\n const res = await fetch(this.baseUrl + path, {\n method,\n headers,\n body: form,\n credentials: \"include\",\n });\n\n const data = (await res.json()) as { ok?: boolean; message?: string };\n if (!data.ok) {\n const err = new Error(\n data.message ?? `EntityServer error (HTTP ${res.status})`,\n );\n (err as { status?: number }).status = res.status;\n throw err;\n }\n return data as T;\n }\n\n /** multipart/form-data \uC694\uCCAD\uC744 \uBCF4\uB0B4\uACE0 \uBC14\uC774\uB108\uB9AC(ArrayBuffer)\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4. */\n async _requestFormBinary(\n method: string,\n path: string,\n form: FormData,\n withAuth = true,\n ): Promise<ArrayBuffer> {\n const headers: Record<string, string> = {};\n if (withAuth && this.token)\n headers[\"Authorization\"] = `Bearer ${this.token}`;\n if (this.apiKey) headers[\"X-API-Key\"] = this.apiKey;\n\n const res = await fetch(this.baseUrl + path, {\n method,\n headers,\n body: form,\n credentials: \"include\",\n });\n\n if (!res.ok) {\n const text = await res.text();\n const err = new Error(`HTTP ${res.status}: ${text}`);\n (err as { status?: number }).status = res.status;\n throw err;\n }\n\n return res.arrayBuffer();\n }\n}\n"],
5
+ "mappings": "AAIA,OAAS,WAAAA,MAAe,aACxB,OAAS,mBAAAC,EAAiB,oBAAAC,MAAwB,cAClD,OAAS,iBAAAC,MAA0C,eAK5C,MAAMC,CAAuB,CAChC,QACA,MACA,qBACA,OACA,WACA,gBACA,YACA,UACA,eACA,gBACA,kBACA,WAA4B,KAG5B,YACA,cACA,iBACA,iBACA,qBAAsC,KACtC,cAAsD,KACtD,kBAA0D,KAC1D,oBAA8C,KAU9C,YAAYC,EAAqC,CAAC,EAAG,CACjD,MAAMC,EAAaN,EAAQ,wBAAwB,EAEnD,KAAK,SAAWK,EAAQ,SAAWC,GAAc,IAAI,QAAQ,MAAO,EAAE,EACtE,KAAK,MAAQD,EAAQ,OAAS,GAC9B,KAAK,qBAAuBA,EAAQ,sBAAwB,GAC5D,KAAK,OAASA,EAAQ,QAAU,GAChC,KAAK,WAAaA,EAAQ,YAAc,GACxC,KAAK,gBAAkBA,EAAQ,iBAAmB,GAClD,KAAK,YAAcA,EAAQ,aAAe,GAC1C,KAAK,UAAYA,EAAQ,WAAa,GACtC,KAAK,eAAiBA,EAAQ,gBAAkB,eAChD,KAAK,gBAAkBA,EAAQ,iBAAmB,iBAClD,KAAK,kBAAoBA,EAAQ,mBAAqB,GACtD,KAAK,YAAcA,EAAQ,aAAe,GAC1C,KAAK,cAAgBA,EAAQ,eAAiB,GAC9C,KAAK,iBAAmBA,EAAQ,iBAChC,KAAK,iBAAmBA,EAAQ,gBACpC,CAGA,UAAUA,EAAmD,CACrD,OAAOA,EAAQ,SAAY,WAC3B,KAAK,QAAUA,EAAQ,QAAQ,QAAQ,MAAO,EAAE,GAEhD,OAAOA,EAAQ,OAAU,WAAU,KAAK,MAAQA,EAAQ,OACxD,OAAOA,EAAQ,sBAAyB,WACxC,KAAK,qBAAuBA,EAAQ,sBAEpC,OAAOA,EAAQ,iBAAoB,YACnC,KAAK,gBAAkBA,EAAQ,iBAC/B,OAAOA,EAAQ,aAAgB,YAC/B,KAAK,YAAcA,EAAQ,YACtBA,EAAQ,cACT,KAAK,UAAY,GACjB,KAAK,gBAAgB,IAGzB,OAAOA,EAAQ,WAAc,WAC7B,KAAK,UAAYA,EAAQ,WACzB,OAAOA,EAAQ,gBAAmB,WAClC,KAAK,eAAiBA,EAAQ,gBAE9B,OAAOA,EAAQ,iBAAoB,WACnC,KAAK,gBAAkBA,EAAQ,iBAE/B,OAAOA,EAAQ,mBAAsB,WACrC,KAAK,kBAAoBA,EAAQ,mBAEjC,OAAOA,EAAQ,QAAW,WAAU,KAAK,OAASA,EAAQ,QAC1D,OAAOA,EAAQ,YAAe,WAC9B,KAAK,WAAaA,EAAQ,YAC1B,OAAOA,EAAQ,aAAgB,YAC/B,KAAK,YAAcA,EAAQ,aAC3B,OAAOA,EAAQ,eAAkB,WACjC,KAAK,cAAgBA,EAAQ,eAC7BA,EAAQ,mBACR,KAAK,iBAAmBA,EAAQ,kBAChCA,EAAQ,mBACR,KAAK,iBAAmBA,EAAQ,iBACxC,CAGA,SAASE,EAAqB,CAC1B,KAAK,MAAQA,CACjB,CAGA,wBAAwBA,EAAqB,CACzC,KAAK,qBAAuBA,CAChC,CAGA,UAAUC,EAAsB,CAC5B,KAAK,OAASA,CAClB,CAGA,cAAcC,EAAsB,CAChC,KAAK,WAAaA,CACtB,CAGA,mBAAmBC,EAAsB,CACrC,KAAK,gBAAkBA,CAC3B,CAEA,aAAaH,EAAqB,CAC9B,KAAK,UAAYA,CACrB,CAEA,eAAeI,EAAwB,CACnC,KAAK,YAAcA,EACdA,IACD,KAAK,UAAY,GACjB,KAAK,gBAAgB,EAE7B,CAKA,qBACIC,EACAC,EACAC,EAGI,CACJ,KAAK,mBAAmB,EACxB,KAAK,qBAAuBF,EAC5B,MAAMG,EAAU,KAAK,KAAKF,EAAY,KAAK,eAAiB,IAAM,CAAC,EACnE,KAAK,cAAgB,WAAW,SAAY,CACxC,GAAK,KAAK,qBACV,GAAI,CACA,MAAMG,EAAS,MAAMF,EAAU,KAAK,oBAAoB,EACxD,KAAK,mBAAmBE,EAAO,aAAcA,EAAO,UAAU,EAC9D,KAAK,qBACD,KAAK,qBACLA,EAAO,WACPF,CACJ,CACJ,OAASG,EAAK,CACV,KAAK,mBAAmB,EACxB,KAAK,mBACDA,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CACtD,CACJ,CACJ,EAAGF,CAAO,CACd,CAGA,oBAA2B,CACnB,KAAK,gBAAkB,OACvB,aAAa,KAAK,aAAa,EAC/B,KAAK,cAAgB,KAE7B,CAMA,iBAAwB,CACpB,KAAK,mBAAmB,EACxB,KAAK,qBAAuB,IAChC,CAEA,wBAA+B,CACvB,KAAK,oBAAsB,OAC3B,aAAa,KAAK,iBAAiB,EACnC,KAAK,kBAAoB,KAEjC,CAEA,iBAAwB,CACpB,KAAK,uBAAuB,EAC5B,KAAK,oBAAsB,IAC/B,CAEA,qBAAqBF,EAAyB,CAE1C,GADA,KAAK,uBAAuB,EACxB,CAAC,KAAK,aAAe,CAAC,KAAK,gBAC3B,OAGJ,MAAME,EAAU,KAAK,KAChBF,EAAY,KAAK,mBAAqB,IACvC,CACJ,EACA,KAAK,kBAAoB,WAAW,IAAM,CACjC,KAAK,iBAAiB,EAAE,MAAM,IAAM,CACrC,KAAK,uBAAuB,CAChC,CAAC,CACL,EAAGE,CAAO,CACd,CAEA,MAAM,kBAAoC,CACtC,MAAI,CAAC,KAAK,aAAe,CAAC,KAAK,gBACpB,IAGN,KAAK,sBACN,KAAK,qBAAuB,SAAY,CACpC,MAAMG,EAAM,MAAM,MACd,GAAG,KAAK,OAAO,GAAG,KAAK,eAAe,GACtC,CACI,OAAQ,MACR,YAAa,SACjB,CACJ,EAEA,GAAI,CAACA,EAAI,GAAI,CACT,MAAMC,EAAU,MAAMD,EAAI,KAAK,EAAE,MAAM,IAAM,EAAE,EACzCD,EAAM,IAAI,MAAME,GAAW,QAAQD,EAAI,MAAM,EAAE,EACrD,MAACD,EAA4B,OAASC,EAAI,OACpCD,CACV,CAEA,MAAMG,EAAW,MAAMF,EAAI,KAAK,EAAE,MAAM,IAAM,IAAI,EAI5CG,EACFD,GAAW,OAAOA,GAAY,UAAY,SAAUA,EAC7CA,EAAQ,MAAQ,KACjBA,EAGV,GAAI,CAACC,GAAM,SAAW,OAAOA,EAAK,OAAU,SACxC,MAAM,IAAI,MAAM,2BAA2B,EAG/C,YAAK,iBAAiBA,CAAI,EACnB,KAAK,SAChB,GAAG,EAAE,QAAQ,IAAM,CACf,KAAK,oBAAsB,IAC/B,CAAC,GAGE,KAAK,oBAChB,CAEA,iBAAiBA,EAAkD,CAC/D,GAAI,CAACA,GAAM,QAAS,CAChB,KAAK,eAAe,EAAK,EACzB,MACJ,CAEA,KAAK,YAAc,GACf,OAAOA,EAAK,OAAU,WACtB,KAAK,UAAYA,EAAK,OAEtB,OAAOA,EAAK,YAAe,WAC3B,KAAK,eAAiBA,EAAK,YAE3B,OAAOA,EAAK,aAAgB,WAC5B,KAAK,gBAAkBA,EAAK,aAE5B,OAAOA,EAAK,WAAc,UAAYA,EAAK,UAAY,GACvD,KAAK,qBAAqBA,EAAK,SAAS,CAEhD,CAUA,gBACIC,EACAC,EAAc,mBACdC,EAAmB,GAClB,CACD,MAAMC,EAAMxB,EACR,KAAK,WACL,KAAK,OAAS,KAAK,oBACvB,EACA,OAAOC,EAAoBoB,EAAMC,EAAaC,EAAkBC,CAAG,CACvE,CAIA,IAAI,UAA2B,CAC3B,MAAO,CACH,QAAS,KAAK,QACd,MAAO,KAAK,MACZ,qBAAsB,KAAK,qBAC3B,OAAQ,KAAK,OACb,WAAY,KAAK,WACjB,gBAAiB,KAAK,gBACtB,YAAa,KAAK,YAClB,UAAW,KAAK,UAChB,eAAgB,KAAK,eACrB,iBAAkB,KAAK,YACjB,IAAM,KAAK,iBAAiB,EAC5B,IACV,CACJ,CAOA,YACIC,EACAC,EACAL,EACAM,EAAW,GACXC,EACU,CACV,OAAO1B,EACH,KAAK,SACLuB,EACAC,EACAL,EACAM,EACAC,EACA,EACJ,CACJ,CAMA,cACIH,EACAC,EACAL,EACAM,EAAW,GACS,CACpB,OAAO,KAAK,eAAeF,EAAQC,EAAML,EAAMM,CAAQ,CAC3D,CAMA,YACIF,EACAC,EACAG,EACAF,EAAW,GACD,CACV,OAAO,KAAK,aAAgBF,EAAQC,EAAMG,EAAMF,CAAQ,CAC5D,CAKA,kBACIF,EACAC,EACAG,EACAF,EAAW,GACS,CACpB,OAAO,KAAK,mBAAmBF,EAAQC,EAAMG,EAAMF,CAAQ,CAC/D,CAEA,SACIF,EACAC,EACAL,EACAM,EAAW,GACXC,EACU,CACV,OAAO1B,EACH,KAAK,SACLuB,EACAC,EACAL,EACAM,EACAC,EACA,EACJ,CACJ,CAGA,MAAM,eACFH,EACAC,EACAL,EACAM,EAAW,GACS,CACpB,MAAMG,EAAkC,CACpC,eAAgB,kBACpB,EACIH,GAAY,KAAK,QACjBG,EAAQ,cAAmB,UAAU,KAAK,KAAK,IAC/C,KAAK,SAAQA,EAAQ,WAAW,EAAI,KAAK,QAE7C,MAAMb,EAAM,MAAM,MAAM,KAAK,QAAUS,EAAM,CACzC,OAAAD,EACA,QAAAK,EACA,GAAIT,GAAQ,KAAO,CAAE,KAAM,KAAK,UAAUA,CAAI,CAAE,EAAI,CAAC,EACrD,YAAa,SACjB,CAAC,EAED,GAAI,CAACJ,EAAI,GAAI,CACT,MAAMc,EAAO,MAAMd,EAAI,KAAK,EACtBD,EAAM,IAAI,MAAM,QAAQC,EAAI,MAAM,KAAKc,CAAI,EAAE,EACnD,MAACf,EAA4B,OAASC,EAAI,OACpCD,CACV,CAEA,OAAOC,EAAI,YAAY,CAC3B,CAGA,MAAM,aACFQ,EACAC,EACAG,EACAF,EAAW,GACD,CACV,MAAMG,EAAkC,CAAC,EACrCH,GAAY,KAAK,QACjBG,EAAQ,cAAmB,UAAU,KAAK,KAAK,IAC/C,KAAK,SAAQA,EAAQ,WAAW,EAAI,KAAK,QAE7C,MAAMb,EAAM,MAAM,MAAM,KAAK,QAAUS,EAAM,CACzC,OAAAD,EACA,QAAAK,EACA,KAAMD,EACN,YAAa,SACjB,CAAC,EAEKG,EAAQ,MAAMf,EAAI,KAAK,EAC7B,GAAI,CAACe,EAAK,GAAI,CACV,MAAMhB,EAAM,IAAI,MACZgB,EAAK,SAAW,4BAA4Bf,EAAI,MAAM,GAC1D,EACA,MAACD,EAA4B,OAASC,EAAI,OACpCD,CACV,CACA,OAAOgB,CACX,CAGA,MAAM,mBACFP,EACAC,EACAG,EACAF,EAAW,GACS,CACpB,MAAMG,EAAkC,CAAC,EACrCH,GAAY,KAAK,QACjBG,EAAQ,cAAmB,UAAU,KAAK,KAAK,IAC/C,KAAK,SAAQA,EAAQ,WAAW,EAAI,KAAK,QAE7C,MAAMb,EAAM,MAAM,MAAM,KAAK,QAAUS,EAAM,CACzC,OAAAD,EACA,QAAAK,EACA,KAAMD,EACN,YAAa,SACjB,CAAC,EAED,GAAI,CAACZ,EAAI,GAAI,CACT,MAAMc,EAAO,MAAMd,EAAI,KAAK,EACtBD,EAAM,IAAI,MAAM,QAAQC,EAAI,MAAM,KAAKc,CAAI,EAAE,EACnD,MAACf,EAA4B,OAASC,EAAI,OACpCD,CACV,CAEA,OAAOC,EAAI,YAAY,CAC3B,CACJ",
6
+ "names": ["readEnv", "derivePacketKey", "parseRequestBody", "entityRequest", "EntityServerClientBase", "options", "envBaseUrl", "token", "apiKey", "secret", "value", "enabled", "refreshToken", "expiresIn", "refreshFn", "delayMs", "result", "err", "res", "message", "payload", "csrf", "body", "contentType", "requireEncrypted", "key", "method", "path", "withAuth", "extraHeaders", "form", "headers", "text", "data"]
7
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * HMAC-SHA256 서명 헤더를 생성합니다.
3
+ *
4
+ * 서명 대상: `METHOD|PATH|TIMESTAMP|NONCE|BODY`
5
+ *
6
+ * @returns `X-API-Key`, `X-Timestamp`, `X-Nonce`, `X-Signature` 헤더 객체
7
+ */
8
+ export declare function buildHmacHeaders(method: string, path: string, bodyBytes: Uint8Array, apiKey: string, hmacSecret: string): Record<string, string>;
@@ -0,0 +1,2 @@
1
+ import{sha256 as p}from"@noble/hashes/sha2";import{hmac as d}from"@noble/hashes/hmac";function u(i,s,r,a,c){const e=String(Math.floor(Date.now()/1e3)),o=crypto.randomUUID(),t=new TextEncoder().encode(`${i}|${s}|${e}|${o}|`),n=new Uint8Array(t.length+r.length);n.set(t,0),n.set(r,t.length);const g=[...d(p,new TextEncoder().encode(c),n)].map(m=>m.toString(16).padStart(2,"0")).join("");return{"X-API-Key":a,"X-Timestamp":e,"X-Nonce":o,"X-Signature":g}}export{u as buildHmacHeaders};
2
+ //# sourceMappingURL=hmac.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/client/hmac.ts"],
4
+ "sourcesContent": ["// @ts-ignore\nimport { sha256 } from \"@noble/hashes/sha2\";\n// @ts-ignore\nimport { hmac } from \"@noble/hashes/hmac\";\n\n/**\n * HMAC-SHA256 \uC11C\uBA85 \uD5E4\uB354\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4.\n *\n * \uC11C\uBA85 \uB300\uC0C1: `METHOD|PATH|TIMESTAMP|NONCE|BODY`\n *\n * @returns `X-API-Key`, `X-Timestamp`, `X-Nonce`, `X-Signature` \uD5E4\uB354 \uAC1D\uCCB4\n */\nexport function buildHmacHeaders(\n method: string,\n path: string,\n bodyBytes: Uint8Array,\n apiKey: string,\n hmacSecret: string,\n): Record<string, string> {\n const timestamp = String(Math.floor(Date.now() / 1000));\n const nonce = crypto.randomUUID();\n\n const prefix = new TextEncoder().encode(\n `${method}|${path}|${timestamp}|${nonce}|`,\n );\n const payload = new Uint8Array(prefix.length + bodyBytes.length);\n payload.set(prefix, 0);\n payload.set(bodyBytes, prefix.length);\n\n const sig = hmac(sha256, new TextEncoder().encode(hmacSecret), payload);\n const signature = [...sig]\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return {\n \"X-API-Key\": apiKey,\n \"X-Timestamp\": timestamp,\n \"X-Nonce\": nonce,\n \"X-Signature\": signature,\n };\n}\n"],
5
+ "mappings": "AACA,OAAS,UAAAA,MAAc,qBAEvB,OAAS,QAAAC,MAAY,qBASd,SAASC,EACZC,EACAC,EACAC,EACAC,EACAC,EACsB,CACtB,MAAMC,EAAY,OAAO,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAAC,EAChDC,EAAQ,OAAO,WAAW,EAE1BC,EAAS,IAAI,YAAY,EAAE,OAC7B,GAAGP,CAAM,IAAIC,CAAI,IAAII,CAAS,IAAIC,CAAK,GAC3C,EACME,EAAU,IAAI,WAAWD,EAAO,OAASL,EAAU,MAAM,EAC/DM,EAAQ,IAAID,EAAQ,CAAC,EACrBC,EAAQ,IAAIN,EAAWK,EAAO,MAAM,EAGpC,MAAME,EAAY,CAAC,GADPX,EAAKD,EAAQ,IAAI,YAAY,EAAE,OAAOO,CAAU,EAAGI,CAAO,CAC7C,EACpB,IAAKE,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,EAEZ,MAAO,CACH,YAAaP,EACb,cAAeE,EACf,UAAWC,EACX,cAAeG,CACnB,CACJ",
6
+ "names": ["sha256", "hmac", "buildHmacHeaders", "method", "path", "bodyBytes", "apiKey", "hmacSecret", "timestamp", "nonce", "prefix", "payload", "signature", "b"]
7
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 패킷 암호화 키를 유도합니다.
3
+ * - HMAC 모드 (`hmacSecret` 유효 시): HKDF-SHA256(hmac_secret, "entity-server:packet-encryption")
4
+ * - JWT 모드: HKDF-SHA256(jwt_token, "entity-server:packet-encryption")
5
+ */
6
+ export declare function derivePacketKey(hmacSecret: string, token: string): Uint8Array;
7
+ /**
8
+ * 평문 바이트를 XChaCha20-Poly1305로 암호화합니다.
9
+ * 포맷: [random_magic:K][random_nonce:24][ciphertext+tag]
10
+ * K = 2 + key[31] % 14 (패킷 키에서 자동 파생)
11
+ */
12
+ export declare function encryptPacket(plaintext: Uint8Array, key: Uint8Array): Uint8Array;
13
+ /**
14
+ * XChaCha20-Poly1305 패킷을 복호화해 JSON 객체로 변환합니다.
15
+ * 포맷: [magic:K][nonce:24][ciphertext+tag]
16
+ * K = 2 + key[31] % 14 (패킷 키에서 자동 파생)
17
+ */
18
+ export declare function decryptPacket<T>(buffer: ArrayBuffer, key: Uint8Array): T;
19
+ /**
20
+ * 요청 바디를 파싱합니다. `application/octet-stream`이면 복호화, 그 외는 JSON 파싱합니다.
21
+ *
22
+ * @param requireEncrypted `true`이면 암호화된 요청만 허용합니다.
23
+ */
24
+ export declare function parseRequestBody<T>(body: ArrayBuffer | Uint8Array | string | T | null | undefined, contentType: string, requireEncrypted: boolean, key: Uint8Array): T;
@@ -0,0 +1,2 @@
1
+ import{derivePacketKey as s,encryptPacket as f,decryptPacket as u}from"../packet.js";function p(r,e){return s(r||e)}function y(r,e){return f(r,e)}function i(r,e){const t=u(r,e);return JSON.parse(new TextDecoder().decode(t))}function A(r,e,t,n){const a=e.toLowerCase().includes("application/octet-stream");if(t&&!a)throw new Error("Encrypted request required: Content-Type must be application/octet-stream");if(a){if(r==null)throw new Error("Encrypted request body is empty");if(r instanceof ArrayBuffer)return i(r,n);if(r instanceof Uint8Array){const c=r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength);return i(c,n)}throw new Error("Encrypted request body must be ArrayBuffer or Uint8Array")}return r==null||r===""?{}:typeof r=="string"?JSON.parse(r):r}export{i as decryptPacket,p as derivePacketKey,y as encryptPacket,A as parseRequestBody};
2
+ //# sourceMappingURL=packet.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/client/packet.ts"],
4
+ "sourcesContent": ["import {\n derivePacketKey as derivePacketKeyCore,\n encryptPacket as encryptPacketCore,\n decryptPacket as decryptPacketCore,\n} from \"../packet.js\";\n\n/**\n * \uD328\uD0B7 \uC554\uD638\uD654 \uD0A4\uB97C \uC720\uB3C4\uD569\uB2C8\uB2E4.\n * - HMAC \uBAA8\uB4DC (`hmacSecret` \uC720\uD6A8 \uC2DC): HKDF-SHA256(hmac_secret, \"entity-server:packet-encryption\")\n * - JWT \uBAA8\uB4DC: HKDF-SHA256(jwt_token, \"entity-server:packet-encryption\")\n */\nexport function derivePacketKey(hmacSecret: string, token: string): Uint8Array {\n return derivePacketKeyCore(hmacSecret || token);\n}\n\n/**\n * \uD3C9\uBB38 \uBC14\uC774\uD2B8\uB97C XChaCha20-Poly1305\uB85C \uC554\uD638\uD654\uD569\uB2C8\uB2E4.\n * \uD3EC\uB9F7: [random_magic:K][random_nonce:24][ciphertext+tag]\n * K = 2 + key[31] % 14 (\uD328\uD0B7 \uD0A4\uC5D0\uC11C \uC790\uB3D9 \uD30C\uC0DD)\n */\nexport function encryptPacket(\n plaintext: Uint8Array,\n key: Uint8Array,\n): Uint8Array {\n return encryptPacketCore(plaintext, key);\n}\n\n/**\n * XChaCha20-Poly1305 \uD328\uD0B7\uC744 \uBCF5\uD638\uD654\uD574 JSON \uAC1D\uCCB4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4.\n * \uD3EC\uB9F7: [magic:K][nonce:24][ciphertext+tag]\n * K = 2 + key[31] % 14 (\uD328\uD0B7 \uD0A4\uC5D0\uC11C \uC790\uB3D9 \uD30C\uC0DD)\n */\nexport function decryptPacket<T>(buffer: ArrayBuffer, key: Uint8Array): T {\n const plaintext = decryptPacketCore(buffer, key);\n return JSON.parse(new TextDecoder().decode(plaintext)) as T;\n}\n\n/**\n * \uC694\uCCAD \uBC14\uB514\uB97C \uD30C\uC2F1\uD569\uB2C8\uB2E4. `application/octet-stream`\uC774\uBA74 \uBCF5\uD638\uD654, \uADF8 \uC678\uB294 JSON \uD30C\uC2F1\uD569\uB2C8\uB2E4.\n *\n * @param requireEncrypted `true`\uC774\uBA74 \uC554\uD638\uD654\uB41C \uC694\uCCAD\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4.\n */\nexport function parseRequestBody<T>(\n body: ArrayBuffer | Uint8Array | string | T | null | undefined,\n contentType: string,\n requireEncrypted: boolean,\n key: Uint8Array,\n): T {\n const isEncrypted = contentType\n .toLowerCase()\n .includes(\"application/octet-stream\");\n\n if (requireEncrypted && !isEncrypted) {\n throw new Error(\n \"Encrypted request required: Content-Type must be application/octet-stream\",\n );\n }\n\n if (isEncrypted) {\n if (body == null) throw new Error(\"Encrypted request body is empty\");\n if (body instanceof ArrayBuffer) return decryptPacket<T>(body, key);\n if (body instanceof Uint8Array) {\n const sliced = body.buffer.slice(\n body.byteOffset,\n body.byteOffset + body.byteLength,\n );\n return decryptPacket<T>(sliced as ArrayBuffer, key);\n }\n throw new Error(\n \"Encrypted request body must be ArrayBuffer or Uint8Array\",\n );\n }\n\n if (body == null || body === \"\") return {} as T;\n if (typeof body === \"string\") return JSON.parse(body) as T;\n return body as T;\n}\n"],
5
+ "mappings": "AAAA,OACI,mBAAmBA,EACnB,iBAAiBC,EACjB,iBAAiBC,MACd,eAOA,SAASC,EAAgBC,EAAoBC,EAA2B,CAC3E,OAAOL,EAAoBI,GAAcC,CAAK,CAClD,CAOO,SAASC,EACZC,EACAC,EACU,CACV,OAAOP,EAAkBM,EAAWC,CAAG,CAC3C,CAOO,SAASC,EAAiBC,EAAqBF,EAAoB,CACtE,MAAMD,EAAYL,EAAkBQ,EAAQF,CAAG,EAC/C,OAAO,KAAK,MAAM,IAAI,YAAY,EAAE,OAAOD,CAAS,CAAC,CACzD,CAOO,SAASI,EACZC,EACAC,EACAC,EACAN,EACC,CACD,MAAMO,EAAcF,EACf,YAAY,EACZ,SAAS,0BAA0B,EAExC,GAAIC,GAAoB,CAACC,EACrB,MAAM,IAAI,MACN,2EACJ,EAGJ,GAAIA,EAAa,CACb,GAAIH,GAAQ,KAAM,MAAM,IAAI,MAAM,iCAAiC,EACnE,GAAIA,aAAgB,YAAa,OAAOH,EAAiBG,EAAMJ,CAAG,EAClE,GAAII,aAAgB,WAAY,CAC5B,MAAMI,EAASJ,EAAK,OAAO,MACvBA,EAAK,WACLA,EAAK,WAAaA,EAAK,UAC3B,EACA,OAAOH,EAAiBO,EAAuBR,CAAG,CACtD,CACA,MAAM,IAAI,MACN,0DACJ,CACJ,CAEA,OAAII,GAAQ,MAAQA,IAAS,GAAW,CAAC,EACrC,OAAOA,GAAS,SAAiB,KAAK,MAAMA,CAAI,EAC7CA,CACX",
6
+ "names": ["derivePacketKeyCore", "encryptPacketCore", "decryptPacketCore", "derivePacketKey", "hmacSecret", "token", "encryptPacket", "plaintext", "key", "decryptPacket", "buffer", "parseRequestBody", "body", "contentType", "requireEncrypted", "isEncrypted", "sliced"]
7
+ }
@@ -0,0 +1,20 @@
1
+ export interface RequestOptions {
2
+ baseUrl: string;
3
+ token: string;
4
+ anonymousPacketToken: string;
5
+ apiKey: string;
6
+ hmacSecret: string;
7
+ encryptRequests: boolean;
8
+ csrfEnabled: boolean;
9
+ csrfToken: string;
10
+ csrfHeaderName: string;
11
+ refreshCsrfToken: (() => Promise<string>) | null;
12
+ }
13
+ /**
14
+ * Entity Server에 HTTP 요청을 보냅니다.
15
+ *
16
+ * - `encryptRequests` 활성화 시 인증된 POST 바디를 자동 암호화합니다.
17
+ * - 응답이 `application/octet-stream`이면 자동 복호화합니다.
18
+ * - JSON 응답의 `ok`가 false이면 에러를 던집니다.
19
+ */
20
+ export declare function entityRequest<T>(opts: RequestOptions, method: string, path: string, body?: unknown, withAuth?: boolean, extraHeaders?: Record<string, string>, requireOkShape?: boolean): Promise<T>;
@@ -0,0 +1,2 @@
1
+ import{derivePacketKey as E,encryptPacket as U,decryptPacket as N}from"./packet.js";import{buildHmacHeaders as B}from"./hmac.js";function v(e){return e.hmacSecret||e.token||e.anonymousPacketToken}function K(e){return e!=="GET"&&e!=="HEAD"&&e!=="OPTIONS"}function $(e,r){return e===403&&/csrf/i.test(r)?!0:/csrf/i.test(r)&&/expired|token validation failed/i.test(r)}async function P(e){if((e.headers.get("Content-Type")??"").includes("application/json")){const a=await e.json().catch(()=>null);if(a?.error)return a.error;if(a?.message)return a.message}return await e.text().catch(()=>"")||`HTTP ${e.status}`}async function I(e,r,y,a,k=!0,R={},h=!0){const{baseUrl:H,token:i,apiKey:T,hmacSecret:u,encryptRequests:q,anonymousPacketToken:f,csrfEnabled:x,csrfHeaderName:S,refreshCsrfToken:l}=e,g=k&&!!(T&&u),C=v(e),d=x&&K(r)&&!g;let c=e.csrfToken,m="application/json";const O=!i&&!g&&!!f;let o=null;if(a!=null)if(q&&!!C&&r!=="GET"&&r!=="HEAD"){const s=E(u,i||f);o=U(new TextEncoder().encode(JSON.stringify(a)),s),m="application/octet-stream"}else o=JSON.stringify(a);const j=t=>{const s={"Content-Type":m,...R};if(!g&&k&&i&&(s.Authorization=`Bearer ${i}`),O&&(s["X-Packet-Token"]=f),d&&t&&(s[S]=t),g){const A=o instanceof Uint8Array?o:typeof o=="string"?new TextEncoder().encode(o):new Uint8Array(0);Object.assign(s,B(r,y,A,T,u))}return s};d&&!c&&l&&(c=await l());const w=t=>fetch(H+y,{method:r,headers:j(t),...o!=null?{body:o}:{},credentials:"include"});let n=await w(c);if(!n.ok){const t=await P(n.clone());if(d&&l&&$(n.status,t))c=await l(),n=await w(c);else{const s=new Error(t);throw s.status=n.status,s}}if(!n.ok){const t=new Error(await P(n));throw t.status=n.status,t}const b=n.headers.get("Content-Type")??"";if(b.includes("application/octet-stream")){const t=E(u,i||f);return N(await n.arrayBuffer(),t)}if(!b.includes("application/json"))return await n.text();const p=await n.json();if(h&&!p.ok){const t=new Error(p.message??`EntityServer error (HTTP ${n.status})`);throw t.status=n.status,t}return p}export{I as entityRequest};
2
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/client/request.ts"],
4
+ "sourcesContent": ["import { derivePacketKey, encryptPacket, decryptPacket } from \"./packet.js\";\nimport { buildHmacHeaders } from \"./hmac.js\";\n\nexport interface RequestOptions {\n baseUrl: string;\n token: string;\n anonymousPacketToken: string;\n apiKey: string;\n hmacSecret: string;\n encryptRequests: boolean;\n csrfEnabled: boolean;\n csrfToken: string;\n csrfHeaderName: string;\n refreshCsrfToken: (() => Promise<string>) | null;\n}\n\nfunction resolvePacketSource(opts: RequestOptions): string {\n return opts.hmacSecret || opts.token || opts.anonymousPacketToken;\n}\n\nfunction requiresCsrf(method: string): boolean {\n return method !== \"GET\" && method !== \"HEAD\" && method !== \"OPTIONS\";\n}\n\nfunction isCsrfError(status: number, message: string): boolean {\n if (status === 403 && /csrf/i.test(message)) {\n return true;\n }\n\n return (\n /csrf/i.test(message) &&\n /expired|token validation failed/i.test(message)\n );\n}\n\nasync function readErrorMessage(res: Response): Promise<string> {\n const contentType = res.headers.get(\"Content-Type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n const data = (await res.json().catch(() => null)) as {\n error?: string;\n message?: string;\n } | null;\n if (data?.error) return data.error;\n if (data?.message) return data.message;\n }\n\n const text = await res.text().catch(() => \"\");\n return text || `HTTP ${res.status}`;\n}\n\n/**\n * Entity Server\uC5D0 HTTP \uC694\uCCAD\uC744 \uBCF4\uB0C5\uB2C8\uB2E4.\n *\n * - `encryptRequests` \uD65C\uC131\uD654 \uC2DC \uC778\uC99D\uB41C POST \uBC14\uB514\uB97C \uC790\uB3D9 \uC554\uD638\uD654\uD569\uB2C8\uB2E4.\n * - \uC751\uB2F5\uC774 `application/octet-stream`\uC774\uBA74 \uC790\uB3D9 \uBCF5\uD638\uD654\uD569\uB2C8\uB2E4.\n * - JSON \uC751\uB2F5\uC758 `ok`\uAC00 false\uC774\uBA74 \uC5D0\uB7EC\uB97C \uB358\uC9D1\uB2C8\uB2E4.\n */\nexport async function entityRequest<T>(\n opts: RequestOptions,\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n extraHeaders: Record<string, string> = {},\n requireOkShape = true,\n): Promise<T> {\n const {\n baseUrl,\n token,\n apiKey,\n hmacSecret,\n encryptRequests,\n anonymousPacketToken,\n csrfEnabled,\n csrfHeaderName,\n refreshCsrfToken,\n } = opts;\n const isHmacMode = withAuth && !!(apiKey && hmacSecret);\n const packetSource = resolvePacketSource(opts);\n const shouldUseCsrf = csrfEnabled && requiresCsrf(method) && !isHmacMode;\n let csrfToken = opts.csrfToken;\n let requestContentType = \"application/json\";\n const includeAnonymousPacketHeader =\n !token && !isHmacMode && !!anonymousPacketToken;\n\n let fetchBody: string | Uint8Array | null = null;\n if (body != null) {\n const shouldEncrypt =\n encryptRequests &&\n !!packetSource &&\n method !== \"GET\" &&\n method !== \"HEAD\";\n\n if (shouldEncrypt) {\n const key = derivePacketKey(\n hmacSecret,\n token || anonymousPacketToken,\n );\n fetchBody = encryptPacket(\n new TextEncoder().encode(JSON.stringify(body)),\n key,\n );\n requestContentType = \"application/octet-stream\";\n } else {\n fetchBody = JSON.stringify(body);\n }\n }\n\n const buildHeaders = (\n resolvedCsrfToken: string,\n ): Record<string, string> => {\n const headers: Record<string, string> = {\n \"Content-Type\": requestContentType,\n ...extraHeaders,\n };\n if (!isHmacMode && withAuth && token) {\n headers.Authorization = `Bearer ${token}`;\n }\n if (includeAnonymousPacketHeader) {\n headers[\"X-Packet-Token\"] = anonymousPacketToken;\n }\n if (shouldUseCsrf && resolvedCsrfToken) {\n headers[csrfHeaderName] = resolvedCsrfToken;\n }\n if (isHmacMode) {\n const bodyBytes =\n fetchBody instanceof Uint8Array\n ? fetchBody\n : typeof fetchBody === \"string\"\n ? new TextEncoder().encode(fetchBody)\n : new Uint8Array(0);\n Object.assign(\n headers,\n buildHmacHeaders(method, path, bodyBytes, apiKey, hmacSecret),\n );\n }\n return headers;\n };\n\n if (shouldUseCsrf && !csrfToken && refreshCsrfToken) {\n csrfToken = await refreshCsrfToken();\n }\n\n const executeRequest = (resolvedCsrfToken: string): Promise<Response> =>\n fetch(baseUrl + path, {\n method,\n headers: buildHeaders(resolvedCsrfToken),\n ...(fetchBody != null\n ? { body: fetchBody as RequestInit[\"body\"] }\n : {}),\n credentials: \"include\",\n });\n\n let res = await executeRequest(csrfToken);\n\n if (!res.ok) {\n const message = await readErrorMessage(res.clone());\n if (\n shouldUseCsrf &&\n refreshCsrfToken &&\n isCsrfError(res.status, message)\n ) {\n csrfToken = await refreshCsrfToken();\n res = await executeRequest(csrfToken);\n } else {\n const err = new Error(message);\n (err as { status?: number }).status = res.status;\n throw err;\n }\n }\n\n if (!res.ok) {\n const err = new Error(await readErrorMessage(res));\n (err as { status?: number }).status = res.status;\n throw err;\n }\n\n const contentType = res.headers.get(\"Content-Type\") ?? \"\";\n if (contentType.includes(\"application/octet-stream\")) {\n const key = derivePacketKey(hmacSecret, token || anonymousPacketToken);\n return decryptPacket<T>(await res.arrayBuffer(), key);\n }\n\n if (!contentType.includes(\"application/json\")) {\n return (await res.text()) as T;\n }\n\n const data = (await res.json()) as { ok?: boolean; message?: string };\n if (requireOkShape && !data.ok) {\n const err = new Error(\n data.message ?? `EntityServer error (HTTP ${res.status})`,\n );\n (err as { status?: number }).status = res.status;\n throw err;\n }\n\n return data as T;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["derivePacketKey", "encryptPacket", "decryptPacket", "buildHmacHeaders", "resolvePacketSource", "opts", "requiresCsrf", "method", "isCsrfError", "status", "message", "readErrorMessage", "res", "data", "entityRequest", "path", "body", "withAuth", "extraHeaders", "requireOkShape", "baseUrl", "token", "apiKey", "hmacSecret", "encryptRequests", "anonymousPacketToken", "csrfEnabled", "csrfHeaderName", "refreshCsrfToken", "isHmacMode", "packetSource", "shouldUseCsrf", "csrfToken", "requestContentType", "includeAnonymousPacketHeader", "fetchBody", "key", "buildHeaders", "resolvedCsrfToken", "headers", "bodyBytes", "executeRequest", "err", "contentType"]
7
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 환경변수를 읽습니다.
3
+ * - 브라우저/Vite: `import.meta.env`
4
+ * - Node.js: `process.env`
5
+ */
6
+ export declare function readEnv(name: string): string | undefined;
7
+ /** 쿼리 파라미터 객체를 URL 쿼리 문자열로 변환합니다. `orderBy` 키는 `order_by`로 변환됩니다. */
8
+ export declare function buildQuery(params: Record<string, unknown>): string;
@@ -0,0 +1,2 @@
1
+ function t(e){const n=import.meta;if(n?.env?.[e]!=null)return n.env[e];const r=globalThis.process;if(r?.env?.[e]!=null)return r.env[e]}function o(e){return Object.entries(e).filter(([,n])=>n!=null).map(([n,r])=>`${encodeURIComponent(n==="orderBy"?"order_by":n)}=${encodeURIComponent(String(r))}`).join("&")}export{o as buildQuery,t as readEnv};
2
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/client/utils.ts"],
4
+ "sourcesContent": ["/**\n * \uD658\uACBD\uBCC0\uC218\uB97C \uC77D\uC2B5\uB2C8\uB2E4.\n * - \uBE0C\uB77C\uC6B0\uC800/Vite: `import.meta.env`\n * - Node.js: `process.env`\n */\nexport function readEnv(name: string): string | undefined {\n // Vite / \uAE30\uD0C0 \uBC88\uB4E4\uB7EC (import.meta.env)\n const meta = import.meta as unknown as {\n env?: Record<string, string | undefined>;\n };\n if (meta?.env?.[name] != null) return meta.env[name];\n\n // Node.js (process.env)\n const _proc = (\n globalThis as { process?: { env?: Record<string, string | undefined> } }\n ).process;\n if (_proc?.env?.[name] != null) {\n return _proc.env[name];\n }\n\n return undefined;\n}\n\n/** \uCFFC\uB9AC \uD30C\uB77C\uBBF8\uD130 \uAC1D\uCCB4\uB97C URL \uCFFC\uB9AC \uBB38\uC790\uC5F4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4. `orderBy` \uD0A4\uB294 `order_by`\uB85C \uBCC0\uD658\uB429\uB2C8\uB2E4. */\nexport function buildQuery(params: Record<string, unknown>): string {\n return Object.entries(params)\n .filter(([, value]) => value != null)\n .map(\n ([key, value]) =>\n `${encodeURIComponent(key === \"orderBy\" ? \"order_by\" : key)}=${encodeURIComponent(String(value))}`,\n )\n .join(\"&\");\n}\n"],
5
+ "mappings": "AAKO,SAASA,EAAQC,EAAkC,CAEtD,MAAMC,EAAO,YAGb,GAAIA,GAAM,MAAMD,CAAI,GAAK,KAAM,OAAOC,EAAK,IAAID,CAAI,EAGnD,MAAME,EACF,WACF,QACF,GAAIA,GAAO,MAAMF,CAAI,GAAK,KACtB,OAAOE,EAAM,IAAIF,CAAI,CAI7B,CAGO,SAASG,EAAWC,EAAyC,CAChE,OAAO,OAAO,QAAQA,CAAM,EACvB,OAAO,CAAC,CAAC,CAAEC,CAAK,IAAMA,GAAS,IAAI,EACnC,IACG,CAAC,CAACC,EAAKD,CAAK,IACR,GAAG,mBAAmBC,IAAQ,UAAY,WAAaA,CAAG,CAAC,IAAI,mBAAmB,OAAOD,CAAK,CAAC,CAAC,EACxG,EACC,KAAK,GAAG,CACjB",
6
+ "names": ["readEnv", "name", "meta", "_proc", "buildQuery", "params", "value", "key"]
7
+ }
@@ -0,0 +1,104 @@
1
+ import { EntityAppServerClient, EntityServerClient, type EntityQueryRequest, type EntityServerClientOptions } from "../index.js";
2
+ export interface UseEntityServerOptions extends EntityServerClientOptions {
3
+ singleton?: boolean;
4
+ tokenResolver?: () => string | undefined | null;
5
+ /**
6
+ * 페이지 새로고침 후 로그인 상태를 복원할 때 사용합니다.
7
+ * 이 값이 있으면 마운트 시 `client.refreshToken()`을 호출해 새 access_token을 발급받습니다.
8
+ * `keepSession: true`와 함께 사용하면 세션 유지 타이머도 재시작됩니다.
9
+ * 갱신 성공 시 `onTokenRefreshed` 콜백이 호출됩니다.
10
+ */
11
+ resumeSession?: string;
12
+ }
13
+ export interface UseEntityAppServerOptions extends UseEntityServerOptions {
14
+ }
15
+ type BaseClient = EntityServerClient | EntityAppServerClient;
16
+ interface UseEntityClientResult<TClient extends BaseClient> {
17
+ client: TClient;
18
+ isPending: boolean;
19
+ error: Error | null;
20
+ reset: () => void;
21
+ submit: (entity: string, data: Record<string, unknown>, opts?: {
22
+ transactionId?: string;
23
+ skipHooks?: boolean;
24
+ }) => Promise<{
25
+ ok: boolean;
26
+ seq: number;
27
+ }>;
28
+ del: (entity: string, seq: number, opts?: {
29
+ transactionId?: string;
30
+ hard?: boolean;
31
+ skipHooks?: boolean;
32
+ }) => Promise<{
33
+ ok: boolean;
34
+ deleted: number;
35
+ }>;
36
+ query: <T = unknown>(entity: string, req: EntityQueryRequest) => Promise<{
37
+ ok: boolean;
38
+ data: {
39
+ items: T[];
40
+ count: number;
41
+ };
42
+ }>;
43
+ }
44
+ export interface UseEntityServerResult {
45
+ /** EntityServerClient 인스턴스 (read 전용 메서드 직접 호출 시 사용) */
46
+ client: EntityServerClient;
47
+ /** submit 또는 delete 진행 중 여부 */
48
+ isPending: boolean;
49
+ /** 마지막 mutation 에러 (없으면 null) */
50
+ error: Error | null;
51
+ /** 에러·결과 상태 초기화 */
52
+ reset: () => void;
53
+ /** entity 데이터 생성/수정 (seq 없으면 INSERT, 있으면 UPDATE) */
54
+ submit: (entity: string, data: Record<string, unknown>, opts?: {
55
+ transactionId?: string;
56
+ skipHooks?: boolean;
57
+ }) => Promise<{
58
+ ok: boolean;
59
+ seq: number;
60
+ }>;
61
+ /** entity 데이터 삭제 */
62
+ del: (entity: string, seq: number, opts?: {
63
+ transactionId?: string;
64
+ hard?: boolean;
65
+ skipHooks?: boolean;
66
+ }) => Promise<{
67
+ ok: boolean;
68
+ deleted: number;
69
+ }>;
70
+ /** 커스텀 SQL 조회 */
71
+ query: <T = unknown>(entity: string, req: EntityQueryRequest) => Promise<{
72
+ ok: boolean;
73
+ data: {
74
+ items: T[];
75
+ count: number;
76
+ };
77
+ }>;
78
+ }
79
+ export interface UseEntityAppServerResult extends UseEntityClientResult<EntityAppServerClient> {
80
+ }
81
+ /**
82
+ * React 환경에서 EntityServerClient 인스턴스와 mutation 상태를 반환합니다.
83
+ *
84
+ * - `singleton=true`(기본): 패키지 전역 `entityServer` 인스턴스를 사용합니다.
85
+ * - `singleton=false`: 컴포넌트 스코프의 새 인스턴스를 생성합니다.
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * const { submit, del, isPending, error, reset } = useEntityServer();
90
+ *
91
+ * const handleSave = async () => {
92
+ * await submit("account", { name: "홍길동" });
93
+ * };
94
+ * ```
95
+ */
96
+ export declare function useEntityServer(options?: UseEntityServerOptions): UseEntityServerResult;
97
+ /**
98
+ * React 환경에서 EntityAppServerClient 인스턴스와 mutation 상태를 반환합니다.
99
+ *
100
+ * - `singleton=true`(기본): 패키지 전역 `entityAppServer` 인스턴스를 사용합니다.
101
+ * - `singleton=false`: 컴포넌트 스코프의 새 인스턴스를 생성합니다.
102
+ */
103
+ export declare function useEntityAppServer(options?: UseEntityAppServerOptions): UseEntityAppServerResult;
104
+ export {};
@@ -0,0 +1,2 @@
1
+ import{useCallback as l,useEffect as E,useMemo as x,useRef as m,useState as b}from"react";import{EntityAppServerClient as P,EntityServerClient as w,entityAppServer as I,entityServer as A}from"../index.js";function f(i,u){const{singleton:a=!0,tokenResolver:d,baseUrl:c,token:y,resumeSession:v}=i,[k,p]=b(!1),[S,C]=b(null),o=m(!0);E(()=>(o.current=!0,()=>{o.current=!1}),[]);const n=x(()=>{const e=a?u.singletonInstance:new u.ClientClass({baseUrl:c,token:y});a&&e.configure({baseUrl:c,token:y});const t=d?.();return typeof t=="string"&&e.setToken(t),e},[a,d,c,y,u.ClientClass,u.singletonInstance]),g=m(v);E(()=>{const e=g.current;e&&n.refreshToken(e).catch(()=>{})},[n]);const r=l(async e=>{o.current&&(p(!0),C(null));try{return await e()}catch(t){const s=t instanceof Error?t:new Error(String(t));throw o.current&&C(s),s}finally{o.current&&p(!1)}},[]),T=l((e,t,s)=>r(()=>n.submit(e,t,s)),[n,r]),R=l((e,t,s)=>r(()=>n.delete(e,t,s)),[n,r]),U=l((e,t)=>r(()=>n.query(e,t)),[n,r]),q=l(()=>{p(!1),C(null)},[]);return{client:n,isPending:k,error:S,reset:q,submit:T,del:R,query:U}}function B(i={}){return f(i,{singletonInstance:A,ClientClass:w})}function H(i={}){return f(i,{singletonInstance:I,ClientClass:P})}export{H as useEntityAppServer,B as useEntityServer};
2
+ //# sourceMappingURL=useEntityServer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hooks/useEntityServer.ts"],
4
+ "sourcesContent": ["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n EntityAppServerClient,\n EntityServerClient,\n entityAppServer,\n entityServer,\n type EntityQueryRequest,\n type EntityServerClientOptions,\n} from \"../index.js\";\n\nexport interface UseEntityServerOptions extends EntityServerClientOptions {\n singleton?: boolean;\n tokenResolver?: () => string | undefined | null;\n /**\n * \uD398\uC774\uC9C0 \uC0C8\uB85C\uACE0\uCE68 \uD6C4 \uB85C\uADF8\uC778 \uC0C1\uD0DC\uB97C \uBCF5\uC6D0\uD560 \uB54C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n * \uC774 \uAC12\uC774 \uC788\uC73C\uBA74 \uB9C8\uC6B4\uD2B8 \uC2DC `client.refreshToken()`\uC744 \uD638\uCD9C\uD574 \uC0C8 access_token\uC744 \uBC1C\uAE09\uBC1B\uC2B5\uB2C8\uB2E4.\n * `keepSession: true`\uC640 \uD568\uAED8 \uC0AC\uC6A9\uD558\uBA74 \uC138\uC158 \uC720\uC9C0 \uD0C0\uC774\uBA38\uB3C4 \uC7AC\uC2DC\uC791\uB429\uB2C8\uB2E4.\n * \uAC31\uC2E0 \uC131\uACF5 \uC2DC `onTokenRefreshed` \uCF5C\uBC31\uC774 \uD638\uCD9C\uB429\uB2C8\uB2E4.\n */\n resumeSession?: string;\n}\n\nexport interface UseEntityAppServerOptions extends UseEntityServerOptions {}\n\ntype BaseClient = EntityServerClient | EntityAppServerClient;\n\ntype ClientConstructor<TClient extends BaseClient> = new (\n options?: EntityServerClientOptions,\n) => TClient;\n\ninterface UseEntityClientResult<TClient extends BaseClient> {\n client: TClient;\n isPending: boolean;\n error: Error | null;\n reset: () => void;\n submit: (\n entity: string,\n data: Record<string, unknown>,\n opts?: { transactionId?: string; skipHooks?: boolean },\n ) => Promise<{ ok: boolean; seq: number }>;\n del: (\n entity: string,\n seq: number,\n opts?: { transactionId?: string; hard?: boolean; skipHooks?: boolean },\n ) => Promise<{ ok: boolean; deleted: number }>;\n query: <T = unknown>(\n entity: string,\n req: EntityQueryRequest,\n ) => Promise<{ ok: boolean; data: { items: T[]; count: number } }>;\n}\n\nexport interface UseEntityServerResult {\n /** EntityServerClient \uC778\uC2A4\uD134\uC2A4 (read \uC804\uC6A9 \uBA54\uC11C\uB4DC \uC9C1\uC811 \uD638\uCD9C \uC2DC \uC0AC\uC6A9) */\n client: EntityServerClient;\n /** submit \uB610\uB294 delete \uC9C4\uD589 \uC911 \uC5EC\uBD80 */\n isPending: boolean;\n /** \uB9C8\uC9C0\uB9C9 mutation \uC5D0\uB7EC (\uC5C6\uC73C\uBA74 null) */\n error: Error | null;\n /** \uC5D0\uB7EC\u00B7\uACB0\uACFC \uC0C1\uD0DC \uCD08\uAE30\uD654 */\n reset: () => void;\n /** entity \uB370\uC774\uD130 \uC0DD\uC131/\uC218\uC815 (seq \uC5C6\uC73C\uBA74 INSERT, \uC788\uC73C\uBA74 UPDATE) */\n submit: (\n entity: string,\n data: Record<string, unknown>,\n opts?: { transactionId?: string; skipHooks?: boolean },\n ) => Promise<{ ok: boolean; seq: number }>;\n /** entity \uB370\uC774\uD130 \uC0AD\uC81C */\n del: (\n entity: string,\n seq: number,\n opts?: { transactionId?: string; hard?: boolean; skipHooks?: boolean },\n ) => Promise<{ ok: boolean; deleted: number }>;\n /** \uCEE4\uC2A4\uD140 SQL \uC870\uD68C */\n query: <T = unknown>(\n entity: string,\n req: EntityQueryRequest,\n ) => Promise<{ ok: boolean; data: { items: T[]; count: number } }>;\n}\n\nexport interface UseEntityAppServerResult extends UseEntityClientResult<EntityAppServerClient> {}\n\nfunction useEntityClient<TClient extends BaseClient>(\n options: UseEntityServerOptions,\n config: {\n singletonInstance: TClient;\n ClientClass: ClientConstructor<TClient>;\n },\n): UseEntityClientResult<TClient> {\n const {\n singleton = true,\n tokenResolver,\n baseUrl,\n token,\n resumeSession,\n } = options;\n\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const mountedRef = useRef(true);\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const client = useMemo(() => {\n const instance = singleton\n ? config.singletonInstance\n : new config.ClientClass({ baseUrl, token });\n\n if (singleton) {\n instance.configure({ baseUrl, token });\n }\n\n const resolvedToken = tokenResolver?.();\n if (typeof resolvedToken === \"string\") {\n instance.setToken(resolvedToken);\n }\n\n return instance;\n }, [\n singleton,\n tokenResolver,\n baseUrl,\n token,\n config.ClientClass,\n config.singletonInstance,\n ]);\n\n const resumeTokenRef = useRef(resumeSession);\n useEffect(() => {\n const storedRefreshToken = resumeTokenRef.current;\n if (!storedRefreshToken) return;\n client.refreshToken(storedRefreshToken).catch(() => {\n // refresh_token \uB9CC\uB8CC \uB4F1 \u2014 onSessionExpired \uCF5C\uBC31\uC774 \uC774\uBBF8 \uCC98\uB9AC\n });\n }, [client]);\n\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n if (mountedRef.current) {\n setIsPending(true);\n setError(null);\n }\n try {\n const result = await fn();\n return result;\n } catch (err) {\n const wrapped = err instanceof Error ? err : new Error(String(err));\n if (mountedRef.current) setError(wrapped);\n throw wrapped;\n } finally {\n if (mountedRef.current) setIsPending(false);\n }\n }, []);\n\n const submit = useCallback<UseEntityClientResult<TClient>[\"submit\"]>(\n (entity, data, opts) => run(() => client.submit(entity, data, opts)),\n [client, run],\n );\n\n const del = useCallback<UseEntityClientResult<TClient>[\"del\"]>(\n (entity, seq, opts) => run(() => client.delete(entity, seq, opts)),\n [client, run],\n );\n\n const query = useCallback<UseEntityClientResult<TClient>[\"query\"]>(\n (entity, req) => run(() => client.query(entity, req)),\n [client, run],\n );\n\n const reset = useCallback(() => {\n setIsPending(false);\n setError(null);\n }, []);\n\n return { client, isPending, error, reset, submit, del, query };\n}\n\n/**\n * React \uD658\uACBD\uC5D0\uC11C EntityServerClient \uC778\uC2A4\uD134\uC2A4\uC640 mutation \uC0C1\uD0DC\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4.\n *\n * - `singleton=true`(\uAE30\uBCF8): \uD328\uD0A4\uC9C0 \uC804\uC5ED `entityServer` \uC778\uC2A4\uD134\uC2A4\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n * - `singleton=false`: \uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCF54\uD504\uC758 \uC0C8 \uC778\uC2A4\uD134\uC2A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4.\n *\n * @example\n * ```tsx\n * const { submit, del, isPending, error, reset } = useEntityServer();\n *\n * const handleSave = async () => {\n * await submit(\"account\", { name: \"\uD64D\uAE38\uB3D9\" });\n * };\n * ```\n */\nexport function useEntityServer(\n options: UseEntityServerOptions = {},\n): UseEntityServerResult {\n return useEntityClient(options, {\n singletonInstance: entityServer,\n ClientClass: EntityServerClient,\n });\n}\n\n/**\n * React \uD658\uACBD\uC5D0\uC11C EntityAppServerClient \uC778\uC2A4\uD134\uC2A4\uC640 mutation \uC0C1\uD0DC\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4.\n *\n * - `singleton=true`(\uAE30\uBCF8): \uD328\uD0A4\uC9C0 \uC804\uC5ED `entityAppServer` \uC778\uC2A4\uD134\uC2A4\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.\n * - `singleton=false`: \uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCF54\uD504\uC758 \uC0C8 \uC778\uC2A4\uD134\uC2A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4.\n */\nexport function useEntityAppServer(\n options: UseEntityAppServerOptions = {},\n): UseEntityAppServerResult {\n return useEntityClient(options, {\n singletonInstance: entityAppServer,\n ClientClass: EntityAppServerClient,\n });\n}\n"],
5
+ "mappings": "AAAA,OAAS,eAAAA,EAAa,aAAAC,EAAW,WAAAC,EAAS,UAAAC,EAAQ,YAAAC,MAAgB,QAClE,OACI,yBAAAC,EACA,sBAAAC,EACA,mBAAAC,EACA,gBAAAC,MAGG,cAyEP,SAASC,EACLC,EACAC,EAI8B,CAC9B,KAAM,CACF,UAAAC,EAAY,GACZ,cAAAC,EACA,QAAAC,EACA,MAAAC,EACA,cAAAC,CACJ,EAAIN,EAEE,CAACO,EAAWC,CAAY,EAAId,EAAS,EAAK,EAC1C,CAACe,EAAOC,CAAQ,EAAIhB,EAAuB,IAAI,EAE/CiB,EAAalB,EAAO,EAAI,EAC9BF,EAAU,KACNoB,EAAW,QAAU,GACd,IAAM,CACTA,EAAW,QAAU,EACzB,GACD,CAAC,CAAC,EAEL,MAAMC,EAASpB,EAAQ,IAAM,CACzB,MAAMqB,EAAWX,EACXD,EAAO,kBACP,IAAIA,EAAO,YAAY,CAAE,QAAAG,EAAS,MAAAC,CAAM,CAAC,EAE3CH,GACAW,EAAS,UAAU,CAAE,QAAAT,EAAS,MAAAC,CAAM,CAAC,EAGzC,MAAMS,EAAgBX,IAAgB,EACtC,OAAI,OAAOW,GAAkB,UACzBD,EAAS,SAASC,CAAa,EAG5BD,CACX,EAAG,CACCX,EACAC,EACAC,EACAC,EACAJ,EAAO,YACPA,EAAO,iBACX,CAAC,EAEKc,EAAiBtB,EAAOa,CAAa,EAC3Cf,EAAU,IAAM,CACZ,MAAMyB,EAAqBD,EAAe,QACrCC,GACLJ,EAAO,aAAaI,CAAkB,EAAE,MAAM,IAAM,CAEpD,CAAC,CACL,EAAG,CAACJ,CAAM,CAAC,EAEX,MAAMK,EAAM3B,EAAY,MAAU4B,GAAqC,CAC/DP,EAAW,UACXH,EAAa,EAAI,EACjBE,EAAS,IAAI,GAEjB,GAAI,CAEA,OADe,MAAMQ,EAAG,CAE5B,OAASC,EAAK,CACV,MAAMC,EAAUD,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,EAClE,MAAIR,EAAW,SAASD,EAASU,CAAO,EAClCA,CACV,QAAE,CACMT,EAAW,SAASH,EAAa,EAAK,CAC9C,CACJ,EAAG,CAAC,CAAC,EAECa,EAAS/B,EACX,CAACgC,EAAQC,EAAMC,IAASP,EAAI,IAAML,EAAO,OAAOU,EAAQC,EAAMC,CAAI,CAAC,EACnE,CAACZ,EAAQK,CAAG,CAChB,EAEMQ,EAAMnC,EACR,CAACgC,EAAQI,EAAKF,IAASP,EAAI,IAAML,EAAO,OAAOU,EAAQI,EAAKF,CAAI,CAAC,EACjE,CAACZ,EAAQK,CAAG,CAChB,EAEMU,EAAQrC,EACV,CAACgC,EAAQM,IAAQX,EAAI,IAAML,EAAO,MAAMU,EAAQM,CAAG,CAAC,EACpD,CAAChB,EAAQK,CAAG,CAChB,EAEMY,EAAQvC,EAAY,IAAM,CAC5BkB,EAAa,EAAK,EAClBE,EAAS,IAAI,CACjB,EAAG,CAAC,CAAC,EAEL,MAAO,CAAE,OAAAE,EAAQ,UAAAL,EAAW,MAAAE,EAAO,MAAAoB,EAAO,OAAAR,EAAQ,IAAAI,EAAK,MAAAE,CAAM,CACjE,CAiBO,SAASG,EACZ9B,EAAkC,CAAC,EACd,CACrB,OAAOD,EAAgBC,EAAS,CAC5B,kBAAmBF,EACnB,YAAaF,CACjB,CAAC,CACL,CAQO,SAASmC,EACZ/B,EAAqC,CAAC,EACd,CACxB,OAAOD,EAAgBC,EAAS,CAC5B,kBAAmBH,EACnB,YAAaF,CACjB,CAAC,CACL",
6
+ "names": ["useCallback", "useEffect", "useMemo", "useRef", "useState", "EntityAppServerClient", "EntityServerClient", "entityAppServer", "entityServer", "useEntityClient", "options", "config", "singleton", "tokenResolver", "baseUrl", "token", "resumeSession", "isPending", "setIsPending", "error", "setError", "mountedRef", "client", "instance", "resolvedToken", "resumeTokenRef", "storedRefreshToken", "run", "fn", "err", "wrapped", "submit", "entity", "data", "opts", "del", "seq", "query", "req", "reset", "useEntityServer", "useEntityAppServer"]
7
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./types.js";
2
+ export * from "./EntityServerClient.js";
3
+ export * from "./EntityAppServerClient.js";
4
+ export * from "./mixins/server/index.js";
5
+ export * from "./mixins/app/index.js";
6
+ export * from "./packet.js";
7
+ import { EntityServerClient } from "./EntityServerClient.js";
8
+ import { EntityAppServerClient } from "./EntityAppServerClient.js";
9
+ export declare const entityServer: EntityServerClient;
10
+ export declare const entityAppServer: EntityAppServerClient;
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ function z(s){let r=import.meta;if(r?.env?.[s]!=null)return r.env[s];let e=globalThis.process;if(e?.env?.[s]!=null)return e.env[s]}function d(s){return Object.entries(s).filter(([,r])=>r!=null).map(([r,e])=>`${encodeURIComponent(r==="orderBy"?"order_by":r)}=${encodeURIComponent(String(e))}`).join("&")}import{xchacha20poly1305 as W}from"@noble/ciphers/chacha";import{sha256 as fe}from"@noble/hashes/sha2";import{hkdf as le}from"@noble/hashes/hkdf";var A=32,B=2,C=14,k=24,he=16,Te="entity-server:hkdf:v1",pe="entity-server:packet-encryption";function y(s){return s instanceof Uint8Array?s:new Uint8Array(s)}function Z(s,r=pe){return le(fe,new TextEncoder().encode(s),new TextEncoder().encode(Te),new TextEncoder().encode(r),A)}function Y(s,r=B,e=C){let t=y(s);return t.length<A?r:r+t[A-1]%e}function ee(s,r,e=B,t=C){let n=y(s),i=y(r),o=Y(i,e,t),a=crypto.getRandomValues(new Uint8Array(o)),u=crypto.getRandomValues(new Uint8Array(k)),l=W(i,u).encrypt(n),h=new Uint8Array(o+k+l.length);return h.set(a,0),h.set(u,o),h.set(l,o+k),h}function te(s,r,e=B,t=C){let n=y(s),i=y(r),o=Y(i,e,t);if(n.length<o+k+he)throw new Error("Encrypted packet too short");let a=n.slice(o,o+k),u=n.slice(o+k);return W(i,a).decrypt(u)}function w(s,r){return Z(s||r)}function re(s,r){return ee(s,r)}function E(s,r){let e=te(s,r);return JSON.parse(new TextDecoder().decode(e))}function ne(s,r,e,t){let n=r.toLowerCase().includes("application/octet-stream");if(e&&!n)throw new Error("Encrypted request required: Content-Type must be application/octet-stream");if(n){if(s==null)throw new Error("Encrypted request body is empty");if(s instanceof ArrayBuffer)return E(s,t);if(s instanceof Uint8Array){let i=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);return E(i,t)}throw new Error("Encrypted request body must be ArrayBuffer or Uint8Array")}return s==null||s===""?{}:typeof s=="string"?JSON.parse(s):s}import{sha256 as ke}from"@noble/hashes/sha2";import{hmac as ge}from"@noble/hashes/hmac";function se(s,r,e,t,n){let i=String(Math.floor(Date.now()/1e3)),o=crypto.randomUUID(),a=new TextEncoder().encode(`${s}|${r}|${i}|${o}|`),u=new Uint8Array(a.length+e.length);u.set(a,0),u.set(e,a.length);let l=[...ge(ke,new TextEncoder().encode(n),u)].map(h=>h.toString(16).padStart(2,"0")).join("");return{"X-API-Key":t,"X-Timestamp":i,"X-Nonce":o,"X-Signature":l}}function Pe(s){return s.hmacSecret||s.token||s.anonymousPacketToken}function ye(s){return s!=="GET"&&s!=="HEAD"&&s!=="OPTIONS"}function we(s,r){return s===403&&/csrf/i.test(r)?!0:/csrf/i.test(r)&&/expired|token validation failed/i.test(r)}async function ie(s){if((s.headers.get("Content-Type")??"").includes("application/json")){let t=await s.json().catch(()=>null);if(t?.error)return t.error;if(t?.message)return t.message}return await s.text().catch(()=>"")||`HTTP ${s.status}`}async function $(s,r,e,t,n=!0,i={},o=!0){let{baseUrl:a,token:u,apiKey:f,hmacSecret:l,encryptRequests:h,anonymousPacketToken:b,csrfEnabled:oe,csrfHeaderName:ae,refreshCsrfToken:v}=s,_=n&&!!(f&&l),ue=Pe(s),x=oe&&ye(r)&&!_,P=s.csrfToken,V="application/json",ce=!u&&!_&&!!b,p=null;if(t!=null)if(h&&!!ue&&r!=="GET"&&r!=="HEAD"){let T=w(l,u||b);p=re(new TextEncoder().encode(JSON.stringify(t)),T),V="application/octet-stream"}else p=JSON.stringify(t);let de=c=>{let T={"Content-Type":V,...i};if(!_&&n&&u&&(T.Authorization=`Bearer ${u}`),ce&&(T["X-Packet-Token"]=b),x&&c&&(T[ae]=c),_){let me=p instanceof Uint8Array?p:typeof p=="string"?new TextEncoder().encode(p):new Uint8Array(0);Object.assign(T,se(r,e,me,f,l))}return T};x&&!P&&v&&(P=await v());let Q=c=>fetch(a+e,{method:r,headers:de(c),...p!=null?{body:p}:{},credentials:"include"}),m=await Q(P);if(!m.ok){let c=await ie(m.clone());if(x&&v&&we(m.status,c))P=await v(),m=await Q(P);else{let T=new Error(c);throw T.status=m.status,T}}if(!m.ok){let c=new Error(await ie(m));throw c.status=m.status,c}let X=m.headers.get("Content-Type")??"";if(X.includes("application/octet-stream")){let c=w(l,u||b);return E(await m.arrayBuffer(),c)}if(!X.includes("application/json"))return await m.text();let q=await m.json();if(o&&!q.ok){let c=new Error(q.message??`EntityServer error (HTTP ${m.status})`);throw c.status=m.status,c}return q}var R=class{baseUrl;token;anonymousPacketToken;apiKey;hmacSecret;encryptRequests;csrfEnabled;csrfToken;csrfHeaderName;csrfRefreshPath;csrfRefreshBuffer;activeTxId=null;keepSession;refreshBuffer;onTokenRefreshed;onSessionExpired;_sessionRefreshToken=null;_refreshTimer=null;_csrfRefreshTimer=null;_csrfRefreshPromise=null;constructor(r={}){let e=z("VITE_ENTITY_SERVER_URL");this.baseUrl=(r.baseUrl??e??"").replace(/\/$/,""),this.token=r.token??"",this.anonymousPacketToken=r.anonymousPacketToken??"",this.apiKey=r.apiKey??"",this.hmacSecret=r.hmacSecret??"",this.encryptRequests=r.encryptRequests??!1,this.csrfEnabled=r.csrfEnabled??!1,this.csrfToken=r.csrfToken??"",this.csrfHeaderName=r.csrfHeaderName??"x-csrf-token",this.csrfRefreshPath=r.csrfRefreshPath??"/v1/csrf-token",this.csrfRefreshBuffer=r.csrfRefreshBuffer??60,this.keepSession=r.keepSession??!1,this.refreshBuffer=r.refreshBuffer??60,this.onTokenRefreshed=r.onTokenRefreshed,this.onSessionExpired=r.onSessionExpired}configure(r){typeof r.baseUrl=="string"&&(this.baseUrl=r.baseUrl.replace(/\/$/,"")),typeof r.token=="string"&&(this.token=r.token),typeof r.anonymousPacketToken=="string"&&(this.anonymousPacketToken=r.anonymousPacketToken),typeof r.encryptRequests=="boolean"&&(this.encryptRequests=r.encryptRequests),typeof r.csrfEnabled=="boolean"&&(this.csrfEnabled=r.csrfEnabled,r.csrfEnabled||(this.csrfToken="",this.stopCsrfRefresh())),typeof r.csrfToken=="string"&&(this.csrfToken=r.csrfToken),typeof r.csrfHeaderName=="string"&&(this.csrfHeaderName=r.csrfHeaderName),typeof r.csrfRefreshPath=="string"&&(this.csrfRefreshPath=r.csrfRefreshPath),typeof r.csrfRefreshBuffer=="number"&&(this.csrfRefreshBuffer=r.csrfRefreshBuffer),typeof r.apiKey=="string"&&(this.apiKey=r.apiKey),typeof r.hmacSecret=="string"&&(this.hmacSecret=r.hmacSecret),typeof r.keepSession=="boolean"&&(this.keepSession=r.keepSession),typeof r.refreshBuffer=="number"&&(this.refreshBuffer=r.refreshBuffer),r.onTokenRefreshed&&(this.onTokenRefreshed=r.onTokenRefreshed),r.onSessionExpired&&(this.onSessionExpired=r.onSessionExpired)}setToken(r){this.token=r}setAnonymousPacketToken(r){this.anonymousPacketToken=r}setApiKey(r){this.apiKey=r}setHmacSecret(r){this.hmacSecret=r}setEncryptRequests(r){this.encryptRequests=r}setCsrfToken(r){this.csrfToken=r}setCsrfEnabled(r){this.csrfEnabled=r,r||(this.csrfToken="",this.stopCsrfRefresh())}_scheduleKeepSession(r,e,t){this._clearRefreshTimer(),this._sessionRefreshToken=r;let n=Math.max((e-this.refreshBuffer)*1e3,0);this._refreshTimer=setTimeout(async()=>{if(this._sessionRefreshToken)try{let i=await t(this._sessionRefreshToken);this.onTokenRefreshed?.(i.access_token,i.expires_in),this._scheduleKeepSession(this._sessionRefreshToken,i.expires_in,t)}catch(i){this._clearRefreshTimer(),this.onSessionExpired?.(i instanceof Error?i:new Error(String(i)))}},n)}_clearRefreshTimer(){this._refreshTimer!==null&&(clearTimeout(this._refreshTimer),this._refreshTimer=null)}stopKeepSession(){this._clearRefreshTimer(),this._sessionRefreshToken=null}_clearCsrfRefreshTimer(){this._csrfRefreshTimer!==null&&(clearTimeout(this._csrfRefreshTimer),this._csrfRefreshTimer=null)}stopCsrfRefresh(){this._clearCsrfRefreshTimer(),this._csrfRefreshPromise=null}_scheduleCsrfRefresh(r){if(this._clearCsrfRefreshTimer(),!this.csrfEnabled||!this.csrfRefreshPath)return;let e=Math.max((r-this.csrfRefreshBuffer)*1e3,0);this._csrfRefreshTimer=setTimeout(()=>{this.refreshCsrfToken().catch(()=>{this._clearCsrfRefreshTimer()})},e)}async refreshCsrfToken(){return!this.csrfEnabled||!this.csrfRefreshPath?"":(this._csrfRefreshPromise||(this._csrfRefreshPromise=(async()=>{let r=await fetch(`${this.baseUrl}${this.csrfRefreshPath}`,{method:"GET",credentials:"include"});if(!r.ok){let n=await r.text().catch(()=>""),i=new Error(n||`HTTP ${r.status}`);throw i.status=r.status,i}let e=await r.json().catch(()=>null),t=e&&typeof e=="object"&&"data"in e?e.data??null:e;if(!t?.enabled||typeof t.token!="string")throw new Error("CSRF token refresh failed");return this._applyCsrfHealth(t),this.csrfToken})().finally(()=>{this._csrfRefreshPromise=null})),this._csrfRefreshPromise)}_applyCsrfHealth(r){if(!r?.enabled){this.setCsrfEnabled(!1);return}this.csrfEnabled=!0,typeof r.token=="string"&&(this.csrfToken=r.token),typeof r.headerName=="string"&&(this.csrfHeaderName=r.headerName),typeof r.refreshPath=="string"&&(this.csrfRefreshPath=r.refreshPath),typeof r.expiresIn=="number"&&r.expiresIn>0&&this._scheduleCsrfRefresh(r.expiresIn)}readRequestBody(r,e="application/json",t=!1){let n=w(this.hmacSecret,this.token||this.anonymousPacketToken);return ne(r,e,t,n)}get _reqOpts(){return{baseUrl:this.baseUrl,token:this.token,anonymousPacketToken:this.anonymousPacketToken,apiKey:this.apiKey,hmacSecret:this.hmacSecret,encryptRequests:this.encryptRequests,csrfEnabled:this.csrfEnabled,csrfToken:this.csrfToken,csrfHeaderName:this.csrfHeaderName,refreshCsrfToken:this.csrfEnabled?()=>this.refreshCsrfToken():null}}requestJson(r,e,t,n=!0,i){return $(this._reqOpts,r,e,t,n,i,!1)}requestBinary(r,e,t,n=!0){return this._requestBinary(r,e,t,n)}requestForm(r,e,t,n=!0){return this._requestForm(r,e,t,n)}requestFormBinary(r,e,t,n=!0){return this._requestFormBinary(r,e,t,n)}_request(r,e,t,n=!0,i){return $(this._reqOpts,r,e,t,n,i,!0)}async _requestBinary(r,e,t,n=!0){let i={"Content-Type":"application/json"};n&&this.token&&(i.Authorization=`Bearer ${this.token}`),this.apiKey&&(i["X-API-Key"]=this.apiKey);let o=await fetch(this.baseUrl+e,{method:r,headers:i,...t!=null?{body:JSON.stringify(t)}:{},credentials:"include"});if(!o.ok){let a=await o.text(),u=new Error(`HTTP ${o.status}: ${a}`);throw u.status=o.status,u}return o.arrayBuffer()}async _requestForm(r,e,t,n=!0){let i={};n&&this.token&&(i.Authorization=`Bearer ${this.token}`),this.apiKey&&(i["X-API-Key"]=this.apiKey);let o=await fetch(this.baseUrl+e,{method:r,headers:i,body:t,credentials:"include"}),a=await o.json();if(!a.ok){let u=new Error(a.message??`EntityServer error (HTTP ${o.status})`);throw u.status=o.status,u}return a}async _requestFormBinary(r,e,t,n=!0){let i={};n&&this.token&&(i.Authorization=`Bearer ${this.token}`),this.apiKey&&(i["X-API-Key"]=this.apiKey);let o=await fetch(this.baseUrl+e,{method:r,headers:i,body:t,credentials:"include"});if(!o.ok){let a=await o.text(),u=new Error(`HTTP ${o.status}: ${a}`);throw u.status=o.status,u}return o.arrayBuffer()}};function O(s){return class extends s{async checkHealth(){let t=await(await fetch(`${this.baseUrl}/v1/health`,{signal:AbortSignal.timeout(3e3),credentials:"include"})).json();return t.packet_encryption&&(this.encryptRequests=!0),typeof t.packet_token=="string"&&(this.anonymousPacketToken=t.packet_token),this._applyCsrfHealth(t.csrf),t}async login(e,t){let n=await this._request("POST","/v1/auth/login",{email:e,passwd:t},!1);return this.token=n.data.access_token,this.keepSession&&this._scheduleKeepSession(n.data.refresh_token,n.data.expires_in,i=>this.refreshToken(i)),n.data}async refreshToken(e){let t=await this._request("POST","/v1/auth/refresh",{refresh_token:e},!1);return this.token=t.data.access_token,this.keepSession&&this._scheduleKeepSession(e,t.data.expires_in,n=>this.refreshToken(n)),t.data}async logout(e){this.stopKeepSession();let t=await this._request("POST","/v1/auth/logout",{refresh_token:e},!1);return this.token="",t}me(){return this._request("GET","/v1/auth/me")}withdraw(e){return this._request("POST","/v1/auth/withdraw",e?{passwd:e}:{})}reactivate(e){return this._request("POST","/v1/auth/reactivate",e,!1)}}}function U(s){return class extends s{async transStart(){let e=await this._request("POST","/v1/transaction/start",void 0,!1);return this.activeTxId=e.transaction_id,this.activeTxId}transRollback(e){let t=e??this.activeTxId;return t?(this.activeTxId=null,this._request("POST",`/v1/transaction/rollback/${t}`)):Promise.reject(new Error("No active transaction. Call transStart() first."))}transCommit(e){let t=e??this.activeTxId;return t?(this.activeTxId=null,this._request("POST",`/v1/transaction/commit/${t}`)):Promise.reject(new Error("No active transaction. Call transStart() first."))}meta(e){return this._request("POST",`/v1/entity/${e}/meta`,{})}validate(e,t){return this.requestJson("POST",`/v1/entity/${e}/validate`,t)}get(e,t,n={}){let i=n.skipHooks?"?skipHooks=true":"";return this._request("GET",`/v1/entity/${e}/${t}${i}`)}find(e,t,n={}){let i=n.skipHooks?"?skipHooks=true":"";return this._request("POST",`/v1/entity/${e}/find${i}`,t??{})}list(e,t={}){let{conditions:n,fields:i,orderDir:o,orderBy:a,...u}=t,f={page:1,limit:20,...u};return a&&(f.orderBy=o==="DESC"?`-${a}`:a),i?.length&&(f.fields=i.join(",")),this._request("POST",`/v1/entity/${e}/list?${d(f)}`,n??{})}count(e,t){return this._request("POST",`/v1/entity/${e}/count`,t??{})}query(e,t){return this._request("POST",`/v1/entity/${e}/query`,t)}submit(e,t,n={}){let i=n.transactionId??this.activeTxId,o=i?{"X-Transaction-ID":i}:void 0,a=n.skipHooks?"?skipHooks=true":"";return this._request("POST",`/v1/entity/${e}/submit${a}`,t,!0,o)}delete(e,t,n={}){let i=new URLSearchParams;n.hard&&i.set("hard","true"),n.skipHooks&&i.set("skipHooks","true");let o=i.size?`?${i}`:"",a=n.transactionId??this.activeTxId,u=a?{"X-Transaction-ID":a}:void 0;return this._request("POST",`/v1/entity/${e}/delete/${t}${o}`,void 0,!0,u)}history(e,t,n={}){return this._request("GET",`/v1/entity/${e}/history/${t}?${d({page:1,limit:50,...n})}`)}rollback(e,t){return this._request("POST",`/v1/entity/${e}/rollback/${t}`)}}}function G(s){return class extends s{async fileUpload(e,t,n={}){let i=new FormData;return i.append("file",t,t instanceof File?t.name:"upload"),n.refSeq!=null&&i.append("ref_seq",String(n.refSeq)),n.isPublic!=null&&i.append("is_public",n.isPublic?"true":"false"),this._requestForm("POST",`/v1/files/${e}/upload`,i)}fileDownload(e,t){return this._requestBinary("POST",`/v1/files/${e}/download/${t}`,{})}fileDelete(e,t){return this._request("POST",`/v1/files/${e}/delete/${t}`,{})}fileList(e,t={}){return this._request("POST",`/v1/files/${e}/list`,t.refSeq?{ref_seq:t.refSeq}:{})}fileMeta(e,t){return this._request("POST",`/v1/files/${e}/meta/${t}`,{})}fileToken(e){return this._request("POST",`/v1/files/token/${e}`,{})}fileViewUrl(e,t={}){let n=t.download?"?download=true":"";return`${this.baseUrl}/v1/files/${e}${n}`}fileUrl(e){return`${this.baseUrl}/v1/files/${e}`}}}function J(s){return class extends s{push(e,t,n={}){return this.submit(e,t,n)}pushLogList(e={}){return this.list("push_log",e)}registerPushDevice(e,t,n,i={}){let{platform:o,deviceType:a,browser:u,browserVersion:f,pushEnabled:l=!0,transactionId:h}=i;return this.submit("account_device",{id:t,account_seq:e,push_token:n,push_enabled:l,...o?{platform:o}:{},...a?{device_type:a}:{},...u?{browser:u}:{},...f?{browser_version:f}:{}},{transactionId:h})}updatePushDeviceToken(e,t,n={}){let{pushEnabled:i=!0,transactionId:o}=n;return this.submit("account_device",{seq:e,push_token:t,push_enabled:i},{transactionId:o})}disablePushDevice(e,t={}){return this.submit("account_device",{seq:e,push_enabled:!1},{transactionId:t.transactionId})}}}function M(s){return class extends s{smtpSend(e){return this._request("POST","/v1/smtp/send",e)}smtpStatus(e){return this._request("POST",`/v1/smtp/status/${e}`,{})}}}function H(s){return class extends s{addressSido(){return this.requestJson("GET","/v1/utils/address/sido",void 0,!1)}addressSigungu(e){let t=new URLSearchParams({sido:e.sido}).toString();return this.requestJson("GET",`/v1/utils/address/sigungu?${t}`,void 0,!1)}addressDong(e){let t=new URLSearchParams({sido:e.sido,sigungu:e.sigungu}).toString();return this.requestJson("GET",`/v1/utils/address/dong?${t}`,void 0,!1)}addressClean(e){let t=new URLSearchParams({q:e.q}).toString();return this.requestJson("GET",`/v1/utils/address/clean?${t}`,void 0,!1)}qrcode(e,t={}){return this._requestBinary("POST","/v1/utils/qrcode",{content:e,...t})}qrcodeBase64(e,t={}){return this._request("POST","/v1/utils/qrcode/base64",{content:e,...t})}qrcodeText(e,t={}){return this._request("POST","/v1/utils/qrcode/text",{content:e,...t})}barcode(e,t={}){return this._requestBinary("POST","/v1/utils/barcode",{content:e,...t})}pdf2png(e,t={}){let n=new FormData;n.append("file",new Blob([e],{type:"application/pdf"}),"document.pdf");let i=new URLSearchParams;t.dpi!=null&&i.set("dpi",String(t.dpi)),t.firstPage!=null&&i.set("first_page",String(t.firstPage)),t.lastPage!=null&&i.set("last_page",String(t.lastPage));let o=i.toString(),a="/v1/utils/pdf2png"+(o?`?${o}`:"");return this._requestFormBinary("POST",a,n)}pdf2pngByFileSeq(e,t={}){return this.requestBinary("POST",`/v1/utils/pdf2png/${e}`,t)}pdf2jpg(e,t={}){let n=new FormData;n.append("file",new Blob([e],{type:"application/pdf"}),"document.pdf");let i=new URLSearchParams;t.dpi!=null&&i.set("dpi",String(t.dpi)),t.firstPage!=null&&i.set("first_page",String(t.firstPage)),t.lastPage!=null&&i.set("last_page",String(t.lastPage));let o=i.toString(),a="/v1/utils/pdf2jpg"+(o?`?${o}`:"");return this._requestFormBinary("POST",a,n)}pdf2jpgByFileSeq(e,t={}){return this.requestBinary("POST",`/v1/utils/pdf2jpg/${e}`,t)}}}function F(s){return class extends s{_adminPath(e){return`/v1/admin${e}`}_adminGet(e){return this.requestJson("GET",this._adminPath(e))}_adminPost(e,t){return this.requestJson("POST",this._adminPath(e),t)}_adminPut(e,t){return this.requestJson("PUT",this._adminPath(e),t)}_adminPatch(e,t){return this.requestJson("PATCH",this._adminPath(e),t)}_adminDelete(e,t){return this.requestJson("DELETE",this._adminPath(e),t)}listAdminEntities(){return this._adminGet("/entities")}getAdminErdSchema(){return this._adminGet("/erd/schema")}batchEnsureAdminEntities(e){return this._adminPost("/entities/batch-ensure",e)}createAdminEntityConfig(e,t){return this._adminPost(`/${e}/create`,t)}getAdminEntityConfig(e){return this._adminGet(`/${e}/config`)}updateAdminEntityConfig(e,t){return this._adminPut(`/${e}/config`,t)}validateAdminEntityConfig(e,t){return this._adminPost(t?`/${t}/validate`:"/entity/validate",e)}normalizeAdminEntityConfig(e,t){return this._adminPost(t?`/${t}/normalize`:"/entity/normalize",e)}getAdminEntityStats(e,t){return this._adminPost(`/${e}/stats`,t)}reindexAdminEntity(e){return this._adminPost(`/${e}/reindex`)}syncAdminEntitySchema(e){return this._adminPost(`/${e}/sync-schema`)}resetAdminEntity(e){return this._adminPost(`/${e}/reset`)}truncateAdminEntity(e){return this._adminPost(`/${e}/truncate`)}dropAdminEntity(e){return this._adminPost(`/${e}/drop`)}resetAllAdmin(e){return this._adminPost("/reset-all",e)}listAdminConfigs(){return this._adminGet("/configs")}getAdminConfig(e){return this._adminGet(`/configs/${e}`)}updateAdminConfig(e,t){return this._adminPatch(`/configs/${e}`,t)}listAdminRoles(){return this._adminGet("/roles")}createAdminRole(e){return this._adminPost("/roles",e)}getAdminRole(e){return this._adminGet(`/roles/${e}`)}updateAdminRole(e,t){return this._adminPatch(`/roles/${e}`,t)}deleteAdminRole(e){return this._adminDelete(`/roles/${e}`)}listAdminApiKeys(){return this._adminGet("/api-keys")}createAdminApiKey(e){return this._adminPost("/api-keys",e)}getAdminApiKey(e){return this._adminGet(`/api-keys/${e}`)}updateAdminApiKey(e,t){return this._adminPatch(`/api-keys/${e}`,t)}deleteAdminApiKey(e){return this._adminDelete(`/api-keys/${e}`)}regenerateAdminApiKeySecret(e){return this._adminPost(`/api-keys/${e}/regenerate-secret`)}listAdminAccounts(){return this._adminGet("/accounts")}createAdminAccount(e){return this._adminPost("/accounts",e)}getAdminAccount(e){return this._adminGet(`/accounts/${e}`)}updateAdminAccount(e,t){return this._adminPatch(`/accounts/${e}`,t)}deleteAdminAccount(e){return this._adminDelete(`/accounts/${e}`)}listAdminLicenses(){return this._adminGet("/licenses")}createAdminLicense(e){return this._adminPost("/licenses",e)}getAdminLicense(e){return this._adminGet(`/licenses/${e}`)}updateAdminLicense(e,t){return this._adminPatch(`/licenses/${e}`,t)}deleteAdminLicense(e){return this._adminDelete(`/licenses/${e}`)}runAdminBackup(e){return this._adminPost("/backup/run",e)}getAdminBackupStatus(e){return this._adminPost("/backup/status",e)}listAdminBackups(e){return this._adminPost("/backup/list",e)}restoreAdminBackup(e){return this._adminPost("/backup/restore",e)}deleteAdminBackup(e){return this._adminPost("/backup/delete",e)}disableAdminAccountTwoFactor(e){return this._adminDelete(`/accounts/${e}/2fa`)}}}var g=class extends H(G(M(J(F(U(O(R))))))){};function I(s){return class extends s{accountRegister(e){return this.requestJson("POST","/v1/account/register",e,!1)}accountWithdraw(e){return this.requestJson("POST","/v1/account/withdraw",e)}accountChangePassword(e){return this.requestJson("POST","/v1/account/change-password",e)}accountReactivate(e){return this.requestJson("POST","/v1/account/reactivate",e,!1)}listAccountBiometrics(){return this.requestJson("GET","/v1/account/biometric")}registerAccountBiometric(e){return this.requestJson("POST","/v1/account/biometric",e)}deleteAccountBiometric(e){return this.requestJson("DELETE",`/v1/account/biometric/${e}`)}}}function K(s){return class extends s{listBoardCategories(e={}){let t=d(e);return this.requestJson("GET",`/v1/board/categories${t?`?${t}`:""}`,void 0,!1)}getBoardCategory(e){return this.requestJson("GET",`/v1/board/categories/${e}`,void 0,!1)}createBoardCategory(e){return this.requestJson("POST","/v1/board/categories",e)}updateBoardCategory(e,t){return this.requestJson("PUT",`/v1/board/categories/${e}`,t)}deleteBoardCategory(e){return this.requestJson("DELETE",`/v1/board/categories/${e}`)}listBoardPosts(e,t={}){let n=d(t);return this.requestJson("GET",`/v1/board/${e}/list${n?`?${n}`:""}`,void 0,!1)}getBoardPost(e){return this.requestJson("GET",`/v1/board/posts/${e}`,void 0,!1)}createBoardPost(e,t){return this.requestJson("POST",`/v1/board/${e}/submit`,t)}updateBoardPost(e,t){return this.requestJson("PUT",`/v1/board/posts/${e}`,t)}deleteBoardPost(e){return this.requestJson("DELETE",`/v1/board/posts/${e}`)}listBoardComments(e,t={}){let n=d(t);return this.requestJson("GET",`/v1/board/posts/${e}/comments${n?`?${n}`:""}`,void 0,!1)}createBoardComment(e,t){return this.requestJson("POST",`/v1/board/posts/${e}/comments/submit`,t)}updateBoardComment(e,t){return this.requestJson("PUT",`/v1/board/comments/${e}`,t)}deleteBoardComment(e){return this.requestJson("DELETE",`/v1/board/comments/${e}`)}listBoardFiles(e){return this.requestJson("GET",`/v1/board/posts/${e}/files`,void 0,!1)}async uploadBoardFile(e,t){let n=new FormData;return n.append("file",t,t instanceof File?t.name:"upload"),this.requestForm("POST",`/v1/board/posts/${e}/files`,n)}boardFileUrl(e){return`${this.baseUrl}/v1/board/files/${e}`}deleteBoardFile(e){return this.requestJson("DELETE",`/v1/board/files/${e}`)}createBoardGuestPost(e,t){return this.requestJson("POST",`/v1/board/${e}/guest-submit`,t,!1)}authenticateBoardGuestPost(e,t){return this.requestJson("POST",`/v1/board/posts/${e}/guest-auth`,t,!1)}toggleBoardPostLike(e){return this.requestJson("POST",`/v1/board/posts/${e}/like`,{})}acceptBoardPost(e){return this.requestJson("POST",`/v1/board/posts/${e}/accept`,{})}rateBoardPost(e,t){return this.requestJson("POST",`/v1/board/posts/${e}/rating`,t)}rateBoardComment(e,t){return this.requestJson("POST",`/v1/board/comments/${e}/rating`,t)}listBoardTags(e={}){let t=d(e);return this.requestJson("GET",`/v1/board/tags${t?`?${t}`:""}`,void 0,!1)}setBoardPostTags(e,t){return this.requestJson("PUT",`/v1/board/posts/${e}/tags`,t)}reportBoardPost(e,t){return this.requestJson("POST",`/v1/board/posts/${e}/report`,t)}reportBoardComment(e,t){return this.requestJson("POST",`/v1/board/comments/${e}/report`,t)}listBoardReports(e={}){let t=d(e);return this.requestJson("GET",`/v1/board/admin/reports${t?`?${t}`:""}`)}updateBoardReport(e,t){return this.requestJson("PATCH",`/v1/board/admin/reports/${e}`,t)}markBoardPostRead(e){return this.requestJson("POST",`/v1/board/posts/${e}/read`,{})}listBoardMentions(e={}){let t=d(e);return this.requestJson("GET",`/v1/board/mentions${t?`?${t}`:""}`)}markBoardMentionRead(e){return this.requestJson("PATCH",`/v1/board/mentions/${e}/read`,{})}}}function L(s){return class extends s{sendEmailVerification(e){return this.requestJson("POST","/v1/email-verify/send",e,!1)}confirmEmailVerification(e){return this.requestJson("POST","/v1/email-verify/confirm",e,!1)}activateEmailVerification(e){let t=d(e);return this.requestJson("GET",`/v1/email-verify/activate${t?`?${t}`:""}`,void 0,!1)}getEmailVerificationStatus(){return this.requestJson("GET","/v1/email-verify/status")}changeVerifiedEmail(e){return this.requestJson("POST","/v1/email-verify/change",e)}}}function j(s){return class extends s{oauthAuthorizeUrl(e,t={}){let n=d(t);return`${this.baseUrl}/v1/oauth/${e}${n?`?${n}`:""}`}oauthCallback(e,t,n="POST"){if(n==="GET"){let i=d(t??{});return this.requestJson("GET",`/v1/oauth/${e}/callback${i?`?${i}`:""}`,void 0,!1)}return this.requestJson("POST",`/v1/oauth/${e}/callback`,t,!1)}linkOAuthAccount(e){return this.requestJson("POST","/v1/account/oauth/link",e)}unlinkOAuthAccount(e){return this.requestJson("DELETE",`/v1/account/oauth/link/${e}`)}listOAuthProviders(){return this.requestJson("GET","/v1/account/oauth/providers")}refreshOAuthProviderToken(e,t){return this.requestJson("POST",`/v1/account/oauth/refresh/${e}`,t)}}}function D(s){return class extends s{requestPasswordReset(e){return this.requestJson("POST","/v1/password-reset/request",e,!1)}validatePasswordResetToken(e){return this.requestJson("GET",`/v1/password-reset/validate/${encodeURIComponent(e)}`,void 0,!1)}verifyPasswordReset(e){return this.requestJson("POST","/v1/password-reset/verify",e,!1)}}}function N(s){return class extends s{setupTwoFactor(e){return this.requestJson("POST","/v1/account/2fa/setup",e)}verifyTwoFactorSetup(e){return this.requestJson("POST","/v1/account/2fa/setup/verify",e,!1)}disableTwoFactor(){return this.requestJson("DELETE","/v1/account/2fa")}getTwoFactorStatus(){return this.requestJson("GET","/v1/account/2fa/status")}regenerateTwoFactorRecoveryCodes(){return this.requestJson("POST","/v1/account/2fa/recovery/regenerate")}verifyTwoFactor(e){return this.requestJson("POST","/v1/account/2fa/verify",e,!1)}recoverTwoFactorAccess(e){return this.requestJson("POST","/v1/account/2fa/recovery",e,!1)}}}var S=class extends j(N(D(L(K(I(g)))))){};var Mt=new g,Ht=new S;export{I as AccountAppMixin,F as AdminMixin,O as AuthMixin,K as BoardMixin,L as EmailVerifyMixin,S as EntityAppServerClient,U as EntityMixin,g as EntityServerClient,G as FileMixin,j as OAuthMixin,Te as PACKET_HKDF_SALT,pe as PACKET_INFO_LABEL,A as PACKET_KEY_SIZE,B as PACKET_MAGIC_MIN,C as PACKET_MAGIC_RANGE,k as PACKET_NONCE_SIZE,he as PACKET_TAG_SIZE,D as PasswordResetMixin,J as PushMixin,M as SmtpMixin,N as TwoFactorMixin,H as UtilsMixin,te as decryptPacket,Z as derivePacketKey,ee as encryptPacket,Ht as entityAppServer,Mt as entityServer,Y as packetMagicLenFromKey};
2
+ //# sourceMappingURL=index.js.map