@djvlc/runtime-host-vue 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/dist/index.js ADDED
@@ -0,0 +1,458 @@
1
+ // src/vue-runtime.ts
2
+ import { ref, shallowRef, readonly } from "vue";
3
+ import {
4
+ createRuntime
5
+ } from "@djvlc/runtime-core";
6
+ function createVueRuntime(options) {
7
+ const runtime = shallowRef(null);
8
+ const loading = ref(true);
9
+ const phase = ref("idle");
10
+ const page = shallowRef(null);
11
+ const error = shallowRef(null);
12
+ const hostApi = shallowRef(null);
13
+ const init = async () => {
14
+ const container = options.containerRef?.value;
15
+ if (!container) {
16
+ throw new Error("Container element not found");
17
+ }
18
+ const runtimeInstance = createRuntime({
19
+ ...options,
20
+ container,
21
+ onError: (err) => {
22
+ error.value = err;
23
+ options.onError?.(err);
24
+ },
25
+ onEvent: (event) => {
26
+ options.onEvent?.(event);
27
+ }
28
+ });
29
+ runtime.value = runtimeInstance;
30
+ runtimeInstance.onStateChange((state) => {
31
+ phase.value = state.phase;
32
+ loading.value = state.phase !== "ready" && state.phase !== "error";
33
+ if (state.page) {
34
+ page.value = state.page;
35
+ }
36
+ if (state.error) {
37
+ error.value = state.error;
38
+ }
39
+ });
40
+ await runtimeInstance.init();
41
+ hostApi.value = runtimeInstance.getHostApi();
42
+ };
43
+ const load = async () => {
44
+ if (!runtime.value) {
45
+ throw new Error("Runtime not initialized");
46
+ }
47
+ const result = await runtime.value.load();
48
+ page.value = result;
49
+ hostApi.value = runtime.value.getHostApi();
50
+ return result;
51
+ };
52
+ const render = async () => {
53
+ if (!runtime.value) {
54
+ throw new Error("Runtime not initialized");
55
+ }
56
+ await runtime.value.render();
57
+ loading.value = false;
58
+ };
59
+ const destroy = () => {
60
+ runtime.value?.destroy();
61
+ runtime.value = null;
62
+ hostApi.value = null;
63
+ page.value = null;
64
+ error.value = null;
65
+ phase.value = "idle";
66
+ loading.value = true;
67
+ };
68
+ const setVariable = (key, value) => {
69
+ runtime.value?.setVariable(key, value);
70
+ };
71
+ const refreshData = async (queryId) => {
72
+ await runtime.value?.refreshData(queryId);
73
+ };
74
+ return {
75
+ runtime,
76
+ loading: readonly(loading),
77
+ phase: readonly(phase),
78
+ page,
79
+ error,
80
+ hostApi,
81
+ init,
82
+ load,
83
+ render,
84
+ destroy,
85
+ setVariable,
86
+ refreshData
87
+ };
88
+ }
89
+
90
+ // src/components/DJVRenderer.ts
91
+ import {
92
+ defineComponent,
93
+ ref as ref3,
94
+ shallowRef as shallowRef2,
95
+ onMounted,
96
+ onUnmounted as onUnmounted2,
97
+ watch,
98
+ h
99
+ } from "vue";
100
+
101
+ // src/composables/useRuntime.ts
102
+ import { inject, provide, ref as ref2, computed, onUnmounted } from "vue";
103
+ var RuntimeContextKey = /* @__PURE__ */ Symbol("DJVRuntime");
104
+ function provideRuntime(value) {
105
+ provide(RuntimeContextKey, value);
106
+ }
107
+ function injectRuntime() {
108
+ const context = inject(RuntimeContextKey);
109
+ if (!context) {
110
+ throw new Error("useDJVRuntime must be used within a DJVProvider");
111
+ }
112
+ return context;
113
+ }
114
+ function useDJVRuntime() {
115
+ const context = injectRuntime();
116
+ return {
117
+ runtime: computed(() => context.value.runtime),
118
+ state: computed(() => context.value.state),
119
+ loading: computed(() => {
120
+ const phase = context.value.state.phase;
121
+ return phase !== "ready" && phase !== "error";
122
+ }),
123
+ phase: computed(() => context.value.state.phase),
124
+ error: computed(() => context.value.state.error),
125
+ page: computed(() => context.value.state.page)
126
+ };
127
+ }
128
+ function useHostApi() {
129
+ const context = injectRuntime();
130
+ const hostApi = context.value.hostApi;
131
+ if (!hostApi) {
132
+ throw new Error("HostAPI not available. Make sure runtime is initialized.");
133
+ }
134
+ return hostApi;
135
+ }
136
+ function useRuntimeState(key) {
137
+ const context = injectRuntime();
138
+ return computed(() => {
139
+ return context.value.state.variables[key];
140
+ });
141
+ }
142
+ function useRuntimeStateWritable(key, defaultValue) {
143
+ const context = injectRuntime();
144
+ const value = computed(() => {
145
+ return context.value.state.variables[key] ?? defaultValue;
146
+ });
147
+ const setValue = (newValue) => {
148
+ context.value.runtime?.setVariable(key, newValue);
149
+ };
150
+ return [value, setValue];
151
+ }
152
+ function useQuery(queryId) {
153
+ const context = injectRuntime();
154
+ const loading = ref2(false);
155
+ const error = ref2(null);
156
+ const data = computed(() => {
157
+ return context.value.state.queries[queryId];
158
+ });
159
+ const refetch = async () => {
160
+ loading.value = true;
161
+ error.value = null;
162
+ try {
163
+ await context.value.runtime?.refreshData(queryId);
164
+ } catch (e) {
165
+ error.value = e;
166
+ } finally {
167
+ loading.value = false;
168
+ }
169
+ };
170
+ return {
171
+ data,
172
+ loading,
173
+ error,
174
+ refetch
175
+ };
176
+ }
177
+ function useAction(actionType) {
178
+ const context = injectRuntime();
179
+ const loading = ref2(false);
180
+ const result = ref2();
181
+ const error = ref2(null);
182
+ const execute = async (params) => {
183
+ const hostApi = context.value.hostApi;
184
+ if (!hostApi) {
185
+ throw new Error("HostAPI not available");
186
+ }
187
+ loading.value = true;
188
+ error.value = null;
189
+ try {
190
+ const response = await hostApi.executeAction(actionType, params);
191
+ if (response.success) {
192
+ result.value = response.data;
193
+ return response.data;
194
+ } else {
195
+ throw new Error(response.message || "Action failed");
196
+ }
197
+ } catch (e) {
198
+ error.value = e;
199
+ throw e;
200
+ } finally {
201
+ loading.value = false;
202
+ }
203
+ };
204
+ return {
205
+ execute,
206
+ loading,
207
+ result,
208
+ error
209
+ };
210
+ }
211
+ function useData(queryId, params, options) {
212
+ const context = injectRuntime();
213
+ const data = ref2();
214
+ const loading = ref2(false);
215
+ const error = ref2(null);
216
+ const refetch = async (newParams) => {
217
+ const hostApi = context.value.hostApi;
218
+ if (!hostApi) {
219
+ throw new Error("HostAPI not available");
220
+ }
221
+ loading.value = true;
222
+ error.value = null;
223
+ try {
224
+ const response = await hostApi.requestData(
225
+ queryId,
226
+ newParams || params
227
+ );
228
+ if (response.success) {
229
+ data.value = response.data;
230
+ } else {
231
+ throw new Error(response.message || "Query failed");
232
+ }
233
+ } catch (e) {
234
+ error.value = e;
235
+ } finally {
236
+ loading.value = false;
237
+ }
238
+ };
239
+ if (options?.immediate !== false) {
240
+ refetch();
241
+ }
242
+ return {
243
+ data,
244
+ loading,
245
+ error,
246
+ refetch
247
+ };
248
+ }
249
+ function useRuntimeEvent(eventType, handler) {
250
+ const context = injectRuntime();
251
+ const unsubscribe = context.value.runtime?.on(eventType, (event) => {
252
+ handler(event.data);
253
+ });
254
+ onUnmounted(() => {
255
+ unsubscribe?.();
256
+ });
257
+ }
258
+
259
+ // src/components/DJVRenderer.ts
260
+ var DJVRenderer = defineComponent({
261
+ name: "DJVRenderer",
262
+ props: {
263
+ pageUid: {
264
+ type: String,
265
+ required: true
266
+ },
267
+ apiBaseUrl: {
268
+ type: String,
269
+ required: true
270
+ },
271
+ cdnBaseUrl: {
272
+ type: String,
273
+ required: true
274
+ },
275
+ channel: {
276
+ type: String,
277
+ default: "prod"
278
+ },
279
+ userId: String,
280
+ deviceId: String,
281
+ authToken: String,
282
+ previewToken: String,
283
+ debug: {
284
+ type: Boolean,
285
+ default: false
286
+ },
287
+ enableSRI: {
288
+ type: Boolean,
289
+ default: true
290
+ }
291
+ },
292
+ emits: ["load", "error", "ready"],
293
+ setup(props, { emit, slots }) {
294
+ const containerRef = ref3(null);
295
+ const contextValue = shallowRef2({
296
+ runtime: null,
297
+ state: {
298
+ phase: "idle",
299
+ page: null,
300
+ variables: {},
301
+ queries: {},
302
+ components: /* @__PURE__ */ new Map(),
303
+ error: null,
304
+ destroyed: false
305
+ },
306
+ hostApi: null
307
+ });
308
+ provideRuntime(contextValue);
309
+ let vueRuntime = null;
310
+ const initAndLoad = async () => {
311
+ if (!containerRef.value) return;
312
+ const options = {
313
+ pageUid: props.pageUid,
314
+ apiBaseUrl: props.apiBaseUrl,
315
+ cdnBaseUrl: props.cdnBaseUrl,
316
+ channel: props.channel,
317
+ userId: props.userId,
318
+ deviceId: props.deviceId,
319
+ authToken: props.authToken,
320
+ previewToken: props.previewToken,
321
+ debug: props.debug,
322
+ enableSRI: props.enableSRI,
323
+ containerRef,
324
+ onError: (error) => {
325
+ emit("error", error);
326
+ }
327
+ };
328
+ vueRuntime = createVueRuntime(options);
329
+ try {
330
+ await vueRuntime.init();
331
+ contextValue.value = {
332
+ runtime: vueRuntime.runtime.value,
333
+ state: vueRuntime.runtime.value?.getState() || contextValue.value.state,
334
+ hostApi: vueRuntime.hostApi.value
335
+ };
336
+ const page = await vueRuntime.load();
337
+ emit("load", page);
338
+ contextValue.value = {
339
+ ...contextValue.value,
340
+ state: vueRuntime.runtime.value?.getState() || contextValue.value.state,
341
+ hostApi: vueRuntime.hostApi.value
342
+ };
343
+ await vueRuntime.render();
344
+ emit("ready");
345
+ vueRuntime.runtime.value?.onStateChange((state) => {
346
+ contextValue.value = {
347
+ ...contextValue.value,
348
+ state
349
+ };
350
+ });
351
+ } catch (error) {
352
+ emit("error", error);
353
+ }
354
+ };
355
+ onMounted(() => {
356
+ initAndLoad();
357
+ });
358
+ onUnmounted2(() => {
359
+ vueRuntime?.destroy();
360
+ });
361
+ watch(
362
+ () => props.pageUid,
363
+ () => {
364
+ vueRuntime?.destroy();
365
+ initAndLoad();
366
+ }
367
+ );
368
+ return () => {
369
+ return h(
370
+ "div",
371
+ {
372
+ ref: containerRef,
373
+ class: "djvlc-renderer"
374
+ },
375
+ [
376
+ // 加载中插槽
377
+ vueRuntime?.loading.value && slots.loading?.(),
378
+ // 错误插槽
379
+ vueRuntime?.error.value && slots.error?.({ error: vueRuntime.error.value }),
380
+ // 默认插槽(额外内容)
381
+ slots.default?.()
382
+ ]
383
+ );
384
+ };
385
+ }
386
+ });
387
+
388
+ // src/components/DJVProvider.ts
389
+ import { defineComponent as defineComponent2, shallowRef as shallowRef3, h as h2 } from "vue";
390
+ var DJVProvider = defineComponent2({
391
+ name: "DJVProvider",
392
+ props: {
393
+ runtime: {
394
+ type: Object,
395
+ default: null
396
+ },
397
+ hostApi: {
398
+ type: Object,
399
+ default: null
400
+ }
401
+ },
402
+ setup(props, { slots }) {
403
+ const contextValue = shallowRef3({
404
+ runtime: props.runtime,
405
+ state: props.runtime?.getState() || {
406
+ phase: "idle",
407
+ page: null,
408
+ variables: {},
409
+ queries: {},
410
+ components: /* @__PURE__ */ new Map(),
411
+ error: null,
412
+ destroyed: false
413
+ },
414
+ hostApi: props.hostApi
415
+ });
416
+ provideRuntime(contextValue);
417
+ if (props.runtime) {
418
+ props.runtime.onStateChange((state) => {
419
+ contextValue.value = {
420
+ ...contextValue.value,
421
+ state
422
+ };
423
+ });
424
+ }
425
+ return () => h2("div", { class: "djvlc-provider" }, slots.default?.());
426
+ }
427
+ });
428
+
429
+ // src/plugin.ts
430
+ var DJVPlugin = {
431
+ install(app, options = {}) {
432
+ if (options.registerComponents !== false) {
433
+ app.component("DJVRenderer", DJVRenderer);
434
+ app.component("DJVProvider", DJVProvider);
435
+ }
436
+ app.provide("djvlc-config", {
437
+ apiBaseUrl: options.defaultApiBaseUrl,
438
+ cdnBaseUrl: options.defaultCdnBaseUrl
439
+ });
440
+ }
441
+ };
442
+ export {
443
+ DJVPlugin,
444
+ DJVProvider,
445
+ DJVRenderer,
446
+ RuntimeContextKey,
447
+ createVueRuntime,
448
+ injectRuntime,
449
+ provideRuntime,
450
+ useAction,
451
+ useDJVRuntime,
452
+ useData,
453
+ useHostApi,
454
+ useQuery,
455
+ useRuntimeEvent,
456
+ useRuntimeState,
457
+ useRuntimeStateWritable
458
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@djvlc/runtime-host-vue",
3
+ "version": "1.0.0",
4
+ "description": "DJV 低代码平台 Vue3 宿主适配器",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --external vue",
21
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch --external vue",
22
+ "test": "vitest run --passWithNoTests",
23
+ "test:watch": "vitest",
24
+ "lint": "eslint src --ext .ts,.vue",
25
+ "typecheck": "vue-tsc --noEmit",
26
+ "clean": "rimraf dist"
27
+ },
28
+ "dependencies": {
29
+ "@djvlc/runtime-core": "1.0.0",
30
+ "@djvlc/contracts-types": "^1.4.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.10.0",
34
+ "eslint": "^8.55.0",
35
+ "rimraf": "^5.0.5",
36
+ "tsup": "^8.0.0",
37
+ "typescript": "^5.3.0",
38
+ "vitest": "^1.0.0",
39
+ "vue-tsc": "^2.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "vue": "^3.3.0"
43
+ },
44
+ "keywords": [
45
+ "lowcode",
46
+ "runtime",
47
+ "vue",
48
+ "vue3",
49
+ "djv"
50
+ ],
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "license": "MIT"
55
+ }