entity-server-client 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EntityServerClient.d.ts +203 -0
- package/dist/client/hmac.d.ts +8 -0
- package/dist/client/packet.d.ts +22 -0
- package/dist/client/request.d.ts +16 -0
- package/dist/client/utils.d.ts +4 -0
- package/dist/index.d.ts +3 -393
- package/dist/index.js +1 -1
- package/dist/index.js.map +4 -4
- package/dist/react.js +1 -1
- package/dist/react.js.map +4 -4
- package/dist/types.d.ts +165 -0
- package/package.json +1 -1
- package/src/EntityServerClient.ts +546 -0
- package/src/client/hmac.ts +41 -0
- package/src/client/packet.ts +113 -0
- package/src/client/request.ts +102 -0
- package/src/client/utils.ts +18 -0
- package/src/index.ts +3 -917
- package/src/types.ts +186 -0
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{xchacha20poly1305 as
|
|
1
|
+
function v(s){return import.meta?.env?.[s]}function R(s){return Object.entries(s).filter(([,e])=>e!=null).map(([e,t])=>`${encodeURIComponent(e==="orderBy"?"order_by":e)}=${encodeURIComponent(String(t))}`).join("&")}import{xchacha20poly1305 as E}from"@noble/ciphers/chacha";import{sha256 as S}from"@noble/hashes/sha2";import{hkdf as I}from"@noble/hashes/hkdf";function f(s,e){if(s){let t=new TextEncoder().encode("entity-server:hkdf:v1"),r=new TextEncoder().encode("entity-server:packet-encryption");return I(S,new TextEncoder().encode(s),t,r,32)}return S(new TextEncoder().encode(e))}function _(s,e,t){let r=new Uint8Array(t),n=new Uint8Array(24);crypto.getRandomValues(r),crypto.getRandomValues(n);let a=E(e,n).encrypt(s),o=new Uint8Array(t+24+a.length);return o.set(r,0),o.set(n,t),o.set(a,t+24),o}function k(s,e,t){let r=new Uint8Array(s);if(r.length<t+24+16)throw new Error("Encrypted packet too short");let n=r.slice(t,t+24),i=r.slice(t+24),o=E(e,n).decrypt(i);return JSON.parse(new TextDecoder().decode(o))}function P(s,e,t,r,n){let i=e.toLowerCase().includes("application/octet-stream");if(t&&!i)throw new Error("Encrypted request required: Content-Type must be application/octet-stream");if(i){if(s==null)throw new Error("Encrypted request body is empty");if(s instanceof ArrayBuffer)return k(s,r,n);if(s instanceof Uint8Array){let a=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);return k(a,r,n)}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 U}from"@noble/hashes/sha2";import{hmac as $}from"@noble/hashes/hmac";function q(s,e,t,r,n){let i=String(Math.floor(Date.now()/1e3)),a=crypto.randomUUID(),o=new TextEncoder().encode(`${s}|${e}|${i}|${a}|`),c=new Uint8Array(o.length+t.length);c.set(o,0),c.set(t,o.length);let l=[...$(U,new TextEncoder().encode(n),c)].map(g=>g.toString(16).padStart(2,"0")).join("");return{"X-API-Key":r,"X-Timestamp":i,"X-Nonce":a,"X-Signature":l}}async function w(s,e,t,r,n=!0,i={}){let{baseUrl:a,token:o,apiKey:c,hmacSecret:p,packetMagicLen:l,encryptRequests:g}=s,T=n&&!!(c&&p),y={"Content-Type":"application/json",...i};!T&&n&&o&&(y.Authorization=`Bearer ${o}`);let u=null;if(r!=null)if(g&&n&&(o||T)&&e!=="GET"&&e!=="HEAD"){let x=f(p,o);u=_(new TextEncoder().encode(JSON.stringify(r)),x,l),y["Content-Type"]="application/octet-stream"}else u=JSON.stringify(r);if(T){let h=u instanceof Uint8Array?u:typeof u=="string"?new TextEncoder().encode(u):new Uint8Array(0);Object.assign(y,q(e,t,h,c,p))}let d=await fetch(a+t,{method:e,headers:y,...u!=null?{body:u}:{}});if((d.headers.get("Content-Type")??"").includes("application/octet-stream")){let h=f(p,o);return k(await d.arrayBuffer(),h,l)}let b=await d.json();if(!b.ok){let h=new Error(b.message??`EntityServer error (HTTP ${d.status})`);throw h.status=d.status,h}return b}var m=class{baseUrl;token;apiKey;hmacSecret;packetMagicLen;encryptRequests;activeTxId=null;keepSession;refreshBuffer;onTokenRefreshed;onSessionExpired;_sessionRefreshToken=null;_refreshTimer=null;constructor(e={}){let t=v("VITE_ENTITY_SERVER_URL"),r=v("VITE_ENTITY_SERVER_PACKET_MAGIC_LEN");this.baseUrl=(e.baseUrl??t??"http://localhost:47200").replace(/\/$/,""),this.token=e.token??"",this.apiKey=e.apiKey??"",this.hmacSecret=e.hmacSecret??"",this.packetMagicLen=e.packetMagicLen??(r?Number(r):4),this.encryptRequests=e.encryptRequests??!1,this.keepSession=e.keepSession??!1,this.refreshBuffer=e.refreshBuffer??60,this.onTokenRefreshed=e.onTokenRefreshed,this.onSessionExpired=e.onSessionExpired}configure(e){e.baseUrl&&(this.baseUrl=e.baseUrl.replace(/\/$/,"")),typeof e.token=="string"&&(this.token=e.token),typeof e.packetMagicLen=="number"&&(this.packetMagicLen=e.packetMagicLen),typeof e.encryptRequests=="boolean"&&(this.encryptRequests=e.encryptRequests),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}setApiKey(e){this.apiKey=e}setHmacSecret(e){this.hmacSecret=e}setPacketMagicLen(e){this.packetMagicLen=e}getPacketMagicLen(){return this.packetMagicLen}_scheduleKeepSession(e,t){this._clearRefreshTimer(),this._sessionRefreshToken=e;let r=Math.max((t-this.refreshBuffer)*1e3,0);this._refreshTimer=setTimeout(async()=>{if(this._sessionRefreshToken)try{let n=await this.refreshToken(this._sessionRefreshToken);this.onTokenRefreshed?.(n.access_token,n.expires_in),this._scheduleKeepSession(this._sessionRefreshToken,n.expires_in)}catch(n){this._clearRefreshTimer(),this.onSessionExpired?.(n instanceof Error?n:new Error(String(n)))}},r)}_clearRefreshTimer(){this._refreshTimer!==null&&(clearTimeout(this._refreshTimer),this._refreshTimer=null)}stopKeepSession(){this._clearRefreshTimer(),this._sessionRefreshToken=null}async checkHealth(){let t=await(await fetch(`${this.baseUrl}/v1/health`,{signal:AbortSignal.timeout(3e3)})).json();return t.packet_encryption&&(this.encryptRequests=!0),t}async login(e,t){let r=await this.request("POST","/v1/auth/login",{email:e,passwd:t},!1);return this.token=r.data.access_token,this.keepSession&&this._scheduleKeepSession(r.data.refresh_token,r.data.expires_in),r.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),t.data}async logout(e){this.stopKeepSession();let t=await this.request("POST","/v1/auth/logout",{refresh_token:e},!1);return this.token="",t}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."))}get(e,t,r={}){let n=r.skipHooks?"?skipHooks=true":"";return this.request("GET",`/v1/entity/${e}/${t}${n}`)}find(e,t,r={}){let n=r.skipHooks?"?skipHooks=true":"";return this.request("POST",`/v1/entity/${e}/find${n}`,t??{})}list(e,t={}){let{conditions:r,fields:n,orderDir:i,orderBy:a,...o}=t,c={page:1,limit:20,...o};return a&&(c.orderBy=i==="DESC"?`-${a}`:a),n?.length&&(c.fields=n.join(",")),this.request("POST",`/v1/entity/${e}/list?${R(c)}`,r??{})}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,r={}){let n=r.transactionId??this.activeTxId,i=n?{"X-Transaction-ID":n}:void 0,a=r.skipHooks?"?skipHooks=true":"";return this.request("POST",`/v1/entity/${e}/submit${a}`,t,!0,i)}delete(e,t,r={}){let n=new URLSearchParams;r.hard&&n.set("hard","true"),r.skipHooks&&n.set("skipHooks","true");let i=n.size?`?${n}`:"",a=r.transactionId??this.activeTxId,o=a?{"X-Transaction-ID":a}:void 0;return this.request("POST",`/v1/entity/${e}/delete/${t}${i}`,void 0,!0,o)}history(e,t,r={}){return this.request("GET",`/v1/entity/${e}/history/${t}?${R({page:1,limit:50,...r})}`)}rollback(e,t){return this.request("POST",`/v1/entity/${e}/rollback/${t}`)}push(e,t,r={}){return this.submit(e,t,r)}pushLogList(e={}){return this.list("push_log",e)}registerPushDevice(e,t,r,n={}){let{platform:i,deviceType:a,browser:o,browserVersion:c,pushEnabled:p=!0,transactionId:l}=n;return this.submit("account_device",{id:t,account_seq:e,push_token:r,push_enabled:p,...i?{platform:i}:{},...a?{device_type:a}:{},...o?{browser:o}:{},...c?{browser_version:c}:{}},{transactionId:l})}updatePushDeviceToken(e,t,r={}){let{pushEnabled:n=!0,transactionId:i}=r;return this.submit("account_device",{seq:e,push_token:t,push_enabled:n},{transactionId:i})}disablePushDevice(e,t={}){return this.submit("account_device",{seq:e,push_enabled:!1},{transactionId:t.transactionId})}readRequestBody(e,t="application/json",r=!1){let n=f(this.hmacSecret,this.token);return P(e,t,r,n,this.packetMagicLen)}get _reqOpts(){return{baseUrl:this.baseUrl,token:this.token,apiKey:this.apiKey,hmacSecret:this.hmacSecret,packetMagicLen:this.packetMagicLen,encryptRequests:this.encryptRequests}}request(e,t,r,n=!0,i){return w(this._reqOpts,e,t,r,n,i)}};var Y=new m;export{m as EntityServerClient,Y as entityServer};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["// @ts-ignore\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha\";\n// @ts-ignore\nimport { sha256 } from \"@noble/hashes/sha2\";\n// @ts-ignore\nimport { hkdf } from \"@noble/hashes/hkdf\";\n// @ts-ignore\nimport { hmac } from \"@noble/hashes/hmac\";\n\n/**\n * \uC5D4\uD2F0\uD2F0 \uBAA9\uB85D \uC870\uD68C \uD30C\uB77C\uBBF8\uD130\uC785\uB2C8\uB2E4.\n *\n * ```ts\n * client.list(\"post\", {\n * page: 1, limit: 10,\n * orderBy: \"created_time\", orderDir: \"DESC\",\n * fields: [\"seq\", \"title\", \"created_time\"],\n * conditions: { status: \"active\" },\n * });\n * ```\n */\nexport interface EntityListParams {\n /** \uC870\uD68C \uD398\uC774\uC9C0 \uBC88\uD638. \uAE30\uBCF8\uAC12: `1` */\n page?: number;\n /** \uD398\uC774\uC9C0\uB2F9 \uB808\uCF54\uB4DC \uC218. \uAE30\uBCF8\uAC12: `20` */\n limit?: number;\n /** \uC815\uB82C \uAE30\uC900 \uD544\uB4DC\uBA85 */\n orderBy?: string;\n /** \uC815\uB82C \uBC29\uD5A5. \uAE30\uBCF8\uAC12: `\"ASC\"` */\n orderDir?: \"ASC\" | \"DESC\";\n /**\n * \uBC18\uD658\uD560 \uD544\uB4DC \uBAA9\uB85D.\n *\n * - **\uBBF8\uC9C0\uC815 (\uAE30\uBCF8\uAC12)**: \uC5D4\uD2F0\uD2F0\uC758 \uC778\uB371\uC2A4 \uD544\uB4DC\uB9CC \uBC18\uD658\uD569\uB2C8\uB2E4.\n * \uBCF5\uD638\uD654\uB97C \uAC74\uB108\uB6F0\uAE30 \uB54C\uBB38\uC5D0 **\uAC00\uC7A5 \uBE60\uB985\uB2C8\uB2E4**.\n * - `[\"*\"]`: \uC804\uCCB4 \uD544\uB4DC \uBC18\uD658 (\uBCF5\uD638\uD654 \uC218\uD589).\n * - \uD544\uB4DC\uBA85 \uBAA9\uB85D: \uD574\uB2F9 \uD544\uB4DC\uB9CC \uBC18\uD658\uD569\uB2C8\uB2E4.\n * \uC5D4\uD2F0\uD2F0 \uC124\uC815\uC5D0 `index`\uB85C \uC120\uC5B8\uB41C \uD544\uB4DC\uB9CC \uC9C0\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.\n * \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uD544\uB4DC\uBA85\uC744 \uC9C0\uC815\uD558\uBA74 \uC11C\uBC84 \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD569\uB2C8\uB2E4.\n * - `seq`, `created_time`, `updated_time`, `license_seq`\uB294 \uD544\uB4DC\uC5D0 \uAD00\uACC4\uC5C6\uC774 \uD56D\uC0C1 \uD3EC\uD568\uB429\uB2C8\uB2E4.\n *\n * ```ts\n * // \uAE30\uBCF8\uAC12 (\uC778\uB371\uC2A4 \uD544\uB4DC\uB9CC, \uAC00\uC7A5 \uBE60\uB984)\n * client.list(\"account\")\n * // \uC804\uCCB4 \uD544\uB4DC\n * client.list(\"account\", { fields: [\"*\"] })\n * // seq, name, email\uB9CC\n * client.list(\"account\", { fields: [\"seq\", \"name\", \"email\"] })\n * ```\n */\n fields?: string[];\n /** \uD544\uD130 \uC870\uAC74. POST body\uB85C \uC804\uB2EC\uB429\uB2C8\uB2E4. (\uC608: `{ status: \"active\" }`) */\n conditions?: Record<string, unknown>;\n}\n\n/**\n * `query()` \uBA54\uC11C\uB4DC\uC5D0 \uC804\uB2EC\uD558\uB294 SQL \uCFFC\uB9AC \uC694\uCCAD\uC785\uB2C8\uB2E4.\n *\n * - `sql`: SELECT \uC804\uC6A9 SQL. \uC778\uB371\uC2A4 \uD14C\uC774\uBE14\uB9CC \uC870\uD68C \uAC00\uB2A5\uD558\uBA70 JOIN \uC9C0\uC6D0.\n * - `params`: SQL \uBC14\uC778\uB529 \uD30C\uB77C\uBBF8\uD130 (`?` \uD50C\uB808\uC774\uC2A4\uD640\uB354 \uB300\uC751).\n * - `limit`: \uCD5C\uB300 \uBC18\uD658 \uAC74\uC218 (\uCD5C\uB300 1000. \uBBF8\uC9C0\uC815 \uC2DC \uC11C\uBC84 \uAE30\uBCF8\uAC12 \uC801\uC6A9).\n *\n * ```ts\n * client.query(\"order\", {\n * sql: `SELECT o.seq, o.status, u.name\n * FROM order o\n * JOIN account u ON u.data_seq = o.account_seq\n * WHERE o.status = ?`,\n * params: [\"pending\"],\n * limit: 100,\n * });\n * ```\n */\nexport interface EntityQueryRequest {\n sql: string;\n params?: unknown[];\n limit?: number;\n}\n\nexport interface RegisterPushDeviceOptions {\n platform?: string;\n deviceType?: string;\n browser?: string;\n browserVersion?: string;\n pushEnabled?: boolean;\n transactionId?: string;\n}\n\n/** EntityServerClient \uC0DD\uC131/\uC124\uC815 \uC635\uC158\uC785\uB2C8\uB2E4. */\nexport interface EntityServerClientOptions {\n baseUrl?: string;\n token?: string;\n packetMagicLen?: number;\n /**\n * `true`\uC774\uBA74 \uC778\uC99D\uB41C POST/PUT \uC694\uCCAD \uBC14\uB514\uB97C XChaCha20-Poly1305\uB85C \uC554\uD638\uD654\uD569\uB2C8\uB2E4.\n *\n * \uC11C\uBC84\uC758 `EnablePacketEncryption`\uC774 \uD65C\uC131\uD654\uB41C \uACBD\uC6B0 \uD544\uC218\uB85C \uC124\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.\n * \uB85C\uADF8\uC778(`login()`)\u00B7\uD1A0\uD070 \uAC31\uC2E0(`refreshToken()`)\uC740 \uC778\uC99D \uC804 \uC694\uCCAD\uC774\uBBC0\uB85C \uC790\uB3D9\uC73C\uB85C \uAC74\uB108\uB701\uB2C8\uB2E4.\n *\n * \uAE30\uBCF8\uAC12: `false`\n */\n encryptRequests?: boolean;\n /**\n * `true`\uC774\uBA74 `login()` \uC131\uACF5 \uD6C4 Access Token \uB9CC\uB8CC \uC804\uC5D0 \uC790\uB3D9\uC73C\uB85C \uAC31\uC2E0(silent refresh)\uD569\uB2C8\uB2E4.\n * \uAC31\uC2E0 \uC2DC\uC810\uC740 `expires_in - refreshBuffer` \uCD08\uC785\uB2C8\uB2E4.\n *\n * \uAC31\uC2E0 \uC131\uACF5 \uC2DC `onTokenRefreshed`, \uC2E4\uD328 \uC2DC `onSessionExpired` \uCF5C\uBC31\uC774 \uD638\uCD9C\uB429\uB2C8\uB2E4.\n *\n * \uAE30\uBCF8\uAC12: `false`\n */\n keepSession?: boolean;\n /**\n * \uB9CC\uB8CC \uBA87 \uCD08 \uC804\uC5D0 \uC790\uB3D9 \uAC31\uC2E0\uC744 \uC2DC\uB3C4\uD560\uC9C0 \uC124\uC815\uD569\uB2C8\uB2E4.\n *\n * \uC608: `expires_in = 3600`, `refreshBuffer = 60` \u2192 3540\uCD08 \uD6C4 \uAC31\uC2E0\n *\n * \uAE30\uBCF8\uAC12: `60`\n */\n refreshBuffer?: number;\n /**\n * \uC790\uB3D9 \uAC31\uC2E0 \uC131\uACF5 \uC2DC \uD638\uCD9C\uB418\uB294 \uCF5C\uBC31\uC785\uB2C8\uB2E4.\n * \uC0C8 `access_token`\uACFC `expires_in`\uC774 \uC804\uB2EC\uB429\uB2C8\uB2E4.\n * \uC571\uC740 \uC774 \uCF5C\uBC31\uC5D0\uC11C localStorage \uB4F1\uC5D0 \uD1A0\uD070\uC744 \uC800\uC7A5\uD574\uC57C \uD569\uB2C8\uB2E4.\n */\n onTokenRefreshed?: (accessToken: string, expiresIn: number) => void;\n /**\n * \uC138\uC158 \uC720\uC9C0 \uAC31\uC2E0 \uC2E4\uD328 \uC2DC \uD638\uCD9C\uB418\uB294 \uCF5C\uBC31\uC785\uB2C8\uB2E4.\n * refresh_token \uB9CC\uB8CC \uB4F1\uC73C\uB85C \uC7AC\uBC1C\uAE09\uC774 \uBD88\uAC00\uB2A5\uD55C \uACBD\uC6B0\uC785\uB2C8\uB2E4.\n * \uC571\uC740 \uC774 \uCF5C\uBC31\uC5D0\uC11C \uB85C\uADF8\uC778 \uD398\uC774\uC9C0\uB85C \uC774\uB3D9\uD558\uB294 \uB4F1\uC758 \uCC98\uB9AC\uB97C \uD574\uC57C \uD569\uB2C8\uB2E4.\n */\n onSessionExpired?: (error: Error) => void;\n /**\n * HMAC \uC778\uC99D\uC6A9 API Key (`X-API-Key` \uD5E4\uB354).\n * `hmacSecret`\uACFC \uD568\uAED8 \uC124\uC815\uD558\uBA74 HMAC \uC778\uC99D \uBAA8\uB4DC\uB85C \uB3D9\uC791\uD569\uB2C8\uB2E4.\n * **\uC11C\uBC84 \uC0AC\uC774\uB4DC(Node.js \uB4F1) \uC804\uC6A9. \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.**\n */\n apiKey?: string;\n /**\n * HMAC \uC778\uC99D \uC2DC\uD06C\uB9BF. `apiKey`\uC640 \uD568\uAED8 \uC124\uC815\uD558\uBA74 HMAC \uC778\uC99D \uBAA8\uB4DC\uB85C \uB3D9\uC791\uD569\uB2C8\uB2E4.\n *\n * \uD328\uD0B7 \uC554\uD638\uD654 \uD0A4\uB3C4 \uC774 \uAC12\uC5D0\uC11C HKDF-SHA256\uC73C\uB85C \uC720\uB3C4\uD569\uB2C8\uB2E4:\n * `key = HKDF-SHA256(hmac_secret, info=\"entity-server:packet-encryption\", salt=\"entity-server:hkdf:v1\")`\n *\n * **\uC11C\uBC84 \uC0AC\uC774\uB4DC(Node.js \uB4F1) \uC804\uC6A9. \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.**\n */\n hmacSecret?: string;\n}\n\n/**\n * `list()`, `history()` \uC751\uB2F5\uC758 `data` \uD544\uB4DC \uAD6C\uC870\uC785\uB2C8\uB2E4.\n *\n * \uC11C\uBC84\uB294 \uD56D\uC0C1 \uC774 \uAD6C\uC870\uB85C \uBC18\uD658\uD569\uB2C8\uB2E4:\n * ```json\n * { \"ok\": true, \"data\": { \"items\": [...], \"total\": 100, \"page\": 1, \"limit\": 20 } }\n * ```\n */\nexport interface EntityListResult<T = unknown> {\n items: T[];\n /** \uC804\uCCB4 \uB808\uCF54\uB4DC \uC218 */\n total: number;\n /** \uD604\uC7AC \uD398\uC774\uC9C0 \uBC88\uD638 */\n page: number;\n /** \uD398\uC774\uC9C0\uB2F9 \uB808\uCF54\uB4DC \uC218 */\n limit: number;\n}\n\n/**\n * `history()` \uC751\uB2F5\uC758 \uAC1C\uBCC4 \uC774\uB825 \uB808\uCF54\uB4DC \uAD6C\uC870\uC785\uB2C8\uB2E4.\n *\n * - `action`: `\"INSERT\"` | `\"UPDATE\"` | `\"DELETE_SOFT\"` | `\"DELETE_HARD\"` | `\"ROLLBACK\"`\n * - `data_snapshot`: \uBCC0\uACBD \uB2F9\uC2DC \uC5D4\uD2F0\uD2F0 \uB370\uC774\uD130 \uC2A4\uB0C5\uC0F7\n */\nexport interface EntityHistoryRecord<T = unknown> {\n seq: number;\n action:\n | \"INSERT\"\n | \"UPDATE\"\n | \"DELETE_SOFT\"\n | \"DELETE_HARD\"\n | \"ROLLBACK\"\n | string;\n data_snapshot: T | null;\n changed_by: number | null;\n changed_time: string;\n}\n\n/** Vite \uD658\uACBD\uBCC0\uC218(`import.meta.env`)\uC5D0\uC11C \uAC12\uC744 \uC77D\uC2B5\uB2C8\uB2E4. */\nfunction readEnv(name: string): string | undefined {\n const meta = import.meta as unknown as {\n env?: Record<string, string | undefined>;\n };\n return meta?.env?.[name];\n}\n\nexport class EntityServerClient {\n private baseUrl: string;\n private token: string;\n private apiKey: string;\n private hmacSecret: string;\n private packetMagicLen: number;\n private encryptRequests: boolean;\n private activeTxId: string | null = null;\n\n // \uC138\uC158 \uC720\uC9C0 \uAD00\uB828\n private keepSession: boolean;\n private refreshBuffer: number;\n private onTokenRefreshed?: (accessToken: string, expiresIn: number) => void;\n private onSessionExpired?: (error: Error) => void;\n private _sessionRefreshToken: string | null = null;\n private _refreshTimer: ReturnType<typeof setTimeout> | null = null;\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 `http://localhost:47200`\n * - `packetMagicLen`: `VITE_ENTITY_SERVER_PACKET_MAGIC_LEN` \uB610\uB294 `4`\n */\n constructor(options: EntityServerClientOptions = {}) {\n const envBaseUrl = readEnv(\"VITE_ENTITY_SERVER_URL\");\n const envMagicLen = readEnv(\"VITE_ENTITY_SERVER_PACKET_MAGIC_LEN\");\n\n this.baseUrl = (\n options.baseUrl ??\n envBaseUrl ??\n \"http://localhost:47200\"\n ).replace(/\\/$/, \"\");\n\n this.token = options.token ?? \"\";\n this.apiKey = options.apiKey ?? \"\";\n this.hmacSecret = options.hmacSecret ?? \"\";\n this.packetMagicLen =\n options.packetMagicLen ?? (envMagicLen ? Number(envMagicLen) : 4);\n this.encryptRequests = options.encryptRequests ?? false;\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, packetMagicLen, encryptRequests \uAC12\uC744 \uB7F0\uD0C0\uC784\uC5D0 \uAC31\uC2E0\uD569\uB2C8\uB2E4. */\n configure(options: Partial<EntityServerClientOptions>): void {\n if (options.baseUrl) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n }\n if (typeof options.token === \"string\") {\n this.token = options.token;\n }\n if (typeof options.packetMagicLen === \"number\") {\n this.packetMagicLen = options.packetMagicLen;\n }\n if (typeof options.encryptRequests === \"boolean\") {\n this.encryptRequests = options.encryptRequests;\n }\n if (typeof options.apiKey === \"string\") {\n this.apiKey = options.apiKey;\n }\n if (typeof options.hmacSecret === \"string\") {\n this.hmacSecret = options.hmacSecret;\n }\n if (typeof options.keepSession === \"boolean\") {\n this.keepSession = options.keepSession;\n }\n if (typeof options.refreshBuffer === \"number\") {\n this.refreshBuffer = options.refreshBuffer;\n }\n if (options.onTokenRefreshed) {\n this.onTokenRefreshed = options.onTokenRefreshed;\n }\n if (options.onSessionExpired) {\n this.onSessionExpired = options.onSessionExpired;\n }\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 /** 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 \uD328\uD0B7 magic \uAE38\uC774(`packet_magic_len`)\uB97C \uC124\uC815\uD569\uB2C8\uB2E4. */\n setPacketMagicLen(length: number): void {\n this.packetMagicLen = length;\n }\n\n /** \uD604\uC7AC \uC554\uD638\uD654 \uD328\uD0B7 magic \uAE38\uC774\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4. */\n getPacketMagicLen(): number {\n return this.packetMagicLen;\n }\n\n /**\n * \uC790\uB3D9 \uD1A0\uD070 \uAC31\uC2E0 \uD0C0\uC774\uBA38\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.\n * @param refreshToken \uAC31\uC2E0\uC5D0 \uC0AC\uC6A9\uD560 Refresh Token\n * @param expiresIn Access Token\uC758 \uC720\uD6A8 \uAE30\uAC04 (\uCD08)\n */\n private _scheduleKeepSession(\n refreshToken: string,\n expiresIn: number,\n ): void {\n this._clearRefreshTimer();\n this._sessionRefreshToken = refreshToken;\n\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 this.refreshToken(\n this._sessionRefreshToken,\n );\n this.onTokenRefreshed?.(result.access_token, result.expires_in);\n // \uAC31\uC2E0 \uC131\uACF5 \uC2DC \uB2E4\uC74C \uB9CC\uB8CC \uC804 \uD0C0\uC774\uBA38 \uC7AC\uC608\uC57D\n this._scheduleKeepSession(\n this._sessionRefreshToken,\n result.expires_in,\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 /** \uC790\uB3D9 \uAC31\uC2E0 \uD0C0\uC774\uBA38\uB97C \uC815\uB9AC\uD569\uB2C8\uB2E4. */\n private _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\uBBC0\uB85C \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 /**\n * \uC11C\uBC84 \uD5EC\uC2A4 \uCCB4\uD06C\uB97C \uC218\uD589\uD558\uACE0 \uD328\uD0B7 \uC554\uD638\uD654 \uD65C\uC131 \uC5EC\uBD80\uB97C \uC790\uB3D9\uC73C\uB85C \uAC10\uC9C0\uD569\uB2C8\uB2E4.\n *\n * \uC11C\uBC84\uAC00 `packet_encryption: true`\uB97C \uC751\uB2F5\uD558\uBA74 \uC774\uD6C4 \uBAA8\uB4E0 \uC694\uCCAD\uC5D0 \uC554\uD638\uD654\uAC00 \uC790\uB3D9 \uC801\uC6A9\uB429\uB2C8\uB2E4.\n * \uCD08\uAE30\uD654 \uC9C1\uD6C4 \uB610\uB294 \uB85C\uADF8\uC778 \uC804\uC5D0 \uD638\uCD9C\uD558\uBA74 \uC554\uD638\uD654 \uC124\uC815\uC744 \uC790\uB3D9\uC73C\uB85C \uAD6C\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n *\n * ```ts\n * await client.checkHealth();\n * await client.login(email, password); // \uC774\uD6C4 \uC694\uCCAD\uC740 \uC554\uD638\uD654 \uC790\uB3D9 \uC801\uC6A9\n * ```\n *\n * @returns `{ ok: true }` \uB610\uB294 `{ ok: true, packet_encryption: true }`\n */\n async checkHealth(): Promise<{ ok: boolean; packet_encryption?: boolean }> {\n const res = await fetch(`${this.baseUrl}/v1/health`, {\n signal: AbortSignal.timeout(3000),\n });\n const data = (await res.json()) as {\n ok: boolean;\n packet_encryption?: boolean;\n };\n if (data.packet_encryption) {\n this.encryptRequests = true;\n }\n return data;\n }\n\n /** \uB85C\uADF8\uC778 \uD6C4 `access_token`\uC744 \uB0B4\uBD80 \uC0C1\uD0DC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4. */\n async login(\n email: string,\n password: string,\n ): Promise<{\n access_token: string;\n refresh_token: string;\n expires_in: number;\n }> {\n const data = await this.request<{\n data: {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n };\n }>(\"POST\", \"/v1/auth/login\", { email, passwd: password }, false);\n this.token = data.data.access_token;\n if (this.keepSession) {\n this._scheduleKeepSession(\n data.data.refresh_token,\n data.data.expires_in,\n );\n }\n return data.data;\n }\n\n /** Refresh Token\uC73C\uB85C Access Token\uC744 \uC7AC\uBC1C\uAE09\uBC1B\uC544 \uB0B4\uBD80 \uD1A0\uD070\uC744 \uAD50\uCCB4\uD569\uB2C8\uB2E4. */\n async refreshToken(\n refreshToken: string,\n ): Promise<{ access_token: string; expires_in: number }> {\n const data = await this.request<{\n data: { access_token: string; expires_in: number };\n }>(\"POST\", \"/v1/auth/refresh\", { refresh_token: refreshToken }, false);\n this.token = data.data.access_token;\n if (this.keepSession) {\n this._scheduleKeepSession(refreshToken, data.data.expires_in);\n }\n return data.data;\n }\n\n /**\n * \uC11C\uBC84\uC5D0 \uB85C\uADF8\uC544\uC6C3\uC744 \uC694\uCCAD\uD558\uACE0 \uB0B4\uBD80 \uD1A0\uD070\uC744 \uCD08\uAE30\uD654\uD569\uB2C8\uB2E4.\n * refresh_token\uC744 \uC11C\uBC84\uC5D0 \uC804\uB2EC\uD574 \uBB34\uD6A8\uD654\uD569\uB2C8\uB2E4.\n */\n async logout(refreshToken: string): Promise<{ ok: boolean }> {\n this.stopKeepSession();\n const data = await this.request<{ ok: boolean }>(\n \"POST\",\n \"/v1/auth/logout\",\n { refresh_token: refreshToken },\n false,\n );\n this.token = \"\";\n return data;\n }\n\n /** \uD2B8\uB79C\uC7AD\uC158\uC744 \uC2DC\uC791\uD558\uACE0 \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158 ID\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. */\n async transStart(): Promise<string> {\n const res = await this.request<{ ok: boolean; transaction_id: string }>(\n \"POST\",\n \"/v1/transaction/start\",\n undefined,\n false,\n );\n this.activeTxId = res.transaction_id;\n return this.activeTxId;\n }\n\n /** \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158(\uB610\uB294 \uC804\uB2EC\uB41C transactionId)\uC744 \uB864\uBC31\uD569\uB2C8\uB2E4. */\n transRollback(transactionId?: string): Promise<{ ok: boolean }> {\n const txId = transactionId ?? this.activeTxId;\n if (!txId) {\n return Promise.reject(\n new Error(\"No active transaction. Call transStart() first.\"),\n );\n }\n this.activeTxId = null;\n return this.request(\"POST\", `/v1/transaction/rollback/${txId}`);\n }\n\n /** \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158(\uB610\uB294 \uC804\uB2EC\uB41C transactionId)\uC744 \uCEE4\uBC0B\uD569\uB2C8\uB2E4.\n *\n * @returns `results` \uBC30\uC5F4: commit\uB41C \uAC01 \uC791\uC5C5\uC758 `entity`, `action`, `seq`\n */\n transCommit(transactionId?: string): Promise<{\n ok: boolean;\n results: Array<{ entity: string; action: string; seq: number }>;\n }> {\n const txId = transactionId ?? this.activeTxId;\n if (!txId) {\n return Promise.reject(\n new Error(\"No active transaction. Call transStart() first.\"),\n );\n }\n this.activeTxId = null;\n return this.request(\"POST\", `/v1/transaction/commit/${txId}`);\n }\n\n /** \uC2DC\uD000\uC2A4 ID\uB85C \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n get<T = unknown>(\n entity: string,\n seq: number,\n opts: { skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; data: T }> {\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n return this.request(\"GET\", `/v1/entity/${entity}/${seq}${q}`);\n }\n\n /** \uC870\uAC74\uC73C\uB85C \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. data \uCEEC\uB7FC\uC744 \uC644\uC804\uD788 \uBCF5\uD638\uD654\uD558\uC5EC \uBC18\uD658\uD569\uB2C8\uB2E4. */\n find<T = unknown>(\n entity: string,\n conditions?: Record<string, unknown>,\n opts: { skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; data: T }> {\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/find${q}`,\n conditions ?? {},\n );\n }\n\n /** \uD398\uC774\uC9C0\uB124\uC774\uC158/\uC815\uB82C/\uD544\uD130 \uC870\uAC74\uC73C\uB85C \uC5D4\uD2F0\uD2F0 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n list<T = unknown>(\n entity: string,\n params: EntityListParams = {},\n ): Promise<{ ok: boolean; data: EntityListResult<T> }> {\n const { conditions, fields, orderDir, orderBy, ...rest } = params;\n\n const queryObj: Record<string, unknown> = {\n page: 1,\n limit: 20,\n ...rest,\n };\n if (orderBy) {\n queryObj.orderBy = orderDir === \"DESC\" ? `-${orderBy}` : orderBy;\n }\n if (fields?.length) {\n queryObj.fields = fields.join(\",\");\n }\n\n const q = buildQuery(queryObj);\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/list?${q}`,\n conditions ?? {},\n );\n }\n\n /**\n * \uC5D4\uD2F0\uD2F0 \uCD1D \uAC74\uC218\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.\n *\n * @param conditions \uD544\uD130 \uC870\uAC74 (\uC608: `{ status: \"active\" }`)\n */\n count(\n entity: string,\n conditions?: Record<string, unknown>,\n ): Promise<{ ok: boolean; count: number }> {\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/count`,\n conditions ?? {},\n );\n }\n\n /**\n * \uCEE4\uC2A4\uD140 SQL\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.\n *\n * SELECT \uC804\uC6A9\uC774\uBA70 \uC778\uB371\uC2A4 \uD14C\uC774\uBE14\uB9CC \uC870\uD68C \uAC00\uB2A5\uD569\uB2C8\uB2E4.\n * JOIN\uC744 \uC0AC\uC6A9\uD574 \uC5EC\uB7EC \uC5D4\uD2F0\uD2F0\uB97C \uC870\uD569\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n * `entity`\uB294 SQL\uC5D0 \uD3EC\uD568\uB41C \uAE30\uBCF8 \uC5D4\uD2F0\uD2F0\uBA85(\uB77C\uC6B0\uD2B8 \uACBD\uB85C\uC6A9)\uC785\uB2C8\uB2E4.\n */\n query<T = unknown>(\n entity: string,\n req: EntityQueryRequest,\n ): Promise<{ ok: boolean; data: { items: T[]; count: number } }> {\n return this.request(\"POST\", `/v1/entity/${entity}/query`, req);\n }\n\n /** \uC5D4\uD2F0\uD2F0 \uB370\uC774\uD130\uB97C \uC0DD\uC131/\uC218\uC815(Submit)\uD569\uB2C8\uB2E4. `seq`\uAC00 \uC5C6\uC73C\uBA74 INSERT, \uC788\uC73C\uBA74 UPDATE\uC785\uB2C8\uB2E4. */\n submit(\n entity: string,\n data: Record<string, unknown>,\n opts: { transactionId?: string; skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const txId = opts.transactionId ?? this.activeTxId;\n const extraHeaders = txId ? { \"X-Transaction-ID\": txId } : undefined;\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/submit${q}`,\n data,\n true,\n extraHeaders,\n );\n }\n\n /** \uC2DC\uD000\uC2A4 ID\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4(`hard=true`\uBA74 \uD558\uB4DC \uC0AD\uC81C, \uAE30\uBCF8\uC740 \uC18C\uD504\uD2B8 \uC0AD\uC81C). */\n delete(\n entity: string,\n seq: number,\n opts: {\n transactionId?: string;\n hard?: boolean;\n skipHooks?: boolean;\n } = {},\n ): Promise<{ ok: boolean; deleted: number }> {\n const params = new URLSearchParams();\n if (opts.hard) params.set(\"hard\", \"true\");\n if (opts.skipHooks) params.set(\"skipHooks\", \"true\");\n const q = params.size ? `?${params}` : \"\";\n const txId = opts.transactionId ?? this.activeTxId;\n const extraHeaders = txId ? { \"X-Transaction-ID\": txId } : undefined;\n\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/delete/${seq}${q}`,\n undefined,\n true,\n extraHeaders,\n );\n }\n\n /** \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC758 \uBCC0\uACBD \uC774\uB825\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uC774\uB825 \uD56D\uBAA9\uB2F9 `action`, `data_snapshot`, `changed_by`, `changed_time`\uC744 \uD3EC\uD568\uD569\uB2C8\uB2E4. */\n history<T = unknown>(\n entity: string,\n seq: number,\n params: Pick<EntityListParams, \"page\" | \"limit\"> = {},\n ): Promise<{\n ok: boolean;\n data: EntityListResult<EntityHistoryRecord<T>>;\n }> {\n const q = buildQuery({ page: 1, limit: 50, ...params });\n return this.request(\"GET\", `/v1/entity/${entity}/history/${seq}?${q}`);\n }\n\n /** \uD2B9\uC815 \uC774\uB825 \uC2DC\uC810\uC73C\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uB864\uBC31\uD569\uB2C8\uB2E4. */\n rollback(entity: string, historySeq: number): Promise<{ ok: boolean }> {\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/rollback/${historySeq}`,\n );\n }\n\n /** \uD478\uC2DC \uAD00\uB828 \uC5D4\uD2F0\uD2F0\uB85C payload\uB97C \uC804\uC1A1(Submit)\uD569\uB2C8\uB2E4. */\n push(\n pushEntity: string,\n payload: Record<string, unknown>,\n opts: { transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n return this.submit(pushEntity, payload, opts);\n }\n\n /** \uD478\uC2DC \uB85C\uADF8 \uC5D4\uD2F0\uD2F0 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n pushLogList<T = unknown>(\n params: EntityListParams = {},\n ): Promise<{ ok: boolean; data: EntityListResult<T> }> {\n return this.list<T>(\"push_log\", params);\n }\n\n /** \uACC4\uC815\uC758 \uD478\uC2DC \uB514\uBC14\uC774\uC2A4\uB97C \uB4F1\uB85D\uD569\uB2C8\uB2E4. */\n registerPushDevice(\n accountSeq: number,\n deviceId: string,\n pushToken: string,\n opts: RegisterPushDeviceOptions = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const {\n platform,\n deviceType,\n browser,\n browserVersion,\n pushEnabled = true,\n transactionId,\n } = opts;\n\n return this.submit(\n \"account_device\",\n {\n id: deviceId,\n account_seq: accountSeq,\n push_token: pushToken,\n push_enabled: pushEnabled,\n ...(platform ? { platform } : {}),\n ...(deviceType ? { device_type: deviceType } : {}),\n ...(browser ? { browser } : {}),\n ...(browserVersion ? { browser_version: browserVersion } : {}),\n },\n { transactionId },\n );\n }\n\n /** \uB514\uBC14\uC774\uC2A4 \uB808\uCF54\uB4DC\uC758 \uD478\uC2DC \uD1A0\uD070\uC744 \uAC31\uC2E0\uD569\uB2C8\uB2E4. */\n updatePushDeviceToken(\n deviceSeq: number,\n pushToken: string,\n opts: { pushEnabled?: boolean; transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const { pushEnabled = true, transactionId } = opts;\n return this.submit(\n \"account_device\",\n {\n seq: deviceSeq,\n push_token: pushToken,\n push_enabled: pushEnabled,\n },\n { transactionId },\n );\n }\n\n /** \uB514\uBC14\uC774\uC2A4\uC758 \uD478\uC2DC \uC218\uC2E0\uC744 \uBE44\uD65C\uC131\uD654\uD569\uB2C8\uB2E4. */\n disablePushDevice(\n deviceSeq: number,\n opts: { transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n return this.submit(\n \"account_device\",\n {\n seq: deviceSeq,\n push_enabled: false,\n },\n { transactionId: opts.transactionId },\n );\n }\n\n /**\n * \uC694\uCCAD \uBC14\uB514\uB97C \uD30C\uC2F1\uD558\uACE0 `application/octet-stream`\uC778 \uACBD\uC6B0 \uBCF5\uD638\uD654\uD569\uB2C8\uB2E4.\n *\n * \uC6D0\uC2DC \uC554\uD638\uD654 payload\uB97C \uC9C1\uC811 \uB2E4\uB8E8\uB294 \uD074\uB77C\uC774\uC5B8\uD2B8\uC5D0\uC11C \uC0AC\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 lowered = contentType.toLowerCase();\n const isEncrypted = lowered.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) {\n throw new Error(\"Encrypted request body is empty\");\n }\n if (body instanceof ArrayBuffer) {\n return this.decryptPacket<T>(body);\n }\n if (body instanceof Uint8Array) {\n const sliced = body.buffer.slice(\n body.byteOffset,\n body.byteOffset + body.byteLength,\n );\n return this.decryptPacket<T>(sliced as ArrayBuffer);\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\n /**\n * \uACF5\uD1B5 HTTP \uC694\uCCAD \uD568\uC218\uC785\uB2C8\uB2E4.\n *\n * - `encryptRequests`\uAC00 \uD65C\uC131\uD654\uB41C \uC778\uC99D \uC694\uCCAD\uC758 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 */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n withAuth = true,\n extraHeaders: Record<string, string> = {},\n ): Promise<T> {\n const isHmacMode = withAuth && !!(this.apiKey && this.hmacSecret);\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...extraHeaders,\n };\n if (!isHmacMode && withAuth && this.token) {\n headers.Authorization = `Bearer ${this.token}`;\n }\n\n // \uC694\uCCAD \uBC14\uB514 \uACB0\uC815: encryptRequests \uD65C\uC131\uD654 \uC2DC POST \uBC14\uB514\uB97C \uC554\uD638\uD654\uD569\uB2C8\uB2E4.\n // - \uB85C\uADF8\uC778/\uD1A0\uD070 \uAC31\uC2E0(withAuth=false)\uC740 \uC554\uD638\uD654\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.\n // - GET \uC740 \uBC14\uB514\uAC00 \uC5C6\uC73C\uBBC0\uB85C \uAC74\uB108\uB701\uB2C8\uB2E4.\n // - HMAC \uBAA8\uB4DC\uB294 token \uC5C6\uC774\uB3C4 hmacSecret \uC774 \uC788\uC73C\uBA74 \uC554\uD638\uD654\uD569\uB2C8\uB2E4.\n let fetchBody: string | Uint8Array | null = null;\n if (body != null) {\n const shouldEncrypt =\n this.encryptRequests &&\n withAuth &&\n (this.token || isHmacMode) &&\n method !== \"GET\" &&\n method !== \"HEAD\";\n\n if (shouldEncrypt) {\n const plaintext = new TextEncoder().encode(\n JSON.stringify(body),\n );\n const encrypted = this.encryptPacket(plaintext);\n headers[\"Content-Type\"] = \"application/octet-stream\";\n fetchBody = encrypted;\n } else {\n fetchBody = JSON.stringify(body);\n }\n }\n\n // HMAC \uBAA8\uB4DC: X-API-Key / X-Timestamp / X-Nonce / X-Signature \uD5E4\uB354 \uCD94\uAC00\n if (isHmacMode) {\n const timestamp = String(Math.floor(Date.now() / 1000));\n const nonce = crypto.randomUUID();\n const bodyBytes =\n fetchBody instanceof Uint8Array\n ? fetchBody\n : typeof fetchBody === \"string\"\n ? new TextEncoder().encode(fetchBody)\n : new Uint8Array(0);\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 const sig = hmac(\n sha256,\n new TextEncoder().encode(this.hmacSecret),\n payload,\n );\n const signature = [...sig]\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n headers[\"X-API-Key\"] = this.apiKey;\n headers[\"X-Timestamp\"] = timestamp;\n headers[\"X-Nonce\"] = nonce;\n headers[\"X-Signature\"] = signature;\n }\n\n const res = await fetch(this.baseUrl + path, {\n method,\n headers,\n ...(fetchBody != null ? { body: fetchBody as BodyInit } : {}),\n });\n\n const contentType = res.headers.get(\"Content-Type\") ?? \"\";\n\n if (contentType.includes(\"application/octet-stream\")) {\n const buffer = await res.arrayBuffer();\n return this.decryptPacket<T>(buffer);\n }\n\n const data = await res.json();\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 /**\n * \uD328\uD0B7 \uC554\uD638\uD654 \uD0A4\uB97C \uC720\uB3C4\uD569\uB2C8\uB2E4.\n * - HMAC \uBAA8\uB4DC (`hmacSecret` \uC124\uC815 \uC2DC): HKDF-SHA256(hmac_secret, \"entity-server:packet-encryption\")\n * - JWT \uBAA8\uB4DC: sha256(jwt_token)\n */\n private derivePacketKey(): Uint8Array {\n if (this.hmacSecret) {\n const salt = new TextEncoder().encode(\"entity-server:hkdf:v1\");\n const info = new TextEncoder().encode(\n \"entity-server:packet-encryption\",\n );\n return hkdf(\n sha256,\n new TextEncoder().encode(this.hmacSecret),\n salt,\n info,\n 32,\n );\n }\n return sha256(new TextEncoder().encode(this.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:packetMagicLen][random_nonce:24][ciphertext+tag]\n */\n private encryptPacket(plaintext: Uint8Array): Uint8Array {\n const key = this.derivePacketKey();\n const magic = new Uint8Array(this.packetMagicLen);\n const nonce = new Uint8Array(24);\n crypto.getRandomValues(magic);\n crypto.getRandomValues(nonce);\n const cipher = xchacha20poly1305(key, nonce);\n const ciphertext = cipher.encrypt(plaintext);\n const result = new Uint8Array(\n this.packetMagicLen + 24 + ciphertext.length,\n );\n result.set(magic, 0);\n result.set(nonce, this.packetMagicLen);\n result.set(ciphertext, this.packetMagicLen + 24);\n return result;\n }\n\n /** \uC11C\uBC84\uC758 \uC554\uD638\uD654 \uD328\uD0B7\uC744 \uBCF5\uD638\uD654\uD574 JSON \uAC1D\uCCB4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4. */\n private decryptPacket<T>(buffer: ArrayBuffer): T {\n const key = this.derivePacketKey();\n const data = new Uint8Array(buffer);\n\n if (data.length < this.packetMagicLen + 24 + 16) {\n throw new Error(\"Encrypted packet too short\");\n }\n\n const nonce = data.slice(this.packetMagicLen, this.packetMagicLen + 24);\n const ciphertext = data.slice(this.packetMagicLen + 24);\n const cipher = xchacha20poly1305(key, nonce);\n const plaintext = cipher.decrypt(ciphertext);\n return JSON.parse(new TextDecoder().decode(plaintext)) as T;\n }\n}\n\n/** \uCFFC\uB9AC \uD30C\uB77C\uBBF8\uD130 \uAC1D\uCCB4\uB97C URL \uCFFC\uB9AC \uBB38\uC790\uC5F4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4. */\nfunction 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\nexport const entityServer = new EntityServerClient();\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["xchacha20poly1305", "sha256", "hkdf", "hmac", "
|
|
3
|
+
"sources": ["../src/client/utils.ts", "../src/client/packet.ts", "../src/client/hmac.ts", "../src/client/request.ts", "../src/EntityServerClient.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["/** Vite \uD658\uACBD\uBCC0\uC218(`import.meta.env`)\uC5D0\uC11C \uAC12\uC744 \uC77D\uC2B5\uB2C8\uB2E4. */\nexport function readEnv(name: string): string | undefined {\n const meta = import.meta as unknown as {\n env?: Record<string, string | undefined>;\n };\n return meta?.env?.[name];\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", "// @ts-ignore\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha\";\n// @ts-ignore\nimport { sha256 } from \"@noble/hashes/sha2\";\n// @ts-ignore\nimport { hkdf } from \"@noble/hashes/hkdf\";\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: SHA-256(jwt_token)\n */\nexport function derivePacketKey(hmacSecret: string, token: string): Uint8Array {\n if (hmacSecret) {\n const salt = new TextEncoder().encode(\"entity-server:hkdf:v1\");\n const info = new TextEncoder().encode(\n \"entity-server:packet-encryption\",\n );\n return hkdf(\n sha256,\n new TextEncoder().encode(hmacSecret),\n salt,\n info,\n 32,\n );\n }\n return sha256(new TextEncoder().encode(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:magicLen][random_nonce:24][ciphertext+tag]\n */\nexport function encryptPacket(\n plaintext: Uint8Array,\n key: Uint8Array,\n magicLen: number,\n): Uint8Array {\n const magic = new Uint8Array(magicLen);\n const nonce = new Uint8Array(24);\n crypto.getRandomValues(magic);\n crypto.getRandomValues(nonce);\n const cipher = xchacha20poly1305(key, nonce);\n const ciphertext = cipher.encrypt(plaintext);\n const result = new Uint8Array(magicLen + 24 + ciphertext.length);\n result.set(magic, 0);\n result.set(nonce, magicLen);\n result.set(ciphertext, magicLen + 24);\n return result;\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:magicLen][nonce:24][ciphertext+tag]\n */\nexport function decryptPacket<T>(\n buffer: ArrayBuffer,\n key: Uint8Array,\n magicLen: number,\n): T {\n const data = new Uint8Array(buffer);\n if (data.length < magicLen + 24 + 16) {\n throw new Error(\"Encrypted packet too short\");\n }\n const nonce = data.slice(magicLen, magicLen + 24);\n const ciphertext = data.slice(magicLen + 24);\n const cipher = xchacha20poly1305(key, nonce);\n const plaintext = cipher.decrypt(ciphertext);\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 magicLen: number,\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)\n return decryptPacket<T>(body, key, magicLen);\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, magicLen);\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", "// @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", "import { derivePacketKey, encryptPacket, decryptPacket } from \"./packet\";\nimport { buildHmacHeaders } from \"./hmac\";\n\nexport interface RequestOptions {\n baseUrl: string;\n token: string;\n apiKey: string;\n hmacSecret: string;\n packetMagicLen: number;\n encryptRequests: boolean;\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): Promise<T> {\n const {\n baseUrl,\n token,\n apiKey,\n hmacSecret,\n packetMagicLen,\n encryptRequests,\n } = opts;\n const isHmacMode = withAuth && !!(apiKey && hmacSecret);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...extraHeaders,\n };\n if (!isHmacMode && withAuth && token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n let fetchBody: string | Uint8Array | null = null;\n if (body != null) {\n const shouldEncrypt =\n encryptRequests &&\n withAuth &&\n (token || isHmacMode) &&\n method !== \"GET\" &&\n method !== \"HEAD\";\n\n if (shouldEncrypt) {\n const key = derivePacketKey(hmacSecret, token);\n fetchBody = encryptPacket(\n new TextEncoder().encode(JSON.stringify(body)),\n key,\n packetMagicLen,\n );\n headers[\"Content-Type\"] = \"application/octet-stream\";\n } else {\n fetchBody = JSON.stringify(body);\n }\n }\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\n const res = await fetch(baseUrl + path, {\n method,\n headers,\n ...(fetchBody != null ? { body: fetchBody as BodyInit } : {}),\n });\n\n const contentType = res.headers.get(\"Content-Type\") ?? \"\";\n if (contentType.includes(\"application/octet-stream\")) {\n const key = derivePacketKey(hmacSecret, token);\n return decryptPacket<T>(await res.arrayBuffer(), key, packetMagicLen);\n }\n\n const data = await res.json();\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", "import type {\n EntityHistoryRecord,\n EntityListParams,\n EntityListResult,\n EntityQueryRequest,\n EntityServerClientOptions,\n RegisterPushDeviceOptions,\n} from \"./types\";\nimport { readEnv, buildQuery } from \"./client/utils\";\nimport { derivePacketKey, parseRequestBody } from \"./client/packet\";\nimport { entityRequest, type RequestOptions } from \"./client/request\";\n\nexport class EntityServerClient {\n private baseUrl: string;\n private token: string;\n private apiKey: string;\n private hmacSecret: string;\n private packetMagicLen: number;\n private encryptRequests: boolean;\n private activeTxId: string | null = null;\n\n // \uC138\uC158 \uC720\uC9C0 \uAD00\uB828\n private keepSession: boolean;\n private refreshBuffer: number;\n private onTokenRefreshed?: (accessToken: string, expiresIn: number) => void;\n private onSessionExpired?: (error: Error) => void;\n private _sessionRefreshToken: string | null = null;\n private _refreshTimer: ReturnType<typeof setTimeout> | 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 `http://localhost:47200`\n * - `packetMagicLen`: `VITE_ENTITY_SERVER_PACKET_MAGIC_LEN` \uB610\uB294 `4`\n */\n constructor(options: EntityServerClientOptions = {}) {\n const envBaseUrl = readEnv(\"VITE_ENTITY_SERVER_URL\");\n const envMagicLen = readEnv(\"VITE_ENTITY_SERVER_PACKET_MAGIC_LEN\");\n\n this.baseUrl = (\n options.baseUrl ??\n envBaseUrl ??\n \"http://localhost:47200\"\n ).replace(/\\/$/, \"\");\n this.token = options.token ?? \"\";\n this.apiKey = options.apiKey ?? \"\";\n this.hmacSecret = options.hmacSecret ?? \"\";\n this.packetMagicLen =\n options.packetMagicLen ?? (envMagicLen ? Number(envMagicLen) : 4);\n this.encryptRequests = options.encryptRequests ?? false;\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, packetMagicLen, encryptRequests \uAC12\uC744 \uB7F0\uD0C0\uC784\uC5D0 \uAC31\uC2E0\uD569\uB2C8\uB2E4. */\n configure(options: Partial<EntityServerClientOptions>): void {\n if (options.baseUrl) this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n if (typeof options.token === \"string\") this.token = options.token;\n if (typeof options.packetMagicLen === \"number\")\n this.packetMagicLen = options.packetMagicLen;\n if (typeof options.encryptRequests === \"boolean\")\n this.encryptRequests = options.encryptRequests;\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 /** 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 \uD328\uD0B7 magic \uAE38\uC774(`packet_magic_len`)\uB97C \uC124\uC815\uD569\uB2C8\uB2E4. */\n setPacketMagicLen(length: number): void {\n this.packetMagicLen = length;\n }\n\n /** \uD604\uC7AC \uC554\uD638\uD654 \uD328\uD0B7 magic \uAE38\uC774\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4. */\n getPacketMagicLen(): number {\n return this.packetMagicLen;\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 private _scheduleKeepSession(\n refreshToken: string,\n expiresIn: 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 this.refreshToken(\n this._sessionRefreshToken,\n );\n this.onTokenRefreshed?.(result.access_token, result.expires_in);\n this._scheduleKeepSession(\n this._sessionRefreshToken,\n result.expires_in,\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 private _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 // \u2500\u2500\u2500 \uC778\uC99D \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\u2500\u2500\u2500\u2500\u2500\n\n /**\n * \uC11C\uBC84 \uD5EC\uC2A4 \uCCB4\uD06C\uB97C \uC218\uD589\uD558\uACE0 \uD328\uD0B7 \uC554\uD638\uD654 \uD65C\uC131 \uC5EC\uBD80\uB97C \uC790\uB3D9\uC73C\uB85C \uAC10\uC9C0\uD569\uB2C8\uB2E4.\n *\n * \uC11C\uBC84\uAC00 `packet_encryption: true`\uB97C \uC751\uB2F5\uD558\uBA74 \uC774\uD6C4 \uBAA8\uB4E0 \uC694\uCCAD\uC5D0 \uC554\uD638\uD654\uAC00 \uC790\uB3D9 \uC801\uC6A9\uB429\uB2C8\uB2E4.\n *\n * ```ts\n * await client.checkHealth();\n * await client.login(email, password);\n * ```\n */\n async checkHealth(): Promise<{ ok: boolean; packet_encryption?: boolean }> {\n const res = await fetch(`${this.baseUrl}/v1/health`, {\n signal: AbortSignal.timeout(3000),\n });\n const data = (await res.json()) as {\n ok: boolean;\n packet_encryption?: boolean;\n };\n if (data.packet_encryption) this.encryptRequests = true;\n return data;\n }\n\n /** \uB85C\uADF8\uC778 \uD6C4 `access_token`\uC744 \uB0B4\uBD80 \uC0C1\uD0DC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4. */\n async login(\n email: string,\n password: string,\n ): Promise<{\n access_token: string;\n refresh_token: string;\n expires_in: number;\n }> {\n const data = await this.request<{\n data: {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n };\n }>(\"POST\", \"/v1/auth/login\", { email, passwd: password }, false);\n this.token = data.data.access_token;\n if (this.keepSession)\n this._scheduleKeepSession(\n data.data.refresh_token,\n data.data.expires_in,\n );\n return data.data;\n }\n\n /** Refresh Token\uC73C\uB85C Access Token\uC744 \uC7AC\uBC1C\uAE09\uBC1B\uC544 \uB0B4\uBD80 \uD1A0\uD070\uC744 \uAD50\uCCB4\uD569\uB2C8\uB2E4. */\n async refreshToken(\n refreshToken: string,\n ): Promise<{ access_token: string; expires_in: number }> {\n const data = await this.request<{\n data: { access_token: string; expires_in: number };\n }>(\"POST\", \"/v1/auth/refresh\", { refresh_token: refreshToken }, false);\n this.token = data.data.access_token;\n if (this.keepSession)\n this._scheduleKeepSession(refreshToken, data.data.expires_in);\n return data.data;\n }\n\n /**\n * \uC11C\uBC84\uC5D0 \uB85C\uADF8\uC544\uC6C3\uC744 \uC694\uCCAD\uD558\uACE0 \uB0B4\uBD80 \uD1A0\uD070\uC744 \uCD08\uAE30\uD654\uD569\uB2C8\uB2E4.\n * refresh_token\uC744 \uC11C\uBC84\uC5D0 \uC804\uB2EC\uD574 \uBB34\uD6A8\uD654\uD569\uB2C8\uB2E4.\n */\n async logout(refreshToken: string): Promise<{ ok: boolean }> {\n this.stopKeepSession();\n const data = await this.request<{ ok: boolean }>(\n \"POST\",\n \"/v1/auth/logout\",\n { refresh_token: refreshToken },\n false,\n );\n this.token = \"\";\n return data;\n }\n\n // \u2500\u2500\u2500 \uD2B8\uB79C\uC7AD\uC158 \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\u2500\n\n /** \uD2B8\uB79C\uC7AD\uC158\uC744 \uC2DC\uC791\uD558\uACE0 \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158 ID\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. */\n async transStart(): Promise<string> {\n const res = await this.request<{ ok: boolean; transaction_id: string }>(\n \"POST\",\n \"/v1/transaction/start\",\n undefined,\n false,\n );\n this.activeTxId = res.transaction_id;\n return this.activeTxId;\n }\n\n /** \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158(\uB610\uB294 \uC804\uB2EC\uB41C transactionId)\uC744 \uB864\uBC31\uD569\uB2C8\uB2E4. */\n transRollback(transactionId?: string): Promise<{ ok: boolean }> {\n const txId = transactionId ?? this.activeTxId;\n if (!txId)\n return Promise.reject(\n new Error(\"No active transaction. Call transStart() first.\"),\n );\n this.activeTxId = null;\n return this.request(\"POST\", `/v1/transaction/rollback/${txId}`);\n }\n\n /**\n * \uD65C\uC131 \uD2B8\uB79C\uC7AD\uC158(\uB610\uB294 \uC804\uB2EC\uB41C transactionId)\uC744 \uCEE4\uBC0B\uD569\uB2C8\uB2E4.\n *\n * @returns `results` \uBC30\uC5F4: commit\uB41C \uAC01 \uC791\uC5C5\uC758 `entity`, `action`, `seq`\n */\n transCommit(transactionId?: string): Promise<{\n ok: boolean;\n results: Array<{ entity: string; action: string; seq: number }>;\n }> {\n const txId = transactionId ?? this.activeTxId;\n if (!txId)\n return Promise.reject(\n new Error(\"No active transaction. Call transStart() first.\"),\n );\n this.activeTxId = null;\n return this.request(\"POST\", `/v1/transaction/commit/${txId}`);\n }\n\n // \u2500\u2500\u2500 \uC5D4\uD2F0\uD2F0 CRUD \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 /** \uC2DC\uD000\uC2A4 ID\uB85C \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n get<T = unknown>(\n entity: string,\n seq: number,\n opts: { skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; data: T }> {\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n return this.request(\"GET\", `/v1/entity/${entity}/${seq}${q}`);\n }\n\n /** \uC870\uAC74\uC73C\uB85C \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. data \uCEEC\uB7FC\uC744 \uC644\uC804\uD788 \uBCF5\uD638\uD654\uD558\uC5EC \uBC18\uD658\uD569\uB2C8\uB2E4. */\n find<T = unknown>(\n entity: string,\n conditions?: Record<string, unknown>,\n opts: { skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; data: T }> {\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/find${q}`,\n conditions ?? {},\n );\n }\n\n /** \uD398\uC774\uC9C0\uB124\uC774\uC158/\uC815\uB82C/\uD544\uD130 \uC870\uAC74\uC73C\uB85C \uC5D4\uD2F0\uD2F0 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n list<T = unknown>(\n entity: string,\n params: EntityListParams = {},\n ): Promise<{ ok: boolean; data: EntityListResult<T> }> {\n const { conditions, fields, orderDir, orderBy, ...rest } = params;\n const queryObj: Record<string, unknown> = {\n page: 1,\n limit: 20,\n ...rest,\n };\n if (orderBy)\n queryObj.orderBy = orderDir === \"DESC\" ? `-${orderBy}` : orderBy;\n if (fields?.length) queryObj.fields = fields.join(\",\");\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/list?${buildQuery(queryObj)}`,\n conditions ?? {},\n );\n }\n\n /**\n * \uC5D4\uD2F0\uD2F0 \uCD1D \uAC74\uC218\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.\n *\n * @param conditions \uD544\uD130 \uC870\uAC74 (\uC608: `{ status: \"active\" }`)\n */\n count(\n entity: string,\n conditions?: Record<string, unknown>,\n ): Promise<{ ok: boolean; count: number }> {\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/count`,\n conditions ?? {},\n );\n }\n\n /**\n * \uCEE4\uC2A4\uD140 SQL\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.\n *\n * SELECT \uC804\uC6A9\uC774\uBA70 \uC778\uB371\uC2A4 \uD14C\uC774\uBE14\uB9CC \uC870\uD68C \uAC00\uB2A5\uD569\uB2C8\uB2E4. JOIN \uC9C0\uC6D0.\n */\n query<T = unknown>(\n entity: string,\n req: EntityQueryRequest,\n ): Promise<{ ok: boolean; data: { items: T[]; count: number } }> {\n return this.request(\"POST\", `/v1/entity/${entity}/query`, req);\n }\n\n /** \uC5D4\uD2F0\uD2F0 \uB370\uC774\uD130\uB97C \uC0DD\uC131/\uC218\uC815(Submit)\uD569\uB2C8\uB2E4. `seq`\uAC00 \uC5C6\uC73C\uBA74 INSERT, \uC788\uC73C\uBA74 UPDATE\uC785\uB2C8\uB2E4. */\n submit(\n entity: string,\n data: Record<string, unknown>,\n opts: { transactionId?: string; skipHooks?: boolean } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const txId = opts.transactionId ?? this.activeTxId;\n const extraHeaders = txId ? { \"X-Transaction-ID\": txId } : undefined;\n const q = opts.skipHooks ? \"?skipHooks=true\" : \"\";\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/submit${q}`,\n data,\n true,\n extraHeaders,\n );\n }\n\n /** \uC2DC\uD000\uC2A4 ID\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4(`hard=true`\uBA74 \uD558\uB4DC \uC0AD\uC81C, \uAE30\uBCF8\uC740 \uC18C\uD504\uD2B8 \uC0AD\uC81C). */\n delete(\n entity: string,\n seq: number,\n opts: {\n transactionId?: string;\n hard?: boolean;\n skipHooks?: boolean;\n } = {},\n ): Promise<{ ok: boolean; deleted: number }> {\n const params = new URLSearchParams();\n if (opts.hard) params.set(\"hard\", \"true\");\n if (opts.skipHooks) params.set(\"skipHooks\", \"true\");\n const q = params.size ? `?${params}` : \"\";\n const txId = opts.transactionId ?? this.activeTxId;\n const extraHeaders = txId ? { \"X-Transaction-ID\": txId } : undefined;\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/delete/${seq}${q}`,\n undefined,\n true,\n extraHeaders,\n );\n }\n\n /** \uC5D4\uD2F0\uD2F0 \uB2E8\uAC74\uC758 \uBCC0\uACBD \uC774\uB825\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n history<T = unknown>(\n entity: string,\n seq: number,\n params: Pick<EntityListParams, \"page\" | \"limit\"> = {},\n ): Promise<{\n ok: boolean;\n data: EntityListResult<EntityHistoryRecord<T>>;\n }> {\n return this.request(\n \"GET\",\n `/v1/entity/${entity}/history/${seq}?${buildQuery({ page: 1, limit: 50, ...params })}`,\n );\n }\n\n /** \uD2B9\uC815 \uC774\uB825 \uC2DC\uC810\uC73C\uB85C \uC5D4\uD2F0\uD2F0\uB97C \uB864\uBC31\uD569\uB2C8\uB2E4. */\n rollback(entity: string, historySeq: number): Promise<{ ok: boolean }> {\n return this.request(\n \"POST\",\n `/v1/entity/${entity}/rollback/${historySeq}`,\n );\n }\n\n // \u2500\u2500\u2500 \uD478\uC2DC \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\u2500\u2500\u2500\u2500\u2500\n\n /** \uD478\uC2DC \uAD00\uB828 \uC5D4\uD2F0\uD2F0\uB85C payload\uB97C \uC804\uC1A1(Submit)\uD569\uB2C8\uB2E4. */\n push(\n pushEntity: string,\n payload: Record<string, unknown>,\n opts: { transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n return this.submit(pushEntity, payload, opts);\n }\n\n /** \uD478\uC2DC \uB85C\uADF8 \uC5D4\uD2F0\uD2F0 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. */\n pushLogList<T = unknown>(\n params: EntityListParams = {},\n ): Promise<{ ok: boolean; data: EntityListResult<T> }> {\n return this.list<T>(\"push_log\", params);\n }\n\n /** \uACC4\uC815\uC758 \uD478\uC2DC \uB514\uBC14\uC774\uC2A4\uB97C \uB4F1\uB85D\uD569\uB2C8\uB2E4. */\n registerPushDevice(\n accountSeq: number,\n deviceId: string,\n pushToken: string,\n opts: RegisterPushDeviceOptions = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const {\n platform,\n deviceType,\n browser,\n browserVersion,\n pushEnabled = true,\n transactionId,\n } = opts;\n return this.submit(\n \"account_device\",\n {\n id: deviceId,\n account_seq: accountSeq,\n push_token: pushToken,\n push_enabled: pushEnabled,\n ...(platform ? { platform } : {}),\n ...(deviceType ? { device_type: deviceType } : {}),\n ...(browser ? { browser } : {}),\n ...(browserVersion ? { browser_version: browserVersion } : {}),\n },\n { transactionId },\n );\n }\n\n /** \uB514\uBC14\uC774\uC2A4 \uB808\uCF54\uB4DC\uC758 \uD478\uC2DC \uD1A0\uD070\uC744 \uAC31\uC2E0\uD569\uB2C8\uB2E4. */\n updatePushDeviceToken(\n deviceSeq: number,\n pushToken: string,\n opts: { pushEnabled?: boolean; transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n const { pushEnabled = true, transactionId } = opts;\n return this.submit(\n \"account_device\",\n {\n seq: deviceSeq,\n push_token: pushToken,\n push_enabled: pushEnabled,\n },\n { transactionId },\n );\n }\n\n /** \uB514\uBC14\uC774\uC2A4\uC758 \uD478\uC2DC \uC218\uC2E0\uC744 \uBE44\uD65C\uC131\uD654\uD569\uB2C8\uB2E4. */\n disablePushDevice(\n deviceSeq: number,\n opts: { transactionId?: string } = {},\n ): Promise<{ ok: boolean; seq: number }> {\n return this.submit(\n \"account_device\",\n { seq: deviceSeq, push_enabled: false },\n { transactionId: opts.transactionId },\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(this.hmacSecret, this.token);\n return parseRequestBody<T>(\n body,\n contentType,\n requireEncrypted,\n key,\n this.packetMagicLen,\n );\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 private get _reqOpts(): RequestOptions {\n return {\n baseUrl: this.baseUrl,\n token: this.token,\n apiKey: this.apiKey,\n hmacSecret: this.hmacSecret,\n packetMagicLen: this.packetMagicLen,\n encryptRequests: this.encryptRequests,\n };\n }\n\n private 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 );\n }\n}\n", "export * from \"./types\";\nexport * from \"./EntityServerClient\";\n\nimport { EntityServerClient } from \"./EntityServerClient\";\n\nexport const entityServer = new EntityServerClient();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["readEnv", "name", "buildQuery", "params", "value", "key", "xchacha20poly1305", "sha256", "hkdf", "derivePacketKey", "hmacSecret", "token", "salt", "info", "encryptPacket", "plaintext", "key", "magicLen", "magic", "nonce", "ciphertext", "result", "decryptPacket", "buffer", "data", "parseRequestBody", "body", "contentType", "requireEncrypted", "isEncrypted", "sliced", "sha256", "hmac", "buildHmacHeaders", "method", "path", "bodyBytes", "apiKey", "hmacSecret", "timestamp", "nonce", "prefix", "payload", "signature", "b", "entityRequest", "opts", "method", "path", "body", "withAuth", "extraHeaders", "baseUrl", "token", "apiKey", "hmacSecret", "packetMagicLen", "encryptRequests", "isHmacMode", "headers", "fetchBody", "key", "derivePacketKey", "encryptPacket", "bodyBytes", "buildHmacHeaders", "res", "decryptPacket", "data", "err", "EntityServerClient", "options", "envBaseUrl", "readEnv", "envMagicLen", "token", "apiKey", "secret", "length", "refreshToken", "expiresIn", "delayMs", "result", "err", "data", "email", "password", "res", "transactionId", "txId", "entity", "seq", "opts", "q", "conditions", "params", "fields", "orderDir", "orderBy", "rest", "queryObj", "buildQuery", "req", "extraHeaders", "historySeq", "pushEntity", "payload", "accountSeq", "deviceId", "pushToken", "platform", "deviceType", "browser", "browserVersion", "pushEnabled", "deviceSeq", "body", "contentType", "requireEncrypted", "key", "derivePacketKey", "parseRequestBody", "method", "path", "withAuth", "entityRequest", "entityServer", "EntityServerClient"]
|
|
7
7
|
}
|
package/dist/react.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useCallback as
|
|
1
|
+
import{useCallback as S,useEffect as H,useMemo as D,useRef as L,useState as B}from"react";function P(s){return import.meta?.env?.[s]}function q(s){return Object.entries(s).filter(([,e])=>e!=null).map(([e,t])=>`${encodeURIComponent(e==="orderBy"?"order_by":e)}=${encodeURIComponent(String(t))}`).join("&")}import{xchacha20poly1305 as x}from"@noble/ciphers/chacha";import{sha256 as w}from"@noble/hashes/sha2";import{hkdf as K}from"@noble/hashes/hkdf";function E(s,e){if(s){let t=new TextEncoder().encode("entity-server:hkdf:v1"),r=new TextEncoder().encode("entity-server:packet-encryption");return K(w,new TextEncoder().encode(s),t,r,32)}return w(new TextEncoder().encode(e))}function U(s,e,t){let r=new Uint8Array(t),n=new Uint8Array(24);crypto.getRandomValues(r),crypto.getRandomValues(n);let a=x(e,n).encrypt(s),o=new Uint8Array(t+24+a.length);return o.set(r,0),o.set(n,t),o.set(a,t+24),o}function R(s,e,t){let r=new Uint8Array(s);if(r.length<t+24+16)throw new Error("Encrypted packet too short");let n=r.slice(t,t+24),i=r.slice(t+24),o=x(e,n).decrypt(i);return JSON.parse(new TextDecoder().decode(o))}function I(s,e,t,r,n){let i=e.toLowerCase().includes("application/octet-stream");if(t&&!i)throw new Error("Encrypted request required: Content-Type must be application/octet-stream");if(i){if(s==null)throw new Error("Encrypted request body is empty");if(s instanceof ArrayBuffer)return R(s,r,n);if(s instanceof Uint8Array){let a=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);return R(a,r,n)}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 M}from"@noble/hashes/sha2";import{hmac as C}from"@noble/hashes/hmac";function O(s,e,t,r,n){let i=String(Math.floor(Date.now()/1e3)),a=crypto.randomUUID(),o=new TextEncoder().encode(`${s}|${e}|${i}|${a}|`),c=new Uint8Array(o.length+t.length);c.set(o,0),c.set(t,o.length);let h=[...C(M,new TextEncoder().encode(n),c)].map(y=>y.toString(16).padStart(2,"0")).join("");return{"X-API-Key":r,"X-Timestamp":i,"X-Nonce":a,"X-Signature":h}}async function $(s,e,t,r,n=!0,i={}){let{baseUrl:a,token:o,apiKey:c,hmacSecret:f,packetMagicLen:h,encryptRequests:y}=s,b=n&&!!(c&&f),p={"Content-Type":"application/json",...i};!b&&n&&o&&(p.Authorization=`Bearer ${o}`);let l=null;if(r!=null)if(y&&n&&(o||b)&&e!=="GET"&&e!=="HEAD"){let u=E(f,o);l=U(new TextEncoder().encode(JSON.stringify(r)),u,h),p["Content-Type"]="application/octet-stream"}else l=JSON.stringify(r);if(b){let m=l instanceof Uint8Array?l:typeof l=="string"?new TextEncoder().encode(l):new Uint8Array(0);Object.assign(p,O(e,t,m,c,f))}let k=await fetch(a+t,{method:e,headers:p,...l!=null?{body:l}:{}});if((k.headers.get("Content-Type")??"").includes("application/octet-stream")){let m=E(f,o);return R(await k.arrayBuffer(),m,h)}let v=await k.json();if(!v.ok){let m=new Error(v.message??`EntityServer error (HTTP ${k.status})`);throw m.status=k.status,m}return v}var T=class{baseUrl;token;apiKey;hmacSecret;packetMagicLen;encryptRequests;activeTxId=null;keepSession;refreshBuffer;onTokenRefreshed;onSessionExpired;_sessionRefreshToken=null;_refreshTimer=null;constructor(e={}){let t=P("VITE_ENTITY_SERVER_URL"),r=P("VITE_ENTITY_SERVER_PACKET_MAGIC_LEN");this.baseUrl=(e.baseUrl??t??"http://localhost:47200").replace(/\/$/,""),this.token=e.token??"",this.apiKey=e.apiKey??"",this.hmacSecret=e.hmacSecret??"",this.packetMagicLen=e.packetMagicLen??(r?Number(r):4),this.encryptRequests=e.encryptRequests??!1,this.keepSession=e.keepSession??!1,this.refreshBuffer=e.refreshBuffer??60,this.onTokenRefreshed=e.onTokenRefreshed,this.onSessionExpired=e.onSessionExpired}configure(e){e.baseUrl&&(this.baseUrl=e.baseUrl.replace(/\/$/,"")),typeof e.token=="string"&&(this.token=e.token),typeof e.packetMagicLen=="number"&&(this.packetMagicLen=e.packetMagicLen),typeof e.encryptRequests=="boolean"&&(this.encryptRequests=e.encryptRequests),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}setApiKey(e){this.apiKey=e}setHmacSecret(e){this.hmacSecret=e}setPacketMagicLen(e){this.packetMagicLen=e}getPacketMagicLen(){return this.packetMagicLen}_scheduleKeepSession(e,t){this._clearRefreshTimer(),this._sessionRefreshToken=e;let r=Math.max((t-this.refreshBuffer)*1e3,0);this._refreshTimer=setTimeout(async()=>{if(this._sessionRefreshToken)try{let n=await this.refreshToken(this._sessionRefreshToken);this.onTokenRefreshed?.(n.access_token,n.expires_in),this._scheduleKeepSession(this._sessionRefreshToken,n.expires_in)}catch(n){this._clearRefreshTimer(),this.onSessionExpired?.(n instanceof Error?n:new Error(String(n)))}},r)}_clearRefreshTimer(){this._refreshTimer!==null&&(clearTimeout(this._refreshTimer),this._refreshTimer=null)}stopKeepSession(){this._clearRefreshTimer(),this._sessionRefreshToken=null}async checkHealth(){let t=await(await fetch(`${this.baseUrl}/v1/health`,{signal:AbortSignal.timeout(3e3)})).json();return t.packet_encryption&&(this.encryptRequests=!0),t}async login(e,t){let r=await this.request("POST","/v1/auth/login",{email:e,passwd:t},!1);return this.token=r.data.access_token,this.keepSession&&this._scheduleKeepSession(r.data.refresh_token,r.data.expires_in),r.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),t.data}async logout(e){this.stopKeepSession();let t=await this.request("POST","/v1/auth/logout",{refresh_token:e},!1);return this.token="",t}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."))}get(e,t,r={}){let n=r.skipHooks?"?skipHooks=true":"";return this.request("GET",`/v1/entity/${e}/${t}${n}`)}find(e,t,r={}){let n=r.skipHooks?"?skipHooks=true":"";return this.request("POST",`/v1/entity/${e}/find${n}`,t??{})}list(e,t={}){let{conditions:r,fields:n,orderDir:i,orderBy:a,...o}=t,c={page:1,limit:20,...o};return a&&(c.orderBy=i==="DESC"?`-${a}`:a),n?.length&&(c.fields=n.join(",")),this.request("POST",`/v1/entity/${e}/list?${q(c)}`,r??{})}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,r={}){let n=r.transactionId??this.activeTxId,i=n?{"X-Transaction-ID":n}:void 0,a=r.skipHooks?"?skipHooks=true":"";return this.request("POST",`/v1/entity/${e}/submit${a}`,t,!0,i)}delete(e,t,r={}){let n=new URLSearchParams;r.hard&&n.set("hard","true"),r.skipHooks&&n.set("skipHooks","true");let i=n.size?`?${n}`:"",a=r.transactionId??this.activeTxId,o=a?{"X-Transaction-ID":a}:void 0;return this.request("POST",`/v1/entity/${e}/delete/${t}${i}`,void 0,!0,o)}history(e,t,r={}){return this.request("GET",`/v1/entity/${e}/history/${t}?${q({page:1,limit:50,...r})}`)}rollback(e,t){return this.request("POST",`/v1/entity/${e}/rollback/${t}`)}push(e,t,r={}){return this.submit(e,t,r)}pushLogList(e={}){return this.list("push_log",e)}registerPushDevice(e,t,r,n={}){let{platform:i,deviceType:a,browser:o,browserVersion:c,pushEnabled:f=!0,transactionId:h}=n;return this.submit("account_device",{id:t,account_seq:e,push_token:r,push_enabled:f,...i?{platform:i}:{},...a?{device_type:a}:{},...o?{browser:o}:{},...c?{browser_version:c}:{}},{transactionId:h})}updatePushDeviceToken(e,t,r={}){let{pushEnabled:n=!0,transactionId:i}=r;return this.submit("account_device",{seq:e,push_token:t,push_enabled:n},{transactionId:i})}disablePushDevice(e,t={}){return this.submit("account_device",{seq:e,push_enabled:!1},{transactionId:t.transactionId})}readRequestBody(e,t="application/json",r=!1){let n=E(this.hmacSecret,this.token);return I(e,t,r,n,this.packetMagicLen)}get _reqOpts(){return{baseUrl:this.baseUrl,token:this.token,apiKey:this.apiKey,hmacSecret:this.hmacSecret,packetMagicLen:this.packetMagicLen,encryptRequests:this.encryptRequests}}request(e,t,r,n=!0,i){return $(this._reqOpts,e,t,r,n,i)}};var A=new T;function ue(s={}){let{singleton:e=!0,tokenResolver:t,baseUrl:r,packetMagicLen:n,token:i,resumeSession:a}=s,[o,c]=B(!1),[f,h]=B(null),y=L(!0);H(()=>(y.current=!0,()=>{y.current=!1}),[]);let b=L(a);H(()=>{let u=b.current;u&&p.refreshToken(u).catch(()=>{})},[]);let p=D(()=>{let u=e?A:new T({baseUrl:r,packetMagicLen:n,token:i});e&&u.configure({baseUrl:r,packetMagicLen:n,token:i});let d=t?.();return typeof d=="string"&&u.setToken(d),u},[e,t,r,n,i]),l=S(async u=>{y.current&&(c(!0),h(null));try{return await u()}catch(d){let g=d instanceof Error?d:new Error(String(d));throw y.current&&h(g),g}finally{y.current&&c(!1)}},[]),k=S((u,d,g)=>l(()=>p.submit(u,d,g)),[p,l]),_=S((u,d,g)=>l(()=>p.delete(u,d,g)),[p,l]),v=S((u,d)=>l(()=>p.query(u,d)),[p,l]),m=S(()=>{c(!1),h(null)},[]);return{client:p,isPending:o,error:f,reset:m,submit:k,del:_,query:v}}export{ue as useEntityServer};
|
|
2
2
|
//# sourceMappingURL=react.js.map
|