rask-ui 0.21.0 → 0.22.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 (109) hide show
  1. package/dist/asyncState.d.ts +16 -0
  2. package/dist/asyncState.d.ts.map +1 -0
  3. package/dist/asyncState.js +24 -0
  4. package/dist/component.d.ts.map +1 -1
  5. package/dist/component.js +4 -1
  6. package/dist/context.d.ts +5 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +29 -0
  9. package/dist/createAsync.test.d.ts +2 -0
  10. package/dist/createAsync.test.d.ts.map +1 -0
  11. package/dist/createAsync.test.js +110 -0
  12. package/dist/createContext.d.ts +20 -21
  13. package/dist/createContext.d.ts.map +1 -1
  14. package/dist/createContext.js +29 -25
  15. package/dist/createMutation.test.d.ts +2 -0
  16. package/dist/createMutation.test.d.ts.map +1 -0
  17. package/dist/createMutation.test.js +168 -0
  18. package/dist/createQuery.test.d.ts +2 -0
  19. package/dist/createQuery.test.d.ts.map +1 -0
  20. package/dist/createQuery.test.js +156 -0
  21. package/dist/createRef.d.ts +6 -0
  22. package/dist/createRef.d.ts.map +1 -0
  23. package/dist/createRef.js +8 -0
  24. package/dist/createState.d.ts +0 -2
  25. package/dist/createState.d.ts.map +1 -1
  26. package/dist/createState.js +5 -40
  27. package/dist/createState.test.d.ts.map +1 -0
  28. package/dist/createState.test.js +111 -0
  29. package/dist/createView.d.ts +44 -18
  30. package/dist/createView.d.ts.map +1 -1
  31. package/dist/createView.js +48 -57
  32. package/dist/createView.test.d.ts.map +1 -0
  33. package/dist/{tests/createView.test.js → createView.test.js} +40 -40
  34. package/dist/error.d.ts +14 -3
  35. package/dist/error.d.ts.map +1 -1
  36. package/dist/error.js +15 -14
  37. package/dist/jsx.d.ts +256 -10
  38. package/dist/observation.test.d.ts.map +1 -0
  39. package/dist/observation.test.js +150 -0
  40. package/dist/suspense.d.ts +25 -0
  41. package/dist/suspense.d.ts.map +1 -0
  42. package/dist/suspense.js +97 -0
  43. package/dist/test-setup.d.ts +16 -0
  44. package/dist/test-setup.d.ts.map +1 -0
  45. package/dist/test-setup.js +40 -0
  46. package/dist/test.d.ts +2 -0
  47. package/dist/test.d.ts.map +1 -0
  48. package/dist/test.js +24 -0
  49. package/dist/useCatchError.d.ts +3 -1
  50. package/dist/useCatchError.d.ts.map +1 -1
  51. package/dist/useCatchError.js +4 -3
  52. package/package.json +2 -2
  53. package/swc-plugin/target/wasm32-wasip1/release/swc_plugin_rask_component.wasm +0 -0
  54. package/dist/createComputed.d.ts +0 -4
  55. package/dist/createComputed.d.ts.map +0 -1
  56. package/dist/createComputed.js +0 -69
  57. package/dist/createEffect.d.ts +0 -2
  58. package/dist/createEffect.d.ts.map +0 -1
  59. package/dist/createEffect.js +0 -29
  60. package/dist/createRouter.d.ts +0 -8
  61. package/dist/createRouter.d.ts.map +0 -1
  62. package/dist/createRouter.js +0 -27
  63. package/dist/createTask.d.ts +0 -31
  64. package/dist/createTask.d.ts.map +0 -1
  65. package/dist/createTask.js +0 -79
  66. package/dist/patchInferno.d.ts +0 -6
  67. package/dist/patchInferno.d.ts.map +0 -1
  68. package/dist/patchInferno.js +0 -53
  69. package/dist/scheduler.d.ts +0 -4
  70. package/dist/scheduler.d.ts.map +0 -1
  71. package/dist/scheduler.js +0 -107
  72. package/dist/tests/batch.test.d.ts +0 -2
  73. package/dist/tests/batch.test.d.ts.map +0 -1
  74. package/dist/tests/batch.test.js +0 -244
  75. package/dist/tests/createComputed.test.d.ts +0 -2
  76. package/dist/tests/createComputed.test.d.ts.map +0 -1
  77. package/dist/tests/createComputed.test.js +0 -257
  78. package/dist/tests/createContext.test.d.ts +0 -2
  79. package/dist/tests/createContext.test.d.ts.map +0 -1
  80. package/dist/tests/createContext.test.js +0 -136
  81. package/dist/tests/createEffect.test.d.ts +0 -2
  82. package/dist/tests/createEffect.test.d.ts.map +0 -1
  83. package/dist/tests/createEffect.test.js +0 -467
  84. package/dist/tests/createState.test.d.ts.map +0 -1
  85. package/dist/tests/createState.test.js +0 -144
  86. package/dist/tests/createTask.test.d.ts +0 -2
  87. package/dist/tests/createTask.test.d.ts.map +0 -1
  88. package/dist/tests/createTask.test.js +0 -322
  89. package/dist/tests/createView.test.d.ts.map +0 -1
  90. package/dist/tests/error.test.d.ts +0 -2
  91. package/dist/tests/error.test.d.ts.map +0 -1
  92. package/dist/tests/error.test.js +0 -168
  93. package/dist/tests/observation.test.d.ts.map +0 -1
  94. package/dist/tests/observation.test.js +0 -341
  95. package/dist/useComputed.d.ts +0 -5
  96. package/dist/useComputed.d.ts.map +0 -1
  97. package/dist/useComputed.js +0 -69
  98. package/dist/useQuery.d.ts +0 -25
  99. package/dist/useQuery.d.ts.map +0 -1
  100. package/dist/useQuery.js +0 -25
  101. package/dist/useSuspendAsync.d.ts +0 -18
  102. package/dist/useSuspendAsync.d.ts.map +0 -1
  103. package/dist/useSuspendAsync.js +0 -37
  104. package/dist/useTask.d.ts +0 -25
  105. package/dist/useTask.d.ts.map +0 -1
  106. package/dist/useTask.js +0 -70
  107. /package/dist/{tests/createState.test.d.ts → createState.test.d.ts} +0 -0
  108. /package/dist/{tests/createView.test.d.ts → createView.test.d.ts} +0 -0
  109. /package/dist/{tests/observation.test.d.ts → observation.test.d.ts} +0 -0
@@ -0,0 +1,16 @@
1
+ type AsyncState<T> = {
2
+ isPending: true;
3
+ value: null;
4
+ error: null;
5
+ } | {
6
+ isPending: false;
7
+ value: T;
8
+ error: null;
9
+ } | {
10
+ isPending: false;
11
+ value: null;
12
+ error: string;
13
+ };
14
+ export declare function createAsyncState<T>(promise: Promise<T>): AsyncState<T>;
15
+ export {};
16
+ //# sourceMappingURL=asyncState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncState.d.ts","sourceRoot":"","sources":["../src/asyncState.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,CAAC,CAAC,IACb;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iBAwBtD"}
@@ -0,0 +1,24 @@
1
+ import { createState } from "./createState";
2
+ export function createAsyncState(promise) {
3
+ const state = createState({
4
+ isPending: true,
5
+ error: null,
6
+ value: null,
7
+ });
8
+ promise
9
+ .then((value) => {
10
+ Object.assign(state, {
11
+ value,
12
+ error: null,
13
+ isPending: false,
14
+ });
15
+ })
16
+ .catch((error) => {
17
+ Object.assign(state, {
18
+ value: null,
19
+ error: String(error),
20
+ isPending: false,
21
+ });
22
+ });
23
+ return state;
24
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EACL,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAIrE,MAAM,MAAM,8BAA8B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC3D,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1B,qBAAa,sBAAuB,SAAQ,SAAS;IAC3C,QAAQ,EAAE,8BAA8B,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,QAAQ,WAML;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAC1C,OAAO,CAAC,aAAa,CAAc;IAEnC,qBAAqB,IAAI,OAAO;IAMhC,kBAAkB,IAAI,IAAI;IAG1B,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI;IAe/C,MAAM;CAoBP;AAID,wBAAgB,mBAAmB,2CAElC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,QAM5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,QAMxC;AAED,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC1D,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAC1C,OAAO,CAAC,aAAa,CAAc;IAwBnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,QAAQ,WAOL;IAEH,WAAW,UAAS;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IAEnC,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAI5B,yBAAyB,CACvB,SAAS,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,CAAC,CAAC,GAClD,IAAI;IAcP,qBAAqB,IAAI,OAAO;IAMhC,MAAM;CA6CP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EACL,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAIrE,MAAM,MAAM,8BAA8B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC3D,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1B,qBAAa,sBAAuB,SAAQ,SAAS;IAC3C,QAAQ,EAAE,8BAA8B,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,QAAQ,WAML;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAC1C,OAAO,CAAC,aAAa,CAAc;IAEnC,qBAAqB,IAAI,OAAO;IAMhC,kBAAkB,IAAI,IAAI;IAG1B,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI;IAe/C,MAAM;CAoBP;AAID,wBAAgB,mBAAmB,2CAElC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,QAM5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,QAMxC;AAED,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC1D,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAC1C,OAAO,CAAC,aAAa,CAAc;IAwBnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAE9B,QAAQ,WAOL;IAEH,WAAW,UAAS;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAcf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IAEnC,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAI5B,yBAAyB,CACvB,SAAS,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,CAAC,CAAC,GAClD,IAAI;IAcP,qBAAqB,IAAI,OAAO;IAMhC,MAAM;CA6CP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
package/dist/component.js CHANGED
@@ -112,7 +112,10 @@ export class RaskStatefulComponent extends Component {
112
112
  effects = [];
113
113
  contexts = new Map();
114
114
  getChildContext() {
115
- const parentGetContext = this.context.getContext;
115
+ const parentGetContext = this.context.getContext ||
116
+ (() => {
117
+ throw new Error("No context available");
118
+ });
116
119
  return {
117
120
  ...this.context,
118
121
  getContext: (context) => {
@@ -0,0 +1,5 @@
1
+ export declare function createContext<T extends object>(): {
2
+ set(value: T): void;
3
+ get(): T;
4
+ };
5
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM;eAE/B,CAAC;WAaL,CAAC;EAmBX"}
@@ -0,0 +1,29 @@
1
+ import { getCurrentComponent } from "./component";
2
+ export function createContext() {
3
+ const context = {
4
+ set(value) {
5
+ const currentComponent = getCurrentComponent();
6
+ if (!currentComponent) {
7
+ throw new Error("You can not set context out component setup");
8
+ }
9
+ if (!currentComponent.contexts) {
10
+ currentComponent.contexts = new Map();
11
+ }
12
+ currentComponent.contexts.set(context, value);
13
+ },
14
+ get() {
15
+ let currentComponent = getCurrentComponent();
16
+ if (!currentComponent) {
17
+ throw new Error("You can not set context out component setup");
18
+ }
19
+ while (currentComponent) {
20
+ if (currentComponent.contexts?.has(context)) {
21
+ return currentComponent.contexts.get(context);
22
+ }
23
+ currentComponent = currentComponent.parent;
24
+ }
25
+ throw new Error("Could not find context in parent components");
26
+ },
27
+ };
28
+ return context;
29
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createAsync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createAsync.test.d.ts","sourceRoot":"","sources":["../src/createAsync.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createAsync } from './createAsync';
3
+ describe('createAsync', () => {
4
+ it('should start in pending state', () => {
5
+ const promise = new Promise(() => { });
6
+ const async = createAsync(promise);
7
+ expect(async.isPending).toBe(true);
8
+ expect(async.value).toBeNull();
9
+ expect(async.error).toBeNull();
10
+ });
11
+ it('should resolve to value state on success', async () => {
12
+ const promise = Promise.resolve('success');
13
+ const async = createAsync(promise);
14
+ expect(async.isPending).toBe(true);
15
+ await promise;
16
+ // Wait for state update
17
+ await new Promise((resolve) => setTimeout(resolve, 0));
18
+ expect(async.isPending).toBe(false);
19
+ expect(async.value).toBe('success');
20
+ expect(async.error).toBeNull();
21
+ });
22
+ it('should resolve to error state on rejection', async () => {
23
+ const promise = Promise.reject(new Error('failed'));
24
+ const async = createAsync(promise);
25
+ expect(async.isPending).toBe(true);
26
+ try {
27
+ await promise;
28
+ }
29
+ catch {
30
+ // Ignore
31
+ }
32
+ // Wait for state update
33
+ await new Promise((resolve) => setTimeout(resolve, 0));
34
+ expect(async.isPending).toBe(false);
35
+ expect(async.value).toBeNull();
36
+ expect(async.error).toContain('failed');
37
+ });
38
+ it('should handle numeric values', async () => {
39
+ const promise = Promise.resolve(42);
40
+ const async = createAsync(promise);
41
+ await promise;
42
+ await new Promise((resolve) => setTimeout(resolve, 0));
43
+ expect(async.value).toBe(42);
44
+ });
45
+ it('should handle object values', async () => {
46
+ const data = { id: 1, name: 'Test' };
47
+ const promise = Promise.resolve(data);
48
+ const async = createAsync(promise);
49
+ await promise;
50
+ await new Promise((resolve) => setTimeout(resolve, 0));
51
+ expect(async.value).toEqual(data);
52
+ });
53
+ it('should handle array values', async () => {
54
+ const data = [1, 2, 3, 4, 5];
55
+ const promise = Promise.resolve(data);
56
+ const async = createAsync(promise);
57
+ await promise;
58
+ await new Promise((resolve) => setTimeout(resolve, 0));
59
+ expect(async.value).toEqual(data);
60
+ });
61
+ it('should convert error to string', async () => {
62
+ const promise = Promise.reject('string error');
63
+ const async = createAsync(promise);
64
+ try {
65
+ await promise;
66
+ }
67
+ catch {
68
+ // Ignore
69
+ }
70
+ await new Promise((resolve) => setTimeout(resolve, 0));
71
+ expect(typeof async.error).toBe('string');
72
+ expect(async.error).toBe('string error');
73
+ });
74
+ it('should handle error objects', async () => {
75
+ const error = new Error('Something went wrong');
76
+ const promise = Promise.reject(error);
77
+ const async = createAsync(promise);
78
+ try {
79
+ await promise;
80
+ }
81
+ catch {
82
+ // Ignore
83
+ }
84
+ await new Promise((resolve) => setTimeout(resolve, 0));
85
+ expect(async.error).toContain('Something went wrong');
86
+ });
87
+ it('should create reactive state', async () => {
88
+ const promise = new Promise((resolve) => {
89
+ setTimeout(() => resolve('delayed'), 10);
90
+ });
91
+ const async = createAsync(promise);
92
+ expect(async.isPending).toBe(true);
93
+ await promise;
94
+ await new Promise((resolve) => setTimeout(resolve, 20));
95
+ expect(async.isPending).toBe(false);
96
+ expect(async.value).toBe('delayed');
97
+ });
98
+ it('should handle immediate resolution', async () => {
99
+ const async = createAsync(Promise.resolve('immediate'));
100
+ await new Promise((resolve) => setTimeout(resolve, 0));
101
+ expect(async.isPending).toBe(false);
102
+ expect(async.value).toBe('immediate');
103
+ });
104
+ it('should handle immediate rejection', async () => {
105
+ const async = createAsync(Promise.reject('immediate error'));
106
+ await new Promise((resolve) => setTimeout(resolve, 0));
107
+ expect(async.isPending).toBe(false);
108
+ expect(async.error).toBe('immediate error');
109
+ });
110
+ });
@@ -1,33 +1,32 @@
1
1
  /**
2
- * Creates a context object for providing and consuming values across component trees.
2
+ * Creates a context by wrapping a hook function that will be used as a context identifier.
3
3
  *
4
- * @warning **Do not destructure context values returned by context.get()!** The returned
5
- * value may be a reactive object, and destructuring breaks reactivity.
4
+ * @warning **Do not destructure context values!** The returned value may be a reactive
5
+ * object, and destructuring breaks reactivity.
6
6
  *
7
7
  * @example
8
- * // Bad - destructuring context value
9
- * const ThemeContext = createContext<{ color: string }>();
8
+ * // Create a context hook
9
+ * const useTheme = createContext(() => {
10
+ * return useState({ color: "blue" });
11
+ * });
10
12
  *
11
- * function Consumer() {
12
- * const theme = ThemeContext.get();
13
- * const { color } = theme; // Don't do this!
14
- * return () => <div style={{ color }}>Text</div>; // Won't update!
13
+ * // Provider component
14
+ * function App() {
15
+ * const theme = useInjectContext(useTheme);
16
+ * // theme is now available to all children
17
+ * return () => <Child />;
15
18
  * }
16
19
  *
17
- * // Good - access properties directly
18
- * function Consumer() {
19
- * const theme = ThemeContext.get();
20
+ * // Consumer component
21
+ * function Child() {
22
+ * const theme = useContext(useTheme);
20
23
  * return () => <div style={{ color: theme.color }}>Text</div>;
21
24
  * }
22
25
  *
23
- * @returns Context object with inject() and get() methods
26
+ * @param hook - A function that will be used as the context identifier
27
+ * @returns The same hook function, to be used with useContext() and useInjectContext()
24
28
  */
25
- declare const Type: unique symbol;
26
- export type Context<T> = symbol & {
27
- readonly [Type]: T;
28
- };
29
- export declare function createContext<T>(): Context<T>;
30
- export declare function useContext<T>(context: Context<T>): T;
31
- export declare function useInjectContext<T>(context: Context<T>): (value: T) => void;
32
- export {};
29
+ export declare function createContext<T, P extends any[]>(hook: (...params: P) => T): (...params: P) => T;
30
+ export declare function useContext<T, P extends any[]>(hook: (...params: P) => T): T;
31
+ export declare function useInjectContext<T, P extends any[]>(hook: (...params: P) => T, ...params: P): T;
33
32
  //# sourceMappingURL=createContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createContext.d.ts","sourceRoot":"","sources":["../src/createContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,CAAC,MAAM,IAAI,EAAE,OAAO,MAAM,CAAC;AAElC,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,MAAM,GAAG;IAChC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;CACpB,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,KACV,OAAO,CAAC,CAAC,CAAC,CAC9B;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAkBpD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAC7C,OAAO,CAAC,UASjB"}
1
+ {"version":3,"file":"createContext.d.ts","sourceRoot":"","sources":["../src/createContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,eAAP,CAAC,KAAK,CAAC,CAE1E;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,SAAS,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAkB3E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,GAAG,EAAE,EACjD,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,EACzB,GAAG,MAAM,EAAE,CAAC,KAab"}
@@ -1,32 +1,36 @@
1
1
  /**
2
- * Creates a context object for providing and consuming values across component trees.
2
+ * Creates a context by wrapping a hook function that will be used as a context identifier.
3
3
  *
4
- * @warning **Do not destructure context values returned by context.get()!** The returned
5
- * value may be a reactive object, and destructuring breaks reactivity.
4
+ * @warning **Do not destructure context values!** The returned value may be a reactive
5
+ * object, and destructuring breaks reactivity.
6
6
  *
7
7
  * @example
8
- * // Bad - destructuring context value
9
- * const ThemeContext = createContext<{ color: string }>();
8
+ * // Create a context hook
9
+ * const useTheme = createContext(() => {
10
+ * return useState({ color: "blue" });
11
+ * });
10
12
  *
11
- * function Consumer() {
12
- * const theme = ThemeContext.get();
13
- * const { color } = theme; // Don't do this!
14
- * return () => <div style={{ color }}>Text</div>; // Won't update!
13
+ * // Provider component
14
+ * function App() {
15
+ * const theme = useInjectContext(useTheme);
16
+ * // theme is now available to all children
17
+ * return () => <Child />;
15
18
  * }
16
19
  *
17
- * // Good - access properties directly
18
- * function Consumer() {
19
- * const theme = ThemeContext.get();
20
+ * // Consumer component
21
+ * function Child() {
22
+ * const theme = useContext(useTheme);
20
23
  * return () => <div style={{ color: theme.color }}>Text</div>;
21
24
  * }
22
25
  *
23
- * @returns Context object with inject() and get() methods
26
+ * @param hook - A function that will be used as the context identifier
27
+ * @returns The same hook function, to be used with useContext() and useInjectContext()
24
28
  */
25
29
  import { getCurrentComponent } from "./component";
26
- export function createContext() {
27
- return Symbol();
30
+ export function createContext(hook) {
31
+ return hook;
28
32
  }
29
- export function useContext(context) {
33
+ export function useContext(hook) {
30
34
  let currentComponent = getCurrentComponent();
31
35
  if (!currentComponent) {
32
36
  throw new Error("Only use useContext in component setup");
@@ -34,18 +38,18 @@ export function useContext(context) {
34
38
  if (typeof currentComponent.context.getContext !== "function") {
35
39
  throw new Error("There is no parent context");
36
40
  }
37
- const contextValue = currentComponent.context.getContext(context);
41
+ const contextValue = currentComponent.context.getContext(hook);
38
42
  if (!contextValue) {
39
43
  throw new Error("There is a parent context, but not the one you are using");
40
44
  }
41
45
  return contextValue;
42
46
  }
43
- export function useInjectContext(context) {
44
- return (value) => {
45
- const currentComponent = getCurrentComponent();
46
- if (!currentComponent) {
47
- throw new Error("Only use useInjectContext in component setup");
48
- }
49
- currentComponent.contexts.set(context, value);
50
- };
47
+ export function useInjectContext(hook, ...params) {
48
+ const currentComponent = getCurrentComponent();
49
+ if (!currentComponent) {
50
+ throw new Error("Only use useInjectContext in component setup");
51
+ }
52
+ const value = hook(...params);
53
+ currentComponent.contexts.set(hook, value);
54
+ return value;
51
55
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createMutation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMutation.test.d.ts","sourceRoot":"","sources":["../src/createMutation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,168 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { createMutation } from './createMutation';
3
+ describe('createMutation', () => {
4
+ it('should start in idle state', () => {
5
+ const mutator = vi.fn(() => Promise.resolve(null));
6
+ const mutation = createMutation(mutator);
7
+ expect(mutation.isPending).toBe(false);
8
+ expect(mutation.params).toBeNull();
9
+ expect(mutation.error).toBeNull();
10
+ expect(mutator).not.toHaveBeenCalled();
11
+ });
12
+ it('should execute mutator when mutate is called', async () => {
13
+ const mutator = vi.fn((params) => Promise.resolve(params));
14
+ const mutation = createMutation(mutator);
15
+ mutation.mutate('test');
16
+ expect(mutation.isPending).toBe(true);
17
+ expect(mutation.params).toBe('test');
18
+ expect(mutator).toHaveBeenCalledWith('test');
19
+ await new Promise((resolve) => setTimeout(resolve, 10));
20
+ expect(mutation.isPending).toBe(false);
21
+ expect(mutation.params).toBeNull();
22
+ });
23
+ it('should handle successful mutations', async () => {
24
+ const mutator = (params) => Promise.resolve(params);
25
+ const mutation = createMutation(mutator);
26
+ mutation.mutate({ id: 1 });
27
+ expect(mutation.isPending).toBe(true);
28
+ await new Promise((resolve) => setTimeout(resolve, 10));
29
+ expect(mutation.isPending).toBe(false);
30
+ expect(mutation.error).toBeNull();
31
+ expect(mutation.params).toBeNull();
32
+ });
33
+ it('should handle mutation errors', async () => {
34
+ const mutator = (params) => Promise.reject(new Error('Mutation failed'));
35
+ const mutation = createMutation(mutator);
36
+ mutation.mutate('test');
37
+ expect(mutation.isPending).toBe(true);
38
+ await new Promise((resolve) => setTimeout(resolve, 10));
39
+ expect(mutation.isPending).toBe(false);
40
+ expect(mutation.error).toContain('Mutation failed');
41
+ expect(mutation.params).toBeNull();
42
+ });
43
+ it('should cancel previous mutation on new mutate call', async () => {
44
+ let resolveFirst;
45
+ let resolveSecond;
46
+ const firstPromise = new Promise((resolve) => {
47
+ resolveFirst = resolve;
48
+ });
49
+ const secondPromise = new Promise((resolve) => {
50
+ resolveSecond = resolve;
51
+ });
52
+ const mutator = vi
53
+ .fn()
54
+ .mockReturnValueOnce(firstPromise)
55
+ .mockReturnValueOnce(secondPromise);
56
+ const mutation = createMutation(mutator);
57
+ mutation.mutate('first');
58
+ expect(mutation.params).toBe('first');
59
+ // Trigger second mutation before first completes
60
+ mutation.mutate('second');
61
+ expect(mutation.params).toBe('second');
62
+ // Resolve first (should be ignored due to cancellation)
63
+ resolveFirst('first');
64
+ await new Promise((resolve) => setTimeout(resolve, 10));
65
+ expect(mutation.isPending).toBe(true); // Still pending second
66
+ // Resolve second
67
+ resolveSecond('second');
68
+ await new Promise((resolve) => setTimeout(resolve, 10));
69
+ expect(mutation.isPending).toBe(false);
70
+ expect(mutation.params).toBeNull();
71
+ });
72
+ it('should handle rapid successive mutations', async () => {
73
+ let counter = 0;
74
+ const mutator = vi.fn(() => Promise.resolve(++counter));
75
+ const mutation = createMutation(mutator);
76
+ // Rapid mutations
77
+ mutation.mutate('1');
78
+ mutation.mutate('2');
79
+ mutation.mutate('3');
80
+ await new Promise((resolve) => setTimeout(resolve, 20));
81
+ // Only the last mutation should complete
82
+ expect(mutator).toHaveBeenCalledTimes(3);
83
+ expect(mutation.isPending).toBe(false);
84
+ expect(mutation.params).toBeNull();
85
+ });
86
+ it('should clear error on successful retry', async () => {
87
+ const mutator = vi
88
+ .fn()
89
+ .mockRejectedValueOnce(new Error('First error'))
90
+ .mockResolvedValueOnce('success');
91
+ const mutation = createMutation(mutator);
92
+ mutation.mutate('attempt1');
93
+ await new Promise((resolve) => setTimeout(resolve, 10));
94
+ expect(mutation.error).toContain('First error');
95
+ mutation.mutate('attempt2');
96
+ await new Promise((resolve) => setTimeout(resolve, 10));
97
+ expect(mutation.error).toBeNull();
98
+ expect(mutation.isPending).toBe(false);
99
+ });
100
+ it('should handle different parameter types', async () => {
101
+ const mutator = vi.fn((params) => Promise.resolve(params));
102
+ const mutation = createMutation(mutator);
103
+ // Object params
104
+ mutation.mutate({ id: 1, name: 'test' });
105
+ expect(mutation.params).toEqual({ id: 1, name: 'test' });
106
+ await new Promise((resolve) => setTimeout(resolve, 10));
107
+ // Array params
108
+ const mutation2 = createMutation(mutator);
109
+ mutation2.mutate([1, 2, 3]);
110
+ expect(mutation2.params).toEqual([1, 2, 3]);
111
+ await new Promise((resolve) => setTimeout(resolve, 10));
112
+ // String params
113
+ const mutation3 = createMutation(mutator);
114
+ mutation3.mutate('string');
115
+ expect(mutation3.params).toBe('string');
116
+ });
117
+ it('should convert errors to strings', async () => {
118
+ const mutator = (params) => Promise.reject('string error');
119
+ const mutation = createMutation(mutator);
120
+ mutation.mutate('test');
121
+ await new Promise((resolve) => setTimeout(resolve, 10));
122
+ expect(typeof mutation.error).toBe('string');
123
+ expect(mutation.error).toBe('string error');
124
+ });
125
+ it('should handle AbortController cancellation correctly', async () => {
126
+ const abortedPromise = new Promise((_, reject) => {
127
+ const error = new Error('Aborted');
128
+ error.name = 'AbortError';
129
+ setTimeout(() => reject(error), 5);
130
+ });
131
+ const successPromise = Promise.resolve('success');
132
+ const mutator = vi
133
+ .fn()
134
+ .mockReturnValueOnce(abortedPromise)
135
+ .mockReturnValueOnce(successPromise);
136
+ const mutation = createMutation(mutator);
137
+ mutation.mutate('first');
138
+ // Immediately trigger second mutation to abort first
139
+ mutation.mutate('second');
140
+ await new Promise((resolve) => setTimeout(resolve, 20));
141
+ expect(mutation.isPending).toBe(false);
142
+ expect(mutation.error).toBeNull();
143
+ });
144
+ it('should track params during pending state', () => {
145
+ const mutator = () => new Promise((resolve) => setTimeout(() => resolve(null), 100));
146
+ const mutation = createMutation(mutator);
147
+ const params = { id: 123, action: 'update' };
148
+ mutation.mutate(params);
149
+ expect(mutation.isPending).toBe(true);
150
+ expect(mutation.params).toEqual(params);
151
+ });
152
+ it('should expose reactive getters', async () => {
153
+ const mutator = () => Promise.resolve('data');
154
+ const mutation = createMutation(mutator);
155
+ // Access getters before mutation
156
+ expect(mutation.isPending).toBe(false);
157
+ expect(mutation.params).toBeNull();
158
+ expect(mutation.error).toBeNull();
159
+ mutation.mutate('test');
160
+ // Access getters during mutation
161
+ expect(mutation.isPending).toBe(true);
162
+ expect(mutation.params).toBe('test');
163
+ await new Promise((resolve) => setTimeout(resolve, 10));
164
+ // Access getters after mutation
165
+ expect(mutation.isPending).toBe(false);
166
+ expect(mutation.params).toBeNull();
167
+ });
168
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createQuery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createQuery.test.d.ts","sourceRoot":"","sources":["../src/createQuery.test.ts"],"names":[],"mappings":""}