rask-ui 0.20.1 → 0.20.2

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 (111) 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/compiler.d.ts +2 -0
  5. package/dist/compiler.d.ts.map +1 -0
  6. package/dist/compiler.js +1 -0
  7. package/dist/context.d.ts +5 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +29 -0
  10. package/dist/createAsync.test.d.ts +2 -0
  11. package/dist/createAsync.test.d.ts.map +1 -0
  12. package/dist/createAsync.test.js +110 -0
  13. package/dist/createMutation.test.d.ts +2 -0
  14. package/dist/createMutation.test.d.ts.map +1 -0
  15. package/dist/createMutation.test.js +168 -0
  16. package/dist/createQuery.test.d.ts +2 -0
  17. package/dist/createQuery.test.d.ts.map +1 -0
  18. package/dist/createQuery.test.js +156 -0
  19. package/dist/createRef.d.ts +6 -0
  20. package/dist/createRef.d.ts.map +1 -0
  21. package/dist/createRef.js +8 -0
  22. package/dist/createState.d.ts +0 -2
  23. package/dist/createState.d.ts.map +1 -1
  24. package/dist/createState.js +5 -40
  25. package/dist/createState.test.d.ts.map +1 -0
  26. package/dist/createState.test.js +111 -0
  27. package/dist/createView.d.ts +44 -18
  28. package/dist/createView.d.ts.map +1 -1
  29. package/dist/createView.js +48 -57
  30. package/dist/createView.test.d.ts.map +1 -0
  31. package/dist/{tests/createView.test.js → createView.test.js} +40 -40
  32. package/dist/error.d.ts +14 -3
  33. package/dist/error.d.ts.map +1 -1
  34. package/dist/error.js +15 -14
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -2
  38. package/dist/jsx.d.ts +256 -10
  39. package/dist/observation.test.d.ts.map +1 -0
  40. package/dist/observation.test.js +150 -0
  41. package/dist/plugin.js +1 -1
  42. package/dist/suspense.d.ts +25 -0
  43. package/dist/suspense.d.ts.map +1 -0
  44. package/dist/suspense.js +97 -0
  45. package/dist/test-setup.d.ts +16 -0
  46. package/dist/test-setup.d.ts.map +1 -0
  47. package/dist/test-setup.js +40 -0
  48. package/dist/test.d.ts +2 -0
  49. package/dist/test.d.ts.map +1 -0
  50. package/dist/test.js +24 -0
  51. package/dist/types.d.ts +20 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +1 -0
  54. package/package.json +5 -1
  55. package/swc-plugin/target/wasm32-wasip1/release/swc_plugin_rask_component.wasm +0 -0
  56. package/dist/createComputed.d.ts +0 -4
  57. package/dist/createComputed.d.ts.map +0 -1
  58. package/dist/createComputed.js +0 -69
  59. package/dist/createEffect.d.ts +0 -2
  60. package/dist/createEffect.d.ts.map +0 -1
  61. package/dist/createEffect.js +0 -29
  62. package/dist/createRouter.d.ts +0 -8
  63. package/dist/createRouter.d.ts.map +0 -1
  64. package/dist/createRouter.js +0 -27
  65. package/dist/createTask.d.ts +0 -31
  66. package/dist/createTask.d.ts.map +0 -1
  67. package/dist/createTask.js +0 -79
  68. package/dist/patchInferno.d.ts +0 -6
  69. package/dist/patchInferno.d.ts.map +0 -1
  70. package/dist/patchInferno.js +0 -53
  71. package/dist/scheduler.d.ts +0 -4
  72. package/dist/scheduler.d.ts.map +0 -1
  73. package/dist/scheduler.js +0 -107
  74. package/dist/tests/batch.test.d.ts +0 -2
  75. package/dist/tests/batch.test.d.ts.map +0 -1
  76. package/dist/tests/batch.test.js +0 -244
  77. package/dist/tests/createComputed.test.d.ts +0 -2
  78. package/dist/tests/createComputed.test.d.ts.map +0 -1
  79. package/dist/tests/createComputed.test.js +0 -257
  80. package/dist/tests/createContext.test.d.ts +0 -2
  81. package/dist/tests/createContext.test.d.ts.map +0 -1
  82. package/dist/tests/createContext.test.js +0 -136
  83. package/dist/tests/createEffect.test.d.ts +0 -2
  84. package/dist/tests/createEffect.test.d.ts.map +0 -1
  85. package/dist/tests/createEffect.test.js +0 -467
  86. package/dist/tests/createState.test.d.ts.map +0 -1
  87. package/dist/tests/createState.test.js +0 -144
  88. package/dist/tests/createTask.test.d.ts +0 -2
  89. package/dist/tests/createTask.test.d.ts.map +0 -1
  90. package/dist/tests/createTask.test.js +0 -322
  91. package/dist/tests/createView.test.d.ts.map +0 -1
  92. package/dist/tests/error.test.d.ts +0 -2
  93. package/dist/tests/error.test.d.ts.map +0 -1
  94. package/dist/tests/error.test.js +0 -168
  95. package/dist/tests/observation.test.d.ts.map +0 -1
  96. package/dist/tests/observation.test.js +0 -341
  97. package/dist/useComputed.d.ts +0 -5
  98. package/dist/useComputed.d.ts.map +0 -1
  99. package/dist/useComputed.js +0 -69
  100. package/dist/useQuery.d.ts +0 -25
  101. package/dist/useQuery.d.ts.map +0 -1
  102. package/dist/useQuery.js +0 -25
  103. package/dist/useSuspendAsync.d.ts +0 -18
  104. package/dist/useSuspendAsync.d.ts.map +0 -1
  105. package/dist/useSuspendAsync.js +0 -37
  106. package/dist/useTask.d.ts +0 -25
  107. package/dist/useTask.d.ts.map +0 -1
  108. package/dist/useTask.js +0 -70
  109. /package/dist/{tests/createState.test.d.ts → createState.test.d.ts} +0 -0
  110. /package/dist/{tests/createView.test.d.ts → createView.test.d.ts} +0 -0
  111. /package/dist/{tests/observation.test.d.ts → observation.test.d.ts} +0 -0
package/dist/error.js CHANGED
@@ -1,16 +1,17 @@
1
- import { useState } from "./useState";
2
- import { createContext, useInjectContext } from "./createContext";
3
- import { getCurrentComponent } from "./component";
4
- export const CatchErrorContext = createContext();
5
- export function useCatchError() {
6
- const currentComponent = getCurrentComponent();
7
- if (!currentComponent || currentComponent.isRendering) {
8
- throw new Error("Only use the useCatchError hook in setup");
1
+ import { Component } from "inferno";
2
+ export class ErrorBoundary extends Component {
3
+ getChildContext() {
4
+ return {
5
+ notifyError: (error) => {
6
+ this.setState({ error });
7
+ },
8
+ };
9
+ }
10
+ state = { error: null };
11
+ render() {
12
+ if (this.state.error) {
13
+ return this.props.error(this.state.error);
14
+ }
15
+ return this.props.children;
9
16
  }
10
- const inject = useInjectContext(CatchErrorContext);
11
- const state = useState({
12
- error: null,
13
- });
14
- inject((error) => (state.error = error));
15
- return state;
16
17
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import "./types";
1
2
  export { render } from "./render";
2
3
  export { useCleanup, useMountEffect, RaskStatefulComponent, RaskStatelessComponent, } from "./component";
3
4
  export { createContext, useContext, useInjectContext } from "./createContext";
@@ -13,5 +14,4 @@ export { useDerived, Derived } from "./useDerived";
13
14
  export { syncBatch } from "./batch";
14
15
  export { inspect } from "./inspect";
15
16
  export { Router, useRouter } from "./useRouter";
16
- export { createVNode, createComponentVNode, createFragment, createTextVNode, normalizeProps, Component, } from "inferno";
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,CAAC;AAEjB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ // Import global types - this makes the Rask namespace available globally
2
+ import "./types";
1
3
  export { render } from "./render";
2
4
  export { useCleanup, useMountEffect, RaskStatefulComponent, RaskStatelessComponent, } from "./component";
3
5
  export { createContext, useContext, useInjectContext } from "./createContext";
@@ -13,5 +15,3 @@ export { useDerived } from "./useDerived";
13
15
  export { syncBatch } from "./batch";
14
16
  export { inspect } from "./inspect";
15
17
  export { useRouter } from "./useRouter";
16
- // Re-export Inferno JSX runtime functions so users don't need to install Inferno directly
17
- export { createVNode, createComponentVNode, createFragment, createTextVNode, normalizeProps, Component, } from "inferno";
package/dist/jsx.d.ts CHANGED
@@ -1,11 +1,257 @@
1
1
  // JSX type definitions
2
- // Re-export Inferno's comprehensive JSX types
3
- import 'inferno';
4
-
5
- // Re-export useful Inferno types for convenience
6
- export type {
7
- InfernoNode,
8
- InfernoChild,
9
- Component,
10
- ComponentType,
11
- } from 'inferno';
2
+ // Note: This is JSXInternal, which gets renamed to JSX on export from jsx-runtime
3
+ import type { Ref } from "./state";
4
+
5
+ export namespace JSXInternal {
6
+ export type Element = any;
7
+
8
+ export interface ElementAttributesProperty {
9
+ props: {};
10
+ }
11
+
12
+ export interface ElementChildrenAttribute {
13
+ children: {};
14
+ }
15
+
16
+ export interface IntrinsicAttributes {
17
+ key?: any;
18
+ }
19
+
20
+ // CSS Properties
21
+ export type CSSProperties = {
22
+ [key: string]: string | number | undefined;
23
+ };
24
+
25
+ // Common HTML Attributes
26
+ export interface HTMLAttributes<T = HTMLElement> {
27
+ ref?: Ref<T> | ((element: T | null) => void);
28
+ id?: string;
29
+ class?: string | Record<string, boolean>;
30
+ style?: string | CSSProperties;
31
+ title?: string;
32
+ role?: string;
33
+ tabIndex?: number;
34
+
35
+ onClick?: (event: MouseEvent) => void;
36
+ onDblClick?: (event: MouseEvent) => void;
37
+ onChange?: (event: Event) => void;
38
+ onInput?: (event: Event) => void;
39
+ onSubmit?: (event: Event) => void;
40
+ onFocus?: (event: FocusEvent) => void;
41
+ onBlur?: (event: FocusEvent) => void;
42
+ onKeyDown?: (event: KeyboardEvent) => void;
43
+ onKeyUp?: (event: KeyboardEvent) => void;
44
+ onKeyPress?: (event: KeyboardEvent) => void;
45
+ onMouseDown?: (event: MouseEvent) => void;
46
+ onMouseUp?: (event: MouseEvent) => void;
47
+ onMouseEnter?: (event: MouseEvent) => void;
48
+ onMouseLeave?: (event: MouseEvent) => void;
49
+ onMouseMove?: (event: MouseEvent) => void;
50
+ onMouseOver?: (event: MouseEvent) => void;
51
+ onMouseOut?: (event: MouseEvent) => void;
52
+ onWheel?: (event: WheelEvent) => void;
53
+ onScroll?: (event: Event) => void;
54
+ onTouchStart?: (event: TouchEvent) => void;
55
+ onTouchEnd?: (event: TouchEvent) => void;
56
+ onTouchMove?: (event: TouchEvent) => void;
57
+ onTouchCancel?: (event: TouchEvent) => void;
58
+
59
+ [key: `aria-${string}`]: string | boolean | number | undefined;
60
+ [key: `data-${string}`]: string | boolean | number | undefined;
61
+
62
+ children?: any;
63
+ }
64
+
65
+ export interface AnchorHTMLAttributes<T = HTMLAnchorElement>
66
+ extends HTMLAttributes<T> {
67
+ href?: string;
68
+ target?: "_blank" | "_self" | "_parent" | "_top";
69
+ rel?: string;
70
+ download?: string;
71
+ }
72
+
73
+ export interface ButtonHTMLAttributes<T = HTMLButtonElement>
74
+ extends HTMLAttributes<T> {
75
+ type?: "button" | "submit" | "reset";
76
+ disabled?: boolean;
77
+ name?: string;
78
+ value?: string;
79
+ }
80
+
81
+ export interface FormHTMLAttributes<T = HTMLFormElement>
82
+ extends HTMLAttributes<T> {
83
+ action?: string;
84
+ method?: "get" | "post";
85
+ enctype?: string;
86
+ target?: string;
87
+ noValidate?: boolean;
88
+ }
89
+
90
+ export interface InputHTMLAttributes<T = HTMLInputElement>
91
+ extends HTMLAttributes<T> {
92
+ type?:
93
+ | "button"
94
+ | "checkbox"
95
+ | "color"
96
+ | "date"
97
+ | "datetime-local"
98
+ | "email"
99
+ | "file"
100
+ | "hidden"
101
+ | "image"
102
+ | "month"
103
+ | "number"
104
+ | "password"
105
+ | "radio"
106
+ | "range"
107
+ | "reset"
108
+ | "search"
109
+ | "submit"
110
+ | "tel"
111
+ | "text"
112
+ | "time"
113
+ | "url"
114
+ | "week";
115
+ value?: string | number;
116
+ defaultValue?: string | number;
117
+ placeholder?: string;
118
+ disabled?: boolean;
119
+ required?: boolean;
120
+ readOnly?: boolean;
121
+ name?: string;
122
+ checked?: boolean;
123
+ defaultChecked?: boolean;
124
+ min?: string | number;
125
+ max?: string | number;
126
+ step?: string | number;
127
+ pattern?: string;
128
+ accept?: string;
129
+ multiple?: boolean;
130
+ autoComplete?: string;
131
+ autoFocus?: boolean;
132
+ }
133
+
134
+ export interface LabelHTMLAttributes<T = HTMLLabelElement>
135
+ extends HTMLAttributes<T> {
136
+ htmlFor?: string;
137
+ for?: string;
138
+ }
139
+
140
+ export interface SelectHTMLAttributes<T = HTMLSelectElement>
141
+ extends HTMLAttributes<T> {
142
+ value?: string | string[];
143
+ defaultValue?: string | string[];
144
+ disabled?: boolean;
145
+ required?: boolean;
146
+ name?: string;
147
+ multiple?: boolean;
148
+ size?: number;
149
+ }
150
+
151
+ export interface OptionHTMLAttributes<T = HTMLOptionElement>
152
+ extends HTMLAttributes<T> {
153
+ value?: string | number;
154
+ selected?: boolean;
155
+ disabled?: boolean;
156
+ label?: string;
157
+ }
158
+
159
+ export interface TextareaHTMLAttributes<T = HTMLTextAreaElement>
160
+ extends HTMLAttributes<T> {
161
+ value?: string;
162
+ defaultValue?: string;
163
+ placeholder?: string;
164
+ disabled?: boolean;
165
+ required?: boolean;
166
+ readOnly?: boolean;
167
+ name?: string;
168
+ rows?: number;
169
+ cols?: number;
170
+ maxLength?: number;
171
+ wrap?: "soft" | "hard";
172
+ }
173
+
174
+ export interface FieldsetHTMLAttributes<T = HTMLFieldSetElement>
175
+ extends HTMLAttributes<T> {
176
+ disabled?: boolean;
177
+ name?: string;
178
+ }
179
+
180
+ export interface ImgHTMLAttributes<T = HTMLImageElement>
181
+ extends HTMLAttributes<T> {
182
+ src?: string;
183
+ alt?: string;
184
+ width?: number | string;
185
+ height?: number | string;
186
+ loading?: "eager" | "lazy";
187
+ crossOrigin?: "anonymous" | "use-credentials";
188
+ }
189
+
190
+ export interface SVGAttributes<T = SVGElement> extends HTMLAttributes<T> {
191
+ xmlns?: string;
192
+ viewBox?: string;
193
+ width?: number | string;
194
+ height?: number | string;
195
+ fill?: string;
196
+ stroke?: string;
197
+ strokeWidth?: number | string;
198
+ }
199
+
200
+ // Intrinsic Elements
201
+ export interface IntrinsicElements {
202
+ a: AnchorHTMLAttributes<HTMLAnchorElement>;
203
+ abbr: HTMLAttributes<HTMLElement>;
204
+ address: HTMLAttributes<HTMLElement>;
205
+ article: HTMLAttributes<HTMLElement>;
206
+ aside: HTMLAttributes<HTMLElement>;
207
+ b: HTMLAttributes<HTMLElement>;
208
+ blockquote: HTMLAttributes<HTMLQuoteElement>;
209
+ body: HTMLAttributes<HTMLBodyElement>;
210
+ br: HTMLAttributes<HTMLBRElement>;
211
+ button: ButtonHTMLAttributes<HTMLButtonElement>;
212
+ canvas: HTMLAttributes<HTMLCanvasElement>;
213
+ code: HTMLAttributes<HTMLElement>;
214
+ div: HTMLAttributes<HTMLDivElement>;
215
+ em: HTMLAttributes<HTMLElement>;
216
+ fieldset: FieldsetHTMLAttributes<HTMLFieldSetElement>;
217
+ footer: HTMLAttributes<HTMLElement>;
218
+ form: FormHTMLAttributes<HTMLFormElement>;
219
+ h1: HTMLAttributes<HTMLHeadingElement>;
220
+ h2: HTMLAttributes<HTMLHeadingElement>;
221
+ h3: HTMLAttributes<HTMLHeadingElement>;
222
+ h4: HTMLAttributes<HTMLHeadingElement>;
223
+ h5: HTMLAttributes<HTMLHeadingElement>;
224
+ h6: HTMLAttributes<HTMLHeadingElement>;
225
+ head: HTMLAttributes<HTMLHeadElement>;
226
+ header: HTMLAttributes<HTMLElement>;
227
+ hr: HTMLAttributes<HTMLHRElement>;
228
+ html: HTMLAttributes<HTMLHtmlElement>;
229
+ i: HTMLAttributes<HTMLElement>;
230
+ img: ImgHTMLAttributes<HTMLImageElement>;
231
+ input: InputHTMLAttributes<HTMLInputElement>;
232
+ label: LabelHTMLAttributes<HTMLLabelElement>;
233
+ legend: HTMLAttributes<HTMLLegendElement>;
234
+ li: HTMLAttributes<HTMLLIElement>;
235
+ main: HTMLAttributes<HTMLElement>;
236
+ nav: HTMLAttributes<HTMLElement>;
237
+ ol: HTMLAttributes<HTMLOListElement>;
238
+ option: OptionHTMLAttributes<HTMLOptionElement>;
239
+ p: HTMLAttributes<HTMLParagraphElement>;
240
+ pre: HTMLAttributes<HTMLPreElement>;
241
+ section: HTMLAttributes<HTMLElement>;
242
+ select: SelectHTMLAttributes<HTMLSelectElement>;
243
+ small: HTMLAttributes<HTMLElement>;
244
+ span: HTMLAttributes<HTMLSpanElement>;
245
+ strong: HTMLAttributes<HTMLElement>;
246
+ style: HTMLAttributes<HTMLStyleElement>;
247
+ textarea: TextareaHTMLAttributes<HTMLTextAreaElement>;
248
+ ul: HTMLAttributes<HTMLUListElement>;
249
+
250
+ // SVG
251
+ svg: SVGAttributes<SVGSVGElement>;
252
+ circle: SVGAttributes<SVGCircleElement>;
253
+ line: SVGAttributes<SVGLineElement>;
254
+ path: SVGAttributes<SVGPathElement>;
255
+ rect: SVGAttributes<SVGRectElement>;
256
+ }
257
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation.test.d.ts","sourceRoot":"","sources":["../src/observation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,150 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { Signal, Observer, getCurrentObserver } from './observation';
3
+ describe('Signal', () => {
4
+ it('should allow subscribing to notifications', () => {
5
+ const signal = new Signal();
6
+ const callback = vi.fn();
7
+ signal.subscribe(callback);
8
+ signal.notify();
9
+ expect(callback).toHaveBeenCalledTimes(1);
10
+ });
11
+ it('should return a disposer function', () => {
12
+ const signal = new Signal();
13
+ const callback = vi.fn();
14
+ const dispose = signal.subscribe(callback);
15
+ dispose();
16
+ signal.notify();
17
+ expect(callback).not.toHaveBeenCalled();
18
+ });
19
+ it('should handle multiple subscribers', () => {
20
+ const signal = new Signal();
21
+ const callback1 = vi.fn();
22
+ const callback2 = vi.fn();
23
+ signal.subscribe(callback1);
24
+ signal.subscribe(callback2);
25
+ signal.notify();
26
+ expect(callback1).toHaveBeenCalledTimes(1);
27
+ expect(callback2).toHaveBeenCalledTimes(1);
28
+ });
29
+ it('should allow unsubscribing individual callbacks', () => {
30
+ const signal = new Signal();
31
+ const callback1 = vi.fn();
32
+ const callback2 = vi.fn();
33
+ const dispose1 = signal.subscribe(callback1);
34
+ signal.subscribe(callback2);
35
+ dispose1();
36
+ signal.notify();
37
+ expect(callback1).not.toHaveBeenCalled();
38
+ expect(callback2).toHaveBeenCalledTimes(1);
39
+ });
40
+ });
41
+ describe('Observer', () => {
42
+ it('should queue notifications in microtasks', async () => {
43
+ let callCount = 0;
44
+ const observer = new Observer(() => {
45
+ callCount++;
46
+ });
47
+ const signal = new Signal();
48
+ const dispose = observer.observe();
49
+ observer.subscribeSignal(signal);
50
+ dispose();
51
+ // Trigger multiple notifications
52
+ signal.notify();
53
+ signal.notify();
54
+ signal.notify();
55
+ // Should not be called synchronously
56
+ expect(callCount).toBe(0);
57
+ // Wait for microtask
58
+ await new Promise((resolve) => queueMicrotask(() => resolve()));
59
+ // Should be called only once due to queuing
60
+ expect(callCount).toBe(1);
61
+ });
62
+ it('should track signals during observation', () => {
63
+ const callback = vi.fn();
64
+ const observer = new Observer(callback);
65
+ const signal = new Signal();
66
+ const dispose = observer.observe();
67
+ observer.subscribeSignal(signal);
68
+ dispose();
69
+ signal.notify();
70
+ return new Promise((resolve) => {
71
+ queueMicrotask(() => {
72
+ expect(callback).toHaveBeenCalledTimes(1);
73
+ resolve(undefined);
74
+ });
75
+ });
76
+ });
77
+ it('should clear signals when observing again', async () => {
78
+ let callCount = 0;
79
+ const observer = new Observer(() => {
80
+ callCount++;
81
+ });
82
+ const signal1 = new Signal();
83
+ const signal2 = new Signal();
84
+ // First observation
85
+ let dispose = observer.observe();
86
+ observer.subscribeSignal(signal1);
87
+ dispose();
88
+ // Second observation - should clear previous signals
89
+ dispose = observer.observe();
90
+ observer.subscribeSignal(signal2);
91
+ dispose();
92
+ // Notify first signal - should not trigger observer
93
+ signal1.notify();
94
+ await new Promise((resolve) => queueMicrotask(() => resolve()));
95
+ expect(callCount).toBe(0);
96
+ // Notify second signal - should trigger observer
97
+ signal2.notify();
98
+ await new Promise((resolve) => queueMicrotask(() => resolve()));
99
+ expect(callCount).toBe(1);
100
+ });
101
+ it('should dispose of all signal subscriptions', async () => {
102
+ const callback = vi.fn();
103
+ const observer = new Observer(callback);
104
+ const signal = new Signal();
105
+ const dispose = observer.observe();
106
+ observer.subscribeSignal(signal);
107
+ dispose();
108
+ observer.dispose();
109
+ signal.notify();
110
+ await new Promise((resolve) => queueMicrotask(() => resolve()));
111
+ expect(callback).not.toHaveBeenCalled();
112
+ });
113
+ it('should set current observer during observation', () => {
114
+ const observer = new Observer(() => { });
115
+ expect(getCurrentObserver()).toBeUndefined();
116
+ const dispose = observer.observe();
117
+ expect(getCurrentObserver()).toBe(observer);
118
+ dispose();
119
+ expect(getCurrentObserver()).toBeUndefined();
120
+ });
121
+ it('should handle nested observations with stack', () => {
122
+ const observer1 = new Observer(() => { });
123
+ const observer2 = new Observer(() => { });
124
+ const dispose1 = observer1.observe();
125
+ expect(getCurrentObserver()).toBe(observer1);
126
+ const dispose2 = observer2.observe();
127
+ expect(getCurrentObserver()).toBe(observer2);
128
+ dispose2();
129
+ expect(getCurrentObserver()).toBe(observer1);
130
+ dispose1();
131
+ expect(getCurrentObserver()).toBeUndefined();
132
+ });
133
+ it('should prevent duplicate notifications while queued', async () => {
134
+ let callCount = 0;
135
+ const observer = new Observer(() => {
136
+ callCount++;
137
+ });
138
+ const signal = new Signal();
139
+ const dispose = observer.observe();
140
+ observer.subscribeSignal(signal);
141
+ dispose();
142
+ // Rapid-fire notifications
143
+ for (let i = 0; i < 100; i++) {
144
+ signal.notify();
145
+ }
146
+ await new Promise((resolve) => queueMicrotask(() => resolve()));
147
+ // Should only be called once
148
+ expect(callCount).toBe(1);
149
+ });
150
+ });
package/dist/plugin.js CHANGED
@@ -40,7 +40,7 @@ export default function raskPlugin(options = {}) {
40
40
  [
41
41
  infernoPluginPath,
42
42
  {
43
- importSource: importSource,
43
+ importSource: `${importSource}/compiler`,
44
44
  defineAllArguments: false,
45
45
  },
46
46
  ],
@@ -0,0 +1,25 @@
1
+ import { VNode } from "snabbdom";
2
+ export declare function createSuspense<T extends Record<string, Promise<any>>>(promises: T): {
3
+ [K in keyof T]: Awaited<T[K]>;
4
+ };
5
+ export declare function Suspense(props: {
6
+ fallback: VNode;
7
+ children: VNode | VNode[];
8
+ }): () => VNode | VNode[];
9
+ type SuspensePromiseState<T> = {
10
+ status: "pending";
11
+ value: null;
12
+ error: null;
13
+ } | {
14
+ status: "resolved";
15
+ value: T;
16
+ error: null;
17
+ } | {
18
+ status: "rejected";
19
+ value: null;
20
+ error: string;
21
+ };
22
+ export type SuspensePromise<T> = Promise<T> & SuspensePromiseState<T>;
23
+ export declare function createSuspensePromise<T>(promise: Promise<T>): SuspensePromise<T>;
24
+ export {};
25
+ //# sourceMappingURL=suspense.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../src/suspense.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAKjC,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,EACnE,QAAQ,EAAE,CAAC,GACV;KACA,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC9B,CAuBA;AAQD,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,QAAQ,EAAE,KAAK,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CAC3B,yBAmBA;AAED,KAAK,oBAAoB,CAAC,CAAC,IACvB;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAEtE,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,eAAe,CAAC,CAAC,CAAC,CAgEpB"}
@@ -0,0 +1,97 @@
1
+ import { getCurrentComponent } from "./component";
2
+ import { createState } from "./createState";
3
+ import { getCurrentObserver, Signal } from "./observation";
4
+ export function createSuspense(promises) {
5
+ let currentComponent = getCurrentComponent();
6
+ if (!currentComponent) {
7
+ throw new Error("createSuspense must be used in the setup of a component");
8
+ }
9
+ const proxy = {};
10
+ for (const key in promises) {
11
+ const promise = promises[key];
12
+ const suspensePromise = isSuspensePromise(promise)
13
+ ? promise
14
+ : createSuspensePromise(promise);
15
+ currentComponent.notifyAsync(suspensePromise);
16
+ Object.defineProperty(proxy, key, {
17
+ get() {
18
+ return suspensePromise.value;
19
+ },
20
+ });
21
+ }
22
+ return proxy;
23
+ }
24
+ function isSuspensePromise(promise) {
25
+ return "status" in promise;
26
+ }
27
+ export function Suspense(props) {
28
+ const currentComponent = getCurrentComponent();
29
+ const state = createState({
30
+ suspendingPromises: [],
31
+ });
32
+ currentComponent.onAsync((promise) => {
33
+ state.suspendingPromises = state.suspendingPromises.concat(promise);
34
+ });
35
+ return () => {
36
+ const isAllResolved = state.suspendingPromises.every((promise) => promise.status === "resolved");
37
+ console.log(isAllResolved);
38
+ return isAllResolved ? props.children : props.fallback;
39
+ };
40
+ }
41
+ export function createSuspensePromise(promise) {
42
+ const signal = new Signal();
43
+ const state = {
44
+ error: null,
45
+ status: "pending",
46
+ value: null,
47
+ };
48
+ Object.defineProperty(promise, "value", {
49
+ get() {
50
+ const observer = getCurrentObserver();
51
+ if (observer) {
52
+ observer.subscribeSignal(signal);
53
+ }
54
+ return state.value;
55
+ },
56
+ set(newValue) {
57
+ state.value = newValue;
58
+ signal.notify();
59
+ },
60
+ });
61
+ Object.defineProperty(promise, "error", {
62
+ get() {
63
+ const observer = getCurrentObserver();
64
+ if (observer) {
65
+ observer.subscribeSignal(signal);
66
+ }
67
+ return state.error;
68
+ },
69
+ });
70
+ Object.defineProperty(promise, "status", {
71
+ get() {
72
+ const observer = getCurrentObserver();
73
+ if (observer) {
74
+ observer.subscribeSignal(signal);
75
+ }
76
+ return state.status;
77
+ },
78
+ });
79
+ promise
80
+ .then((value) => {
81
+ Object.assign(state, {
82
+ value,
83
+ error: null,
84
+ status: "resolved",
85
+ });
86
+ signal.notify();
87
+ })
88
+ .catch((error) => {
89
+ Object.assign(state, {
90
+ value: null,
91
+ error: String(error),
92
+ status: "rejected",
93
+ });
94
+ signal.notify();
95
+ });
96
+ return promise;
97
+ }
@@ -0,0 +1,16 @@
1
+ import type { VNode } from 'snabbdom';
2
+ /**
3
+ * Test helper to render a component and provide easy cleanup
4
+ *
5
+ * @example
6
+ * const { container, unmount } = renderComponent(<MyComponent />);
7
+ * expect(container.textContent).toBe('hello');
8
+ * unmount();
9
+ */
10
+ export declare function renderComponent(vnode: VNode): {
11
+ container: HTMLElement;
12
+ vnode: VNode;
13
+ unmount: () => void;
14
+ rerender: (newVnode: VNode) => VNode;
15
+ };
16
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAUtC;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK;;;;yBAmBnB,KAAK;EAK7B"}
@@ -0,0 +1,40 @@
1
+ // Test setup file for vitest
2
+ import { afterEach } from 'vitest';
3
+ import { render, patch } from './render';
4
+ // Clean up after each test
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ // Remove any style tags added by render function
8
+ document.querySelectorAll('style').forEach(style => style.remove());
9
+ });
10
+ /**
11
+ * Test helper to render a component and provide easy cleanup
12
+ *
13
+ * @example
14
+ * const { container, unmount } = renderComponent(<MyComponent />);
15
+ * expect(container.textContent).toBe('hello');
16
+ * unmount();
17
+ */
18
+ export function renderComponent(vnode) {
19
+ const container = document.createElement('div');
20
+ document.body.appendChild(container);
21
+ let currentVnode = render(vnode, container);
22
+ const actualElement = currentVnode.elm;
23
+ return {
24
+ // The actual rendered DOM element (after patch replaces container)
25
+ container: actualElement,
26
+ // The vnode returned by render
27
+ vnode: currentVnode,
28
+ // Cleanup function
29
+ unmount: () => {
30
+ if (actualElement && actualElement.parentNode) {
31
+ actualElement.parentNode.removeChild(actualElement);
32
+ }
33
+ },
34
+ // Re-render with new vnode
35
+ rerender: (newVnode) => {
36
+ currentVnode = patch(currentVnode, newVnode);
37
+ return currentVnode;
38
+ }
39
+ };
40
+ }
package/dist/test.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function test(container: any): void;
2
+ //# sourceMappingURL=test.d.ts.map