effectweb 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +35 -4
  2. package/dist/AsyncContent.d.ts +1 -1
  3. package/dist/AsyncContent.js +18 -35
  4. package/dist/cache.d.ts +2 -2
  5. package/dist/cache.js +7 -3
  6. package/dist/collection.d.ts +1 -1
  7. package/dist/collection.js +1 -3
  8. package/dist/component.js +5 -2
  9. package/dist/dom.d.ts +26 -23
  10. package/dist/dom.js +150 -119
  11. package/dist/effectEvent.d.ts +3 -3
  12. package/dist/effectEvent.js +27 -39
  13. package/dist/form.d.ts +2 -1
  14. package/dist/http.d.ts +2 -1
  15. package/dist/http.js +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/infinite-query.d.ts +1 -1
  19. package/dist/infinite-query.js +1 -1
  20. package/dist/jsx-runtime.d.ts +6 -0
  21. package/dist/jsx-runtime.js +14 -1
  22. package/dist/jsx.d.ts +2 -1
  23. package/dist/keyed-tasks.d.ts +5 -4
  24. package/dist/keyed-tasks.js +27 -22
  25. package/dist/lazy.d.ts +2 -1
  26. package/dist/lazy.js +13 -8
  27. package/dist/load.d.ts +1 -1
  28. package/dist/load.js +1 -1
  29. package/dist/mount.d.ts +12 -3
  30. package/dist/mount.js +63 -20
  31. package/dist/owner.d.ts +8 -6
  32. package/dist/owner.js +22 -40
  33. package/dist/pages.d.ts +1 -1
  34. package/dist/pages.js +2 -1
  35. package/dist/program.d.ts +7 -5
  36. package/dist/program.js +33 -20
  37. package/dist/query-internals.d.ts +1 -1
  38. package/dist/query.d.ts +1 -1
  39. package/dist/query.js +0 -2
  40. package/dist/resource.js +3 -1
  41. package/dist/runtime.d.ts +2 -1
  42. package/dist/runtime.js +3 -1
  43. package/dist/session.js +10 -3
  44. package/dist/settlement.d.ts +2 -1
  45. package/dist/settlement.js +13 -8
  46. package/dist/snapshot.d.ts +1 -1
  47. package/dist/snapshot.js +10 -12
  48. package/dist/task.d.ts +1 -1
  49. package/dist/task.js +3 -1
  50. package/dist/tasks.d.ts +1 -1
  51. package/dist/tasks.js +2 -1
  52. package/dist/testing.d.ts +5 -4
  53. package/dist/testing.js +4 -2
  54. package/package.json +1 -1
  55. package/dist/intrinsic.d.ts +0 -2
  56. package/dist/intrinsic.js +0 -120
  57. package/dist/slotIdentity.d.ts +0 -1
  58. package/dist/slotIdentity.js +0 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # effectweb
2
2
 
3
- Immutable Effect models and JSX compiled to direct DOM updates, without signals, proxies, or virtual DOM.
3
+ Immutable Effect models and JSX with direct DOM rendering.
4
4
 
5
5
  Use with `@effectweb/compiler/vite` and Effect `4.0.0-rc.112`. The package includes programs, named tasks, async presentation, query caching, DOM lifetimes, and `effectweb/testing` helpers.
6
6
 
@@ -8,6 +8,37 @@ See [setup and example](https://github.com/DerpyCrabs/EffectWeb#vite-setup).
8
8
 
9
9
  Runtime and compiler versions advance together. Client-side only. Persistence and multi-tab coordination belong to the application.
10
10
 
11
+ ## Views and list identity
12
+
13
+ `view(render)` evaluates `render(model, send)` as ordinary JavaScript on each immutable model publication. Calls, local variables, destructuring, loops, conditionals, and JSX helpers retain their JavaScript behavior. The renderer reconciles the returned content; the compiler does not infer dependencies or cache helper results.
14
+
15
+ Use `list(rows, render)` to preserve domain identity:
16
+
17
+ ```tsx
18
+ import { entities, list, view } from 'effectweb';
19
+
20
+ type Todo = { id: string; title: string };
21
+ const Todos = view<{ todos: Todo[] }>((model) => (
22
+ <ul>
23
+ {list(entities(model.todos), (todo) => (
24
+ <li>{todo.title}</li>
25
+ ))}
26
+ </ul>
27
+ ));
28
+ ```
29
+
30
+ `collection(identity).from(items)` supplies a custom domain identity; `sequence(items)` supplies positional identity. `list(rawArray, render)` uses each item itself as its identity, so repeated values or references require an explicit collection identity. Duplicate identities throw with both positions. The callback receives the exact current item, including a replacement object with the same ID. `Rows.map` and ordinary array `.map` remain normal JavaScript mapping operations; their rendered arrays use positional identity.
31
+
32
+ `slot(render)` is a typed render callback. Call it explicitly, including `footer()` for a `Slot<void>`; a function is not implicit JSX content. See the [0.4.0 migration guide](https://github.com/DerpyCrabs/EffectWeb/blob/main/docs/migration-0.4.0.md).
33
+
34
+ ## DOM and Effect lifetimes
35
+
36
+ `domMount(acquire)` uses the acquisition function as its identity. Declare it outside the view body when its lifetime should survive model updates. `domBinding(data, acquire)` keeps a stable acquisition while supplying fresh data through `input()`. Return `{ update, dispose }` when the integration must apply changed data, or a scoped Effect that remains active for the lifetime. A new acquisition function replaces the old lifetime.
37
+
38
+ Fresh event callbacks see current model data without canceling already running listener work. An Effect event's `replace` policy cancels the previous request when a new event submits work; removing the handler or unmounting disposes its owned work.
39
+
40
+ `dispose()` interrupts owned work synchronously. `close()`, `awaitIdle()`, and `awaitStopped()` return Effects; merely creating or JavaScript-awaiting one does not execute it. Use `yield* owner.close()` inside an Effect or `await Effect.runPromise(owner.close())` at a Promise integration boundary. `close()` waits for finalizers; `awaitIdle()` waits without closing the owner. A mount returned by `mountView` also has `close()` for its DOM-owned work. The supplied program keeps its separate ownership.
41
+
11
42
  ## Query identity and account ownership
12
43
 
13
44
  A query definition has its own identity. Within one cache, all request arguments form its key automatically:
@@ -76,7 +107,7 @@ Queue progress continues after successes, typed failures, or defects. Component
76
107
 
77
108
  `<Portal mount={model.dialogHost}>...</Portal>` renders into a supplied HTML or SVG element. Omitting `mount` uses the document body. Portal content retains its owner, events, and cleanup; changing between HTML and SVG targets rebuilds content in the correct namespace.
78
109
 
79
- ## Inspect source dependencies
110
+ ## Inspect source bindings
80
111
 
81
112
  Mount a development panel before mounting the application so it sees initial evaluations:
82
113
 
@@ -88,11 +119,11 @@ const removeInspector = mountBindingInspector(document.querySelector<HTMLElement
88
119
  // removeInspector();
89
120
  ```
90
121
 
91
- The live, filterable table shows original file/line/column, source expressions, inferred snapshot dependencies, the latest changed dependency names, and derive/binding evaluation counts. It uses development compiler metadata; production builds emit none. Counts include initial evaluations, aggregate instances of the same source expression, and measure evaluations rather than actual DOM writes. Change reasons use reference/value equality, without retaining previous or next values.
122
+ The live, filterable table shows original file/line/column, source expressions, and binding evaluation counts. Development metadata records the expressions actually evaluated for text and attributes; it does not infer which model fields a helper reads. Production builds emit none. Counts include initial evaluations, aggregate instances of the same source expression, and measure binding evaluations rather than DOM writes. Change reasons compare evaluated values without retaining previous or next values.
92
123
 
93
124
  For custom tooling, `inspectBindings({ limit: 200 })` returns `entries()`, `subscribe(listener)`, `clear()`, and `dispose()`. Entries are immutable metadata, newest first, with at most 1000 source records. Least recently updated sources are evicted and start fresh if seen again. `dispose()` unsubscribes and clears retained metadata. `mountBindingInspector(element, inspector)` can share an inspector; removing that panel leaves the supplied inspector running. Low-level `observeBindings` remains available.
94
125
 
95
- The inspector covers instrumented derivations and text/attribute bindings; it is not a snapshot recorder, time-travel debugger, or complete profile of branch/list reconciliation. Source labels describe the compiler's inferred dependencies, not a proof that an opaque helper has no hidden state.
126
+ The inspector covers instrumented text/attribute bindings; it is not a snapshot recorder, time-travel debugger, or complete profile of view evaluation and list reconciliation. Use application profiling for expensive render helpers.
96
127
 
97
128
  ## Immutable inputs and outputs
98
129
 
@@ -1,4 +1,4 @@
1
- import { Cause } from 'effect';
1
+ import * as Cause from 'effect/Cause';
2
2
  import * as AsyncResult from 'effect/unstable/reactivity/AsyncResult';
3
3
  import { type Slot, type View } from './index.js';
4
4
  import type { JSX } from './jsx.js';
@@ -1,6 +1,8 @@
1
1
  import * as _ew_dom from "./dom.js";
2
2
  import { commandSlot } from "./program.js";
3
- import { Cause, Effect, Option } from "effect";
3
+ import * as Cause from "effect/Cause";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Option from "effect/Option";
4
6
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
5
7
  import { component } from "./component.js";
6
8
  import { view } from "./index.js";
@@ -47,40 +49,21 @@ const implementation = component({
47
49
  ...model,
48
50
  visible: true
49
51
  } }),
50
- view: /* @__PURE__ */ _ew_dom.compiled((_ew_scope_1, _ew_parent_2, _ew_before_3) => {
51
- const _ew_derived_4 = _ew_scope_1.derive(() => [_ew_scope_1.value?.props], () => {
52
- const _ew_capture_6 = _ew_scope_1.value;
53
- return _ew_dom.intrinsic(_ew_dom.intrinsic(_ew_capture_6, ["props", "content"], "@slot"), ["props", "failure"], "@slot").props;
54
- });
55
- const _ew_derived_7 = _ew_scope_1.derive(() => [_ew_derived_4()?.result], () => {
56
- const _ew_capture_9 = _ew_derived_4();
57
- return AsyncResult.value(_ew_capture_9.result);
58
- });
59
- const _ew_derived_10 = _ew_scope_1.derive(() => [_ew_derived_4()?.result, _ew_derived_4()?.result?.waiting], () => {
60
- const _ew_capture_12 = _ew_derived_4();
61
- return AsyncResult.isFailure(_ew_capture_12.result) && !_ew_capture_12.result.waiting ? _ew_capture_12.result : undefined;
62
- });
63
- _ew_dom.branch(_ew_scope_1, _ew_parent_2, _ew_before_3, () => Option.isSome(_ew_derived_7()), (_ew_scope_1, _ew_parent_13, _ew_before_14) => {
64
- _ew_dom.text(_ew_scope_1, _ew_parent_13, _ew_before_14, () => [_ew_derived_4(), _ew_derived_7()?.value], () => _ew_derived_4().content(_ew_derived_7().value));
65
- _ew_dom.branch(_ew_scope_1, _ew_parent_13, _ew_before_14, () => _ew_derived_4().result.waiting, (_ew_scope_1, _ew_parent_15, _ew_before_16) => {
66
- _ew_dom.text(_ew_scope_1, _ew_parent_15, _ew_before_16, () => [_ew_derived_4()?.refreshing], () => _ew_derived_4().refreshing);
67
- }, (_ew_scope_1, _ew_parent_15, _ew_before_16) => { });
68
- _ew_dom.branch(_ew_scope_1, _ew_parent_13, _ew_before_14, () => _ew_derived_10() && _ew_derived_4().failure, (_ew_scope_1, _ew_parent_17, _ew_before_18) => {
69
- _ew_dom.text(_ew_scope_1, _ew_parent_17, _ew_before_18, () => [_ew_derived_4(), _ew_derived_10()?.cause], () => _ew_derived_4().failure(_ew_derived_10().cause));
70
- }, (_ew_scope_1, _ew_parent_17, _ew_before_18) => { });
71
- }, (_ew_scope_1, _ew_parent_13, _ew_before_14) => {
72
- _ew_dom.branch(_ew_scope_1, _ew_parent_13, _ew_before_14, () => _ew_derived_10() && _ew_derived_4().failure, (_ew_scope_1, _ew_parent_19, _ew_before_20) => {
73
- _ew_dom.text(_ew_scope_1, _ew_parent_19, _ew_before_20, () => [_ew_derived_4(), _ew_derived_10()?.cause], () => _ew_derived_4().failure(_ew_derived_10().cause));
74
- }, (_ew_scope_1, _ew_parent_19, _ew_before_20) => {
75
- _ew_dom.branch(_ew_scope_1, _ew_parent_19, _ew_before_20, () => _ew_scope_1.value.pending, (_ew_scope_1, _ew_parent_21, _ew_before_22) => {
76
- _ew_dom.branch(_ew_scope_1, _ew_parent_21, _ew_before_22, () => _ew_scope_1.value.visible, (_ew_scope_1, _ew_parent_23, _ew_before_24) => {
77
- _ew_dom.text(_ew_scope_1, _ew_parent_23, _ew_before_24, () => [_ew_derived_4()?.pending], () => _ew_derived_4().pending);
78
- }, (_ew_scope_1, _ew_parent_23, _ew_before_24) => { });
79
- }, (_ew_scope_1, _ew_parent_21, _ew_before_22) => {
80
- _ew_dom.text(_ew_scope_1, _ew_parent_21, _ew_before_22, () => [_ew_derived_4()?.empty], () => _ew_derived_4().empty);
81
- });
82
- });
83
- });
52
+ view: view((model, _send) => {
53
+ const props = model.props;
54
+ const data = AsyncResult.value(props.result);
55
+ const failure = AsyncResult.isFailure(props.result) && !props.result.waiting ? props.result : undefined;
56
+ if (Option.isSome(data))
57
+ return [
58
+ props.content(data.value),
59
+ props.result.waiting ? props.refreshing : null,
60
+ failure && props.failure ? props.failure(failure.cause) : null
61
+ ];
62
+ if (failure && props.failure)
63
+ return props.failure(failure.cause);
64
+ if (model.pending)
65
+ return model.visible ? props.pending : null;
66
+ return props.empty;
84
67
  })
85
68
  });
86
69
  /** Presentation only: resource owners choose identity, loading, caching and cancellation. */
package/dist/cache.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Snapshot } from './snapshot.js';
2
- import { Effect } from 'effect';
2
+ import * as Effect from 'effect/Effect';
3
3
  import { type Query, type QueryGroup } from './query.js';
4
4
  import type { UiRuntime } from './runtime.js';
5
5
  export { loadEffect, type UiLoad } from './load.js';
@@ -36,6 +36,6 @@ export interface QueryCache<R = never> {
36
36
  onReset(listener: () => void): () => void;
37
37
  resetResources(): void;
38
38
  /** Interrupt every request and wait for its finalizers, including previously canceled requests. */
39
- close(): Promise<void>;
39
+ close(): Effect.Effect<void>;
40
40
  dispose(): void;
41
41
  }
package/dist/cache.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Settlement } from './settlement.js';
2
2
  import { protectSnapshot } from './snapshot.js';
3
- import { Deferred, Effect, Option } from 'effect';
3
+ import * as Deferred from 'effect/Deferred';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as Option from 'effect/Option';
4
6
  import * as AsyncResult from 'effect/unstable/reactivity/AsyncResult';
5
7
  import * as Atom from 'effect/unstable/reactivity/Atom';
6
8
  import * as AtomRegistry from 'effect/unstable/reactivity/AtomRegistry';
@@ -292,8 +294,10 @@ function createQueryCache(runtime, options = {}) {
292
294
  },
293
295
  close() {
294
296
  if (!closing) {
295
- cache.dispose();
296
- closing = settlement.wait();
297
+ closing = Effect.suspend(() => {
298
+ cache.dispose();
299
+ return settlement.wait();
300
+ });
297
301
  }
298
302
  return closing;
299
303
  },
@@ -9,7 +9,7 @@ export interface Rows<A> {
9
9
  slice(start?: number, end?: number): Rows<A>;
10
10
  }
11
11
  /** Validate without retaining values. Positions are zero-based array indices. */
12
- export declare function validateIdentities<A>(items: readonly A[], identity: (item: A, index: number) => Identity): Identity[];
12
+ export declare function validateIdentities<A, K>(items: readonly A[], identity: (item: A, index: number) => K): K[];
13
13
  export interface Collection<A> {
14
14
  from(this: void, items: readonly (A | Snapshot<A>)[]): Rows<Snapshot<A>>;
15
15
  share<B extends A | Snapshot<A>>(this: void, previous: readonly B[], next: readonly B[]): readonly Snapshot<B>[];
@@ -2,10 +2,8 @@ import { shareData } from './sharing.js';
2
2
  /** Validate without retaining values. Positions are zero-based array indices. */
3
3
  export function validateIdentities(items, identity) {
4
4
  const seen = new Map();
5
- return items.map((item, index) => {
5
+ return Array.from(items, (item, index) => {
6
6
  const key = identity(item, index);
7
- if (typeof key !== 'string' && typeof key !== 'number')
8
- throw new Error(`Invalid collection identity at index ${index}. Use a string or number domain identity, or sequence(items) for positional identity.`);
9
7
  const previous = seen.get(key);
10
8
  if (previous !== undefined)
11
9
  throw new Error(`Duplicate collection identity at indices ${previous} and ${index}. Identity must be unique within the collection; use a composite domain identity when IDs are only locally unique.`);
package/dist/component.js CHANGED
@@ -1,3 +1,5 @@
1
+ import * as Cause from 'effect/Cause';
2
+ import * as Effect from 'effect/Effect';
1
3
  import { compiled, Scope, viewRegion } from './dom.js';
2
4
  import { mapCommand, program } from './program.js';
3
5
  import { patchModel } from './state.js';
@@ -82,9 +84,10 @@ function closeProgram(scope, source) {
82
84
  const finish = scope.settlement.begin();
83
85
  try {
84
86
  if (source.close) {
85
- source.close().then(finish, (error) => {
87
+ Effect.runFork(source.close()).addObserver((exit) => {
86
88
  finish();
87
- reportSafely(scope.report, error);
89
+ if (exit._tag === 'Failure')
90
+ reportSafely(scope.report, Cause.squash(exit.cause));
88
91
  });
89
92
  }
90
93
  else {
package/dist/dom.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as Effect from 'effect/Effect';
1
2
  import type { Snapshot } from './snapshot.js';
2
3
  import { type ReportError } from './errors.js';
3
4
  import { type BindingSource } from './diagnostics.js';
@@ -7,6 +8,7 @@ import type { DomMount } from './mount.js';
7
8
  import { Settlement } from './settlement.js';
8
9
  import type { Program, Send } from './program.js';
9
10
  type Dependencies = () => readonly unknown[];
11
+ type BindingLocation = BindingSource | (() => BindingSource | undefined);
10
12
  type Cleanup = () => void;
11
13
  type Build<M, E> = (scope: Scope<M, E>, parent: Node, before: Node | null) => void;
12
14
  /** Compiler implementation. No implicit tracking, proxies, or per-binding subscriptions. */
@@ -18,10 +20,8 @@ export declare class Scope<M, E> {
18
20
  readonly jobs: Cleanup[];
19
21
  readonly cleanups: Cleanup[];
20
22
  disposed: boolean;
21
- private revision;
22
23
  constructor(value: M, send: Send<E>, report?: ReportError, settlement?: Settlement);
23
- derive<A>(dependencies: Dependencies, compute: () => A, source?: BindingSource): () => A;
24
- watch(dependencies: Dependencies, apply: () => void, source?: BindingSource): void;
24
+ watch(dependencies: Dependencies, apply: () => void, source?: BindingLocation): void;
25
25
  set(value: M): void;
26
26
  dispose(): void;
27
27
  }
@@ -29,8 +29,8 @@ export interface View<M, E> extends JSX.ComponentType {
29
29
  (this: never, props: [E] extends [never] ? M | Snapshot<M> : never): JSX.Element;
30
30
  readonly build: Build<M, E>;
31
31
  }
32
- /** Compiler marker for mounting a view with an explicit model and message dispatcher. */
33
- export declare const ViewBinding: (<M, E>(this: never, _props: {
32
+ /** Mount a view with an explicit model and message dispatcher. */
33
+ export declare const ViewBinding: (<M, E>(this: never, props: {
34
34
  view: View<M, E>;
35
35
  model: NoInfer<M> | Snapshot<NoInfer<M>>;
36
36
  send: Send<NoInfer<E>>;
@@ -44,18 +44,22 @@ declare const contentBrand: unique symbol;
44
44
  export interface CompiledContent {
45
45
  readonly [contentBrand]: true;
46
46
  }
47
- /** A compiled template with a typed placement value, captured in its declaring view. */
47
+ /** An ordinary render callback with a typed input. Invoke it to produce content. */
48
48
  export interface Slot<A = void> {
49
49
  (value: A | Snapshot<A>): JSX.Element;
50
- readonly [contentBrand]: [A] extends [void] ? true : false;
51
50
  }
52
- /** Compiler marker: declare inside view(), or directly in a compiled component prop. */
53
- export declare function slot<A = void>(_render: (value: Snapshot<A>) => JSX.Element): Slot<A>;
54
- /** Compiler output. Captures follow their declaring scope; placements own independent scopes. */
55
- export declare function compiledSlot<M, E, A>(owner: Scope<M, E>, build: Build<A, E>): Slot<A>;
56
- /** This declaration is a compiler marker, never a component setup callback. */
57
- export declare function view<M, E = never>(_render: (model: Snapshot<M>, send: Send<E>) => JSX.Element): View<M, E>;
51
+ export declare function slot<A = void>(render: (value: Snapshot<A>) => JSX.Element): Slot<A>;
52
+ /** Evaluate ordinary synchronous JavaScript for each immutable model publication. */
53
+ export declare function view<M, E = never>(render: (model: Snapshot<M>, send: Send<E>) => JSX.Element): View<M, E>;
58
54
  export declare function compiled<M, E>(build: Build<M, E>): View<M, E>;
55
+ type MarkupProps = Readonly<Record<string, unknown>> & {
56
+ readonly children?: JSX.Element;
57
+ };
58
+ /** JSX factories preserve host identity by element type; source locations are metadata only. */
59
+ export declare function markup(tag: string, sources?: Readonly<Record<string, BindingSource>>): (props: MarkupProps) => JSX.Element;
60
+ export declare function renderComponent(component: (props: never) => JSX.Element, props: Readonly<Record<string, unknown>>): JSX.Element;
61
+ /** Preserve collection identity (or array reference identity) while rendering rows. */
62
+ export declare function list<A>(rows: Rows<A> | readonly A[], render: (item: A, index: number) => JSX.Element): JSX.Element;
59
63
  export interface PortalProps {
60
64
  readonly children?: JSX.Element;
61
65
  readonly mount?: Element | undefined;
@@ -66,23 +70,22 @@ export declare function mountView<M, E>(parent: Node, definition: View<M, E>, so
66
70
  onError?: ReportError;
67
71
  }): (() => void) & {
68
72
  dispose: () => void;
69
- close: () => Promise<void>;
73
+ close: () => Effect.Effect<void, never, never>;
70
74
  };
71
75
  export declare function element(parent: Node, before: Node | null, tag: string): Element;
72
76
  export declare function attribute(element: Element, name: string, value: unknown): void;
73
77
  /** Compiler output for attributes whose unchanged value needs no DOM refresh. */
74
- export declare function bindAttribute<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => unknown, source?: BindingSource): void;
78
+ export declare function bindAttribute<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => unknown, source?: BindingLocation): void;
75
79
  /** Handled edits can change a control even when dispatch publishes no new model. */
76
- export declare function bindControl<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => unknown, source?: BindingSource): void;
77
- export declare function text<M, E>(scope: Scope<M, E>, parent: Node, before: Node | null, dependencies: Dependencies, read: () => unknown, source?: BindingSource): void;
78
- export declare function event<M, E>(scope: Scope<M, E>, element: Element, name: string, handler: (event: Event) => unknown): void;
79
- /** One handler identity owns its requests, regardless of the JSX attribute spelling. */
80
- export declare function bindEvent<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => unknown): void;
80
+ export declare function bindControl<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => unknown, source?: BindingLocation): void;
81
+ export declare function text<M, E>(scope: Scope<M, E>, parent: Node, before: Node | null, dependencies: Dependencies, read: () => JSX.Element, source?: BindingLocation): void;
82
+ export declare function event<M, E>(scope: Scope<M, E>, element: Element, name: string, handler: (event: Event) => JSX.EventResult): void;
83
+ /** The event binding owns its requests; snapshot renders may replace its callback. */
84
+ export declare function bindEvent<M, E>(scope: Scope<M, E>, element: Element, name: string, dependencies: Dependencies, read: () => ((event: Event) => JSX.EventResult) | undefined): void;
81
85
  /** Reconcile merged JSX attributes. Each host, event and control owns its cleanup. */
82
- export declare function bindAttributes<M, E>(scope: Scope<M, E>, element: Element, dependencies: Dependencies, read: () => Readonly<Record<string, unknown>>): void;
86
+ export declare function bindAttributes<M, E>(scope: Scope<M, E>, element: Element, dependencies: Dependencies, read: () => Readonly<Record<string, unknown>>, sources?: () => Readonly<Record<string, BindingSource>> | undefined): void;
83
87
  export declare function branch<M, E>(scope: Scope<M, E>, parent: Node, before: Node | null, choose: () => boolean, yes: Build<M, E>, no: Build<M, E>): void;
84
88
  export declare function each<M, E, A>(scope: Scope<M, E>, parent: Node, before: Node | null, read: () => Rows<A> | readonly A[], outer: Dependencies, indexUsed: boolean, build: Build<readonly [A, number], E>): void;
85
- export declare function invoke<M, E>(scope: Scope<M, E>, parent: Node, before: Node | null, dependencies: Dependencies, read: () => readonly unknown[], build: Build<readonly unknown[], E>): void;
86
89
  export declare function child<M, E, C, F>(scope: Scope<M, E>, parent: Node, before: Node | null, definition: View<C, F>, dependencies: Dependencies, model: () => C, send: Send<F>): void;
87
90
  /** A late-bound compiled definition owns one replaceable DOM region. */
88
91
  export declare function viewRegion<M, E>(scope: Scope<M, E>, parent: Node, before: Node | null, options?: {
@@ -94,4 +97,4 @@ export declare function portal<M, E>(scope: Scope<M, E>, build: Build<M, E>, mou
94
97
  /** Build static markup once per document/namespace, then clone native nodes at each use. */
95
98
  export declare function template(build: string | ((parent: Node, before: Node | null) => void), depth?: number): (parent: Node, before: Node | null) => Node;
96
99
  export declare function literal(parent: Node, before: Node | null, value: string): void;
97
- export { intrinsic } from './intrinsic.js';
100
+ export {};