@djvlc/runtime-host-vue 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +1 -1082
  2. package/dist/index.js +1 -1060
  3. package/package.json +5 -4
package/dist/index.cjs CHANGED
@@ -1,1082 +1 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- DJVLC_CONFIG_KEY: () => DJVLC_CONFIG_KEY,
24
- DJVPlugin: () => DJVPlugin,
25
- DJVProvider: () => DJVProvider,
26
- DJVRenderer: () => DJVRenderer,
27
- RuntimeContextKey: () => RuntimeContextKey,
28
- createVueRuntime: () => createVueRuntime,
29
- injectRuntime: () => injectRuntime,
30
- provideRuntime: () => provideRuntime,
31
- useAction: () => useAction,
32
- useComponentState: () => useComponentState,
33
- useDJVRuntime: () => useDJVRuntime,
34
- useData: () => useData,
35
- useDebouncedAction: () => useDebouncedAction,
36
- useGlobalConfig: () => useGlobalConfig,
37
- useHostApi: () => useHostApi,
38
- useLifecycle: () => useLifecycle,
39
- usePageInfo: () => usePageInfo,
40
- useQuery: () => useQuery,
41
- useRuntimeEvent: () => useRuntimeEvent,
42
- useRuntimeState: () => useRuntimeState,
43
- useRuntimeStateWritable: () => useRuntimeStateWritable,
44
- useWhen: () => useWhen
45
- });
46
- module.exports = __toCommonJS(index_exports);
47
-
48
- // src/vue-runtime.ts
49
- var import_vue = require("vue");
50
- var import_runtime_core = require("@djvlc/runtime-core");
51
- function createVueRuntime(options) {
52
- const runtime = (0, import_vue.shallowRef)(null);
53
- const loading = (0, import_vue.ref)(true);
54
- const phase = (0, import_vue.ref)("idle");
55
- const page = (0, import_vue.shallowRef)(null);
56
- const error = (0, import_vue.shallowRef)(null);
57
- const hostApi = (0, import_vue.shallowRef)(null);
58
- const isReady = (0, import_vue.computed)(() => phase.value === "ready");
59
- const hasError = (0, import_vue.computed)(() => phase.value === "error" || error.value !== null);
60
- const metrics = (0, import_vue.shallowRef)({
61
- initTime: 0,
62
- loadTime: 0,
63
- renderTime: 0,
64
- totalTime: 0,
65
- initTimestamp: null,
66
- readyTimestamp: null
67
- });
68
- let startTime = 0;
69
- let initStartTime = 0;
70
- let loadStartTime = 0;
71
- let renderStartTime = 0;
72
- const init = async () => {
73
- const container = options.containerRef?.value;
74
- if (!container) {
75
- throw new Error("Container element not found");
76
- }
77
- startTime = performance.now();
78
- initStartTime = startTime;
79
- if (options.enableMetrics) {
80
- metrics.value = {
81
- ...metrics.value,
82
- initTimestamp: Date.now()
83
- };
84
- }
85
- const runtimeInstance = (0, import_runtime_core.createRuntime)({
86
- ...options,
87
- container,
88
- onError: (err) => {
89
- error.value = err;
90
- options.onError?.(err);
91
- },
92
- onEvent: (event) => {
93
- options.onEvent?.(event);
94
- }
95
- });
96
- runtime.value = runtimeInstance;
97
- runtimeInstance.onStateChange((state) => {
98
- phase.value = state.phase;
99
- loading.value = state.phase !== "ready" && state.phase !== "error";
100
- if (state.page) {
101
- page.value = state.page;
102
- }
103
- if (state.error) {
104
- error.value = state.error;
105
- }
106
- });
107
- await runtimeInstance.init();
108
- hostApi.value = runtimeInstance.getHostApi();
109
- if (options.enableMetrics) {
110
- metrics.value = {
111
- ...metrics.value,
112
- initTime: performance.now() - initStartTime
113
- };
114
- }
115
- };
116
- const load = async () => {
117
- if (!runtime.value) {
118
- throw new Error("Runtime not initialized");
119
- }
120
- loadStartTime = performance.now();
121
- const result = await runtime.value.load();
122
- page.value = result;
123
- hostApi.value = runtime.value.getHostApi();
124
- if (options.enableMetrics) {
125
- metrics.value = {
126
- ...metrics.value,
127
- loadTime: performance.now() - loadStartTime
128
- };
129
- }
130
- return result;
131
- };
132
- const render = async () => {
133
- if (!runtime.value) {
134
- throw new Error("Runtime not initialized");
135
- }
136
- renderStartTime = performance.now();
137
- await runtime.value.render();
138
- loading.value = false;
139
- if (options.enableMetrics) {
140
- const now = performance.now();
141
- metrics.value = {
142
- ...metrics.value,
143
- renderTime: now - renderStartTime,
144
- totalTime: now - startTime,
145
- readyTimestamp: Date.now()
146
- };
147
- }
148
- };
149
- const destroy = () => {
150
- runtime.value?.destroy();
151
- runtime.value = null;
152
- hostApi.value = null;
153
- page.value = null;
154
- error.value = null;
155
- phase.value = "idle";
156
- loading.value = true;
157
- };
158
- const reload = async () => {
159
- if (!runtime.value) {
160
- throw new Error("Runtime not initialized");
161
- }
162
- error.value = null;
163
- loading.value = true;
164
- await load();
165
- await render();
166
- };
167
- const setVariable = (key, value) => {
168
- runtime.value?.setVariable(key, value);
169
- };
170
- const setVariables = (variables) => {
171
- if (!runtime.value) return;
172
- Object.entries(variables).forEach(([key, value]) => {
173
- runtime.value?.setVariable(key, value);
174
- });
175
- };
176
- const getVariable = (key) => {
177
- return runtime.value?.getState().variables[key];
178
- };
179
- const refreshData = async (queryId) => {
180
- await runtime.value?.refreshData(queryId);
181
- };
182
- const executeAction = async (actionType, params) => {
183
- const api = hostApi.value;
184
- if (!api) {
185
- throw new Error("HostAPI not available");
186
- }
187
- const response = await api.executeAction(actionType, params || {});
188
- if (response.success) {
189
- return response.data;
190
- }
191
- throw new Error(response.errorMessage || "Action failed");
192
- };
193
- return {
194
- runtime,
195
- loading: (0, import_vue.readonly)(loading),
196
- phase: (0, import_vue.readonly)(phase),
197
- page,
198
- error,
199
- hostApi,
200
- isReady,
201
- hasError,
202
- metrics,
203
- init,
204
- load,
205
- render,
206
- destroy,
207
- reload,
208
- setVariable,
209
- setVariables,
210
- getVariable,
211
- refreshData,
212
- executeAction
213
- };
214
- }
215
-
216
- // src/components/DJVRenderer.ts
217
- var import_vue3 = require("vue");
218
-
219
- // src/composables/useRuntime.ts
220
- var import_vue2 = require("vue");
221
- var RuntimeContextKey = /* @__PURE__ */ Symbol("DJVRuntime");
222
- function provideRuntime(value) {
223
- (0, import_vue2.provide)(RuntimeContextKey, value);
224
- }
225
- function injectRuntime() {
226
- const context = (0, import_vue2.inject)(RuntimeContextKey);
227
- if (!context) {
228
- throw new Error("useDJVRuntime must be used within a DJVProvider");
229
- }
230
- return context;
231
- }
232
- function useDJVRuntime() {
233
- const context = injectRuntime();
234
- const loading = (0, import_vue2.computed)(() => {
235
- const phase = context.value.state.phase;
236
- return phase !== "ready" && phase !== "error";
237
- });
238
- const isReady = (0, import_vue2.computed)(() => context.value.state.phase === "ready");
239
- const hasError = (0, import_vue2.computed)(() => context.value.state.phase === "error" || context.value.state.error !== null);
240
- const reload = async () => {
241
- const runtime = context.value.runtime;
242
- if (!runtime) {
243
- throw new Error("Runtime not available");
244
- }
245
- await runtime.load();
246
- await runtime.render();
247
- };
248
- return {
249
- runtime: (0, import_vue2.computed)(() => context.value.runtime),
250
- state: (0, import_vue2.computed)(() => context.value.state),
251
- loading,
252
- phase: (0, import_vue2.computed)(() => context.value.state.phase),
253
- error: (0, import_vue2.computed)(() => context.value.state.error),
254
- page: (0, import_vue2.computed)(() => context.value.state.page),
255
- isReady,
256
- hasError,
257
- reload
258
- };
259
- }
260
- function useHostApi() {
261
- const context = injectRuntime();
262
- const hostApi = context.value.hostApi;
263
- if (!hostApi) {
264
- throw new Error("HostAPI not available. Make sure runtime is initialized.");
265
- }
266
- return hostApi;
267
- }
268
- function useRuntimeState(key) {
269
- const context = injectRuntime();
270
- return (0, import_vue2.computed)(() => {
271
- return context.value.state.variables[key];
272
- });
273
- }
274
- function useRuntimeStateWritable(key, defaultValue) {
275
- const context = injectRuntime();
276
- const value = (0, import_vue2.computed)(() => {
277
- return context.value.state.variables[key] ?? defaultValue;
278
- });
279
- const setValue = (newValue) => {
280
- context.value.runtime?.setVariable(key, newValue);
281
- };
282
- return [value, setValue];
283
- }
284
- function useQuery(queryId, options) {
285
- const context = injectRuntime();
286
- const loading = (0, import_vue2.ref)(false);
287
- const error = (0, import_vue2.ref)(null);
288
- const lastUpdated = (0, import_vue2.ref)(null);
289
- let intervalTimer = null;
290
- const data = (0, import_vue2.computed)(() => {
291
- return context.value.state.queries[queryId];
292
- });
293
- const refetch = async () => {
294
- loading.value = true;
295
- error.value = null;
296
- try {
297
- await context.value.runtime?.refreshData(queryId);
298
- lastUpdated.value = Date.now();
299
- } catch (e) {
300
- error.value = e;
301
- } finally {
302
- loading.value = false;
303
- }
304
- };
305
- (0, import_vue2.onMounted)(() => {
306
- if (options?.refreshOnMount && context.value.runtime) {
307
- refetch();
308
- }
309
- if (options?.refreshInterval && options.refreshInterval > 0) {
310
- intervalTimer = setInterval(refetch, options.refreshInterval);
311
- }
312
- if (options?.refreshOnFocus) {
313
- window.addEventListener("focus", refetch);
314
- }
315
- });
316
- (0, import_vue2.onUnmounted)(() => {
317
- if (intervalTimer) {
318
- clearInterval(intervalTimer);
319
- }
320
- if (options?.refreshOnFocus) {
321
- window.removeEventListener("focus", refetch);
322
- }
323
- });
324
- return {
325
- data,
326
- loading,
327
- error,
328
- refetch,
329
- lastUpdated
330
- };
331
- }
332
- function useAction(actionType, options) {
333
- const context = injectRuntime();
334
- const loading = (0, import_vue2.ref)(false);
335
- const result = (0, import_vue2.shallowRef)();
336
- const error = (0, import_vue2.ref)(null);
337
- const executionCount = (0, import_vue2.ref)(0);
338
- const reset = () => {
339
- result.value = void 0;
340
- error.value = null;
341
- executionCount.value = 0;
342
- };
343
- const executeWithRetry = async (params, retriesLeft) => {
344
- const hostApi = context.value.hostApi;
345
- if (!hostApi) {
346
- throw new Error("HostAPI not available");
347
- }
348
- try {
349
- const response = await hostApi.executeAction(actionType, params);
350
- if (response.success) {
351
- return response.data;
352
- } else {
353
- throw new Error(response.errorMessage || "Action failed");
354
- }
355
- } catch (e) {
356
- if (retriesLeft > 0) {
357
- await new Promise((resolve) => setTimeout(resolve, options?.retryDelay || 1e3));
358
- return executeWithRetry(params, retriesLeft - 1);
359
- }
360
- throw e;
361
- }
362
- };
363
- const execute = async (params) => {
364
- loading.value = true;
365
- error.value = null;
366
- executionCount.value++;
367
- try {
368
- const data = await executeWithRetry(params, options?.retryCount || 0);
369
- result.value = data;
370
- options?.onSuccess?.(data);
371
- return data;
372
- } catch (e) {
373
- const err = e;
374
- error.value = err;
375
- options?.onError?.(err);
376
- throw e;
377
- } finally {
378
- loading.value = false;
379
- }
380
- };
381
- return {
382
- execute,
383
- loading,
384
- result,
385
- error,
386
- reset,
387
- executionCount
388
- };
389
- }
390
- function useData(queryId, params, options) {
391
- const context = injectRuntime();
392
- const data = (0, import_vue2.shallowRef)();
393
- const loading = (0, import_vue2.ref)(false);
394
- const error = (0, import_vue2.ref)(null);
395
- const isFetched = (0, import_vue2.ref)(false);
396
- let intervalTimer = null;
397
- const getCurrentParams = () => {
398
- if (!params) return void 0;
399
- if (typeof params === "object" && "value" in params) {
400
- return params.value;
401
- }
402
- return params;
403
- };
404
- const refetch = async (newParams) => {
405
- const hostApi = context.value.hostApi;
406
- if (!hostApi) {
407
- throw new Error("HostAPI not available");
408
- }
409
- loading.value = true;
410
- error.value = null;
411
- try {
412
- const result = await hostApi.requestData(
413
- queryId,
414
- newParams || getCurrentParams()
415
- );
416
- data.value = result;
417
- isFetched.value = true;
418
- options?.onSuccess?.(result);
419
- } catch (e) {
420
- const err = e;
421
- error.value = err;
422
- options?.onError?.(err);
423
- } finally {
424
- loading.value = false;
425
- }
426
- };
427
- if (params && typeof params === "object" && "value" in params && options?.refreshOnParamsChange !== false) {
428
- (0, import_vue2.watch)(params, () => {
429
- if (isFetched.value) {
430
- refetch();
431
- }
432
- }, { deep: true });
433
- }
434
- (0, import_vue2.onMounted)(() => {
435
- if (options?.immediate !== false && context.value.hostApi) {
436
- refetch();
437
- }
438
- if (options?.refreshInterval && options.refreshInterval > 0) {
439
- intervalTimer = setInterval(refetch, options.refreshInterval);
440
- }
441
- });
442
- (0, import_vue2.onUnmounted)(() => {
443
- if (intervalTimer) {
444
- clearInterval(intervalTimer);
445
- }
446
- });
447
- return {
448
- data,
449
- loading,
450
- error,
451
- refetch,
452
- isFetched
453
- };
454
- }
455
- function useRuntimeEvent(eventType, handler) {
456
- const context = injectRuntime();
457
- const unsubscribe = context.value.runtime?.on(eventType, (event) => {
458
- handler(event.data);
459
- });
460
- (0, import_vue2.onUnmounted)(() => {
461
- unsubscribe?.();
462
- });
463
- }
464
- function usePageInfo() {
465
- const context = injectRuntime();
466
- return {
467
- /** 页面 UID */
468
- pageUid: (0, import_vue2.computed)(() => context.value.state.page?.pageUid),
469
- /** 页面版本 ID */
470
- pageVersionId: (0, import_vue2.computed)(() => context.value.state.page?.pageVersionId),
471
- /** Schema 版本 */
472
- schemaVersion: (0, import_vue2.computed)(() => context.value.state.page?.pageJson?.schemaVersion),
473
- /** 页面标题 */
474
- title: (0, import_vue2.computed)(() => {
475
- const page = context.value.state.page;
476
- return page?.title;
477
- }),
478
- /** 页面配置 */
479
- config: (0, import_vue2.computed)(() => {
480
- const page = context.value.state.page;
481
- return page?.config;
482
- }),
483
- /** 页面是否已加载 */
484
- isLoaded: (0, import_vue2.computed)(() => context.value.state.page !== null)
485
- };
486
- }
487
- function useComponentState(componentId) {
488
- const context = injectRuntime();
489
- const componentStatus = (0, import_vue2.computed)(() => {
490
- return context.value.state.components.get(componentId);
491
- });
492
- return {
493
- /** 组件是否已加载 */
494
- isLoaded: (0, import_vue2.computed)(() => componentStatus.value?.status === "loaded"),
495
- /** 组件是否加载中 */
496
- isLoading: (0, import_vue2.computed)(() => componentStatus.value?.status === "loading"),
497
- /** 组件是否加载失败 */
498
- hasError: (0, import_vue2.computed)(() => componentStatus.value?.status === "failed"),
499
- /** 加载耗时 */
500
- loadTime: (0, import_vue2.computed)(() => componentStatus.value?.loadTime),
501
- /** 组件信息 */
502
- info: componentStatus
503
- };
504
- }
505
- function useLifecycle(options) {
506
- const context = injectRuntime();
507
- const hasMounted = (0, import_vue2.ref)(false);
508
- const hasReady = (0, import_vue2.ref)(false);
509
- (0, import_vue2.watch)(
510
- () => context.value.state.phase,
511
- async (newPhase, oldPhase) => {
512
- options?.onPhaseChange?.(newPhase);
513
- if (!hasMounted.value && newPhase !== "idle") {
514
- hasMounted.value = true;
515
- try {
516
- await options?.onMounted?.();
517
- } catch (error) {
518
- options?.onError?.(error);
519
- }
520
- }
521
- if (!hasReady.value && newPhase === "ready") {
522
- hasReady.value = true;
523
- try {
524
- await options?.onReady?.();
525
- } catch (error) {
526
- options?.onError?.(error);
527
- }
528
- }
529
- if (newPhase === "error" && oldPhase !== "error") {
530
- options?.onError?.(context.value.state.error);
531
- }
532
- },
533
- { immediate: true }
534
- );
535
- return {
536
- /** 当前阶段 */
537
- phase: (0, import_vue2.computed)(() => context.value.state.phase),
538
- /** 是否已 mounted */
539
- hasMounted: (0, import_vue2.readonly)(hasMounted),
540
- /** 是否已 ready */
541
- hasReady: (0, import_vue2.readonly)(hasReady)
542
- };
543
- }
544
- function useWhen(condition, callback, options) {
545
- const executed = (0, import_vue2.ref)(false);
546
- const { once = true, immediate = true } = options || {};
547
- const stop = (0, import_vue2.watch)(
548
- condition,
549
- async (value) => {
550
- if (value && (!once || !executed.value)) {
551
- executed.value = true;
552
- await callback();
553
- if (once) {
554
- stop();
555
- }
556
- }
557
- },
558
- { immediate }
559
- );
560
- (0, import_vue2.onUnmounted)(() => {
561
- stop();
562
- });
563
- return {
564
- /** 是否已执行 */
565
- executed: (0, import_vue2.readonly)(executed),
566
- /** 手动停止监听 */
567
- stop
568
- };
569
- }
570
- function useDebouncedAction(actionType, delay = 300) {
571
- const { execute: rawExecute, loading, result, error } = useAction(actionType);
572
- let timeoutId = null;
573
- const execute = (params) => {
574
- if (timeoutId) {
575
- clearTimeout(timeoutId);
576
- }
577
- timeoutId = setTimeout(() => {
578
- rawExecute(params).catch(() => {
579
- });
580
- }, delay);
581
- };
582
- const cancel = () => {
583
- if (timeoutId) {
584
- clearTimeout(timeoutId);
585
- timeoutId = null;
586
- }
587
- };
588
- (0, import_vue2.onUnmounted)(() => {
589
- cancel();
590
- });
591
- return {
592
- execute,
593
- loading,
594
- result,
595
- error,
596
- cancel
597
- };
598
- }
599
- function useGlobalConfig() {
600
- const config = (0, import_vue2.inject)("djvlc-config", {});
601
- return config;
602
- }
603
-
604
- // src/components/DJVRenderer.ts
605
- var DJVRenderer = (0, import_vue3.defineComponent)({
606
- name: "DJVRenderer",
607
- props: {
608
- pageUid: {
609
- type: String,
610
- required: true
611
- },
612
- apiBaseUrl: {
613
- type: String,
614
- required: true
615
- },
616
- cdnBaseUrl: {
617
- type: String,
618
- required: true
619
- },
620
- channel: {
621
- type: String,
622
- default: "prod"
623
- },
624
- userId: String,
625
- deviceId: String,
626
- authToken: String,
627
- previewToken: String,
628
- debug: {
629
- type: Boolean,
630
- default: false
631
- },
632
- enableSRI: {
633
- type: Boolean,
634
- default: true
635
- },
636
- retryCount: {
637
- type: Number,
638
- default: 3
639
- },
640
- retryDelay: {
641
- type: Number,
642
- default: 1e3
643
- },
644
- timeout: {
645
- type: Number,
646
- default: 3e4
647
- }
648
- },
649
- emits: ["load", "error", "ready", "phase-change"],
650
- setup(props, { emit, slots }) {
651
- const containerRef = (0, import_vue3.ref)(null);
652
- const mounted = (0, import_vue3.ref)(true);
653
- const retryAttempt = (0, import_vue3.ref)(0);
654
- const phase = (0, import_vue3.ref)("idle");
655
- const contextValue = (0, import_vue3.shallowRef)({
656
- runtime: null,
657
- state: {
658
- phase: "idle",
659
- page: null,
660
- variables: {},
661
- queries: {},
662
- components: /* @__PURE__ */ new Map(),
663
- error: null,
664
- destroyed: false
665
- },
666
- hostApi: null
667
- });
668
- provideRuntime(contextValue);
669
- let vueRuntime = null;
670
- const withTimeout = (promise, ms) => {
671
- return Promise.race([
672
- promise,
673
- new Promise(
674
- (_, reject) => setTimeout(() => reject(new Error("\u52A0\u8F7D\u8D85\u65F6")), ms)
675
- )
676
- ]);
677
- };
678
- const initWithRetry = async () => {
679
- if (!containerRef.value || !mounted.value) return;
680
- const options = {
681
- pageUid: props.pageUid,
682
- apiBaseUrl: props.apiBaseUrl,
683
- cdnBaseUrl: props.cdnBaseUrl,
684
- channel: props.channel,
685
- userId: props.userId,
686
- deviceId: props.deviceId,
687
- authToken: props.authToken,
688
- previewToken: props.previewToken,
689
- debug: props.debug,
690
- enableSRI: props.enableSRI,
691
- containerRef,
692
- onError: (error) => {
693
- emit("error", error);
694
- }
695
- };
696
- vueRuntime = createVueRuntime(options);
697
- try {
698
- await withTimeout(
699
- (async () => {
700
- await vueRuntime.init();
701
- if (!mounted.value) return;
702
- phase.value = "loading";
703
- emit("phase-change", "loading");
704
- contextValue.value = {
705
- runtime: vueRuntime.runtime.value,
706
- state: vueRuntime.runtime.value?.getState() || contextValue.value.state,
707
- hostApi: vueRuntime.hostApi.value
708
- };
709
- const pageData = await vueRuntime.load();
710
- if (!mounted.value) return;
711
- emit("load", pageData);
712
- contextValue.value = {
713
- ...contextValue.value,
714
- state: vueRuntime.runtime.value?.getState() || contextValue.value.state,
715
- hostApi: vueRuntime.hostApi.value
716
- };
717
- await vueRuntime.render();
718
- if (!mounted.value) return;
719
- phase.value = "ready";
720
- emit("phase-change", "ready");
721
- emit("ready");
722
- retryAttempt.value = 0;
723
- vueRuntime.runtime.value?.onStateChange((state) => {
724
- if (!mounted.value) return;
725
- contextValue.value = {
726
- ...contextValue.value,
727
- state
728
- };
729
- phase.value = state.phase;
730
- emit("phase-change", state.phase);
731
- });
732
- })(),
733
- props.timeout
734
- );
735
- } catch (error) {
736
- if (!mounted.value) return;
737
- if (retryAttempt.value < props.retryCount) {
738
- retryAttempt.value++;
739
- if (props.debug) {
740
- console.log(`[DJVRenderer] \u91CD\u8BD5 ${retryAttempt.value}/${props.retryCount}...`);
741
- }
742
- setTimeout(() => {
743
- if (mounted.value) {
744
- initWithRetry();
745
- }
746
- }, props.retryDelay);
747
- } else {
748
- phase.value = "error";
749
- emit("phase-change", "error");
750
- emit("error", error);
751
- }
752
- }
753
- };
754
- const handleRetry = () => {
755
- retryAttempt.value = 0;
756
- initWithRetry();
757
- };
758
- (0, import_vue3.onMounted)(() => {
759
- mounted.value = true;
760
- initWithRetry();
761
- });
762
- (0, import_vue3.onUnmounted)(() => {
763
- mounted.value = false;
764
- vueRuntime?.destroy();
765
- });
766
- (0, import_vue3.watch)(
767
- () => props.pageUid,
768
- () => {
769
- vueRuntime?.destroy();
770
- retryAttempt.value = 0;
771
- initWithRetry();
772
- }
773
- );
774
- const renderDefaultLoading = () => {
775
- return (0, import_vue3.h)("div", { class: "djvlc-loading" }, [
776
- (0, import_vue3.h)("div", { class: "djvlc-loading-spinner" }),
777
- (0, import_vue3.h)("span", {}, "\u52A0\u8F7D\u4E2D..."),
778
- retryAttempt.value > 0 && (0, import_vue3.h)("span", { class: "djvlc-loading-retry" }, `\u91CD\u8BD5 ${retryAttempt.value}/${props.retryCount}`)
779
- ]);
780
- };
781
- const renderDefaultError = (error) => {
782
- return (0, import_vue3.h)("div", { class: "djvlc-error" }, [
783
- (0, import_vue3.h)("div", { class: "djvlc-error-icon" }, "\u26A0\uFE0F"),
784
- (0, import_vue3.h)("span", { class: "djvlc-error-message" }, `\u52A0\u8F7D\u5931\u8D25\uFF1A${error.message}`),
785
- (0, import_vue3.h)(
786
- "button",
787
- {
788
- class: "djvlc-error-retry-btn",
789
- onClick: handleRetry,
790
- type: "button"
791
- },
792
- "\u91CD\u8BD5"
793
- )
794
- ]);
795
- };
796
- const renderDefaultEmpty = () => {
797
- return (0, import_vue3.h)("div", { class: "djvlc-empty" }, [(0, import_vue3.h)("span", {}, "\u6682\u65E0\u5185\u5BB9")]);
798
- };
799
- return () => {
800
- const isLoading = vueRuntime?.loading.value ?? false;
801
- const currentError = vueRuntime?.error.value;
802
- const currentPage = vueRuntime?.page.value;
803
- return (0, import_vue3.h)(
804
- "div",
805
- {
806
- ref: containerRef,
807
- class: "djvlc-renderer",
808
- "data-phase": phase.value,
809
- "data-page-uid": props.pageUid
810
- },
811
- [
812
- // 加载中插槽
813
- isLoading && (slots.loading?.() || renderDefaultLoading()),
814
- // 错误插槽
815
- currentError && (slots.error?.({ error: currentError, retry: handleRetry }) || renderDefaultError(currentError)),
816
- // 空状态
817
- !isLoading && !currentError && !currentPage && phase.value === "ready" && (slots.empty?.() || renderDefaultEmpty()),
818
- // 默认插槽(额外内容)
819
- slots.default?.()
820
- ]
821
- );
822
- };
823
- }
824
- });
825
-
826
- // src/components/DJVProvider.ts
827
- var import_vue4 = require("vue");
828
- var defaultState = {
829
- phase: "idle",
830
- page: null,
831
- variables: {},
832
- queries: {},
833
- components: /* @__PURE__ */ new Map(),
834
- error: null,
835
- destroyed: false
836
- };
837
- var DJVProvider = (0, import_vue4.defineComponent)({
838
- name: "DJVProvider",
839
- props: {
840
- runtime: {
841
- type: Object,
842
- default: null
843
- },
844
- hostApi: {
845
- type: Object,
846
- default: null
847
- },
848
- class: {
849
- type: String,
850
- default: ""
851
- },
852
- debug: {
853
- type: Boolean,
854
- default: false
855
- }
856
- },
857
- emits: ["state-change", "phase-change", "error"],
858
- setup(props, { slots, emit }) {
859
- let unsubscribe = null;
860
- const contextValue = (0, import_vue4.shallowRef)({
861
- runtime: props.runtime,
862
- state: props.runtime?.getState() || defaultState,
863
- hostApi: props.hostApi
864
- });
865
- provideRuntime(contextValue);
866
- const subscribeToRuntime = (runtime) => {
867
- if (unsubscribe) {
868
- unsubscribe();
869
- unsubscribe = null;
870
- }
871
- if (!runtime) return;
872
- unsubscribe = runtime.onStateChange((state) => {
873
- const prevPhase = contextValue.value.state.phase;
874
- contextValue.value = {
875
- ...contextValue.value,
876
- state
877
- };
878
- emit("state-change", state);
879
- if (state.phase !== prevPhase) {
880
- emit("phase-change", state.phase);
881
- if (props.debug) {
882
- console.log(`[DJVProvider] Phase changed: ${prevPhase} -> ${state.phase}`);
883
- }
884
- }
885
- if (state.error) {
886
- emit("error", state.error);
887
- }
888
- });
889
- };
890
- subscribeToRuntime(props.runtime);
891
- (0, import_vue4.watch)(
892
- () => props.runtime,
893
- (newRuntime, oldRuntime) => {
894
- if (newRuntime !== oldRuntime) {
895
- contextValue.value = {
896
- runtime: newRuntime,
897
- state: newRuntime?.getState() || defaultState,
898
- hostApi: props.hostApi
899
- };
900
- subscribeToRuntime(newRuntime);
901
- }
902
- }
903
- );
904
- (0, import_vue4.watch)(
905
- () => props.hostApi,
906
- (newHostApi) => {
907
- contextValue.value = {
908
- ...contextValue.value,
909
- hostApi: newHostApi
910
- };
911
- }
912
- );
913
- (0, import_vue4.onUnmounted)(() => {
914
- if (unsubscribe) {
915
- unsubscribe();
916
- unsubscribe = null;
917
- }
918
- });
919
- return () => (0, import_vue4.h)(
920
- "div",
921
- {
922
- class: ["djvlc-provider", props.class].filter(Boolean).join(" "),
923
- "data-phase": contextValue.value.state.phase
924
- },
925
- slots.default?.()
926
- );
927
- }
928
- });
929
-
930
- // src/plugin.ts
931
- var DJVLC_CONFIG_KEY = "djvlc-config";
932
- function createTrackDirective() {
933
- const observedElements = /* @__PURE__ */ new WeakSet();
934
- return {
935
- mounted(el, binding) {
936
- const { event, data = {}, trigger = "click" } = binding.value;
937
- if (trigger === "click") {
938
- el.addEventListener("click", () => {
939
- dispatchTrackEvent(event, { ...data, element: el.tagName });
940
- });
941
- } else if (trigger === "view") {
942
- if (!observedElements.has(el)) {
943
- observedElements.add(el);
944
- const observer = new IntersectionObserver(
945
- (entries) => {
946
- entries.forEach((entry) => {
947
- if (entry.isIntersecting) {
948
- dispatchTrackEvent(event, { ...data, element: el.tagName });
949
- observer.unobserve(el);
950
- }
951
- });
952
- },
953
- { threshold: 0.5 }
954
- );
955
- observer.observe(el);
956
- }
957
- } else if (trigger === "mounted") {
958
- dispatchTrackEvent(event, { ...data, element: el.tagName });
959
- }
960
- }
961
- };
962
- }
963
- function dispatchTrackEvent(event, data) {
964
- window.dispatchEvent(
965
- new CustomEvent("djvlc:track", {
966
- detail: { event, data, timestamp: Date.now() }
967
- })
968
- );
969
- }
970
- function createVisibleDirective() {
971
- return {
972
- mounted(el, binding) {
973
- updateVisibility(el, binding.value);
974
- },
975
- updated(el, binding) {
976
- updateVisibility(el, binding.value);
977
- }
978
- };
979
- }
980
- function updateVisibility(el, visible) {
981
- el.style.display = visible ? "" : "none";
982
- }
983
- function createLoadingDirective() {
984
- const loadingOverlays = /* @__PURE__ */ new WeakMap();
985
- return {
986
- mounted(el, binding) {
987
- el.style.position = "relative";
988
- updateLoading(el, binding.value, loadingOverlays);
989
- },
990
- updated(el, binding) {
991
- updateLoading(el, binding.value, loadingOverlays);
992
- },
993
- unmounted(el) {
994
- const overlay = loadingOverlays.get(el);
995
- if (overlay) {
996
- overlay.remove();
997
- loadingOverlays.delete(el);
998
- }
999
- }
1000
- };
1001
- }
1002
- function updateLoading(el, loading, overlays) {
1003
- let overlay = overlays.get(el);
1004
- if (loading) {
1005
- if (!overlay) {
1006
- overlay = document.createElement("div");
1007
- overlay.className = "djvlc-loading-overlay";
1008
- overlay.innerHTML = `
1009
- <div class="djvlc-loading-spinner"></div>
1010
- `;
1011
- overlay.style.cssText = `
1012
- position: absolute;
1013
- inset: 0;
1014
- display: flex;
1015
- align-items: center;
1016
- justify-content: center;
1017
- background: rgba(255, 255, 255, 0.8);
1018
- z-index: 100;
1019
- `;
1020
- el.appendChild(overlay);
1021
- overlays.set(el, overlay);
1022
- }
1023
- overlay.style.display = "flex";
1024
- } else if (overlay) {
1025
- overlay.style.display = "none";
1026
- }
1027
- }
1028
- var DJVPlugin = {
1029
- install(app, options = {}) {
1030
- const prefix = options.componentPrefix || "";
1031
- if (options.registerComponents !== false) {
1032
- app.component(`${prefix}DJVRenderer`, DJVRenderer);
1033
- app.component(`${prefix}DJVProvider`, DJVProvider);
1034
- }
1035
- if (options.registerDirectives !== false) {
1036
- app.directive("djv-track", createTrackDirective());
1037
- app.directive("djv-visible", createVisibleDirective());
1038
- app.directive("djv-loading", createLoadingDirective());
1039
- }
1040
- const globalConfig = {
1041
- apiBaseUrl: options.apiBaseUrl,
1042
- cdnBaseUrl: options.cdnBaseUrl,
1043
- channel: options.channel,
1044
- debug: options.debug,
1045
- enableSRI: options.enableSRI,
1046
- enableMetrics: options.enableMetrics,
1047
- retryCount: options.retryCount,
1048
- retryDelay: options.retryDelay,
1049
- timeout: options.timeout
1050
- };
1051
- app.provide(DJVLC_CONFIG_KEY, globalConfig);
1052
- app.config.globalProperties.$djvlc = {
1053
- config: globalConfig,
1054
- track: dispatchTrackEvent
1055
- };
1056
- }
1057
- };
1058
- // Annotate the CommonJS export names for ESM import in node:
1059
- 0 && (module.exports = {
1060
- DJVLC_CONFIG_KEY,
1061
- DJVPlugin,
1062
- DJVProvider,
1063
- DJVRenderer,
1064
- RuntimeContextKey,
1065
- createVueRuntime,
1066
- injectRuntime,
1067
- provideRuntime,
1068
- useAction,
1069
- useComponentState,
1070
- useDJVRuntime,
1071
- useData,
1072
- useDebouncedAction,
1073
- useGlobalConfig,
1074
- useHostApi,
1075
- useLifecycle,
1076
- usePageInfo,
1077
- useQuery,
1078
- useRuntimeEvent,
1079
- useRuntimeState,
1080
- useRuntimeStateWritable,
1081
- useWhen
1082
- });
1
+ "use strict";var e=require("vue"),t=require("@djvlc/runtime-core");function r(r){const n=e.shallowRef(null),a=e.ref(!0),o=e.ref("idle"),i=e.shallowRef(null),s=e.shallowRef(null),l=e.shallowRef(null),c=e.computed(()=>"ready"===o.value),u=e.computed(()=>"error"===o.value||null!==s.value),d=e.shallowRef({initTime:0,loadTime:0,renderTime:0,totalTime:0,initTimestamp:null,readyTimestamp:null});let p=0,m=0,f=0,y=0;const h=async()=>{if(!n.value)throw new Error("Runtime not initialized");f=performance.now();const e=await n.value.load();return i.value=e,l.value=n.value.getHostApi(),r.enableMetrics&&(d.value={...d.value,loadTime:performance.now()-f}),e},v=async()=>{if(!n.value)throw new Error("Runtime not initialized");if(y=performance.now(),await n.value.render(),a.value=!1,r.enableMetrics){const e=performance.now();d.value={...d.value,renderTime:e-y,totalTime:e-p,readyTimestamp:Date.now()}}};return{runtime:n,loading:e.readonly(a),phase:e.readonly(o),page:i,error:s,hostApi:l,isReady:c,hasError:u,metrics:d,init:async()=>{const e=r.containerRef?.value;if(!e)throw new Error("Container element not found");p=performance.now(),m=p,r.enableMetrics&&(d.value={...d.value,initTimestamp:Date.now()});const c=t.createRuntime({...r,container:e,onError:e=>{s.value=e,r.onError?.(e)},onEvent:e=>{r.onEvent?.(e)}});n.value=c,c.onStateChange(e=>{o.value=e.phase,a.value="ready"!==e.phase&&"error"!==e.phase,e.page&&(i.value=e.page),e.error&&(s.value=e.error)}),await c.init(),l.value=c.getHostApi(),r.enableMetrics&&(d.value={...d.value,initTime:performance.now()-m})},load:h,render:v,destroy:()=>{n.value?.destroy(),n.value=null,l.value=null,i.value=null,s.value=null,o.value="idle",a.value=!0},reload:async()=>{if(!n.value)throw new Error("Runtime not initialized");s.value=null,a.value=!0,await h(),await v()},setVariable:(e,t)=>{n.value?.setVariable(e,t)},setVariables:e=>{n.value&&Object.entries(e).forEach(([e,t])=>{n.value?.setVariable(e,t)})},getVariable:e=>n.value?.getState().variables[e],refreshData:async e=>{await(n.value?.refreshData(e))},executeAction:async(e,t)=>{const r=l.value;if(!r)throw new Error("HostAPI not available");const n=await r.executeAction(e,t||{});if(n.success)return n.data;throw new Error(n.errorMessage||"Action failed")}}}var n=Symbol("DJVRuntime");function a(t){e.provide(n,t)}function o(){const t=e.inject(n);if(!t)throw new Error("useDJVRuntime must be used within a DJVProvider");return t}function i(t,r){const n=o(),a=e.ref(!1),i=e.shallowRef(),s=e.ref(null),l=e.ref(0),c=async(e,a)=>{const o=n.value.hostApi;if(!o)throw new Error("HostAPI not available");try{const r=await o.executeAction(t,e);if(r.success)return r.data;throw new Error(r.errorMessage||"Action failed")}catch(t){if(a>0)return await new Promise(e=>setTimeout(e,r?.retryDelay||1e3)),c(e,a-1);throw t}};return{execute:async e=>{a.value=!0,s.value=null,l.value++;try{const t=await c(e,r?.retryCount||0);return i.value=t,r?.onSuccess?.(t),t}catch(e){const t=e;throw s.value=t,r?.onError?.(t),e}finally{a.value=!1}},loading:a,result:i,error:s,reset:()=>{i.value=void 0,s.value=null,l.value=0},executionCount:l}}var s=e.defineComponent({name:"DJVRenderer",props:{pageUid:{type:String,required:!0},apiBaseUrl:{type:String,required:!0},cdnBaseUrl:{type:String,required:!0},channel:{type:String,default:"prod"},userId:String,deviceId:String,authToken:String,previewToken:String,debug:{type:Boolean,default:!1},enableSRI:{type:Boolean,default:!0},retryCount:{type:Number,default:3},retryDelay:{type:Number,default:1e3},timeout:{type:Number,default:3e4}},emits:["load","error","ready","phase-change"],setup(t,{emit:n,slots:o}){const i=e.ref(null),s=e.ref(!0),l=e.ref(0),c=e.ref("idle"),u=e.shallowRef({runtime:null,state:{phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},hostApi:null});a(u);let d=null;const p=async()=>{if(!i.value||!s.value)return;const e={pageUid:t.pageUid,apiBaseUrl:t.apiBaseUrl,cdnBaseUrl:t.cdnBaseUrl,channel:t.channel,userId:t.userId,deviceId:t.deviceId,authToken:t.authToken,previewToken:t.previewToken,debug:t.debug,enableSRI:t.enableSRI,containerRef:i,onError:e=>{n("error",e)}};d=r(e);try{await(a=(async()=>{if(await d.init(),!s.value)return;c.value="loading",n("phase-change","loading"),u.value={runtime:d.runtime.value,state:d.runtime.value?.getState()||u.value.state,hostApi:d.hostApi.value};const e=await d.load();s.value&&(n("load",e),u.value={...u.value,state:d.runtime.value?.getState()||u.value.state,hostApi:d.hostApi.value},await d.render(),s.value&&(c.value="ready",n("phase-change","ready"),n("ready"),l.value=0,d.runtime.value?.onStateChange(e=>{s.value&&(u.value={...u.value,state:e},c.value=e.phase,n("phase-change",e.phase))})))})(),o=t.timeout,Promise.race([a,new Promise((e,t)=>setTimeout(()=>t(new Error("加载超时")),o))]))}catch(e){if(!s.value)return;l.value<t.retryCount?(l.value++,t.debug,setTimeout(()=>{s.value&&p()},t.retryDelay)):(c.value="error",n("phase-change","error"),n("error",e))}var a,o},m=()=>{l.value=0,p()};return e.onMounted(()=>{s.value=!0,p()}),e.onUnmounted(()=>{s.value=!1,d?.destroy()}),e.watch(()=>t.pageUid,()=>{d?.destroy(),l.value=0,p()}),()=>{const r=d?.loading.value??!1,n=d?.error.value,a=d?.page.value;return e.h("div",{ref:i,class:"djvlc-renderer","data-phase":c.value,"data-page-uid":t.pageUid},[r&&(o.loading?.()||e.h("div",{class:"djvlc-loading"},[e.h("div",{class:"djvlc-loading-spinner"}),e.h("span",{},"加载中..."),l.value>0&&e.h("span",{class:"djvlc-loading-retry"},`重试 ${l.value}/${t.retryCount}`)])),n&&(o.error?.({error:n,retry:m})||(s=n,e.h("div",{class:"djvlc-error"},[e.h("div",{class:"djvlc-error-icon"},"⚠️"),e.h("span",{class:"djvlc-error-message"},`加载失败:${s.message}`),e.h("button",{class:"djvlc-error-retry-btn",onClick:m,type:"button"},"重试")]))),!r&&!n&&!a&&"ready"===c.value&&(o.empty?.()||e.h("div",{class:"djvlc-empty"},[e.h("span",{},"暂无内容")])),o.default?.()]);var s}}}),l={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},c=e.defineComponent({name:"DJVProvider",props:{runtime:{type:Object,default:null},hostApi:{type:Object,default:null},class:{type:String,default:""},debug:{type:Boolean,default:!1}},emits:["state-change","phase-change","error"],setup(t,{slots:r,emit:n}){let o=null;const i=e.shallowRef({runtime:t.runtime,state:t.runtime?.getState()||l,hostApi:t.hostApi});a(i);const s=e=>{o&&(o(),o=null),e&&(o=e.onStateChange(e=>{const r=i.value.state.phase;i.value={...i.value,state:e},n("state-change",e),e.phase!==r&&(n("phase-change",e.phase),t.debug),e.error&&n("error",e.error)}))};return s(t.runtime),e.watch(()=>t.runtime,(e,r)=>{e!==r&&(i.value={runtime:e,state:e?.getState()||l,hostApi:t.hostApi},s(e))}),e.watch(()=>t.hostApi,e=>{i.value={...i.value,hostApi:e}}),e.onUnmounted(()=>{o&&(o(),o=null)}),()=>e.h("div",{class:["djvlc-provider",t.class].filter(Boolean).join(" "),"data-phase":i.value.state.phase},r.default?.())}}),u="djvlc-config";function d(e,t){window.dispatchEvent(new CustomEvent("djvlc:track",{detail:{event:e,data:t,timestamp:Date.now()}}))}function p(e,t){e.style.display=t?"":"none"}function m(e,t,r){let n=r.get(e);t?(n||(n=document.createElement("div"),n.className="djvlc-loading-overlay",n.innerHTML='\n <div class="djvlc-loading-spinner"></div>\n ',n.style.cssText="\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.8);\n z-index: 100;\n ",e.appendChild(n),r.set(e,n)),n.style.display="flex"):n&&(n.style.display="none")}var f={install(e,t={}){const r=t.componentPrefix||"";!1!==t.registerComponents&&(e.component(`${r}DJVRenderer`,s),e.component(`${r}DJVProvider`,c)),!1!==t.registerDirectives&&(e.directive("djv-track",function(){const e=new WeakSet;return{mounted(t,r){const{event:n,data:a={},trigger:o="click"}=r.value;if("click"===o)t.addEventListener("click",()=>{d(n,{...a,element:t.tagName})});else if("view"===o){if(!e.has(t)){e.add(t);const r=new IntersectionObserver(e=>{e.forEach(e=>{e.isIntersecting&&(d(n,{...a,element:t.tagName}),r.unobserve(t))})},{threshold:.5});r.observe(t)}}else"mounted"===o&&d(n,{...a,element:t.tagName})}}}()),e.directive("djv-visible",{mounted(e,t){p(e,t.value)},updated(e,t){p(e,t.value)}}),e.directive("djv-loading",function(){const e=new WeakMap;return{mounted(t,r){t.style.position="relative",m(t,r.value,e)},updated(t,r){m(t,r.value,e)},unmounted(t){const r=e.get(t);r&&(r.remove(),e.delete(t))}}}()));const n={apiBaseUrl:t.apiBaseUrl,cdnBaseUrl:t.cdnBaseUrl,channel:t.channel,debug:t.debug,enableSRI:t.enableSRI,enableMetrics:t.enableMetrics,retryCount:t.retryCount,retryDelay:t.retryDelay,timeout:t.timeout};e.provide(u,n),e.config.globalProperties.$djvlc={config:n,track:d}}};exports.DJVLC_CONFIG_KEY=u,exports.DJVPlugin=f,exports.DJVProvider=c,exports.DJVRenderer=s,exports.RuntimeContextKey=n,exports.createVueRuntime=r,exports.injectRuntime=o,exports.provideRuntime=a,exports.useAction=i,exports.useComponentState=function(t){const r=o(),n=e.computed(()=>r.value.state.components.get(t));return{isLoaded:e.computed(()=>"loaded"===n.value?.status),isLoading:e.computed(()=>"loading"===n.value?.status),hasError:e.computed(()=>"failed"===n.value?.status),loadTime:e.computed(()=>n.value?.loadTime),info:n}},exports.useDJVRuntime=function(){const t=o(),r=e.computed(()=>{const e=t.value.state.phase;return"ready"!==e&&"error"!==e}),n=e.computed(()=>"ready"===t.value.state.phase),a=e.computed(()=>"error"===t.value.state.phase||null!==t.value.state.error);return{runtime:e.computed(()=>t.value.runtime),state:e.computed(()=>t.value.state),loading:r,phase:e.computed(()=>t.value.state.phase),error:e.computed(()=>t.value.state.error),page:e.computed(()=>t.value.state.page),isReady:n,hasError:a,reload:async()=>{const e=t.value.runtime;if(!e)throw new Error("Runtime not available");await e.load(),await e.render()}}},exports.useData=function(t,r,n){const a=o(),i=e.shallowRef(),s=e.ref(!1),l=e.ref(null),c=e.ref(!1);let u=null;const d=async e=>{const o=a.value.hostApi;if(!o)throw new Error("HostAPI not available");s.value=!0,l.value=null;try{const a=await o.requestData(t,e||(()=>{if(r)return"object"==typeof r&&"value"in r?r.value:r})());i.value=a,c.value=!0,n?.onSuccess?.(a)}catch(e){const t=e;l.value=t,n?.onError?.(t)}finally{s.value=!1}};return r&&"object"==typeof r&&"value"in r&&!1!==n?.refreshOnParamsChange&&e.watch(r,()=>{c.value&&d()},{deep:!0}),e.onMounted(()=>{!1!==n?.immediate&&a.value.hostApi&&d(),n?.refreshInterval&&n.refreshInterval>0&&(u=setInterval(d,n.refreshInterval))}),e.onUnmounted(()=>{u&&clearInterval(u)}),{data:i,loading:s,error:l,refetch:d,isFetched:c}},exports.useDebouncedAction=function(t,r=300){const{execute:n,loading:a,result:o,error:s}=i(t);let l=null;const c=()=>{l&&(clearTimeout(l),l=null)};return e.onUnmounted(()=>{c()}),{execute:e=>{l&&clearTimeout(l),l=setTimeout(()=>{n(e).catch(()=>{})},r)},loading:a,result:o,error:s,cancel:c}},exports.useGlobalConfig=function(){return e.inject("djvlc-config",{})},exports.useHostApi=function(){const e=o().value.hostApi;if(!e)throw new Error("HostAPI not available. Make sure runtime is initialized.");return e},exports.useLifecycle=function(t){const r=o(),n=e.ref(!1),a=e.ref(!1);return e.watch(()=>r.value.state.phase,async(e,o)=>{if(t?.onPhaseChange?.(e),!n.value&&"idle"!==e){n.value=!0;try{await(t?.onMounted?.())}catch(e){t?.onError?.(e)}}if(!a.value&&"ready"===e){a.value=!0;try{await(t?.onReady?.())}catch(e){t?.onError?.(e)}}"error"===e&&"error"!==o&&t?.onError?.(r.value.state.error)},{immediate:!0}),{phase:e.computed(()=>r.value.state.phase),hasMounted:e.readonly(n),hasReady:e.readonly(a)}},exports.usePageInfo=function(){const t=o();return{pageUid:e.computed(()=>t.value.state.page?.pageUid),pageVersionId:e.computed(()=>t.value.state.page?.pageVersionId),schemaVersion:e.computed(()=>t.value.state.page?.pageJson?.schemaVersion),title:e.computed(()=>{const e=t.value.state.page;return e?.title}),config:e.computed(()=>{const e=t.value.state.page;return e?.config}),isLoaded:e.computed(()=>null!==t.value.state.page)}},exports.useQuery=function(t,r){const n=o(),a=e.ref(!1),i=e.ref(null),s=e.ref(null);let l=null;const c=e.computed(()=>n.value.state.queries[t]),u=async()=>{a.value=!0,i.value=null;try{await(n.value.runtime?.refreshData(t)),s.value=Date.now()}catch(e){i.value=e}finally{a.value=!1}};return e.onMounted(()=>{r?.refreshOnMount&&n.value.runtime&&u(),r?.refreshInterval&&r.refreshInterval>0&&(l=setInterval(u,r.refreshInterval)),r?.refreshOnFocus&&window.addEventListener("focus",u)}),e.onUnmounted(()=>{l&&clearInterval(l),r?.refreshOnFocus&&window.removeEventListener("focus",u)}),{data:c,loading:a,error:i,refetch:u,lastUpdated:s}},exports.useRuntimeEvent=function(t,r){const n=o(),a=n.value.runtime?.on(t,e=>{r(e.data)});e.onUnmounted(()=>{a?.()})},exports.useRuntimeState=function(t){const r=o();return e.computed(()=>r.value.state.variables[t])},exports.useRuntimeStateWritable=function(t,r){const n=o();return[e.computed(()=>n.value.state.variables[t]??r),e=>{n.value.runtime?.setVariable(t,e)}]},exports.useWhen=function(t,r,n){const a=e.ref(!1),{once:o=!0,immediate:i=!0}=n||{},s=e.watch(t,async e=>{!e||o&&a.value||(a.value=!0,await r(),o&&s())},{immediate:i});return e.onUnmounted(()=>{s()}),{executed:e.readonly(a),stop:s}};