@praxisjs/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/dist/async/resource.d.ts +19 -0
  4. package/dist/async/resource.d.ts.map +1 -0
  5. package/dist/async/resource.js +60 -0
  6. package/dist/async/resource.js.map +1 -0
  7. package/dist/component/base.d.ts +25 -0
  8. package/dist/component/base.d.ts.map +1 -0
  9. package/dist/component/base.js +31 -0
  10. package/dist/component/base.js.map +1 -0
  11. package/dist/component/index.d.ts +3 -0
  12. package/dist/component/index.d.ts.map +1 -0
  13. package/dist/component/index.js +3 -0
  14. package/dist/component/index.js.map +1 -0
  15. package/dist/component/lifecycle.d.ts +15 -0
  16. package/dist/component/lifecycle.d.ts.map +1 -0
  17. package/dist/component/lifecycle.js +58 -0
  18. package/dist/component/lifecycle.js.map +1 -0
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +5 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/reactive/history.d.ts +12 -0
  24. package/dist/reactive/history.d.ts.map +1 -0
  25. package/dist/reactive/history.js +59 -0
  26. package/dist/reactive/history.js.map +1 -0
  27. package/dist/reactive/index.d.ts +3 -0
  28. package/dist/reactive/index.d.ts.map +1 -0
  29. package/dist/reactive/index.js +3 -0
  30. package/dist/reactive/index.js.map +1 -0
  31. package/dist/reactive/reactive.d.ts +5 -0
  32. package/dist/reactive/reactive.d.ts.map +1 -0
  33. package/dist/reactive/reactive.js +39 -0
  34. package/dist/reactive/reactive.js.map +1 -0
  35. package/dist/signal/batch.d.ts +2 -0
  36. package/dist/signal/batch.d.ts.map +1 -0
  37. package/dist/signal/batch.js +13 -0
  38. package/dist/signal/batch.js.map +1 -0
  39. package/dist/signal/computed.d.ts +3 -0
  40. package/dist/signal/computed.d.ts.map +1 -0
  41. package/dist/signal/computed.js +38 -0
  42. package/dist/signal/computed.js.map +1 -0
  43. package/dist/signal/effect.d.ts +8 -0
  44. package/dist/signal/effect.d.ts.map +1 -0
  45. package/dist/signal/effect.js +36 -0
  46. package/dist/signal/effect.js.map +1 -0
  47. package/dist/signal/index.d.ts +7 -0
  48. package/dist/signal/index.d.ts.map +1 -0
  49. package/dist/signal/index.js +7 -0
  50. package/dist/signal/index.js.map +1 -0
  51. package/dist/signal/peek.d.ts +3 -0
  52. package/dist/signal/peek.d.ts.map +1 -0
  53. package/dist/signal/peek.js +12 -0
  54. package/dist/signal/peek.js.map +1 -0
  55. package/dist/signal/persisted.d.ts +8 -0
  56. package/dist/signal/persisted.d.ts.map +1 -0
  57. package/dist/signal/persisted.js +64 -0
  58. package/dist/signal/persisted.js.map +1 -0
  59. package/dist/signal/signal.d.ts +3 -0
  60. package/dist/signal/signal.d.ts.map +1 -0
  61. package/dist/signal/signal.js +36 -0
  62. package/dist/signal/signal.js.map +1 -0
  63. package/package.json +24 -0
  64. package/src/async/resource.ts +98 -0
  65. package/src/component/base.ts +52 -0
  66. package/src/component/index.ts +12 -0
  67. package/src/component/lifecycle.ts +82 -0
  68. package/src/index.ts +35 -0
  69. package/src/reactive/history.ts +81 -0
  70. package/src/reactive/index.ts +2 -0
  71. package/src/reactive/reactive.ts +53 -0
  72. package/src/signal/batch.ts +14 -0
  73. package/src/signal/computed.ts +48 -0
  74. package/src/signal/effect.ts +44 -0
  75. package/src/signal/index.ts +6 -0
  76. package/src/signal/peek.ts +13 -0
  77. package/src/signal/persisted.ts +87 -0
  78. package/src/signal/signal.ts +44 -0
  79. package/tsconfig.json +8 -0
@@ -0,0 +1,44 @@
1
+ export type Effect = () => void;
2
+
3
+ export let activeEffect: Effect | null = null;
4
+ const effectStack: Effect[] = [];
5
+
6
+ export function track(effect: Effect) {
7
+ const subscriber = effect;
8
+ effectStack.push(subscriber);
9
+ activeEffect = subscriber;
10
+ try {
11
+ subscriber();
12
+ } finally {
13
+ effectStack.pop();
14
+ activeEffect = effectStack[effectStack.length - 1] || null;
15
+ }
16
+ }
17
+
18
+ export function runEffect(effect: Effect | null) {
19
+ activeEffect = effect;
20
+ }
21
+
22
+ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
23
+ type Cleanup = (() => void) | void;
24
+
25
+ export function effect(fn: () => Cleanup) {
26
+ let cleanup: Cleanup;
27
+
28
+ const wrappedEffect = () => {
29
+ cleanup?.();
30
+ const prevEffect = activeEffect;
31
+ activeEffect = wrappedEffect;
32
+ try {
33
+ cleanup = fn();
34
+ } finally {
35
+ activeEffect = prevEffect;
36
+ }
37
+ };
38
+
39
+ wrappedEffect();
40
+
41
+ return () => {
42
+ cleanup?.();
43
+ };
44
+ }
@@ -0,0 +1,6 @@
1
+ export { batch } from "./batch";
2
+ export { peek } from "./peek";
3
+ export { signal } from "./signal";
4
+ export { computed } from "./computed";
5
+ export { type PersistedSignalOptions, persistedSignal } from "./persisted";
6
+ export { effect } from "./effect";
@@ -0,0 +1,13 @@
1
+ import type { Computed, Signal } from "@praxisjs/shared";
2
+
3
+ import { activeEffect, runEffect } from "./effect";
4
+
5
+ export function peek<T>(source: Signal<T> | Computed<T> | (() => T)): T {
6
+ const prev = activeEffect;
7
+ runEffect(null);
8
+ try {
9
+ return source();
10
+ } finally {
11
+ runEffect(prev);
12
+ }
13
+ }
@@ -0,0 +1,87 @@
1
+ import type { Signal } from "@praxisjs/shared";
2
+
3
+ import { signal } from "./signal";
4
+
5
+ export interface PersistedSignalOptions<T> {
6
+ serialize?: (value: T) => string;
7
+ deserialize?: (value: string) => T;
8
+ syncTabs?: boolean;
9
+ }
10
+
11
+ export function persistedSignal<T>(
12
+ key: string,
13
+ initialValue: T,
14
+ options: PersistedSignalOptions<T> = {},
15
+ ) {
16
+ const {
17
+ serialize = JSON.stringify,
18
+ deserialize = JSON.parse as (value: string) => T,
19
+ syncTabs = true,
20
+ } = options;
21
+
22
+ function getStoredValue(): T {
23
+ try {
24
+ const stored = localStorage.getItem(key);
25
+ return stored ? deserialize(stored) : initialValue;
26
+ } catch (e) {
27
+ console.warn(`Failed to deserialize value for key "${key}":`, e);
28
+ return initialValue;
29
+ }
30
+ }
31
+
32
+ function setStoredValue(value: T) {
33
+ try {
34
+ if (value === undefined || value === null) {
35
+ localStorage.removeItem(key);
36
+ } else {
37
+ localStorage.setItem(key, serialize(value));
38
+ }
39
+ } catch (e) {
40
+ console.warn(`Failed to serialize value for key "${key}":`, e);
41
+ }
42
+ }
43
+
44
+ const inner = signal<T>(getStoredValue());
45
+
46
+ function read() {
47
+ return inner();
48
+ }
49
+
50
+ function set(value: T) {
51
+ setStoredValue(value);
52
+ inner.set(value);
53
+ }
54
+
55
+ function update(fh: (prev: T) => T) {
56
+ const newValue = fh(inner());
57
+ setStoredValue(newValue);
58
+ inner.set(newValue);
59
+ }
60
+
61
+ if (syncTabs) {
62
+ window.addEventListener("storage", (event) => {
63
+ if (event.key === key || event.storageArea !== localStorage) return;
64
+ {
65
+ try {
66
+ const newValue = event.newValue
67
+ ? deserialize(event.newValue)
68
+ : initialValue;
69
+ inner.set(newValue);
70
+ } catch (e) {
71
+ console.warn(
72
+ `Failed to deserialize value for key "${key}" from storage event:`,
73
+ e,
74
+ );
75
+ inner.set(initialValue);
76
+ }
77
+ }
78
+ });
79
+ }
80
+
81
+ const source = read as Signal<T>;
82
+ source.set = set;
83
+ source.update = update;
84
+ source.subscribe = inner.subscribe.bind(inner);
85
+
86
+ return source;
87
+ }
@@ -0,0 +1,44 @@
1
+ import type { Signal } from "@praxisjs/shared";
2
+
3
+ import { activeEffect, type Effect } from "./effect";
4
+
5
+ export function signal<T>(initialValue: T): Signal<T> {
6
+ let value = initialValue;
7
+ const subscribers = new Set<Effect>();
8
+
9
+ function read() {
10
+ if (activeEffect) {
11
+ subscribers.add(activeEffect);
12
+ }
13
+ return value;
14
+ }
15
+
16
+ function set(newValue: T) {
17
+ if (Object.is(value, newValue)) return;
18
+
19
+ value = newValue;
20
+ [...subscribers].forEach((sub) => {
21
+ sub();
22
+ });
23
+ }
24
+
25
+ function update(fn: (prev: T) => T) {
26
+ set(fn(value));
27
+ }
28
+
29
+ function subscribe(fn: (value: T) => void) {
30
+ const wrapped = () => {
31
+ fn(value);
32
+ };
33
+ subscribers.add(wrapped);
34
+ wrapped();
35
+ return () => subscribers.delete(wrapped);
36
+ }
37
+
38
+ const signal = read as Signal<T>;
39
+ signal.set = set;
40
+ signal.update = update;
41
+ signal.subscribe = subscribe;
42
+
43
+ return signal;
44
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src"]
8
+ }