@retronew/call-vue 0.4.0 → 0.4.1

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.mjs CHANGED
@@ -1,172 +1 @@
1
- import { defineComponent, h, onMounted, onUnmounted, shallowRef } from "vue";
2
- //#region src/createCallable/store.ts
3
- /**
4
- * Vue-native replacement for react-call's `useSyncExternalStore`-backed
5
- * store: `shallowRef` already gives us a single reactive snapshot per
6
- * store, so there's no separate subscribe/getSnapshot machinery to hand-roll.
7
- *
8
- * `rootCount` plays the role react-call's `listeners.size` plays — it's how
9
- * `assertSingleRoot` in `createCallable` detects a missing or duplicated
10
- * `<Root>`. It's tracked here (rather than as its own `ref`) because it must
11
- * never trigger a re-render on its own; only stack changes should.
12
- */
13
- function createStackStore() {
14
- let nextKey = 0;
15
- let upsertPromise = null;
16
- let rootCount = 0;
17
- const stack = shallowRef([]);
18
- return {
19
- stack,
20
- add: (call) => {
21
- stack.value = [...stack.value, {
22
- ...call,
23
- key: String(nextKey++)
24
- }];
25
- },
26
- set: (promise, updateFn) => {
27
- stack.value = stack.value.map((call) => promise && call.promise !== promise ? call : updateFn(call));
28
- },
29
- remove: (promises) => {
30
- stack.value = stack.value.filter((c) => !promises.has(c.promise));
31
- },
32
- /** Call once from a mounted `<Root>`; call the returned function on unmount. */
33
- mountRoot: () => {
34
- rootCount++;
35
- return () => {
36
- rootCount--;
37
- if (rootCount === 0) {
38
- nextKey = 0;
39
- stack.value = [];
40
- upsertPromise = null;
41
- }
42
- };
43
- },
44
- getRootCount: () => rootCount,
45
- getUpsertPromise: () => upsertPromise,
46
- setUpsertPromise: (p) => {
47
- upsertPromise = p;
48
- }
49
- };
50
- }
51
- //#endregion
52
- //#region src/createCallable/index.ts
53
- /**
54
- * Turns a Vue component into a "callable": mount it once as `<Confirm />`
55
- * and then `await Confirm.call(props)` from anywhere to push an instance
56
- * onto its stack and get back a promise that resolves when the instance
57
- * calls `call.end(response)`.
58
- *
59
- * This is a Vue-native port of react-call's `createCallable` — see
60
- * `skills/call-vue/SKILL.md` for the full mental model (stack, upsert,
61
- * lifecycle constraints).
62
- */
63
- function createCallable(UserComponent, unmountingDelay = 0) {
64
- const store = createStackStore();
65
- const createEnd = (promise) => (response) => {
66
- const ending = /* @__PURE__ */ new Set();
67
- store.set(promise, (call) => {
68
- call.resolve(response);
69
- ending.add(call.promise);
70
- return {
71
- ...call,
72
- ended: true
73
- };
74
- });
75
- globalThis.setTimeout(() => store.remove(ending), unmountingDelay);
76
- };
77
- const assertSingleRoot = () => {
78
- const count = store.getRootCount();
79
- if (!count) throw new Error("No <Root> found!");
80
- if (count > 1) throw new Error("Multiple instances of <Root> found!");
81
- };
82
- const call = (props) => {
83
- assertSingleRoot();
84
- let resolve;
85
- const promise = new Promise((res) => {
86
- resolve = res;
87
- });
88
- store.add({
89
- props,
90
- end: createEnd(promise),
91
- ended: false,
92
- promise,
93
- resolve
94
- });
95
- return promise;
96
- };
97
- const upsert = (props) => {
98
- assertSingleRoot();
99
- const existing = store.getUpsertPromise();
100
- if (existing) {
101
- store.set(existing, (c) => ({
102
- ...c,
103
- props
104
- }));
105
- return existing;
106
- }
107
- let resolve;
108
- const promise = new Promise((res) => {
109
- resolve = res;
110
- });
111
- store.setUpsertPromise(promise);
112
- store.add({
113
- props,
114
- end: (response) => {
115
- store.setUpsertPromise(null);
116
- createEnd(promise)(response);
117
- },
118
- ended: false,
119
- promise,
120
- resolve
121
- });
122
- return promise;
123
- };
124
- const end = ((...args) => {
125
- const targeted = args.length === 2;
126
- const promise = targeted ? args[0] : null;
127
- const response = targeted ? args[1] : args[0];
128
- if (!targeted || promise === store.getUpsertPromise()) store.setUpsertPromise(null);
129
- return createEnd(promise)(response);
130
- });
131
- const update = (...args) => {
132
- const targeted = args.length === 2;
133
- store.set(targeted ? args[0] : null, (c) => ({
134
- ...c,
135
- props: {
136
- ...c.props,
137
- ...targeted ? args[1] : args[0]
138
- }
139
- }));
140
- };
141
- const Root = defineComponent({
142
- name: "CallableRoot",
143
- inheritAttrs: false,
144
- setup(_, { attrs }) {
145
- let unmountRoot;
146
- onMounted(() => {
147
- unmountRoot = store.mountRoot();
148
- });
149
- onUnmounted(() => unmountRoot?.());
150
- return () => store.stack.value.map((item, index, stack) => h(UserComponent, {
151
- ...item.props,
152
- key: item.key,
153
- call: {
154
- key: item.key,
155
- end: item.end,
156
- ended: item.ended,
157
- root: attrs,
158
- index,
159
- stackSize: stack.length
160
- }
161
- }));
162
- }
163
- });
164
- return Object.assign(Root, {
165
- call,
166
- upsert,
167
- end,
168
- update
169
- });
170
- }
171
- //#endregion
172
- export { createCallable };
1
+ import{defineComponent as e,h as t,onMounted as n,onUnmounted as r,shallowRef as i}from"vue";function a(){let e=0,t=null,n=0,r=i([]);return{stack:r,add:t=>{r.value=[...r.value,{...t,key:String(e++)}]},set:(e,t)=>{r.value=r.value.map(n=>e&&n.promise!==e?n:t(n))},remove:e=>{r.value=r.value.filter(t=>!e.has(t.promise))},mountRoot:()=>(n++,()=>{n--,n===0&&(e=0,r.value=[],t=null)}),getRootCount:()=>n,getUpsertPromise:()=>t,setUpsertPromise:e=>{t=e}}}function o(i,o=0){let s=a(),c=e=>t=>{let n=new Set;s.set(e,e=>(e.resolve(t),n.add(e.promise),{...e,ended:!0})),globalThis.setTimeout(()=>s.remove(n),o)},l=()=>{let e=s.getRootCount();if(!e)throw Error(`No <Root> found!`);if(e>1)throw Error(`Multiple instances of <Root> found!`)},u=e=>{l();let t,n=new Promise(e=>{t=e});return s.add({props:e,end:c(n),ended:!1,promise:n,resolve:t}),n},d=e=>{l();let t=s.getUpsertPromise();if(t)return s.set(t,t=>({...t,props:e})),t;let n,r=new Promise(e=>{n=e});return s.setUpsertPromise(r),s.add({props:e,end:e=>{s.setUpsertPromise(null),c(r)(e)},ended:!1,promise:r,resolve:n}),r},f=((...e)=>{let t=e.length===2,n=t?e[0]:null,r=t?e[1]:e[0];return(!t||n===s.getUpsertPromise())&&s.setUpsertPromise(null),c(n)(r)}),p=(...e)=>{let t=e.length===2;s.set(t?e[0]:null,n=>({...n,props:{...n.props,...t?e[1]:e[0]}}))},m=e({name:`CallableRoot`,inheritAttrs:!1,setup(e,{attrs:a}){let o;return n(()=>{o=s.mountRoot()}),r(()=>o?.()),()=>s.stack.value.map((e,n,r)=>t(i,{...e.props,key:e.key,call:{key:e.key,end:e.end,ended:e.ended,root:a,index:n,stackSize:r.length}}))}});return Object.assign(m,{call:u,upsert:d,end:f,update:p})}export{o as createCallable};
@@ -1,29 +1 @@
1
- import { isRef, ref } from "vue";
2
- //#region src/mutation-flow/index.ts
3
- const noopChain = { orEnd: () => {} };
4
- function resolveMutation(source) {
5
- return isRef(source) ? source.value : source;
6
- }
7
- function useMutationFlow(call, mutationSource) {
8
- const pending = ref(false);
9
- let inFlight = false;
10
- const trigger = ((payload) => {
11
- if (inFlight) return noopChain;
12
- const mutationFn = resolveMutation(mutationSource);
13
- if (!mutationFn) return { orEnd: (value) => call.end(value) };
14
- inFlight = true;
15
- pending.value = true;
16
- mutationFn(call, payload).finally(() => {
17
- inFlight = false;
18
- pending.value = false;
19
- });
20
- return noopChain;
21
- });
22
- Object.defineProperty(trigger, "pending", {
23
- enumerable: true,
24
- get: () => pending.value
25
- });
26
- return trigger;
27
- }
28
- //#endregion
29
- export { useMutationFlow };
1
+ import{isRef as e,ref as t}from"vue";const n={orEnd:()=>{}};function r(t){return e(t)?t.value:t}function i(e,i){let a=t(!1),o=!1,s=(t=>{if(o)return n;let s=r(i);return s?(o=!0,a.value=!0,s(e,t).finally(()=>{o=!1,a.value=!1}),n):{orEnd:t=>e.end(t)}});return Object.defineProperty(s,"pending",{enumerable:!0,get:()=>a.value}),s}export{i as useMutationFlow};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retronew/call-vue",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Call & await Vue components like async functions — a Vue 3 port of react-call.",
5
5
  "keywords": [
6
6
  "async",