phecda-vue 4.0.1 → 4.0.3

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.
@@ -0,0 +1,41 @@
1
+ import { WebPhecda, Construct, Events } from 'phecda-web';
2
+ export * from 'phecda-web';
3
+ import { App, Ref, UnwrapNestedRefs, WatchOptions } from 'vue';
4
+
5
+ declare const phecdaSymbol: unique symbol;
6
+ declare class VuePhecda extends WebPhecda {
7
+ vueApp: App;
8
+ install(app: App): void;
9
+ }
10
+ declare function createPhecda(): VuePhecda;
11
+
12
+ declare const RawSymbol: unique symbol;
13
+ type Raw<T> = T & {
14
+ [RawSymbol]: true;
15
+ };
16
+ type ReplaceInstanceValues<I> = {
17
+ [P in keyof I]: I[P] extends (...args: any[]) => any ? I[P] : I[P] extends Raw<infer O> ? O : Ref<I[P]>;
18
+ };
19
+ type SchemaToObj<S> = {
20
+ [P in keyof S]: S[P] extends object ? SchemaToObj<S[P]> : (S[P] extends string ? any : S[P]);
21
+ };
22
+
23
+ declare function useRaw<T extends Construct>(model: T): InstanceType<T>;
24
+ declare function usePhecda(): VuePhecda;
25
+ declare function getPhecda(phecda?: VuePhecda): VuePhecda;
26
+ declare function useEvent<Key extends keyof Events>(eventName: Key, cb: (event: Events[Key]) => void): {
27
+ emit: (arg: Events[Key]) => void;
28
+ cancel: () => void;
29
+ };
30
+ declare function useR<T extends Construct>(model: T): UnwrapNestedRefs<InstanceType<T>>;
31
+ declare function getR<T extends Construct>(model: T, phecda?: VuePhecda): UnwrapNestedRefs<InstanceType<T>>;
32
+ declare function useV<T extends Construct>(model: T): ReplaceInstanceValues<InstanceType<T>>;
33
+ declare function getV<T extends Construct>(model: T, phecda?: VuePhecda): ReplaceInstanceValues<InstanceType<T>>;
34
+
35
+ declare function Shallow(isShallow?: boolean): (model: any) => void;
36
+ declare function WatchEffect(option?: WatchOptions): (proto: any, key: string) => void;
37
+
38
+ declare function markRaw<T extends object>(value: T): Raw<T>;
39
+ declare function createSharedReactive<F extends (...args: any) => any>(composable: F): () => ReturnType<F>;
40
+
41
+ export { type Raw, RawSymbol, type ReplaceInstanceValues, type SchemaToObj, Shallow, VuePhecda, WatchEffect, createPhecda, createSharedReactive, getPhecda, getR, getV, markRaw, phecdaSymbol, useEvent, usePhecda, useR, useRaw, useV };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { App, Ref, UnwrapNestedRefs, WatchOptions } from 'vue';
4
4
 
5
5
  declare const phecdaSymbol: unique symbol;
6
6
  declare class VuePhecda extends WebPhecda {
7
+ vueApp: App;
7
8
  install(app: App): void;
8
9
  }
9
10
  declare function createPhecda(): VuePhecda;
@@ -20,13 +21,16 @@ type SchemaToObj<S> = {
20
21
  };
21
22
 
22
23
  declare function useRaw<T extends Construct>(model: T): InstanceType<T>;
23
- declare function usePhecda(phecda?: VuePhecda): VuePhecda;
24
+ declare function usePhecda(): VuePhecda;
25
+ declare function getPhecda(phecda?: VuePhecda): VuePhecda;
24
26
  declare function useEvent<Key extends keyof Events>(eventName: Key, cb: (event: Events[Key]) => void): {
25
27
  emit: (arg: Events[Key]) => void;
26
28
  cancel: () => void;
27
29
  };
28
- declare function useR<T extends Construct>(model: T, phecda?: VuePhecda): UnwrapNestedRefs<InstanceType<T>>;
29
- declare function useV<T extends Construct>(model: T, phecda?: VuePhecda): ReplaceInstanceValues<InstanceType<T>>;
30
+ declare function useR<T extends Construct>(model: T): UnwrapNestedRefs<InstanceType<T>>;
31
+ declare function getR<T extends Construct>(model: T, phecda?: VuePhecda): UnwrapNestedRefs<InstanceType<T>>;
32
+ declare function useV<T extends Construct>(model: T): ReplaceInstanceValues<InstanceType<T>>;
33
+ declare function getV<T extends Construct>(model: T, phecda?: VuePhecda): ReplaceInstanceValues<InstanceType<T>>;
30
34
 
31
35
  declare function Shallow(isShallow?: boolean): (model: any) => void;
32
36
  declare function WatchEffect(option?: WatchOptions): (proto: any, key: string) => void;
@@ -34,4 +38,4 @@ declare function WatchEffect(option?: WatchOptions): (proto: any, key: string) =
34
38
  declare function markRaw<T extends object>(value: T): Raw<T>;
35
39
  declare function createSharedReactive<F extends (...args: any) => any>(composable: F): () => ReturnType<F>;
36
40
 
37
- export { Raw, RawSymbol, ReplaceInstanceValues, SchemaToObj, Shallow, VuePhecda, WatchEffect, createPhecda, createSharedReactive, markRaw, phecdaSymbol, useEvent, usePhecda, useR, useRaw, useV };
41
+ export { type Raw, RawSymbol, type ReplaceInstanceValues, type SchemaToObj, Shallow, VuePhecda, WatchEffect, createPhecda, createSharedReactive, getPhecda, getR, getV, markRaw, phecdaSymbol, useEvent, usePhecda, useR, useRaw, useV };
package/dist/index.js CHANGED
@@ -27,6 +27,9 @@ __export(src_exports, {
27
27
  WatchEffect: () => WatchEffect,
28
28
  createPhecda: () => createPhecda,
29
29
  createSharedReactive: () => createSharedReactive,
30
+ getPhecda: () => getPhecda,
31
+ getR: () => getR,
32
+ getV: () => getV,
30
33
  markRaw: () => markRaw,
31
34
  phecdaSymbol: () => phecdaSymbol,
32
35
  useEvent: () => useEvent,
@@ -41,19 +44,335 @@ __reExport(src_exports, require("phecda-web"), module.exports);
41
44
  // src/core.ts
42
45
  var import_vue = require("vue");
43
46
  var import_phecda_web = require("phecda-web");
47
+ var import_devtools_api = require("@vue/devtools-api");
48
+
49
+ // src/devtools.ts
50
+ var componentStateTypes = [];
51
+ var MUTATIONS_LAYER_ID = "phecda-vue:mutations";
52
+ var INSPECTOR_ID = "phecda-vue";
53
+ function toastMessage(message, type) {
54
+ const piniaMessage = `[phecda-vue]: ${message}`;
55
+ if (type === "error") console.error(piniaMessage);
56
+ else if (type === "warn") console.warn(piniaMessage);
57
+ else console.log(piniaMessage);
58
+ }
59
+ __name(toastMessage, "toastMessage");
60
+ var USE_DEVTOOLS = process.env.NODE_ENV === "development" && typeof window;
61
+
62
+ // src/core.ts
44
63
  var phecdaSymbol = Symbol(process.env.NODE_ENV === "development" ? "phecda-vue" : void 0);
45
64
  var VuePhecda = class extends import_phecda_web.WebPhecda {
65
+ static {
66
+ __name(this, "VuePhecda");
67
+ }
68
+ vueApp;
46
69
  install(app) {
47
70
  app.provide(phecdaSymbol, this);
71
+ this.vueApp = app;
72
+ if (USE_DEVTOOLS) {
73
+ (0, import_devtools_api.setupDevtoolsPlugin)({
74
+ settings: {
75
+ sendTrigger: {
76
+ label: "send DebuggerEvent in onTrigger to timeline",
77
+ type: "boolean",
78
+ defaultValue: false
79
+ },
80
+ triggerEventSync: {
81
+ label: "send trigger event to timeline Synchronously",
82
+ type: "boolean",
83
+ defaultValue: false
84
+ },
85
+ sendUpdate: {
86
+ label: "Record view update caused by model to timeline",
87
+ type: "boolean",
88
+ defaultValue: false
89
+ }
90
+ },
91
+ id: "dev.esm.phecda",
92
+ label: "Phecda Vue",
93
+ packageName: "phecda",
94
+ // @todo
95
+ // logo: 'https://phecda.vuejs.org/logo.svg',
96
+ // homepage: 'https://phecda.vuejs.org',
97
+ componentStateTypes,
98
+ app
99
+ }, (api) => {
100
+ const now = typeof api.now === "function" ? api.now.bind(api) : Date.now;
101
+ api.addTimelineLayer({
102
+ id: MUTATIONS_LAYER_ID,
103
+ label: "Phecda Vue",
104
+ color: 9089261
105
+ });
106
+ const watchModule = /* @__PURE__ */ __name((tag) => {
107
+ (0, import_vue.watch)(this.get(tag), (data) => {
108
+ const { sendUpdate } = api.getSettings();
109
+ if (sendUpdate) {
110
+ api.addTimelineEvent({
111
+ layerId: MUTATIONS_LAYER_ID,
112
+ event: {
113
+ time: now(),
114
+ title: "Update",
115
+ subtitle: String(tag),
116
+ data: {
117
+ ...data
118
+ }
119
+ }
120
+ });
121
+ }
122
+ api.notifyComponentUpdate();
123
+ api.sendInspectorState(INSPECTOR_ID);
124
+ }, {
125
+ deep: true,
126
+ onTrigger(event) {
127
+ const { triggerEventSync, sendTrigger } = api.getSettings();
128
+ if (sendTrigger && !triggerEventSync) {
129
+ api.addTimelineEvent({
130
+ layerId: MUTATIONS_LAYER_ID,
131
+ event: {
132
+ time: now(),
133
+ title: "Trigger",
134
+ subtitle: String(tag),
135
+ data: event
136
+ }
137
+ });
138
+ }
139
+ }
140
+ });
141
+ (0, import_vue.watch)(this.get(tag), () => {
142
+ }, {
143
+ deep: true,
144
+ flush: "sync",
145
+ onTrigger(event) {
146
+ const { triggerEventSync, sendTrigger } = api.getSettings();
147
+ if (sendTrigger && triggerEventSync) {
148
+ api.addTimelineEvent({
149
+ layerId: MUTATIONS_LAYER_ID,
150
+ event: {
151
+ time: now(),
152
+ title: "Trigger",
153
+ subtitle: String(tag),
154
+ data: event
155
+ }
156
+ });
157
+ }
158
+ }
159
+ });
160
+ }, "watchModule");
161
+ for (const tag in this.state) watchModule(tag);
162
+ this.on("Instantiate", ({ tag }) => {
163
+ api.sendInspectorTree(INSPECTOR_ID);
164
+ watchModule(tag);
165
+ });
166
+ this.on("*", (type, event) => {
167
+ api.addTimelineEvent({
168
+ layerId: MUTATIONS_LAYER_ID,
169
+ event: {
170
+ time: now(),
171
+ title: type,
172
+ subtitle: event.tag,
173
+ data: event
174
+ }
175
+ });
176
+ });
177
+ api.addInspector({
178
+ id: INSPECTOR_ID,
179
+ label: "Phecda Vue",
180
+ icon: "storage",
181
+ treeFilterPlaceholder: "Search",
182
+ actions: [
183
+ {
184
+ icon: "content_copy",
185
+ action: /* @__PURE__ */ __name(async () => {
186
+ toastMessage("Global state copied to clipboard.");
187
+ await navigator.clipboard.writeText(this.serialize());
188
+ }, "action"),
189
+ tooltip: "Serialize and copy the state"
190
+ },
191
+ {
192
+ icon: "content_paste",
193
+ action: /* @__PURE__ */ __name(async () => {
194
+ toastMessage("Global state pasted from clipboard.");
195
+ await this.load(await navigator.clipboard.readText());
196
+ api.sendInspectorTree(INSPECTOR_ID);
197
+ api.sendInspectorState(INSPECTOR_ID);
198
+ }, "action"),
199
+ tooltip: "Replace the state with the content of your clipboard"
200
+ }
201
+ ],
202
+ nodeActions: [
203
+ {
204
+ icon: "restore",
205
+ tooltip: "Reset the state ",
206
+ action: /* @__PURE__ */ __name((nodeId) => {
207
+ this.reset(this.getModel(nodeId));
208
+ }, "action")
209
+ }
210
+ ]
211
+ });
212
+ api.on.inspectComponent((payload) => {
213
+ const proxy = payload.componentInstance && payload.componentInstance.proxy;
214
+ if (proxy && proxy._phecda_vue) {
215
+ for (const tag in proxy._phecda_vue) {
216
+ payload.instanceData.state.push({
217
+ type: "phecda-vue",
218
+ key: tag,
219
+ editable: true,
220
+ value: proxy._phecda_vue[tag]
221
+ });
222
+ }
223
+ }
224
+ });
225
+ api.on.getInspectorTree((payload) => {
226
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
227
+ payload.rootNodes = Object.keys(this.state).map((tag) => {
228
+ return {
229
+ id: tag,
230
+ label: tag
231
+ };
232
+ });
233
+ }
234
+ });
235
+ api.on.getInspectorState((payload) => {
236
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
237
+ if (this.has(payload.nodeId)) {
238
+ const instance = this.get(payload.nodeId);
239
+ payload.state = {
240
+ state: [],
241
+ methods: [],
242
+ getters: [],
243
+ internals: [],
244
+ memory: Object.entries(this.memory[payload.nodeId] || {}).map(([key, value]) => {
245
+ return {
246
+ editable: false,
247
+ key,
248
+ value
249
+ };
250
+ })
251
+ };
252
+ Object.entries(instance).forEach(([key, value]) => {
253
+ if (this.modelMap.has(value)) {
254
+ const tag = String((0, import_phecda_web.getTag)((0, import_vue.toRaw)(value)));
255
+ payload.state.state.unshift({
256
+ editable: false,
257
+ key,
258
+ value: `[PV] ${tag}`,
259
+ raw: `Phecda Vue Module [${tag}]`
260
+ });
261
+ return;
262
+ }
263
+ if (!key.startsWith("__")) payload.state.state.push({
264
+ editable: true,
265
+ key,
266
+ value
267
+ });
268
+ else payload.state.internals.push({
269
+ editable: false,
270
+ key,
271
+ value
272
+ });
273
+ });
274
+ getAllGetters(instance).forEach((item) => {
275
+ payload.state.getters.push({
276
+ editable: false,
277
+ key: item,
278
+ value: instance[item]
279
+ });
280
+ });
281
+ getAllMethods(instance).forEach((item) => {
282
+ if (typeof instance[item] === "function") payload.state[item.startsWith("__") ? "internals" : "methods"].push({
283
+ editable: false,
284
+ key: item,
285
+ value: Object.getPrototypeOf(instance)[item]
286
+ });
287
+ });
288
+ }
289
+ }
290
+ });
291
+ api.on.editInspectorState((payload) => {
292
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
293
+ const state = this.get(payload.nodeId);
294
+ const { path } = payload;
295
+ payload.set(state, path, payload.state.value);
296
+ }
297
+ });
298
+ api.on.editComponentState((payload) => {
299
+ const { path, type } = payload;
300
+ if (type === "phecda-vue") payload.set(this.get(path.shift()), path, payload.state.value);
301
+ });
302
+ });
303
+ }
48
304
  }
49
305
  };
50
- __name(VuePhecda, "VuePhecda");
51
306
  function createPhecda() {
52
- return new VuePhecda((instance) => {
53
- return (0, import_phecda_web.bindMethod)((0, import_phecda_web.get)(instance, "shallow") ? (0, import_vue.shallowReactive)(instance) : (0, import_vue.reactive)(instance));
307
+ const phecda = new VuePhecda("vue", (instance) => {
308
+ return (0, import_phecda_web.bindMethod)((0, import_phecda_web.get)(instance, "shallow") ? (0, import_vue.shallowReactive)(instance) : (0, import_vue.reactive)(instance), USE_DEVTOOLS ? (instance2, key) => {
309
+ const cb = instance2[key].bind(instance2);
310
+ if (findPrototypeWithMethod(instance2, key).constructor.name === "Object") return cb;
311
+ const tag = (0, import_phecda_web.getTag)(instance2);
312
+ return (...args) => {
313
+ const name = `${tag}.${key}`;
314
+ phecda.emit(`Invoke ${name}`, {
315
+ args,
316
+ tag,
317
+ key
318
+ });
319
+ const ret = cb(...args);
320
+ if (ret instanceof Promise) ret.then(() => phecda.emit(`End ${name}(Async)`, {
321
+ args,
322
+ tag,
323
+ key
324
+ }));
325
+ else phecda.emit(`End ${name}`, {
326
+ args,
327
+ tag,
328
+ key
329
+ });
330
+ return ret;
331
+ };
332
+ } : void 0);
54
333
  });
334
+ return phecda;
55
335
  }
56
336
  __name(createPhecda, "createPhecda");
337
+ function findPrototypeWithMethod(instance, method) {
338
+ let proto = Object.getPrototypeOf(instance);
339
+ while (proto) {
340
+ if (proto.hasOwnProperty(method)) return proto;
341
+ proto = Object.getPrototypeOf(proto);
342
+ }
343
+ return null;
344
+ }
345
+ __name(findPrototypeWithMethod, "findPrototypeWithMethod");
346
+ function getAllMethods(obj) {
347
+ const methods = /* @__PURE__ */ new Set();
348
+ obj = Object.getPrototypeOf(obj);
349
+ while (obj.constructor.name !== "Object") {
350
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
351
+ const propDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
352
+ if (typeof propDescriptor.value === "function" && prop !== "constructor") methods.add(prop);
353
+ });
354
+ obj = Object.getPrototypeOf(obj);
355
+ }
356
+ return [
357
+ ...methods
358
+ ];
359
+ }
360
+ __name(getAllMethods, "getAllMethods");
361
+ function getAllGetters(obj) {
362
+ const getters = /* @__PURE__ */ new Set();
363
+ obj = Object.getPrototypeOf(obj);
364
+ while (obj.constructor.name !== "Object") {
365
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
366
+ const propDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
367
+ if (typeof propDescriptor.get === "function") getters.add(prop);
368
+ });
369
+ obj = Object.getPrototypeOf(obj);
370
+ }
371
+ return [
372
+ ...getters
373
+ ];
374
+ }
375
+ __name(getAllGetters, "getAllGetters");
57
376
 
58
377
  // src/composable.ts
59
378
  var import_phecda_web2 = require("phecda-web");
@@ -95,45 +414,64 @@ function useRaw(model) {
95
414
  return (0, import_vue3.toRaw)(useR(model));
96
415
  }
97
416
  __name(useRaw, "useRaw");
98
- function usePhecda(phecda) {
99
- const activePhecda = phecda || (0, import_vue3.hasInjectionContext)() && (0, import_vue3.inject)(phecdaSymbol);
100
- if (!activePhecda)
101
- throw new Error("[phecda-vue]: must install the vue plugin (if used in setup) or manually inject the phecda instance ");
102
- if (!cacheMap.has(activePhecda))
103
- cacheMap.set(activePhecda, (0, import_phecda_web2.bindMethod)(activePhecda));
417
+ function usePhecda() {
418
+ if (!(0, import_vue3.hasInjectionContext)()) throw new Error("[phecda-vue]: use hook inside component setup function");
419
+ const activePhecda = (0, import_vue3.inject)(phecdaSymbol);
420
+ if (!activePhecda) throw new Error("[phecda-vue]: must install the vue plugin ");
421
+ if (!cacheMap.has(activePhecda)) cacheMap.set(activePhecda, (0, import_phecda_web2.bindMethod)(activePhecda));
104
422
  return cacheMap.get(activePhecda);
105
423
  }
106
424
  __name(usePhecda, "usePhecda");
425
+ function setStateToComponent(model) {
426
+ if (USE_DEVTOOLS) {
427
+ const currentInstance = (0, import_vue3.getCurrentInstance)();
428
+ if (currentInstance && currentInstance.proxy) {
429
+ const vm = currentInstance.proxy;
430
+ const cache = "_phecda_vue" in vm ? vm._phecda_vue : vm._phecda_vue = {};
431
+ const tag = (0, import_phecda_web2.getTag)(model);
432
+ cache[tag] = usePhecda().init(model);
433
+ }
434
+ }
435
+ }
436
+ __name(setStateToComponent, "setStateToComponent");
437
+ function getPhecda(phecda) {
438
+ const activePhecda = phecda || (0, import_phecda_web2.getDefaultPhecda)("vue");
439
+ if (!activePhecda) throw new Error("[phecda-vue]: manually inject the phecda instance if there is no default phecda");
440
+ if (!cacheMap.has(activePhecda)) cacheMap.set(activePhecda, (0, import_phecda_web2.bindMethod)(activePhecda));
441
+ return cacheMap.get(activePhecda);
442
+ }
443
+ __name(getPhecda, "getPhecda");
107
444
  function useEvent(eventName, cb) {
108
445
  (0, import_vue3.onBeforeUnmount)(() => {
109
446
  import_phecda_web2.emitter.off(eventName, cb);
110
447
  });
111
448
  import_phecda_web2.emitter.on(eventName, cb);
112
449
  return {
113
- emit: (arg) => import_phecda_web2.emitter.emit(eventName, arg),
114
- cancel: () => import_phecda_web2.emitter.off(eventName, cb)
450
+ emit: /* @__PURE__ */ __name((arg) => import_phecda_web2.emitter.emit(eventName, arg), "emit"),
451
+ cancel: /* @__PURE__ */ __name(() => import_phecda_web2.emitter.off(eventName, cb), "cancel")
115
452
  };
116
453
  }
117
454
  __name(useEvent, "useEvent");
118
- function useR(model, phecda) {
119
- return usePhecda(phecda).init(model);
455
+ function useR(model) {
456
+ setStateToComponent(model);
457
+ return usePhecda().init(model);
120
458
  }
121
459
  __name(useR, "useR");
122
- function useV(model, phecda) {
123
- const instance = usePhecda(phecda).init(model);
124
- if (cacheMap.has(instance))
125
- return cacheMap.get(instance);
460
+ function getR(model, phecda) {
461
+ return getPhecda(phecda).init(model);
462
+ }
463
+ __name(getR, "getR");
464
+ function useV(model) {
465
+ setStateToComponent(model);
466
+ const instance = usePhecda().init(model);
467
+ if (cacheMap.has(instance)) return cacheMap.get(instance);
126
468
  const cache = {};
127
469
  const proxy = new Proxy(instance, {
128
470
  get(target, key) {
129
- if (typeof target[key] === "function") {
130
- return target[key];
131
- }
132
- if (target[key]?.__v_skip)
133
- return target[key];
471
+ if (typeof target[key] === "function") return target[key];
472
+ if (target[key]?.__v_skip) return target[key];
134
473
  const cacheRef = cache[key];
135
- if (cacheRef && cacheRef.r)
136
- return cacheRef();
474
+ if (cacheRef && cacheRef.r) return cacheRef();
137
475
  cache[key] = createSharedReactive(() => {
138
476
  return (0, import_vue3.toRef)(target, key);
139
477
  });
@@ -147,6 +485,29 @@ function useV(model, phecda) {
147
485
  return proxy;
148
486
  }
149
487
  __name(useV, "useV");
488
+ function getV(model, phecda) {
489
+ const instance = getPhecda(phecda).init(model);
490
+ if (cacheMap.has(instance)) return cacheMap.get(instance);
491
+ const cache = {};
492
+ const proxy = new Proxy(instance, {
493
+ get(target, key) {
494
+ if (typeof target[key] === "function") return target[key];
495
+ if (target[key]?.__v_skip) return target[key];
496
+ const cacheRef = cache[key];
497
+ if (cacheRef && cacheRef.r) return cacheRef();
498
+ cache[key] = createSharedReactive(() => {
499
+ return (0, import_vue3.toRef)(target, key);
500
+ });
501
+ return cache[key]();
502
+ },
503
+ set() {
504
+ return false;
505
+ }
506
+ });
507
+ cacheMap.set(instance, proxy);
508
+ return proxy;
509
+ }
510
+ __name(getV, "getV");
150
511
 
151
512
  // src/decorator.ts
152
513
  var import_phecda_web3 = require("phecda-web");
@@ -163,8 +524,7 @@ function WatchEffect(option) {
163
524
  let stopHandler;
164
525
  (0, import_phecda_web3.setHandler)(proto, key, {
165
526
  init(instance) {
166
- if (typeof instance[key] !== "function")
167
- throw new Error("WatchEffect must decorate function");
527
+ if (typeof instance[key] !== "function") throw new Error("WatchEffect must decorate function");
168
528
  stopHandler = (0, import_vue4.watchEffect)(instance[key].bind(instance), option);
169
529
  },
170
530
  unmount() {
@@ -181,6 +541,9 @@ __name(WatchEffect, "WatchEffect");
181
541
  WatchEffect,
182
542
  createPhecda,
183
543
  createSharedReactive,
544
+ getPhecda,
545
+ getR,
546
+ getV,
184
547
  markRaw,
185
548
  phecdaSymbol,
186
549
  useEvent,
package/dist/index.mjs CHANGED
@@ -5,25 +5,341 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
5
5
  export * from "phecda-web";
6
6
 
7
7
  // src/core.ts
8
- import { reactive, shallowReactive } from "vue";
9
- import { WebPhecda, bindMethod, get } from "phecda-web";
8
+ import { reactive, shallowReactive, toRaw, watch } from "vue";
9
+ import { WebPhecda, bindMethod, get, getTag } from "phecda-web";
10
+ import { setupDevtoolsPlugin } from "@vue/devtools-api";
11
+
12
+ // src/devtools.ts
13
+ var componentStateTypes = [];
14
+ var MUTATIONS_LAYER_ID = "phecda-vue:mutations";
15
+ var INSPECTOR_ID = "phecda-vue";
16
+ function toastMessage(message, type) {
17
+ const piniaMessage = `[phecda-vue]: ${message}`;
18
+ if (type === "error") console.error(piniaMessage);
19
+ else if (type === "warn") console.warn(piniaMessage);
20
+ else console.log(piniaMessage);
21
+ }
22
+ __name(toastMessage, "toastMessage");
23
+ var USE_DEVTOOLS = process.env.NODE_ENV === "development" && typeof window;
24
+
25
+ // src/core.ts
10
26
  var phecdaSymbol = Symbol(process.env.NODE_ENV === "development" ? "phecda-vue" : void 0);
11
27
  var VuePhecda = class extends WebPhecda {
28
+ static {
29
+ __name(this, "VuePhecda");
30
+ }
31
+ vueApp;
12
32
  install(app) {
13
33
  app.provide(phecdaSymbol, this);
34
+ this.vueApp = app;
35
+ if (USE_DEVTOOLS) {
36
+ setupDevtoolsPlugin({
37
+ settings: {
38
+ sendTrigger: {
39
+ label: "send DebuggerEvent in onTrigger to timeline",
40
+ type: "boolean",
41
+ defaultValue: false
42
+ },
43
+ triggerEventSync: {
44
+ label: "send trigger event to timeline Synchronously",
45
+ type: "boolean",
46
+ defaultValue: false
47
+ },
48
+ sendUpdate: {
49
+ label: "Record view update caused by model to timeline",
50
+ type: "boolean",
51
+ defaultValue: false
52
+ }
53
+ },
54
+ id: "dev.esm.phecda",
55
+ label: "Phecda Vue",
56
+ packageName: "phecda",
57
+ // @todo
58
+ // logo: 'https://phecda.vuejs.org/logo.svg',
59
+ // homepage: 'https://phecda.vuejs.org',
60
+ componentStateTypes,
61
+ app
62
+ }, (api) => {
63
+ const now = typeof api.now === "function" ? api.now.bind(api) : Date.now;
64
+ api.addTimelineLayer({
65
+ id: MUTATIONS_LAYER_ID,
66
+ label: "Phecda Vue",
67
+ color: 9089261
68
+ });
69
+ const watchModule = /* @__PURE__ */ __name((tag) => {
70
+ watch(this.get(tag), (data) => {
71
+ const { sendUpdate } = api.getSettings();
72
+ if (sendUpdate) {
73
+ api.addTimelineEvent({
74
+ layerId: MUTATIONS_LAYER_ID,
75
+ event: {
76
+ time: now(),
77
+ title: "Update",
78
+ subtitle: String(tag),
79
+ data: {
80
+ ...data
81
+ }
82
+ }
83
+ });
84
+ }
85
+ api.notifyComponentUpdate();
86
+ api.sendInspectorState(INSPECTOR_ID);
87
+ }, {
88
+ deep: true,
89
+ onTrigger(event) {
90
+ const { triggerEventSync, sendTrigger } = api.getSettings();
91
+ if (sendTrigger && !triggerEventSync) {
92
+ api.addTimelineEvent({
93
+ layerId: MUTATIONS_LAYER_ID,
94
+ event: {
95
+ time: now(),
96
+ title: "Trigger",
97
+ subtitle: String(tag),
98
+ data: event
99
+ }
100
+ });
101
+ }
102
+ }
103
+ });
104
+ watch(this.get(tag), () => {
105
+ }, {
106
+ deep: true,
107
+ flush: "sync",
108
+ onTrigger(event) {
109
+ const { triggerEventSync, sendTrigger } = api.getSettings();
110
+ if (sendTrigger && triggerEventSync) {
111
+ api.addTimelineEvent({
112
+ layerId: MUTATIONS_LAYER_ID,
113
+ event: {
114
+ time: now(),
115
+ title: "Trigger",
116
+ subtitle: String(tag),
117
+ data: event
118
+ }
119
+ });
120
+ }
121
+ }
122
+ });
123
+ }, "watchModule");
124
+ for (const tag in this.state) watchModule(tag);
125
+ this.on("Instantiate", ({ tag }) => {
126
+ api.sendInspectorTree(INSPECTOR_ID);
127
+ watchModule(tag);
128
+ });
129
+ this.on("*", (type, event) => {
130
+ api.addTimelineEvent({
131
+ layerId: MUTATIONS_LAYER_ID,
132
+ event: {
133
+ time: now(),
134
+ title: type,
135
+ subtitle: event.tag,
136
+ data: event
137
+ }
138
+ });
139
+ });
140
+ api.addInspector({
141
+ id: INSPECTOR_ID,
142
+ label: "Phecda Vue",
143
+ icon: "storage",
144
+ treeFilterPlaceholder: "Search",
145
+ actions: [
146
+ {
147
+ icon: "content_copy",
148
+ action: /* @__PURE__ */ __name(async () => {
149
+ toastMessage("Global state copied to clipboard.");
150
+ await navigator.clipboard.writeText(this.serialize());
151
+ }, "action"),
152
+ tooltip: "Serialize and copy the state"
153
+ },
154
+ {
155
+ icon: "content_paste",
156
+ action: /* @__PURE__ */ __name(async () => {
157
+ toastMessage("Global state pasted from clipboard.");
158
+ await this.load(await navigator.clipboard.readText());
159
+ api.sendInspectorTree(INSPECTOR_ID);
160
+ api.sendInspectorState(INSPECTOR_ID);
161
+ }, "action"),
162
+ tooltip: "Replace the state with the content of your clipboard"
163
+ }
164
+ ],
165
+ nodeActions: [
166
+ {
167
+ icon: "restore",
168
+ tooltip: "Reset the state ",
169
+ action: /* @__PURE__ */ __name((nodeId) => {
170
+ this.reset(this.getModel(nodeId));
171
+ }, "action")
172
+ }
173
+ ]
174
+ });
175
+ api.on.inspectComponent((payload) => {
176
+ const proxy = payload.componentInstance && payload.componentInstance.proxy;
177
+ if (proxy && proxy._phecda_vue) {
178
+ for (const tag in proxy._phecda_vue) {
179
+ payload.instanceData.state.push({
180
+ type: "phecda-vue",
181
+ key: tag,
182
+ editable: true,
183
+ value: proxy._phecda_vue[tag]
184
+ });
185
+ }
186
+ }
187
+ });
188
+ api.on.getInspectorTree((payload) => {
189
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
190
+ payload.rootNodes = Object.keys(this.state).map((tag) => {
191
+ return {
192
+ id: tag,
193
+ label: tag
194
+ };
195
+ });
196
+ }
197
+ });
198
+ api.on.getInspectorState((payload) => {
199
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
200
+ if (this.has(payload.nodeId)) {
201
+ const instance = this.get(payload.nodeId);
202
+ payload.state = {
203
+ state: [],
204
+ methods: [],
205
+ getters: [],
206
+ internals: [],
207
+ memory: Object.entries(this.memory[payload.nodeId] || {}).map(([key, value]) => {
208
+ return {
209
+ editable: false,
210
+ key,
211
+ value
212
+ };
213
+ })
214
+ };
215
+ Object.entries(instance).forEach(([key, value]) => {
216
+ if (this.modelMap.has(value)) {
217
+ const tag = String(getTag(toRaw(value)));
218
+ payload.state.state.unshift({
219
+ editable: false,
220
+ key,
221
+ value: `[PV] ${tag}`,
222
+ raw: `Phecda Vue Module [${tag}]`
223
+ });
224
+ return;
225
+ }
226
+ if (!key.startsWith("__")) payload.state.state.push({
227
+ editable: true,
228
+ key,
229
+ value
230
+ });
231
+ else payload.state.internals.push({
232
+ editable: false,
233
+ key,
234
+ value
235
+ });
236
+ });
237
+ getAllGetters(instance).forEach((item) => {
238
+ payload.state.getters.push({
239
+ editable: false,
240
+ key: item,
241
+ value: instance[item]
242
+ });
243
+ });
244
+ getAllMethods(instance).forEach((item) => {
245
+ if (typeof instance[item] === "function") payload.state[item.startsWith("__") ? "internals" : "methods"].push({
246
+ editable: false,
247
+ key: item,
248
+ value: Object.getPrototypeOf(instance)[item]
249
+ });
250
+ });
251
+ }
252
+ }
253
+ });
254
+ api.on.editInspectorState((payload) => {
255
+ if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {
256
+ const state = this.get(payload.nodeId);
257
+ const { path } = payload;
258
+ payload.set(state, path, payload.state.value);
259
+ }
260
+ });
261
+ api.on.editComponentState((payload) => {
262
+ const { path, type } = payload;
263
+ if (type === "phecda-vue") payload.set(this.get(path.shift()), path, payload.state.value);
264
+ });
265
+ });
266
+ }
14
267
  }
15
268
  };
16
- __name(VuePhecda, "VuePhecda");
17
269
  function createPhecda() {
18
- return new VuePhecda((instance) => {
19
- return bindMethod(get(instance, "shallow") ? shallowReactive(instance) : reactive(instance));
270
+ const phecda = new VuePhecda("vue", (instance) => {
271
+ return bindMethod(get(instance, "shallow") ? shallowReactive(instance) : reactive(instance), USE_DEVTOOLS ? (instance2, key) => {
272
+ const cb = instance2[key].bind(instance2);
273
+ if (findPrototypeWithMethod(instance2, key).constructor.name === "Object") return cb;
274
+ const tag = getTag(instance2);
275
+ return (...args) => {
276
+ const name = `${tag}.${key}`;
277
+ phecda.emit(`Invoke ${name}`, {
278
+ args,
279
+ tag,
280
+ key
281
+ });
282
+ const ret = cb(...args);
283
+ if (ret instanceof Promise) ret.then(() => phecda.emit(`End ${name}(Async)`, {
284
+ args,
285
+ tag,
286
+ key
287
+ }));
288
+ else phecda.emit(`End ${name}`, {
289
+ args,
290
+ tag,
291
+ key
292
+ });
293
+ return ret;
294
+ };
295
+ } : void 0);
20
296
  });
297
+ return phecda;
21
298
  }
22
299
  __name(createPhecda, "createPhecda");
300
+ function findPrototypeWithMethod(instance, method) {
301
+ let proto = Object.getPrototypeOf(instance);
302
+ while (proto) {
303
+ if (proto.hasOwnProperty(method)) return proto;
304
+ proto = Object.getPrototypeOf(proto);
305
+ }
306
+ return null;
307
+ }
308
+ __name(findPrototypeWithMethod, "findPrototypeWithMethod");
309
+ function getAllMethods(obj) {
310
+ const methods = /* @__PURE__ */ new Set();
311
+ obj = Object.getPrototypeOf(obj);
312
+ while (obj.constructor.name !== "Object") {
313
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
314
+ const propDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
315
+ if (typeof propDescriptor.value === "function" && prop !== "constructor") methods.add(prop);
316
+ });
317
+ obj = Object.getPrototypeOf(obj);
318
+ }
319
+ return [
320
+ ...methods
321
+ ];
322
+ }
323
+ __name(getAllMethods, "getAllMethods");
324
+ function getAllGetters(obj) {
325
+ const getters = /* @__PURE__ */ new Set();
326
+ obj = Object.getPrototypeOf(obj);
327
+ while (obj.constructor.name !== "Object") {
328
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
329
+ const propDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
330
+ if (typeof propDescriptor.get === "function") getters.add(prop);
331
+ });
332
+ obj = Object.getPrototypeOf(obj);
333
+ }
334
+ return [
335
+ ...getters
336
+ ];
337
+ }
338
+ __name(getAllGetters, "getAllGetters");
23
339
 
24
340
  // src/composable.ts
25
- import { bindMethod as bindMethod2, emitter } from "phecda-web";
26
- import { hasInjectionContext, inject, onBeforeUnmount, toRaw, toRef } from "vue";
341
+ import { bindMethod as bindMethod2, emitter, getDefaultPhecda, getTag as getTag2 } from "phecda-web";
342
+ import { getCurrentInstance, hasInjectionContext, inject, onBeforeUnmount, toRaw as toRaw2, toRef } from "vue";
27
343
 
28
344
  // src/utils.ts
29
345
  import { effectScope, onScopeDispose, markRaw as raw } from "vue";
@@ -58,48 +374,67 @@ __name(createSharedReactive, "createSharedReactive");
58
374
  // src/composable.ts
59
375
  var cacheMap = /* @__PURE__ */ new WeakMap();
60
376
  function useRaw(model) {
61
- return toRaw(useR(model));
377
+ return toRaw2(useR(model));
62
378
  }
63
379
  __name(useRaw, "useRaw");
64
- function usePhecda(phecda) {
65
- const activePhecda = phecda || hasInjectionContext() && inject(phecdaSymbol);
66
- if (!activePhecda)
67
- throw new Error("[phecda-vue]: must install the vue plugin (if used in setup) or manually inject the phecda instance ");
68
- if (!cacheMap.has(activePhecda))
69
- cacheMap.set(activePhecda, bindMethod2(activePhecda));
380
+ function usePhecda() {
381
+ if (!hasInjectionContext()) throw new Error("[phecda-vue]: use hook inside component setup function");
382
+ const activePhecda = inject(phecdaSymbol);
383
+ if (!activePhecda) throw new Error("[phecda-vue]: must install the vue plugin ");
384
+ if (!cacheMap.has(activePhecda)) cacheMap.set(activePhecda, bindMethod2(activePhecda));
70
385
  return cacheMap.get(activePhecda);
71
386
  }
72
387
  __name(usePhecda, "usePhecda");
388
+ function setStateToComponent(model) {
389
+ if (USE_DEVTOOLS) {
390
+ const currentInstance = getCurrentInstance();
391
+ if (currentInstance && currentInstance.proxy) {
392
+ const vm = currentInstance.proxy;
393
+ const cache = "_phecda_vue" in vm ? vm._phecda_vue : vm._phecda_vue = {};
394
+ const tag = getTag2(model);
395
+ cache[tag] = usePhecda().init(model);
396
+ }
397
+ }
398
+ }
399
+ __name(setStateToComponent, "setStateToComponent");
400
+ function getPhecda(phecda) {
401
+ const activePhecda = phecda || getDefaultPhecda("vue");
402
+ if (!activePhecda) throw new Error("[phecda-vue]: manually inject the phecda instance if there is no default phecda");
403
+ if (!cacheMap.has(activePhecda)) cacheMap.set(activePhecda, bindMethod2(activePhecda));
404
+ return cacheMap.get(activePhecda);
405
+ }
406
+ __name(getPhecda, "getPhecda");
73
407
  function useEvent(eventName, cb) {
74
408
  onBeforeUnmount(() => {
75
409
  emitter.off(eventName, cb);
76
410
  });
77
411
  emitter.on(eventName, cb);
78
412
  return {
79
- emit: (arg) => emitter.emit(eventName, arg),
80
- cancel: () => emitter.off(eventName, cb)
413
+ emit: /* @__PURE__ */ __name((arg) => emitter.emit(eventName, arg), "emit"),
414
+ cancel: /* @__PURE__ */ __name(() => emitter.off(eventName, cb), "cancel")
81
415
  };
82
416
  }
83
417
  __name(useEvent, "useEvent");
84
- function useR(model, phecda) {
85
- return usePhecda(phecda).init(model);
418
+ function useR(model) {
419
+ setStateToComponent(model);
420
+ return usePhecda().init(model);
86
421
  }
87
422
  __name(useR, "useR");
88
- function useV(model, phecda) {
89
- const instance = usePhecda(phecda).init(model);
90
- if (cacheMap.has(instance))
91
- return cacheMap.get(instance);
423
+ function getR(model, phecda) {
424
+ return getPhecda(phecda).init(model);
425
+ }
426
+ __name(getR, "getR");
427
+ function useV(model) {
428
+ setStateToComponent(model);
429
+ const instance = usePhecda().init(model);
430
+ if (cacheMap.has(instance)) return cacheMap.get(instance);
92
431
  const cache = {};
93
432
  const proxy = new Proxy(instance, {
94
433
  get(target, key) {
95
- if (typeof target[key] === "function") {
96
- return target[key];
97
- }
98
- if (target[key]?.__v_skip)
99
- return target[key];
434
+ if (typeof target[key] === "function") return target[key];
435
+ if (target[key]?.__v_skip) return target[key];
100
436
  const cacheRef = cache[key];
101
- if (cacheRef && cacheRef.r)
102
- return cacheRef();
437
+ if (cacheRef && cacheRef.r) return cacheRef();
103
438
  cache[key] = createSharedReactive(() => {
104
439
  return toRef(target, key);
105
440
  });
@@ -113,6 +448,29 @@ function useV(model, phecda) {
113
448
  return proxy;
114
449
  }
115
450
  __name(useV, "useV");
451
+ function getV(model, phecda) {
452
+ const instance = getPhecda(phecda).init(model);
453
+ if (cacheMap.has(instance)) return cacheMap.get(instance);
454
+ const cache = {};
455
+ const proxy = new Proxy(instance, {
456
+ get(target, key) {
457
+ if (typeof target[key] === "function") return target[key];
458
+ if (target[key]?.__v_skip) return target[key];
459
+ const cacheRef = cache[key];
460
+ if (cacheRef && cacheRef.r) return cacheRef();
461
+ cache[key] = createSharedReactive(() => {
462
+ return toRef(target, key);
463
+ });
464
+ return cache[key]();
465
+ },
466
+ set() {
467
+ return false;
468
+ }
469
+ });
470
+ cacheMap.set(instance, proxy);
471
+ return proxy;
472
+ }
473
+ __name(getV, "getV");
116
474
 
117
475
  // src/decorator.ts
118
476
  import { set, setHandler, setStateKey } from "phecda-web";
@@ -129,8 +487,7 @@ function WatchEffect(option) {
129
487
  let stopHandler;
130
488
  setHandler(proto, key, {
131
489
  init(instance) {
132
- if (typeof instance[key] !== "function")
133
- throw new Error("WatchEffect must decorate function");
490
+ if (typeof instance[key] !== "function") throw new Error("WatchEffect must decorate function");
134
491
  stopHandler = watchEffect(instance[key].bind(instance), option);
135
492
  },
136
493
  unmount() {
@@ -146,6 +503,9 @@ export {
146
503
  WatchEffect,
147
504
  createPhecda,
148
505
  createSharedReactive,
506
+ getPhecda,
507
+ getR,
508
+ getV,
149
509
  markRaw,
150
510
  phecdaSymbol,
151
511
  useEvent,
package/package.json CHANGED
@@ -1,27 +1,28 @@
1
1
  {
2
2
  "name": "phecda-vue",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "provide phecda function to vue",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
5
+ "author": "fgsreally",
6
+ "license": "MIT",
7
+ "repository": "https://github.com/fgsreally/phecda/tree/main/packages/vue",
8
8
  "keywords": [
9
9
  "phecda",
10
10
  "vue"
11
11
  ],
12
- "author": "fgsreally",
13
- "repository": "https://github.com/fgsreally/phecda/tree/main/packages/vue",
12
+ "main": "dist/index.js",
13
+ "module": "dist/index.mjs",
14
+ "types": "dist/index.d.ts",
14
15
  "files": [
15
16
  "dist"
16
17
  ],
17
- "license": "MIT",
18
18
  "dependencies": {
19
+ "@vue/devtools-api": "^6.6.3",
19
20
  "vue": "^3.2.45",
20
- "phecda-web": "2.0.1"
21
+ "phecda-web": "2.0.3"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@vue/test-utils": "^2.4.6",
24
- "tsup": "^6.5.0"
25
+ "tsup": "^8.1.0"
25
26
  },
26
27
  "scripts": {
27
28
  "build": "tsup",