René's URL Explorer Experiment


Title: feat(forms): graduate forms toward Stable — progressive-enhancement actions + optimistic updates (#139, #140) by JosunLP · Pull Request #156 · bQuery/bQuery · GitHub

Open Graph Title: feat(forms): graduate forms toward Stable — progressive-enhancement actions + optimistic updates (#139, #140) by JosunLP · Pull Request #156 · bQuery/bQuery

X Title: feat(forms): graduate forms toward Stable — progressive-enhancement actions + optimistic updates (#139, #140) by JosunLP · Pull Request #156 · bQuery/bQuery

Description: Collected PR for both open forms tickets, implemented on one branch. Closes #139 Closes #140 #139 — Promote forms to Stable (freeze the 1.13 surface) The forms surface expanded materially in 1.13.0 and has documented sharp edges. This freezes the surface for one minor cycle and settles those edges into guaranteed, tested contracts: validationStrategy default reviewed + documented. The default stays 'manual' (the least-surprising choice for "validate on submit"). The contract is now explicit: handleSubmit() always runs the full validation pass, regardless of strategy — validationStrategy only gates automatic per-change/per-blur validation. Behaviour is unchanged; the surprise is removed by documentation. SSR serialization boundary is now guaranteed. serializeFormState() deterministically drops functions, File / Blob / FileList, bigint, and symbol via an explicit replacer, instead of relying on incidental JSON.stringify behaviour. Re-attach blobs on the client after hydration. createFieldArray() stable-key contract validated with clear errors. New optional getKey enforces present, unique keys on every structural mutation and throws a descriptive error naming the offending key (e.g. "requires stable, unique item keys, but getKey returned "a" for both index 0 and index 1"). Adds keys() / keyAt(index). Without getKey the array stays positional — no behaviour change. Surface frozen + documented. New Stability section in the Forms guide with the exit-criteria checklist and frozen-surface reference; module header and stability matrices updated to "targeting Stable in 1.15.0". #140 — Progressive-enhancement form actions + optimistic updates The headline React-19-parity feature: forms that work before/without JS and reconcile optimistically. import { formAction, useFormStatus, optimistic } from '@bquery/bquery/forms'; const submit = formAction('/todos', { method: 'POST', csrf: () => csrfToken }); const { pending } = useFormStatus(submit); const list = optimistic(todos, (cur, draft) => [...cur, draft]); submit.enhance(document.querySelector('form')!); // native POST without JS; fetch-enhanced with JS formAction(target, options) → reactive pending / error / result / submitCount / submittedAt; enhance(form) sets the native action/method (+ optional hidden CSRF field) for the no-JS path, then intercepts submit for a fetch-based, optimistic-aware submit; programmatic submit(formData); reset(). target is an endpoint URL or a function. Non-OK responses throw FormActionError (carrying status / response). Composes with the validation pipeline and the server module's csrf() (#132). Native forms only support GET/POST, so PUT/PATCH/DELETE degrade to a native POST (the enhanced fetch keeps the real verb). useFormStatus(action) → read-only readonly() views of the action's signals, mirroring React 19's useFormStatus. optimistic(base, reducer) → an optimistic-update primitive whose reactive value folds pending drafts over the base and reverts automatically. add(draft) → handle with remove(); run(draft, task) applies the overlay around an async task; pending / drafts reactive; clear(). Quality 28 new tests across tests/forms-stable.test.ts (field-array key contract, SSR boundary, validationStrategy timing) and tests/forms-actions.test.ts (formAction function + string targets, CSRF, enhance/PE, optimistic composition, useFormStatus). Full suite 2882 pass / 0 fail; tsc --noEmit, eslint, check-full-bundle (in sync), check-doc-exports (forms 46/46), and build:lib / build:types / build:umd all green. New public exports (formAction, useFormStatus, optimistic, FormActionError, FieldArrayKeyFn + action/optimistic types) are wired into src/full.ts and documented in the guide. 🤖 Generated with Claude Code

Open Graph Description: Collected PR for both open forms tickets, implemented on one branch. Closes #139 Closes #140 #139 — Promote forms to Stable (freeze the 1.13 surface) The forms surface expanded materially in 1.13.0...

X Description: Collected PR for both open forms tickets, implemented on one branch. Closes #139 Closes #140 #139 — Promote forms to Stable (freeze the 1.13 surface) The forms surface expanded materially in 1.13.0...

Opengraph URL: https://github.com/bQuery/bQuery/pull/156

X: @github

direct link

Domain: github.com

route-pattern/:user_id/:repository/pull/:id/files(.:format)
route-controllerpull_requests
route-actionfiles
fetch-noncev2:d26b2b5f-fb4e-3f06-f249-c6721db4806f
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-id9584:1E6125:2EFECF:4283C3:6A4E6AF6
html-safe-nonce6659e233ec2521d73242bb7224a41ffe32f5d8035400349c018fdb311c1e1231
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NTg0OjFFNjEyNToyRUZFQ0Y6NDI4M0MzOjZBNEU2QUY2IiwidmlzaXRvcl9pZCI6IjM0MTYyNjcyNzQ0OTkwMjU2NTQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmace026edae42bd6ae684368a0b60286a95e5f8a10916579bff0ad27a413188f2e6
hovercard-subject-tagpull_request:3951880978
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/bQuery/bQuery/pull/156/files
twitter:imagehttps://avatars.githubusercontent.com/u/20913954?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/20913954?s=400&v=4
og:image:altCollected PR for both open forms tickets, implemented on one branch. Closes #139 Closes #140 #139 — Promote forms to Stable (freeze the 1.13 surface) The forms surface expanded materially in 1.13.0...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
None41b6ab3ba6d20a71766ac245b5a4a94c6fc672a9cd4da7d44c1b33ab8bf6a21c
turbo-cache-controlno-preview
diff-viewunified
go-importgithub.com/bQuery/bQuery git https://github.com/bQuery/bQuery.git
octolytics-dimension-user_id256381806
octolytics-dimension-user_loginbQuery
octolytics-dimension-repository_id1139284244
octolytics-dimension-repository_nwobQuery/bQuery
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id1139284244
octolytics-dimension-repository_network_root_nwobQuery/bQuery
turbo-body-classeslogged-out env-production page-responsive full-width
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
releasee6a744804e8e70f97b4d5a18a94dcc63db22f97a
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/bQuery/bQuery/pull/156/files#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FbQuery%2FbQuery%2Fpull%2F156%2Ffiles
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FbQuery%2FbQuery%2Fpull%2F156%2Ffiles
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=bQuery%2FbQuery
Reloadhttps://github.com/bQuery/bQuery/pull/156/files
Reloadhttps://github.com/bQuery/bQuery/pull/156/files
Reloadhttps://github.com/bQuery/bQuery/pull/156/files
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
bQuery https://github.com/bQuery
bQueryhttps://github.com/bQuery/bQuery
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
Notifications https://github.com/login?return_to=%2FbQuery%2FbQuery
Fork 0 https://github.com/login?return_to=%2FbQuery%2FbQuery
Star 1 https://github.com/login?return_to=%2FbQuery%2FbQuery
Code https://github.com/bQuery/bQuery
Issues 0 https://github.com/bQuery/bQuery/issues
Pull requests 0 https://github.com/bQuery/bQuery/pulls
Actions https://github.com/bQuery/bQuery/actions
Security and quality 0 https://github.com/bQuery/bQuery/security
Insights https://github.com/bQuery/bQuery/pulse
Code https://github.com/bQuery/bQuery
Issues https://github.com/bQuery/bQuery/issues
Pull requests https://github.com/bQuery/bQuery/pulls
Actions https://github.com/bQuery/bQuery/actions
Security and quality https://github.com/bQuery/bQuery/security
Insights https://github.com/bQuery/bQuery/pulse
Sign up for GitHub https://github.com/signup?return_to=%2FbQuery%2FbQuery%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2FbQuery%2FbQuery%2Fissues%2Fnew%2Fchoose
JosunLPhttps://github.com/JosunLP
devhttps://github.com/bQuery/bQuery/tree/dev
feat/forms-stable-actionshttps://github.com/bQuery/bQuery/tree/feat/forms-stable-actions
Conversation 1 https://github.com/bQuery/bQuery/pull/156
Commits 1 https://github.com/bQuery/bQuery/pull/156/commits
Checks 8 https://github.com/bQuery/bQuery/pull/156/checks
Files changed https://github.com/bQuery/bQuery/pull/156/files
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
feat(forms): graduate forms toward Stable — progressive-enhancement actions + optimistic updates (#139, #140) https://github.com/bQuery/bQuery/pull/156/files#top
Show all changes 1 commit https://github.com/bQuery/bQuery/pull/156/files
2d4d3d4 feat(forms): graduate forms toward Stable — progressive-enhancement a… JosunLP Jun 28, 2026 https://github.com/bQuery/bQuery/pull/156/commits/2d4d3d45cbe069827f78aaa45584532fc4e914af
Clear filters https://github.com/bQuery/bQuery/pull/156/files
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
CHANGELOG.md https://github.com/bQuery/bQuery/pull/156/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed
README.md https://github.com/bQuery/bQuery/pull/156/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5
forms.md https://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
introduction.md https://github.com/bQuery/bQuery/pull/156/files#diff-56f8d2b8c873ba84c43d98f257eeec70dc5901f0b9635db6f90bd874b914e97d
action.ts https://github.com/bQuery/bQuery/pull/156/files#diff-d72b4b905f4e079ab25ee646f1ca2a6b664b22348774f4aca6d7bc0f2051ef16
field-array.ts https://github.com/bQuery/bQuery/pull/156/files#diff-eea9ebf6949e2ff2eecaad4330bb3c926f78378a713df06eed5644b3f2dd3a74
index.ts https://github.com/bQuery/bQuery/pull/156/files#diff-b6f5bbedcf1b6d4f4f4eb80366b8e250a1d2261c206e834d5c9496b25ff5f7a8
optimistic.ts https://github.com/bQuery/bQuery/pull/156/files#diff-a87cbf6a7b3f2834f242c8d78eb6f4795205eb9dc22d4fc4cdb6189fcd909bc6
ssr.ts https://github.com/bQuery/bQuery/pull/156/files#diff-5c24cb388da0a0e41aedf67efebd9274a80fc961298c1efb1a61f19ae9011f9e
types.ts https://github.com/bQuery/bQuery/pull/156/files#diff-2eaa2ac755a5d04c172fe10ac10dee2af9e35f6ced2b25dcefa5948ec2afcb76
full.ts https://github.com/bQuery/bQuery/pull/156/files#diff-3061502541780be549a3418b68cb60d2fbe8fc17f84f6ec533ce6bd0ab4eb95b
forms-actions.test.ts https://github.com/bQuery/bQuery/pull/156/files#diff-00c91af15b033631edc0e5be3b32a96f95359030970084d683f697869e55400e
forms-stable.test.ts https://github.com/bQuery/bQuery/pull/156/files#diff-e2e2fe446516ce86a1dc2c80792d9c496cc73cca512754f0a7324447df0444cf
CHANGELOG.mdhttps://github.com/bQuery/bQuery/pull/156/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed
View file https://github.com/bQuery/bQuery/blob/2d4d3d45cbe069827f78aaa45584532fc4e914af/CHANGELOG.md
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/bQuery/bQuery/pull/156/{{ revealButtonHref }}
https://github.com/bQuery/bQuery/pull/156/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed
https://github.com/bQuery/bQuery/pull/156/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed
README.mdhttps://github.com/bQuery/bQuery/pull/156/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5
View file https://github.com/bQuery/bQuery/blob/2d4d3d45cbe069827f78aaa45584532fc4e914af/README.md
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/bQuery/bQuery/pull/156/{{ revealButtonHref }}
https://github.com/bQuery/bQuery/pull/156/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5
https://github.com/bQuery/bQuery/pull/156/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5
docs/guide/forms.mdhttps://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
View file https://github.com/bQuery/bQuery/blob/2d4d3d45cbe069827f78aaa45584532fc4e914af/docs/guide/forms.md
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/bQuery/bQuery/pull/156/{{ revealButtonHref }}
https://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
https://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
https://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
https://github.com/bQuery/bQuery/pull/156/files#diff-0153325d0827f1e414579473024be97daee5112f98f535fe8fd16bc7ed957c9d
docs/introduction.mdhttps://github.com/bQuery/bQuery/pull/156/files#diff-56f8d2b8c873ba84c43d98f257eeec70dc5901f0b9635db6f90bd874b914e97d
View file https://github.com/bQuery/bQuery/blob/2d4d3d45cbe069827f78aaa45584532fc4e914af/docs/introduction.md
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/bQuery/bQuery/pull/156/{{ revealButtonHref }}
https://github.com/bQuery/bQuery/pull/156/files#diff-56f8d2b8c873ba84c43d98f257eeec70dc5901f0b9635db6f90bd874b914e97d
https://github.com/bQuery/bQuery/pull/156/files#diff-56f8d2b8c873ba84c43d98f257eeec70dc5901f0b9635db6f90bd874b914e97d
Please reload this pagehttps://github.com/bQuery/bQuery/pull/156/files
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.