effectweb 0.2.2 → 0.3.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.
- package/LICENSE +1 -1
- package/README.md +94 -2
- package/dist/AsyncContent.d.ts +2 -2
- package/dist/AsyncContent.js +36 -24
- package/dist/actions.d.ts +4 -4
- package/dist/actions.js +1 -1
- package/dist/cache-internals.d.ts +14 -0
- package/dist/cache-internals.js +11 -0
- package/dist/cache.d.ts +17 -15
- package/dist/cache.js +98 -27
- package/dist/collection.d.ts +10 -10
- package/dist/collection.js +32 -6
- package/dist/component.d.ts +8 -7
- package/dist/diagnostics.d.ts +25 -0
- package/dist/diagnostics.js +167 -1
- package/dist/dom-attributes.json +71 -0
- package/dist/dom.d.ts +36 -13
- package/dist/dom.js +547 -84
- package/dist/form.d.ts +61 -0
- package/dist/form.js +98 -0
- package/dist/html-attributes.d.ts +602 -0
- package/dist/html-attributes.js +1 -0
- package/dist/http.d.ts +5 -0
- package/dist/http.js +7 -0
- package/dist/index.d.ts +12 -10
- package/dist/index.js +7 -5
- package/dist/jsx.d.ts +165 -33
- package/dist/jsx.js +2 -1
- package/dist/lazy.d.ts +23 -0
- package/dist/lazy.js +58 -0
- package/dist/load.d.ts +3 -4
- package/dist/mount.d.ts +8 -11
- package/dist/mount.js +1 -3
- package/dist/owner.d.ts +11 -9
- package/dist/owner.js +27 -38
- package/dist/pages.d.ts +23 -23
- package/dist/pages.js +15 -10
- package/dist/program.d.ts +19 -10
- package/dist/program.js +172 -66
- package/dist/query-internals.d.ts +11 -0
- package/dist/query-internals.js +10 -0
- package/dist/query.d.ts +24 -20
- package/dist/query.js +60 -7
- package/dist/resource.d.ts +4 -10
- package/dist/resource.js +14 -5
- package/dist/runtime.d.ts +1 -2
- package/dist/runtime.js +3 -3
- package/dist/session.d.ts +12 -18
- package/dist/session.js +15 -15
- package/dist/share.d.ts +5 -4
- package/dist/share.js +5 -44
- package/dist/sharing.d.ts +6 -0
- package/dist/sharing.js +93 -0
- package/dist/snapshot.d.ts +16 -5
- package/dist/snapshot.js +6 -7
- package/dist/state.d.ts +0 -1
- package/dist/state.js +0 -1
- package/dist/svg-attributes.d.ts +12 -0
- package/dist/svg-attributes.js +1 -0
- package/dist/task.d.ts +9 -8
- package/dist/task.js +75 -47
- package/dist/tasks.d.ts +20 -15
- package/dist/tasks.js +39 -25
- package/dist/testing.d.ts +10 -15
- package/package.json +5 -1
package/dist/jsx.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import type { HtmlAttributes } from './html-attributes.js';
|
|
2
|
+
import type { SvgAttributes, SvgPresentationAttributes } from './svg-attributes.js';
|
|
3
|
+
import type { SnapshotOpaque } from './snapshot.js';
|
|
1
4
|
import type { EffectEventRequest } from './effectEvent.js';
|
|
2
5
|
import type { CompiledContent } from './dom.js';
|
|
3
6
|
import type { DomMount } from './mount.js';
|
|
7
|
+
/** Nominal eligibility for compiled JSX components and compiler primitives. */
|
|
8
|
+
export declare const jsxComponent: unique symbol;
|
|
4
9
|
/** JSX is a compiler input. No renderer library owns its types. */
|
|
5
10
|
export declare namespace JSX {
|
|
6
|
-
export
|
|
11
|
+
export interface ComponentType {
|
|
12
|
+
readonly [jsxComponent]: true;
|
|
13
|
+
}
|
|
14
|
+
export type ElementType = keyof IntrinsicElements | (ComponentType & ((props: never) => Element));
|
|
15
|
+
/** Immutable compiler input; preserve this recursive presentation type through snapshots. */
|
|
16
|
+
export interface ElementArray extends ReadonlyArray<Element>, SnapshotOpaque {
|
|
17
|
+
}
|
|
18
|
+
export type Element = string | number | boolean | null | undefined | CompiledContent | ElementArray;
|
|
7
19
|
export type EventHandler<T, E extends Event> = (event: E & {
|
|
8
20
|
currentTarget: T;
|
|
9
21
|
target: EventTarget & globalThis.Element;
|
|
@@ -12,73 +24,193 @@ export declare namespace JSX {
|
|
|
12
24
|
export type CSSProperties = {
|
|
13
25
|
[property: string]: string | number | undefined | null;
|
|
14
26
|
};
|
|
27
|
+
/** Standard event payloads not yet declared by the minimum supported TypeScript DOM library. */
|
|
28
|
+
export interface CommandEvent extends Event {
|
|
29
|
+
readonly command: string;
|
|
30
|
+
readonly source: globalThis.Element | null;
|
|
31
|
+
}
|
|
32
|
+
export interface SnapEvent extends Event {
|
|
33
|
+
readonly snapTargetBlock: Node | null;
|
|
34
|
+
readonly snapTargetInline: Node | null;
|
|
35
|
+
}
|
|
36
|
+
export interface TimeEvent extends Event {
|
|
37
|
+
readonly detail: number;
|
|
38
|
+
readonly view: Window | null;
|
|
39
|
+
}
|
|
40
|
+
/** Native element events; window/document listeners belong in an owned DOM binding. */
|
|
15
41
|
export interface Events<T> {
|
|
16
|
-
|
|
42
|
+
onAbort?: EventHandler<T, UIEvent> | undefined;
|
|
43
|
+
onAnimationCancel?: EventHandler<T, AnimationEvent> | undefined;
|
|
44
|
+
onAnimationEnd?: EventHandler<T, AnimationEvent> | undefined;
|
|
45
|
+
onAnimationIteration?: EventHandler<T, AnimationEvent> | undefined;
|
|
46
|
+
onAnimationStart?: EventHandler<T, AnimationEvent> | undefined;
|
|
47
|
+
onAuxClick?: EventHandler<T, PointerEvent> | undefined;
|
|
48
|
+
onBeforeInput?: EventHandler<T, InputEvent> | undefined;
|
|
49
|
+
onBeforeMatch?: EventHandler<T, Event> | undefined;
|
|
50
|
+
onBeforeToggle?: EventHandler<T, ToggleEvent> | undefined;
|
|
51
|
+
onBlur?: EventHandler<T, FocusEvent> | undefined;
|
|
52
|
+
onCancel?: EventHandler<T, Event> | undefined;
|
|
53
|
+
onCanPlay?: EventHandler<T, Event> | undefined;
|
|
54
|
+
onCanPlayThrough?: EventHandler<T, Event> | undefined;
|
|
55
|
+
onChange?: EventHandler<T, Event> | undefined;
|
|
56
|
+
onClick?: EventHandler<T, PointerEvent> | undefined;
|
|
57
|
+
onClose?: EventHandler<T, Event> | undefined;
|
|
58
|
+
onCommand?: EventHandler<T, CommandEvent> | undefined;
|
|
59
|
+
onCompositionEnd?: EventHandler<T, CompositionEvent> | undefined;
|
|
60
|
+
onCompositionStart?: EventHandler<T, CompositionEvent> | undefined;
|
|
61
|
+
onCompositionUpdate?: EventHandler<T, CompositionEvent> | undefined;
|
|
62
|
+
onContentVisibilityAutoStateChange?: EventHandler<T, ContentVisibilityAutoStateChangeEvent> | undefined;
|
|
63
|
+
onContextLost?: EventHandler<T, Event> | undefined;
|
|
64
|
+
onContextMenu?: EventHandler<T, PointerEvent> | undefined;
|
|
65
|
+
onContextRestored?: EventHandler<T, Event> | undefined;
|
|
66
|
+
onCopy?: EventHandler<T, ClipboardEvent> | undefined;
|
|
67
|
+
onCueChange?: EventHandler<T, Event> | undefined;
|
|
68
|
+
onCut?: EventHandler<T, ClipboardEvent> | undefined;
|
|
17
69
|
onDblClick?: EventHandler<T, MouseEvent> | undefined;
|
|
18
|
-
|
|
70
|
+
onDrag?: EventHandler<T, DragEvent> | undefined;
|
|
71
|
+
onDragEnd?: EventHandler<T, DragEvent> | undefined;
|
|
72
|
+
onDragEnter?: EventHandler<T, DragEvent> | undefined;
|
|
73
|
+
onDragLeave?: EventHandler<T, DragEvent> | undefined;
|
|
74
|
+
onDragOver?: EventHandler<T, DragEvent> | undefined;
|
|
75
|
+
onDragStart?: EventHandler<T, DragEvent> | undefined;
|
|
76
|
+
onDrop?: EventHandler<T, DragEvent> | undefined;
|
|
77
|
+
onDurationChange?: EventHandler<T, Event> | undefined;
|
|
78
|
+
onEmptied?: EventHandler<T, Event> | undefined;
|
|
79
|
+
onEnded?: EventHandler<T, Event> | undefined;
|
|
80
|
+
onError?: EventHandler<T, Event> | undefined;
|
|
81
|
+
onFocus?: EventHandler<T, FocusEvent> | undefined;
|
|
82
|
+
onFocusIn?: EventHandler<T, FocusEvent> | undefined;
|
|
83
|
+
onFocusOut?: EventHandler<T, FocusEvent> | undefined;
|
|
84
|
+
onFormData?: EventHandler<T, FormDataEvent> | undefined;
|
|
85
|
+
onFullscreenChange?: EventHandler<T, Event> | undefined;
|
|
86
|
+
onFullscreenError?: EventHandler<T, Event> | undefined;
|
|
87
|
+
onGotPointerCapture?: EventHandler<T, PointerEvent> | undefined;
|
|
19
88
|
onInput?: EventHandler<T, InputEvent> | undefined;
|
|
20
|
-
|
|
21
|
-
onSubmit?: EventHandler<T, SubmitEvent> | undefined;
|
|
89
|
+
onInvalid?: EventHandler<T, Event> | undefined;
|
|
22
90
|
onKeyDown?: EventHandler<T, KeyboardEvent> | undefined;
|
|
91
|
+
/** @deprecated Use onKeyDown or onBeforeInput. */
|
|
92
|
+
onKeyPress?: EventHandler<T, KeyboardEvent> | undefined;
|
|
23
93
|
onKeyUp?: EventHandler<T, KeyboardEvent> | undefined;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
onPointerCancel?: EventHandler<T, PointerEvent> | undefined;
|
|
94
|
+
onLoad?: EventHandler<T, Event> | undefined;
|
|
95
|
+
onLoadedData?: EventHandler<T, Event> | undefined;
|
|
96
|
+
onLoadedMetadata?: EventHandler<T, Event> | undefined;
|
|
97
|
+
onLoadStart?: EventHandler<T, Event> | undefined;
|
|
98
|
+
onLostPointerCapture?: EventHandler<T, PointerEvent> | undefined;
|
|
30
99
|
onMouseDown?: EventHandler<T, MouseEvent> | undefined;
|
|
31
100
|
onMouseEnter?: EventHandler<T, MouseEvent> | undefined;
|
|
32
101
|
onMouseLeave?: EventHandler<T, MouseEvent> | undefined;
|
|
33
|
-
|
|
34
|
-
|
|
102
|
+
onMouseMove?: EventHandler<T, MouseEvent> | undefined;
|
|
103
|
+
onMouseOut?: EventHandler<T, MouseEvent> | undefined;
|
|
104
|
+
onMouseOver?: EventHandler<T, MouseEvent> | undefined;
|
|
105
|
+
onMouseUp?: EventHandler<T, MouseEvent> | undefined;
|
|
106
|
+
onPaste?: EventHandler<T, ClipboardEvent> | undefined;
|
|
107
|
+
onPause?: EventHandler<T, Event> | undefined;
|
|
108
|
+
onPlay?: EventHandler<T, Event> | undefined;
|
|
109
|
+
onPlaying?: EventHandler<T, Event> | undefined;
|
|
110
|
+
onPointerCancel?: EventHandler<T, PointerEvent> | undefined;
|
|
111
|
+
onPointerDown?: EventHandler<T, PointerEvent> | undefined;
|
|
112
|
+
onPointerEnter?: EventHandler<T, PointerEvent> | undefined;
|
|
113
|
+
onPointerLeave?: EventHandler<T, PointerEvent> | undefined;
|
|
114
|
+
onPointerMove?: EventHandler<T, PointerEvent> | undefined;
|
|
115
|
+
onPointerOut?: EventHandler<T, PointerEvent> | undefined;
|
|
116
|
+
onPointerOver?: EventHandler<T, PointerEvent> | undefined;
|
|
117
|
+
onPointerRawUpdate?: EventHandler<T, PointerEvent> | undefined;
|
|
118
|
+
onPointerUp?: EventHandler<T, PointerEvent> | undefined;
|
|
119
|
+
onProgress?: EventHandler<T, ProgressEvent> | undefined;
|
|
120
|
+
onRateChange?: EventHandler<T, Event> | undefined;
|
|
121
|
+
onReset?: EventHandler<T, Event> | undefined;
|
|
122
|
+
onResize?: EventHandler<T, UIEvent> | undefined;
|
|
35
123
|
onScroll?: EventHandler<T, Event> | undefined;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
124
|
+
onScrollEnd?: EventHandler<T, Event> | undefined;
|
|
125
|
+
onScrollSnapChange?: EventHandler<T, SnapEvent> | undefined;
|
|
126
|
+
onScrollSnapChanging?: EventHandler<T, SnapEvent> | undefined;
|
|
127
|
+
onSecurityPolicyViolation?: EventHandler<T, SecurityPolicyViolationEvent> | undefined;
|
|
128
|
+
onSeeked?: EventHandler<T, Event> | undefined;
|
|
129
|
+
onSeeking?: EventHandler<T, Event> | undefined;
|
|
130
|
+
onSelect?: EventHandler<T, Event> | undefined;
|
|
131
|
+
onSelectionChange?: EventHandler<T, Event> | undefined;
|
|
132
|
+
onSelectStart?: EventHandler<T, Event> | undefined;
|
|
133
|
+
onSlotChange?: EventHandler<T, Event> | undefined;
|
|
134
|
+
onStalled?: EventHandler<T, Event> | undefined;
|
|
135
|
+
onSubmit?: EventHandler<T, SubmitEvent> | undefined;
|
|
136
|
+
onSuspend?: EventHandler<T, Event> | undefined;
|
|
40
137
|
onTimeUpdate?: EventHandler<T, Event> | undefined;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
138
|
+
onToggle?: EventHandler<T, ToggleEvent> | undefined;
|
|
139
|
+
onTouchCancel?: EventHandler<T, TouchEvent> | undefined;
|
|
140
|
+
onTouchEnd?: EventHandler<T, TouchEvent> | undefined;
|
|
141
|
+
onTouchMove?: EventHandler<T, TouchEvent> | undefined;
|
|
142
|
+
onTouchStart?: EventHandler<T, TouchEvent> | undefined;
|
|
143
|
+
onTransitionCancel?: EventHandler<T, TransitionEvent> | undefined;
|
|
144
|
+
onTransitionEnd?: EventHandler<T, TransitionEvent> | undefined;
|
|
145
|
+
onTransitionRun?: EventHandler<T, TransitionEvent> | undefined;
|
|
146
|
+
onTransitionStart?: EventHandler<T, TransitionEvent> | undefined;
|
|
147
|
+
onVolumeChange?: EventHandler<T, Event> | undefined;
|
|
148
|
+
onWaiting?: EventHandler<T, Event> | undefined;
|
|
149
|
+
onWheel?: EventHandler<T, WheelEvent> | undefined;
|
|
48
150
|
}
|
|
151
|
+
type ElementEvents<T> = Events<T> & (T extends HTMLMediaElement ? {
|
|
152
|
+
onEncrypted?: EventHandler<T, MediaEncryptedEvent> | undefined;
|
|
153
|
+
onWaitingForKey?: EventHandler<T, Event> | undefined;
|
|
154
|
+
} : {}) & (T extends HTMLVideoElement ? {
|
|
155
|
+
onEnterPictureInPicture?: EventHandler<T, PictureInPictureEvent> | undefined;
|
|
156
|
+
onLeavePictureInPicture?: EventHandler<T, PictureInPictureEvent> | undefined;
|
|
157
|
+
} : {}) & (T extends HTMLCanvasElement ? {
|
|
158
|
+
onWebGLContextCreationError?: EventHandler<T, WebGLContextEvent> | undefined;
|
|
159
|
+
onWebGLContextLost?: EventHandler<T, WebGLContextEvent> | undefined;
|
|
160
|
+
onWebGLContextRestored?: EventHandler<T, WebGLContextEvent> | undefined;
|
|
161
|
+
} : {}) & (T extends SVGAnimationElement ? {
|
|
162
|
+
onBegin?: EventHandler<T, TimeEvent> | undefined;
|
|
163
|
+
onEnd?: EventHandler<T, TimeEvent> | undefined;
|
|
164
|
+
onRepeat?: EventHandler<T, TimeEvent> | undefined;
|
|
165
|
+
} : {});
|
|
49
166
|
type Scalar = string | number | boolean;
|
|
167
|
+
type Equal<X, Y> = (<G>() => G extends X ? 1 : 2) extends <G>() => G extends Y ? 1 : 2 ? true : false;
|
|
168
|
+
type AttributeKey<K extends string> = Lowercase<K> extends keyof HtmlAttributes ? K | Lowercase<K> : K extends 'className' | 'htmlFor' | 'acceptCharset' | 'httpEquiv' ? K | Lowercase<K> : never;
|
|
50
169
|
type DomAttributes<T> = {
|
|
51
|
-
[K in keyof T as K extends string ?
|
|
170
|
+
[K in keyof T as K extends string ? Equal<Pick<T, K>, {
|
|
171
|
+
-readonly [P in K]: T[P];
|
|
172
|
+
}> extends true ? T[K] extends Scalar | null ? AttributeKey<K> : never : never : never]?: K extends 'min' | 'max' | 'step' | 'value' ? string | number | undefined : T[K] extends boolean ? boolean | undefined : T[K] extends number ? number | string | undefined : T[K] | undefined;
|
|
52
173
|
};
|
|
53
174
|
type CaptureEvents<T> = {
|
|
54
|
-
[K in keyof
|
|
175
|
+
[K in keyof ElementEvents<T> as `${K & string}Capture`]?: ElementEvents<T>[K];
|
|
55
176
|
};
|
|
56
|
-
export type Attributes<T> =
|
|
177
|
+
export type Attributes<T> = ElementEvents<T> & CaptureEvents<T> & Omit<DomAttributes<T>, 'contenteditable' | 'contentEditable' | 'spellcheck' | 'draggable' | 'translate' | 'download'> & {
|
|
57
178
|
children?: Element | undefined;
|
|
58
179
|
class?: string | undefined;
|
|
59
180
|
className?: string | undefined;
|
|
60
181
|
classList?: Record<string, boolean | undefined> | undefined;
|
|
61
182
|
style?: string | CSSProperties | undefined;
|
|
62
|
-
use?: DomMount | undefined;
|
|
183
|
+
use?: DomMount<T & globalThis.Element> | undefined;
|
|
63
184
|
role?: string | undefined;
|
|
185
|
+
nonce?: string | undefined;
|
|
64
186
|
tabindex?: number | undefined;
|
|
65
187
|
loading?: 'lazy' | 'eager' | undefined;
|
|
66
188
|
download?: string | boolean | undefined;
|
|
67
189
|
autocomplete?: string | undefined;
|
|
68
190
|
inputmode?: string | undefined;
|
|
69
|
-
spellcheck?: boolean | 'true' | 'false' | undefined;
|
|
70
|
-
contenteditable?: boolean | 'true' | 'false' | undefined;
|
|
191
|
+
spellcheck?: boolean | 'true' | 'false' | '' | undefined;
|
|
192
|
+
contenteditable?: boolean | 'true' | 'false' | 'plaintext-only' | 'inherit' | '' | undefined;
|
|
193
|
+
contentEditable?: boolean | 'true' | 'false' | 'plaintext-only' | 'inherit' | '' | undefined;
|
|
194
|
+
draggable?: boolean | 'true' | 'false' | undefined;
|
|
195
|
+
translate?: boolean | 'yes' | 'no' | '' | undefined;
|
|
196
|
+
part?: string | undefined;
|
|
197
|
+
exportparts?: string | undefined;
|
|
71
198
|
[data: `data-${string}`]: Scalar | null | undefined;
|
|
72
199
|
[aria: `aria-${string}`]: Scalar | null | undefined;
|
|
73
200
|
};
|
|
74
|
-
type
|
|
201
|
+
type ApplicableAttributes<Tag extends string> = {
|
|
202
|
+
[Name in keyof HtmlAttributes]: Tag extends keyof HtmlAttributes[Name] ? Name : '*' extends keyof HtmlAttributes[Name] ? Name : never;
|
|
203
|
+
}[keyof HtmlAttributes];
|
|
204
|
+
type SharedSvgAttributes<Tag extends string> = Tag extends keyof SVGElementTagNameMap ? SvgPresentationAttributes & (Tag extends 'a' ? Pick<SvgAttributes, 'requiredExtensions' | 'systemLanguage'> : {}) & (Tag extends 'script' ? Pick<SvgAttributes, 'href'> : {}) : {};
|
|
75
205
|
type Html = {
|
|
76
|
-
[Tag in keyof HTMLElementTagNameMap]: Attributes<HTMLElementTagNameMap[Tag]
|
|
206
|
+
[Tag in keyof HTMLElementTagNameMap]: Attributes<HTMLElementTagNameMap[Tag]> & SharedSvgAttributes<Tag> & Partial<Record<Exclude<ApplicableAttributes<Tag>, keyof Attributes<HTMLElementTagNameMap[Tag]>>, Scalar | null | undefined>>;
|
|
77
207
|
};
|
|
78
208
|
type Svg = {
|
|
79
209
|
[Tag in Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>]: Attributes<SVGElementTagNameMap[Tag]> & SvgAttributes;
|
|
80
210
|
};
|
|
81
|
-
|
|
211
|
+
/** Consumers can explicitly augment custom tags without weakening the built-in contract. */
|
|
212
|
+
export interface IntrinsicElements extends Html, Svg {
|
|
213
|
+
}
|
|
82
214
|
export interface ElementChildrenAttribute {
|
|
83
215
|
children: unknown;
|
|
84
216
|
}
|
package/dist/jsx.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
/** Nominal eligibility for compiled JSX components and compiler primitives. */
|
|
2
|
+
export const jsxComponent = Symbol('effectweb.component');
|
package/dist/lazy.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Effect, type Cause } from 'effect';
|
|
2
|
+
import { type View } from './dom.js';
|
|
3
|
+
import { type UiRuntime } from './runtime.js';
|
|
4
|
+
export interface LazyViewOptions<Model, Message, E> {
|
|
5
|
+
readonly pending?: View<Model, Message>;
|
|
6
|
+
readonly failure?: View<{
|
|
7
|
+
readonly model: Model;
|
|
8
|
+
readonly cause: Cause.Cause<E>;
|
|
9
|
+
}, Message>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Mount-owned loading with current inputs and a reusable successful definition.
|
|
13
|
+
* Unresolved placements own independent requests; unmount interrupts their work.
|
|
14
|
+
*/
|
|
15
|
+
export declare function lazyView<Model, Message = never, E = never, R = never>(load: () => Effect.Effect<View<Model, Message>, E, R>, ...provided: [R] extends [never] ? [
|
|
16
|
+
options?: LazyViewOptions<NoInfer<Model>, NoInfer<Message>, NoInfer<E>> & {
|
|
17
|
+
readonly runtime?: UiRuntime<R>;
|
|
18
|
+
}
|
|
19
|
+
] : [
|
|
20
|
+
options: LazyViewOptions<NoInfer<Model>, NoInfer<Message>, NoInfer<E>> & {
|
|
21
|
+
readonly runtime: UiRuntime<R>;
|
|
22
|
+
}
|
|
23
|
+
]): View<Model, Message>;
|
package/dist/lazy.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Effect, Fiber } from 'effect';
|
|
2
|
+
import { child, compiled, viewRegion } from './dom.js';
|
|
3
|
+
import { reportSafely } from './errors.js';
|
|
4
|
+
import { defaultUiRuntime } from './runtime.js';
|
|
5
|
+
/**
|
|
6
|
+
* Mount-owned loading with current inputs and a reusable successful definition.
|
|
7
|
+
* Unresolved placements own independent requests; unmount interrupts their work.
|
|
8
|
+
*/
|
|
9
|
+
export function lazyView(load, ...provided) {
|
|
10
|
+
const options = provided[0];
|
|
11
|
+
const runtime = options?.runtime ?? defaultUiRuntime;
|
|
12
|
+
let loaded;
|
|
13
|
+
return compiled((scope, parent, before) => {
|
|
14
|
+
const render = viewRegion(scope, parent, before);
|
|
15
|
+
if (loaded) {
|
|
16
|
+
render(loaded);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
render(options?.pending);
|
|
20
|
+
let fiber;
|
|
21
|
+
const interrupt = () => {
|
|
22
|
+
if (fiber)
|
|
23
|
+
Effect.runFork(Fiber.interrupt(fiber));
|
|
24
|
+
};
|
|
25
|
+
scope.cleanups.push(interrupt);
|
|
26
|
+
if (scope.disposed)
|
|
27
|
+
return;
|
|
28
|
+
fiber = Effect.runFork(runtime.provide(Effect.suspend(load)));
|
|
29
|
+
if (scope.disposed) {
|
|
30
|
+
interrupt();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
fiber.addObserver((exit) => {
|
|
34
|
+
if (scope.disposed)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
if (exit._tag === 'Success') {
|
|
38
|
+
loaded = exit.value;
|
|
39
|
+
render(loaded);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const failure = options?.failure;
|
|
43
|
+
if (failure)
|
|
44
|
+
render(compiled((scope, parent, before) => {
|
|
45
|
+
child(scope, parent, before, failure, () => [scope.value], () => ({ model: scope.value, cause: exit.cause }), scope.send);
|
|
46
|
+
}));
|
|
47
|
+
else {
|
|
48
|
+
render(undefined);
|
|
49
|
+
reportSafely(scope.report, exit.cause);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
reportSafely(scope.report, error);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
package/dist/load.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Effect } from 'effect';
|
|
2
|
-
/** Loaders stay lazy and use one error/cancellation model, whether their work is sync or async. */
|
|
3
2
|
export type UiLoad<A, E = unknown, R = never> = Effect.Effect<A, E, R>;
|
|
4
3
|
export declare const loadEffect: <A, E, R>(load: () => UiLoad<A, E, R>) => UiLoad<A, E, R>;
|
|
5
4
|
/** Adapt Promise libraries at the integration boundary. Pass the signal to APIs that support it. */
|
|
6
5
|
export declare const fromPromise: <A>(load: (signal: AbortSignal) => PromiseLike<A>) => UiLoad<A>;
|
|
7
6
|
export interface UiPage<A, Cursor> {
|
|
8
|
-
items: A[];
|
|
9
|
-
totalCount?: number;
|
|
10
|
-
next: Cursor | undefined;
|
|
7
|
+
readonly items: readonly A[];
|
|
8
|
+
readonly totalCount?: number;
|
|
9
|
+
readonly next: Cursor | undefined;
|
|
11
10
|
}
|
|
12
11
|
/** Read a local cache when executed, then acquire missing data through the same Effect contract. */
|
|
13
12
|
export declare function cachedLoad<A, E, R>(read: () => A | undefined, load: () => UiLoad<A, E, R>): UiLoad<A, E, R>;
|
package/dist/mount.d.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
+
import type { Snapshot } from './snapshot.js';
|
|
1
2
|
import { type ReportError } from './errors.js';
|
|
2
3
|
import { Effect } from 'effect';
|
|
3
4
|
import { type UiRuntime } from './runtime.js';
|
|
4
|
-
|
|
5
|
+
export { Portal, type PortalProps } from './dom.js';
|
|
5
6
|
type Work<R = never> = (() => void) | {
|
|
6
7
|
dispose: () => void;
|
|
7
8
|
update?: () => void;
|
|
8
9
|
} | Effect.Effect<never, unknown, R>;
|
|
9
|
-
export
|
|
10
|
-
children?: JSX.Element;
|
|
11
|
-
}): JSX.Element;
|
|
12
|
-
export interface DomMount {
|
|
10
|
+
export interface DomMount<T extends Element = Element> {
|
|
13
11
|
readonly identity: unknown;
|
|
14
12
|
readonly data: unknown;
|
|
15
|
-
readonly acquire: (element:
|
|
13
|
+
readonly acquire: (element: T, input: () => unknown) => Work;
|
|
16
14
|
}
|
|
17
15
|
/** Element-owned work. Changed acquisition functions replace and interrupt the prior lifetime. */
|
|
18
|
-
export declare function domMount<T extends Element, R = never>(start: (element: T) => Work<R>, ...provided: [R] extends [never] ? [runtime?: UiRuntime<R>] : [runtime: UiRuntime<R>]): DomMount
|
|
16
|
+
export declare function domMount<T extends Element, R = never>(start: (element: T) => Work<R>, ...provided: [R] extends [never] ? [runtime?: UiRuntime<R>] : [runtime: UiRuntime<R>]): DomMount<T>;
|
|
19
17
|
/** A stable DOM lifecycle with fresh immutable inputs, for focus, measurements and native events. */
|
|
20
|
-
export declare function domBinding<T extends Element, A, R = never>(data: A
|
|
21
|
-
export declare function startMount(element:
|
|
22
|
-
update(next: DomMount): boolean;
|
|
18
|
+
export declare function domBinding<T extends Element, A, R = never>(data: A | Snapshot<A>, acquire: (element: T, input: () => Snapshot<A>) => Work<R>, ...provided: [R] extends [never] ? [runtime?: UiRuntime<R>] : [runtime: UiRuntime<R>]): DomMount<T>;
|
|
19
|
+
export declare function startMount<T extends Element>(element: T, mount: DomMount<T>, report?: ReportError): {
|
|
20
|
+
update(next: DomMount<T>): boolean;
|
|
23
21
|
dispose(): void;
|
|
24
22
|
};
|
|
25
|
-
export {};
|
package/dist/mount.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { reportError, reportSafely } from './errors.js';
|
|
2
2
|
import { Effect, Fiber } from 'effect';
|
|
3
3
|
import { defaultUiRuntime } from './runtime.js';
|
|
4
|
-
export
|
|
5
|
-
throw new Error('Portal must be compiled inside a view');
|
|
6
|
-
}
|
|
4
|
+
export { Portal } from './dom.js';
|
|
7
5
|
/** Element-owned work. Changed acquisition functions replace and interrupt the prior lifetime. */
|
|
8
6
|
export function domMount(start, ...provided) {
|
|
9
7
|
const runtime = provided[0] ?? defaultUiRuntime;
|
package/dist/owner.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Snapshot } from './snapshot.js';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
|
-
import { type Program } from './program.js';
|
|
3
|
+
import { type Program, type TaskPolicy, type CommandSlot } from './program.js';
|
|
4
4
|
import { type ReportError } from './errors.js';
|
|
5
5
|
import type { UiRuntime } from './runtime.js';
|
|
6
|
-
export type TaskPolicy
|
|
6
|
+
export type { TaskPolicy } from './program.js';
|
|
7
7
|
export interface DisposableOwner {
|
|
8
8
|
readonly disposed: boolean;
|
|
9
9
|
readonly own: <A extends {
|
|
@@ -13,18 +13,21 @@ export interface DisposableOwner {
|
|
|
13
13
|
export interface ModelOwner<Model extends object, R = never> extends DisposableOwner {
|
|
14
14
|
readonly source: Program<Model, never>;
|
|
15
15
|
readonly read: () => Snapshot<Model>;
|
|
16
|
-
|
|
17
|
-
readonly
|
|
16
|
+
/** Expose only controller-selected editable fields to a view. */
|
|
17
|
+
readonly fields: <const Keys extends readonly (keyof Model)[]>(...keys: Keys) => {
|
|
18
|
+
readonly [K in Keys[number]]: (value: Model[K] | Snapshot<Model[K]>) => void;
|
|
19
|
+
};
|
|
20
|
+
readonly patch: (changes: Partial<Model> | Partial<Snapshot<Model>>) => void;
|
|
21
|
+
readonly edit: <K extends keyof Model>(key: K, change: (value: Snapshot<Model[K]>) => Model[K] | Snapshot<Model[K]>) => void;
|
|
18
22
|
/** Synchronous transaction. Reads see staged changes; a throw discards changes and work. */
|
|
19
23
|
readonly transaction: <A>(work: () => A & (A extends PromiseLike<unknown> ? never : unknown)) => A;
|
|
20
|
-
readonly run: (slot:
|
|
21
|
-
readonly cancel: (slot:
|
|
22
|
-
readonly isRunning: (slot:
|
|
24
|
+
readonly run: (slot: CommandSlot, effect: Effect.Effect<unknown, unknown, R>, policy: TaskPolicy) => void;
|
|
25
|
+
readonly cancel: (slot: CommandSlot) => void;
|
|
26
|
+
readonly isRunning: (slot: CommandSlot) => boolean;
|
|
23
27
|
readonly awaitIdle: () => Promise<void>;
|
|
24
28
|
readonly dispose: () => void;
|
|
25
29
|
}
|
|
26
30
|
type Options = {
|
|
27
|
-
checkSnapshots?: boolean;
|
|
28
31
|
name?: string;
|
|
29
32
|
onDefect?: ReportError;
|
|
30
33
|
};
|
|
@@ -32,4 +35,3 @@ export declare function modelOwner<Model extends object>(initial: Model, options
|
|
|
32
35
|
export declare function modelOwner<Model extends object, R>(initial: Model, options: Options & {
|
|
33
36
|
runtime: UiRuntime<R>;
|
|
34
37
|
}): ModelOwner<Model, R>;
|
|
35
|
-
export {};
|
package/dist/owner.js
CHANGED
|
@@ -1,63 +1,44 @@
|
|
|
1
|
-
import { protectSnapshot
|
|
1
|
+
import { protectSnapshot } from './snapshot.js';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
|
-
import { program } from './program.js';
|
|
3
|
+
import { program, } from './program.js';
|
|
4
4
|
import { patchModel } from './state.js';
|
|
5
5
|
import { runAll, reportError } from './errors.js';
|
|
6
|
-
/** Immutable model publication and task ownership, using the same program queue as components. */
|
|
7
6
|
export function modelOwner(initial, options = {}) {
|
|
8
|
-
let disposed = false
|
|
7
|
+
let disposed = false;
|
|
9
8
|
let staged;
|
|
10
|
-
const groups = new Map();
|
|
11
9
|
const cleanups = [];
|
|
12
10
|
const source = program({
|
|
13
11
|
initial,
|
|
14
12
|
...options,
|
|
15
|
-
update(
|
|
13
|
+
update(snapshot, operations) {
|
|
14
|
+
let model = snapshot;
|
|
16
15
|
const single = operations.length === 1 ? operations[0] : undefined;
|
|
17
16
|
if (single?.type === 'Patch')
|
|
18
17
|
return { model: patchModel(model, single.changes) };
|
|
19
|
-
const commands =
|
|
18
|
+
const commands = [];
|
|
20
19
|
const cancel = new Set();
|
|
21
|
-
const stop = (slot) => {
|
|
22
|
-
for (const key of groups.get(slot) ?? []) {
|
|
23
|
-
cancel.add(key);
|
|
24
|
-
commands.delete(key);
|
|
25
|
-
}
|
|
26
|
-
groups.delete(slot);
|
|
27
|
-
};
|
|
28
20
|
for (const operation of operations) {
|
|
29
21
|
if (operation.type === 'Patch')
|
|
30
22
|
model = patchModel(model, operation.changes);
|
|
31
|
-
else if (operation.type === 'Cancel')
|
|
32
|
-
|
|
23
|
+
else if (operation.type === 'Cancel') {
|
|
24
|
+
cancel.add(operation.slot);
|
|
25
|
+
for (let index = commands.length - 1; index >= 0; index--)
|
|
26
|
+
if (commands[index].slot === operation.slot)
|
|
27
|
+
commands.splice(index, 1);
|
|
28
|
+
}
|
|
33
29
|
else {
|
|
34
30
|
const { slot, effect, policy } = operation;
|
|
35
|
-
|
|
36
|
-
continue;
|
|
37
|
-
if (policy === 'replace')
|
|
38
|
-
stop(slot);
|
|
39
|
-
const key = `${slot}:${++nextTask}`;
|
|
40
|
-
const group = groups.get(slot) ?? new Set();
|
|
41
|
-
groups.set(slot, group);
|
|
42
|
-
group.add(key);
|
|
43
|
-
commands.set(key, {
|
|
44
|
-
slot: key,
|
|
45
|
-
action: Effect.asVoid(effect).pipe(Effect.ensuring(Effect.sync(() => {
|
|
46
|
-
group.delete(key);
|
|
47
|
-
if (!group.size && groups.get(slot) === group)
|
|
48
|
-
groups.delete(slot);
|
|
49
|
-
}))),
|
|
50
|
-
});
|
|
31
|
+
commands.push({ slot, policy, action: Effect.asVoid(effect) });
|
|
51
32
|
}
|
|
52
33
|
}
|
|
53
34
|
return {
|
|
54
35
|
model,
|
|
55
36
|
cancel: [...cancel],
|
|
56
|
-
commands:
|
|
37
|
+
commands: commands.map((command) => options.runtime ? options.runtime.command(command) : command),
|
|
57
38
|
};
|
|
58
39
|
},
|
|
59
40
|
});
|
|
60
|
-
const read = () => protectSnapshot(staged?.model ?? source.model()
|
|
41
|
+
const read = () => protectSnapshot(staged?.model ?? source.model());
|
|
61
42
|
const submit = (operation) => {
|
|
62
43
|
if (disposed)
|
|
63
44
|
return;
|
|
@@ -75,17 +56,25 @@ export function modelOwner(initial, options = {}) {
|
|
|
75
56
|
return;
|
|
76
57
|
disposed = true;
|
|
77
58
|
source.dispose();
|
|
78
|
-
groups.clear();
|
|
79
59
|
runAll(cleanups.splice(0).reverse(), options.onDefect ?? reportError);
|
|
80
60
|
};
|
|
81
61
|
return {
|
|
82
62
|
source: { model: source.model, send: source.send, subscribe: source.subscribe, dispose },
|
|
83
63
|
read,
|
|
84
64
|
patch,
|
|
65
|
+
fields: (...keys) => {
|
|
66
|
+
const controls = Object.create(null);
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
Object.defineProperty(controls, key, {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
value: (value) => patch({ [key]: value }),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return Object.freeze(controls);
|
|
74
|
+
},
|
|
85
75
|
edit: (key, change) => {
|
|
86
76
|
if (!disposed) {
|
|
87
77
|
const changes = {};
|
|
88
|
-
// Readonly changes remain immutable model data; no clone is needed for a no-op.
|
|
89
78
|
changes[key] = change(read()[key]);
|
|
90
79
|
patch(changes);
|
|
91
80
|
}
|
|
@@ -118,9 +107,9 @@ export function modelOwner(initial, options = {}) {
|
|
|
118
107
|
staged = parent;
|
|
119
108
|
}
|
|
120
109
|
},
|
|
121
|
-
run: (slot, effect, policy
|
|
110
|
+
run: (slot, effect, policy) => submit({ type: 'Run', slot, effect, policy }),
|
|
122
111
|
cancel: (slot) => submit({ type: 'Cancel', slot }),
|
|
123
|
-
isRunning: (slot) =>
|
|
112
|
+
isRunning: (slot) => source.activeSlots().includes(slot),
|
|
124
113
|
awaitIdle: source.awaitIdle,
|
|
125
114
|
get disposed() {
|
|
126
115
|
return disposed;
|
package/dist/pages.d.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
import type { Snapshot } from './snapshot.js';
|
|
1
2
|
import { Cause } from 'effect';
|
|
2
3
|
import * as AsyncResult from 'effect/unstable/reactivity/AsyncResult';
|
|
3
4
|
import type { UiLoad, UiPage } from './load.js';
|
|
4
|
-
import { type Transition, type
|
|
5
|
+
import { type Transition, type RunningProgram } from './program.js';
|
|
5
6
|
export interface PagesModel<Props, A, Cursor> {
|
|
6
7
|
readonly props: Props;
|
|
7
8
|
readonly key: string | undefined;
|
|
8
9
|
readonly result: AsyncResult.AsyncResult<UiPage<A, Cursor>, unknown>;
|
|
9
10
|
readonly append: boolean;
|
|
10
11
|
}
|
|
11
|
-
export type
|
|
12
|
+
export type PagesRequest = {
|
|
12
13
|
type: 'More';
|
|
13
14
|
} | {
|
|
14
15
|
type: 'Retry';
|
|
15
16
|
} | {
|
|
16
17
|
type: 'Refresh';
|
|
17
|
-
}
|
|
18
|
+
};
|
|
19
|
+
/** Low-level reducer protocol, including completions for explicit program composition. */
|
|
20
|
+
export type PagesMessage<A, Cursor> = PagesRequest | {
|
|
18
21
|
type: 'Loaded';
|
|
19
22
|
page: UiPage<A, Cursor>;
|
|
20
23
|
append: boolean;
|
|
@@ -22,29 +25,26 @@ export type PagesMessage<A, Cursor> = {
|
|
|
22
25
|
type: 'Failed';
|
|
23
26
|
cause: Cause.Cause<unknown>;
|
|
24
27
|
};
|
|
28
|
+
/** An owned pagination source with immutable input updates. */
|
|
29
|
+
export interface PagesProgram<Props, A, Cursor> extends RunningProgram<PagesModel<Props, A, Cursor>, PagesRequest> {
|
|
30
|
+
readonly receive: (props: Props | Snapshot<Props>) => void;
|
|
31
|
+
}
|
|
32
|
+
/** Reusable pagination transitions, or an independently owned source through create. */
|
|
33
|
+
export interface Pagination<Props, A, Cursor> {
|
|
34
|
+
readonly init: (props: Props | Snapshot<Props>) => PagesModel<Props, A, Cursor> | Snapshot<PagesModel<Props, A, Cursor>>;
|
|
35
|
+
readonly receive: (model: PagesModel<Props, A, Cursor> | Snapshot<PagesModel<Props, A, Cursor>>, props: Props | Snapshot<Props>, options?: {
|
|
36
|
+
refresh?: boolean;
|
|
37
|
+
}) => Transition<PagesModel<Props, A, Cursor>, PagesMessage<A, Cursor>>;
|
|
38
|
+
readonly update: (model: PagesModel<Props, A, Cursor> | Snapshot<PagesModel<Props, A, Cursor>>, message: PagesMessage<A, Cursor>) => Transition<PagesModel<Props, A, Cursor>, PagesMessage<A, Cursor>>;
|
|
39
|
+
readonly create: (props: Props | Snapshot<Props>) => PagesProgram<Props, A, Cursor>;
|
|
40
|
+
}
|
|
25
41
|
/**
|
|
26
42
|
* Pagination owns the `page` command slot. Key changes clear results and replace the request;
|
|
27
43
|
* same-key refreshes keep the last success, and retries repeat the failed cursor.
|
|
28
44
|
* Feed request inputs through receive; pass { refresh: true } for explicit same-key submissions.
|
|
29
45
|
*/
|
|
30
46
|
export declare function pages<Props, A, Cursor>(definition: {
|
|
31
|
-
key: (props: Props) => string | undefined;
|
|
32
|
-
load: (props: Props
|
|
33
|
-
itemKey: (item: A) => string;
|
|
34
|
-
}):
|
|
35
|
-
/** Own pagination directly in a controller; components can use the same reducer. */
|
|
36
|
-
create(props: Props): {
|
|
37
|
-
send: Send<PagesMessage<A, Cursor>>;
|
|
38
|
-
receive: (props: Props) => void;
|
|
39
|
-
activeSlots: () => readonly string[];
|
|
40
|
-
awaitIdle: (slot?: string) => Promise<void>;
|
|
41
|
-
model: () => Readonly<PagesModel<Props, A, Cursor>>;
|
|
42
|
-
subscribe: (listener: (model: Readonly<PagesModel<Props, A, Cursor>>) => void) => () => void;
|
|
43
|
-
dispose: () => void;
|
|
44
|
-
};
|
|
45
|
-
init: (props: Props) => PagesModel<Props, A, Cursor>;
|
|
46
|
-
receive(model: PagesModel<Props, A, Cursor>, props: Props, options?: {
|
|
47
|
-
refresh?: boolean;
|
|
48
|
-
}): Transition<PagesModel<Props, A, Cursor>, PagesMessage<A, Cursor>>;
|
|
49
|
-
update(model: PagesModel<Props, A, Cursor>, message: PagesMessage<A, Cursor>): Transition<PagesModel<Props, A, Cursor>, PagesMessage<A, Cursor>>;
|
|
50
|
-
};
|
|
47
|
+
key: (props: Snapshot<Props>) => string | undefined;
|
|
48
|
+
load: (props: Snapshot<Props>, cursor: Snapshot<Cursor> | undefined) => UiLoad<UiPage<A, Cursor>>;
|
|
49
|
+
itemKey: (item: Snapshot<A>) => string;
|
|
50
|
+
}): Pagination<Props, A, Cursor>;
|