@italone/solace 0.0.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.
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('./jsx-runtime.cjs');
4
+ var h = require('./h-CSqpvOof.cjs');
5
+
6
+ function jsxDEV(type, props, key) {
7
+ return jsxRuntime.jsx(type, props, key);
8
+ }
9
+
10
+ exports.jsx = jsxRuntime.jsx;
11
+ exports.jsxs = jsxRuntime.jsxs;
12
+ exports.Fragment = h.Fragment;
13
+ exports.jsxDEV = jsxDEV;
@@ -0,0 +1,7 @@
1
+ import { jsx, JSX } from './jsx-runtime.js';
2
+ export { jsxs } from './jsx-runtime.js';
3
+ export { F as Fragment } from './vnode-zJOgBYFm.js';
4
+
5
+ declare function jsxDEV(type: Parameters<typeof jsx>[0], props: Parameters<typeof jsx>[1], key?: string): JSX.Element;
6
+
7
+ export { JSX, jsx, jsxDEV };
@@ -0,0 +1,9 @@
1
+ import { jsx } from './jsx-runtime.js';
2
+ export { jsxs } from './jsx-runtime.js';
3
+ export { F as Fragment } from './h-B1loyNPC.js';
4
+
5
+ function jsxDEV(type, props, key) {
6
+ return jsx(type, props, key);
7
+ }
8
+
9
+ export { jsx, jsxDEV };
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var h = require('./h-CSqpvOof.cjs');
4
+
5
+ function jsx(type, props = null, key) {
6
+ return createJsxVNode(type, props, key);
7
+ }
8
+ function jsxs(type, props = null, key) {
9
+ return createJsxVNode(type, props, key);
10
+ }
11
+ function createJsxVNode(type, props, key) {
12
+ const { children, ...restProps } = props ?? {};
13
+ const vnodeProps = {
14
+ ...restProps,
15
+ ...(key !== undefined ? { key } : {}),
16
+ };
17
+ return h.h(type, vnodeProps, normalizeChildren(children));
18
+ }
19
+ function normalizeChildren(children) {
20
+ if (Array.isArray(children)) {
21
+ const normalized = children
22
+ .filter((child) => child !== null && child !== undefined && typeof child !== "boolean")
23
+ .map((child) => (typeof child === "number" ? String(child) : child));
24
+ if (normalized.every((child) => typeof child === "string")) {
25
+ return normalized.join("");
26
+ }
27
+ return normalized.map((child) => typeof child === "string" ? h.h("span", null, child) : child);
28
+ }
29
+ if (children === null || children === undefined || typeof children === "boolean") {
30
+ return null;
31
+ }
32
+ if (typeof children === "number") {
33
+ return String(children);
34
+ }
35
+ return typeof children === "string" ? children : [children];
36
+ }
37
+
38
+ exports.Fragment = h.Fragment;
39
+ exports.jsx = jsx;
40
+ exports.jsxs = jsxs;
@@ -0,0 +1,24 @@
1
+ import { V as VNode, a as VNodeProps, k as VNodeChild, d as VNodeType } from './vnode-zJOgBYFm.js';
2
+ export { F as Fragment } from './vnode-zJOgBYFm.js';
3
+
4
+ type JSXChild = VNodeChild | number | boolean | null | undefined;
5
+ type JSXChildren = JSXChild | JSXChild[];
6
+ type JSXProps = VNodeProps & {
7
+ children?: JSXChildren;
8
+ };
9
+
10
+ declare function jsx(type: VNodeType, props?: JSXProps | null, key?: string): VNode;
11
+ declare function jsxs(type: VNodeType, props?: JSXProps | null, key?: string): VNode;
12
+ declare namespace JSX {
13
+ type Element = VNode;
14
+ interface IntrinsicElements {
15
+ [name: string]: VNodeProps & {
16
+ children?: JSXChildren;
17
+ };
18
+ }
19
+ interface ElementChildrenAttribute {
20
+ children: unknown;
21
+ }
22
+ }
23
+
24
+ export { JSX, jsx, jsxs };
@@ -0,0 +1,37 @@
1
+ import { h } from './h-B1loyNPC.js';
2
+ export { F as Fragment } from './h-B1loyNPC.js';
3
+
4
+ function jsx(type, props = null, key) {
5
+ return createJsxVNode(type, props, key);
6
+ }
7
+ function jsxs(type, props = null, key) {
8
+ return createJsxVNode(type, props, key);
9
+ }
10
+ function createJsxVNode(type, props, key) {
11
+ const { children, ...restProps } = props ?? {};
12
+ const vnodeProps = {
13
+ ...restProps,
14
+ ...(key !== undefined ? { key } : {}),
15
+ };
16
+ return h(type, vnodeProps, normalizeChildren(children));
17
+ }
18
+ function normalizeChildren(children) {
19
+ if (Array.isArray(children)) {
20
+ const normalized = children
21
+ .filter((child) => child !== null && child !== undefined && typeof child !== "boolean")
22
+ .map((child) => (typeof child === "number" ? String(child) : child));
23
+ if (normalized.every((child) => typeof child === "string")) {
24
+ return normalized.join("");
25
+ }
26
+ return normalized.map((child) => typeof child === "string" ? h("span", null, child) : child);
27
+ }
28
+ if (children === null || children === undefined || typeof children === "boolean") {
29
+ return null;
30
+ }
31
+ if (typeof children === "number") {
32
+ return String(children);
33
+ }
34
+ return typeof children === "string" ? children : [children];
35
+ }
36
+
37
+ export { jsx, jsxs };
@@ -0,0 +1,43 @@
1
+ declare enum ShapeFlags {
2
+ ELEMENT = 1,
3
+ TEXT_CHILDREN = 2,
4
+ ARRAY_CHILDREN = 4,
5
+ COMPONENT = 8,
6
+ FRAGMENT = 16
7
+ }
8
+
9
+ type EmitFn = (event: string, ...args: unknown[]) => void;
10
+ type SlotProps = Record<string, unknown>;
11
+ type Slot = (props?: SlotProps) => VNodeChildren;
12
+ interface Slots {
13
+ default?: Slot;
14
+ [name: string]: Slot | undefined;
15
+ }
16
+ interface ComponentSetupContext {
17
+ emit: EmitFn;
18
+ slots: Slots;
19
+ }
20
+
21
+ type ComponentProps = Record<string, unknown>;
22
+ type ComponentRender = () => VNode;
23
+ type ComponentType<Props extends object = ComponentProps> = (props: Props, context: ComponentSetupContext) => ComponentRender | VNode;
24
+ declare const Fragment: unique symbol;
25
+ type FragmentType = typeof Fragment;
26
+ type VNodeType = string | ComponentType<never> | FragmentType;
27
+ type VNodeProps = Record<string, unknown>;
28
+ type VNodeChild = string | VNode;
29
+ type VNodeChildren = string | VNode | VNode[] | null;
30
+ type VNodeSlots = Record<string, Slot>;
31
+ type ComponentVNodeChildren = VNodeChildren | VNodeSlots;
32
+ interface VNode {
33
+ type: VNodeType;
34
+ props: VNodeProps | null;
35
+ key: string | number | null;
36
+ children: ComponentVNodeChildren;
37
+ shapeFlag: ShapeFlags;
38
+ el: Element | Text | null;
39
+ component: unknown;
40
+ }
41
+
42
+ export { Fragment as F };
43
+ export type { ComponentType as C, EmitFn as E, Slot as S, VNode as V, VNodeProps as a, VNodeChildren as b, ComponentVNodeChildren as c, VNodeType as d, ComponentRender as e, ComponentSetupContext as f, ComponentProps as g, FragmentType as h, SlotProps as i, Slots as j, VNodeChild as k, VNodeSlots as l };
package/docs/api.md ADDED
@@ -0,0 +1,302 @@
1
+ # API
2
+
3
+ ## Public Root Export
4
+
5
+ The `solace` package root exposes the stable runtime surface:
6
+
7
+ - App: `createApp`
8
+ - Reactivity: `reactive`, `effect`, `computed`, `ref`, `watch`, `watchEffect`
9
+ - Rendering: `h`, `render`, `Fragment`
10
+ - Components: `defineComponent`, `defineAsyncComponent`, default slots
11
+ - Component context: `provide`, `inject`
12
+ - Lifecycle: `onMounted`, `onUpdated`, `onUnmounted`
13
+ - Scheduler: `nextTick`
14
+ - Store: `createStore`
15
+
16
+ Public TypeScript types include `App`, `AsyncComponentLoader`, `AsyncComponentOptions`, `AsyncComponentSource`, `ComponentSetupContext`, `EmitFn`, `Slot`, `Slots`, `Store`, `StoreContext`, `StoreGetterContext`, `StoreGetters`, `StoreOptions`, `VNode`, `VNodeProps`, and related component/VNode helper types.
17
+
18
+ Internal helpers such as scheduler queues, shape flags, component instance setup, and VNode factory internals are not exported from the package root.
19
+
20
+ ## App
21
+
22
+ ### `createApp(rootComponent)`
23
+
24
+ Creates an app wrapper around a root component or VNode.
25
+
26
+ ```ts
27
+ import { createApp, h } from "solace";
28
+
29
+ const App = () => h("p", null, "hello");
30
+
31
+ createApp(App).mount(document.querySelector("#app") as Element);
32
+ ```
33
+
34
+ Returns:
35
+
36
+ - `mount(container: Element): void`
37
+ - `provide(key, value): App`
38
+ - `use(plugin, ...options): App`
39
+
40
+ Provide an app-level value before mounting. Components can read it with `inject()`, and component-level
41
+ providers override app-level values.
42
+
43
+ ```ts
44
+ import { createApp } from "solace";
45
+
46
+ createApp(App)
47
+ .provide("theme", "dark")
48
+ .mount(document.querySelector("#app") as Element);
49
+ ```
50
+
51
+ Install a plugin before mounting an app. A plugin can be a function or an object with `install`.
52
+ Each plugin is installed once per app instance, and `use()` returns the app for chaining. Plugins can
53
+ also call `app.provide()` to expose values to the mounted component tree.
54
+
55
+ ```ts
56
+ import { createApp } from "solace";
57
+ import type { App, Plugin } from "solace";
58
+
59
+ const plugin: Plugin = (app: App, option) => {
60
+ app.provide("theme", "dark");
61
+ console.log(app, option);
62
+ };
63
+
64
+ createApp(App)
65
+ .use(plugin, "enabled")
66
+ .mount(document.querySelector("#app") as Element);
67
+ ```
68
+
69
+ ## Reactivity
70
+
71
+ ### `reactive(target)`
72
+
73
+ Wraps an object in a reactive proxy.
74
+
75
+ ```ts
76
+ const state = reactive({ count: 0 });
77
+ state.count += 1;
78
+ ```
79
+
80
+ ### `effect(fn)`
81
+
82
+ Runs `fn` immediately and reruns it when tracked dependencies change.
83
+
84
+ ```ts
85
+ effect(() => {
86
+ console.log(state.count);
87
+ });
88
+ ```
89
+
90
+ ### `computed(getter)`
91
+
92
+ Returns a cached ref-like object with readonly `.value`.
93
+
94
+ ```ts
95
+ const double = computed(() => state.count * 2);
96
+ console.log(double.value);
97
+ ```
98
+
99
+ ### `ref(value)`
100
+
101
+ Creates a reactive value holder.
102
+
103
+ ```ts
104
+ const count = ref(0);
105
+ count.value += 1;
106
+ ```
107
+
108
+ ### `watch(source, callback)`
109
+
110
+ Runs `callback` when the source result changes.
111
+
112
+ ```ts
113
+ watch(
114
+ () => state.count,
115
+ (value, oldValue) => {
116
+ console.log(value, oldValue);
117
+ },
118
+ );
119
+ ```
120
+
121
+ ### `watchEffect(effect)`
122
+
123
+ Runs `effect` immediately and reruns it when tracked dependencies change.
124
+
125
+ ```ts
126
+ const stop = watchEffect(() => {
127
+ console.log(state.count);
128
+ });
129
+
130
+ stop();
131
+ ```
132
+
133
+ ## Rendering
134
+
135
+ ### `h(type, props?, children?)`
136
+
137
+ Creates a VNode for an element, component, or Fragment.
138
+
139
+ ```ts
140
+ h("button", { onClick: save }, "Save");
141
+ ```
142
+
143
+ ### `render(vnode, container)`
144
+
145
+ Mounts or patches a VNode into a DOM container.
146
+
147
+ ```ts
148
+ render(h(App), document.querySelector("#app") as Element);
149
+ ```
150
+
151
+ ### `Fragment`
152
+
153
+ Groups multiple children without an extra DOM wrapper.
154
+
155
+ ## Components
156
+
157
+ Function components receive props and a setup context with `emit` and `slots`.
158
+
159
+ ```ts
160
+ import { h } from "solace";
161
+ import type { ComponentSetupContext } from "solace";
162
+
163
+ const Button = (props: { label: string }, { emit }: ComponentSetupContext) =>
164
+ h("button", { onClick: () => emit("change") }, props.label);
165
+
166
+ const Panel =
167
+ (_props: object, { slots }: ComponentSetupContext) =>
168
+ () =>
169
+ h("section", null, [
170
+ h("header", null, slots.header?.() ?? null),
171
+ h("main", null, slots.default?.({ text: "Body" }) ?? null),
172
+ ]);
173
+
174
+ h(Panel, null, {
175
+ header: () => h("h1", null, "Title"),
176
+ default: (slotProps) => h("p", null, String(slotProps?.text)),
177
+ });
178
+ ```
179
+
180
+ ### `defineComponent(component)`
181
+
182
+ Declares a Solace component while preserving the existing function component contract.
183
+
184
+ ```ts
185
+ import { defineComponent, h } from "solace";
186
+
187
+ const Button = defineComponent((props: { label: string }) => h("button", null, props.label));
188
+ ```
189
+
190
+ ### `defineAsyncComponent(loader | options)`
191
+
192
+ Declares a component that loads another component asynchronously. While the loader is pending,
193
+ the wrapper renders an empty Fragment or `loadingComponent`. If the loader rejects, it renders
194
+ `errorComponent` when provided. Resolved, loading, and error components receive the latest props
195
+ and default slot children. Use `delay` to postpone the loading component and `timeout` to enter
196
+ the error state when loading takes too long. Use `retry` to make additional attempts after loader
197
+ rejection or timeout, and `retryDelay` to wait before each retry.
198
+
199
+ ```ts
200
+ import { defineAsyncComponent, h } from "solace";
201
+
202
+ const LazyMessage = defineAsyncComponent<{ text: string }>(() =>
203
+ Promise.resolve((props: { text: string }) => h("p", null, props.text)),
204
+ );
205
+
206
+ const LazyPanel = defineAsyncComponent<{ title: string }>({
207
+ loader: () => Promise.resolve((props: { title: string }) => h("section", null, props.title)),
208
+ loadingComponent: () => h("span", null, "loading"),
209
+ errorComponent: () => h("strong", null, "failed"),
210
+ delay: 200,
211
+ timeout: 3000,
212
+ retry: 2,
213
+ retryDelay: 100,
214
+ });
215
+ ```
216
+
217
+ ### `provide(key, value)` / `inject(key, defaultValue?)`
218
+
219
+ Passes values from an ancestor component setup to descendant component setup without prop drilling.
220
+
221
+ ```ts
222
+ import { h, inject, provide } from "solace";
223
+
224
+ const ThemeKey = Symbol("theme");
225
+
226
+ const Child = () => {
227
+ const theme = inject(ThemeKey, "light");
228
+
229
+ return () => h("span", null, theme);
230
+ };
231
+
232
+ const Parent = () => {
233
+ provide(ThemeKey, "dark");
234
+
235
+ return () => h(Child);
236
+ };
237
+ ```
238
+
239
+ ### Lifecycle
240
+
241
+ - `onMounted(hook)`
242
+ - `onUpdated(hook)`
243
+ - `onUnmounted(hook)`
244
+
245
+ Lifecycle hooks register during component setup.
246
+
247
+ ## Scheduler
248
+
249
+ ### `nextTick()`
250
+
251
+ Resolves after queued component updates flush.
252
+
253
+ ```ts
254
+ state.count += 1;
255
+ await nextTick();
256
+ ```
257
+
258
+ ## Store
259
+
260
+ ### `createStore({ state, getters, actions })`
261
+
262
+ Creates a small centralized store.
263
+
264
+ ```ts
265
+ import { createStore } from "solace";
266
+ import type { StoreContext, StoreGetterContext } from "solace";
267
+
268
+ const store = createStore({
269
+ state: () => ({ count: 0 }),
270
+ getters: {
271
+ double({ state }: StoreGetterContext<{ count: number }>) {
272
+ return state.count * 2;
273
+ },
274
+ },
275
+ actions: {
276
+ increment({ state }: StoreContext<{ count: number }, { double: number }>, amount: number) {
277
+ state.count += amount;
278
+ },
279
+ },
280
+ });
281
+
282
+ store.actions.increment(1);
283
+ console.log(store.getters.double);
284
+ ```
285
+
286
+ ## JSX
287
+
288
+ Use TypeScript automatic JSX runtime:
289
+
290
+ ```json
291
+ {
292
+ "compilerOptions": {
293
+ "jsx": "react-jsx",
294
+ "jsxImportSource": "solace"
295
+ }
296
+ }
297
+ ```
298
+
299
+ Public JSX entry points:
300
+
301
+ - `solace/jsx-runtime`
302
+ - `solace/jsx-dev-runtime`
@@ -0,0 +1,80 @@
1
+ # Architecture
2
+
3
+ ## Layers
4
+
5
+ Solace is organized as a small runtime pipeline:
6
+
7
+ ```text
8
+ reactivity -> scheduler -> component -> vnode -> renderer -> DOM
9
+ ```
10
+
11
+ - `reactivity`: tracks reads and triggers effects on writes.
12
+ - `scheduler`: batches component update jobs and exposes `nextTick`.
13
+ - `component`: creates component instances, owns props, render effects, emit, and lifecycle hooks.
14
+ - `vnode`: represents elements, components, Fragment, props, keys, and children.
15
+ - `renderer`: mounts, patches, diffs, and unmounts VNodes.
16
+ - `event`: patches DOM event listeners through invoker caching.
17
+ - `store`: composes `reactive` and `computed` into a centralized state container.
18
+
19
+ ## Update Flow
20
+
21
+ 1. A component render function reads reactive state.
22
+ 2. The active component `ReactiveEffect` tracks those reads.
23
+ 3. A state write calls `trigger`.
24
+ 4. The component effect scheduler queues the component update with `queueJob`.
25
+ 5. The queued job reruns the component render.
26
+ 6. The renderer patches the previous subtree against the next subtree.
27
+ 7. `nextTick` resolves after the scheduler queue is flushed.
28
+
29
+ ## Component Flow
30
+
31
+ Mounting a component creates a `ComponentInstance` with:
32
+
33
+ - `vnode`, `type`, `props`, `subTree`
34
+ - `render`, `effect`, `update`
35
+ - `emit`
36
+ - lifecycle arrays for mounted, updated, and unmounted hooks
37
+
38
+ The first render mounts the component subtree. Later renders patch the old subtree against the new subtree. Unmounting stops the component effect, recursively unmounts the subtree, and calls unmounted hooks.
39
+
40
+ ## DOM Patch Flow
41
+
42
+ The renderer compares VNode type and key:
43
+
44
+ - Different type or key: unmount old node, mount new node.
45
+ - Same element: patch props, then patch children.
46
+ - Same component: update props, then run the component update.
47
+ - Fragment: patch or unmount its children without adding a wrapper element.
48
+
49
+ Children diff favors correctness and DOM reuse. Keyed children support insert, delete, move, and patch, and the
50
+ keyed-middle path uses LIS optimization to avoid unnecessary DOM moves.
51
+
52
+ ## Event Flow
53
+
54
+ DOM event props use the `onXxx` convention. Each DOM element caches event invokers:
55
+
56
+ - First listener: add one native listener.
57
+ - Handler update: replace `invoker.value`.
58
+ - Prop removal or element unmount: remove the cached native listener.
59
+
60
+ This avoids repeated add/remove work when only the handler function changes.
61
+
62
+ ## Package Shape
63
+
64
+ The package exposes:
65
+
66
+ - `solace`: root runtime APIs.
67
+ - `solace/jsx-runtime`: automatic JSX runtime.
68
+ - `solace/jsx-dev-runtime`: development JSX runtime.
69
+
70
+ Rollup builds ESM, CJS, and `.d.ts` artifacts for each public entry point.
71
+
72
+ ## Examples
73
+
74
+ The Vite examples in `examples/` exercise the package through local aliases:
75
+
76
+ - `basic-counter`: JSX runtime, reactive state, and DOM events.
77
+ - `todo-app`: form input, keyed list updates, checkbox state, and deletion.
78
+ - `large-list`: 10,000 keyed rows and targeted text/class patching.
79
+
80
+ See `docs/examples.md` for run commands and e2e coverage.