feat(forms, component): expand into batteries-included tier (1.13.0) - #95
Merged
Conversation
1.12..0 Sync full bundle and enhance WebSocket support with new types
…ld arrays, schema, bindings, SSR Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/0a88b845-e43a-4d9a-b820-4b9573637e8b Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/0a88b845-e43a-4d9a-b820-4b9573637e8b Co-authored-by: JosunLP <[email protected]>
…events perf, validators jsdoc, css guard) Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/0a88b845-e43a-4d9a-b820-4b9573637e8b Co-authored-by: JosunLP <[email protected]>
Copilot created this pull request from a session on behalf of
JosunLP
May 20, 2026 06:08
View session
JosunLP
marked this pull request as ready for review
May 20, 2026 06:09
| initialNotifyRun = false; | ||
| return; | ||
| } | ||
| if (suppressNotify) { |
Contributor
There was a problem hiding this comment.
Pull request overview
Expands the @bquery/bquery/forms and @bquery/bquery/component modules into a more “batteries-included” tier for the 1.13.0 release, adding new validators, form/field APIs, DOM bindings, SSR helpers, and multiple component ergonomics (refs/slots/DI/events/styles/async/lifecycle), plus re-export wiring, docs, and tests.
Changes:
- Forms: adds new validators + combinators, schema builder, field arrays, DOM bindings, SSR serialize/hydrate helpers, and richer field/form state & APIs.
- Component: adds delegated event binding, DI helpers, refs/slots, async helpers,
csstagged template + adopted styles support, keyed list helpers, and lifecycle additions. - Release wiring: bumps version to 1.13.0 and updates
/fullexports, docs, changelog, and repo guidance snapshots; adds new test suites.
Reviewed changes
Copilot reviewed 34 out of 35 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/forms.test.ts | Updates an internal-subscriber-count assertion after new field effects. |
| tests/forms-extensions.test.ts | New tests covering added forms validators, field/form extensions, bindings, schema, and SSR helpers. |
| tests/component-extensions.test.ts | New tests covering component additions (refs/slots/DI/events/css/keyed lists/async/lifecycle/props). |
| src/full.ts | Re-exports new forms + component runtime/type surface for the full bundle. |
| src/forms/validators.ts | Adds new validators (numeric/date/file/etc.) and combinators (compose/all/not/withMessage). |
| src/forms/use-field.ts | Extends standalone fields with focus/disabled/dirtySince + helper methods. |
| src/forms/types.ts | Extends forms public types (field/form state, arrays, bindings, SSR snapshots, strategies). |
| src/forms/ssr.ts | Adds inline-JSON serialization + DOM read/hydrate helpers for SSR. |
| src/forms/schema.ts | Adds fluent field() / schema() builder over validator factories. |
| src/forms/index.ts | Exposes the expanded forms surface via the module barrel. |
| src/forms/field-array.ts | Adds reactive field array helper with mutation + validation hooks. |
| src/forms/create-form.ts | Expands form runtime: new state signals, subscribe/snapshot/toFormData, auto-validation wiring. |
| src/forms/composables.ts | Adds component-scope-aware useForm/useField/useFieldArray. |
| src/forms/bind.ts | Adds bindField / bindForm DOM bridge for form fields. |
| src/component/types.ts | Extends component types (props API, styles payload type, lifecycle/error boundary additions). |
| src/component/slots.ts | Adds reactive slot helpers (useSlot, hasSlot, slotText). |
| src/component/refs.ts | Adds useRef() utility that auto-clears on disconnect. |
| src/component/keyed-list.ts | Adds keyedList renderer + reconcileKeyed reordering helper. |
| src/component/inject.ts | Adds DI primitives (provide/inject/injectionKey) + formContextKey. |
| src/component/index.ts | Re-exports new component APIs/types from the public barrel. |
| src/component/events.ts | Adds sanitizer-safe delegated event binding (on*, bindDelegatedEvents). |
| src/component/css.ts | Adds css tagged template + adopted stylesheet support + type guard. |
| src/component/component.ts | Integrates new styles payload handling, setProp/getProp, beforeUnmount, and render error boundary. |
| src/component/async.ts | Adds whenIdle and useAsync helpers with scope disposal + aborting. |
| README.md | Updates “New in …” section to 1.13.0 highlights. |
| package.json | Bumps version to 1.13.0. |
| llms.txt | Updates baseline version and adds 1.13.0 highlights. |
| docs/guide/forms.md | Adds “What’s new in 1.13.0” section for forms. |
| docs/guide/components.md | Adds “What’s new in 1.13.0” section for components. |
| CHANGELOG.md | Adds 1.13.0 release notes. |
| bun.lock | Updates dev dependency versions (storybook/eslint/bun-types/vite). |
| AGENT.md | Updates baseline version and adds 1.13.0 highlights. |
| .github/copilot-instructions.md | Updates baseline version and adds 1.13.0 highlights. |
| .cursorrules | Updates baseline version. |
| .clinerules | Updates baseline version. |
Comments suppressed due to low confidence (1)
src/forms/ssr.ts:45
- The
serializeFormStatedocstring says the script writes towindow.__BQUERY_FORMS__[id], but the implementation returns a<script type="application/json" data-bq-form="...">containing raw JSON and never writes towindow. This mismatch will confuse users; update the docs to describe the actual DOM-embedded JSON approach (and/or change implementation).
* Serialize a form snapshot to an inline `<script>` tag suitable for embedding
* in SSR-rendered HTML. The script writes the payload to
* `window.__BQUERY_FORMS__[id]` where it can be read on the client by
* {@link readSerializedFormState} and applied via {@link Form.restore}.
*
* @param id - Stable identifier for this form
* @param snapshot - Snapshot returned by {@link Form.snapshot}
* @returns A complete `<script>` tag string
*
* @example
* ```ts
* import { serializeFormState } from '@bquery/bquery/forms';
*
* const html = `
* <form id="register">...</form>
* ${serializeFormState('register', form.snapshot())}
* `;
* ```
*/
export const serializeFormState = <T extends Record<string, unknown>>(
id: string,
snapshot: FormSnapshot<T>
): string => {
const safeId = String(id).replace(/[^\w-]/g, '');
const json = JSON.stringify(snapshot);
const payload = escapeForScript(json);
return `<script type="application/json" data-bq-form="${safeId}">${payload}</script>`;
};
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/96f1b13a-cb41-4e08-a683-ce8a4d6754ed Co-authored-by: JosunLP <[email protected]>
…, create-form, types, use-field Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/38cd5f64-7b62-470d-a950-9592d50e484a Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/c6685368-00c2-4005-b8ed-64198ec680f6 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/7274dfbe-d040-480d-86a5-08761dd250cc Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/7274dfbe-d040-480d-86a5-08761dd250cc Co-authored-by: JosunLP <[email protected]>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
docs/guide/components.md:501
updated()referencesstate.items, butupdatedhooks don’t havestatein scope here. Use the component instance to read state (e.g.this.getState('items')) or otherwise make the desired key list available insideupdated().
updated() {
reconcileKeyed(
this.shadowRoot!.querySelector('ul')!,
state.items.map((item) => item.id)
);
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/e957d161-f002-402b-a0ed-2d512428be32 Co-authored-by: JosunLP <[email protected]>
Agent-Logs-Url: https://github.com/bQuery/bQuery/sessions/67d159af-2b53-4773-8339-b73b6980dc86 Co-authored-by: JosunLP <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Turns
@bquery/bquery/formsand@bquery/bquery/componentfrom solid building blocks into a true batteries-included tier — covering everything users typically reach for — while preserving zero deps, tree-shakeability, and thecore → reactive → view/forms/componentdependency rule.Forms (
src/forms/*)integer,numeric,between,length,oneOf,notOneOf,arrayOf,requiredIf,requiredUnless,dateAfter,dateBefore,validDate,fileSize,fileType(validDaterenamed from the plannedisDateto avoid collision with the existingcoretype guard), plus combinatorscompose,all,not,withMessage.isValidating/isFocused/dirtySinceonto everyFormField; addedfocus/blur,setValue(v, { touch, validate, silent }),setError/clearError, adisabledsignal that excludes the field from validation, per-fieldvalidateOn/debounceMs, andparse/formatonFieldConfig.submitCount,lastSubmittedAt,submitError, aggregatedisValidating/isPristine,touchAll/untouchAll,resetField,resetErrors,getDirtyValues,subscribe, plusonSubmitError/onSubmitSuccess/validationStrategy/mode: 'all' | 'first'onFormConfig.createFieldArray({ initial, factory, validators })withadd/remove/move/insert/clear.bindFieldandbindForm(auto-discovers[name], wiressubmit, marksaria-invalid, configurableerrorSlotmapper); both return cleanup.useForm/useField/useFieldArraythat auto-dispose with the owning component.schema({ name: field<string>().required().min(2), … })over the existing validator factories.form.toJSON/toFormData/snapshot/restore, plusserializeFormState/readSerializedFormState/hydrateFormbuilt onsrc/ssr/escape.ts.Component (
src/component/*)useRef<T>()(auto-cleared on disconnect),useSlot(reactiveSignal<Element[]>),hasSlot,slotText.on/onClick/onInput/onChange/onSubmit+bindDelegatedEvents(host). Handlers live in a module map keyed by opaque IDs; templates only carrydata-bq-on-<event>="<id>", so noeval, no inlineon*, and the existing sanitizer accepts the markup unchanged. One delegated listener per event type per host.provide/inject/injectionKey<T>()over the composed event path; ships a built-informContextKey.beforeUnmount,errorBoundary(error, info)returning fallback markup, scope-trackedwhenIdle(fn).useAsync(fn)→{ data, error, loading, refresh }withAbortControllercancellation on disconnect or re-invocation.setProp(name, value)/getProp(name)for objects/arrays/callbacks that bypass attribute serialization and trigger a re-render.csstagged template returning aComponentStylespayload, shared across instances viadocument.adoptedStyleSheetswhen constructable stylesheets are available (falls back to<style>); interpolations are CSS-escaped.keyedList(items, key, renderItem)injectsdata-bq-keyinto the first opening tag of each item;reconcileKeyed(container)reorders existing children to match key order.Example
Wiring & docs
src/full.tsand both module barrels re-export every new runtime + type surface;bun run check:full-bundlecontinues to enforce drift detection.tests/forms-extensions.test.ts,tests/component-extensions.test.ts.docs/guide/forms.mdanddocs/guide/components.mdextended with “What's new in 1.13.0” sections.bun run check:ai-guidancepasses).Notes for reviewers
hasMountedsemantics after addingbeforeUnmount— covered by the existing reconnect tests.compose/all/not/withMessage/arrayOfcast their return toValidator<T>because the type is aSyncValidator | AsyncValidatorunion and a single function value cannot inhabit both members of the union purely structurally; runtime behavior is correct in both modes.data-bq-*data attributes that the default sanitizer already permits; no sanitizer changes were needed.