@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,145 @@
1
+ # DevTools
2
+
3
+ Solace exposes a narrow public DevTools integration surface through `solace/devtools`. This document records the public
4
+ lifecycle, private runtime boundary, and safe constraints for future instrumentation.
5
+
6
+ ## Goals
7
+
8
+ - Help developers inspect component, reactivity, scheduler, renderer, and store behavior.
9
+ - Keep instrumentation opt-in.
10
+ - Avoid stabilizing internal runtime objects as public API.
11
+ - Avoid adding measurable overhead to production builds or benchmarks.
12
+
13
+ ## Non-Goals
14
+
15
+ - No browser extension, custom panel, network transport, storage persistence, or automatic telemetry in the current phase.
16
+
17
+ ## Public API
18
+
19
+ DevTools integrations should import from the `solace/devtools` subpath:
20
+
21
+ ```ts
22
+ import { createDevtoolsRecorder, onDevtoolsEvent } from "solace/devtools";
23
+ import type { DevtoolsEvent } from "solace/devtools";
24
+ ```
25
+
26
+ The public subpath exports listener and recorder APIs only. It does not export emit helpers, listener-state helpers,
27
+ global cleanup helpers, serializers, DOM nodes, VNode trees, component instances, props, reactive targets, store state,
28
+ action arguments, or action results.
29
+
30
+ ## Public API Lifecycle
31
+
32
+ `solace/devtools` is the only supported public DevTools entry point. New runtime exports require package boundary tests,
33
+ packed consumer smoke coverage, documentation, and a project log entry before they are treated as supported API.
34
+
35
+ Event payload additions must remain small serializable summaries and must update payload stability coverage. They should
36
+ not include raw props, state, DOM nodes, VNodes, reactive targets, action arguments, action results, stack traces, or
37
+ user content.
38
+
39
+ Renames or removals require an intentional breaking-change plan. Internal helpers remain private even when public APIs
40
+ reuse them internally, and incidental runtime cleanup must not change the public subpath shape.
41
+
42
+ ## Candidate Capabilities
43
+
44
+ | Area | Useful Signals | Notes |
45
+ | ---------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
46
+ | Components | mount, update, unmount, props, emits, lifecycle hooks | Component lifecycle and emit summaries are emitted by the internal event bus |
47
+ | Reactivity | effect creation, dependency tracking, triggers, stops | Trigger summaries are emitted without raw targets, keys, or values |
48
+ | Scheduler | queued jobs, flush duration, skipped stale jobs | `scheduler:flush` summary is emitted by the internal event bus |
49
+ | Renderer | element mount, prop patch, child diff, unmount | Element summaries are emitted without DOM nodes or VNode trees |
50
+ | Store | action calls, narrow state paths, getter recomputation | Action summaries are emitted without args, results, or state |
51
+
52
+ ## Hook Boundary
53
+
54
+ Solace has an internal event bus in `src/devtools/events.ts`. Runtime modules emit through that internal bus, while
55
+ public integrations subscribe through `solace/devtools`. The package root intentionally does not export DevTools APIs.
56
+
57
+ ```ts
58
+ type DevtoolsEvent =
59
+ | { type: "component:mount"; id: number; name: string }
60
+ | { type: "component:update"; id: number; name: string }
61
+ | { type: "component:unmount"; id: number; name: string }
62
+ | { type: "component:emit"; id: number; name: string; event: string; handlerCount: number }
63
+ | { type: "scheduler:flush"; queuedJobs: number; dedupedJobs: number; durationMs: number }
64
+ | {
65
+ type: "reactivity:trigger";
66
+ targetType: string;
67
+ keyType: string;
68
+ effectCount: number;
69
+ scheduledEffects: number;
70
+ runEffects: number;
71
+ }
72
+ | {
73
+ type: "renderer:element";
74
+ operation: "mount" | "update" | "unmount";
75
+ tag: string;
76
+ }
77
+ | {
78
+ type: "store:action";
79
+ name: string;
80
+ status: "success" | "error";
81
+ durationMs: number;
82
+ };
83
+ ```
84
+
85
+ `component:emit` summaries include the component id, component name, emitted event name, and callable handler count only.
86
+ They do not include emitted arguments, raw props, handler functions, component instances, VNodes, DOM nodes, or user
87
+ content.
88
+
89
+ `scheduler:flush` summaries include executed job count, deduped queue attempt count, and flush duration only. They do
90
+ not include scheduler job functions, function names, stack traces, component instances, reactive effects, VNodes, DOM
91
+ nodes, or user data.
92
+
93
+ Future runtime modules should emit small serializable events only when a listener is registered. If no listener is registered, the runtime should do no meaningful extra work.
94
+
95
+ `serializeDevtoolsEvent(event)` is available only from the internal event bus module. It returns an explicit plain-object
96
+ copy for the current event union and is used by integration tests to lock the payload boundary. It is not exported from
97
+ the package root.
98
+
99
+ `createDevtoolsRecorder()` is public through `solace/devtools`. It installs a listener, stores serialized events in
100
+ memory, exposes `snapshot()` for a copy of collected events, exposes `clear()` to reset the current capture window, and
101
+ exposes `stop()` to remove the listener. Pass `{ limit }` to keep only the latest N events in memory. It does not
102
+ persist data, send data over the network, write to storage, or install third-party scripts.
103
+
104
+ Production package builds do not publish JavaScript sourcemaps. This keeps internal DevTools wiring visible in source
105
+ control but out of package artifacts, so consumers do not accidentally couple to private helper names or module layout.
106
+
107
+ ## Privacy And Safety
108
+
109
+ - Do not emit full props, state, DOM nodes, or reactive targets by default.
110
+ - Redact or summarize values before exposing them to tooling.
111
+ - Keep hooks disabled unless a dev-only listener is installed.
112
+ - Do not send data over the network.
113
+
114
+ ## Performance Constraints
115
+
116
+ - Production builds should not pay for DevTools instrumentation.
117
+ - Benchmark commands should run with DevTools disabled.
118
+ - Hook payload construction should be lazy or guarded by a listener check.
119
+ - Component tree and dependency graph snapshots should be explicit actions, not automatic on every update.
120
+
121
+ ## Phased Roadmap
122
+
123
+ 1. **Event model design**: completed for initial component and scheduler summary events.
124
+ 2. **Development-only event bus**: internal event bus exists in `src/devtools/events.ts`.
125
+ 3. **Scheduler flush and dedupe summary**: `scheduler:flush` reports executed jobs, deduped queue attempts, and duration.
126
+ 4. **Component lifecycle summaries**: component mount/update/unmount summaries emit id and name only.
127
+ 5. **Component emit summaries**: `component:emit` is emitted with event name and callable handler count only.
128
+ 6. **Store action summaries**: `store:action` is emitted after action success or error without raw values.
129
+ 7. **Reactivity trigger summaries**: `reactivity:trigger` is emitted without raw targets, keys, or values.
130
+ 8. **Renderer element summaries**: `renderer:element` is emitted for element mount/update/unmount without DOM nodes or VNode trees.
131
+ 9. **Payload stability smoke**: integrated runtime events serialize to JSON-safe payloads with allowed fields only.
132
+ 10. **Internal recorder boundary**: `createDevtoolsRecorder()` captures serialized event snapshots for examples and experiments.
133
+ 11. **Example-oriented recorder smoke**: a todo-style interaction validates recorder capture after clearing initial mount noise.
134
+ 12. **Bounded recorder captures**: `createDevtoolsRecorder({ limit })` keeps recorder memory bounded for examples and experiments.
135
+ 13. **Large-list recorder smoke**: a 10,000-row keyed update validates public recorder snapshots remain serialized summaries without DOM, VNode, raw state, or row data.
136
+ 14. **Public package boundary guard**: package exports tests verify DevTools internals are not available from the package root.
137
+ 15. **Public DevTools subpath**: `solace/devtools` exposes listener and recorder APIs without internal emit helpers.
138
+ 16. **Production artifact boundary**: package builds do not publish JavaScript sourcemaps that expose internal wiring.
139
+ 17. **Inspector UI or browser extension**: build only after event payloads prove stable in examples.
140
+
141
+ ## Recommendation
142
+
143
+ Do not implement a DevTools UI yet. The public `solace/devtools` subpath is a low-level integration surface for examples,
144
+ tests, and future inspector tooling. Build a browser extension or custom panel only after more payloads prove stable in
145
+ real examples.
@@ -0,0 +1,78 @@
1
+ # Examples
2
+
3
+ Solace includes three Vite examples that exercise the runtime from small state updates to larger keyed lists.
4
+
5
+ ## Basic Counter
6
+
7
+ Run:
8
+
9
+ ```bash
10
+ pnpm dev
11
+ ```
12
+
13
+ Location: `examples/basic-counter`
14
+
15
+ Coverage:
16
+
17
+ - JSX automatic runtime.
18
+ - `reactive` state.
19
+ - DOM event patching through `onClick`.
20
+ - Reactive re-render after a button click.
21
+
22
+ The Playwright test `tests/e2e/basic-counter.spec.ts` verifies that the counter starts at `count: 0` and increments to `count: 1`.
23
+
24
+ ## Todo App
25
+
26
+ Run:
27
+
28
+ ```bash
29
+ pnpm dev:todo
30
+ ```
31
+
32
+ Location: `examples/todo-app`
33
+
34
+ Coverage:
35
+
36
+ - Form submit handling.
37
+ - Controlled input updates with `onInput`.
38
+ - Keyed list rendering.
39
+ - Checkbox state toggles.
40
+ - Item deletion.
41
+
42
+ The Playwright test `tests/e2e/todo-app.spec.ts` verifies add, toggle, and delete flows.
43
+
44
+ ## Large List
45
+
46
+ Run:
47
+
48
+ ```bash
49
+ pnpm dev:large
50
+ ```
51
+
52
+ Location: `examples/large-list`
53
+
54
+ Coverage:
55
+
56
+ - 10,000 keyed rows.
57
+ - Class and text patching.
58
+ - A targeted state update from row 1 to row 5000.
59
+
60
+ The Playwright test `tests/e2e/large-list.spec.ts` verifies the list renders 10,000 rows and updates the selected row marker.
61
+
62
+ ## E2E Validation
63
+
64
+ Run all browser examples through Playwright:
65
+
66
+ ```bash
67
+ pnpm test:e2e
68
+ ```
69
+
70
+ The Playwright config starts each example on a fixed localhost port:
71
+
72
+ | Example | Port |
73
+ | ------------- | ------ |
74
+ | Basic counter | `5174` |
75
+ | Todo app | `5175` |
76
+ | Large list | `5176` |
77
+
78
+ `pnpm release:check` also runs these e2e tests after quality checks, coverage, package smoke, jsdom benchmark smoke, and the Chromium production browser benchmark.
@@ -0,0 +1,96 @@
1
+ # Package Usage
2
+
3
+ ## Install
4
+
5
+ The package is publishable, so registry install is valid after release:
6
+
7
+ ```bash
8
+ pnpm add @italone/solace
9
+ ```
10
+
11
+ Before that release decision, run `pnpm release:readiness` to check local release metadata and validate package consumption with the packed-consumer smoke test described below.
12
+
13
+ ## Import Runtime APIs
14
+
15
+ ```ts
16
+ import { createApp, h, reactive } from "@italone/solace";
17
+
18
+ const state = reactive({ count: 0 });
19
+
20
+ const App = () =>
21
+ h(
22
+ "button",
23
+ {
24
+ onClick: () => {
25
+ state.count += 1;
26
+ },
27
+ },
28
+ `count: ${state.count}`,
29
+ );
30
+
31
+ createApp(App).mount(document.querySelector("#app") as Element);
32
+ ```
33
+
34
+ ## Use JSX
35
+
36
+ Configure TypeScript with the Solace JSX runtime:
37
+
38
+ ```json
39
+ {
40
+ "compilerOptions": {
41
+ "jsx": "react-jsx",
42
+ "jsxImportSource": "@italone/solace"
43
+ }
44
+ }
45
+ ```
46
+
47
+ Then write components with JSX:
48
+
49
+ ```tsx
50
+ import { createApp, reactive } from "@italone/solace";
51
+
52
+ const state = reactive({ count: 0 });
53
+
54
+ const App = () => (
55
+ <button
56
+ onClick={() => {
57
+ state.count += 1;
58
+ }}
59
+ >
60
+ count: {state.count}
61
+ </button>
62
+ );
63
+
64
+ createApp(App).mount(document.querySelector("#app") as Element);
65
+ ```
66
+
67
+ ## Public Entry Points
68
+
69
+ - `@italone/solace`: core runtime APIs.
70
+ - `@italone/solace/jsx-runtime`: TypeScript automatic JSX runtime.
71
+ - `@italone/solace/jsx-dev-runtime`: development JSX runtime used by Vite.
72
+ - `@italone/solace/devtools`: low-level DevTools listener and recorder APIs.
73
+
74
+ Do not import from `src/**`, `dist/**`, or internal runtime modules directly. Those paths are implementation details and are not part of the package compatibility contract.
75
+
76
+ ## Verify A Packed Consumer
77
+
78
+ Before release, run the package consumer smoke test:
79
+
80
+ ```bash
81
+ pnpm package:smoke
82
+ ```
83
+
84
+ The smoke test builds Solace, packs the current package, installs the tarball into a temporary consumer project, typechecks a JSX entry file, and verifies ESM and CJS imports for all public entry points.
85
+
86
+ For the full local release gate, run:
87
+
88
+ ```bash
89
+ pnpm release:check
90
+ ```
91
+
92
+ That command runs quality checks including format check, coverage thresholds, package consumer smoke, jsdom benchmark smoke, Chromium production browser benchmark, and browser e2e tests.
93
+
94
+ See `docs/release.md` for versioning and publish steps.
95
+
96
+ See `docs/examples.md` for runnable Vite examples and their e2e coverage.
@@ -0,0 +1,230 @@
1
+ # Performance
2
+
3
+ This document defines how Solace performance should be measured. It does not claim unverified results.
4
+
5
+ ## Current Validation
6
+
7
+ The repository currently validates behavior with:
8
+
9
+ - Unit tests for reactivity, scheduler, renderer, components, events, JSX runtime, store, and package exports.
10
+ - Playwright e2e tests for basic counter, todo app, and a 10,000-row large list example.
11
+ - Rollup production build checks.
12
+ - Tinybench smoke benchmarks for initial render, list diff, keyed insert/remove/move/reorder,
13
+ Fragment rendering, batched component updates, and mount/unmount loops.
14
+ - Chromium browser production benchmark for large-list initial render, reactive update, and unmount
15
+ through `pnpm benchmark:browser`.
16
+
17
+ The large-list e2e test confirms that 10,000 rows can render and one selected row can update in a browser smoke test. It is not a benchmark result.
18
+
19
+ ## Latest Local Benchmark Run
20
+
21
+ Date: 2026-07-20
22
+
23
+ Environment:
24
+
25
+ | Item | Value |
26
+ | ------------ | ----------------------- |
27
+ | OS | Darwin 25.4.0 arm64 arm |
28
+ | Node | v22.22.2 |
29
+ | Runtime | darwin arm64 |
30
+ | Vitest | 4.1.10 |
31
+ | CPU / memory | Recorded by metadata |
32
+
33
+ Command:
34
+
35
+ ```bash
36
+ SOLACE_BENCHMARK_HISTORY_PATH=.benchmark-history/jsdom.jsonl SOLACE_BENCHMARK_SAMPLE_SIZE=3 pnpm benchmark
37
+ ```
38
+
39
+ The command logs a `benchmark metadata` JSON line before running the jsdom benchmark suite. The metadata includes package name/version, Node version, OS platform/release/architecture, CPU model, logical CPU count, total memory, benchmark runner, benchmark environment, sample size, and an ISO timestamp.
40
+
41
+ `sampleSize` defaults to `1` so `pnpm benchmark` remains a smoke benchmark run. Set
42
+ `SOLACE_BENCHMARK_SAMPLE_SIZE=3 pnpm benchmark` to run three independent Vitest benchmark samples.
43
+ The command reports the configured sample size in metadata, but it does not yet aggregate medians.
44
+
45
+ Set `SOLACE_BENCHMARK_HISTORY_PATH=.benchmark-history/jsdom.jsonl pnpm benchmark` to append one
46
+ JSONL record after a successful jsdom benchmark run. History recording is opt-in and records
47
+ metadata plus run status. Records created by the current benchmark runner also include
48
+ task-level Tinybench metrics under `summary.tasks`.
49
+
50
+ The local ignored jsdom history currently contains records with task-level Tinybench metrics when created by the current
51
+ benchmark runner. `pnpm benchmark:history -- --json` summarizes those jsdom task metrics with count, median, p95, and
52
+ variance while still accepting older metadata-only records.
53
+
54
+ Result summary:
55
+
56
+ | Scenario | File | Status | Notes |
57
+ | ------------------------------------------------ | --------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
58
+ | 1,000 component initial render | `tests/performance/render.bench.ts` | Passed | Uses jsdom and Tinybench, intended for trend tracking |
59
+ | 10,000 row create/update/delete/reorder | `tests/performance/list-diff.bench.ts` | Passed | Covers list creation, text-to-list mount, initial element child mount, unkeyed append/remove, local text update, delete, and keyed reorder |
60
+ | 10,000 row keyed local insert/remove/move | `tests/performance/list-diff.bench.ts` | Passed | Covers focused middle insert, middle remove, tail-to-head move, mixed insert/move, adjacent insert/move, and adjacent remove/move |
61
+ | 5,000 Fragment child initial render/patch/insert | `tests/performance/fragment.bench.ts` | Passed | Covers Fragment child mount, keyed text patch, and keyed middle insert |
62
+ | 1,000 component batched reactive update | `tests/performance/component-update.bench.ts` | Passed | Covers scheduler batching across many component consumers |
63
+ | Component mount/unmount loop | `tests/performance/memory.bench.ts` | Passed | Observes repeated cleanup path and records heap delta during the run |
64
+
65
+ Conclusion:
66
+
67
+ - The benchmark command is reproducible and currently passes.
68
+ - These runs are smoke benchmarks in jsdom, not browser production benchmarks.
69
+ - No claim is made that Solace meets or exceeds a specific framework performance target yet.
70
+ - The latest renderer follow-ups batch all-element Fragment initial mounts through a `DocumentFragment`,
71
+ batch all-element array children during element initial mount and text-to-array child replacement, skip
72
+ stable child component updates when parent rerenders do not change child props or children, skip
73
+ unchanged keyed element sibling patches during local list updates, and avoid prop patching plus `Object.keys`
74
+ props scans for keyed child-only updates. Unkeyed appended all-element suffixes
75
+ batch through a `DocumentFragment` after index patching, and safe removed leaf suffixes detach through
76
+ a temporary `DocumentFragment`, including adjacent old keyed runs removed during mixed placement. Keyed
77
+ mixed insert/move patches now mount new children directly at their final anchor instead of appending and
78
+ moving them. Contiguous all-element keyed insert segments also batch through a `DocumentFragment` before
79
+ one parent insert, including adjacent new runs discovered during mixed keyed placement. The component update path
80
+ also avoids repeated enqueue attempts while a component update is already pending. The component initial mount path
81
+ also batches child inserts through a `DocumentFragment`. The initial element mount path now uses a conservative props
82
+ fast path for ordinary attributes, avoiding `Object.entries()` scans and redundant attribute removals on fresh
83
+ elements. It also uses a direct HTML `className` fast path for `class` props while keeping the existing attribute
84
+ fallback for non-HTML nodes. Next optimization work should focus on additional browser trend samples.
85
+
86
+ ## Browser Production Benchmark
87
+
88
+ Command:
89
+
90
+ ```bash
91
+ pnpm benchmark:browser
92
+ ```
93
+
94
+ This command builds `examples/performance-benchmark`, serves the production output through Vite
95
+ preview, and runs `tests/e2e/browser-benchmark.spec.ts` in Chromium.
96
+
97
+ Measured scenarios:
98
+
99
+ | Scenario | Scale | Assertion |
100
+ | --------------------------- | ----------- | -------------------------------------- |
101
+ | Initial large-list render | 10,000 rows | selected row 1 is rendered |
102
+ | Reactive selected-row patch | 10,000 rows | selected row 5000 reflects final state |
103
+ | Large-list unmount | 10,000 rows | row nodes are removed |
104
+
105
+ The command logs a `browser benchmark summary` JSON line. It intentionally does not enforce absolute
106
+ timing thresholds because browser, CPU, power mode, and background process variance can dominate
107
+ individual runs.
108
+
109
+ The summary also includes reproducibility metadata:
110
+
111
+ | Field | Source |
112
+ | ---------------------------------------------------------------------------- | ------------------------------- |
113
+ | `metadata.packageName` / `metadata.packageVersion` | `package.json` |
114
+ | `metadata.node` | `process.version` |
115
+ | `metadata.platform`, `metadata.release`, `metadata.arch` | Node `os` module |
116
+ | `metadata.cpuModel`, `metadata.logicalCpuCount`, `metadata.totalMemoryBytes` | Node `os` module |
117
+ | `metadata.browserName`, `metadata.browserVersion`, `metadata.projectName` | Playwright |
118
+ | `metadata.sampleSize` | Current benchmark harness |
119
+ | `metadata.runAt` | ISO timestamp for the local run |
120
+
121
+ `metadata.sampleSize` defaults to `1` so `pnpm benchmark:browser` remains a smoke benchmark run. Set
122
+ `SOLACE_BROWSER_BENCHMARK_SAMPLE_SIZE=3 pnpm benchmark:browser` to run three independent browser benchmark samples in
123
+ one Playwright run. The command logs one `browser benchmark summary` line per sample.
124
+
125
+ Set `SOLACE_BROWSER_BENCHMARK_HISTORY_PATH=.benchmark-history/browser.jsonl pnpm benchmark:browser`
126
+ to append one JSONL record after each successful Chromium production benchmark sample. Browser history
127
+ records persist the existing summary object; they do not add timing thresholds or statistical aggregation.
128
+
129
+ Run `pnpm benchmark:history` to summarize local JSONL history from `.benchmark-history/jsdom.jsonl`
130
+ and `.benchmark-history/browser.jsonl`. Use `pnpm benchmark:history -- --json <path>` for
131
+ machine-readable output. The summary reports record counts plus median, p95, and variance for
132
+ numeric browser timing metrics and jsdom task metrics; it does not enforce thresholds.
133
+
134
+ Use `pnpm benchmark:history -- --min-browser-count 5` to require each browser benchmark scenario
135
+ to have at least five local history records. This is an opt-in trend quality gate for local or CI
136
+ checks; it is not a timing threshold and does not compare measured performance against a target.
137
+ Use `pnpm benchmark:history -- --latest-browser-count 5` to summarize only the latest five browser
138
+ records per scenario while leaving jsdom record counts in the summary. This is useful when older
139
+ slow samples dominate full-history p95 and the next runtime hotspot needs a fresher trend window.
140
+ Run `pnpm benchmark:history -- --help` to list the supported summary options.
141
+
142
+ ### Latest Local Browser History Summary
143
+
144
+ Date: 2026-07-20
145
+
146
+ Local history command:
147
+
148
+ ```bash
149
+ pnpm benchmark:history -- --min-browser-count 20 --json
150
+ ```
151
+
152
+ The local ignored history currently contains forty-five Chromium `large-list` production benchmark records. The
153
+ `--min-browser-count 20` trend gate passes locally. p95 still reflects the slowest observed samples and should be
154
+ treated as trend context only, not a release threshold.
155
+
156
+ | Metric | Count | Median | p95 | Variance |
157
+ | ----------------- | ----- | ------ | ---- | -------- |
158
+ | `initialRenderMs` | 45 | 7.7 | 15.7 | 18.98 |
159
+ | `updateMs` | 45 | 3.6 | 6.1 | 4.23 |
160
+ | `unmountMs` | 45 | 1.2 | 1.4 | 0.14 |
161
+
162
+ Latest-window command:
163
+
164
+ ```bash
165
+ pnpm benchmark:history -- --latest-browser-count 5 --min-browser-count 5 --json
166
+ ```
167
+
168
+ The latest five Chromium `large-list` records still show a slow-sample ceiling on initial render, but the median
169
+ remains lower than the preceding full-history window.
170
+
171
+ | Metric | Count | Median | p95 | Variance |
172
+ | ----------------- | ----- | ------ | ---- | -------- |
173
+ | `initialRenderMs` | 5 | 7.7 | 16.2 | 13.46 |
174
+ | `updateMs` | 5 | 4.0 | 6.1 | 0.93 |
175
+ | `unmountMs` | 5 | 1.2 | 1.3 | 0.01 |
176
+
177
+ ## Benchmark Principles
178
+
179
+ Benchmarks should:
180
+
181
+ - Run in production mode where possible.
182
+ - Separate initial render, update, and unmount costs.
183
+ - Record browser, OS, Node, package version, and commit.
184
+ - Avoid comparing development builds against production builds.
185
+ - Report medians and variance, not a single best run.
186
+ - Keep benchmark fixtures in source control.
187
+
188
+ ## Suggested Benchmarks
189
+
190
+ ### Reactivity
191
+
192
+ - Create many reactive objects.
193
+ - Track many effects.
194
+ - Trigger narrow and broad dependency sets.
195
+ - Measure computed cache hits and invalidations.
196
+
197
+ ### Renderer
198
+
199
+ - Mount 1,000 and 10,000 simple elements.
200
+ - Patch text props across a large list.
201
+ - Insert, remove, and move keyed children.
202
+ - Unmount nested component trees.
203
+
204
+ ### Components
205
+
206
+ - Mount many small components.
207
+ - Batch repeated state writes in one tick.
208
+ - Repeatedly mount and unmount component subtrees.
209
+ - Verify no retained effects after unmount.
210
+
211
+ ### Store
212
+
213
+ - Read state directly in components.
214
+ - Read computed getters in components.
215
+ - Dispatch actions that update narrow state paths.
216
+
217
+ ## Reporting Template
218
+
219
+ ```text
220
+ Scenario:
221
+ Build mode:
222
+ Browser / Node:
223
+ Machine:
224
+ Sample size:
225
+ Median:
226
+ p95:
227
+ Notes:
228
+ ```
229
+
230
+ Performance claims should only be added after this data exists.
@@ -0,0 +1,67 @@
1
+ # Release
2
+
3
+ Solace uses Changesets for version notes and package version updates.
4
+
5
+ ## Local Release Gate
6
+
7
+ Run the full local gate before preparing a release:
8
+
9
+ ```bash
10
+ pnpm release:check
11
+ ```
12
+
13
+ This runs format check, typecheck, JSX dev typecheck, lint, default tests, package exports tests, coverage thresholds, package consumer smoke, jsdom benchmark smoke, Chromium production browser benchmark, and browser e2e tests.
14
+
15
+ The GitHub Actions CI workflow keeps these checks split into named steps and also runs both benchmark commands: `pnpm benchmark` and `pnpm benchmark:browser`.
16
+
17
+ CI also runs `pnpm release:readiness` before the longer checks so package metadata and release script drift fail early.
18
+
19
+ ## Release Readiness
20
+
21
+ Run the local metadata readiness check before changing publishability:
22
+
23
+ ```bash
24
+ pnpm release:readiness
25
+ ```
26
+
27
+ This command checks local package metadata, package entry points, release scripts, and Changesets public access configuration. It does not contact npm and does not publish.
28
+
29
+ The package is now publishable. To verify the stricter publishable mode before publishing, run:
30
+
31
+ ```bash
32
+ pnpm release:readiness -- --publishable
33
+ ```
34
+
35
+ ## Prepare A Version
36
+
37
+ Create a changeset for user-visible changes:
38
+
39
+ ```bash
40
+ pnpm changeset
41
+ ```
42
+
43
+ Apply pending changesets to `package.json` and changelog files:
44
+
45
+ ```bash
46
+ pnpm release:version
47
+ ```
48
+
49
+ ## Publish
50
+
51
+ Before publishing, explicitly confirm:
52
+
53
+ - the npm package name `@italone/solace` is available or controlled by the maintainer,
54
+ - npm authentication and organization access are configured,
55
+ - public access is intended,
56
+ - `pnpm release:readiness -- --publishable` passes,
57
+ - `pnpm release:check` passes,
58
+ - `pnpm package:smoke` passes after the final version update,
59
+ - Changesets versioning has been run for user-visible changes.
60
+
61
+ After that decision, run:
62
+
63
+ ```bash
64
+ pnpm release:publish
65
+ ```
66
+
67
+ `release:publish` runs the full local release gate before `changeset publish`.