effectweb 0.2.3 → 0.3.1

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 (77) hide show
  1. package/README.md +93 -1
  2. package/dist/AsyncContent.d.ts +2 -2
  3. package/dist/AsyncContent.js +7 -4
  4. package/dist/actions.d.ts +4 -3
  5. package/dist/actions.js +1 -0
  6. package/dist/boundary.d.ts +11 -0
  7. package/dist/boundary.js +66 -0
  8. package/dist/cache-internals.d.ts +18 -0
  9. package/dist/cache-internals.js +11 -0
  10. package/dist/cache.d.ts +35 -16
  11. package/dist/cache.js +258 -37
  12. package/dist/collection.d.ts +10 -9
  13. package/dist/collection.js +32 -6
  14. package/dist/component.d.ts +11 -7
  15. package/dist/component.js +70 -10
  16. package/dist/diagnostics.d.ts +25 -0
  17. package/dist/diagnostics.js +167 -1
  18. package/dist/dom-attributes.json +71 -0
  19. package/dist/dom.d.ts +44 -15
  20. package/dist/dom.js +493 -89
  21. package/dist/effectEvent.d.ts +2 -1
  22. package/dist/effectEvent.js +4 -1
  23. package/dist/form.d.ts +61 -0
  24. package/dist/form.js +98 -0
  25. package/dist/html-attributes.d.ts +602 -0
  26. package/dist/html-attributes.js +1 -0
  27. package/dist/http.d.ts +5 -0
  28. package/dist/http.js +7 -0
  29. package/dist/index.d.ts +16 -11
  30. package/dist/index.js +11 -6
  31. package/dist/infinite-query.d.ts +42 -0
  32. package/dist/infinite-query.js +150 -0
  33. package/dist/intrinsic.d.ts +2 -0
  34. package/dist/intrinsic.js +120 -0
  35. package/dist/jsx.d.ts +165 -33
  36. package/dist/jsx.js +2 -1
  37. package/dist/keyed-tasks.d.ts +29 -0
  38. package/dist/keyed-tasks.js +97 -0
  39. package/dist/lazy.d.ts +23 -0
  40. package/dist/lazy.js +66 -0
  41. package/dist/load.d.ts +3 -3
  42. package/dist/mount.d.ts +11 -12
  43. package/dist/mount.js +30 -14
  44. package/dist/owner.d.ts +14 -10
  45. package/dist/owner.js +66 -39
  46. package/dist/pages.d.ts +28 -26
  47. package/dist/pages.js +19 -11
  48. package/dist/program.d.ts +29 -9
  49. package/dist/program.js +204 -61
  50. package/dist/query-internals.d.ts +13 -0
  51. package/dist/query-internals.js +10 -0
  52. package/dist/query.d.ts +30 -19
  53. package/dist/query.js +64 -5
  54. package/dist/resource.d.ts +4 -10
  55. package/dist/resource.js +14 -5
  56. package/dist/runtime.d.ts +1 -2
  57. package/dist/runtime.js +3 -3
  58. package/dist/session.d.ts +12 -15
  59. package/dist/session.js +25 -13
  60. package/dist/settlement.d.ts +7 -0
  61. package/dist/settlement.js +27 -0
  62. package/dist/share.d.ts +5 -4
  63. package/dist/share.js +5 -44
  64. package/dist/sharing.d.ts +6 -0
  65. package/dist/sharing.js +96 -0
  66. package/dist/slotIdentity.d.ts +1 -0
  67. package/dist/slotIdentity.js +2 -0
  68. package/dist/snapshot.d.ts +16 -5
  69. package/dist/snapshot.js +34 -27
  70. package/dist/svg-attributes.d.ts +12 -0
  71. package/dist/svg-attributes.js +1 -0
  72. package/dist/task.d.ts +9 -8
  73. package/dist/task.js +75 -47
  74. package/dist/tasks.d.ts +20 -15
  75. package/dist/tasks.js +40 -25
  76. package/dist/testing.d.ts +10 -14
  77. package/package.json +13 -1
@@ -0,0 +1,42 @@
1
+ import { Effect } from 'effect';
2
+ import { type Query, type QueryArgs, type QueryGroup } from './query.js';
3
+ import type { QueryCache } from './cache.js';
4
+ import { type QueryResource } from './session.js';
5
+ import { type Snapshot } from './snapshot.js';
6
+ export interface InfiniteData<A, Param> {
7
+ readonly pages: readonly {
8
+ readonly param: Param;
9
+ readonly value: A;
10
+ }[];
11
+ readonly next: Param | undefined;
12
+ }
13
+ export interface InfiniteQuery<Args, A, Param, E = never, R = never> {
14
+ readonly query: Query<Args, InfiniteData<A, Param>, E, R>;
15
+ readonly page: Query<{
16
+ args: Args;
17
+ param: Param;
18
+ }, A, E, R>;
19
+ readonly maxPages: number;
20
+ readonly next: (value: Snapshot<A>, param: Snapshot<Param>) => Param | undefined;
21
+ }
22
+ /** Shared pages with explicit cursor identity. Refresh preserves retained page parameters by default. */
23
+ export declare function infiniteQuery<Args, A, Param, E = never, R = never>(definition: {
24
+ readonly name: string;
25
+ readonly initial: Param;
26
+ readonly load: (args: Snapshot<Args>, param: Snapshot<Param>) => Effect.Effect<A, E, R>;
27
+ readonly next: (value: Snapshot<A>, param: Snapshot<Param>) => Param | undefined;
28
+ readonly maxPages?: number;
29
+ readonly refresh?: 'retained' | 'first';
30
+ readonly groups?: readonly QueryGroup[];
31
+ } & (Args extends QueryArgs<Args> ? unknown : {
32
+ readonly nonSerializableQueryArguments: never;
33
+ }) & (Param extends QueryArgs<Param> ? unknown : {
34
+ readonly nonSerializablePageParameters: never;
35
+ })): InfiniteQuery<Args, A, Param, E, R>;
36
+ export interface InfiniteResource<Args, A, Param, E = never> extends QueryResource<Args, InfiniteData<A, Param>, E> {
37
+ loadNext(): Effect.Effect<Snapshot<InfiniteData<A, Param>>, E>;
38
+ retryPage(param: Param | Snapshot<Param>): Effect.Effect<Snapshot<InfiniteData<A, Param>>, E>;
39
+ seed(args: Args | Snapshot<Args>, data: InfiniteData<A, Param> | Snapshot<InfiniteData<A, Param>>): Snapshot<InfiniteData<A, Param>>;
40
+ }
41
+ /** Cache-owned results, observer-owned subscriptions. Operations return typed Effects for task composition. */
42
+ export declare function infiniteResource<Args, A, Param, E, R>(cache: QueryCache<R>, definition: InfiniteQuery<Args, A, Param, E, NoInfer<R>>): InfiniteResource<Args, A, Param, E>;
@@ -0,0 +1,150 @@
1
+ import { Effect } from 'effect';
2
+ import { query, encodeQueryKey, } from './query.js';
3
+ import { cacheInternals } from './cache-internals.js';
4
+ import { queryResource } from './session.js';
5
+ import { protectSnapshot } from './snapshot.js';
6
+ /** Shared pages with explicit cursor identity. Refresh preserves retained page parameters by default. */
7
+ export function infiniteQuery(definition) {
8
+ const config = Object.freeze({ ...definition });
9
+ const maxPages = config.maxPages ?? Infinity;
10
+ if (maxPages !== Infinity && (!Number.isInteger(maxPages) || maxPages < 1))
11
+ throw new RangeError('maxPages must be a positive integer.');
12
+ encodeQueryKey(config.initial);
13
+ const initial = protectSnapshot(config.initial);
14
+ const next = (value, param) => {
15
+ const result = config.next(value, param);
16
+ encodeQueryKey(result);
17
+ return result;
18
+ };
19
+ const page = query({
20
+ name: `${config.name}:page`,
21
+ load: ({ args, param }) => config.load(args, param),
22
+ });
23
+ const root = query({
24
+ name: config.name,
25
+ ...(config.groups ? { groups: config.groups } : {}),
26
+ load: (args, previous) => {
27
+ const params = config.refresh !== 'first' && previous?.pages.length
28
+ ? previous.pages.map((page) => page.param)
29
+ : [initial];
30
+ return Effect.forEach(params, (param) => config.load(args, param).pipe(Effect.map((value) => ({ param: param, value })))).pipe(Effect.map((pages) => {
31
+ const last = pages[pages.length - 1];
32
+ return {
33
+ pages,
34
+ next: next(protectSnapshot(last.value), last.param),
35
+ };
36
+ }));
37
+ },
38
+ });
39
+ return Object.freeze({ query: root, page, maxPages, next });
40
+ }
41
+ const operations = new WeakMap();
42
+ function operationBook(cache, definition) {
43
+ let definitions = operations.get(cache);
44
+ if (!definitions) {
45
+ definitions = new WeakMap();
46
+ operations.set(cache, definitions);
47
+ }
48
+ let book = definitions.get(definition);
49
+ if (!book) {
50
+ book = new Map();
51
+ definitions.set(definition, book);
52
+ }
53
+ return book;
54
+ }
55
+ /** Cache-owned results, observer-owned subscriptions. Operations return typed Effects for task composition. */
56
+ export function infiniteResource(cache, definition) {
57
+ const resource = queryResource({ cache }, definition.query);
58
+ const internal = cacheInternals(cache);
59
+ let selected;
60
+ let disposed = false;
61
+ const stopReset = cache.onReset(() => {
62
+ selected = undefined;
63
+ });
64
+ const book = operationBook(cache, definition.query);
65
+ const updatePage = (retry) => Effect.gen(function* () {
66
+ if (disposed || selected === undefined)
67
+ return yield* Effect.interrupt;
68
+ const args = selected;
69
+ const current = yield* cache.prefetch(definition.query, args);
70
+ const param = retry ? retry.param : current.next;
71
+ if (param === undefined)
72
+ return current;
73
+ const pageKey = encodeQueryKey(param);
74
+ const retained = current.pages.some((page) => encodeQueryKey(page.param) === pageKey);
75
+ if (retry && !retained && encodeQueryKey(current.next) !== pageKey)
76
+ return yield* Effect.die(new RangeError('Retry a retained page or the next page parameter.'));
77
+ const key = encodeQueryKey(args);
78
+ const revision = internal.revision(definition.query, args);
79
+ let operation = book.get(key);
80
+ if (!operation || operation.revision !== revision) {
81
+ operation = { revision, active: 0 };
82
+ book.set(key, operation);
83
+ }
84
+ const state = operation;
85
+ state.active++;
86
+ return yield* Effect.gen(function* () {
87
+ const pageArgs = { args, param };
88
+ const value = yield* cache.prefetch(definition.page, pageArgs, { refresh: true });
89
+ let result;
90
+ cache.batch(() => {
91
+ const latest = cache.getQueryData(definition.query, args);
92
+ result = latest;
93
+ // Other page operations can merge. External refresh, writes and reset own a new revision.
94
+ if (!latest ||
95
+ book.get(key) !== state ||
96
+ state.revision !== internal.revision(definition.query, args))
97
+ return;
98
+ const index = latest.pages.findIndex((page) => encodeQueryKey(page.param) === pageKey);
99
+ if (index < 0 && (retained || encodeQueryKey(latest.next) !== pageKey))
100
+ return;
101
+ if (index >= 0 && latest.pages[index].value === value)
102
+ return;
103
+ const pages = [...latest.pages];
104
+ const page = { param, value };
105
+ if (index >= 0)
106
+ pages[index] = page;
107
+ else
108
+ pages.push(page);
109
+ const kept = pages.slice(-definition.maxPages);
110
+ const last = kept[kept.length - 1];
111
+ result = cache.setQueryData(definition.query, args, {
112
+ pages: kept,
113
+ next: definition.next(last.value, last.param),
114
+ });
115
+ state.revision = internal.revision(definition.query, args);
116
+ });
117
+ return result ?? (yield* Effect.interrupt);
118
+ }).pipe(Effect.ensuring(Effect.sync(() => {
119
+ state.active--;
120
+ if (state.active === 0 && book.get(key) === state)
121
+ book.delete(key);
122
+ })));
123
+ });
124
+ return {
125
+ read: resource.read,
126
+ subscribe: resource.subscribe,
127
+ select(args) {
128
+ selected = args;
129
+ resource.select(args);
130
+ },
131
+ refresh: resource.refresh,
132
+ loadNext: () => updatePage(),
133
+ retryPage: (param) => updatePage({ param }),
134
+ seed(args, data) {
135
+ if (!data.pages.length || data.pages.length > definition.maxPages)
136
+ throw new RangeError('Seed pages must fit the retained page range.');
137
+ encodeQueryKey(data.next);
138
+ const keys = data.pages.map((page) => encodeQueryKey(page.param));
139
+ if (new Set(keys).size !== keys.length)
140
+ throw new TypeError('Seed page parameters must be unique.');
141
+ return cache.setQueryData(definition.query, args, data);
142
+ },
143
+ dispose() {
144
+ disposed = true;
145
+ selected = undefined;
146
+ stopReset();
147
+ resource.dispose();
148
+ },
149
+ };
150
+ }
@@ -0,0 +1,2 @@
1
+ /** Preserve receiver identity and JavaScript call/optional-chain semantics. */
2
+ export declare function intrinsic<T>(receiver: T, path: readonly (string | null)[], method: string): T;
@@ -0,0 +1,120 @@
1
+ import { compiledSlots } from './slotIdentity.js';
2
+ // Compiler implementation detail. An untyped receiver's method name is not
3
+ // evidence that it is a standard data operation. Validate its descriptor before
4
+ // invoking it, without running a user getter in the process.
5
+ const prototypes = [
6
+ Array.prototype,
7
+ String.prototype,
8
+ Number.prototype,
9
+ Boolean.prototype,
10
+ BigInt.prototype,
11
+ Function.prototype,
12
+ Date.prototype,
13
+ RegExp.prototype,
14
+ Map.prototype,
15
+ Set.prototype,
16
+ URL.prototype,
17
+ URLSearchParams.prototype,
18
+ Blob.prototype,
19
+ File.prototype,
20
+ Object.getPrototypeOf(Uint8Array.prototype),
21
+ Intl.DateTimeFormat.prototype,
22
+ Intl.NumberFormat.prototype,
23
+ Intl.Collator.prototype,
24
+ Intl.RelativeTimeFormat.prototype,
25
+ Intl.PluralRules.prototype,
26
+ Intl.ListFormat.prototype,
27
+ Intl.Segmenter.prototype,
28
+ ];
29
+ const operations = new Map();
30
+ for (const prototype of prototypes) {
31
+ for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(prototype))) {
32
+ if (name === 'constructor')
33
+ continue;
34
+ const entries = operations.get(name) ?? [];
35
+ entries.push(descriptor);
36
+ operations.set(name, entries);
37
+ }
38
+ }
39
+ /** Preserve receiver identity and JavaScript call/optional-chain semantics. */
40
+ export function intrinsic(receiver, path, method) {
41
+ checkPath(receiver, path, 0, method);
42
+ return receiver;
43
+ }
44
+ function checkPath(target, path, index, method) {
45
+ if (target === null || target === undefined)
46
+ return;
47
+ if (index === path.length) {
48
+ checkIntrinsic(target, method);
49
+ return;
50
+ }
51
+ const field = path[index];
52
+ if (field === null) {
53
+ if (Array.isArray(target)) {
54
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(target))) {
55
+ if (!/^(0|[1-9]\d*)$/u.test(key))
56
+ continue;
57
+ if (!Object.hasOwn(descriptor, 'value'))
58
+ throw new Error('EffectWeb render data cannot use an array accessor. Pass immutable values.');
59
+ checkPath(descriptor.value, path, index + 1, method);
60
+ }
61
+ }
62
+ else if (target instanceof Map) {
63
+ for (const value of Map.prototype.values.call(target))
64
+ checkPath(value, path, index + 1, method);
65
+ }
66
+ else if (target instanceof Set) {
67
+ for (const value of Set.prototype.values.call(target))
68
+ checkPath(value, path, index + 1, method);
69
+ }
70
+ return;
71
+ }
72
+ let object = Object(target);
73
+ while (object) {
74
+ const descriptor = Object.getOwnPropertyDescriptor(object, field);
75
+ if (descriptor) {
76
+ if (Object.hasOwn(descriptor, 'value')) {
77
+ checkPath(descriptor.value, path, index + 1, method);
78
+ }
79
+ else if (descriptor.get &&
80
+ operations.get(field)?.some((operation) => operation.get === descriptor.get)) {
81
+ // Native getters are checked by identity before execution. File/Blob
82
+ // metadata and collection sizes expose data through readonly accessors.
83
+ checkPath(descriptor.get.call(target), path, index + 1, method);
84
+ }
85
+ else {
86
+ throw new Error('EffectWeb render data cannot use an accessor to supply a standard data operation. Pass immutable data or a checked helper.');
87
+ }
88
+ return;
89
+ }
90
+ object = Object.getPrototypeOf(object);
91
+ }
92
+ }
93
+ function checkIntrinsic(receiver, method) {
94
+ if (receiver === null || receiver === undefined)
95
+ return;
96
+ if (method === '@slot') {
97
+ if (typeof receiver !== 'function' || !compiledSlots.has(receiver))
98
+ throw new Error('EffectWeb render callbacks must be compiled slots. Declare markup with slot(), or use a checked helper with an explicit purity contract.');
99
+ return;
100
+ }
101
+ let object = Object(receiver);
102
+ while (object) {
103
+ const descriptor = Object.getOwnPropertyDescriptor(object, method);
104
+ if (descriptor) {
105
+ const known = operations
106
+ .get(method)
107
+ ?.some((operation) => Object.hasOwn(descriptor, 'value')
108
+ ? typeof descriptor.value === 'function' && descriptor.value === operation.value
109
+ : descriptor.get !== undefined &&
110
+ descriptor.get === operation.get &&
111
+ descriptor.set === undefined);
112
+ if (!known)
113
+ throw new Error(`EffectWeb cannot prove .${method} is a standard data operation. Use a checked local helper or an imported helper with a purity contract.`);
114
+ return;
115
+ }
116
+ object = Object.getPrototypeOf(object);
117
+ }
118
+ // The original member call retains its own missing/optional-method behavior.
119
+ return;
120
+ }
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 type Element = Node | string | number | boolean | null | undefined | CompiledContent | Element[];
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
- onClick?: EventHandler<T, MouseEvent> | undefined;
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
- onContextMenu?: EventHandler<T, MouseEvent> | undefined;
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
- onChange?: EventHandler<T, Event> | undefined;
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
- onPointerDown?: EventHandler<T, PointerEvent> | undefined;
25
- onPointerUp?: EventHandler<T, PointerEvent> | undefined;
26
- onPointerMove?: EventHandler<T, PointerEvent> | undefined;
27
- onPointerEnter?: EventHandler<T, PointerEvent> | undefined;
28
- onPointerLeave?: EventHandler<T, PointerEvent> | undefined;
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
- onFocus?: EventHandler<T, FocusEvent> | undefined;
34
- onBlur?: EventHandler<T, FocusEvent> | undefined;
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
- onWheel?: EventHandler<T, WheelEvent> | undefined;
37
- onLoad?: EventHandler<T, Event> | undefined;
38
- onError?: EventHandler<T, Event> | undefined;
39
- onLoadedMetadata?: EventHandler<T, Event> | undefined;
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
- onEnded?: EventHandler<T, Event> | undefined;
42
- onPlay?: EventHandler<T, Event> | undefined;
43
- onPause?: EventHandler<T, Event> | undefined;
44
- onPaste?: EventHandler<T, ClipboardEvent> | undefined;
45
- onDragOver?: EventHandler<T, DragEvent> | undefined;
46
- onDragLeave?: EventHandler<T, DragEvent> | undefined;
47
- onDrop?: EventHandler<T, DragEvent> | undefined;
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 ? T[K] extends Scalar | null ? K | Lowercase<K> : 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;
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 Events<T> as `${K}Capture`]?: Events<T>[K];
175
+ [K in keyof ElementEvents<T> as `${K & string}Capture`]?: ElementEvents<T>[K];
55
176
  };
56
- export type Attributes<T> = Events<T> & CaptureEvents<T> & DomAttributes<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 SvgAttributes = Partial<Record<'viewBox' | 'fill' | 'stroke' | 'stroke-width' | 'stroke-linecap' | 'stroke-linejoin' | 'stroke-dasharray' | 'stroke-dashoffset' | 'stroke-miterlimit' | 'fill-rule' | 'clip-rule' | 'fill-opacity' | 'stroke-opacity' | 'd' | 'points' | 'x' | 'y' | 'x1' | 'x2' | 'y1' | 'y2' | 'cx' | 'cy' | 'r' | 'rx' | 'ry' | 'width' | 'height' | 'xmlns' | 'transform' | 'preserveAspectRatio' | 'offset' | 'stop-color' | 'stop-opacity' | 'clip-path' | 'mask' | 'opacity' | 'pathLength', string | number | undefined>>;
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
- export type IntrinsicElements = Html & Svg;
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
- export {};
1
+ /** Nominal eligibility for compiled JSX components and compiler primitives. */
2
+ export const jsxComponent = Symbol('effectweb.component');
@@ -0,0 +1,29 @@
1
+ import { Cause, Effect } from 'effect';
2
+ import { type TaskPolicy } from './program.js';
3
+ import type { DisposableOwner } from './owner.js';
4
+ import { type UiRuntime } from './runtime.js';
5
+ import { type Snapshot } from './snapshot.js';
6
+ export type TaskOutcome<A, E> = {
7
+ readonly _tag: 'Success';
8
+ readonly value: Snapshot<A>;
9
+ } | {
10
+ readonly _tag: 'Failure';
11
+ readonly cause: Cause.Cause<E>;
12
+ } | {
13
+ readonly _tag: 'Cancelled' | 'Superseded' | 'Dropped';
14
+ };
15
+ export interface TaskHandle<A, E> {
16
+ readonly outcome: Promise<TaskOutcome<A, E>>;
17
+ }
18
+ /** Per-entity serialization. Inputs are captured now; run reads services when work starts. */
19
+ export declare function keyedTasks<Key, Input, A, E = never, R = never>(owner: DisposableOwner, definition: {
20
+ readonly name: string;
21
+ readonly policy: TaskPolicy;
22
+ readonly run: (key: Key, input: Snapshot<Input>) => Effect.Effect<A, E, R>;
23
+ }, ...provided: [R] extends [never] ? [runtime?: UiRuntime<R>] : [runtime: UiRuntime<R>]): {
24
+ submit(key: Key, input: Input | Snapshot<Input>): TaskHandle<A, E>;
25
+ cancel(key: Key): void;
26
+ drain(...selected: [] | [Key]): Promise<void>;
27
+ close: () => Promise<void>;
28
+ dispose: () => void;
29
+ };