@zoijs/core 1.2.0 → 1.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.
package/src/index.d.ts CHANGED
@@ -1,164 +1,185 @@
1
- // Type definitions for Zoijs.
2
- //
3
- // Zoijs is authored in plain JavaScript; these declarations add editor
4
- // autocomplete and optional type-checking WITHOUT converting the project to
5
- // TypeScript. Using them is entirely opt-in — JS users are unaffected.
6
-
7
- /** A reactive value created by {@link createState}. */
8
- export interface State<T> {
9
- /** Read the current value. Inside a binding/effect this subscribes to it. */
10
- get(): T;
11
- /** Write a new value. Dependents update only if the value actually changed. */
12
- set(next: T): void;
13
- /** Read the current value WITHOUT subscribing. */
14
- peek(): T;
15
- }
16
-
17
- /** A lazy, cached, value-gated derived value created by {@link computed}. */
18
- export interface Computed<T> {
19
- /** Read the current value. Recomputes only if a dependency changed. */
20
- get(): T;
21
- /** Read without subscribing. */
22
- peek(): T;
23
- }
24
-
25
- declare const templateBrand: unique symbol;
26
- /**
27
- * The result of an `html` template. Pass it to {@link mount}, return it from a
28
- * component, or place it in another template. Treat it as opaque.
29
- */
30
- export interface TemplateResult {
31
- readonly [templateBrand]: true;
32
- }
33
-
34
- declare const eachBrand: unique symbol;
35
- /** The result of {@link each}. Place it in a template's child position. */
36
- export interface EachResult {
37
- readonly [eachBrand]: true;
38
- }
39
-
40
- /** A component is a function that returns an `html` template. */
41
- export type Component = () => TemplateResult;
42
-
43
- /**
44
- * A callback ref. Place it on an element as `ref=${fn}`; it receives the real DOM
45
- * element once the surrounding render has been inserted (deferred one microtask,
46
- * so `focus()` / `scrollIntoView()` / `getBoundingClientRect()` work). It may
47
- * return a cleanup function, which runs on unmount or list-item removal. It runs
48
- * once and is not reactive.
49
- *
50
- * ```ts
51
- * html`<input ref=${(el: HTMLInputElement) => el.focus()} />`
52
- * html`<div ref=${(el) => { const c = chart(el); return () => c.destroy(); }}></div>`
53
- * ```
54
- */
55
- export type Ref<E extends Element = Element> = (element: E) => void | (() => void);
56
-
57
- /**
58
- * Tagged-template function — write your markup as HTML.
59
- *
60
- * ```js
61
- * html`<button onclick=${() => count.set(count.get() + 1)}>${() => count.get()}</button>`
62
- * ```
63
- *
64
- * To reach the rendered DOM element, add a callback `ref` (see {@link Ref}):
65
- * `html\`<input ref=${(el) => el.focus()} />\``.
66
- */
67
- export function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
68
-
69
- /**
70
- * Render a component (or a template) into a DOM element or CSS selector.
71
- * Returns an `unmount()` that detaches the DOM and disposes all reactivity.
72
- */
73
- export function mount(component: Component | TemplateResult, target: Element | string): () => void;
74
-
75
- /**
76
- * Create a reactive value.
77
- *
78
- * ```ts
79
- * const count = createState(0); // State<number>
80
- * const name = createState<string>(""); // explicit
81
- * ```
82
- */
83
- export function createState<T>(initial: T, equals?: (a: T, b: T) => boolean): State<T>;
84
-
85
- /**
86
- * Create a lazy, cached derived value.
87
- *
88
- * ```ts
89
- * const fullName = computed(() => `${first.get()} ${last.get()}`); // Computed<string>
90
- * ```
91
- */
92
- export function computed<T>(fn: () => T, equals?: (a: T, b: T) => boolean): Computed<T>;
93
-
94
- /** A disposable handle returned by {@link effect}. */
95
- export interface EffectHandle {
96
- /** Dispose the effect now. It also auto-disposes with its owner (component/list item). */
97
- dispose(): void;
98
- }
99
-
100
- /**
101
- * Run a side effect that re-runs whenever a reactive value it reads changes.
102
- * Runs once immediately, then again (batched on a microtask) on any change.
103
- * Dependencies are tracked automatically — there is no dependency array.
104
- *
105
- * The function may return a cleanup function (same convention as a `ref`); it
106
- * runs **before the next run** and **on dispose**. Use the return value for
107
- * per-run teardown — `onCleanup` is component-scoped (fires on unmount), not
108
- * per-run.
109
- *
110
- * Created inside a component or list item, it auto-disposes with that scope;
111
- * created at module top level, it lives until you call `dispose()`.
112
- *
113
- * ```ts
114
- * // persist on change
115
- * effect(() => localStorage.setItem("theme", theme.get()));
116
- *
117
- * // per-run cleanup
118
- * effect(() => {
119
- * const id = setInterval(() => poll(query.get()), 1000);
120
- * return () => clearInterval(id);
121
- * });
122
- * ```
123
- *
124
- * For reactive *content on screen*, use a binding (`${() => …}`), not an effect.
125
- */
126
- export function effect(fn: () => void | (() => void)): EffectHandle;
127
-
128
- /**
129
- * Keyed list rendering. `items` may be a reactive function or a plain array;
130
- * `keyFn` returns a stable unique key; `renderFn` returns the template for one item.
131
- *
132
- * ```ts
133
- * each(() => todos.get(), (t) => t.id, (t) => html`<li>${() => t.text}</li>`)
134
- * ```
135
- */
136
- export function each<T>(
137
- items: () => readonly T[],
138
- keyFn: (item: T) => unknown,
139
- renderFn: (item: T) => TemplateResult
140
- ): EachResult;
141
- export function each<T>(
142
- items: readonly T[],
143
- keyFn: (item: T) => unknown,
144
- renderFn: (item: T) => TemplateResult
145
- ): EachResult;
146
-
147
- /** Toggle development warnings (default: `dev` is `true`). */
148
- export function configure(options: { dev?: boolean }): void;
149
-
150
- /**
151
- * Register a teardown function for the current component or list item. It runs
152
- * when that component is unmounted or that list item is removed. Use it for
153
- * timers, subscriptions, or third-party widgets created during setup.
154
- *
155
- * ```ts
156
- * function Clock() {
157
- * const now = createState(Date.now());
158
- * const id = setInterval(() => now.set(Date.now()), 1000);
159
- * onCleanup(() => clearInterval(id));
160
- * return html`<time>${() => now.get()}</time>`;
161
- * }
162
- * ```
163
- */
164
- export function onCleanup(fn: () => void): void;
1
+ // Type definitions for Zoijs.
2
+ //
3
+ // Zoijs is authored in plain JavaScript; these declarations add editor
4
+ // autocomplete and optional type-checking WITHOUT converting the project to
5
+ // TypeScript. Using them is entirely opt-in — JS users are unaffected.
6
+
7
+ /** A reactive value created by {@link createState}. */
8
+ export interface State<T> {
9
+ /** Read the current value. Inside a binding/effect this subscribes to it. */
10
+ get(): T;
11
+ /** Write a new value. Dependents update only if the value actually changed. */
12
+ set(next: T): void;
13
+ /** Read the current value WITHOUT subscribing. */
14
+ peek(): T;
15
+ }
16
+
17
+ /** A lazy, cached, value-gated derived value created by {@link computed}. */
18
+ export interface Computed<T> {
19
+ /** Read the current value. Recomputes only if a dependency changed. */
20
+ get(): T;
21
+ /** Read without subscribing. */
22
+ peek(): T;
23
+ }
24
+
25
+ declare const templateBrand: unique symbol;
26
+ /**
27
+ * The result of an `html` template. Pass it to {@link mount}, return it from a
28
+ * component, or place it in another template. Treat it as opaque.
29
+ */
30
+ export interface TemplateResult {
31
+ readonly [templateBrand]: true;
32
+ }
33
+
34
+ declare const eachBrand: unique symbol;
35
+ /** The result of {@link each}. Place it in a template's child position. */
36
+ export interface EachResult {
37
+ readonly [eachBrand]: true;
38
+ }
39
+
40
+ /** A component is a function that returns an `html` template. */
41
+ export type Component = () => TemplateResult;
42
+
43
+ /**
44
+ * A callback ref. Place it on an element as `ref=${fn}`; it receives the real DOM
45
+ * element once the surrounding render has been inserted (deferred one microtask,
46
+ * so `focus()` / `scrollIntoView()` / `getBoundingClientRect()` work). It may
47
+ * return a cleanup function, which runs on unmount or list-item removal. It runs
48
+ * once and is not reactive.
49
+ *
50
+ * ```ts
51
+ * html`<input ref=${(el: HTMLInputElement) => el.focus()} />`
52
+ * html`<div ref=${(el) => { const c = chart(el); return () => c.destroy(); }}></div>`
53
+ * ```
54
+ */
55
+ export type Ref<E extends Element = Element> = (element: E) => void | (() => void);
56
+
57
+ /**
58
+ * Tagged-template function — write your markup as HTML.
59
+ *
60
+ * ```js
61
+ * html`<button onclick=${() => count.set(count.get() + 1)}>${() => count.get()}</button>`
62
+ * ```
63
+ *
64
+ * To reach the rendered DOM element, add a callback `ref` (see {@link Ref}):
65
+ * `html\`<input ref=${(el) => el.focus()} />\``.
66
+ */
67
+ export function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
68
+
69
+ /**
70
+ * Render a component (or a template) into a DOM element or CSS selector.
71
+ * Returns an `unmount()` that detaches the DOM and disposes all reactivity.
72
+ */
73
+ export function mount(component: Component | TemplateResult, target: Element | string): () => void;
74
+
75
+ /**
76
+ * Create a reactive value.
77
+ *
78
+ * ```ts
79
+ * const count = createState(0); // State<number>
80
+ * const name = createState<string>(""); // explicit
81
+ * ```
82
+ */
83
+ export function createState<T>(initial: T, equals?: (a: T, b: T) => boolean): State<T>;
84
+
85
+ /**
86
+ * Create a lazy, cached derived value.
87
+ *
88
+ * ```ts
89
+ * const fullName = computed(() => `${first.get()} ${last.get()}`); // Computed<string>
90
+ * ```
91
+ */
92
+ export function computed<T>(fn: () => T, equals?: (a: T, b: T) => boolean): Computed<T>;
93
+
94
+ /** A disposable handle returned by {@link effect}. */
95
+ export interface EffectHandle {
96
+ /** Dispose the effect now. It also auto-disposes with its owner (component/list item). */
97
+ dispose(): void;
98
+ }
99
+
100
+ /**
101
+ * Run a side effect that re-runs whenever a reactive value it reads changes.
102
+ * Runs once immediately, then again (batched on a microtask) on any change.
103
+ * Dependencies are tracked automatically — there is no dependency array.
104
+ *
105
+ * The function may return a cleanup function (same convention as a `ref`); it
106
+ * runs **before the next run** and **on dispose**. Use the return value for
107
+ * per-run teardown — `onCleanup` is component-scoped (fires on unmount), not
108
+ * per-run.
109
+ *
110
+ * Created inside a component or list item, it auto-disposes with that scope;
111
+ * created at module top level, it lives until you call `dispose()`.
112
+ *
113
+ * ```ts
114
+ * // persist on change
115
+ * effect(() => localStorage.setItem("theme", theme.get()));
116
+ *
117
+ * // per-run cleanup
118
+ * effect(() => {
119
+ * const id = setInterval(() => poll(query.get()), 1000);
120
+ * return () => clearInterval(id);
121
+ * });
122
+ * ```
123
+ *
124
+ * For reactive *content on screen*, use a binding (`${() => …}`), not an effect.
125
+ */
126
+ export function effect(fn: () => void | (() => void)): EffectHandle;
127
+
128
+ /**
129
+ * Keyed list rendering. `items` may be a reactive function or a plain array;
130
+ * `keyFn` returns a stable unique key; `renderFn` returns the template for one item.
131
+ *
132
+ * ```ts
133
+ * each(() => todos.get(), (t) => t.id, (t) => html`<li>${() => t.text}</li>`)
134
+ * ```
135
+ */
136
+ export function each<T>(
137
+ items: () => readonly T[],
138
+ keyFn: (item: T) => unknown,
139
+ renderFn: (item: T) => TemplateResult
140
+ ): EachResult;
141
+ export function each<T>(
142
+ items: readonly T[],
143
+ keyFn: (item: T) => unknown,
144
+ renderFn: (item: T) => TemplateResult
145
+ ): EachResult;
146
+
147
+ /**
148
+ * Render `child`; if it **throws synchronously while building its markup** (a
149
+ * setup/render error that would otherwise break the whole `mount`), tear down the
150
+ * partial work and render `fallback` instead. Place the call in a template slot.
151
+ *
152
+ * Catches synchronous setup/render throws only. Errors in reactive *updates* are
153
+ * already contained per binding by the core; *async* errors belong to
154
+ * `@zoijs/resource` / `@zoijs/action`'s `error()` state. It renders once (no reset
155
+ * — re-mount the subtree to retry), logs in dev, and is silent in production.
156
+ *
157
+ * ```ts
158
+ * html`<section>
159
+ * ${boundary(() => RiskyWidget(), (err) => html`<p>Couldn't load this.</p>`)}
160
+ * </section>`
161
+ * ```
162
+ */
163
+ export function boundary<C, F>(
164
+ child: (() => C) | C,
165
+ fallback: ((error: unknown) => F) | F
166
+ ): C | F;
167
+
168
+ /** Toggle development warnings (default: `dev` is `true`). */
169
+ export function configure(options: { dev?: boolean }): void;
170
+
171
+ /**
172
+ * Register a teardown function for the current component or list item. It runs
173
+ * when that component is unmounted or that list item is removed. Use it for
174
+ * timers, subscriptions, or third-party widgets created during setup.
175
+ *
176
+ * ```ts
177
+ * function Clock() {
178
+ * const now = createState(Date.now());
179
+ * const id = setInterval(() => now.set(Date.now()), 1000);
180
+ * onCleanup(() => clearInterval(id));
181
+ * return html`<time>${() => now.get()}</time>`;
182
+ * }
183
+ * ```
184
+ */
185
+ export function onCleanup(fn: () => void): void;
package/src/index.js CHANGED
@@ -8,6 +8,7 @@
8
8
  export { html } from "./core/html.js";
9
9
  export { mount } from "./core/mount.js";
10
10
  export { each } from "./core/each.js";
11
+ export { boundary } from "./core/boundary.js";
11
12
  export { createState } from "./reactivity/state.js";
12
13
  export { computed } from "./reactivity/computed.js";
13
14
  export { effect } from "./reactivity/effect.js";