Skip to content

Commit 05097b3

Browse files
authored
feat:init
1 parent 4452950 commit 05097b3

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

subscription.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { dynamicallyExecuteFunction } from './utilities/helpers';
2+
import type { DeepReadonly } from 'vue';
3+
4+
/**
5+
* It takes a value and returns an object with a value property that is a shallowRef of the value.
6+
* passed in, and Subscribers(function) are added to a Set of subscribers to be executed when the value is changed.
7+
* @param {T} value - T - The initial value of the subscription.
8+
* @returns A function that returns an object with a shallow reactive value, a subscriber and a
9+
* few extra methods.
10+
*/
11+
export function useSubscription<T>(value: T) {
12+
if (value === undefined) {
13+
throw new Error('No value provided, Initial value is required');
14+
}
15+
16+
const _subRef = shallowRef(value);
17+
type SubType = typeof _subRef['value'];
18+
19+
type Subscriber = (val: SubType) => Promise<void> | void | Promise<SubType> | SubType;
20+
const _subscriptions: Set<Subscriber> = new Set();
21+
/**
22+
* AddSubscriber() takes a function as an argument and adds it to the _subscriptions Set.
23+
* @param subscriber - Subscriber
24+
*/
25+
function addSubscriber(subscriber: Subscriber) {
26+
if (typeof subscriber !== 'function') {
27+
throw new Error('Subscriber must be a function');
28+
}
29+
_subscriptions.add(subscriber);
30+
}
31+
/**
32+
* It deletes a custom effect from the subscriptions list.
33+
* @param subscriber - Subscriber
34+
*/
35+
function deleteSubscriber(subscriber: Subscriber) {
36+
_subscriptions.delete(subscriber);
37+
}
38+
/**
39+
* It loops through the Set of subscribers and executes each function with the value passed in as
40+
* an argument
41+
* @param {SubType} val - The value that is being passed to the subscribers.
42+
*/
43+
function triggerSubscribers(val: SubType) {
44+
_subscriptions.forEach(dep => dynamicallyExecuteFunction(dep, val));
45+
}
46+
47+
const extraHandlers = {
48+
/**
49+
* A method that allows you to delete a subscriber from the Set of subscribers.
50+
* @param subscriber - Subscriber
51+
*/
52+
$deleteSub(subscriber: Subscriber) {
53+
deleteSubscriber(subscriber);
54+
},
55+
/**
56+
* Manually trigger subscribers. Shouldn't be used unless explicity needed (rarely).
57+
*/
58+
$triggerSubs() {
59+
triggerSubscribers(_subRef.value);
60+
},
61+
/**
62+
* It mutates the value of the object.
63+
* @param mutator - (val: SubType) => SubType
64+
*/
65+
$mutate(mutator: (val: SubType) => SubType) {
66+
if (typeof _subRef.value !== 'object') {
67+
throw new Error('Value passed is not an typeof object! Patch only accepts typeof object');
68+
}
69+
_subRef.value = mutator(_subRef.value);
70+
triggerSubscribers(_subRef.value);
71+
}
72+
};
73+
74+
interface $Sub {
75+
triggerSubs: typeof extraHandlers['$triggerSubs'];
76+
/**
77+
* A Subscriber(function) is executed when the value is changed.
78+
* @param subscriber - type Subscriber = (val: SubType) => Promise<void> | void | Promise<SubType> | SubType;
79+
*/
80+
subscriber: Subscriber;
81+
mutate: typeof extraHandlers['$mutate'];
82+
value: typeof _subRef['value'];
83+
}
84+
85+
return {
86+
get value() {
87+
return _subRef.value;
88+
},
89+
set value(val) {
90+
_subRef.value = val;
91+
triggerSubscribers(val);
92+
},
93+
/** ReadOnly version of value. Wraps the shallow ref in readonly */
94+
$ref: readonly(_subRef),
95+
96+
/**
97+
* A Subscriber(function) is executed when the value is changed.
98+
* @param subscriber - type Subscriber = (val: SubType) => Promise<void> | void | Promise<SubType> | SubType;
99+
*/
100+
set $subscriber(subscriber: Subscriber) {
101+
addSubscriber(subscriber);
102+
},
103+
$triggerSubs: extraHandlers.$triggerSubs,
104+
$deleteSub: extraHandlers.$deleteSub,
105+
$mutate: extraHandlers.$mutate,
106+
/**
107+
* A subscription object with all the core properties.
108+
*/
109+
$sub: {
110+
get value() {
111+
return _subRef.value;
112+
},
113+
set value(val) {
114+
_subRef.value = val;
115+
triggerSubscribers(val);
116+
},
117+
/**
118+
* A Subscriber(function) is executed when the value is changed.
119+
* @param subscriber - type Subscriber = (val: SubType) => Promise<void> | void | Promise<SubType> | SubType;
120+
*/
121+
set subscriber(subscriber: Subscriber) {
122+
addSubscriber(subscriber);
123+
},
124+
triggerSubs: extraHandlers.$triggerSubs,
125+
deleteSub: extraHandlers.$deleteSub,
126+
mutate: extraHandlers.$mutate
127+
}
128+
// Why must typescript be weird? Just to get intellisense for subscriptions.
129+
} as unknown as {
130+
$ref: DeepReadonly<$Sub['value']>;
131+
$subscriber: Subscriber;
132+
value: $Sub['value'];
133+
$sub: $Sub;
134+
} & typeof extraHandlers;
135+
}

utilities/helpers.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* It takes a function and an argument, and if the function is an async function, it executes it with
3+
* the argument, otherwise it returns a resolved promise with the result of the function
4+
* @param {Function} func - The function to execute.
5+
* @param {T} arg - The argument to pass to the function.
6+
* @returns A promise that resolves to the result of the function.
7+
*/
8+
export async function dynamicallyExecuteFunction<T>(func: Function, arg: T) {
9+
if (func.constructor.name === 'AsyncFunction') {
10+
return func(arg);
11+
}
12+
13+
return Promise.resolve(func(arg));
14+
}

0 commit comments

Comments
 (0)