@djvlc/runtime-client-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,279 @@
1
+ # @djvlc/runtime-client-sdk
2
+
3
+ DJV 低代码平台运行时 Client SDK - **数据面 API 唯一入口**
4
+
5
+ ## 功能特性
6
+
7
+ | 能力 | 说明 |
8
+ |------|------|
9
+ | **Header 注入** | 每个请求自动带 X-App-Id/X-Trace-Id/Authorization/X-Runtime-Version/X-Client-Version |
10
+ | **TraceId 贯穿** | resolve/query/action/track 默认使用同一个 traceId(除非显式覆盖) |
11
+ | **错误归一** | 统一 `RuntimeClientError` 类型,支持 Network/Timeout/Abort/Http/Biz/Validation/Unknown |
12
+ | **幂等键策略** | 自动为 claim/signin/form_submit 等高频动作生成稳定幂等键 |
13
+ | **prefetchData 注入** | `applyPrefetch(runtime, resolved)` 将预取数据注入 runtime |
14
+ | **重试/超时/取消** | 支持 timeoutMs + AbortSignal,读请求自动重试(可配置) |
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ pnpm add @djvlc/runtime-client-sdk
20
+ ```
21
+
22
+ ## 快速开始
23
+
24
+ ```typescript
25
+ import { createRuntimeClientSdk } from '@djvlc/runtime-client-sdk';
26
+
27
+ // 创建 SDK
28
+ const sdk = createRuntimeClientSdk({
29
+ baseUrl: 'https://api.example.com',
30
+ appId: 'my-app',
31
+ runtimeVersion: '1.0.0',
32
+ clientVersion: '1.0.0',
33
+ getToken: () => localStorage.getItem('token') || undefined,
34
+ retry: true,
35
+ maxRetries: 2,
36
+ });
37
+
38
+ // 获取 UserApiAdapter(向后兼容)
39
+ const userApiAdapter = sdk.getUserApiAdapter();
40
+
41
+ // 获取 Ports(新 API)
42
+ const ports = sdk.getPorts();
43
+
44
+ // 解析页面
45
+ const result = await ports.page.resolvePage({
46
+ pageId: 'page_123',
47
+ trace: { traceId: sdk.createTraceId() },
48
+ });
49
+
50
+ // 执行动作
51
+ const actionResult = await ports.action.executeAction({
52
+ actionType: 'claim',
53
+ payload: { activityId: 'act_123' },
54
+ context: {
55
+ pageId: 'page_123',
56
+ pageVersionId: result.pageVersionId,
57
+ },
58
+ });
59
+
60
+ // 查询数据
61
+ const queryResult = await ports.data.query({
62
+ queryVersionId: 'qv_user_info',
63
+ params: { uid: 'user_123' },
64
+ context: {
65
+ pageId: 'page_123',
66
+ pageVersionId: result.pageVersionId,
67
+ },
68
+ });
69
+
70
+ // 埋点上报
71
+ await ports.track.track({
72
+ events: [{ eventName: 'page_view', eventType: 'page_view' }],
73
+ context: {
74
+ pageId: 'page_123',
75
+ pageVersionId: result.pageVersionId,
76
+ },
77
+ });
78
+
79
+ // 销毁
80
+ await sdk.destroy();
81
+ ```
82
+
83
+ ## 与 @djvlc/runtime-core 配合使用
84
+
85
+ ```typescript
86
+ import { createRuntime } from '@djvlc/runtime-core';
87
+ import { createRuntimeClientSdk, applyPrefetch } from '@djvlc/runtime-client-sdk';
88
+
89
+ // 创建 SDK
90
+ const sdk = createRuntimeClientSdk({
91
+ baseUrl: 'https://api.example.com',
92
+ appId: 'my-app',
93
+ runtimeVersion: '1.0.0',
94
+ clientVersion: '1.0.0',
95
+ });
96
+
97
+ // 创建 Runtime(使用 SDK 的 UserApiAdapter)
98
+ const runtime = createRuntime({
99
+ container: '#app',
100
+ pageId: 'page_123',
101
+ apiBaseUrl: 'https://api.example.com',
102
+ cdnBaseUrl: 'https://cdn.example.com',
103
+ userApiAdapter: sdk.getUserApiAdapter(),
104
+ });
105
+
106
+ // 初始化并加载
107
+ await runtime.init();
108
+ const resolved = await sdk.getPorts().page.resolvePage({ pageId: 'page_123' });
109
+
110
+ // 应用预取数据
111
+ applyPrefetch(runtime, resolved);
112
+
113
+ // 渲染
114
+ await runtime.loadFromResolved(resolved.pageResolveResult!);
115
+ await runtime.render();
116
+ ```
117
+
118
+ ## 配置选项
119
+
120
+ ```typescript
121
+ interface RuntimeClientSdkOptions {
122
+ /** API 基础 URL */
123
+ baseUrl: string;
124
+ /** App ID */
125
+ appId: string;
126
+ /** Runtime 版本 */
127
+ runtimeVersion: string;
128
+ /** Client 版本 */
129
+ clientVersion: string;
130
+ /** 获取 Token 的函数(lazy getter) */
131
+ getToken?: () => string | undefined;
132
+ /** 获取 API Key 的函数(lazy getter) */
133
+ getApiKey?: () => string | undefined;
134
+ /** 额外请求头扩展 */
135
+ extraHeaders?: () => Record<string, string>;
136
+ /** 超时时间(毫秒),默认 30000 */
137
+ timeoutMs?: number;
138
+ /** 是否启用重试,默认 false */
139
+ retry?: boolean;
140
+ /** 最大重试次数,默认 2 */
141
+ maxRetries?: number;
142
+ /** 重试延迟(毫秒),默认 200 */
143
+ retryDelayMs?: number;
144
+ /** 是否启用调试 */
145
+ debug?: boolean;
146
+ /** 默认租户 ID */
147
+ defaultTenantId?: string;
148
+ }
149
+ ```
150
+
151
+ ## 错误处理
152
+
153
+ 所有错误都会被归一为 `RuntimeClientError`:
154
+
155
+ ```typescript
156
+ interface RuntimeClientError {
157
+ name: 'RuntimeClientError';
158
+ kind: 'Network' | 'Timeout' | 'Abort' | 'Http' | 'Biz' | 'Validation' | 'Unknown';
159
+ message: string;
160
+ code?: string;
161
+ httpStatus?: number;
162
+ traceId?: string;
163
+ details?: unknown;
164
+ cause?: unknown;
165
+ }
166
+ ```
167
+
168
+ 使用方式:
169
+
170
+ ```typescript
171
+ import { isRuntimeClientError, normalizeError } from '@djvlc/runtime-client-sdk';
172
+
173
+ try {
174
+ const result = await ports.action.executeAction({ ... });
175
+ if (!result.ok) {
176
+ console.error('Action failed:', result.error);
177
+ }
178
+ } catch (error) {
179
+ if (isRuntimeClientError(error)) {
180
+ switch (error.kind) {
181
+ case 'Network':
182
+ // 网络错误
183
+ break;
184
+ case 'Timeout':
185
+ // 超时
186
+ break;
187
+ case 'Http':
188
+ // HTTP 错误
189
+ console.error('HTTP', error.httpStatus, error.message);
190
+ break;
191
+ case 'Biz':
192
+ // 业务错误
193
+ console.error('Business error:', error.code);
194
+ break;
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ## 幂等键
201
+
202
+ 对于 `claim`/`signin`/`form_submit`/`lottery` 等动作,SDK 会自动生成稳定的幂等键:
203
+
204
+ - 同一用户 + 同一页面版本 + 同一组件 + 同一动作 + 同一参数 → 生成相同的 key
205
+ - key 长度:32 字符(SHA-256 截断)
206
+
207
+ ```typescript
208
+ import { needsIdempotencyKey, generateIdempotencyKey } from '@djvlc/runtime-client-sdk';
209
+
210
+ // 检查是否需要幂等键
211
+ needsIdempotencyKey('claim'); // true
212
+ needsIdempotencyKey('custom'); // false
213
+
214
+ // 手动生成
215
+ const key = await generateIdempotencyKey({
216
+ actionType: 'claim',
217
+ payload: { activityId: 'act_123' },
218
+ context: {
219
+ pageId: 'page_1',
220
+ pageVersionId: 'pv_1',
221
+ componentId: 'btn_1',
222
+ uid: 'user_1',
223
+ },
224
+ });
225
+ ```
226
+
227
+ ## Trace ID 管理
228
+
229
+ ```typescript
230
+ import { TraceContext, createTraceId } from '@djvlc/runtime-client-sdk';
231
+
232
+ // 创建 Trace Context
233
+ const ctx = new TraceContext();
234
+
235
+ // 获取当前 Trace ID
236
+ const traceId = ctx.getTraceId();
237
+
238
+ // 使用临时 Trace ID 执行操作
239
+ await ctx.withTraceAsync('custom-trace-id', async () => {
240
+ // 这里的所有操作都使用 custom-trace-id
241
+ await someOperation();
242
+ });
243
+ // 操作完成后恢复原来的 Trace ID
244
+ ```
245
+
246
+ ## API
247
+
248
+ ### SDK Methods
249
+
250
+ | 方法 | 说明 |
251
+ |------|------|
252
+ | `getPorts()` | 获取 RuntimePorts 对象 |
253
+ | `getUserApiAdapter()` | 获取向后兼容的 UserApiAdapter |
254
+ | `getTraceContext()` | 获取 TraceContext 实例 |
255
+ | `createTraceId()` | 创建新的 Trace ID |
256
+ | `flushTrack()` | 立即刷新埋点缓冲区 |
257
+ | `destroy()` | 销毁 SDK(刷新剩余埋点) |
258
+
259
+ ### Port Interfaces
260
+
261
+ | Port | 方法 | 说明 |
262
+ |------|------|------|
263
+ | `PageRuntimePort` | `resolvePage(input)` | 解析页面 |
264
+ | `ActionPort` | `executeAction(input)` | 执行动作 |
265
+ | `DataPort` | `query(input)` | 查询数据 |
266
+ | `TrackPort` | `track(input)` | 埋点上报 |
267
+ | `TenantPort` | `resolveTenant(input)` | 解析租户 |
268
+
269
+ ## 依赖关系
270
+
271
+ - **运行时依赖**:`@djvlc/runtime-core`
272
+ - **peer 依赖**:
273
+ - `@djvlc/contracts-types` - 类型定义
274
+ - `@djvlc/openapi-user-client` - User API 客户端
275
+ - `@djvlc/contracts-validators` (可选) - 响应校验
276
+
277
+ ## 许可证
278
+
279
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ var e=require("@djvlc/openapi-user-client");function t(e){return function(t){const r={"Content-Type":"application/json","X-App-Id":e.appId,"X-Runtime-Version":e.runtimeVersion,"X-Client-Version":e.clientVersion};if(t&&(r["X-Trace-Id"]=t),e.getToken){const t=e.getToken();t&&(r.Authorization=`Bearer ${t}`)}if(e.getApiKey){const t=e.getApiKey();t&&(r["X-API-Key"]=t)}if(e.extraHeaders){const t=e.extraHeaders();Object.assign(r,t)}return r}}function r(){return e=32,Array.from({length:e},()=>Math.floor(16*Math.random()).toString(16)).join("");var e}function a(){return e=16,Array.from({length:e},()=>Math.floor(16*Math.random()).toString(16)).join("");var e}var n=class{constructor(e){this.defaultTraceId=e||r()}getTraceId(){return this.defaultTraceId}setTraceId(e){this.defaultTraceId=e}resetTraceId(){return this.defaultTraceId=r(),this.defaultTraceId}withTrace(e,t){const r=this.defaultTraceId;this.defaultTraceId=e;try{return t()}finally{this.defaultTraceId=r}}async withTraceAsync(e,t){const r=this.defaultTraceId;this.defaultTraceId=e;try{return await t()}finally{this.defaultTraceId=r}}},s={enabled:!0,maxRetries:2,initialDelayMs:200,backoffMultiplier:2,maxDelayMs:2e3,retryableStatusCodes:[408,429,500,502,503,504]};function o(e,t){const r=t.initialDelayMs*Math.pow(t.backoffMultiplier,e),a=.1*r*(2*Math.random()-1);return Math.min(r+a,t.maxDelayMs)}function i(e,t,r){if(!r.enabled||t>=r.maxRetries)return!1;if(e instanceof TypeError)return!0;if(e instanceof Error&&"status"in e){const t=e.status;return r.retryableStatusCodes.includes(t)}return!(!e||"object"!=typeof e||!("httpStatus"in e)||"number"!=typeof e.httpStatus)&&r.retryableStatusCodes.includes(e.httpStatus)}function c(e){return new Promise(t=>setTimeout(t,e))}async function u(e,t={}){const r={...s,...t};let a;for(let t=0;t<=r.maxRetries;t++)try{return await e()}catch(e){if(a=e,!i(e,t,r))throw e;if(t<r.maxRetries){const e=o(t,r);await c(e)}}throw a}function d(e,t){const r=new AbortController;let a;return e&&e>0&&(a=setTimeout(()=>{r.abort(new Error(`Request timeout after ${e}ms`))},e)),t&&(t.aborted?r.abort(t.reason):t.addEventListener("abort",()=>{r.abort(t.reason)})),{controller:r,cleanup:()=>{a&&clearTimeout(a)}}}function l(e,t,r){return{name:"RuntimeClientError",kind:e,message:t,code:r?.code,httpStatus:r?.httpStatus,traceId:r?.traceId,details:r?.details,cause:r?.cause}}function p(e){return null!==e&&"object"==typeof e&&"name"in e&&"RuntimeClientError"===e.name}function f(e,t){if(p(e))return{...e,traceId:e.traceId||t};if(e instanceof TypeError&&e.message.includes("fetch"))return l("Network","Network request failed",{traceId:t,cause:e});if(e instanceof DOMException&&"AbortError"===e.name)return l("Abort","Request aborted",{traceId:t,cause:e});if(e instanceof Error){if(e.message.includes("timeout")||e.message.includes("Timeout"))return l("Timeout",e.message,{traceId:t,cause:e});if(e.message.includes("abort")||"AbortError"===e.name)return l("Abort",e.message,{traceId:t,cause:e})}if(e&&"object"==typeof e&&"status"in e&&"number"==typeof e.status){const r=e.status;return l("Http",e instanceof Error?e.message:`HTTP error ${r}`,{httpStatus:r,traceId:t,cause:e})}return e instanceof Error?l("Unknown",e.message,{traceId:t,cause:e}):l("Unknown",String(e),{traceId:t,cause:e})}function h(e,t,r){return l("Validation",e,{code:"VALIDATION_ERROR",traceId:t,details:r})}var m=class{constructor(e){this.client=e.client,this.headersBuilder=e.headersBuilder,this.traceContext=e.traceContext,this.retryConfig=e.retryConfig||{},this.defaultTimeoutMs=e.defaultTimeoutMs||3e4}async resolvePage(e){const t=e.trace?.traceId||this.traceContext.getTraceId(),r=e.timeoutMs||this.defaultTimeoutMs,{controller:a,cleanup:n}=d(r,e.signal);try{const r=await u(async()=>await this.client.pages.resolvePage({pageId:e.pageId,env:"prod",uid:e.uid,deviceId:e.client?.deviceId,channel:void 0},{signal:a.signal}),this.retryConfig);if(!r.data||"object"!=typeof r.data)throw h("Invalid page resolve response",t,{response:r});const n=r.data,s=await fetch(n.snapshotUrl,{signal:a.signal,headers:this.headersBuilder(t)});if(!s.ok)throw f({status:s.status},t);const o=await s.json(),i=this.convertSnapshotToPageSchema(o.page);return{pageVersionId:n.resolvedVersionId,page:i,prefetchedData:o.prefetchedData,meta:{cache:"MISS",resolvedAt:Date.now(),etag:n.etag,cacheTtlSeconds:n.cacheTtlSeconds}}}catch(e){throw f(e,t)}finally{n()}}convertSnapshotToPageSchema(e){return e}};var y=new Set(["claim","signin","form_submit","lottery","checkin","reserve","subscribe"]);function g(e){return y.has(e)}function I(e){let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t=Math.imul(t,16777619);return(t>>>0).toString(16).padStart(8,"0")}function x(e){const t=[e.actionType,e.context.pageId,e.context.pageVersionId,e.context.componentId||"",e.context.uid||"",JSON.stringify(e.payload,Object.keys(e.payload||{}).sort())].join("|"),r=I(t),a=I(t.split("").reverse().join(""));return r+a+I(t+r)+I(a+t)}var T=class{constructor(e){this.client=e.client,this.traceContext=e.traceContext,this.defaultTimeoutMs=e.defaultTimeoutMs||3e4}async executeAction(e){const t=e.trace?.traceId||this.traceContext.getTraceId(),r=e.timeoutMs||this.defaultTimeoutMs;let a=e.idempotencyKey;!a&&g(e.actionType)&&(a=x({actionType:e.actionType,payload:e.payload,context:{pageId:e.context.pageId,pageVersionId:e.context.pageVersionId,componentId:e.context.componentId,uid:e.context.uid}}));const{controller:n,cleanup:s}=d(r,e.signal);try{const t=await this.client.actions.executeAction({executeActionRequest:{actionType:e.actionType,params:e.payload,context:{pageVersionId:e.context.pageVersionId,deviceId:e.context.deviceId,channel:void 0,extra:{uid:e.context.uid,componentId:e.context.componentId}},idempotencyKey:a}},{signal:n.signal});return t.success?{ok:!0,data:t.data?.result}:{ok:!1,error:{code:t.data?.errorCode||"BIZ_ERROR",message:t.data?.errorMessage||"Action failed",details:t.data}}}catch(e){const r=f(e,t);return{ok:!1,error:{code:r.code||r.kind,message:r.message,details:r.details}}}finally{s()}}};var v=class{constructor(e){this.client=e.client,this.traceContext=e.traceContext,this.retryConfig=e.retryConfig||{},this.defaultTimeoutMs=e.defaultTimeoutMs||3e4}async query(e){const t=e.trace?.traceId||this.traceContext.getTraceId(),r=e.timeoutMs||this.defaultTimeoutMs,{controller:a,cleanup:n}=d(r,e.signal);try{const t=await u(async()=>await this.client.queries.queryData({queryDataRequest:{queryVersionId:e.queryVersionId,params:e.params||{},context:{pageVersionId:e.context.pageVersionId}}},{signal:a.signal}),this.retryConfig);return t.success?{ok:!0,data:t.data}:{ok:!1,error:{code:"QUERY_ERROR",message:t.errorMessage||t.message||"Query failed",details:t.data}}}catch(e){const r=f(e,t);return{ok:!1,error:{code:r.code||r.kind,message:r.message,details:r.details}}}finally{n()}}};var w=class{constructor(e){this.eventBuffer=[],this.flushTimer=null,this.client=e.client,this.batchSize=e.batchSize||10,this.flushInterval=e.flushInterval||5e3}async track(e){for(const t of e.events)this.eventBuffer.push({event:t,context:e.context});this.eventBuffer.length>=this.batchSize?await this.flush():this.scheduleFlush()}async flush(e){if(0===this.eventBuffer.length)return;const t=[...this.eventBuffer];this.eventBuffer=[],this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null);for(const{event:e,context:r}of t)try{await this.client.track.track({trackRequest:{eventName:e.eventName,eventType:e.eventType,properties:e.properties,timestamp:e.timestamp?new Date(e.timestamp):void 0,context:{pageVersionId:r.pageVersionId,sessionId:r.sessionId}}})}catch(e){}}scheduleFlush(){this.flushTimer||(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush().catch(()=>{})},this.flushInterval))}async destroy(){await this.flush()}};var b=class{constructor(e){this.defaultTenantId=e.defaultTenantId||"default",this.tenantConfigs=e.tenantConfigs||new Map}async resolveTenant(e){let t=this.defaultTenantId;if(e.path){const r=e.path.match(/^\/t\/([^/]+)/);r&&(t=r[1])}if(e.host){const r=e.host.match(/^([^.]+)\./);r&&"www"!==r[1]&&(t=r[1])}return{tenantId:t,config:this.tenantConfigs.get(t)||{}}}};exports.ActionAdapter=T,exports.DEFAULT_RETRY_CONFIG=s,exports.DataAdapter=v,exports.PageRuntimeAdapter=m,exports.TenantAdapter=b,exports.TraceContext=n,exports.TrackAdapter=w,exports.applyPrefetch=function(e,t){t.prefetchedData&&e.setPrefetchedData(t.prefetchedData)},exports.calculateRetryDelay=o,exports.createActionAdapter=function(e){return new T(e)},exports.createBizError=function(e,t,r,a){return l("Biz",t,{code:e,traceId:r,details:a})},exports.createDataAdapter=function(e){return new v(e)},exports.createHeadersBuilder=t,exports.createHttpError=function(e,t,r,a){return l("Http",t,{httpStatus:e,traceId:r,details:a})},exports.createInitialRuntimeState=function(e){return{pageVersionId:e.pageVersionId,prefetchedData:e.prefetchedData||{}}},exports.createPageRuntimeAdapter=function(e){return new m(e)},exports.createRuntimeClientError=l,exports.createRuntimeClientSdk=function(a){const s=t({appId:a.appId,runtimeVersion:a.runtimeVersion,clientVersion:a.clientVersion,getToken:a.getToken,getApiKey:a.getApiKey,extraHeaders:a.extraHeaders}),o=new n,i=e.createUserClient({baseUrl:a.baseUrl,timeout:a.timeoutMs||3e4,debug:a.debug,headers:s(o.getTraceId()),enableRetry:!1}),c=a.retry?{enabled:!0,maxRetries:a.maxRetries??2,initialDelayMs:a.retryDelayMs??200}:{enabled:!1},u=new m({client:i,headersBuilder:s,traceContext:o,retryConfig:c,defaultTimeoutMs:a.timeoutMs}),d=new T({client:i,headersBuilder:s,traceContext:o,defaultTimeoutMs:a.timeoutMs}),l=new v({client:i,headersBuilder:s,traceContext:o,retryConfig:c,defaultTimeoutMs:a.timeoutMs}),p=new w({client:i,headersBuilder:s,traceContext:o}),f={tenant:new b({headersBuilder:s,traceContext:o,defaultTenantId:a.defaultTenantId}),page:u,data:l,action:d,track:p},h=function(e){return{resolvePage:async t=>(await e.pages.resolvePage({pageId:t.pageId,env:t.env,uid:t.uid,deviceId:t.deviceId,channel:t.channel})).data,async executeAction(t){const r=await e.actions.executeAction({executeActionRequest:{actionType:t.actionType,params:t.params,context:{pageVersionId:t.context.pageVersionId,deviceId:t.context.deviceId,channel:t.context.channel,extra:{uid:t.context.uid,appId:t.context.appId}},idempotencyKey:t.idempotencyKey}}),a=r.data;return{success:r.success,data:a?.result,errorCode:a?.errorCode,errorMessage:a?.errorMessage}},async executeQuery(t){const r=await e.queries.queryData({queryDataRequest:{queryVersionId:t.queryVersionId,params:t.params??{},context:{pageVersionId:t.context.pageVersionId}}});return{success:r.success,data:r.data??void 0,message:r.message,errorMessage:r.errorMessage}},track(t){e.track.track({trackRequest:{eventName:t.eventName,eventType:t.type??"custom",properties:t.params,timestamp:null!=t.timestamp?new Date(t.timestamp):void 0,context:{pageVersionId:t.context.pageVersionId,sessionId:t.context.userId}}}).catch(()=>{})}}}(i);return{getPorts:()=>f,getUserApiAdapter:()=>h,getTraceContext:()=>o,createTraceId:()=>r(),flushTrack:()=>p.flush(),destroy:async()=>{await p.destroy()}}},exports.createSpanId=a,exports.createTenantAdapter=function(e){return new b(e)},exports.createTimeoutController=d,exports.createTraceId=r,exports.createTraceparent=function(e,t){return`00-${e||r()}-${t||a()}-01`},exports.createTrackAdapter=function(e){return new w(e)},exports.createValidationError=h,exports.createWrappedUserClient=function(t){const r={baseUrl:t.baseUrl,timeout:t.timeoutMs||3e4,debug:t.debug,headers:t.headersBuilder(),enableRetry:!1};return e.createUserClient(r)},exports.delay=c,exports.generateIdempotencyKey=async function(e){const t=[e.actionType,e.context.pageId,e.context.pageVersionId,e.context.componentId||"",e.context.uid||"",JSON.stringify(e.payload,Object.keys(e.payload||{}).sort())].join("|");return(await async function(e){if("undefined"!=typeof crypto&&crypto.subtle){const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("")}return I(e)+I(e.split("").reverse().join(""))}(t)).substring(0,32)},exports.generateIdempotencyKeySync=x,exports.getPrefetchedQueryData=function(e,t){return e.prefetchedData?.[t]},exports.hasPrefetchedData=function(e){return void 0!==e.prefetchedData&&Object.keys(e.prefetchedData).length>0},exports.isAbortError=function(e){return e instanceof Error?"AbortError"===e.name||e.message.includes("aborted"):e instanceof DOMException&&"AbortError"===e.name},exports.isRuntimeClientError=p,exports.isTimeoutError=function(e){return e instanceof Error&&(e.message.includes("timeout")||e.message.includes("Timeout")||"TimeoutError"===e.name||"AbortError"===e.name)},exports.needsIdempotencyKey=g,exports.normalizeError=f,exports.shouldRetry=i,exports.withRetry=u,exports.withTimeout=async function(e,t,r){const{controller:a,cleanup:n}=d(t,r);try{return await Promise.race([e,new Promise((e,t)=>{a.signal.addEventListener("abort",()=>{t(a.signal.reason||new Error("Request aborted"))})})])}finally{n()}};