René's URL Explorer Experiment


Title: feat(forms, component): expand into batteries-included tier (1.13.0) by Copilot · Pull Request #95 · bQuery/bQuery · GitHub

Open Graph Title: feat(forms, component): expand into batteries-included tier (1.13.0) by Copilot · Pull Request #95 · bQuery/bQuery

X Title: feat(forms, component): expand into batteries-included tier (1.13.0) by Copilot · Pull Request #95 · bQuery/bQuery

Description: Turns @bquery/bquery/forms and @bquery/bquery/component from solid building blocks into a true batteries-included tier — covering everything users typically reach for — while preserving zero deps, tree-shakeability, and the core → reactive → view/forms/component dependency rule. Forms (src/forms/*) Validators: integer, numeric, between, length, oneOf, notOneOf, arrayOf, requiredIf, requiredUnless, dateAfter, dateBefore, validDate, fileSize, fileType (validDate renamed from the planned isDate to avoid collision with the existing core type guard), plus combinators compose, all, not, withMessage. Field state: lifted isValidating/isFocused/dirtySince onto every FormField; added focus/blur, setValue(v, { touch, validate, silent }), setError/clearError, a disabled signal that excludes the field from validation, per-field validateOn/debounceMs, and parse/format on FieldConfig. Form state: submitCount, lastSubmittedAt, submitError, aggregated isValidating/isPristine, touchAll/untouchAll, resetField, resetErrors, getDirtyValues, subscribe, plus onSubmitError/onSubmitSuccess/validationStrategy/mode: 'all' | 'first' on FormConfig. Field arrays: createFieldArray({ initial, factory, validators }) with add/remove/move/insert/clear. DOM bindings: bindField and bindForm (auto-discovers [name], wires submit, marks aria-invalid, configurable errorSlot mapper); both return cleanup. Composables: scope-aware useForm/useField/useFieldArray that auto-dispose with the owning component. Schema: fluent schema({ name: field().required().min(2), … }) over the existing validator factories. Serialization & SSR: form.toJSON/toFormData/snapshot/restore, plus serializeFormState/readSerializedFormState/hydrateForm built on src/ssr/escape.ts. Component (src/component/*) Refs & slots: useRef() (auto-cleared on disconnect), useSlot (reactive Signal), hasSlot, slotText. Delegated events: on/onClick/onInput/onChange/onSubmit + bindDelegatedEvents(host). Handlers live in a module map keyed by opaque IDs; templates only carry data-bq-on-="", so no eval, no inline on*, and the existing sanitizer accepts the markup unchanged. One delegated listener per event type per host. DI: provide/inject/injectionKey() over the composed event path; ships a built-in formContextKey. Lifecycle: additive beforeUnmount, errorBoundary(error, info) returning fallback markup, scope-tracked whenIdle(fn). Async: useAsync(fn) → { data, error, loading, refresh } with AbortController cancellation on disconnect or re-invocation. Reactive props: instance setProp(name, value)/getProp(name) for objects/arrays/callbacks that bypass attribute serialization and trigger a re-render. Styles: css tagged template returning a ComponentStyles payload, shared across instances via document.adoptedStyleSheets when constructable stylesheets are available (falls back to