vaderjs 1.0.1 → 1.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.
package/config.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "port": 3001,
3
+ "type": "http",
4
+ "origin": "http://localhost:3000",
5
+ "path": "/api"
6
+
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A Reactive Framework for Single-Page Applications (SPA)",
5
5
  "main": "vader.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # VaderJS: A Reactive Framework for SPAs
2
2
 
3
- [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE)
3
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/vaderjs.svg?style=flat)](https://www.npmjs.com/package/vaderjs)
4
4
 
5
5
  VaderJS is a powerful reactive framework for building Single-Page Applications (SPAs), inspired by React.js.
6
6
 
@@ -24,7 +24,7 @@ const [state, setState] = useState("count", initialState);
24
24
  function increment(){
25
25
  setState(state + 1)
26
26
  }
27
- registerFunction('increment', increment)
27
+ rf('increment', increment)
28
28
  useEffect((state)=>{
29
29
  console.log('New State for count' + state)
30
30
  }[state])
@@ -34,7 +34,7 @@ useEffect((state)=>{
34
34
  ### Function Binding
35
35
 
36
36
  ```javascript
37
- registerFunction('login', login);
37
+ rf('login', login);
38
38
  return html`<button onclick="login()">Login</button>`;
39
39
  ```
40
40
 
@@ -60,23 +60,53 @@ const { state, setState, subscribe } = store;
60
60
  ### Simplified Component Creation
61
61
 
62
62
  ```javascript
63
- async function myComponent(props){
63
+ // id is a unique component key in which allows vader to update the component state!
64
+ const myComponent = (id) = component(id, {
65
+ render: (states, props) => {
64
66
  return vhtml`
65
67
  <div>${props.message}</div>
66
68
  `
67
69
  }
68
-
69
- async function app(props){
70
- let html = await createComponent(myComponent, {
71
- message: 'Hello Vader'
72
- })
73
- return vhtml `
74
- <div>
75
- ${html}
76
- </div>
77
- `
78
- }
79
-
70
+ })
71
+
72
+ // then call
73
+
74
+ myComponent(key).render({props})
75
+
76
+ //example
77
+
78
+ import VaderRouter from "./router.js";
79
+ import { vhtml, component, rf } from './script.js'
80
+
81
+ const app = component('app', {
82
+ render: (states) => {
83
+ let [count, setCount] = useState('count', 0);
84
+ useEffect(() => {
85
+ console.log(states)
86
+ console.log('App component mounted');
87
+ }, [count]);
88
+
89
+ function incrementHandler() {
90
+ setCount(count + 1);
91
+ }
92
+ rf('incrementHandler', incrementHandler);
93
+
94
+
95
+ return vhtml`
96
+
97
+ <div>
98
+ <button className="btn" onclick="incrementHandler()"
99
+
100
+ >Count: ${count}</button>
101
+ ${
102
+ count > 10 ? '<h1 style="color:lightBlue">Greater Than 10</h1>' : 'Less than 10'
103
+ }
104
+ </div>
105
+ `;
106
+ },
107
+ })
108
+
109
+ document.body.innerHTML = app.render()
80
110
  ```
81
111
 
82
112
  ## Get Started
@@ -98,4 +128,4 @@ VaderJS is released under the MIT License. See the [LICENSE](https://github.com/
98
128
 
99
129
  ## Join the Community
100
130
 
101
- Connect with the VaderJS community on [GitHub](https://github.com/Postr-Inc/Vader.js). Contribute, share feedback, and improve VaderJS for SPA development.
131
+ Connect with the VaderJS community on [GitHub](https://github.com/Postr-Inc/Vader.js). Contribute, share feedback, and improve VaderJS for SPA development.
package/vader.js CHANGED
@@ -1,867 +1,373 @@
1
- const templates = [];
2
- const cache = {};
3
1
  /**
4
- * @file Vader.js
5
- * @version 1.0.0
6
- * @license MIT
7
- * @description A simple ReactLike - framework for building web applications.
2
+ * @Object window
3
+ * @property {Object} props
4
+ * @description Allows you to store props for component
8
5
  */
6
+ window.props = {}
9
7
  /**
8
+ * @function vhtml
9
+ * @param {String} strings
10
+ * @param {...any} args
11
+ * @returns modified string
10
12
  *
11
- * @param {*} strings
12
- * @param {...any} values
13
- * @returns {string}
14
- * @description Creates a template literal and returns it as a string.
15
13
  */
16
- const vhtml = (strings, ...values) => {
14
+ export function vhtml(strings, ...args) {
17
15
  let result = "";
16
+
18
17
  for (let i = 0; i < strings.length; i++) {
19
18
  result += strings[i];
20
- if (i < values.length) {
21
- result += values[i];
19
+ if (i < args.length) {
20
+ result += args[i];
22
21
  }
23
22
  }
24
- let dom = new DOMParser().parseFromString(result, "text/html");
25
23
 
26
- let eventTypes = [
27
- "click",
28
- "dblclick",
29
- "mousedown",
30
- "mouseup",
31
- "mouseenter",
32
- "mouseleave",
33
- "mousemove",
34
- "keydown",
35
- "keyup",
36
- "focus",
37
- "blur",
38
- ];
39
-
40
- dom.querySelectorAll("[data-on]").forEach((element) => {
41
- eventTypes.forEach((eventType) => {
42
- const attributeName = `data-on-${eventType}`;
43
- if (element.hasAttribute(attributeName)) {
44
- awaitElement(element.tagName).then((el) => {
45
- el.addEventListener(eventType, () => {
46
- const eventCode = el.getAttribute(attributeName);
47
- let eventFunction = new Function(eventCode);
48
- eventFunction();
49
- });
50
- });
51
- }
52
- });
53
- });
54
-
55
- dom.querySelectorAll("[id]").forEach((element) => {
56
- const id = element.getAttribute("id");
57
- window[id] = element;
58
- });
59
-
60
- return dom.body.innerHTML;
61
- };
62
-
63
- let init = false;
64
- /**
65
- * @param {string} selector
66
- * @param {function} rt
67
- * @returns {void}
68
- * @description Registers a template to be rendered.
69
- */
70
- const render = async (template) => {
71
- if (!document.querySelector(template.selector)) {
72
- throw new Error(`No element found with selector ${template.selector}`);
73
- } else {
74
- const content = await template.rt();
75
-
76
- document.querySelector(template.selector).innerHTML = content;
77
- window["currentRender"] = template;
78
- if (!init) {
79
- init = true;
80
- hydrate();
81
- }
24
+ let dom = new DOMParser().parseFromString(result, 'text/html')
25
+ if(dom.body.firstChild.nodeName.toLowerCase() !== 'div'){
26
+ throw new Error(`Ensure that you have a parent div for all component elements`)
82
27
  }
83
- return {
84
- register: () => {
85
- register(template);
86
- },
87
- };
88
- };
89
- /**
90
- * @alias form
91
- *
92
- * @param {*} config
93
- * @returns form component
94
- * @description Creates a form component based on the config object.
95
- * @example
96
- * const form = form({
97
- * name: 'myForm',
98
- * fields: {
99
- * name: {
100
- * value: 'John Doe',
101
- * type: 'text',
102
- * placeholder: 'Enter your name'
103
- * },
104
- * email: {
105
- * value: ''
106
- * }
107
- * },
108
- * onSubmit: (e) => {
109
- * console.log(e)
110
- * }
111
- *
112
- *
113
- * })
114
- */
115
- const form = (config) => {
116
- const { name, fields, onSubmit, inputs, button, onReset, onChange, rules } =
117
- config;
118
- const formData = {};
119
-
120
- const componentInstance = {
121
- state: {},
122
- props: {},
123
- reset: () => {
124
- for (const fieldName in fields) {
125
- formData[fieldName] = fields[fieldName].value || "";
126
- }
127
- document.forms[name].reset();
128
- },
129
- componentDidMount: (form) => {
130
- form.setAttribute("onsubmit", "return false;");
131
-
132
- for (const fieldName in fields) {
133
- formData[fieldName] = fields[fieldName].value || "";
134
- const fieldElement = form[fieldName];
135
-
136
- fieldElement.addEventListener("input", (event) => {
137
- formData[fieldName] = event.target.value;
138
- });
139
- }
140
-
141
- if (onSubmit) {
142
- document.onsubmit = (ev) => {
143
- if (ev.target.name === name) {
144
- let event = formData;
145
-
146
- event["reset"] = componentInstance.reset;
147
- onSubmit(event);
148
- }
149
- };
150
- } else if (onReset) {
151
- document.onreset = (ev) => {
152
- if (ev.target.name === name) {
153
- for (const fieldName in fields) {
154
- formData[fieldName] = fields[fieldName].value || "";
155
- }
156
- onReset();
157
- }
158
- };
159
- } else if (onChange) {
160
- document.onchange = (ev) => {
161
- if (formData[ev.target.name]) {
162
- let event = formData;
163
-
164
- event["reset"] = componentInstance.reset;
165
- onChange(event);
166
- }
167
- };
168
- }
169
- },
170
- render: async () => {
171
- const formElement = document.createElement("form");
172
- formElement.name = name;
173
-
174
- for (const fieldName in fields) {
175
- const fieldConfig = fields[fieldName];
176
- const fieldElement = document.createElement("input");
177
- fieldElement.name = fieldName;
178
- fieldElement.value = fieldConfig.value || "";
179
- fieldElement.type = fieldConfig.type || "text";
180
- fieldElement.placeholder = fieldConfig.placeholder || "";
181
- if (inputs && inputs[fieldName]) {
182
- const fieldStyles = inputs[fieldName];
183
- for (const key in fieldStyles) {
184
- fieldElement.style[key] = fieldStyles[key];
185
- }
186
- }
187
-
188
- if (rules) {
189
- Object.keys(rules).forEach((rule) => {
190
- if (rule === fieldName) {
191
- const rulesobj = rules[rule];
192
- // set all attributes to fieldElement
193
- for (const key in rulesobj) {
194
- fieldElement.setAttribute(key, rulesobj[key]);
195
- }
196
- }
197
- });
198
- }
199
-
200
- document.oninput = (ev) => {
201
- let fieldName = ev.target.name;
202
- let fieldValue = ev.target.value;
203
- formData[fieldName] = fieldValue;
204
- };
205
-
206
- // Add more attributes or properties to the fieldElement if needed
207
- formElement.appendChild(fieldElement);
208
- }
209
- const submitButton = document.createElement("button");
210
- submitButton.type = "submit";
211
- submitButton.textContent = button.text || "Submit";
212
- if (button.styles) {
213
- for (const key in button.styles) {
214
- submitButton.style[key] = button.styles[key];
215
- }
216
- }
217
- formElement.appendChild(submitButton);
218
-
219
- // Call componentDidMount
220
- componentInstance.componentDidMount(formElement);
221
-
222
- return formElement.outerHTML;
223
- },
224
- };
225
- window.currentRender = componentInstance;
226
-
227
- return componentInstance;
228
- };
229
-
230
- /**
231
- * Component Lifecycle Hooks
232
- */
233
- const componentLifecycle = {
234
- componentWillMount: [],
235
- componentDidMount: [],
236
- componentWillUnmount: [],
237
- };
238
-
239
- /**
240
- * Register a lifecycle hook for a component
241
- * @param {string} hookName - Name of the lifecycle hook
242
- * @param {Function} hookFunction - Function to be called at the lifecycle stage
243
- */
244
- const registerLifecycleHook = (hookName, hookFunction) => {
245
- componentLifecycle[hookName].push(hookFunction);
246
- };
247
-
248
- /**
249
- * Call all registered lifecycle hooks for a component
250
- * @param {string} hookName - Name of the lifecycle hook
251
- * @param {Object} component - Component object
252
- */
253
- const callLifecycleHooks = (hookName, component) => {
254
- componentLifecycle[hookName].forEach((hookFunction) => {
255
- hookFunction(component);
256
- });
257
- };
258
-
259
- /**
260
- * Component wrapper function
261
- * @param {Function} componentFn - Component function
262
- * @param {Object} props - Component props
263
- * @returns {Promise} - Promise with the component content
264
- */
265
- const component = async (componentFn, props) => {
266
- let isMounted = true;
267
28
 
268
- const componentInstance = {
269
- state: {},
270
- props,
271
- setState: (newState) => {
272
- if (isMounted) {
273
- componentInstance.state = { ...componentInstance.state, ...newState };
274
- render(template); // Assuming 'template' is defined somewhere
275
- }
276
- },
277
- };
278
-
279
- // Call componentDidMount lifecycle hooks
280
- callLifecycleHooks("componentWillMount", componentInstance);
281
-
282
- const content = await componentFn(props);
283
-
284
- // Call componentDidMount lifecycle hooks
285
- callLifecycleHooks("componentDidMount", componentInstance);
286
-
287
- const unmount = () => {
288
- isMounted = false;
289
- // Call componentWillUnmount lifecycle hooks
290
- callLifecycleHooks("componentWillUnmount", componentInstance);
291
- };
292
-
293
- return {
294
- ...content,
295
- unmount,
296
- };
297
- };
298
-
299
- /**
300
- * Create a new component instance
301
- * @param {Function} componentFn - Component function
302
- * @param {Object} props - Component props
303
- * @returns {Promise<ComponentInstance>} - Promise with the component instance
304
- * @typedef {Object} ComponentInstance - Component instance
305
- * @property {Object} state - Component state
306
- * @property {Object} props - Component props
307
- * @property {Function} setState - Function to update the component state
308
- */
309
- const createComponent = async (componentFn, props) => {
310
- const componentInstance = {
311
- state: {},
312
- props,
313
- setState: (newState) => {
314
- componentInstance.state = { ...componentInstance.state, ...newState };
315
- },
316
- };
317
- /** @type {Function} */
318
- return await componentFn(props);
319
- };
320
-
321
- // ... (other functions)
322
-
323
- // Exported functions
324
- export {
325
- render,
326
- vhtml,
327
- component,
328
- form,
329
- useAuth,
330
- createComponent,
331
- useExternalStore,
332
- useState,
333
- useEffect,
334
- useReduce,
335
- useSyncStore,
336
- require,
337
- $s,
338
- registerFunction,
339
- };
340
-
341
- // ... (remaining code)
342
-
343
- function hydrate() {
344
- templates.forEach(async (template) => {
345
- if (template === window["currentRender"]) {
346
- render(template);
347
- return;
348
- }
349
- const content = await template.rt();
350
- const element = document.querySelector(template.selector);
351
-
352
- if (element) {
353
- if (template.renderedContent !== content) {
354
- element.innerHTML = content;
355
- template.renderedContent = content;
356
- }
357
- }
358
- });
29
+ dom.body.querySelectorAll('[className]').forEach((el)=>{
30
+ el.setAttribute('class', el.getAttribute('classname'))
31
+ el.removeAttribute('classname')
32
+ })
33
+ return dom.body.innerHTML
359
34
  }
360
35
  /**
361
- * Register a function to be available in the component scope for vhtml elements.
362
- *
363
- * @param {string} name - The name to assign to the function in the global scope.
364
- * @param {Function} fn - The function to register.
365
- * @throws {Error} Throws an error if the name is not a string or if the function is not a valid function.
366
- * @throws {Error} Throws an error if the name is already used in the global scope.
367
- * @param {string} name @type {string}
368
- * @example
369
- * function login() {
370
- * setState({ loggedIn: true });
371
- * }
372
- * registerFunction('login', login);
373
- * return html`<button onclick="login()">Login</button>`;
374
- */
375
- const registerFunction = (name, fn) => {
376
- if (typeof name !== "string") {
377
- throw new Error("The name parameter must be a string.");
378
- }
379
-
380
- if (typeof fn !== "function") {
381
- throw new Error("The fn parameter must be a function.");
382
- }
383
-
384
-
385
-
36
+ * @function component
37
+ * @param {*} name
38
+ * @param {*} options
39
+ * @returns
40
+ * @param {*} states
41
+ * @param {*} setState
42
+ * @param {*} useState
43
+ * @param {*} useEffect
44
+ * @param {*} useAuth
45
+ * @param {*} render
46
+ *
47
+ * @example
48
+ *
49
+ * const app = component('app', {
50
+ render: (states, props) => {
51
+
52
+
53
+ let [count, setCount] = useState('count', 0);
54
+
55
+ useEffect(() => {
56
+ console.log('App component mounted');
57
+ });
58
+
59
+ function incrementHandler() {
60
+ setCount(count + 1);
61
+ }
62
+ rf('incrementHandler', incrementHandler);
63
+
64
+
65
+ return vhtml`
66
+ <div>
67
+ <button onclick="incrementHandler()" >Click me</button>
68
+ <p>You clicked ${count} times</p>
69
+ </div>
70
+ `;
71
+ },
72
+ });
73
+ ***/
74
+
75
+ export function component(name, options) {
76
+ const states = {};
77
+ const effects = {};
78
+ const executedEffects = {};
79
+ let storedProps = {};
386
80
  /**
387
- * @global
388
- * @function
389
- * @name {Function}
81
+ * @function setState
82
+ * @param {*} key
83
+ * @param {*} value
84
+ * @returns {null}
85
+ * @description Allows you to change state of component and re-render it
390
86
  */
391
- window[name] = fn;
392
- };
393
-
394
- function doxMethods(element) {
395
- element.on = function (event, callback) {
396
- element.addEventListener(event, callback);
397
- };
398
- element.query = function (selector) {
399
- return element.querySelector(selector);
87
+ const setState = (key, value) => {
88
+ states[key] = value;
89
+ updateComponent();
400
90
  };
401
91
 
402
- return element;
403
- }
404
-
405
- const awaitElement = (selector) => {
406
- return new Promise((resolve, reject) => {
407
- const interval = setInterval(() => {
408
- if (document.querySelector(selector)) {
409
- clearInterval(interval);
410
- resolve(document.querySelector(selector));
411
- }
412
- }, 100);
413
- });
414
- };
415
-
416
- /**
417
- * Create a new state variable with optional initial value.
418
- *
419
- * @param {string} StateName - The name of the state variable.
420
- * @param {*} initialState - The initial state value.
421
- * @returns {Array} An array containing the current state value and a function to update the state.
422
- * @typedef {Array} StateHook
423
- * @property {*} 0 - The current state value.
424
- * @property {Function} 1 - A function to update the state.
425
- */
426
- const useState = (StateName, initialState) => {
427
- let currentstate;
428
-
429
- // Attempt to retrieve state from sessionStorage
430
- if (sessionStorage.getItem(StateName)) {
431
- currentstate = JSON.parse(sessionStorage.getItem(StateName));
432
- } else {
433
- // If state is not found in sessionStorage, use initial state
434
- currentstate = initialState;
435
- sessionStorage.setItem(StateName, JSON.stringify(initialState));
436
- }
437
-
438
92
  /**
439
- * Update the state with a new value.
440
- *
441
- * @param {*} newState - The new state value.
442
- * @returns {*} The new state value.
93
+ * @function useState
94
+ * @param {*} key
95
+ * @param {*} initialValue
96
+ * @returns {Array} [state, setState]
97
+ * @description Allows you to bind state to component
443
98
  */
444
- const setState = (newState) => {
445
- currentstate = newState;
446
- sessionStorage.setItem(StateName, JSON.stringify(newState));
447
- let clonedState = JSON.parse(JSON.stringify(newState));
448
- window.postMessage({ state: clonedState }, "*");
449
- hydrate();
450
- window[StateName] = newState;
451
- return newState;
452
- };
453
-
454
- return [currentstate, setState];
455
- };
456
-
457
- /**
458
- * Update a state variable in sessionStorage and the global scope.
459
- *
460
- * @param {string} statename - The name of the state variable.
461
- * @param {*} newState - The new state value.
462
- */
463
- const setState = (statename, newState) => {
464
- window[statename] = sessionStorage.setItem(
465
- statename,
466
- JSON.stringify(newState)
467
- );
468
- window.postMessage({ state: newState }, "*");
469
- hydrate();
470
- };
99
+ const useState = (key, initialValue) => {
100
+ if (!(key in states)) {
101
+ states[key] = initialValue;
102
+ window.props[key] = initialValue;
103
+ }
471
104
 
472
- /**
473
- * Create a store with state management functionality.
474
- *
475
- * @param {Object} initialState - The initial state of the store.
476
- * @returns {Object} An object containing state management functions.
477
- */
478
- const createStore = (initialState) => {
479
- let state = initialState;
480
- const subscribers = new Set();
105
+ /**
106
+ * @Array state
107
+ * @param {*} states
108
+ * @description Allows you to get state of component
109
+ */
481
110
 
111
+ return [states[key], (newValue) => setState(key, newValue)];
112
+ };
482
113
  /**
483
- * Update the store's state and notify subscribers.
484
- *
485
- * @param {Object} newState - The new state to set.
114
+ * @function useEffect
115
+ * @param {*} effectFn
116
+ * @returns {null}
117
+ * @description Allows you to run side effects
486
118
  */
487
- const setState = (newState) => {
488
- state = newState;
489
- subscribers.forEach((subscriber) => subscriber(state));
490
119
 
491
- // Update local storage with the new state
492
- if (localStorage.getItem("store")) {
493
- let store = JSON.parse(localStorage.getItem("store"));
494
- store = { ...store, ...state };
495
- localStorage.setItem("store", JSON.stringify(store));
496
- } else {
497
- localStorage.setItem("store", JSON.stringify(state));
120
+ const useEffect = (effectFn, dependencies) => {
121
+ if (!effects[name]) {
122
+ effects[name] = [];
498
123
  }
499
-
500
- // Notify other windows/frames about the state change
501
- window.postMessage({ state: state }, "*");
502
- hydrate();
124
+ if (dependencies.length > 1) {
125
+ runEffects();
126
+ }
127
+ effects[name].push(effectFn);
128
+ runEffects();
503
129
  };
504
130
 
505
131
  /**
506
- * Subscribe a function to be notified of state changes.
507
- *
508
- * @param {Function} subscriber - The function to call when the state changes.
509
- * @returns {Function} A function to unsubscribe the subscriber.
132
+ * @function useSyncStore
133
+ * @param {*} storeName
134
+ * @param {*} initialState
135
+ * @returns {Object} {getField, setField, subscribe, clear}
136
+ * @description Allows you to manage state in local storage
510
137
  */
511
- const subscribe = (subscriber) => {
512
- subscribers.add(subscriber);
513
- return () => {
514
- subscribers.delete(subscriber);
515
- };
516
- };
517
-
518
- return { state, setState, subscribe };
519
- };
520
-
521
- /**
522
- * Create an authentication object with utility methods for managing user permissions and roles.
523
- *
524
- * @param {Object} options - The options object.
525
- * @param {Array} options.rulesets - An array of rulesets defining user permissions.
526
- * @param {Object} options.user - The user object containing roles and other information.
527
- * @returns {Object} The authentication object with utility methods.
528
- */
529
- function useAuth(options) {
530
- if (!options.rulesets) {
531
- throw new Error("No rulesets provided");
532
- }
533
-
534
- let rules = options.rulesets;
535
- let user = options.user;
138
+ const useSyncStore = (storeName, initialState) => {
139
+ const storedState =
140
+ JSON.parse(localStorage.getItem(storeName)) || initialState;
141
+ const store = createStore(storedState);
536
142
 
537
- const auth = {
538
143
  /**
539
- * Check if the user can perform a specific action.
144
+ * Get the value of a specific field from the store's state.
540
145
  *
541
- * @param {string} action - The action to check.
542
- * @returns {boolean} True if the user can perform the action, false otherwise.
146
+ * @param {string} fieldName - The name of the field.
147
+ * @returns {*} The value of the specified field.
543
148
  */
544
- can: (action) => {
545
- let can = false;
546
- rules.forEach((rule) => {
547
- if (rule.action === action) {
548
- if (rule.condition(user)) {
549
- can = true;
550
- }
551
- }
552
- });
553
- return can;
554
- },
555
- /**
556
- * Check if the user has a specific role.
557
- *
558
- * @param {string} role - The role to check.
559
- * @returns {boolean} True if the user has the role, false otherwise.
560
- */
561
- hasRole: (role) => {
562
- return user.roles && user.roles.includes(role);
563
- },
564
- /**
565
- * Check if the user can perform a specific action with a specific role.
566
- *
567
- * @param {string} action - The action to check.
568
- * @param {string} role - The role to check.
569
- * @returns {boolean} True if the user can perform the action with the role, false otherwise.
570
- */
571
- canWithRole: (action, role) => {
572
- return auth.can(action) && auth.hasRole(role);
573
- },
574
- /**
575
- * Assign a new rule to the rulesets.
576
- *
577
- * @param {Object} rule - The rule to assign.
578
- */
579
- assignRule: (rule) => {
580
- if (!rules.some((existingRule) => existingRule.action === rule.action)) {
581
- rules.push(rule);
582
- }
583
- },
584
- /**
585
- * Revoke a rule from the rulesets.
586
- *
587
- * @param {string} action - The action of the rule to revoke.
588
- */
589
- revokeRule: (action) => {
590
- rules = rules.filter((rule) => rule.action !== action);
591
- },
149
+ const getField = (fieldName) => {
150
+ return store.state[fieldName];
151
+ };
152
+
592
153
  /**
593
- * Check if the user can perform any of the specified actions.
154
+ * Set the value of a specific field in the store's state.
594
155
  *
595
- * @param {Array} actions - An array of actions to check.
596
- * @returns {boolean} True if the user can perform any of the actions, false otherwise.
156
+ * @param {string} fieldName - The name of the field.
157
+ * @param {*} value - The new value to set for the field.
597
158
  */
598
- canAnyOf: (actions) => {
599
- return actions.some((action) => auth.can(action));
600
- },
159
+ const setField = (fieldName, value) => {
160
+ const newState = { ...store.state, [fieldName]: value };
161
+ store.setState(newState);
162
+ };
163
+
601
164
  /**
602
- * Check if the user can perform all of the specified actions.
165
+ * Subscribe a function to be notified of state changes.
603
166
  *
604
- * @param {Array} actions - An array of actions to check.
605
- * @returns {boolean} True if the user can perform all of the actions, false otherwise.
167
+ * @param {Function} subscriber - The function to call when the state changes.
168
+ * @returns {Function} A function to unsubscribe the subscriber.
606
169
  */
607
- canAllOf: (actions) => {
608
- return actions.every((action) => auth.can(action));
609
- },
170
+ const subscribe = (subscriber) => {
171
+ return store.subscribe(subscriber);
172
+ };
173
+
610
174
  /**
611
- * Check if the user can perform a group of actions based on a logical operator.
612
- *
613
- * @param {Array} actions - An array of actions to check.
614
- * @param {string} logicalOperator - The logical operator to use ('any' or 'all').
615
- * @returns {boolean} True if the user can perform the actions based on the logical operator, false otherwise.
175
+ * Clear the stored state from local storage.
616
176
  */
617
- canGroup: (actions, logicalOperator = "any") => {
618
- return logicalOperator === "any"
619
- ? auth.canAnyOf(actions)
620
- : auth.canAllOf(actions);
621
- },
622
- };
623
-
624
- return auth;
625
- }
626
-
627
- /**
628
- * Create a synchronized store with field-level state management.
629
- *
630
- * @param {string} storeName - The name of the store.
631
- * @param {Object} initialState - The initial state of the store.
632
- * @returns {Object} An object containing field management functions.
633
- */
634
- const useSyncStore = (storeName, initialState) => {
635
- const storedState = JSON.parse(localStorage.getItem("store")) || initialState;
636
- const store = createStore(storedState);
637
-
638
- /**
639
- * Get the value of a specific field from the store's state.
640
- *
641
- * @param {string} fieldName - The name of the field.
642
- * @returns {*} The value of the specified field.
643
- */
644
- const getField = (fieldName) => {
645
- return store.state[fieldName];
646
- };
647
-
648
- /**
649
- * Set the value of a specific field in the store's state.
650
- *
651
- * @param {string} fieldName - The name of the field.
652
- * @param {*} value - The new value to set for the field.
653
- */
654
- const setField = (fieldName, value) => {
655
- const newState = { ...store.state, [fieldName]: value };
656
- store.setState(newState);
657
- };
177
+ const clear = () => {
178
+ localStorage.setItem(storeName, "");
179
+ };
658
180
 
659
- /**
660
- * Subscribe a function to be notified of state changes.
661
- *
662
- * @param {Function} subscriber - The function to call when the state changes.
663
- * @returns {Function} A function to unsubscribe the subscriber.
664
- */
665
- const subscribe = (subscriber) => {
666
- return store.subscribe(subscriber);
181
+ return {
182
+ getField,
183
+ setField,
184
+ subscribe,
185
+ clear,
186
+ };
667
187
  };
668
-
669
188
  /**
670
- * Clear the stored state from local storage.
189
+ * @function useAuth
190
+ * @param {*} rulesets
191
+ * @param {*} options
192
+ * @returns {Object} {canAccess, grantAccess, revokeAccess}
193
+ * @description Allows you to manage access to resources through rulesets
194
+ * @returns
671
195
  */
672
- const clear = () => {
673
- localStorage.setItem("store", "");
674
- };
675
196
 
676
- return {
677
- getField,
678
- setField,
679
- subscribe,
680
- clear,
681
- };
682
- };
683
-
684
- /**
685
- * Create an external store with API integration and local storage caching.
686
- *
687
- * @param {Object} options - Configuration options for the external store.
688
- * @param {Function} options.api - The API method to fetch data.
689
- * @param {string} options.storeKey - The key for storing data in local storage.
690
- * @param {Object} initialState - The initial state of the store.
691
- * @returns {Object} An object containing store management functions.
692
- */
693
- const useExternalStore = (options, initialState) => {
694
- const store = createStore(initialState);
695
- const { subscribe, setState } = store;
696
-
697
- let consecutiveFetches = 0;
698
- let lastFetchedData = null;
699
- let debounceTimer = null;
700
-
701
- if (options.api) {
702
- const apiMethod = options.api;
703
-
704
- /**
705
- * Update the store with fetched data from the API.
706
- *
707
- * @param {...*} args - Arguments to pass to the API method.
708
- */
709
- store.update = async (...args) => {
710
- try {
711
- const fetchedData = await apiMethod(...args);
712
-
713
- if (JSON.stringify(fetchedData) === JSON.stringify(lastFetchedData)) {
714
- consecutiveFetches++;
715
- } else {
716
- consecutiveFetches = 1;
717
- lastFetchedData = fetchedData;
718
-
719
- // Save the fetched data to localStorage
720
- localStorage.setItem(options.storeKey, JSON.stringify(fetchedData));
721
- }
197
+ function useAuth(options) {
198
+ if (!options.rulesets) {
199
+ throw new Error("No rulesets provided");
200
+ }
722
201
 
723
- if (consecutiveFetches === 3) {
724
- clearTimeout(debounceTimer); // Clear previous timer
725
- debounceTimer = setTimeout(() => {
726
- console.log("Updating state with fetched data:", fetchedData);
727
- setState(fetchedData);
728
- consecutiveFetches = 0;
729
- }, 500); // Adjust the delay time as needed
202
+ let rules = options.rulesets;
203
+ let user = options.user;
204
+
205
+ const auth = {
206
+ /**
207
+ * Check if the user can perform a specific action.
208
+ *
209
+ * @param {string} action - The action to check.
210
+ * @returns {boolean} True if the user can perform the action, false otherwise.
211
+ */
212
+ can: (action) => {
213
+ let can = false;
214
+ rules.forEach((rule) => {
215
+ if (rule.action === action) {
216
+ if (rule.condition(user)) {
217
+ can = true;
218
+ }
219
+ }
220
+ });
221
+ return can;
222
+ },
223
+ /**
224
+ * Check if the user has a specific role.
225
+ *
226
+ * @param {string} role - The role to check.
227
+ * @returns {boolean} True if the user has the role, false otherwise.
228
+ */
229
+ hasRole: (role) => {
230
+ return user.role && user.role.includes(role);
231
+ },
232
+ /**
233
+ * Check if the user can perform a specific action with a specific role.
234
+ *
235
+ * @param {string} action - The action to check.
236
+ * @param {string} role - The role to check.
237
+ * @returns {boolean} True if the user can perform the action with the role, false otherwise.
238
+ */
239
+ canWithRole: (action, role) => {
240
+ return auth.can(action) && auth.hasRole(role);
241
+ },
242
+ /**
243
+ * Assign a new rule to the rulesets.
244
+ *
245
+ * @param {Object} rule - The rule to assign.
246
+ */
247
+ assignRule: (rule) => {
248
+ if (
249
+ !rules.some((existingRule) => existingRule.action === rule.action)
250
+ ) {
251
+ rules.push(rule);
730
252
  }
731
- } catch (error) {
732
- console.error("Error fetching data:", error);
733
- }
253
+ },
254
+ /**
255
+ * Revoke a rule from the rulesets.
256
+ *
257
+ * @param {string} action - The action of the rule to revoke.
258
+ */
259
+ revokeRule: (action) => {
260
+ rules = rules.filter((rule) => rule.action !== action);
261
+ },
262
+ /**
263
+ * Check if the user can perform any of the specified actions.
264
+ *
265
+ * @param {Array} actions - An array of actions to check.
266
+ * @returns {boolean} True if the user can perform any of the actions, false otherwise.
267
+ */
268
+ canAnyOf: (actions) => {
269
+ return actions.some((action) => auth.can(action));
270
+ },
271
+ /**
272
+ * Check if the user can perform all of the specified actions.
273
+ *
274
+ * @param {Array} actions - An array of actions to check.
275
+ * @returns {boolean} True if the user can perform all of the actions, false otherwise.
276
+ */
277
+ canAllOf: (actions) => {
278
+ return actions.every((action) => auth.can(action));
279
+ },
280
+ /**
281
+ * Check if the user can perform a group of actions based on a logical operator.
282
+ *
283
+ * @param {Array} actions - An array of actions to check.
284
+ * @param {string} logicalOperator - The logical operator to use ('any' or 'all').
285
+ * @returns {boolean} True if the user can perform the actions based on the logical operator, false otherwise.
286
+ */
287
+ canGroup: (actions, logicalOperator = "any") => {
288
+ return logicalOperator === "any"
289
+ ? auth.canAnyOf(actions)
290
+ : auth.canAllOf(actions);
291
+ },
734
292
  };
735
293
 
736
- // Load initial state from localStorage
737
- const storedData = localStorage.getItem(options.storeKey);
738
- if (storedData) {
739
- localStorage.setItem(options.storeKey, storedData);
740
- if (!isntHydrated) {
741
- hydrate(isntHydrated);
742
- isntHydrated = true;
743
- }
744
- }
294
+ return auth;
745
295
  }
746
296
 
747
- return {
748
- ...store,
749
- subscribe,
750
- delete: () => {
751
- localStorage.removeItem(options.storeKey);
752
- },
753
- clear: () => {
754
- localStorage.setItem(options.storeKey, "");
755
- },
756
- return: () => {
757
- isntHydrated = false;
758
- },
759
- };
760
- };
761
-
762
- /**
763
- * Create a state management hook that uses a reducer function to update state.
764
- *
765
- * @template S, A
766
- * @param {function(S, A): S} reducer - The reducer function that updates the state.
767
- * @param {S} initialState - The initial state value.
768
- * @returns {[S, function(A): void]} An array containing the current state and a dispatch function.
769
- */
770
- const useReduce = (reducer, initialState) => {
771
297
  /**
772
- * The current state managed by the reducer.
773
- * @type {S}
298
+ * @function runEffects
299
+ * @returns {null}
300
+ * @description Allows you to run side effects
774
301
  */
775
- const [state, setState] = useState(initialState + "state", initialState);
776
-
302
+ const runEffects = () => {
303
+ if (!executedEffects[name] && effects[name]) {
304
+ effects[name].forEach((effectFn) => effectFn());
305
+ executedEffects[name] = true;
306
+ }
307
+ };
308
+ window.useState = useState;
309
+ window.setState = setState;
310
+ window.useEffect = useEffect;
311
+ window.useAuth = useAuth;
312
+ window.useSyncStore = useSyncStore;
313
+ const updateComponent = () => {
314
+ const componentContainer = document.querySelector(
315
+ `[data-component="${name}"]`
316
+ );
317
+ if (componentContainer) {
318
+ runEffects;
319
+
320
+ componentContainer.innerHTML = options.render(
321
+ states,
322
+ (storedProps = null)
323
+ );
324
+ }
325
+ };
777
326
  /**
778
- * Dispatches an action to the reducer to update the state.
779
- *
780
- * @param {A} action - The action to be dispatched.
327
+ * @function render
328
+ * @param {*} states
329
+ * @param {*} props
330
+ * @description Allows you to render component to DOM
331
+ * @returns {HTMLcContent}
332
+ * @returns
781
333
  */
782
- const dispatch = (action) => {
783
- const newState = reducer(state, action);
784
- setState(newState);
785
- };
786
-
787
- return [state, dispatch];
788
- };
789
-
790
- const getState = (statename) => {
791
- const storedState = sessionStorage.getItem(statename);
792
- return storedState ? JSON.parse(storedState) : null;
793
- };
794
- window["getState"] = getState;
795
- window["setState"] = setState;
796
334
 
797
- /**
798
- *
799
- * @param {Function} callback
800
- * @param {Array} dependencies
801
- * @example
802
- * useEffect(() => {
803
- * console.log('state changed');
804
- * }, ['state']);
805
- */
806
- const useEffect = (callback, dependencies) => {
807
- callback();
808
- window.addEventListener("message", (event) => {
809
- if (dependencies.includes(event.data.state)) {
810
- callback(event.data.state);
811
- return;
335
+ const render = (props) => {
336
+ storedProps = props;
337
+ const componentContainer = document.querySelector(
338
+ `[data-component="${name}"]`
339
+ );
340
+ if (componentContainer) {
341
+ runEffects();
342
+
343
+ componentContainer.innerHTML = options.render(
344
+ states,
345
+ (storedProps = null)
346
+ );
347
+ } else {
348
+ return vhtml`
349
+ <div data-component="${name}">
350
+ ${options.render(
351
+ states,
352
+ props
353
+ )}
354
+ </div>
355
+ `;
812
356
  }
813
- });
814
- };
815
-
816
- const register = (template) => {
817
- templates.push(template);
818
- };
819
- /**
820
- *
821
- * @param {string} path
822
- * @param {object} props
823
- * @returns {Promise} - Promise with the obstructed html.
824
- * @example
825
- * const header = await require('./components/header.html');
826
- */
357
+ };
827
358
 
828
- const require = (path, props) => {
829
- const promise = new Promise((resolve, reject) => {
830
- if (cache[path]) {
831
- resolve(cache[path]);
832
- }
833
- if (!path.endsWith(".html")) {
834
- reject("Only html files are supported");
835
- }
836
- fetch(path)
837
- .then((response) => {
838
- return response.text();
839
- })
840
- .then((code) => {
841
- cache[path] = new Function("props", "return " + "`" + code + "`");
842
- hydrate();
843
- resolve(cache[path]);
844
- });
845
- });
846
- return promise;
847
- };
848
- window["require"] = require;
359
+ return {
360
+ render,
361
+ };
362
+ }
849
363
 
850
364
  /**
851
- * @typedef {Object} $s
852
- * @property {object} styles - Object with css properties.
853
- * @returns {string} - String with css properties.
854
- * @example
855
- * $s({ color: 'red' })
365
+ * @function rf
366
+ * @param {*} name
367
+ * @param {*} fn
368
+ * @returns {null}
369
+ * @description Allows you to register function in global scope
856
370
  */
857
- function $s(styles = {}) {
858
- let result = "";
859
- for (let key in styles) {
860
- result += `${key}: ${styles[key]};`;
861
- }
862
- return `style="${result}"`;
863
- }
864
-
865
- window.onbeforeunload = function () {
866
- sessionStorage.clear();
371
+ export const rf = (name, fn) => {
372
+ window[name] = fn;
867
373
  };
package/vaderRouter.js CHANGED
@@ -206,6 +206,9 @@ class VaderRouter {
206
206
  return: function (data) {
207
207
  this.hooked = false;
208
208
  },
209
+ render: function (selector, data) {
210
+ document.querySelector(selector).innerHTML = data;
211
+ },
209
212
  };
210
213
 
211
214
  callback(req, res);
@@ -351,6 +354,14 @@ class VaderRouter {
351
354
  url: window.location.hash.substring(1),
352
355
  method: "POST",
353
356
  };
357
+ const res = {
358
+ return: function (data) {
359
+ this.hooked = false;
360
+ },
361
+ render: function (selector, data) {
362
+ document.querySelector(selector).innerHTML = data;
363
+ },
364
+ };
354
365
  window.$URL_QUERY = query;
355
366
  window.$URL_PARAMS = params;
356
367
 
@@ -362,7 +373,7 @@ class VaderRouter {
362
373
  * @memberof VaderRouter
363
374
  * @description Allows you to perform actions when the currentRoute changes.
364
375
  */
365
- callback(req);
376
+ callback(req, res);
366
377
  }
367
378
  });
368
379
  }