Showing preview only (5,246K chars total). Download the full file or copy to clipboard to get everything.
Repository: remix-run/remix
Branch: main
Commit: b1741d7dbf36
Files: 1168
Total size: 4.8 MB
Directory structure:
gitextract_a33wb0o_/
├── .agents/
│ └── skills/
│ ├── add-package/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-change-file/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-demo/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-pr/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── publish-placeholder-package/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── supersede-pr/
│ │ ├── SKILL.md
│ │ ├── agents/
│ │ │ └── openai.yaml
│ │ ├── scripts/
│ │ │ └── close_superseded_pr.ts
│ │ └── tsconfig.json
│ ├── update-pr/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── write-api-docs/
│ │ └── SKILL.md
│ └── write-readme/
│ ├── SKILL.md
│ └── agents/
│ └── openai.yaml
├── .github/
│ └── workflows/
│ ├── build.yaml
│ ├── check.yaml
│ ├── data-table-integration.yaml
│ ├── file-storage-integration.yaml
│ ├── format.yml
│ ├── generate-remix.yaml
│ ├── preview.yml
│ ├── publish.yaml
│ ├── release-pr.yaml
│ ├── session-integration.yaml
│ └── test.yaml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode/
│ ├── settings.json
│ └── task.json
├── AGENTS.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cspell.yml
├── decisions/
│ ├── 001-route-pattern-vs-url-pattern.md
│ └── 002-branching-and-releasing.md
├── demos/
│ ├── bookstore/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── account.test.ts
│ │ │ ├── account.tsx
│ │ │ ├── admin.books.test.ts
│ │ │ ├── admin.books.tsx
│ │ │ ├── admin.orders.tsx
│ │ │ ├── admin.test.ts
│ │ │ ├── admin.tsx
│ │ │ ├── admin.users.tsx
│ │ │ ├── assets/
│ │ │ │ ├── cart-button.tsx
│ │ │ │ ├── cart-items.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── image-carousel.tsx
│ │ │ ├── auth.test.ts
│ │ │ ├── auth.tsx
│ │ │ ├── books.test.ts
│ │ │ ├── books.tsx
│ │ │ ├── cart.test.ts
│ │ │ ├── cart.tsx
│ │ │ ├── checkout.test.ts
│ │ │ ├── checkout.tsx
│ │ │ ├── components/
│ │ │ │ ├── book-card.tsx
│ │ │ │ └── restful-form.tsx
│ │ │ ├── data/
│ │ │ │ ├── cart.ts
│ │ │ │ ├── schema.ts
│ │ │ │ ├── setup.test.ts
│ │ │ │ └── setup.ts
│ │ │ ├── fragments.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── marketing.test.ts
│ │ │ ├── marketing.tsx
│ │ │ ├── middleware/
│ │ │ │ ├── admin.ts
│ │ │ │ ├── auth.ts
│ │ │ │ └── database.ts
│ │ │ ├── router.test.ts
│ │ │ ├── router.ts
│ │ │ ├── routes.ts
│ │ │ ├── uploads.test.ts
│ │ │ ├── uploads.tsx
│ │ │ └── utils/
│ │ │ ├── context.ts
│ │ │ ├── ids.ts
│ │ │ ├── render.ts
│ │ │ ├── session.ts
│ │ │ └── uploads.ts
│ │ ├── data/
│ │ │ └── migrations/
│ │ │ └── 20260228090000_create_bookstore_schema.ts
│ │ ├── package.json
│ │ ├── public/
│ │ │ └── app.css
│ │ ├── server.ts
│ │ ├── test/
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── frame-navigation/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── dashboard-stat-grid.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── fake.tsx
│ │ │ ├── auth/
│ │ │ │ ├── controller.tsx
│ │ │ │ └── session.ts
│ │ │ ├── lib/
│ │ │ │ ├── Layout.tsx
│ │ │ │ └── NavLink.tsx
│ │ │ ├── main/
│ │ │ │ ├── account.tsx
│ │ │ │ ├── calendar.tsx
│ │ │ │ ├── controller.tsx
│ │ │ │ ├── courses.tsx
│ │ │ │ └── index.tsx
│ │ │ └── settings/
│ │ │ ├── controller.tsx
│ │ │ ├── grading.tsx
│ │ │ ├── index.tsx
│ │ │ ├── integrations.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── notifications.tsx
│ │ │ ├── privacy.tsx
│ │ │ └── profile.tsx
│ │ ├── config/
│ │ │ ├── render.tsx
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── server.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── frames/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── client-frame-example.tsx
│ │ │ │ ├── client-mounted-page-example.tsx
│ │ │ │ ├── counter.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── reload-scope.tsx
│ │ │ │ ├── reload-time.tsx
│ │ │ │ └── state-search-page.tsx
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── us-states.ts
│ │ ├── package.json
│ │ ├── server.ts
│ │ └── tsconfig.json
│ ├── sse/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── message-stream.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── router.test.ts
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── utils/
│ │ │ └── render.ts
│ │ ├── package.json
│ │ ├── server.ts
│ │ └── tsconfig.json
│ └── unpkg/
│ ├── README.md
│ ├── app/
│ │ ├── breadcrumb.ts
│ │ ├── directory.ts
│ │ ├── error.ts
│ │ ├── file-content.ts
│ │ ├── router.test.ts
│ │ ├── router.ts
│ │ ├── routes.ts
│ │ └── utils/
│ │ ├── cache.ts
│ │ ├── npm.test.ts
│ │ ├── npm.ts
│ │ ├── render.test.ts
│ │ └── render.ts
│ ├── package.json
│ ├── server.ts
│ ├── test/
│ │ ├── fixtures/
│ │ │ ├── is-number-7.0.0.tgz
│ │ │ └── is-number-metadata.json
│ │ └── mock-fetch.ts
│ └── tsconfig.json
├── docs/
│ ├── .gitignore
│ ├── package.json
│ ├── public/
│ │ └── docs.css
│ ├── src/
│ │ ├── client/
│ │ │ └── entry.tsx
│ │ ├── generate/
│ │ │ ├── documented-api.ts
│ │ │ ├── index.ts
│ │ │ ├── markdown.ts
│ │ │ ├── symbols.ts
│ │ │ ├── typedoc.ts
│ │ │ └── utils.ts
│ │ └── server/
│ │ ├── components.tsx
│ │ ├── index.ts
│ │ ├── markdown.ts
│ │ ├── prerender.ts
│ │ ├── router.tsx
│ │ └── routes.ts
│ └── tsconfig.json
├── eslint.config.js
├── package.json
├── packages/
│ ├── async-context-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── async-context.test.ts
│ │ │ └── async-context.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── component/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.01-add-mixin-system-and-core-helpers.md
│ │ │ ├── minor.02-add-press-and-keyboard-mixins.md
│ │ │ ├── minor.03-add-animation-mixins.md
│ │ │ ├── minor.04-remove-legacy-on-prop.md
│ │ │ ├── minor.05-remove-legacy-css-prop.md
│ │ │ ├── minor.06-remove-legacy-animate-prop.md
│ │ │ ├── minor.07-remove-legacy-connect-prop.md
│ │ │ ├── minor.08-interaction-package-removed.md
│ │ │ ├── minor.09-allow-single-mix-values.md
│ │ │ ├── minor.13-allow-remix-node-frame-content.md
│ │ │ ├── minor.frame-navigation-link-attributes.md
│ │ │ ├── minor.frame-navigation-runtime.md
│ │ │ ├── minor.remove-head-hoisting.md
│ │ │ ├── minor.resolve-frame-target.md
│ │ │ ├── minor.ssr-frame-src-context.md
│ │ │ ├── patch.10-preserve-live-dom-state.md
│ │ │ ├── patch.11-forward-client-entry-root-errors.md
│ │ │ ├── patch.defer-mixin-lifecycle-events.md
│ │ │ ├── patch.fix-adjacent-hydration-markers.md
│ │ │ ├── patch.fix-svg-classname-mapping.md
│ │ │ ├── patch.resolve-svg-link-targets.md
│ │ │ └── patch.skip-download-link-interception.md
│ │ ├── AGENTS.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── .gitignore
│ │ │ ├── frameworks/
│ │ │ │ ├── preact/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── preact-signals/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── react/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── remix/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── shared.ts
│ │ │ │ ├── solid/
│ │ │ │ │ ├── build.js
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ └── styles.css
│ │ │ ├── package.json
│ │ │ ├── runner.ts
│ │ │ ├── server.ts
│ │ │ └── tsconfig.json
│ │ ├── demos/
│ │ │ ├── .gitignore
│ │ │ ├── animation/
│ │ │ │ ├── aspect-ratio.tsx
│ │ │ │ ├── bouncy-switch.tsx
│ │ │ │ ├── color-interpolation.tsx
│ │ │ │ ├── cube.tsx
│ │ │ │ ├── default-animate.tsx
│ │ │ │ ├── enter.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── exit.tsx
│ │ │ │ ├── flip-toggle.tsx
│ │ │ │ ├── hold-to-confirm.tsx
│ │ │ │ ├── html-content.tsx
│ │ │ │ ├── index.html
│ │ │ │ ├── interruptible-keyframes.tsx
│ │ │ │ ├── keyframes.tsx
│ │ │ │ ├── material-ripple.tsx
│ │ │ │ ├── mixin-presence-list.tsx
│ │ │ │ ├── mixin-reclaim.tsx
│ │ │ │ ├── multi-state-badge.tsx
│ │ │ │ ├── press.tsx
│ │ │ │ ├── reordering.tsx
│ │ │ │ ├── rolling-square.tsx
│ │ │ │ ├── rotate.tsx
│ │ │ │ ├── shared-layout.tsx
│ │ │ │ └── transition-options.tsx
│ │ │ ├── basic/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── controlled-uncontrolled-values/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── draggable/
│ │ │ │ ├── draggable.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── drummer/
│ │ │ │ ├── app.tsx
│ │ │ │ ├── components.tsx
│ │ │ │ ├── drummer.ts
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── index.html
│ │ │ │ ├── tempo-interaction.tsx
│ │ │ │ └── voice-looper.ts
│ │ │ ├── keyed-list/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── package.json
│ │ │ ├── readme/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── server.ts
│ │ │ ├── spring/
│ │ │ │ ├── drag-release.ts
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ └── tsconfig.json
│ │ ├── docs/
│ │ │ ├── components.md
│ │ │ ├── composition.md
│ │ │ ├── context.md
│ │ │ ├── events.md
│ │ │ ├── frames.md
│ │ │ ├── getting-started.md
│ │ │ ├── handle.md
│ │ │ ├── hydration.md
│ │ │ ├── interactions.md
│ │ │ ├── patterns.md
│ │ │ ├── server-rendering.md
│ │ │ ├── spring.md
│ │ │ ├── styling.md
│ │ │ ├── testing.md
│ │ │ └── tween.md
│ │ ├── package.json
│ │ ├── skills/
│ │ │ ├── animate-elements/
│ │ │ │ └── SKILL.md
│ │ │ └── create-mixins/
│ │ │ └── SKILL.md
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── jsx-dev-runtime.ts
│ │ │ ├── jsx-runtime.ts
│ │ │ ├── lib/
│ │ │ │ ├── client-entries.ts
│ │ │ │ ├── component.ts
│ │ │ │ ├── create-element.ts
│ │ │ │ ├── diff-dom.ts
│ │ │ │ ├── diff-props.ts
│ │ │ │ ├── document-state.ts
│ │ │ │ ├── dom.ts
│ │ │ │ ├── error-event.ts
│ │ │ │ ├── event-listeners.ts
│ │ │ │ ├── frame.ts
│ │ │ │ ├── invariant.ts
│ │ │ │ ├── jsx.ts
│ │ │ │ ├── mixin.ts
│ │ │ │ ├── mixins/
│ │ │ │ │ ├── animate-layout-mixin.test.tsx
│ │ │ │ │ ├── animate-layout-mixin.tsx
│ │ │ │ │ ├── animate-mixins.test.tsx
│ │ │ │ │ ├── animate-mixins.tsx
│ │ │ │ │ ├── css-mixin.test.tsx
│ │ │ │ │ ├── css-mixin.tsx
│ │ │ │ │ ├── keys-mixin.test.tsx
│ │ │ │ │ ├── keys-mixin.tsx
│ │ │ │ │ ├── link-mixin.test.tsx
│ │ │ │ │ ├── link-mixin.tsx
│ │ │ │ │ ├── on-mixin.test.tsx
│ │ │ │ │ ├── on-mixin.tsx
│ │ │ │ │ ├── press-mixin.test.tsx
│ │ │ │ │ ├── press-mixin.tsx
│ │ │ │ │ ├── ref-mixin.test.tsx
│ │ │ │ │ └── ref-mixin.tsx
│ │ │ │ ├── navigation.ts
│ │ │ │ ├── reconcile.ts
│ │ │ │ ├── run.ts
│ │ │ │ ├── scheduler.ts
│ │ │ │ ├── spring.ts
│ │ │ │ ├── stream.ts
│ │ │ │ ├── style/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib/
│ │ │ │ │ ├── style.ts
│ │ │ │ │ └── stylesheet.ts
│ │ │ │ ├── svg-attributes.ts
│ │ │ │ ├── to-vnode.ts
│ │ │ │ ├── tween.ts
│ │ │ │ ├── typed-event-target.ts
│ │ │ │ ├── vdom.ts
│ │ │ │ └── vnode.ts
│ │ │ ├── server.ts
│ │ │ └── test/
│ │ │ ├── client-entry.test.tsx
│ │ │ ├── create-element.test.ts
│ │ │ ├── diff-dom.test.tsx
│ │ │ ├── document-state.test.ts
│ │ │ ├── event-listeners.test.tsx
│ │ │ ├── frame.test.tsx
│ │ │ ├── hydration.attributes.test.tsx
│ │ │ ├── hydration.boolean-attrs.test.tsx
│ │ │ ├── hydration.components.test.tsx
│ │ │ ├── hydration.css.test.tsx
│ │ │ ├── hydration.extra-nodes.test.tsx
│ │ │ ├── hydration.forms.test.tsx
│ │ │ ├── hydration.mismatch.test.tsx
│ │ │ ├── hydration.text.test.tsx
│ │ │ ├── hydration.void-elements.test.tsx
│ │ │ ├── jsx.test.tsx
│ │ │ ├── navigation.test.ts
│ │ │ ├── spring.test.ts
│ │ │ ├── stream.test.tsx
│ │ │ ├── style.test.ts
│ │ │ ├── stylesheet.test.ts
│ │ │ ├── utils.ts
│ │ │ ├── vdom.components.test.tsx
│ │ │ ├── vdom.connect.test.tsx
│ │ │ ├── vdom.context.test.tsx
│ │ │ ├── vdom.controlled-props.test.tsx
│ │ │ ├── vdom.dom-order.test.tsx
│ │ │ ├── vdom.elements-fragments.test.tsx
│ │ │ ├── vdom.errors.test.tsx
│ │ │ ├── vdom.events.test.tsx
│ │ │ ├── vdom.insert-remove.test.tsx
│ │ │ ├── vdom.keys.test.tsx
│ │ │ ├── vdom.mixins.test.tsx
│ │ │ ├── vdom.props.test.tsx
│ │ │ ├── vdom.range-root.test.tsx
│ │ │ ├── vdom.replacements.test.tsx
│ │ │ ├── vdom.scheduler.test.tsx
│ │ │ ├── vdom.signals.test.tsx
│ │ │ ├── vdom.svg.test.tsx
│ │ │ └── vdom.tasks.test.tsx
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── compression-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── global.d.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── compression.test.ts
│ │ │ └── compression.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cookie/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cookie-signing.ts
│ │ │ ├── cookie.test.ts
│ │ │ └── cookie.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cop-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cop.test.ts
│ │ │ └── cop.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cors-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cors.test.ts
│ │ │ └── cors.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── csrf-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── csrf.test.ts
│ │ │ └── csrf.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-schema/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.form-data.md
│ │ │ └── patch.remove-unnecessary-as-const-from-enum.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── checks.ts
│ │ │ ├── coerce.ts
│ │ │ ├── form-data.ts
│ │ │ ├── index.ts
│ │ │ ├── lazy.ts
│ │ │ └── lib/
│ │ │ ├── checks.test.ts
│ │ │ ├── checks.ts
│ │ │ ├── coerce.test.ts
│ │ │ ├── coerce.ts
│ │ │ ├── form-data.test.ts
│ │ │ ├── form-data.ts
│ │ │ ├── lazy.test.ts
│ │ │ ├── lazy.ts
│ │ │ ├── parse.test.ts
│ │ │ ├── pipe.test.ts
│ │ │ ├── schema.test.ts
│ │ │ ├── schema.ts
│ │ │ └── variant.test.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.database-class-export.md
│ │ │ ├── minor.migration-system-features.md
│ │ │ ├── minor.operation-contract-split.md
│ │ │ ├── minor.query-object-api.md
│ │ │ ├── minor.sql-root-api.md
│ │ │ ├── minor.table-column-cutover.md
│ │ │ └── minor.table-lifecycle-callbacks.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── adapter.ts
│ │ │ │ ├── column.ts
│ │ │ │ ├── database/
│ │ │ │ │ ├── execution-context.ts
│ │ │ │ │ ├── helpers.ts
│ │ │ │ │ ├── query-execution.ts
│ │ │ │ │ ├── relations.ts
│ │ │ │ │ └── write-lifecycle.ts
│ │ │ │ ├── database.test.ts
│ │ │ │ ├── database.ts
│ │ │ │ ├── errors.test.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── inflection.test.ts
│ │ │ │ ├── inflection.ts
│ │ │ │ ├── migrations/
│ │ │ │ │ ├── filename.ts
│ │ │ │ │ ├── helpers.ts
│ │ │ │ │ ├── journal-store.ts
│ │ │ │ │ ├── registry.ts
│ │ │ │ │ ├── runner.ts
│ │ │ │ │ └── schema-api.ts
│ │ │ │ ├── migrations-node.test.ts
│ │ │ │ ├── migrations-node.ts
│ │ │ │ ├── migrations.test.ts
│ │ │ │ ├── migrations.ts
│ │ │ │ ├── operators.test.ts
│ │ │ │ ├── operators.ts
│ │ │ │ ├── query.ts
│ │ │ │ ├── references.test.ts
│ │ │ │ ├── references.ts
│ │ │ │ ├── sql-helpers.ts
│ │ │ │ ├── sql.test.ts
│ │ │ │ ├── sql.ts
│ │ │ │ ├── table.test.ts
│ │ │ │ ├── table.ts
│ │ │ │ ├── type-safety.test.ts
│ │ │ │ └── types.ts
│ │ │ ├── migrations/
│ │ │ │ └── node.ts
│ │ │ ├── migrations.ts
│ │ │ ├── operators.ts
│ │ │ └── sql-helpers.ts
│ │ ├── test/
│ │ │ ├── adapter-integration-contract.ts
│ │ │ ├── adapter-integration-schema.ts
│ │ │ ├── sqlite-adapter.ts
│ │ │ └── sqlite-test-database.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-mysql/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-postgres/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-sqlite/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fetch-proxy/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fetch-proxy.test.ts
│ │ │ └── fetch-proxy.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fetch-router/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.request-context-storage-methods.md
│ │ │ ├── minor.simplify-controller-shape.md
│ │ │ └── patch.optional-action-middleware.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ ├── bun/
│ │ │ │ ├── README.md
│ │ │ │ ├── app/
│ │ │ │ │ ├── data.ts
│ │ │ │ │ ├── router.ts
│ │ │ │ │ └── routes.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── package.json
│ │ │ │ └── tsconfig.json
│ │ │ ├── cf-workers/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── app/
│ │ │ │ │ ├── data.ts
│ │ │ │ │ ├── router.ts
│ │ │ │ │ └── routes.ts
│ │ │ │ ├── migrations/
│ │ │ │ │ └── 0001_initial.sql
│ │ │ │ ├── package.json
│ │ │ │ ├── tsconfig.json
│ │ │ │ ├── worker-configuration.d.ts
│ │ │ │ ├── worker.ts
│ │ │ │ └── wrangler.jsonc
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── app/
│ │ │ │ ├── data.ts
│ │ │ │ ├── router.ts
│ │ │ │ └── routes.ts
│ │ │ ├── package.json
│ │ │ ├── server.ts
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── controller.ts
│ │ │ │ ├── middleware.test.ts
│ │ │ │ ├── middleware.ts
│ │ │ │ ├── request-abort.test.ts
│ │ │ │ ├── request-abort.ts
│ │ │ │ ├── request-context.test.ts
│ │ │ │ ├── request-context.ts
│ │ │ │ ├── request-methods.ts
│ │ │ │ ├── route-helpers/
│ │ │ │ │ ├── form.test.ts
│ │ │ │ │ ├── form.ts
│ │ │ │ │ ├── method.test.ts
│ │ │ │ │ ├── method.ts
│ │ │ │ │ ├── resource.test.ts
│ │ │ │ │ ├── resource.ts
│ │ │ │ │ ├── resources.test.ts
│ │ │ │ │ └── resources.ts
│ │ │ │ ├── route-map.test.ts
│ │ │ │ ├── route-map.ts
│ │ │ │ ├── router-abort.test.ts
│ │ │ │ ├── router.test.ts
│ │ │ │ ├── router.ts
│ │ │ │ └── type-utils.ts
│ │ │ └── routes.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── file-storage/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── fs.ts
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── backends/
│ │ │ │ │ ├── fs.test.ts
│ │ │ │ │ ├── fs.ts
│ │ │ │ │ ├── memory.test.ts
│ │ │ │ │ └── memory.ts
│ │ │ │ └── file-storage.ts
│ │ │ └── memory.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── file-storage-s3/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── s3.integration.test.ts
│ │ │ ├── s3.test.ts
│ │ │ └── s3.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── form-data-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.context-form-data-key.md
│ │ │ └── patch.no-op-if-already-parsed.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── form-data.test.ts
│ │ │ └── form-data.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── form-data-parser/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.aggregate-multipart-limits.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── form-data.test.ts
│ │ │ └── form-data.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fs/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fs.test.ts
│ │ │ └── fs.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── headers/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── accept-encoding.test.ts
│ │ │ ├── accept-encoding.ts
│ │ │ ├── accept-language.test.ts
│ │ │ ├── accept-language.ts
│ │ │ ├── accept.test.ts
│ │ │ ├── accept.ts
│ │ │ ├── cache-control.test.ts
│ │ │ ├── cache-control.ts
│ │ │ ├── content-disposition.test.ts
│ │ │ ├── content-disposition.ts
│ │ │ ├── content-range.test.ts
│ │ │ ├── content-range.ts
│ │ │ ├── content-type.test.ts
│ │ │ ├── content-type.ts
│ │ │ ├── cookie.test.ts
│ │ │ ├── cookie.ts
│ │ │ ├── header-names.test.ts
│ │ │ ├── header-names.ts
│ │ │ ├── header-value.ts
│ │ │ ├── if-match.test.ts
│ │ │ ├── if-match.ts
│ │ │ ├── if-none-match.test.ts
│ │ │ ├── if-none-match.ts
│ │ │ ├── if-range.test.ts
│ │ │ ├── if-range.ts
│ │ │ ├── param-values.test.ts
│ │ │ ├── param-values.ts
│ │ │ ├── range.test.ts
│ │ │ ├── range.ts
│ │ │ ├── raw-headers.test.ts
│ │ │ ├── raw-headers.ts
│ │ │ ├── set-cookie.test.ts
│ │ │ ├── set-cookie.ts
│ │ │ ├── utils.ts
│ │ │ ├── vary.test.ts
│ │ │ └── vary.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── html-template/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── safe-html.test.ts
│ │ │ └── safe-html.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lazy-file/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── globals.ts
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── byte-range.test.ts
│ │ │ ├── byte-range.ts
│ │ │ ├── lazy-file.test.ts
│ │ │ └── lazy-file.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── logger-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── logger.test.ts
│ │ │ └── logger.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── method-override-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── method-override.test.ts
│ │ │ └── method-override.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── mime/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ ├── codegen.test.ts
│ │ │ └── codegen.ts
│ │ ├── src/
│ │ │ ├── generated/
│ │ │ │ ├── compressible-mime-types.ts
│ │ │ │ └── mime-types.ts
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── define-mime-type.test.ts
│ │ │ ├── define-mime-type.ts
│ │ │ ├── detect-content-type.test.ts
│ │ │ ├── detect-content-type.ts
│ │ │ ├── detect-mime-type.test.ts
│ │ │ ├── detect-mime-type.ts
│ │ │ ├── is-compressible-mime-type.test.ts
│ │ │ ├── is-compressible-mime-type.ts
│ │ │ ├── mime-type-to-content-type.test.ts
│ │ │ └── mime-type-to-content-type.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── multipart-parser/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.aggregate-multipart-limits.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── messages.ts
│ │ │ ├── package.json
│ │ │ ├── parsers/
│ │ │ │ ├── busboy.ts
│ │ │ │ ├── fastify-busboy.ts
│ │ │ │ ├── multipart-parser.ts
│ │ │ │ └── multipasta.ts
│ │ │ ├── runner.ts
│ │ │ └── utils.ts
│ │ ├── demos/
│ │ │ ├── bun/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── server.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── cf-workers/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── tsconfig.json
│ │ │ │ ├── worker-configuration.d.ts
│ │ │ │ └── wrangler.toml
│ │ │ ├── deno/
│ │ │ │ ├── README.md
│ │ │ │ ├── main.ts
│ │ │ │ └── package.json
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── buffer-search.test.ts
│ │ │ │ ├── buffer-search.ts
│ │ │ │ ├── multipart-request.test.ts
│ │ │ │ ├── multipart-request.ts
│ │ │ │ ├── multipart.node.test.ts
│ │ │ │ ├── multipart.node.ts
│ │ │ │ ├── multipart.test.ts
│ │ │ │ ├── multipart.ts
│ │ │ │ └── read-stream.ts
│ │ │ └── node.ts
│ │ ├── test/
│ │ │ ├── utils.node.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── node-fetch-server/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── package.json
│ │ │ ├── runner.sh
│ │ │ └── servers/
│ │ │ ├── express.ts
│ │ │ ├── node-fetch-server.ts
│ │ │ └── node-http.ts
│ │ ├── demos/
│ │ │ └── http2/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.crt
│ │ │ ├── server.csr
│ │ │ ├── server.js
│ │ │ ├── server.key
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fetch-handler.ts
│ │ │ ├── read-stream.ts
│ │ │ ├── request-listener.test.ts
│ │ │ └── request-listener.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── remix/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── config.json
│ │ │ ├── minor.remix.add-cors-middleware-export.md
│ │ │ ├── minor.remix.component-exports.md
│ │ │ ├── minor.remix.update-exports.md
│ │ │ └── minor.request-protection-middlewares.md
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── async-context-middleware.ts
│ │ │ ├── component/
│ │ │ │ ├── jsx-dev-runtime.ts
│ │ │ │ ├── jsx-runtime.ts
│ │ │ │ └── server.ts
│ │ │ ├── component.ts
│ │ │ ├── compression-middleware.ts
│ │ │ ├── cookie.ts
│ │ │ ├── cop-middleware.ts
│ │ │ ├── cors-middleware.ts
│ │ │ ├── csrf-middleware.ts
│ │ │ ├── data-schema/
│ │ │ │ ├── checks.ts
│ │ │ │ ├── coerce.ts
│ │ │ │ ├── form-data.ts
│ │ │ │ └── lazy.ts
│ │ │ ├── data-schema.ts
│ │ │ ├── data-table/
│ │ │ │ ├── migrations/
│ │ │ │ │ └── node.ts
│ │ │ │ ├── migrations.ts
│ │ │ │ ├── operators.ts
│ │ │ │ └── sql-helpers.ts
│ │ │ ├── data-table-mysql.ts
│ │ │ ├── data-table-postgres.ts
│ │ │ ├── data-table-sqlite.ts
│ │ │ ├── data-table.ts
│ │ │ ├── fetch-proxy.ts
│ │ │ ├── fetch-router/
│ │ │ │ └── routes.ts
│ │ │ ├── fetch-router.ts
│ │ │ ├── file-storage/
│ │ │ │ ├── fs.ts
│ │ │ │ └── memory.ts
│ │ │ ├── file-storage-s3.ts
│ │ │ ├── file-storage.ts
│ │ │ ├── form-data-middleware.ts
│ │ │ ├── form-data-parser.ts
│ │ │ ├── fs.ts
│ │ │ ├── headers.ts
│ │ │ ├── html-template.ts
│ │ │ ├── lazy-file.ts
│ │ │ ├── logger-middleware.ts
│ │ │ ├── method-override-middleware.ts
│ │ │ ├── mime.ts
│ │ │ ├── multipart-parser/
│ │ │ │ └── node.ts
│ │ │ ├── multipart-parser.ts
│ │ │ ├── node-fetch-server.ts
│ │ │ ├── response/
│ │ │ │ ├── compress.ts
│ │ │ │ ├── file.ts
│ │ │ │ ├── html.ts
│ │ │ │ └── redirect.ts
│ │ │ ├── route-pattern/
│ │ │ │ └── specificity.ts
│ │ │ ├── route-pattern.ts
│ │ │ ├── session/
│ │ │ │ ├── cookie-storage.ts
│ │ │ │ ├── fs-storage.ts
│ │ │ │ └── memory-storage.ts
│ │ │ ├── session-middleware.ts
│ │ │ ├── session-storage-memcache.ts
│ │ │ ├── session-storage-redis.ts
│ │ │ ├── session.ts
│ │ │ ├── static-middleware.ts
│ │ │ └── tar-parser.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── response/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── compress.ts
│ │ │ ├── file.ts
│ │ │ ├── html.ts
│ │ │ ├── lib/
│ │ │ │ ├── compress.test.ts
│ │ │ │ ├── compress.ts
│ │ │ │ ├── file.test.ts
│ │ │ │ ├── file.ts
│ │ │ │ ├── html.test.ts
│ │ │ │ ├── html.ts
│ │ │ │ ├── redirect.test.ts
│ │ │ │ └── redirect.ts
│ │ │ └── redirect.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── route-pattern/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.readonly-ast.md
│ │ │ └── patch.type-inference-perf.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── patterns/
│ │ │ │ ├── mediarss.ts
│ │ │ │ └── shopify.ts
│ │ │ ├── src/
│ │ │ │ ├── comparison.bench.ts
│ │ │ │ ├── href.bench.ts
│ │ │ │ ├── pathological.bench.ts
│ │ │ │ ├── shopify.bench.ts
│ │ │ │ └── simple.bench.ts
│ │ │ └── types/
│ │ │ ├── href.ts
│ │ │ ├── join.ts
│ │ │ ├── match.ts
│ │ │ ├── new.ts
│ │ │ └── params.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── array-matcher.ts
│ │ │ │ ├── matcher.test.ts
│ │ │ │ ├── matcher.ts
│ │ │ │ ├── regexp.ts
│ │ │ │ ├── route-pattern/
│ │ │ │ │ ├── AGENTS.md
│ │ │ │ │ ├── href.test.ts
│ │ │ │ │ ├── href.ts
│ │ │ │ │ ├── join.ts
│ │ │ │ │ ├── match.ts
│ │ │ │ │ ├── params.test.ts
│ │ │ │ │ ├── params.ts
│ │ │ │ │ ├── parse.test.ts
│ │ │ │ │ ├── parse.ts
│ │ │ │ │ ├── part-pattern.test.ts
│ │ │ │ │ ├── part-pattern.ts
│ │ │ │ │ ├── serialize.ts
│ │ │ │ │ ├── split.test.ts
│ │ │ │ │ └── split.ts
│ │ │ │ ├── route-pattern.test.ts
│ │ │ │ ├── route-pattern.ts
│ │ │ │ ├── specificity.test.ts
│ │ │ │ ├── specificity.ts
│ │ │ │ ├── trie-matcher/
│ │ │ │ │ ├── variant.test.ts
│ │ │ │ │ └── variant.ts
│ │ │ │ ├── trie-matcher.ts
│ │ │ │ ├── types/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── join.test.ts
│ │ │ │ │ ├── join.ts
│ │ │ │ │ ├── parse.test.ts
│ │ │ │ │ ├── parse.ts
│ │ │ │ │ ├── split.test.ts
│ │ │ │ │ ├── split.ts
│ │ │ │ │ ├── stringify.test.ts
│ │ │ │ │ ├── stringify.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ └── unreachable.ts
│ │ │ └── specificity.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── cookie-storage.ts
│ │ │ ├── fs-storage.ts
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── session-storage/
│ │ │ │ │ ├── cookie.test.ts
│ │ │ │ │ ├── cookie.ts
│ │ │ │ │ ├── fs.test.ts
│ │ │ │ │ ├── fs.ts
│ │ │ │ │ ├── memory.test.ts
│ │ │ │ │ └── memory.ts
│ │ │ │ ├── session-storage.ts
│ │ │ │ ├── session.test.ts
│ │ │ │ └── session.ts
│ │ │ └── memory-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.session-context-key.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── session.test.ts
│ │ │ └── session.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-storage-memcache/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── memcache-client.test.ts
│ │ │ ├── memcache-client.ts
│ │ │ ├── memcache-storage.integration.test.ts
│ │ │ ├── memcache-storage.test.ts
│ │ │ └── memcache-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-storage-redis/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── redis-storage.integration.test.ts
│ │ │ ├── redis-storage.test.ts
│ │ │ └── redis-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── static-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ └── list-files/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── directory-listing.ts
│ │ │ ├── static.test.ts
│ │ │ └── static.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── tar-parser/
│ ├── .changes/
│ │ └── README.md
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── bench/
│ │ ├── package.json
│ │ ├── parsers/
│ │ │ ├── node-tar.ts
│ │ │ ├── tar-parser.ts
│ │ │ └── tar-stream.ts
│ │ └── runner.ts
│ ├── package.json
│ ├── src/
│ │ ├── globals.ts
│ │ ├── index.ts
│ │ └── lib/
│ │ ├── read-stream.ts
│ │ ├── tar.test.ts
│ │ ├── tar.ts
│ │ └── utils.ts
│ ├── test/
│ │ ├── fixtures/
│ │ │ ├── express-4.21.1.tgz
│ │ │ ├── lodash-4.17.21.tgz
│ │ │ └── npm-11.0.0.tgz
│ │ └── utils.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── pnpm-workspace.yaml
└── scripts/
├── changes-preview.ts
├── changes-validate.ts
├── changes-version.ts
├── detect-changed-packages.ts
├── generate-remix.ts
├── package.json
├── pr-preview.ts
├── publish.ts
├── release-pr.ts
├── setup-installable-branch.ts
├── tsconfig.json
└── utils/
├── changes.test.ts
├── changes.ts
├── color.ts
├── fs.ts
├── git.test.ts
├── git.ts
├── github.ts
├── packages.ts
├── process.ts
├── release-pr.test.ts
├── release-pr.ts
└── semver.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .agents/skills/add-package/SKILL.md
================================================
---
name: add-package
description: Create or align a package in the Remix monorepo to match existing package conventions. Use when adding a brand new package under packages/, or when fixing an existing package's structure, test setup, TypeScript/build config, code style, and README layout to match the rest of Remix 3.
---
# Add Package
## Overview
Use this skill to scaffold and standardize packages so they look and behave like the existing `@remix-run/*` packages.
Follow this exactly when creating package files, public exports, tests, and docs.
## Workflow
1. Create the package directory and baseline files.
- Create `packages/<package-name>/`.
- Add:
- `package.json`
- `tsconfig.json`
- `tsconfig.build.json`
- `CHANGELOG.md`
- `README.md`
- `LICENSE`
- `.changes/README.md`
- `src/`
- For new packages, start `CHANGELOG.md` with `## Unreleased` as the first section to indicate changes are not released yet.
2. Set up `package.json` using monorepo conventions.
- Use:
- `name`: `@remix-run/<package-name>`
- `version` (for brand-new packages): `"0.0.0"`
- `type`: `"module"`
- `license`: `"MIT"`
- `repository.directory`: `packages/<package-name>`
- `homepage`: `https://github.com/remix-run/remix/tree/main/packages/<package-name>#readme`
- Include `files`:
- `LICENSE`
- `README.md`
- `dist`
- `src`
- `!src/**/*.test.ts`
- Add standard scripts:
- `build`: `tsgo -p tsconfig.build.json`
- `clean`: `git clean -fdX`
- `prepublishOnly`: `pnpm run build`
- `test`: `node --disable-warning=ExperimentalWarning --test`
- `typecheck`: `tsgo --noEmit`
- Use baseline dev dependencies:
- `"@types/node": "catalog:"`
- `"@typescript/native-preview": "catalog:"`
- Add `keywords` like existing packages (short, lowercase, feature-focused).
3. Define exports with `src` entry files only.
- In `exports`, map each public subpath to a dedicated file in `src`.
- Always include `./package.json`.
- Mirror each export in `publishConfig.exports` with `dist` output:
- `types`: `./dist/<entry>.d.ts`
- `default`: `./dist/<entry>.js`
- Rule: every export must have a `src` file that re-exports from `src/lib`.
- Example: export `./foo` -> `src/foo.ts` -> `export { ... } from './lib/foo.ts'`
4. Add TypeScript config files with shared defaults.
Use this `tsconfig.json` pattern:
```json
{
"compilerOptions": {
"strict": true,
"lib": ["ES2024", "DOM", "DOM.Iterable"],
"module": "ES2022",
"moduleResolution": "Bundler",
"target": "ESNext",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"verbatimModuleSyntax": true
}
}
```
Use this `tsconfig.build.json` pattern:
```json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"outDir": "./dist"
},
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}
```
5. Implement source structure and test setup.
- Structure source as:
- `src/<entry>.ts` for public entry points
- `src/lib/*.ts` for implementation
- `src/lib/*.test.ts` for tests (colocated with implementation)
- Tests use Node's built-in test runner:
- `import * as assert from 'node:assert/strict'`
- `import { describe, it } from 'node:test'`
- Keep tests IDE-friendly:
- Do not generate tests with loops/conditionals inside `describe()`.
6. Follow monorepo code style rules while implementing.
- Use `import type { ... }` and `export type { ... }` for types.
- Include `.ts` extensions in relative imports.
- Prefer `let` for locals; use `const` only at module scope.
- Never use `var`.
- Prefer function declarations/expressions for normal functions.
- Use arrow functions for callbacks; use concise callbacks when returning a single expression.
- Use object method shorthand (`method() {}`) instead of arrow properties.
- Use native class fields and `#private` members.
- Avoid Node-specific APIs when Web APIs are available.
7. Write README in the same style and section order as existing packages.
- Start with:
- `# <package-name>`
- One short paragraph describing purpose.
- Typical section order:
- `## Features`
- `## Installation`
- `## Usage`
- Optional deep-dive sections (only if needed)
- `## Related Packages` (if applicable)
- `## License`
- Installation instructions must always include installing the `remix` package.
- If using the package requires a peer dependency, installation instructions must also include that peer dependency in the command.
- Preferred installation pattern:
```sh
npm i remix
```
- Example when a peer dependency is required:
```sh
npm i remix <peer-dependency>
```
- Usage examples must always import from `remix` package exports, not from `@remix-run/<package-name>` directly.
- License section format:
- `See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)`
8. Do not manually update the generated `remix` package in PRs.
- `packages/remix` is generated automatically in CI.
- Do not manually edit `packages/remix/package.json` or `packages/remix/src/*` in new pull requests.
- Do not add `packages/remix/.changes/*` change files in new pull requests.
- If user asks for full surfacing, you can still update root `README.md` package list when applicable.
9. Validate before finishing.
- Run package checks:
- `pnpm --filter @remix-run/<package-name> run typecheck`
- `pnpm --filter @remix-run/<package-name> run test`
- `pnpm --filter @remix-run/<package-name> run build`
- Run repo lint (required):
- `pnpm run lint`
- Add or update a change file under `packages/<package-name>/.changes/` when requested by contribution workflow.
- For a brand-new package, the initial change file should use a `minor.` filename (for example, `minor.initial-release.md`) so the first release bumps `0.0.0` to `0.1.0`.
- Exception: do not add a change file under `packages/remix/.changes/`; `remix` package updates are CI-generated.
## Templates
Use this minimal `src/index.ts` style:
```ts
export { createThing, type ThingOptions } from './lib/thing.ts'
```
Use this minimal test style:
```ts
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { createThing } from './thing.ts'
describe('createThing', () => {
it('returns expected value', () => {
let result = createThing()
assert.equal(result, 'ok')
})
})
```
================================================
FILE: .agents/skills/add-package/agents/openai.yaml
================================================
interface:
display_name: 'Add Package'
short_description: 'Create new Remix monorepo packages consistently'
default_prompt: 'Use $add-package to scaffold and wire a new package in the Remix monorepo with the standard layout, scripts, tests, and README structure.'
================================================
FILE: .agents/skills/make-change-file/SKILL.md
================================================
---
name: make-change-file
description: Create or update Remix repo change files under `packages/*/.changes`. Use when a user asks for release notes, a change file, a missing changelog entry, a prerelease note, or an update to existing unpublished release notes.
---
# Make Change File
## Overview
Write release notes that match this repository's `.changes` conventions. Use it for both new change files and edits to existing unpublished ones.
## Workflow
1. Read the package's `package.json`, `.changes/` directory, and any relevant PR diff or commit range before writing anything.
2. Check whether an unpublished change file already exists for the same work. If it does, update it in place instead of creating a duplicate note.
3. Choose the bump type from the package version and the user-facing impact.
4. Write concise, user-facing release notes that describe shipped behavior, APIs, or exports.
5. Run `pnpm changes:preview` to verify the rendered changelog output.
6. Run `pnpm run lint` before finishing.
## Naming
- Use `packages/<package>/.changes/[major|minor|patch].short-description.md`.
- Keep the slug short, specific, and stable.
- Reuse existing deterministic names when the repo already has a pattern for that class of note.
- For Remix export-only changes, update `packages/remix/.changes/minor.remix.update-exports.md` in place.
- For brand-new package releases, prefer `minor.initial-release.md`.
## Bump Rules
- For `0.x` packages: use `minor` for new features and breaking changes, `patch` for bug fixes.
- Do not use `major` for `0.x` packages unless explicitly instructed.
- For `1.x+` packages: use standard semver.
- Breaking changes are relative to `main`, not relative to earlier commits in the same PR.
- In `0.x`, breaking change notes must start with `BREAKING CHANGE: `.
- For `remix` prerelease mode, the bump type mostly controls changelog categorization while the prerelease counter advances.
## Content Rules
- Document user-visible behavior, public API changes, exports, migrations, or upgrade work.
- Do not write release notes for internal refactors unless they surface as real API or behavior changes.
- Prefer a small number of logically grouped notes over many tiny files.
- If one package changes internally and another package re-exports the new surface, add notes for both when users can consume the change from both package entrypoints.
- Do not manually hard-wrap prose in `.changes/*.md` files. Keep each paragraph or bullet on a single source line and let rendered changelogs wrap naturally.
- Use flat bullets only when they add clarity. Short paragraphs are usually better.
## Remix-Specific Rules
- `packages/remix/src/*` re-export files are generated. Do not hand-edit them unless the task explicitly requires generated output.
- When `packages/remix/package.json` gains or changes public exports, capture that in `minor.remix.update-exports.md` instead of inventing a one-off filename.
- If the change exposes another package's new APIs through `remix/...`, describe the surfaced `remix/...` entrypoints, not just the underlying workspace package name.
## Checklist
- Did you inspect existing unpublished `.changes` files first?
- Is the bump type correct for the package version?
- Did you reuse any deterministic filename the repo already expects?
- Does the note describe user-facing changes instead of implementation details?
- Does each paragraph or bullet stay on one source line without manual hard wrapping?
- Did `pnpm changes:preview` render the expected changelog entry?
================================================
FILE: .agents/skills/make-change-file/agents/openai.yaml
================================================
interface:
display_name: 'Make Change File'
short_description: 'Create or update Remix change files using repo conventions.'
default_prompt: 'Use $make-change-file to add or revise package change files for this Remix repo.'
================================================
FILE: .agents/skills/make-demo/SKILL.md
================================================
---
name: make-demo
description: Create or revise demos in the Remix repository. Use when adding a new demo under demos/, updating an existing demo, or reviewing demo code to ensure it showcases Remix packages, strong code hygiene, and production-quality patterns.
---
# Make Demo
## Overview
Demos in this repository are not throwaway prototypes. They are durable code artifacts that should teach people and other agents how to write Remix code well.
A good demo should:
- exercise Remix framework behavior in a realistic way
- push the target APIs through meaningful edge cases and composition points
- model clean structure, naming, and accessibility
- be code that a reader could adapt into a real application
## Workflow
1. Read the target APIs and at least one or two existing demos before writing new code.
2. Choose a focused scenario that exists to demonstrate Remix behavior, not a generic app shell.
3. Build the demo under `demos/<name>/` using the same conventions as the existing demos.
4. Treat the code as a reference artifact, not as temporary sample code.
5. Validate the demo locally before finishing.
## Rules
- Use Remix library packages for the demo's framework behavior. Do not introduce unrelated routers, component frameworks, state managers, or middleware stacks that distract from the Remix patterns being demonstrated.
- Keep any non-Remix dependency incidental to the runtime environment only. If a database driver, asset bundler, or type package is needed, it should support the demo rather than define its architecture.
- Demos should push Remix to its limits in a focused way. Prefer realistic edge cases, composition, streaming, middleware, routing, navigation, forms, or request-handling scenarios over toy examples.
- When demos use `remix/component`, prefer idiomatic Remix component patterns. Use normal JSX composition and built-in styling/mixin props such as `css={...}` or `mix={css(...)}`
and `mix={[...]}` instead of dropping down to manual DOM mutation or ad hoc class management.
- Demo code must have good hygiene. Use clear names, small focused modules, explicit control flow, and accessible markup. Avoid hacks, dead code, unexplained shortcuts, or patterns that would be poor examples for users to copy.
- Make the demo teach good patterns. Assume readers and future agents will study it as an example of how Remix code should be written in this repository.
- All demo servers should use port `44100`.
- Demo servers should handle `SIGINT` and `SIGTERM` cleanly by closing the server and exiting.
## Typical Structure
Use only the files the scenario needs, but prefer this shape:
- `demos/<name>/package.json`
- `demos/<name>/server.ts`
- `demos/<name>/README.md`
- `demos/<name>/app/`
- `demos/<name>/public/` when serving built assets or other static files
## README Expectations
- Explain what the demo proves or teaches.
- Document how to run it locally.
- Point out the key Remix APIs or patterns being demonstrated.
- Keep code examples and imports aligned with repo guidance: use `remix` package exports where available.
## Validation
- Run `pnpm -C demos/<name> typecheck` when the demo defines a typecheck script.
- Run `pnpm -C demos/<name> test` when the demo defines tests.
- Smoke-test the demo server locally when behavior depends on live requests or browser interaction.
- Run `pnpm run lint` before finishing.
================================================
FILE: .agents/skills/make-demo/agents/openai.yaml
================================================
interface:
display_name: 'Make Demo'
short_description: 'Create high-quality Remix demos'
default_prompt: 'Use $make-demo to add or revise a demo under demos/ that showcases Remix packages with clean, artifact-quality code and strong repo conventions.'
================================================
FILE: .agents/skills/make-pr/SKILL.md
================================================
---
name: make-pr
description: Create GitHub pull requests with clear, reviewer-friendly descriptions. Use when asked to open or prepare a PR, especially when the PR needs strong context, related links, and feature usage examples. This skill enforces concise PR structure, avoids redundant sections like validation/testing, and creates the PR with gh CLI.
---
# Make PR
## Overview
Use this skill to draft and open a PR with consistent, high-signal writing.
Keep headings sparse and focus on the problem/feature explanation, context links, and practical code examples.
Optimize for the shortest path to a credible PR, not the fullest possible context-gathering pass.
## Workflow
1. Check the fast-path blockers first.
- Check `git status --short --branch` and `git branch --show-current` before doing deeper prep.
- If the repo is in a detached HEAD or worktree state and the user wants a PR opened, create a branch early.
1. Gather only the context needed to write the PR.
- Capture what changed, why it changed, and who it affects.
- Find related issues/PRs and include links when relevant.
- Prefer `git diff --stat` plus the relevant diff over broad repo archaeology when the change is small.
- If the user supplies a report, issue, or related PR, treat that as the primary context source.
1. Get the branch into a PR-ready state quickly.
- If changes are still uncommitted and the user wants a PR, branch first, then commit.
- Prefer a single clean commit unless the user asks for a different history shape.
1. Check whether this PR also needs a change file.
- Do not assume every PR needs one.
- Before opening the PR, decide whether the change is user-facing enough to require release notes in `packages/*/.changes`.
- If a change file is needed or likely needed, use the `make-change-file` skill instead of re-deriving that workflow here.
1. Draft the PR body with minimal structure.
- Start with 1-2 short introductory paragraphs.
- After the intro, include clear bullets describing:
- the feature and/or issue addressed
- key behavior/API changes
- expected impact
- If the change is extensive, expand to up to 3-4 paragraphs and include background context with related links.
1. Add required usage examples for feature work.
- If the PR introduces a new feature, include a comprehensive usage snippet.
- If it replaces or improves an older approach, include before/after examples.
1. Exclude redundant sections.
- Do not include `Validation`, `Testing`, or other process sections that are already implicit in PR workflow.
- Do not add boilerplate sections that do not help review.
1. Create the PR.
- Save the body to a temporary file and run:
```bash
gh pr create --base main --head <branch> --title "<title>" --body-file <file>
```
- If `gh pr create` fails, leave the branch pushed when possible and give the user a ready-to-open compare URL plus the prepared title/body details.
## Body Template
Use this as a base and fill with concrete repo-specific details:
````md
<One or two short intro paragraphs explaining the change and why it matters.>
- <Feature/issue addressed>
- <What changed in behavior or API>
- <Why this is needed now>
<Optional additional context paragraph(s), up to 3-4 total for large changes, including links to related PRs/issues.>
```ts
// New feature usage example
```
```ts
// Before
```
```ts
// After
```
````
================================================
FILE: .agents/skills/make-pr/agents/openai.yaml
================================================
interface:
display_name: 'Make PR'
short_description: 'Create high-quality GitHub pull requests'
default_prompt: 'Use $make-pr to draft and create a GitHub pull request with clear context and examples.'
================================================
FILE: .agents/skills/publish-placeholder-package/SKILL.md
================================================
---
name: publish-placeholder-package
description: Publish a placeholder npm package at version 0.0.0 so package names are reserved and npm OIDC permissions can be configured before CI publishing. Use when creating a brand-new package that is not ready for full release.
---
# Publish Placeholder Package
## Overview
Use this skill to publish a minimal placeholder package to npm at `0.0.0`.
This is used to reserve the package name and unblock npm-side OIDC configuration for CI publishing.
## Workflow
1. Confirm publish target.
- Collect:
- npm package name (for example, `@remix-run/my-package`)
- package directory in repo (for example, `packages/my-package`)
- Validate the package does not already exist at `0.0.0`:
```sh
npm view <package-name>@0.0.0 version
```
- If it already exists, stop and report that no placeholder publish is needed.
2. Build a temporary placeholder package outside the repo.
- Always publish from a temp directory to avoid shipping real package files by mistake.
- Create the temp directory and write a minimal `package.json`:
```sh
tmp_dir="$(mktemp -d)"
cd "$tmp_dir"
cat > package.json <<'JSON'
{
"name": "<package-name>",
"version": "0.0.0",
"description": "Placeholder package for Remix CI/OIDC setup",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/remix-run/remix.git",
"directory": "<repo-package-dir>"
},
"publishConfig": {
"access": "public"
}
}
JSON
```
- Add a short README:
```sh
cat > README.md <<'MD'
# Placeholder Package
This package is a placeholder published at `0.0.0` to reserve the npm name and configure CI publish permissions.
MD
```
3. Ensure npm auth is valid (expect re-auth/OTP).
- Check session:
```sh
npm whoami
```
- If not authenticated, run:
```sh
npm login
```
- Expect npm to require a fresh login and/or one-time password. If prompted for OTP, request it from the user and continue.
4. Publish the placeholder.
- Publish with public access:
```sh
npm publish --access public
```
- If the account enforces 2FA for writes, publish with OTP:
```sh
npm publish --access public --otp <code>
```
5. Verify and report.
- Verify the published version:
```sh
npm view <package-name>@0.0.0 version
```
- Report:
- package name
- published version (`0.0.0`)
- confirmation that npm package exists for OIDC permission setup
6. Clean up temp files.
```sh
rm -rf "$tmp_dir"
```
## Notes
- Keep placeholder publish minimal. Do not publish full source code for this step.
- This is a one-time bootstrap step. Normal releases should continue through CI.
================================================
FILE: .agents/skills/publish-placeholder-package/agents/openai.yaml
================================================
interface:
display_name: 'Publish Placeholder Package'
short_description: 'Publish npm placeholder package at 0.0.0'
default_prompt: 'Use $publish-placeholder-package to publish a minimal npm placeholder package at 0.0.0 so we can configure npm OIDC permissions for CI publishing.'
================================================
FILE: .agents/skills/supersede-pr/SKILL.md
================================================
---
name: supersede-pr
description: Safely replace one GitHub pull request with another. Use when a user says a PR supersedes/replaces an older PR, asks to auto-close a superseded PR, or needs guaranteed closure behavior after merge. This skill explicitly closes the superseded PR with gh CLI and verifies final PR states instead of relying on closing keywords.
---
# Supersede PR
## Overview
Use this skill to handle PR supersession end-to-end.
Do not rely on `Closes #<number>` to close another PR. GitHub closing keywords close issues, not pull requests.
## Workflow
1. Identify PR numbers and target repo.
- Capture `old_pr` (the superseded PR) and `new_pr` (the replacement PR).
- Resolve the repo with `gh repo view --json nameWithOwner -q .nameWithOwner` when not provided.
1. Create or update the replacement PR first.
- Open/push the replacement branch.
- Open the new PR.
- Include a traceable link in the PR body such as `Supersedes #<old_pr>`.
1. Close the superseded PR explicitly.
- Run:
```bash
scripts/close_superseded_pr.ts <old_pr> <new_pr>
```
- This adds a comment (`Superseded by #<new_pr>.`) and closes the old PR.
1. Verify states.
- Confirm the superseded PR is closed:
```bash
gh pr view <old_pr> --json state,url
```
- Confirm the replacement PR status/checks:
```bash
gh pr checks <new_pr>
```
## Rules
1. Do not use `Closes #<old_pr>` when `<old_pr>` is a pull request.
- Use `Closes/Fixes` only for issues.
- Use `Supersedes #<old_pr>` or `Refs #<old_pr>` for PR-to-PR linkage.
1. Prefer explicit closure over implied automation.
- Always run the close command when the user asks to supersede a PR.
- Treat closure as incomplete until `gh pr view <old_pr>` returns `CLOSED`.
## Script
Use the bundled script for deterministic closure:
- `scripts/close_superseded_pr.ts`
================================================
FILE: .agents/skills/supersede-pr/agents/openai.yaml
================================================
interface:
display_name: 'Supersede PR'
short_description: 'Close superseded pull requests safely'
default_prompt: 'Use $supersede-pr to replace a PR and close the superseded PR safely.'
================================================
FILE: .agents/skills/supersede-pr/scripts/close_superseded_pr.ts
================================================
#!/usr/bin/env node
import { spawnSync } from 'node:child_process'
import * as process from 'node:process'
type ParsedArgs = {
dryRun: boolean
newPr: string
oldPr: string
repo: string | null
}
function main(): void {
let parsed = parseArgs(process.argv.slice(2))
ensureNumericPrNumber(parsed.oldPr, 'old_pr')
ensureNumericPrNumber(parsed.newPr, 'new_pr')
if (parsed.oldPr === parsed.newPr) {
fail('old_pr and new_pr must be different.')
}
let repo =
parsed.repo ?? ghCapture(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner'])
let oldState = ghCapture([
'pr',
'view',
parsed.oldPr,
'--repo',
repo,
'--json',
'state',
'-q',
'.state',
])
let newState = ghCapture([
'pr',
'view',
parsed.newPr,
'--repo',
repo,
'--json',
'state',
'-q',
'.state',
])
if (newState !== 'OPEN' && newState !== 'MERGED') {
fail(`Replacement PR #${parsed.newPr} is in state '${newState}'. Expected OPEN or MERGED.`)
}
if (oldState !== 'OPEN') {
process.stdout.write(`Superseded PR #${parsed.oldPr} is already ${oldState}. Nothing to do.\n`)
return
}
let comment = `Superseded by #${parsed.newPr}.`
process.stdout.write(`Repo: ${repo}\n`)
process.stdout.write(`Closing PR #${parsed.oldPr} with comment: ${comment}\n`)
if (parsed.dryRun) {
process.stdout.write(
`[dry-run] gh pr close "${parsed.oldPr}" --repo "${repo}" --comment "${comment}"\n`,
)
return
}
ghInherit(['pr', 'close', parsed.oldPr, '--repo', repo, '--comment', comment])
let finalState = ghCapture([
'pr',
'view',
parsed.oldPr,
'--repo',
repo,
'--json',
'state',
'-q',
'.state',
])
if (finalState !== 'CLOSED') {
fail(`Failed to close PR #${parsed.oldPr}. Final state: ${finalState}`)
}
process.stdout.write(`Closed PR #${parsed.oldPr} successfully.\n`)
}
function parseArgs(argv: string[]): ParsedArgs {
if (argv.includes('-h') || argv.includes('--help')) {
printUsage()
process.exit(0)
}
if (argv.length < 2) {
printUsage()
process.exit(1)
}
let oldPr = argv[0]
let newPr = argv[1]
let repo: string | null = null
let dryRun = false
let index = 2
while (index < argv.length) {
let arg = argv[index]
if (arg === '--repo') {
let next = argv[index + 1]
if (!next) {
fail('--repo requires a value like owner/repo')
}
repo = next
index += 2
continue
}
if (arg === '--dry-run') {
dryRun = true
index++
continue
}
fail(`Unknown argument: ${arg}`)
}
return { dryRun, newPr, oldPr, repo }
}
function printUsage(): void {
process.stdout.write(`Usage:
close_superseded_pr.ts <old_pr> <new_pr> [--repo <owner/repo>] [--dry-run]
Examples:
close_superseded_pr.ts 11085 11087
close_superseded_pr.ts 11085 11087 --repo remix-run/remix
close_superseded_pr.ts 11085 11087 --dry-run
`)
}
function ensureNumericPrNumber(value: string, label: string): void {
if (!/^[0-9]+$/.test(value)) {
fail(`${label} must be a numeric pull request number.`)
}
}
function ghCapture(args: string[]): string {
let result = spawnSync('gh', args, { encoding: 'utf8' })
if (result.status !== 0) {
let stderr = (result.stderr ?? '').trim()
let details = stderr ? `\n${stderr}` : ''
fail(`gh ${args.join(' ')} failed.${details}`)
}
return (result.stdout ?? '').trim()
}
function ghInherit(args: string[]): void {
let result = spawnSync('gh', args, { stdio: 'inherit' })
if (result.status !== 0) {
fail(`gh ${args.join(' ')} failed with exit code ${result.status ?? 'unknown'}.`)
}
}
function fail(message: string): never {
process.stderr.write(`${message}\n`)
process.exit(1)
}
main()
================================================
FILE: .agents/skills/supersede-pr/tsconfig.json
================================================
{
"compilerOptions": {
"allowJs": false,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"strict": true,
"target": "ESNext",
"verbatimModuleSyntax": true
},
"include": ["scripts/**/*.ts"]
}
================================================
FILE: .agents/skills/update-pr/SKILL.md
================================================
---
name: update-pr
description: Update an existing GitHub pull request title and description so they accurately describe the pull request as it exists now. Use when the user asks to update, rewrite, refresh, fix, or tighten a PR title/body, or when the PR scope has changed and the metadata needs to be brought back in sync.
---
# Update PR
## Overview
Rewrite pull request metadata as if drafting it from scratch for the current diff. Treat the title and body as a current reviewer-facing summary of the PR, not as commentary about prior versions of the PR.
## Workflow
1. Read the current PR title/body and the current branch diff before drafting.
2. Identify the PR's current scope, APIs, behavior changes, and reviewer-relevant context.
3. Rewrite the body from scratch so it describes the PR as it exists now.
4. Review the title at the same time and update it whenever the body is updated.
5. Apply the update with `gh pr edit`.
## Rules
- Never write the description as an update to itself. Do not say things like "this expands the original PR", "this PR now also", or similar process narration unless the user explicitly wants history called out.
- Always evaluate the title when updating a PR. If the scope or emphasis changed, rewrite the title too.
- Write in terms of the present PR contents, using concise reviewer-facing language.
- Keep the structure minimal: one short introductory paragraph plus flat bullets is usually enough.
- Include usage examples when the PR introduces or materially changes a feature API.
- Preserve still-relevant issue links or context, but drop stale framing.
## Applying The Update
- Draft the new title and body in a temporary file.
- Use `gh pr edit <number> --title "<title>" --body-file <file>`.
- Re-read the PR after editing to confirm the final title/body match the intended framing.
================================================
FILE: .agents/skills/update-pr/agents/openai.yaml
================================================
interface:
display_name: 'Update PR'
short_description: 'Refresh an existing pull request title and body.'
default_prompt: 'Use $update-pr to rewrite this pull request title and description so they accurately describe the PR as it exists now.'
================================================
FILE: .agents/skills/write-api-docs/SKILL.md
================================================
---
name: write-api-docs
description: Write or audit public API docs for Remix packages. Use when adding or tightening JSDoc on exported functions, classes, interfaces, type aliases, or option objects.
---
# Write API Docs
## Overview
Use this skill when documenting public APIs in Remix packages.
The goal is to document the API users can actually import, not every helper in `src/lib`.
Work from the package exports outward, add concise JSDoc to the public declarations, and make sure the result passes the repo's ESLint JSDoc rules.
## Workflow
1. Identify the package's public exports.
2. Find the `src` entry files that back those exports.
3. Trace those entry files to the declarations they re-export from `src/lib`.
4. Add or tighten JSDoc on the public declarations only.
5. Run package typecheck if appropriate and always run `pnpm run lint`.
## How To Identify Public API
The source of truth is the package's `package.json`.
- Start with `package.json` `exports`.
- Each public export should map to a file directly under `src/`.
- Those `src/*.ts` entry files define the public surface by re-exporting symbols from `src/lib`.
- A declaration in `src/lib` is public only if it is re-exported by one of those public `src/*.ts` entry files.
Rules:
- Do not assume everything in `src/lib` is public.
- Do not document private helpers just because they are exported within `src/lib`.
- If a declaration is not reachable from a package export, it is internal unless the user explicitly asks otherwise.
## What To Document
For public API, add JSDoc to:
- exported functions
- exported classes
- exported interfaces
- exported type aliases
- exported public constants when they are part of the API shape
For public interfaces:
- add a JSDoc block on the interface itself
- add a property-level JSDoc block for every property on the interface, even when the name seems obvious
For public object-shaped type aliases:
- prefer an `interface` when you are introducing a new public object shape
- if an existing public type alias cannot reasonably become an interface, document the object shape as thoroughly as the syntax allows
For overloads:
- document the public overload signatures or the exported declaration in a way that makes the callable surface clear to users
## JSDoc Style For This Repo
Keep comments short, factual, and user-facing.
- Describe what the API does, not how the implementation works internally.
- Prefer one concise summary sentence, then short `@param` / `@returns` docs as needed.
- Do not put TypeScript types in JSDoc tags. ESLint forbids JSDoc type syntax here because the source of truth is the TypeScript signature.
- Keep parameter names in JSDoc exactly aligned with the function signature.
- Use `@returns` for non-void functions and include a real description.
- For `@param`, include descriptions and do not add a hyphen before the description.
- Specify `@param` default values in parenthesis at the end of the comment, do not use `@default` tags
- Include an `@example` code block when it helps to show a use-case or pattern. Skip `@example` for simple getters, trivial constructors, or APIs whose usage is self-evident.
- Use `{@link API}` to link to related Remix APIs when it adds value. Don't link every related API — use discretion to avoid noise.
- Use backticks for all other unlinked code references — identifiers, HTTP methods, special values.
Good:
```ts
/**
* Creates an {@link AuthProvider} for direct credentials-based authentication.
*
* @param options Parsing and verification hooks for submitted credentials.
* @returns A provider that can be passed to `login()`.
*/
export function createCredentialsAuthProvider(...) {}
```
Avoid:
```ts
/**
* @param {CredentialsOptions} options - options
* @returns {CredentialsProvider}
*/
```
## ESLint Expectations
The relevant rules live in [`eslint.config.js`](../../eslint.config.js).
For `packages/**/*.{ts,tsx}` (excluding tests), ESLint enforces JSDoc on callable declarations such as:
- function declarations
- function expressions
- arrow functions
- class declarations
- public methods
Important enforced details:
- `jsdoc/require-param`
- `jsdoc/require-param-name`
- `jsdoc/require-param-description`
- `jsdoc/require-returns`
- `jsdoc/require-returns-description`
- `jsdoc/no-types`
- `jsdoc/check-param-names`
- `jsdoc/check-types`
- `jsdoc/check-alignment`
Practical implication:
- if a public function takes parameters, document all of them
- if a public function returns a value, document the return value
- do not use JSDoc type annotations
- keep the block formatted cleanly enough to satisfy alignment checks
## Review Checklist
- Did you start from `package.json` exports instead of guessing from `src/lib`?
- Are all documented declarations actually reachable from a public `src/*.ts` entry file?
- Do all public functions and methods have JSDoc with `@param` and `@returns` where required?
- Do public interfaces and type aliases have a concise doc block explaining what they represent?
- Does every property on every public interface have its own property-level JSDoc block?
- Did you avoid documenting internal helpers that are not exported publicly?
- Did `pnpm run lint` pass?
================================================
FILE: .agents/skills/write-readme/SKILL.md
================================================
---
name: write-readme
description: Write or rewrite package README files in the style used by the Remix repository. Use when drafting a new package README, revising an existing README, or reviewing README structure, examples, installation instructions, and section ordering for Remix packages.
---
# Write Readme
## Overview
Draft README files as concise package documentation for real users, not as marketing copy or API dumps. Mirror the structure used across this repository, keep examples production-oriented, and avoid awkward manual line breaks in prose.
## Workflow
1. Read the package API and at least one or two sibling package READMEs before drafting.
2. Document the package as it exists today, not the package you wish existed.
3. Start with a realistic production usage example as soon as the installation section is done.
4. Cover each major feature with a concrete example.
5. Finish with internal ecosystem links, external related work, and license info.
## Structure
Use this section order unless there is a strong package-specific reason not to:
1. `# short package-name` (i.e. `fetch-router` instead of `@remix-run/fetch-router`)
2. Intro: one or two sentences explaining what the package does and why it exists
3. `## Features`: a flat bullet list of the main highlights
4. `## Installation`
5. `## Usage`: a production-like example that shows the package in context
6. One section per major feature, each with focused examples
7. `## Related Packages`
8. `## Related Work`
9. `## License`
## Rules
- Installation should always start with:
```sh
npm i remix
```
- If the package requires a third-party dependency or peer, include it explicitly in the installation section after `remix`.
- Usage examples should import from `remix/...`, not `@remix-run/...`.
- The first example should look like real application code, not the smallest possible snippet.
- Feature sections should show how to use the package's major capabilities in practice, with one example per capability when useful.
- Keep prose compact. Do not hard-wrap paragraphs at awkward places in the middle of a sentence just to force a line length.
- Prefer flat bullets and short paragraphs over long explanatory blocks.
- `Related Packages` should point to relevant Remix packages in the monorepo.
- `Related Work` should point to external libraries, specs, standards, or prior art that help readers place the package.
- `License` should use the standard repo wording and link.
## Checklist
- Does the intro explain the package in one or two sentences?
- Does the features list surface the package's main value quickly?
- Does the installation section use `npm i remix`?
- Does the main usage example show a realistic production scenario?
- Does each major feature have an example?
- Does the README end with `Related Packages`, `Related Work`, and `License`?
- Does the prose read naturally without awkward manual line breaks?
================================================
FILE: .agents/skills/write-readme/agents/openai.yaml
================================================
interface:
display_name: 'Write Readme'
short_description: 'Write Remix package READMEs in the repo style.'
default_prompt: 'Use $write-readme to draft or rewrite this package README in the style used by the Remix repository.'
================================================
FILE: .github/workflows/build.yaml
================================================
name: Build
on:
push:
branches:
- main
pull_request:
branches-ignore:
- release-v2
- v2
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm build
================================================
FILE: .github/workflows/check.yaml
================================================
name: Type check, lint, validate change files
on:
push:
branches:
- main
pull_request:
branches-ignore:
- release-v2
- v2
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm lint
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm typecheck
change-files:
name: Validate change files
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
- name: Check change files
run: node ./scripts/changes-validate.ts
================================================
FILE: .github/workflows/data-table-integration.yaml
================================================
name: Data Table Integration Tests
on:
push:
branches:
- main
paths:
- '.github/workflows/data-table-integration.yaml'
- 'packages/data-table/**'
- 'packages/data-table-postgres/**'
- 'packages/data-table-mysql/**'
- 'packages/data-table-sqlite/**'
- 'pnpm-workspace.yaml'
- 'package.json'
pull_request:
paths:
- '.github/workflows/data-table-integration.yaml'
- 'packages/data-table/**'
- 'packages/data-table-postgres/**'
- 'packages/data-table-mysql/**'
- 'packages/data-table-sqlite/**'
- 'pnpm-workspace.yaml'
- 'package.json'
jobs:
unit:
name: Unit and Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run data-table package checks
run: |
pnpm --filter @remix-run/data-table run typecheck
pnpm --filter @remix-run/data-table run test
pnpm --filter @remix-run/data-table run test:coverage
pnpm --filter @remix-run/data-table run build
pnpm --filter @remix-run/data-table-postgres run typecheck
pnpm --filter @remix-run/data-table-postgres run test
pnpm --filter @remix-run/data-table-postgres run test:coverage
pnpm --filter @remix-run/data-table-postgres run build
pnpm --filter @remix-run/data-table-mysql run typecheck
pnpm --filter @remix-run/data-table-mysql run test
pnpm --filter @remix-run/data-table-mysql run test:coverage
pnpm --filter @remix-run/data-table-mysql run build
pnpm --filter @remix-run/data-table-sqlite run typecheck
pnpm --filter @remix-run/data-table-sqlite run test
pnpm --filter @remix-run/data-table-sqlite run test:coverage
pnpm --filter @remix-run/data-table-sqlite run build
postgres-integration:
name: Postgres Integration
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: remix
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U postgres"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run postgres integration tests
env:
DATA_TABLE_INTEGRATION: '1'
DATA_TABLE_POSTGRES_URL: postgres://postgres:postgres@127.0.0.1:5432/remix
run: node --disable-warning=ExperimentalWarning --test './packages/data-table-postgres/src/lib/adapter.integration.test.ts'
mysql-integration:
name: MySQL Integration
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: remix
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot"
--health-interval=10s
--health-timeout=5s
--health-retries=10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run mysql integration tests
env:
DATA_TABLE_INTEGRATION: '1'
DATA_TABLE_MYSQL_URL: mysql://root:root@127.0.0.1:3306/remix
run: node --disable-warning=ExperimentalWarning --test './packages/data-table-mysql/src/lib/adapter.integration.test.ts'
sqlite-integration:
name: SQLite Integration
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build sqlite native module
run: pnpm rebuild better-sqlite3
- name: Run sqlite adapter tests
env:
DATA_TABLE_INTEGRATION: '1'
run: node --disable-warning=ExperimentalWarning --test './packages/data-table-sqlite/src/lib/adapter.integration.test.ts'
================================================
FILE: .github/workflows/file-storage-integration.yaml
================================================
name: File Storage Integration Tests
on:
push:
branches:
- main
paths:
- '.github/workflows/file-storage-integration.yaml'
- 'packages/file-storage/**'
- 'packages/file-storage-s3/**'
- 'pnpm-workspace.yaml'
- 'package.json'
pull_request:
paths:
- '.github/workflows/file-storage-integration.yaml'
- 'packages/file-storage/**'
- 'packages/file-storage-s3/**'
- 'pnpm-workspace.yaml'
- 'package.json'
jobs:
unit:
name: Unit and Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run file-storage-s3 package checks
run: |
pnpm --filter @remix-run/file-storage-s3 run typecheck
pnpm --filter @remix-run/file-storage-s3 run test
pnpm --filter @remix-run/file-storage-s3 run build
s3-integration:
name: S3 Integration
runs-on: ubuntu-latest
services:
localstack:
image: localstack/localstack:4.4.0
env:
SERVICES: s3
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
DEFAULT_REGION: us-east-1
ports:
- 4566:4566
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Wait for LocalStack
run: |
for i in {1..60}; do
if curl -fsS http://127.0.0.1:4566/_localstack/health > /dev/null; then
exit 0
fi
sleep 1
done
echo "LocalStack did not become ready in time"
exit 1
- name: Run S3 integration tests
env:
FILE_STORAGE_S3_INTEGRATION: '1'
FILE_STORAGE_S3_ENDPOINT: http://127.0.0.1:4566
FILE_STORAGE_S3_BUCKET: remix-file-storage-integration
FILE_STORAGE_S3_REGION: us-east-1
FILE_STORAGE_S3_ACCESS_KEY_ID: test
FILE_STORAGE_S3_SECRET_ACCESS_KEY: test
FILE_STORAGE_S3_FORCE_PATH_STYLE: '1'
run: node --disable-warning=ExperimentalWarning --test './packages/file-storage-s3/src/lib/s3.integration.test.ts'
================================================
FILE: .github/workflows/format.yml
================================================
name: Format
on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
format:
if: "${{ github.repository == 'remix-run/remix' && !startsWith(github.event.head_commit.message, 'chore: format') }}"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.FORMAT_PAT }}
fetch-depth: 0
- name: Detect formatting-relevant changes
id: changes
run: |
base="${{ github.event.before }}"
if [ "$base" = "0000000000000000000000000000000000000000" ]; then
files="$(git ls-files)"
else
files="$(git diff --name-only "$base" "$GITHUB_SHA")"
fi
if [ -z "$files" ]; then
echo "No changed files detected"
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
while IFS= read -r file; do
case "$file" in
.prettierignore|.prettierrc|.prettierrc.json|.prettierrc.yml|.prettierrc.yaml|.prettierrc.js|.prettierrc.cjs|.prettierrc.mjs|*.js|*.jsx|*.cjs|*.mjs|*.ts|*.tsx|*.cts|*.mts|*.json|*.jsonc|*.md|*.mdx|*.yaml|*.yml|*.css|*.scss|*.html)
echo "Formatting-relevant change: $file"
echo "should_run=true" >> "$GITHUB_OUTPUT"
exit 0
;;
esac
done <<EOF
$files
EOF
echo "No formatting-relevant files changed"
echo "should_run=false" >> "$GITHUB_OUTPUT"
- name: Install pnpm
if: steps.changes.outputs.should_run == 'true'
uses: pnpm/action-setup@v4
- name: Install Node.js
if: steps.changes.outputs.should_run == 'true'
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
if: steps.changes.outputs.should_run == 'true'
run: pnpm install --frozen-lockfile
- name: Format
if: steps.changes.outputs.should_run == 'true'
run: pnpm format
- name: Commit
if: steps.changes.outputs.should_run == 'true'
run: |
git config --local user.email "hello@remix.run"
git config --local user.name "Remix Run Bot"
git add .
git restore .github/workflows # PAT doesn't have permission to push workflow changes
if [ -z "$(git status --porcelain)" ]; then
echo "No formatting changes"
exit 0
fi
git commit -m "chore: format"
git push
echo "Pushed formatting changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
================================================
FILE: .github/workflows/generate-remix.yaml
================================================
# Update the `remix` package by auto-generating from the `@remix-run/*` packages in the repo
# runs on any pushes/PRs to `main` that touch any `package.json` files since that would
# potentially alter the sub-exports that need to be reflected in the `remix` package.
# Note: Does not currently run on PRs from forked repos.
name: Update Remix package
on:
push:
branches:
- 'main'
paths:
- 'packages/**/package.json'
pull_request:
branches-ignore:
- release-v2
- v2
paths:
- 'packages/**/package.json'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
generate-remix:
if: |
github.repository == 'remix-run/remix' &&
(
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository
)
runs-on: ubuntu-latest
steps:
# Normal checkout of the trigger branch on pushes
- name: Checkout
if: github.event_name == 'push'
uses: actions/checkout@v4
with:
# Use a PAT because using the default `GITHUB_TOKEN`/`github.token` will not
# trigger workflow runs, so use a PAT to ensure new commits will run CI checks.
# See: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow#triggering-a-workflow-from-a-workflow
token: ${{ secrets.GH_REMIX_PAT }}
# Checkout the PR branch when running on PRs
- name: Checkout
if: github.event_name == 'pull_request'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
# Use a PAT because using the default `GITHUB_TOKEN`/`github.token` will not
# trigger workflow runs, so use a PAT to ensure new commits will run CI checks.
# See: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow#triggering-a-workflow-from-a-workflow
token: ${{ secrets.GH_REMIX_PAT }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate remix package
run: pnpm run generate-remix
- name: Commit and push changes
id: commit
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "💿 no updates to the remix package needed"
exit 0
fi
# Check for unintentional changes
OUTSIDE_CHANGES=$(git status --porcelain | awk '{print $2}' | grep -v "^packages/remix/" || true)
if [ -n "$OUTSIDE_CHANGES" ]; then
echo "Refusing to commit changes outside of packages/remix/:"
echo "$OUTSIDE_CHANGES"
exit 1
fi
# Re-install to ensure any new remix peerDependencies are reflected
pnpm install --no-frozen-lockfile
git config --local user.email "hello@remix.run"
git config --local user.name "Remix Run Bot"
git add .
git commit -a -m "build: update remix package"
git push
echo "💿 pushed updates: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
echo "new_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Comment on PR
if: github.event_name == 'pull_request' && steps.commit.outputs.new_sha != ''
env:
# `GH_TOKEN` is required to use the `gh` CLI to add comments to PRs.
GH_TOKEN: ${{ secrets.GH_REMIX_PAT }}
run: gh pr comment ${{ github.event.pull_request.number }} --body "Changes in this PR resulted in updates to the auto-generated \`remix\` package in ${{ steps.commit.outputs.new_sha }}. Please review those changes prior to merging."
================================================
FILE: .github/workflows/preview.yml
================================================
# Create "installable" preview branches
#
# Commits to `main` push builds to a `preview/main` branch:
# pnpm install "remix-run/remix#preview/main&path:packages/remix"
#
# Pull Requests create `preview/pr-{number}` branches:
# pnpm install "remix-run/remix#preview/pr-12345&path:packages/remix"
#
# Can also be dispatched manually with base/installable branches to provide
# `experimental` branches from PRs or otherwise.
name: Preview Build
on:
push:
branches:
- main
workflow_dispatch:
inputs:
baseBranch:
description: Base Branch
required: true
installableBranch:
description: Installable Branch
required: true
pull_request:
types: [opened, synchronize, reopened, closed]
concurrency:
# Include `event_name` here because when a pull_request is merged (closed), the
# `github.ref` goes back to `ref/heads/main` which will conflict with the run on
# `main` from the merged PR
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
preview:
# Don't run on PRs from forked repos
if: github.repository == 'remix-run/remix' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
runs-on: ubuntu-latest
steps:
- name: Checkout (push)
if: github.event_name == 'push'
uses: actions/checkout@v4
- name: Checkout (pull_request)
if: github.event_name == 'pull_request'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Checkout (workflow_dispatch)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v4
with:
ref: ${{ inputs.baseBranch }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup git
run: |
git config --local user.email "hello@remix.run"
git config --local user.name "Remix Run Bot"
# Build and force push over the preview/main branch
- name: Build/push branch (push)
if: github.event_name == 'push'
run: |
pnpm run setup-installable-branch preview/main
git push --force --set-upstream origin preview/main
echo "💿 pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
# Build and force push over the PR preview/pr-{number} branch + comment on the PR
- name: Build/push branch (pull_request)
if: github.event_name == 'pull_request' && github.event.pull_request.state == 'open'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pnpm run setup-installable-branch preview/pr-${{ github.event.pull_request.number }}
git push --force --set-upstream origin preview/pr-${{ github.event.pull_request.number }}
echo "pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
pnpm run pr-preview comment ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }}
# Build and normal push for experimental releases to avoid unintended force
# pushes over remote branches in case of a branch name collision
- name: Build/push branch (workflow_dispatch)
if: github.event_name == 'workflow_dispatch'
run: |
pnpm run setup-installable-branch ${{ inputs.installableBranch }}
git push --set-upstream origin ${{ inputs.installableBranch }}
echo "💿 pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
# Cleanup PR preview/pr-{number} branches when the PR is closed
- name: Cleanup preview branch
if: github.event_name == 'pull_request' && github.event.pull_request.state == 'closed'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pnpm run pr-preview cleanup ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }}
================================================
FILE: .github/workflows/publish.yaml
================================================
name: Publish
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
check:
name: Check release readiness
runs-on: ubuntu-latest
outputs:
has_change_files: ${{ steps.check.outputs.has_change_files }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check for change files
id: check
run: |
# Look for change files in any package's .changes directory (excluding README.md)
change_files=$(find packages/*/.changes -name "*.md" ! -name "README.md" 2>/dev/null)
if [ -n "$change_files" ]; then
echo "Change files found (blocking publish):"
echo "$change_files"
echo "has_change_files=true" >> $GITHUB_OUTPUT
else
echo "No change files found, proceeding to publish."
echo "has_change_files=false" >> $GITHUB_OUTPUT
fi
publish:
name: Publish any unreleased packages
needs: check
if: needs.check.outputs.has_change_files == 'false'
runs-on: ubuntu-latest
permissions:
contents: write # Required for creating tags and GitHub releases
id-token: write # OIDC ID token is used for authentication with npm/jsr
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Get Playwright Version
id: playwright-version
run: echo "version=$(pnpm --filter @remix-run/component exec playwright --version | cut -d ' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-browsers
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright Browsers
if: steps.cache-browsers.outputs.cache-hit != 'true'
run: pnpm --filter @remix-run/component exec playwright install --with-deps
- name: Run tests
run: pnpm test
- name: Build packages
run: pnpm build
- name: Publish to npm and create releases
run: node ./scripts/publish.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Output tag
run: echo "tag=$(git tag --points-at HEAD | grep -e '^remix@3')" >> $GITHUB_OUTPUT
docs:
name: Update API Docs
needs: publish
runs-on: ubuntu-latest
steps:
- name: Trigger remix-api-docs
uses: actions/github-script@v8
with:
github-token: ${{ secrets.REMIX_API_DOCS_PAT }}
script: |
await github.rest.repos.createDispatchEvent({
owner: 'remix-run',
repo: 'remix-api-docs',
event_type: 'update-docs',
client_payload: { tag: '${{ needs.publish.outputs.tag }}' }
});
================================================
FILE: .github/workflows/release-pr.yaml
================================================
name: Update "Release" PR
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
update:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_REMIX_PAT }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Update PR
run: node scripts/release-pr.ts
env:
GITHUB_TOKEN: ${{ secrets.GH_REMIX_PAT }}
================================================
FILE: .github/workflows/session-integration.yaml
================================================
name: Session Integration Tests
on:
push:
branches:
- main
paths:
- '.github/workflows/session-integration.yaml'
- 'packages/session/**'
- 'packages/session-storage-memcache/**'
- 'packages/session-storage-redis/**'
- 'pnpm-workspace.yaml'
- 'package.json'
pull_request:
paths:
- '.github/workflows/session-integration.yaml'
- 'packages/session/**'
- 'packages/session-storage-memcache/**'
- 'packages/session-storage-redis/**'
- 'pnpm-workspace.yaml'
- 'package.json'
jobs:
memcache-integration:
name: Memcache Integration
runs-on: ubuntu-latest
services:
memcached:
image: memcached:1.6
ports:
- 11211:11211
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run memcache integration tests
env:
SESSION_MEMCACHE_INTEGRATION: '1'
SESSION_MEMCACHE_SERVER: 127.0.0.1:11211
run: node --disable-warning=ExperimentalWarning --test './packages/session-storage-memcache/src/lib/memcache-storage.integration.test.ts'
redis-integration:
name: Redis Integration
runs-on: ubuntu-latest
services:
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd="redis-cli ping || exit 1"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run redis integration tests
env:
SESSION_REDIS_INTEGRATION: '1'
SESSION_REDIS_URL: redis://127.0.0.1:6379
run: node --disable-warning=ExperimentalWarning --test './packages/session-storage-redis/src/lib/redis-storage.integration.test.ts'
================================================
FILE: .github/workflows/test.yaml
================================================
name: Test
on:
push:
branches:
- main
pull_request:
branches-ignore:
- release-v2
- v2
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test-ubuntu:
name: test (ubuntu-latest)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Get Playwright Version
id: playwright-version
shell: bash
run: echo "version=$(pnpm --filter @remix-run/component exec playwright --version | cut -d ' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-browsers
with:
path: |
~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright Browsers
if: steps.cache-browsers.outputs.cache-hit != 'true'
run: pnpm --filter @remix-run/component exec playwright install --with-deps
- name: Run tests
run: pnpm test
test-windows-pr:
name: test (windows-latest, changed packages)
if: github.event_name == 'pull_request'
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Get Playwright Version
id: playwright-version
shell: bash
run: echo "version=$(pnpm --filter @remix-run/component exec playwright --version | cut -d ' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-browsers
with:
path: ~/AppData/Local/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright Browsers
if: steps.cache-browsers.outputs.cache-hit != 'true'
run: pnpm --filter @remix-run/component exec playwright install --with-deps
- name: Run changed package tests
run: node ./scripts/detect-changed-packages.ts origin/${{ github.base_ref }}
test-windows-main:
name: test (windows-latest)
if: github.event_name == 'push'
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Get Playwright Version
id: playwright-version
shell: bash
run: echo "version=$(pnpm --filter @remix-run/component exec playwright --version | cut -d ' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-browsers
with:
path: ~/AppData/Local/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright Browsers
if: steps.cache-browsers.outputs.cache-hit != 'true'
run: pnpm --filter @remix-run/component exec playwright install --with-deps
- name: Run tests
run: pnpm test
================================================
FILE: .gitignore
================================================
dist/
node_modules/
pnpm-publish-summary.json
reference/
.tmp/
/demos/tmp/
*.db
*.db-*
*.sqlite
*.sqlite-*
*.sqlite3
*.sqlite3-*
.env
/reference
================================================
FILE: .prettierignore
================================================
node_modules/
dist/
tmp/
reference/
**/*.bundled.*
**/public/assets/
**/test/fixtures/
**/worker-configuration.d.ts
pnpm-lock.yaml
================================================
FILE: .prettierrc
================================================
printWidth: 100
semi: false
singleQuote: true
useTabs: false
================================================
FILE: .vscode/settings.json
================================================
{
"deno.enablePaths": ["./packages/multipart-parser/examples/deno"],
"nodejs-testing.extensions": [
{
"extensions": ["ts", "tsx"],
"parameters": ["--import", "tsx"]
}
],
"typescript.tsdk": "./node_modules/typescript/lib"
}
================================================
FILE: .vscode/task.json
================================================
{
"version": "0.1.0",
"command": "./node_modules/.bin/tsc",
"args": ["-v"],
"echoCommand": true
}
================================================
FILE: AGENTS.md
================================================
# Remix 3 Development Guide
## Commands
- **Build**: `pnpm run build` (all packages) or `pnpm --filter @remix-run/<package> run build` (single package)
- **Test**: `pnpm test` (all packages) or `pnpm --filter @remix-run/<package> run test` (single package)
- **Single test file**: `node --test './packages/<package>/src/**/<filename>.test.ts'`
- **Typecheck**: `pnpm run typecheck` (all packages) or `pnpm --filter @remix-run/<package> run typecheck`
- **Lint**: `pnpm run lint` (check) or `pnpm run lint:fix` (auto-fix)
- **Before finishing work**: Run `pnpm run lint` and resolve any lint errors before reporting completion.
- **Format**: `pnpm run format` (auto-fix) or `pnpm run format:check` (check only)
- **Clean**: `pnpm run clean` (git clean -fdX)
## Architecture
- **Monorepo**: pnpm workspace with packages in `packages/` directory
- **Key packages**: headers, fetch-proxy, fetch-router, file-storage, form-data-parser, lazy-file, multipart-parser, node-fetch-server, route-pattern, tar-parser
- **Package exports**: All `exports` in `package.json` have a dedicated file in `src` that defines the public API by re-exporting from within `src/lib`
- **Lib module boundaries**: Files in `src/lib` are implementation files. Do not add barrel-style re-exports or thin pass-through wrapper APIs between `src/lib` files. Re-exporting belongs only in top-level `src` barrel files that map to package exports.
- **Cross-package boundaries**: Avoid re-exporting APIs/types from other packages. Consumers should import from the owning package directly. Reuse shared concepts from sibling packages internally instead of creating bespoke duplicate implementations.
- **Documentation imports/install**: In package READMEs, documentation, and pull request code examples, installation instructions should always include `npm i remix`, usage examples should import from `remix` package exports (not `@remix-run/*`), and any required peer dependency should be included in the installation command.
- **Philosophy**: Web standards-first, runtime-agnostic (Node.js, Bun, Deno, Cloudflare Workers). Use Web Streams API, Uint8Array, Web Crypto API, Blob/File instead of Node.js APIs
- **Tests run from source** (no build required), using Node.js test runner
## Code Style
- **Imports**: Always use `import type { X }` for types (separate from value imports); use `export type { X }` for type exports; include `.ts` extensions
- **One-off scripts**: Write one-off scripts in this repo as TypeScript and make them executable natively with modern Node.js (for example, executable `.ts` files)
- **Node runtime assumption**: Assume a modern Node.js runtime that supports running TypeScript files natively; prefer `node path/to/script.ts` in examples and instructions.
- **Variables**: Prefer `let` for locals, `const` only at module scope; never use `var`
- **Functions**: Use regular function declarations/expressions by default. For callback-based APIs (array methods, Promise callbacks, test callbacks, transaction callbacks, etc.), prefer arrow functions over `function` expressions. When an arrow callback only returns a single expression, use a concise body (`value => expression`) instead of braces/`return`
- **Object methods**: When defining functions in object literals, use shorthand method syntax (`{ method() {} }`) instead of arrow functions (`{ method: () => {} }`)
- **Classes**: Use native fields (omit `public`), `#private` for private members (no TypeScript accessibility modifiers)
- **Formatting**: Prettier (printWidth: 100, no semicolons, single quotes, spaces not tabs)
- **TypeScript**: Strict mode, ESNext target, ES2022 modules, bundler resolution, verbatimModuleSyntax
- **Generics**: Use descriptive lowercase names for type parameters (e.g., `source`, `method`, `pattern`) instead of single uppercase letters like `T`, `P`, or `K`
- **Comments**: Only add non-JSDoc comments when the code is doing something surprising or non-obvious
## Test Structure
- **No loops or conditionals in test suites**: Do not use `for` loops or conditional statements (`if`, `switch`, etc.) to generate test cases within `describe()` blocks. This breaks the Node.js test runner's ability to run individual tests via IDE features (like clicking test icons in the sidebar).
## Demos
- All demo servers should use port **44100** for consistency across the monorepo
- **Accessible navigation**: Always use proper `<a>` elements for navigation links. Never use JavaScript `onclick` handlers on non-interactive elements like `<tr>`, `<div>`, or `<span>` for navigation. Links should be keyboard accessible and work with screen readers.
- **Clean shutdown**: Demo servers should handle `SIGINT` and `SIGTERM` signals to exit cleanly when Ctrl+C is pressed. Close the server and call `process.exit(0)`.
## Documentation
- API documentation is handled by scripts in the docs/ directory
- We use `typedoc` to process the source code, and then generate markdown files from the typedoc output
- Markdown API documentation files be generated via `pnpm run docs` in the docs/ directory
## Changes and Releases
- **Automated releases**: When changes are pushed to `main`, the [release-pr workflow](/.github/workflows/release-pr.yaml) automatically opens/updates a "Release" PR. The [publish workflow](/.github/workflows/publish.yaml) runs on every push to `main` and publishes when no change files are present (i.e., after merging the Release PR).
- **Manual releases**: `pnpm changes:version` updates package.json, CHANGELOG.md, and creates a git commit. Push to `main` and the publish workflow will handle the rest (including tags and GitHub releases).
- **How publishing works**: The publish workflow checks for change files. If none exist, it runs `pnpm publish --recursive --report-summary`, reads the summary JSON to see what was published, then creates git tags and GitHub releases for each published package.
- **Test change/release code with preview scripts**: When modifying any change/release code, run `pnpm changes:preview` to test locally. For the release PR script, run `node ./scripts/release-pr.ts --preview`. For the publish script, run `node ./scripts/publish.ts --dry-run` to see what commands would be executed without actually publishing.
## Skills
A skill is a reusable local instruction set stored in a `SKILL.md` file.
### Available skills
- **add-package**: Create or align a package in the Remix monorepo to match existing package conventions. Use when adding a brand new package under packages/, or when fixing an existing package's structure, test setup, TypeScript/build config, code style, and README layout to match the rest of Remix 3. (file: `./.agents/skills/add-package/SKILL.md`)
- **make-change-file**: Create or update package change files using Remix repo conventions, deterministic naming, and release-note style. (file: `./.agents/skills/make-change-file/SKILL.md`)
- **make-demo**: Create or revise demos in the Remix repository so they stay focused on Remix packages, strong code hygiene, and production-quality patterns. (file: `./.agents/skills/make-demo/SKILL.md`)
- **make-pr**: Create GitHub pull requests with clear context, issue/feature bullets, and required usage examples for new or changed APIs. (file: `./.agents/skills/make-pr/SKILL.md`)
- **publish-placeholder-package**: Publish a minimal npm package at `0.0.0` to reserve the name and enable npm OIDC setup before CI-based publishing. (file: `./.agents/skills/publish-placeholder-package/SKILL.md`)
- **supersede-pr**: Replace one GitHub PR with another and explicitly close the superseded PR (instead of relying on `Closes #...` keywords). (file: `./.agents/skills/supersede-pr/SKILL.md`)
- **update-pr**: Rewrite GitHub PR titles and descriptions from scratch so they match the PR as it exists now, and always review the title when updating the body. (file: `./.agents/skills/update-pr/SKILL.md`)
- **write-api-docs**: Write or audit public API docs for Remix packages. Use when adding or tightening JSDoc on exported functions, classes, interfaces, type aliases, or option objects. (file: `./.agents/skills/write-api-docs/SKILL.md`)
- **write-readme**: Write or rewrite Remix package READMEs using this repo's structure, installation conventions, production-style examples, and section ordering. (file: `./.agents/skills/write-readme/SKILL.md`)
================================================
FILE: CONTRIBUTING.md
================================================
Welcome to Remix! We're excited to have you contribute.
This guide will help you get started.
## Setting Up Your Environment
We develop Remix using [pnpm](https://pnpm.io) on Node 24.3+.
If you're using [VS Code](https://code.visualstudio.com/), we recommend installing the [`node:test runner` extension](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) for a smooth testing experience.
Once that's set up, run `pnpm install` to get all the project dependencies.
## Testing
All tests run directly from source. This makes it easy to use breakpoint debugging when running tests. This also means you should not need to run a build before running the tests.
```sh
# Run all tests
$ pnpm test
# Run the tests for a specific package
$ pnpm --filter @remix-run/headers run test
```
## Building
All packages are built using a combination of tsc and esbuild.
```sh
# Build all packages
$ pnpm run build
# Build a specific package
$ pnpm --filter @remix-run/headers run build
```
All packages are published with TypeScript types along with both ESM and CJS module formats.
## Making Changes
Packages live in the [`packages` directory](https://github.com/remix-run/remix/tree/v3/packages). At a minimum, each package includes:
- `.changes/`: Directory containing change files for the next release
- `CHANGELOG.md`: A log of what's changed
- `package.json`: Package metadata and dependencies
- `README.md`: Information about the package
- `src/`: The package's source code
When you make changes to a package, please make sure you add a few relevant tests and run the whole test suite to make sure everything still works. Then, [add a change file](#adding-a-change-file) describing your changes and [make a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). We will take a look at it as soon as we can.
### Adding a Change File
When making changes to a package, create a markdown file in the package's `.changes/` directory following this naming convention:
```
[major|minor|patch].short-description.md
```
- `major` - Breaking changes for v1.x+ packages
- `minor` - Breaking changes for v0.x packages, new features
- `patch` - Bug fixes
#### Examples
- `major.change-something.md` - Breaking change for v1.x+ packages
- `minor.change-something.md` - Breaking change for v0.x packages
- `minor.add-something.md` - New feature
- `patch.fix-something.md` - Bug fix
#### Content Format
Write your change as a bullet point (without the leading `-` or `*`). This content will be added to the CHANGELOG during release.
```markdown
Add support for X feature
This is an optional longer explanation that will be indented
under the main bullet point in the CHANGELOG.
```
For breaking changes in v0.x packages, any change files that begin with `BREAKING CHANGE: ` will be hoisted to the top of the release notes:
```markdown
BREAKING CHANGE: Renamed `foo` option to `bar`
Migration: Update your config to use `bar` instead of `foo`.
```
#### Validation
Change files are automatically validated in CI. You can also validate them locally:
```sh
pnpm changes:validate
```
## Releases
Releases are automated via the [release-pr workflow](/.github/workflows/release-pr.yaml) and [publish workflow](/.github/workflows/publish.yaml).
1. **You push changes to `main`** with change files in `packages/*/.changes/`
2. **A "Release" PR is automatically opened** (or updated if one exists)
The PR contains:
- Updated `package.json` versions
- Updated `CHANGELOG.md` files
- Deleted change files
This PR should not be edited manually. If you need to make changes, modify the change files and/or scripts in `main` to trigger an update to the PR.
3. **When you merge the PR**, the publish workflow runs (it runs on every push to `main` and checks for change files). Since the change files have been deleted, it publishes all unpublished packages to npm, then creates git tags and GitHub releases based on what was actually published.
### Manual Versioning
The "Release" PR simply automates the `pnpm changes:version` command. If needed, you can run this command manually. This will update the `package.json` versions, `CHANGELOG.md` files, and delete the change files. It will then commit the result.
```sh
pnpm changes:version
```
You can skip committing the changes by using the `--no-commit` flag. This will leave the changes in a staged state for you to review and commit manually. The command will also output the commit message that would have been used.
```sh
pnpm changes:version --no-commit
```
Tags and GitHub releases are created automatically by the publish workflow after successful npm publish.
### Prerelease Mode for `remix`
The `remix` package supports prerelease mode via an optional `.changes/config.json` file:
```json
{
"prereleaseChannel": "alpha"
}
```
The `prereleaseChannel` field determines the version suffix (e.g. `alpha`, `beta`, `rc`), while prereleases are always published to npm with the `next` tag. This is only supported for `remix` because it's the only package that needs to publish prereleases alongside an existing stable version on npm. All other packages in this monorepo are new and publish directly as `latest`.
#### Bumping `remix` prerelease versions
While in prerelease mode, add change files as normal. The prerelease counter increments (e.g. `3.0.0-alpha.1` → `3.0.0-alpha.2`). Changelog entries still get proper "Major Changes" / "Minor Changes" / "Patch Changes" sections, but the bump type is otherwise ignored—only the prerelease counter is bumped.
#### Transitioning between `remix` prerelease channels
To transition between channels (e.g. `alpha` → `beta`):
1. Update `prereleaseChannel` in `.changes/config.json` to the new channel
2. Add a change file describing the transition
Version resets to the new channel (e.g. `3.0.0-alpha.7` → `3.0.0-beta.0`). The bump type is for changelog categorization only—by convention, use `patch`.
#### Graduating `remix` to stable
To release the stable version:
1. Remove `prereleaseChannel` from `.changes/config.json` (or delete the file)
2. Add a change file describing the stable release
The prerelease suffix is stripped (e.g. `3.0.0-rc.7` → `3.0.0`). The bump type is for changelog categorization only—by convention, use `major` for a major release announcement.
## Preview builds
We maintain installable builds of `main` in a `preview/main` branch as a way for folks to test out the latest `main` branch without needing to publish releases to npm and clutter up the npm registry and version history UI.
This is managed via the [`preview` workflow](/.github/workflows/preview.yaml) which uses the [`setup-installable-branch.ts`](./scripts/setup-installable-branch.ts) script to build and commit the build and required `package.json` changes to the `preview/main` branch on every new commit to `main`.
The `preview/main` branch build can be [installed directly](https://pnpm.io/package-sources#install-from-a-git-repository-combining-different-parameters) with `pnpm` (version 9+):
```sh
pnpm install "remix-run/remix#preview/main&path:packages/remix"
# Or, just install a single package
pnpm install "remix-run/remix#preview/main&path:packages/fetch-router"
```
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Shopify Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Welcome to Remix 3!
This is the source repository for Remix 3. It is under active development.
We published [a blog post](https://remix.run/blog/wake-up-remix) earlier this year with some of our thoughts around Remix 3. It explains our philosophy for web development and why we think the time is right for something new. When working on Remix 3, we follow these principles:
1. **Model-First Development**. AI fundamentally shifts the human-computer interaction model for both user experience and developer workflows. Optimize the source code, documentation, tooling, and abstractions for LLMs. Additionally, develop abstractions for applications to use models in the product itself, not just as a tool to develop it.
2. **Build on Web APIs**. Sharing abstractions across the stack greatly reduces the amount of context switching, both for humans and machines. Build on the foundation of Web APIs and JavaScript because it is the only full stack ecosystem.
3. **Religiously Runtime**. Designing for bundlers/compilers/typegen (and any pre-runtime static analysis) leads to poor API design that eventually pollutes the entire system. All packages must be designed with no expectation of static analysis and all tests must run without bundling. Because browsers are involved, `--import` loaders for simple transformations like TypeScript and JSX are permissible.
4. **Avoid Dependencies**. Dependencies lock you into somebody else's roadmap. Choose them wisely, wrap them completely, and expect to replace most of them with our own package eventually. The goal is zero.
5. **Demand Composition**. Abstractions should be single-purpose and replaceable. A composable abstraction is easy to add and remove from an existing program. Every package must be useful and documented independent of any other context. New features should first be attempted as a new package. If impossible, attempt to break up the existing package to make it more composable. However, tightly coupled modules that almost always change together in both directions should be moved to the same package.
6. **Distribute Cohesively**. Extremely composable ecosystems are difficult to learn and use. Remix will be distributed as a single `remix` package for both distribution and documentation.
## Goals
Although we recommend the `remix` package for ease of use, all packages that make up Remix should be usable standalone as well. This forces us to consider package boundaries and helps us define public interfaces that are portable and interoperable.
Each package in Remix:
- Has a [single responsibility](https://en.wikipedia.org/wiki/Single-responsibility_principle)
- Prioritizes web standards to ensure maximum interoperability and portability across JavaScript runtimes
- Augments standards unobtrusively where they are missing or incomplete, minimizing incompatibility risks
This means Remix code is **portable by default**. Remix packages work seamlessly across [Node.js](https://nodejs.org/), [Bun](https://bun.sh/), [Deno](https://deno.com/), [Cloudflare Workers](https://workers.cloudflare.com/), and other environments.
We leverage server-side web APIs when they are available:
- [The Web Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) instead of `node:stream`
- [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) instead of Node.js `Buffer`s
- [The Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) instead of `node:crypto`
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) instead of some bespoke runtime-specific API
The benefit is code that's not just reusable, but **future-proof**.
## Packages
We currently publish the following packages:
- [async-context-middleware](packages/async-context-middleware): Middleware for storing request context in AsyncLocalStorage
- [component](packages/component): UI components for Remix
- [compression-middleware](packages/compression-middleware): Middleware for compressing HTTP responses
- [cop-middleware](packages/cop-middleware): Middleware for tokenless cross-origin protection in Fetch API servers
- [cors-middleware](packages/cors-middleware): Middleware for handling CORS in Fetch API servers
- [csrf-middleware](packages/csrf-middleware): Middleware for CSRF protection in Fetch API servers
- [cookie](packages/cookie): A toolkit for working with cookies in JavaScript
- [data-schema](packages/data-schema): Tiny, standards-aligned schema validation
- [data-table](packages/data-table): A typed, relational query toolkit for Remix
- [data-table-mysql](packages/data-table-mysql): MySQL adapter for remix/data-table
- [data-table-postgres](packages/data-table-postgres): PostgreSQL adapter for remix/data-table
- [data-table-sqlite](packages/data-table-sqlite): SQLite adapter for remix/data-table
- [fetch-proxy](packages/fetch-proxy): An HTTP proxy for the web Fetch API
- [fetch-router](packages/fetch-router): A minimal, composable router for the web Fetch API
- [file-storage](packages/file-storage): Key/value storage for JavaScript File objects
- [file-storage-s3](packages/file-storage-s3): S3 backend for remix/file-storage
- [form-data-middleware](packages/form-data-middleware): Middleware for parsing FormData from request bodies
- [form-data-parser](packages/form-data-parser): A request.formData() wrapper with streaming file upload handling
- [fs](packages/fs): Filesystem utilities using the Web File API
- [headers](packages/headers): A toolkit for working with HTTP headers in JavaScript
- [html-template](packages/html-template): HTML template tag with auto-escaping for JavaScript
- [lazy-file](packages/lazy-file): Lazy, streaming files for JavaScript
- [logger-middleware](packages/logger-middleware): Middleware for logging HTTP requests and responses
- [method-override-middleware](packages/method-override-middleware): Middleware for overriding HTTP request methods from form data
- [mime](packages/mime): Utilities for working with MIME types
- [multipart-parser](packages/multipart-parser): A fast, efficient parser for multipart streams in any JavaScript environment
- [node-fetch-server](packages/node-fetch-server): Build servers for Node.js using the web fetch API
- [remix](packages/remix): Remix Web Framework
- [response](packages/response): Response helpers for the web Fetch API
- [route-pattern](packages/route-pattern): Match and generate URLs with strong typing
- [session](packages/session): Session management for JavaScript
- [session-middleware](packages/session-middleware): Middleware for managing sessions with cookie-based storage
- [session-storage-memcache](packages/session-storage-memcache): Memcache session storage for remix/session
- [session-storage-redis](packages/session-storage-redis): Redis session storage for remix/session
- [static-middleware](packages/static-middleware): Middleware for serving static files from the filesystem
- [tar-parser](packages/tar-parser): A fast, efficient parser for tar streams in any JavaScript environment
## Installation
To try the current Remix alpha, install the `next` dist-tag:
```sh
npm install remix@next
```
If you want to play around with the bleeding edge, we also build the latest `main` branch into a `preview/main` branch which can be [installed directly](https://pnpm.io/package-sources#install-from-a-git-repository-combining-different-parameters) with `pnpm` (version 9+):
```sh
pnpm install "remix-run/remix#preview/main&path:packages/remix"
# Or, just install a single package
pnpm install "remix-run/remix#preview/main&path:packages/fetch-router"
```
## Contributing
We welcome contributions! If you'd like to contribute, please feel free to open an issue or submit a pull request. See [CONTRIBUTING](https://github.com/remix-run/remix/blob/main/CONTRIBUTING.md) for more information.
## License
See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
================================================
FILE: cspell.yml
================================================
version: '0.2'
language: en
words:
- accentunder
- actiontype
- activedescendant
- aftertoggle
- arcrole
- Arcrole
- autocorrect
- backlink
- bbox
- biblioentry
- biblioref
- Booleanish
- braillelabel
- brailleroledescription
- closedby
- closerequest
- colcount
- colindex
- colindextext
- columnalign
- columnlines
- columnspacing
- columnspan
- commandfor
- contenteditable
- contentinfo
- controlslist
- denomalign
- describedby
- desync
- disableremoteplayback
- displaystyle
- DOMCSS
- dropeffect
- elementtiming
- enterkeyhint
- exportparts
- fetchpriority
- fixpoint
- flowto
- focusin
- focusout
- fontstyle
- fontweight
- formaction
- formenctype
- formmethod
- formnovalidate
- formtarget
- FOUC
- framespacing
- glossref
- haspopup
- healthcheck
- horiz
- hsba
- inlist
- inputmode
- itemprop
- itemref
- itemscope
- itemtype
- jsxs
- keybind
- keyshortcuts
- keyup
- labelledby
- largeop
- linethickness
- lquote
- lspace
- maction
- mathbackground
- mathcolor
- mathsize
- mathvariant
- maxlength
- maxsize
- menclose
- menuitemcheckbox
- menuitemradio
- merror
- mfenced
- mfrac
- Microdata
- minsize
- Mmulti
- mmultiscripts
- movablelimits
- mpadded
- mpath
- mphantom
- mprescripts
- mroot
- mrow
- mspace
- msqrt
- mstyle
- msub
- msubsup
- msup
- mtable
- mtext
- multiselectable
- munder
- munderover
- noteref
- novalidate
- numalign
- outerclick
- pagebreak
- pagelist
- panose
- playsinline
- pointerdown
- pointerenter
- pointerleave
- pointermove
- pointerup
- popovertarget
- popovertargetaction
- posinset
- pullquote
- referrerpolicy
- renderable
- roledescription
- roletype
- rowalign
- rowcount
- rowindex
- rowindextext
- rowlines
- rowspacing
- rowspan
- rquote
- rspace
- scriptlevel
- scriptminsize
- scriptsizemultiplier
- sectionhead
- setsize
- Signalish
- spinbutton
- statusline
- stemh
- stemv
- subscriptshift
- Subsup
- superscriptshift
- SVGM
- treegrid
- treeitems
- unitless
- unkeyed
- valuenow
- valuetext
- vdom
- visibilitychange
- vnode
- VNODE
- vnodes
- voffset
- wmode
- xlink
ignorePaths:
- node_modules/**
- dist/**
- build/**
- pnpm-lock.yaml
================================================
FILE: decisions/001-route-pattern-vs-url-pattern.md
================================================
# RoutePattern vs. URLPattern
The web has a built-in URL matcher called [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern). Why don't we use it instead of creating our own thing with [`RoutePattern`](../packages/route-pattern)?
## Main Differences
There are a number of major differences between `RoutePattern` and `URLPattern`. Here are the main ones:
- **Generating URLs**: `RoutePattern` comes with an "href builder" for building URLs from route patterns. This is the logical bookend to "matching" or parsing a URL.
- **Easier pathname-only Matching**: `RoutePattern` allows matching only a URL pathname without resorting to the object syntax, or beginning with a leading `/`
```tsx
let pattern = new RoutePattern('products/:id') // matches <protocol>://<host>/products/:id
// vs. URLPattern, requires object syntax and leading slash
let pattern = new URLPattern({ pathname: '/products/:id' })
```
- **Non-exhaustive Search Matching**: `RoutePattern` does not treat the pattern's search string as exhaustive. This allows matching URLs that contain additional query parameters, which is important for allowing traffic that comes from sources where you don't have full control over the search string.
```tsx
let pattern = new RoutePattern('?q=remix')
pattern.match('https://remix.run/?q=remix') // match
pattern.match('https://remix.run/?q=remix&utm_source') // also match!
let pattern = new URLPattern({ search: '?q=remix' })
pattern.exec('https://remix.run/?q=remix') // match
pattern.exec('https://remix.run/?q=remix&utm_source') // null :(
```
- **More Intuitive Optionals**: `RoutePattern` expresses optionals using parentheses, similar to Rails. These read like English instead of using `?` to indicate optional groups as in regular expressions. It also makes the start and end positions of an optional group immediately obvious.
```tsx
// An "optional group" using URLPattern
let pattern = new URLPattern('/books/:id?', 'https://example.com')
pattern.test('https://example.com/books/123') // true
pattern.test('https://example.com/books') // true
// This behavior is unintuitive. Is the ":id" optional? Or the "/:id"?
// There's no way to know when you only have a single group modifier character (the `?`)
pattern.test('https://example.com/books/') // false
// An optional using RoutePattern
let pattern = new RoutePattern('/books(/:id)')
pattern.test('https://example.com/books/123') // true
pattern.test('https://example.com/books') // true
// This result is more intuitive because the () surround the optional
// portion of the pattern, indicating both start and end characters
pattern.test('https://example.com/books/') // false
```
- **Uniform Param Access**: `RoutePattern` does not support "unnamed groups" that must be accessed by index in the match result. Instead, all variables (groups) must have names and are accessed by that name at `match.params[name]`.
- **No RegExp Syntax**: `RoutePattern` does not allow regex syntax. This means route patterns are statically analyzable without parsing RegExp grammar, which makes it easier to provide type safety. Also, the whole point of `RoutePattern` is to provide a syntax that is sufficient for matching URLs without resorting to some other syntax.
================================================
FILE: decisions/002-branching-and-releasing.md
================================================
# Branching and Releasing in Remix 3
Beginning in Remix 3 the `remix` package is an umbrella package for everything in Remix. This includes a number of "sub-packages" that are all published under the `@remix-run/*` scope. The `remix` package re-exports everything from all sub-packages as a transparent pass-thru. This means users only have to install `remix` to get everything we publish, and they can import everything from some `remix/*` export.
We anticipate the majority of Remix development will happen directly on the `main` branch. `main` should always be publishable. We will publish minor/patch changes from `main` often for both `remix` and any sub-package that needs it.
## Breaking Changes
When it comes to breaking changes (that require a major version bump), we have 2 goals:
- Be slow and deliberate about cutting major `remix` releases so we don't stress people out by releasing majors too often
- Release breaking changes in sub-packages as soon as they are ready so people can play with them
Major `remix` releases will happen on a predetermined schedule so that users may plan upgrades into their development lifecycle.
Breaking changes will accumulate on a `future` branch. The `future` branch is a preview of what the next major version of Remix will look like. If someone wants to play with the latest stuff, they can build directly from `future`. We don't make any guarantees about the [stability][^stability] of `future`, which is why users must build from source.
We will publish new majors of sub-packages as soon as they are ready from the `future` branch. When it's time to cut the next major `remix` release, we will merge `future` into `main`. Of course, this means that `main` should be merged into `future` periodically to make this easier.
[^stability]: By "stable" we mean "won't break between releases". Both `main` and `future` should always pass all tests and be usable, but on `main` we have versions and stability guarantees between them. On `future`, we don't.
================================================
FILE: demos/bookstore/.gitignore
================================================
public/assets/
tmp/
data/*.sqlite
================================================
FILE: demos/bookstore/README.md
================================================
# Bookstore Demo
A full-featured e-commerce bookstore demonstrating the most powerful patterns and features of Remix. This demo showcases authentication, shopping cart, admin CRUD operations, file uploads, progressive enhancement, and much more.
## Running the Demo
```bash
cd demos/bookstore
pnpm install
pnpm start
```
Then visit http://localhost:44100
### Demo Accounts
- **Admin**: admin@bookstore.com / admin123
- **Customer**: customer@example.com / password123
## Database and Migrations
- The SQLite file is stored at `data/bookstore.sqlite`
- Migration files live in `data/migrations`
- On startup, the app loads migrations from `data/migrations` and runs pending migrations before seeding demo data
## Code Highlights
- [`app/routes.ts`](app/routes.ts) shows declarative route definitions using `route()`, `form()`, and `resources()` helpers. All route URLs are generated with full type safety, so `routes.admin.books.edit.href({ bookId: '123' })` ensures you never have broken links.
- [`app/router.ts`](app/router.ts) demonstrates how to compose middleware for cross-cutting concerns: static file serving, form data parsing, method override, sessions, and async context. Each middleware is independent and reusable.
- [`data/migrations/20260228090000_create_bookstore_schema.ts`](data/migrations/20260228090000_create_bookstore_schema.ts) defines the schema using `remix/data-table/migrations`.
- [`app/middleware/database.ts`](app/middleware/database.ts) stores the bookstore database on request context with `context.set(Database, db)`, and request handlers read it back with `get(Database)` just like they do for `Session` and `FormData`.
- [`app/middleware/auth.ts`](app/middleware/auth.ts) provides two patterns:
- **`loadAuth()`** - Optionally loads the current user without requiring authentication
- **`requireAuth()`** - Redirects to login with a `returnTo` parameter for post-login redirect
- [`app/middleware/admin.ts`](app/middleware/admin.ts) shows role-based authorization that returns 403 for non-admin users.
- [`app/utils/context.ts`](app/utils/context.ts) demonstrates sharing data across the request lifecycle without prop drilling. Any code can call `getCurrentUser()` to access the authenticated user set by middleware earlier in the chain.
- [`app/utils/session.ts`](app/utils/session.ts) configures signed cookies and filesystem-based session storage.
- [`app/utils/uploads.ts`](app/utils/uploads.ts) handles file uploads with `@remix-run/form-data-middleware`. The upload handler stores files and returns public URLs. [`app/uploads.tsx`](app/uploads.tsx) serves uploaded files with appropriate caching headers.
- HTML forms only support GET and POST. [`app/components/restful-form.tsx`](app/components/restful-form.tsx) adds a hidden `_method` field for PUT and DELETE, which the `methodOverride()` middleware translates back to the original method.
- [`app/assets/cart-button.tsx`](app/assets/cart-button.tsx) shows a button that works without JavaScript (full form submission) but upgrades to fetch-based updates when JS is available. Notice how `hydrated()` wraps a component that maintains local state (`updating`) and calls `this.update()` to re-render.
- [`app/assets/image-carousel.tsx`](app/assets/image-carousel.tsx) demonstrates a similar pattern for an interactive image carousel.
- [`app/books.tsx`](app/books.tsx) uses `<Frame>` to render book cards that can be loaded independently. The frame URLs point to [`app/fragments.tsx`](app/fragments.tsx), and [`app/utils/frame.tsx`](app/utils/frame.tsx) shows how frames are resolved server-side during initial render.
- [`app/admin.books.tsx`](app/admin.books.tsx) demonstrates complete CRUD operations with file uploads, using the RESTful routes generated by `resources('books')`.
================================================
FILE: demos/bookstore/app/account.test.ts
================================================
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { router } from './router.ts'
import { loginAsCustomer, requestWithSession, assertContains } from '../test/helpers.ts'
describe('account handlers', () => {
it('GET /account redirects to login when not authenticated', async () => {
let response = await router.fetch('https://remix.run/account')
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/login?returnTo=%2Faccount')
})
it('GET /account returns account page when authenticated', async () => {
let sessionId = await loginAsCustomer(router)
// Now access account page with session
let request = requestWithSession('https://remix.run/account', sessionId)
let response = await router.fetch(request)
assert.equal(response.status, 200)
let html = await response.text()
assertContains(html, 'My Account')
assertContains(html, 'Account Information')
assertContains(html, 'John Doe')
})
it('GET /account/orders/:orderId shows order for authenticated user', async () => {
let sessionId = await loginAsCustomer(router)
// Access existing order
let request = requestWithSession('https://remix.run/account/orders/1001', sessionId)
let response = await router.fetch(request)
assert.equal(response.status, 200)
let html = await response.text()
assertContains(html, 'Order #1001')
assertContains(html, 'Ash & Smoke')
})
it('GET /account/orders shows item counts from normalized order items', async () => {
let sessionId = await loginAsCustomer(router)
let request = requestWithSession('https://remix.run/account/orders', sessionId)
let response = await router.fetch(request)
assert.equal(response.status, 200)
let html = await response.text()
assertContains(html, '2 item(s)')
assertContains(html, '1 item(s)')
})
})
================================================
FILE: demos/bookstore/app/account.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import * as s from 'remix/data-schema'
import * as f from 'remix/data-schema/form-data'
import { Database } from 'remix/data-table'
import { redirect } from 'remix/response/redirect'
import { routes } from './routes.ts'
import { Layout } from './layout.tsx'
import { requireAuth } from './middleware/auth.ts'
import { orders, orderItemsWithBook, users } from './data/schema.ts'
import { getCurrentUser } from './utils/context.ts'
import { parseId } from './utils/ids.ts'
import { render } from './utils/render.ts'
import { RestfulForm } from './components/restful-form.tsx'
const textField = f.field(s.defaulted(s.string(), ''))
const accountSettingsSchema = f.object({
name: textField,
email: textField,
password: textField,
})
export default {
middleware: [requireAuth()],
actions: {
index() {
let user = getCurrentUser()
return render(
<Layout>
<h1>My Account</h1>
<div class="card">
<h2>Account Information</h2>
<p>
<strong>Name:</strong> {user.name}
</p>
<p>
<strong>Email:</strong> {user.email}
</p>
<p>
<strong>Role:</strong> {user.role}
</p>
<p>
<strong>Member Since:</strong> {new Date(user.created_at).toLocaleDateString()}
</p>
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.account.settings.index.href()} class="btn">
Edit Settings
</a>
</p>
</div>
<div class="card" mix={[css({ marginTop: '1.5rem' })]}>
<h2>Quick Links</h2>
<p>
<a href={routes.account.orders.index.href()} class="btn btn-secondary">
View Orders
</a>
<a
href={routes.books.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Browse Books
</a>
</p>
</div>
</Layout>,
)
},
settings: {
actions: {
index() {
let user = getCurrentUser()
return render(
<Layout>
<h1>Account Settings</h1>
<div class="card">
<RestfulForm method="PUT" action={routes.account.settings.update.href()}>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" value={user.name} required />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" value={user.email} required />
</div>
<div class="form-group">
<label for="password">New Password (leave blank to keep current)</label>
<input
type="password"
id="password"
name="password"
autoComplete="new-password"
/>
</div>
<button type="submit" class="btn">
Update Settings
</button>
<a
href={routes.account.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Cancel
</a>
</RestfulForm>
</div>
</Layout>,
)
},
async update({ get }) {
let db = get(Database)
let formData = get(FormData)
let user = getCurrentUser()
let { email, name, password } = s.parse(accountSettingsSchema, formData)
let updateData: any = { name, email }
if (password) {
updateData.password = password
}
await db.update(users, user.id, updateData)
return redirect(routes.account.index.href())
},
},
},
orders: {
actions: {
async index({ get }) {
let db = get(Database)
let user = getCurrentUser()
let userOrders = await db.findMany(orders, {
where: { user_id: user.id },
orderBy: ['created_at', 'asc'],
with: { items: orderItemsWithBook },
})
return render(
<Layout>
<h1>My Orders</h1>
<div class="card">
{userOrders.length > 0 ? (
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Items</th>
<th>Total</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{userOrders.map((order) => (
<tr>
<td>#{order.id}</td>
<td>{new Date(order.created_at).toLocaleDateString()}</td>
<td>{order.items.length} item(s)</td>
<td>${order.total.toFixed(2)}</td>
<td>
<span class="badge badge-info">{order.status}</span>
</td>
<td>
<a
href={routes.account.orders.show.href({ orderId: order.id })}
class="btn btn-secondary"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
View
</a>
</td>
</tr>
))}
</tbody>
</table>
) : (
<p>You have no orders yet.</p>
)}
</div>
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.account.index.href()} class="btn btn-secondary">
Back to Account
</a>
</p>
</Layout>,
)
},
async show({ get, params }) {
let db = get(Database)
let user = getCurrentUser()
let orderId = parseId(params.orderId)
let order =
orderId === undefined
? undefined
: await db.find(orders, orderId, {
with: { items: orderItemsWithBook },
})
if (!order || order.user_id !== user.id) {
return render(
<Layout>
<div class="card">
<h1>Order Not Found</h1>
<p>
<a href={routes.account.orders.index.href()} class="btn">
Back to Orders
</a>
</p>
</div>
</Layout>,
{ status: 404 },
)
}
let shippingAddress = JSON.parse(order.shipping_address_json) as {
street: string
city: string
state: string
zip: string
}
return render(
<Layout>
<h1>Order #{order.id}</h1>
<div class="card">
<p>
<strong>Order Date:</strong> {new Date(order.created_at).toLocaleDateString()}
</p>
<p>
<strong>Status:</strong> <span class="badge badge-info">{order.status}</span>
</p>
<h2 mix={[css({ marginTop: '2rem' })]}>Items</h2>
<table mix={[css({ marginTop: '1rem' })]}>
<thead>
<tr>
<th>Book</th>
<th>Quantity</th>
<th>Price</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
{order.items.map((item) => (
<tr>
<td>{item.title}</td>
<td>{item.quantity}</td>
<td>${item.unit_price.toFixed(2)}</td>
<td>${(item.unit_price * item.quantity).toFixed(2)}</td>
</tr>
))}
</tbody>
<tfoot>
<tr>
<td colSpan={3} mix={[css({ textAlign: 'right', fontWeight: 'bold' })]}>
Total:
</td>
<td mix={[css({ fontWeight: 'bold' })]}>${order.total.toFixed(2)}</td>
</tr>
</tfoot>
</table>
<h2 mix={[css({ marginTop: '2rem' })]}>Shipping Address</h2>
<p>{shippingAddress.street}</p>
<p>
{shippingAddress.city}, {shippingAddress.state} {shippingAddress.zip}
</p>
</div>
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.account.orders.index.href()} class="btn btn-secondary">
Back to Orders
</a>
</p>
</Layout>,
)
},
},
},
},
} satisfies Controller<typeof routes.account>
================================================
FILE: demos/bookstore/app/admin.books.test.ts
================================================
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { router } from './router.ts'
import { loginAsAdmin, requestWithSession } from '../test/helpers.ts'
describe('admin books handlers', () => {
it('POST /admin/books creates new book when admin', async () => {
let sessionId = await loginAsAdmin(router)
// Create new book
let createRequest = requestWithSession('https://remix.run/admin/books', sessionId, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
slug: 'test-book',
title: 'Test Book',
author: 'Test Author',
description: 'Test description',
price: '29.99',
genre: 'test',
isbn: '978-0000000000',
publishedYear: '2024',
inStock: 'true',
}),
})
let response = await router.fetch(createRequest)
assert.equal(response.status, 302)
assert.ok(response.headers.get('Location')?.includes('/admin/books'))
})
})
================================================
FILE: demos/bookstore/app/admin.books.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import * as s from 'remix/data-schema'
import * as f from 'remix/data-schema/form-data'
import * as coerce from 'remix/data-schema/coerce'
import { Database } from 'remix/data-table'
import { redirect } from 'remix/response/redirect'
import { routes } from './routes.ts'
import { books } from './data/schema.ts'
import { Layout } from './layout.tsx'
import { parseId } from './utils/ids.ts'
import { render } from './utils/render.ts'
import { RestfulForm } from './components/restful-form.tsx'
const textField = f.field(s.defaulted(s.string(), ''))
const optionalTextField = f.field(s.optional(s.string()))
const priceField = f.field(s.defaulted(s.string(), '0'))
const publishedYearField = f.field(s.defaulted(s.string(), '2024'), {
name: 'publishedYear',
})
const inStockField = f.field(s.defaulted(coerce.boolean(), false), {
name: 'inStock',
})
const bookSchema = f.object({
slug: textField,
title: textField,
author: textField,
description: textField,
price: priceField,
genre: textField,
cover: optionalTextField,
isbn: textField,
publishedYear: publishedYearField,
inStock: inStockField,
})
export default {
actions: {
async index({ get }) {
let db = get(Database)
let allBooks = await db.findMany(books, { orderBy: ['id', 'asc'] })
return render(
<Layout>
<h1>Manage Books</h1>
<p mix={[css({ marginBottom: '1rem' })]}>
<a href={routes.admin.books.new.href()} class="btn">
Add New Book
</a>
<a
href={routes.admin.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Back to Dashboard
</a>
</p>
<div class="card">
<table>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Genre</th>
<th>Price</th>
<th>Stock</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{allBooks.map((book) => (
<tr>
<td>{book.title}</td>
<td>{book.author}</td>
<td>{book.genre}</td>
<td>${book.price.toFixed(2)}</td>
<td>
<span class={`badge ${book.in_stock ? 'badge-success' : 'badge-warning'}`}>
{book.in_stock ? 'Yes' : 'No'}
</span>
</td>
<td class="actions">
<a
href={routes.admin.books.edit.href({ bookId: book.id })}
class="btn btn-secondary"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
Edit
</a>
<RestfulForm
method="DELETE"
action={routes.admin.books.destroy.href({ bookId: book.id })}
mix={[css({ display: 'inline' })]}
>
<button
type="submit"
class="btn btn-danger"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
Delete
</button>
</RestfulForm>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Layout>,
)
},
async show({ get, params }) {
let db = get(Database)
let bookId = parseId(params.bookId)
let book = bookId === undefined ? undefined : await db.find(books, bookId)
if (!book) {
return render(
<Layout>
<div class="card">
<h1>Book Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
return render(
<Layout>
<h1>Book Details</h1>
<div class="card">
<p>
<strong>Title:</strong> {book.title}
</p>
<p>
<strong>Author:</strong> {book.author}
</p>
<p>
<strong>Slug:</strong> {book.slug}
</p>
<p>
<strong>Description:</strong> {book.description}
</p>
<p>
<strong>Price:</strong> ${book.price.toFixed(2)}
</p>
<p>
<strong>Genre:</strong> {book.genre}
</p>
<p>
<strong>ISBN:</strong> {book.isbn}
</p>
<p>
<strong>Published:</strong> {book.published_year}
</p>
<p>
<strong>In Stock:</strong>{' '}
<span class={`badge ${book.in_stock ? 'badge-success' : 'badge-warning'}`}>
{book.in_stock ? 'Yes' : 'No'}
</span>
</p>
<div mix={[css({ marginTop: '2rem' })]}>
<a href={routes.admin.books.edit.href({ bookId: book.id })} class="btn">
Edit
</a>
<a
href={routes.admin.books.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Back to List
</a>
</div>
</div>
</Layout>,
)
},
new() {
return render(
<Layout>
<h1>Add New Book</h1>
<div class="card">
<form
method="POST"
action={routes.admin.books.create.href()}
encType="multipart/form-data"
>
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" name="title" required />
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" id="author" name="author" required />
</div>
<div class="form-group">
<label for="slug">Slug (URL-friendly name)</label>
<input type="text" id="slug" name="slug" required />
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea id="description" name="description" required></textarea>
</div>
<div class="form-group">
<label for="price">Price</label>
<input type="number" id="price" name="price" step="0.01" required />
</div>
<div class="form-group">
<label for="genre">Genre</label>
<input type="text" id="genre" name="genre" required />
</div>
<div class="form-group">
<label for="isbn">ISBN</label>
<input type="text" id="isbn" name="isbn" required />
</div>
<div class="form-group">
<label for="publishedYear">Published Year</label>
<input type="number" id="publishedYear" name="publishedYear" required />
</div>
<div class="form-group">
<label for="inStock">In Stock</label>
<select id="inStock" name="inStock">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div class="form-group">
<label for="cover">Book Cover Image</label>
<input type="file" id="cover" name="cover" accept="image/*" />
<small mix={[css({ color: '#666' })]}>
Optional. Upload a cover image for this book.
</small>
</div>
<button type="submit" class="btn">
Create Book
</button>
<a
href={routes.admin.books.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Cancel
</a>
</form>
</div>
</Layout>,
)
},
async create({ get }) {
let db = get(Database)
let formData = get(FormData)
let { author, cover, description, genre, inStock, isbn, price, publishedYear, slug, title } =
s.parse(bookSchema, formData)
await db.create(books, {
slug,
title,
author,
description,
price: parseFloat(price),
genre,
cover_url: cover ?? '/images/placeholder.jpg',
image_urls: JSON.stringify([]),
isbn,
published_year: parseInt(publishedYear, 10),
in_stock: inStock,
})
return redirect(routes.admin.books.index.href())
},
async edit({ get, params }) {
let db = get(Database)
let bookId = parseId(params.bookId)
let book = bookId === undefined ? undefined : await db.find(books, bookId)
if (!book) {
return render(
<Layout>
<div class="card">
<h1>Book Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
return render(
<Layout>
<h1>Edit Book</h1>
<div class="card">
<RestfulForm
method="PUT"
action={routes.admin.books.update.href({ bookId: book.id })}
encType="multipart/form-data"
>
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" name="title" value={book.title} required />
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" id="author" name="author" value={book.author} required />
</div>
<div class="form-group">
<label for="slug">Slug (URL-friendly name)</label>
<input type="text" id="slug" name="slug" value={book.slug} required />
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea id="description" name="description" required>
{book.description}
</textarea>
</div>
<div class="form-group">
<label for="price">Price</label>
<input
type="number"
id="price"
name="price"
step="0.01"
value={book.price}
required
/>
</div>
<div class="form-group">
<label for="genre">Genre</label>
<input type="text" id="genre" name="genre" value={book.genre} required />
</div>
<div class="form-group">
<label for="isbn">ISBN</label>
<input type="text" id="isbn" name="isbn" value={book.isbn} required />
</div>
<div class="form-group">
<label for="publishedYear">Published Year</label>
<input
type="number"
id="publishedYear"
name="publishedYear"
value={book.published_year}
required
/>
</div>
<div class="form-group">
<label for="inStock">In Stock</label>
<select id="inStock" name="inStock">
<option value="true" selected={book.in_stock}>
Yes
</option>
<option value="false" selected={!book.in_stock}>
No
</option>
</select>
</div>
<div class="form-group">
<label for="cover">Book Cover Image</label>
{book.cover_url !== '/images/placeholder.jpg' && (
<div mix={[css({ marginBottom: '0.5rem' })]}>
<img
src={book.cover_url}
alt={book.title}
mix={[css({ maxWidth: '200px', height: 'auto', borderRadius: '4px' })]}
/>
<p mix={[css({ fontSize: '0.875rem', color: '#666' })]}>Current cover image</p>
</div>
)}
<input type="file" id="cover" name="cover" accept="image/*" />
<small mix={[css({ color: '#666' })]}>
Optional. Upload a new cover image to replace the current one.
</small>
</div>
<button type="submit" class="btn">
Update Book
</button>
<a
href={routes.admin.books.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Cancel
</a>
</RestfulForm>
</div>
</Layout>,
)
},
async update({ get, params }) {
let db = get(Database)
let formData = get(FormData)
let bookId = parseId(params.bookId)
let book = bookId === undefined ? undefined : await db.find(books, bookId)
if (!book) {
return new Response('Book not found', { status: 404 })
}
let { author, cover, description, genre, inStock, isbn, price, publishedYear, slug, title } =
s.parse(bookSchema, formData)
// The uploadHandler automatically saves the file and returns the URL path
// If no file was uploaded, keep the existing cover_url
let cover_url = cover || book.cover_url
await db.update(books, book.id, {
slug,
title,
author,
description,
price: parseFloat(price),
genre,
cover_url,
isbn,
published_year: parseInt(publishedYear, 10),
in_stock: inStock,
})
return redirect(routes.admin.books.index.href())
},
async destroy({ get, params }) {
let db = get(Database)
let bookId = parseId(params.bookId)
let book = bookId === undefined ? undefined : await db.find(books, bookId)
if (book) {
await db.delete(books, book.id)
}
return redirect(routes.admin.books.index.href())
},
},
} satisfies Controller<typeof routes.admin.books>
================================================
FILE: demos/bookstore/app/admin.orders.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import { Database } from 'remix/data-table'
import { routes } from './routes.ts'
import { orders, orderItemsWithBook } from './data/schema.ts'
import { Layout } from './layout.tsx'
import { parseId } from './utils/ids.ts'
import { render } from './utils/render.ts'
export default {
actions: {
async index({ get }) {
let db = get(Database)
let allOrders = await db.findMany(orders, {
orderBy: ['created_at', 'asc'],
with: { items: orderItemsWithBook },
})
return render(
<Layout>
<h1>Manage Orders</h1>
<p mix={[css({ marginBottom: '1rem' })]}>
<a href={routes.admin.index.href()} class="btn btn-secondary">
Back to Dashboard
</a>
</p>
<div class="card">
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Items</th>
<th>Total</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{allOrders.map((order) => (
<tr>
<td>#{order.id}</td>
<td>{new Date(order.created_at).toLocaleDateString()}</td>
<td>{order.items.length} item(s)</td>
<td>${order.total.toFixed(2)}</td>
<td>
<span class="badge badge-info">{order.status}</span>
</td>
<td>
<a
href={routes.admin.orders.show.href({ orderId: order.id })}
class="btn btn-secondary"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
View
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Layout>,
)
},
async show({ get, params }) {
let db = get(Database)
let orderId = parseId(params.orderId)
let order =
orderId === undefined
? undefined
: await db.find(orders, orderId, {
with: { items: orderItemsWithBook },
})
if (!order) {
return render(
<Layout>
<div class="card">
<h1>Order Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
let shippingAddress = JSON.parse(order.shipping_address_json) as {
street: string
city: string
state: string
zip: string
}
return render(
<Layout>
<h1>Order #{order.id}</h1>
<div class="card">
<p>
<strong>Order Date:</strong> {new Date(order.created_at).toLocaleDateString()}
</p>
<p>
<strong>User ID:</strong> {order.user_id}
</p>
<p>
<strong>Status:</strong> <span class="badge badge-info">{order.status}</span>
</p>
<h2 mix={[css({ marginTop: '2rem' })]}>Items</h2>
<table mix={[css({ marginTop: '1rem' })]}>
<thead>
<tr>
<th>Book</th>
<th>Quantity</th>
<th>Price</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
{order.items.map((item) => (
<tr>
<td>{item.title}</td>
<td>{item.quantity}</td>
<td>${item.unit_price.toFixed(2)}</td>
<td>${(item.unit_price * item.quantity).toFixed(2)}</td>
</tr>
))}
</tbody>
<tfoot>
<tr>
<td colSpan={3} mix={[css({ textAlign: 'right', fontWeight: 'bold' })]}>
Total:
</td>
<td mix={[css({ fontWeight: 'bold' })]}>${order.total.toFixed(2)}</td>
</tr>
</tfoot>
</table>
<h2 mix={[css({ marginTop: '2rem' })]}>Shipping Address</h2>
<p>{shippingAddress.street}</p>
<p>
{shippingAddress.city}, {shippingAddress.state} {shippingAddress.zip}
</p>
</div>
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.admin.orders.index.href()} class="btn btn-secondary">
Back to Orders
</a>
</p>
</Layout>,
)
},
},
} satisfies Controller<typeof routes.admin.orders>
================================================
FILE: demos/bookstore/app/admin.test.ts
================================================
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { router } from './router.ts'
import { loginAsCustomer, requestWithSession } from '../test/helpers.ts'
describe('admin handlers', () => {
it('GET /admin redirects when not authenticated', async () => {
let response = await router.fetch('https://remix.run/admin')
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/login?returnTo=%2Fadmin')
})
it('GET /admin returns 403 for non-admin users', async () => {
let sessionId = await loginAsCustomer(router)
// Try to access admin
let request = requestWithSession('https://remix.run/admin', sessionId)
let response = await router.fetch(request)
assert.equal(response.status, 403)
})
})
================================================
FILE: demos/bookstore/app/admin.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import { routes } from './routes.ts'
import { Layout } from './layout.tsx'
import { requireAuth } from './middleware/auth.ts'
import { requireAdmin } from './middleware/admin.ts'
import { render } from './utils/render.ts'
import adminBooksController from './admin.books.tsx'
import adminOrdersController from './admin.orders.tsx'
import adminUsersController from './admin.users.tsx'
export default {
middleware: [requireAuth(), requireAdmin()],
actions: {
index() {
return render(
<Layout>
<h1>Admin Dashboard</h1>
<div
mix={[
css({
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '1.5rem',
}),
]}
>
<div class="card">
<h2>Manage Books</h2>
<p>Add, edit, or remove books from the catalog.</p>
<a
href={routes.admin.books.index.href()}
class="btn"
mix={[css({ marginTop: '1rem' })]}
>
View Books
</a>
</div>
<div class="card">
<h2>Manage Users</h2>
<p>View and manage user accounts.</p>
<a
href={routes.admin.users.index.href()}
class="btn"
mix={[css({ marginTop: '1rem' })]}
>
View Users
</a>
</div>
<div class="card">
<h2>View Orders</h2>
<p>Monitor and manage customer orders.</p>
<a
href={routes.admin.orders.index.href()}
class="btn"
mix={[css({ marginTop: '1rem' })]}
>
View Orders
</a>
</div>
</div>
</Layout>,
)
},
books: adminBooksController,
users: adminUsersController,
orders: adminOrdersController,
},
} satisfies Controller<typeof routes.admin>
================================================
FILE: demos/bookstore/app/admin.users.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import * as s from 'remix/data-schema'
import * as f from 'remix/data-schema/form-data'
import { Database } from 'remix/data-table'
import { redirect } from 'remix/response/redirect'
import { routes } from './routes.ts'
import { users } from './data/schema.ts'
import { Layout } from './layout.tsx'
import { render } from './utils/render.ts'
import { getCurrentUser } from './utils/context.ts'
import { parseId } from './utils/ids.ts'
import { RestfulForm } from './components/restful-form.tsx'
const textField = f.field(s.defaulted(s.string(), ''))
const roleField = f.field(
s.defaulted(s.union([s.literal('customer'), s.literal('admin')]), 'customer'),
)
const userSchema = f.object({
name: textField,
email: textField,
role: roleField,
})
export default {
actions: {
async index({ get }) {
let db = get(Database)
let user = getCurrentUser()
let allUsers = await db.findMany(users, { orderBy: ['id', 'asc'] })
return render(
<Layout>
<h1>Manage Users</h1>
<p mix={[css({ marginBottom: '1rem' })]}>
<a href={routes.admin.index.href()} class="btn btn-secondary">
Back to Dashboard
</a>
</p>
<div class="card">
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{allUsers.map((u) => (
<tr>
<td>{u.name}</td>
<td>{u.email}</td>
<td>
<span class={`badge ${u.role === 'admin' ? 'badge-info' : 'badge-success'}`}>
{u.role}
</span>
</td>
<td>{new Date(u.created_at).toLocaleDateString()}</td>
<td class="actions">
<a
href={routes.admin.users.edit.href({ userId: u.id })}
class="btn btn-secondary"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
Edit
</a>
{u.id !== user.id ? (
<RestfulForm
method="DELETE"
action={routes.admin.users.destroy.href({ userId: u.id })}
mix={[css({ display: 'inline' })]}
>
<button
type="submit"
class="btn btn-danger"
mix={[css({ fontSize: '0.875rem', padding: '0.25rem 0.5rem' })]}
>
Delete
</button>
</RestfulForm>
) : null}
</td>
</tr>
))}
</tbody>
</table>
</div>
</Layout>,
)
},
async show({ get, params }) {
let db = get(Database)
let userId = parseId(params.userId)
let targetUser = userId === undefined ? undefined : await db.find(users, userId)
if (!targetUser) {
return render(
<Layout>
<div class="card">
<h1>User Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
return render(
<Layout>
<h1>User Details</h1>
<div class="card">
<p>
<strong>Name:</strong> {targetUser.name}
</p>
<p>
<strong>Email:</strong> {targetUser.email}
</p>
<p>
<strong>Role:</strong>{' '}
<span class={`badge ${targetUser.role === 'admin' ? 'badge-info' : 'badge-success'}`}>
{targetUser.role}
</span>
</p>
<p>
<strong>Created:</strong> {new Date(targetUser.created_at).toLocaleDateString()}
</p>
<div mix={[css({ marginTop: '2rem' })]}>
<a href={routes.admin.users.edit.href({ userId: targetUser.id })} class="btn">
Edit
</a>
<a
href={routes.admin.users.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Back to List
</a>
</div>
</div>
</Layout>,
)
},
async edit({ get, params }) {
let db = get(Database)
let userId = parseId(params.userId)
let targetUser = userId === undefined ? undefined : await db.find(users, userId)
if (!targetUser) {
return render(
<Layout>
<div class="card">
<h1>User Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
return render(
<Layout>
<h1>Edit User</h1>
<div class="card">
<RestfulForm
method="PUT"
action={routes.admin.users.update.href({ userId: targetUser.id })}
>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" value={targetUser.name} required />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" value={targetUser.email} required />
</div>
<div class="form-group">
<label for="role">Role</label>
<select id="role" name="role">
<option value="customer" selected={targetUser.role === 'customer'}>
Customer
</option>
<option value="admin" selected={targetUser.role === 'admin'}>
Admin
</option>
</select>
</div>
<button type="submit" class="btn">
Update User
</button>
<a
href={routes.admin.users.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Cancel
</a>
</RestfulForm>
</div>
</Layout>,
)
},
async update({ get, params }) {
let db = get(Database)
let formData = get(FormData)
let userId = parseId(params.userId)
let targetUser = userId === undefined ? undefined : await db.find(users, userId)
let { email, name, role } = s.parse(userSchema, formData)
if (targetUser) {
await db.update(users, targetUser.id, {
name,
email,
role,
})
}
return redirect(routes.admin.users.index.href())
},
async destroy({ get, params }) {
let db = get(Database)
let userId = parseId(params.userId)
let targetUser = userId === undefined ? undefined : await db.find(users, userId)
if (targetUser) {
await db.delete(users, targetUser.id)
}
return redirect(routes.admin.users.index.href())
},
},
} satisfies Controller<typeof routes.admin.users>
================================================
FILE: demos/bookstore/app/assets/cart-button.tsx
================================================
import { type Handle, clientEntry, on } from 'remix/component'
import { routes } from '../routes.ts'
let moduleUrl = routes.assets.href({ path: 'cart-button.js#CartButton' })
export const CartButton = clientEntry(moduleUrl, (handle: Handle) => {
let pending = false
return ({ inCart, id, slug }: { inCart: boolean; id: string | number; slug: string }) => (
<button
type="button"
mix={[
on('click', async (_event, signal) => {
pending = true
handle.update()
let formData = new FormData()
formData.set('bookId', String(id))
formData.set('slug', slug)
await fetch(routes.api.cartToggle.href(), {
method: 'POST',
body: formData,
signal,
})
await handle.frame.reload()
await new Promise((resolve) => setTimeout(resolve, 500))
if (signal.aborted) return
pending = false
handle.update()
}),
]}
class="btn"
>
{pending ? 'Saving...' : inCart ? 'Remove from Cart' : 'Add to Cart'}
</button>
)
})
================================================
FILE: demos/bookstore/app/assets/cart-items.tsx
================================================
import { css, type Handle, clientEntry, on } from 'remix/component'
import { routes } from '../routes.ts'
let moduleUrl = routes.assets.href({ path: 'cart-items.js#CartItems' })
type CartItem = {
bookId: number
slug: string
title: string
price: number
quantity: number
}
type CartItemsProps = {
items: CartItem[]
total: number
canCheckout: boolean
}
type PendingAction = {
type: 'update' | 'remove'
bookId: number
} | null
export let CartItems = clientEntry(moduleUrl, (handle: Handle) => {
let pendingAction: PendingAction = null
let submit = async (form: HTMLFormElement, signal: AbortSignal, nextAction: PendingAction) => {
if (pendingAction) return
pendingAction = nextAction
handle.update()
try {
let formData = new FormData(form)
formData.set('redirect', 'none')
await fetch(form.action, {
method: 'POST',
body: formData,
signal,
})
if (signal.aborted) return
await handle.frame.reload()
} finally {
pendingAction = null
handle.update()
}
}
return ({ items, total, canCheckout }: CartItemsProps) => {
let isPending = pendingAction !== null
let totalLabel = isPending ? '---' : `$${total.toFixed(2)}`
return (
<>
{isPending ? (
<p mix={[css({ marginBottom: '1rem', fontSize: '0.9rem', color: '#666' })]}>
Updating your cart...
</p>
) : null}
<table>
<thead>
<tr>
<th>Book</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{items.map((item) => {
let isUpdating =
pendingAction?.type === 'update' && pendingAction.bookId === item.bookId
let isRemoving =
pendingAction?.type === 'remove' && pendingAction.bookId === item.bookId
return (
<tr key={item.bookId}>
<td>
<a href={routes.books.show.href({ slug: item.slug })}>{item.title}</a>
</td>
<td>${item.price.toFixed(2)}</td>
<td>
<form
method="POST"
action={routes.cart.api.update.href()}
mix={[
on('submit', async (event, signal) => {
event.preventDefault()
await submit(event.currentTarget, signal, {
type: 'update',
bookId: item.bookId,
})
}),
css({ display: 'inline-flex', gap: '0.5rem', alignItems: 'center' }),
]}
>
<input type="hidden" name="_method" value="PUT" />
<input type="hidden" name="bookId" value={item.bookId} />
<input
type="number"
name="quantity"
defaultValue={item.quantity}
min="1"
disabled={isPending}
mix={[css({ width: '70px' })]}
/>
<button
type="submit"
disabled={isPending}
class="btn btn-secondary"
mix={[
css({
fontSize: '0.875rem',
padding: '0.25rem 0.5rem',
minWidth: '6.25rem',
textAlign: 'center',
}),
]}
>
{isUpdating ? 'Saving...' : 'Update'}
</button>
</form>
</td>
<td>${(item.price * item.quantity).toFixed(2)}</td>
<td>
<form
method="POST"
action={routes.cart.api.remove.href()}
mix={[
on('submit', async (event, signal) => {
event.preventDefault()
await submit(event.currentTarget, signal, {
type: 'remove',
bookId: item.bookId,
})
}),
css({ display: 'inline' }),
]}
>
<input type="hidden" name="_method" value="DELETE" />
<input type="hidden" name="bookId" value={item.bookId} />
<button
type="submit"
disabled={isPending}
class="btn btn-danger"
mix={[
css({
fontSize: '0.875rem',
padding: '0.25rem 0.5rem',
minWidth: '7rem',
textAlign: 'center',
}),
]}
>
{isRemoving ? 'Removing...' : 'Remove'}
</button>
</form>
</td>
</tr>
)
})}
</tbody>
</table>
<div mix={[css({ marginTop: '2rem', display: 'flex', alignItems: 'center', gap: '1rem' })]}>
<p
mix={[css({ margin: 0, fontSize: '1.25rem', fontWeight: 'bold', marginRight: 'auto' })]}
>
Total: {totalLabel}
</p>
<a href={routes.books.index.href()} class="btn btn-secondary">
Continue Shopping
</a>
{canCheckout ? (
<a href={routes.checkout.index.href()} class="btn">
Proceed to Checkout
</a>
) : (
<a href={routes.auth.login.index.href()} class="btn">
Login to Checkout
</a>
)}
</div>
</>
)
}
})
================================================
FILE: demos/bookstore/app/assets/entry.tsx
================================================
import { run } from 'remix/component'
let app = run({
async loadModule(moduleUrl: string, exportName: string) {
let mod = await import(moduleUrl)
let Component = (mod as any)[exportName]
if (!Component) {
throw new Error(`Unknown component: ${moduleUrl}#${exportName}`)
}
return Component
},
async resolveFrame(src, signal) {
let response = await fetch(src, { headers: { accept: 'text/html' }, signal })
if (!response.ok) {
return `<pre>Frame error: ${response.status} ${response.statusText}</pre>`
}
// let text = await response.text()
// console.log(text)
// return text
if (response.body) return response.body
return response.text()
},
})
app.ready().catch((error: unknown) => {
console.error('Frame adoption failed:', error)
})
================================================
FILE: demos/bookstore/app/assets/image-carousel.tsx
================================================
import { css, type Handle, clientEntry, on } from 'remix/component'
import { routes } from '../routes.ts'
export const ImageCarousel = clientEntry(
routes.assets.href({ path: 'image-carousel.js#ImageCarousel' }),
function ImageCarousel(handle: Handle, setup?: { startIndex?: number }) {
let index = setup?.startIndex ?? 0
let goPrev = (total: number) => {
if (index <= 0) return
index = index - 1
handle.update()
}
let goNext = (total: number) => {
if (index >= total - 1) return
index = index + 1
handle.update()
}
return ({ images }: { images: string[] }) => {
let total = images.length
if (total === 0) return null
if (index > total - 1) index = total - 1
if (index < 0) index = 0
return (
<div
mix={[
css({
position: 'relative',
width: '100%',
height: '100%',
overflow: 'hidden',
backgroundColor: '#f5f5f5',
}),
]}
>
<div
mix={[
css({
display: 'flex',
height: '100%',
width: '100%',
transition: 'transform 350ms cubic-bezier(0.22, 1, 0.36, 1)',
willChange: 'transform',
}),
]}
style={{
transform: `translateX(-${index * 100}%)`,
}}
>
{images.map((src, i) => (
<div
key={src + i}
mix={[
css({
minWidth: '100%',
height: '100%',
position: 'relative',
}),
]}
>
<img
src={src}
alt={`Image ${i + 1} of ${total}`}
mix={[
css({
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
userSelect: 'none',
pointerEvents: 'none',
}),
]}
draggable={false}
/>
</div>
))}
</div>
<button
aria-label="Previous image"
disabled={index === 0}
mix={[
on('click', () => goPrev(total)),
css({
position: 'absolute',
top: '50%',
left: '8px',
transform: 'translateY(-50%)',
width: '40px',
height: '40px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'transparent',
color: 'white',
border: 'none',
borderRadius: '999px',
cursor: 'pointer',
outline: 'none',
transition: 'background-color 150ms ease, opacity 150ms ease',
}),
]}
style={{
opacity: index === 0 ? 0.4 : 0.9,
}}
>
<span mix={[css({ fontSize: '22px', lineHeight: '1' })]}>{'‹'}</span>
</button>
<button
aria-label="Next image"
disabled={index === total - 1}
mix={[
on('click', () => goNext(total)),
css({
position: 'absolute',
top: '50%',
right: '8px',
transform: 'translateY(-50%)',
width: '40px',
height: '40px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'transparent',
color: 'white',
border: 'none',
borderRadius: '999px',
cursor: 'pointer',
outline: 'none',
transition: 'background-color 150ms ease, opacity 150ms ease',
}),
]}
style={{
opacity: index === total - 1 ? 0.4 : 0.9,
}}
>
<span mix={[css({ fontSize: '22px', lineHeight: '1' })]}>{'›'}</span>
</button>
</div>
)
}
},
)
================================================
FILE: demos/bookstore/app/auth.test.ts
================================================
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { router } from './router.ts'
import { getSessionCookie, assertContains } from '../test/helpers.ts'
describe('auth handlers', () => {
it('POST /login with valid credentials sets session cookie and redirects', async () => {
let response = await router.fetch('https://remix.run/login', {
method: 'POST',
body: new URLSearchParams({
email: 'admin@bookstore.com',
password: 'admin123',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/account')
let sessionId = getSessionCookie(response)
assert.ok(sessionId, 'Expected session cookie to be set')
})
it('POST /login with invalid credentials redirects back to login with error', async () => {
let response = await router.fetch('https://remix.run/login', {
method: 'POST',
body: new URLSearchParams({
email: 'wrong@example.com',
password: 'wrongpassword',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/login')
// Follow redirect to see the error message
let sessionCookie = getSessionCookie(response)
let followUpResponse = await router.fetch('https://remix.run/login', {
headers: {
Cookie: `session=${sessionCookie}`,
},
})
let html = await followUpResponse.text()
assertContains(html, 'Invalid email or password')
})
it('POST /login does not treat wildcard characters as email matches', async () => {
let response = await router.fetch('https://remix.run/login', {
method: 'POST',
body: new URLSearchParams({
email: '%@bookstore.com',
password: 'admin123',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/login')
})
it('flash error message is cleared after being displayed once', async () => {
// POST invalid credentials to trigger flash message
let response = await router.fetch('https://remix.run/login', {
method: 'POST',
body: new URLSearchParams({
email: 'wrong@example.com',
password: 'wrongpassword',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/login')
// Follow redirect to see the error message (first request)
let sessionCookie = getSessionCookie(response)
let firstFollowUp = await router.fetch('https://remix.run/login', {
headers: {
Cookie: `session=${sessionCookie}`,
},
})
let firstHtml = await firstFollowUp.text()
assertContains(firstHtml, 'Invalid email or password')
// Get updated session cookie (session should be updated to clear flash)
let updatedSessionCookie = getSessionCookie(firstFollowUp) || sessionCookie
// Refresh the page (second request) - error should NOT be shown
let secondFollowUp = await router.fetch('https://remix.run/login', {
headers: {
Cookie: `session=${updatedSessionCookie}`,
},
})
let secondHtml = await secondFollowUp.text()
assert.ok(
!secondHtml.includes('Invalid email or password'),
'Expected flash error to be cleared after first display',
)
})
it('POST /register creates new user and sets session', async () => {
let uniqueEmail = `newuser-${Date.now()}@example.com`
let response = await router.fetch('https://remix.run/register', {
method: 'POST',
body: new URLSearchParams({
name: 'New User',
email: uniqueEmail,
password: 'password123',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/account')
let sessionId = getSessionCookie(response)
assert.ok(sessionId, 'Expected session cookie to be set')
})
it('accessing protected route redirects to login with returnTo parameter', async () => {
let response = await router.fetch('https://remix.run/checkout', {
redirect: 'manual',
})
assert.equal(response.status, 302)
let location = response.headers.get('Location')
assert.ok(location, 'Expected Location header')
assert.ok(location.startsWith('/login?returnTo='), 'Expected redirect to login with returnTo')
assert.ok(
location.includes(encodeURIComponent('/checkout')),
'Expected returnTo to contain /checkout',
)
})
it('successful login with returnTo redirects to original destination', async () => {
let response = await router.fetch(
'https://remix.run/login?returnTo=' + encodeURIComponent('/checkout'),
{
method: 'POST',
body: new URLSearchParams({
email: 'customer@example.com',
password: 'password123',
}),
redirect: 'manual',
},
)
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), '/checkout')
let sessionId = getSessionCookie(response)
assert.ok(sessionId, 'Expected session cookie to be set')
})
it('failed login with returnTo preserves returnTo parameter', async () => {
let response = await router.fetch(
'https://remix.run/login?returnTo=' + encodeURIComponent('/checkout'),
{
method: 'POST',
body: new URLSearchParams({
email: 'wrong@example.com',
password: 'wrongpassword',
}),
redirect: 'manual',
},
)
assert.equal(response.status, 302)
let location = response.headers.get('Location')
assert.ok(location, 'Expected Location header')
assert.ok(
location.includes('returnTo=' + encodeURIComponent('/checkout')),
'Expected returnTo to be preserved in redirect',
)
// Follow redirect to verify error message is shown
let sessionCookie = getSessionCookie(response)
let followUpResponse = await router.fetch('https://remix.run' + location, {
headers: {
Cookie: `session=${sessionCookie}`,
},
})
let html = await followUpResponse.text()
assertContains(html, 'Invalid email or password')
assertContains(html, 'returnTo=' + encodeURIComponent('/checkout'))
})
it('POST /reset-password with mismatched passwords redirects back with error', async () => {
// First, request a password reset to get a token
let forgotPasswordResponse = await router.fetch('https://remix.run/forgot-password', {
method: 'POST',
body: new URLSearchParams({
email: 'customer@example.com',
}),
})
let html = await forgotPasswordResponse.text()
// Extract token from the reset link in the demo response
let tokenMatch = html.match(/\/reset-password\/([^"]+)/)
assert.ok(tokenMatch, 'Expected to find reset token in response')
let token = tokenMatch[1]
// Try to reset password with mismatched passwords
let response = await router.fetch(`https://remix.run/reset-password/${token}`, {
method: 'POST',
body: new URLSearchParams({
password: 'newpassword123',
confirmPassword: 'differentpassword',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), `/reset-password/${token}`)
// Follow redirect to see the error message
let sessionCookie = getSessionCookie(response)
let followUpResponse = await router.fetch(`https://remix.run/reset-password/${token}`, {
headers: {
Cookie: `session=${sessionCookie}`,
},
})
let errorHtml = await followUpResponse.text()
assertContains(errorHtml, 'Passwords do not match')
})
it('POST /reset-password with invalid token redirects back with error', async () => {
let invalidToken = 'invalid-token-12345'
let response = await router.fetch(`https://remix.run/reset-password/${invalidToken}`, {
method: 'POST',
body: new URLSearchParams({
password: 'newpassword123',
confirmPassword: 'newpassword123',
}),
redirect: 'manual',
})
assert.equal(response.status, 302)
assert.equal(response.headers.get('Location'), `/reset-password/${invalidToken}`)
// Follow redirect to see the error message
let sessionCookie = getSessionCookie(response)
let followUpResponse = await router.fetch(`https://remix.run/reset-password/${invalidToken}`, {
headers: {
Cookie: `session=${sessionCookie}`,
},
})
let errorHtml = await followUpResponse.text()
assertContains(errorHtml, 'Invalid or expired reset token')
})
})
================================================
FILE: demos/bookstore/app/auth.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { css } from 'remix/component'
import * as s from 'remix/data-schema'
import * as f from 'remix/data-schema/form-data'
import { Database } from 'remix/data-table'
import { redirect } from 'remix/response/redirect'
import { routes } from './routes.ts'
import { passwordResetTokens, users } from './data/schema.ts'
import { Document } from './layout.tsx'
import { loadAuth } from './middleware/auth.ts'
import { render } from './utils/render.ts'
import { Session } from './utils/session.ts'
const textField = f.field(s.defaulted(s.string(), ''))
const loginSchema = f.object({
email: textField,
password: textField,
})
const registrationSchema = f.object({
name: textField,
email: textField,
password: textField,
})
const forgotPasswordSchema = f.object({
email: textField,
})
const resetPasswordSchema = f.object({
password: textField,
confirmPassword: textField,
})
export default {
middleware: [loadAuth()],
actions: {
login: {
actions: {
index({ get, url }) {
let session = get(Session)
let error = session.get('error')
let formAction = routes.auth.login.action.href(undefined, {
returnTo: url.searchParams.get('returnTo') ?? undefined,
})
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<h1>Login</h1>
{typeof error === 'string' ? (
<div class="alert alert-error" mix={[css({ marginBottom: '1.5rem' })]}>
{error}
</div>
) : null}
<form method="POST" action={formAction}>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required autoComplete="email" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
required
autoComplete="current-password"
/>
</div>
<button type="submit" class="btn">
Login
</button>
</form>
<p mix={[css({ marginTop: '1.5rem' })]}>
Don't have an account?{' '}
<a href={routes.auth.register.index.href()}>Register here</a>
</p>
<p>
<a href={routes.auth.forgotPassword.index.href()}>Forgot password?</a>
</p>
<div
mix={[
css({
marginTop: '2rem',
padding: '1rem',
background: '#f8f9fa',
borderRadius: '4px',
}),
]}
>
<p mix={[css({ fontSize: '0.9rem' })]}>
<strong>Demo Accounts:</strong>
</p>
<p mix={[css({ fontSize: '0.9rem' })]}>Admin: admin@bookstore.com / admin123</p>
<p mix={[css({ fontSize: '0.9rem' })]}>
Customer: customer@example.com / password123
</p>
</div>
</div>
</Document>,
)
},
async action({ get, url }) {
let db = get(Database)
let session = get(Session)
let formData = get(FormData)
let { email, password } = s.parse(loginSchema, formData)
let returnTo = url.searchParams.get('returnTo') ?? undefined
let normalizedEmail = normalizeEmail(email)
let user = await db.findOne(users, { where: { email: normalizedEmail } })
if (!user || user.password !== password) {
session.flash('error', 'Invalid email or password. Please try again.')
return redirect(routes.auth.login.index.href(undefined, { returnTo }))
}
session.regenerateId(true)
session.set('userId', user.id)
return redirect(returnTo ?? routes.account.index.href())
},
},
},
register: {
actions: {
index() {
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<h1>Register</h1>
<form method="POST" action={routes.auth.register.action.href()}>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required autoComplete="name" />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required autoComplete="email" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
required
autoComplete="new-password"
/>
</div>
<button type="submit" class="btn">
Register
</button>
</form>
<p mix={[css({ marginTop: '1.5rem' })]}>
Already have an account? <a href={routes.auth.login.index.href()}>Login here</a>
</p>
</div>
</Document>,
)
},
async action({ get }) {
let db = get(Database)
let session = get(Session)
let formData = get(FormData)
let { email, name, password } = s.parse(registrationSchema, formData)
let normalizedEmail = normalizeEmail(email)
// Check if user already exists
if (await db.findOne(users, { where: { email: normalizedEmail } })) {
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<div class="alert alert-error">An account with this email already exists.</div>
<p>
<a href={routes.auth.register.index.href()} class="btn">
Back to Register
</a>
<a
href={routes.auth.login.index.href()}
class="btn btn-secondary"
mix={[css({ marginLeft: '0.5rem' })]}
>
Login
</a>
</p>
</div>
</Document>,
{ status: 400 },
)
}
let user = await db.create(
users,
{
email: normalizedEmail,
password,
name,
},
{ returnRow: true },
)
session.set('userId', user.id)
return redirect(routes.account.index.href())
},
},
},
logout({ get }) {
let session = get(Session)
session.destroy()
return redirect(routes.home.href())
},
forgotPassword: {
actions: {
index() {
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<h1>Forgot Password</h1>
<p>Enter your email address and we'll send you a link to reset your password.</p>
<form method="POST" action={routes.auth.forgotPassword.action.href()}>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required autoComplete="email" />
</div>
<button type="submit" class="btn">
Send Reset Link
</button>
</form>
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.auth.login.index.href()}>Back to Login</a>
</p>
</div>
</Document>,
)
},
async action({ get }) {
let db = get(Database)
let formData = get(FormData)
let { email } = s.parse(forgotPasswordSchema, formData)
let normalizedEmail = normalizeEmail(email)
let user = await db.findOne(users, { where: { email: normalizedEmail } })
let token = undefined as string | undefined
if (user) {
token = Math.random().toString(36).substring(2, 15)
await db.create(passwordResetTokens, {
token,
user_id: user.id,
expires_at: Date.now() + 3600000,
})
}
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<div class="alert alert-success">Password reset link sent! Check your email.</div>
{token ? (
<div
mix={[
css({
marginTop: '1rem',
padding: '1rem',
background: '#f8f9fa',
borderRadius: '4px',
}),
]}
>
<p mix={[css({ fontSize: '0.9rem' })]}>
<strong>Demo Mode:</strong> Click the link below to reset your password
</p>
<p mix={[css({ marginTop: '0.5rem' })]}>
<a
href={routes.auth.resetPassword.index.href({ token })}
class="btn btn-secondary"
>
Reset Password
</a>
</p>
</div>
) : null}
<p mix={[css({ marginTop: '1.5rem' })]}>
<a href={routes.auth.login.index.href()} class="btn">
Back to Login
</a>
</p>
</div>
</Document>,
)
},
},
},
resetPassword: {
actions: {
index({ params, get }) {
let session = get(Session)
let token = params.token
let error = session.get('error')
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<h1>Reset Password</h1>
<p>Enter your new password below.</p>
{typeof error === 'string' ? (
<div class="alert alert-error" mix={[css({ marginBottom: '1.5rem' })]}>
{error}
</div>
) : null}
<form method="POST" action={routes.auth.resetPassword.action.href({ token })}>
<div class="form-group">
<label for="password">New Password</label>
<input
type="password"
id="password"
name="password"
required
autoComplete="new-password"
/>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input
type="password"
id="confirmPassword"
name="confirmPassword"
required
autoComplete="new-password"
/>
</div>
<button type="submit" class="btn">
Reset Password
</button>
</form>
</div>
</Document>,
)
},
async action({ get, params }) {
let db = get(Database)
let session = get(Session)
let formData = get(FormData)
let { confirmPassword, password } = s.parse(resetPasswordSchema, formData)
let token = params.token
if (!token) {
session.flash('error', 'Invalid or expired reset token.')
return redirect(routes.auth.forgotPassword.index.href())
}
if (password !== confirmPassword) {
session.flash('error', 'Passwords do not match.')
return redirect(routes.auth.resetPassword.index.href({ token }))
}
let tokenData = await db.find(passwordResetTokens, { token })
if (!tokenData || tokenData.expires_at < Date.now()) {
session.flash('error', 'Invalid or expired reset token.')
return redirect(routes.auth.resetPassword.index.href({ token }))
}
let user = await db.find(users, tokenData.user_id)
if (!user) {
session.flash('error', 'Invalid or expired reset token.')
return redirect(routes.auth.resetPassword.index.href({ token }))
}
await db.update(users, user.id, { password })
await db.delete(passwordResetTokens, { token })
return render(
<Document>
<div class="card" mix={[css({ maxWidth: '500px', margin: '2rem auto' })]}>
<div class="alert alert-success">
Password reset successfully! You can now login with your new password.
</div>
<p>
<a href={routes.auth.login.index.href()} class="btn">
Login
</a>
</p>
</div>
</Document>,
)
},
},
},
},
} satisfies Controller<typeof routes.auth>
function normalizeEmail(email: string): string {
return email.trim().toLowerCase()
}
================================================
FILE: demos/bookstore/app/books.test.ts
================================================
import * as assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { router } from './router.ts'
import { assertContains } from '../test/helpers.ts'
describe('books handlers', () => {
it('GET /books returns list of books', async () => {
let response = await router.fetch('https://remix.run/books')
assert.equal(response.status, 200)
let html = await response.text()
assertContains(html, 'Browse Books')
assertContains(html, 'Ash & Smoke')
assertContains(html, 'Heavy Metal Guitar Riffs')
assertContains(html, 'Three Ways to Change Your Life')
})
it('GET /books/:slug returns book details', async () => {
let response = await router.fetch('https://remix.run/books/bbq')
assert.equal(response.status, 200)
let html = await response.text()
assertContains(html, 'Ash & Smoke')
assertContains(html, 'Rusty Char-Broil')
assertContains(html, 'Add to Cart')
})
it('GET /books/:slug returns 404 for non-existent book', async () => {
let response = await router.fetch('https://remix.run/books/does-not-exist')
assert.equal(response.status, 404)
let html = await response.text()
assertContains(html, 'Book Not Found')
})
})
================================================
FILE: demos/bookstore/app/books.tsx
================================================
import type { Controller } from 'remix/fetch-router'
import { Frame, css } from 'remix/component'
import { routes } from './routes.ts'
import { Database, ilike } from 'remix/data-table'
import { books } from './data/schema.ts'
import { BookCard } from './components/book-card.tsx'
import { Layout } from './layout.tsx'
import { loadAuth } from './middleware/auth.ts'
import { render } from './utils/render.ts'
import { getCurrentCart } from './utils/context.ts'
import { ImageCarousel } from './assets/image-carousel.tsx'
export default {
middleware: [loadAuth()],
actions: {
async index({ get }) {
let db = get(Database)
let allBooks = await db.findMany(books, { orderBy: ['id', 'asc'] })
let genres = await db.query(books).select('genre').distinct().orderBy('genre', 'asc').all()
let cart = getCurrentCart()
return render(
<Layout>
<h1>Browse Books</h1>
<div class="card" mix={[css({ marginBottom: '2rem' })]}>
<form
action={routes.search.href()}
method="GET"
mix={[css({ display: 'flex', gap: '0.5rem' })]}
>
<input
type="search"
name="q"
placeholder="Search books by title, author, or description..."
mix={[css({ flex: 1, padding: '0.5rem' })]}
/>
<button type="submit" class="btn">
Search
</button>
</form>
</div>
<div class="card" mix={[css({ marginBottom: '2rem' })]}>
<h3>Browse by Genre</h3>
<div
mix={[css({ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginTop: '1rem' })]}
>
{genres.map((genreRow) => (
<a
href={routes.books.genre.href({ genre: genreRow.genre })}
class="btn btn-secondary"
>
{genreRow.genre}
</a>
))}
</div>
</div>
<div class="grid">
{allBooks.map((book) => {
let inCart = cart.items.some((item) => item.slug === book.slug)
return <BookCard book={book} inCart={inCart} />
})}
</div>
</Layout>,
)
},
async genre({ get, params }) {
let db = get(Database)
let genre = params.genre
let matchingBooks = await db.findMany(books, {
where: ilike('genre', genre),
orderBy: ['id', 'asc'],
})
if (matchingBooks.length === 0) {
return render(
<Layout>
<div class="card">
<h1>Genre Not Found</h1>
<p>No books found in the "{genre}" genre.</p>
<p mix={[css({ marginTop: '1rem' })]}>
<a href={routes.books.index.href()} class="btn">
Browse All Books
</a>
</p>
</div>
</Layout>,
{ status: 404 },
)
}
let cart = getCurrentCart()
return render(
<Layout>
<h1>{genre.charAt(0).toUpperCase() + genre.slice(1)} Books</h1>
<p mix={[css({ margin: '1rem 0' })]}>
<a href={routes.books.index.href()} class="btn btn-secondary">
View All Books
</a>
</p>
<div class="grid" mix={[css({ marginTop: '2rem' })]}>
{matchingBooks.map((book) => {
let inCart = cart.items.some((item) => item.slug === book.slug)
return <BookCard book={book} inCart={inCart} />
})}
</div>
</Layout>,
)
},
async show({ get, params }) {
let db = get(Database)
let book = await db.findOne(books, { where: { slug: params.slug } })
if (!book) {
return render(
<Layout>
<div class="card">
<h1>Book Not Found</h1>
</div>
</Layout>,
{ status: 404 },
)
}
let imageUrls = JSON.parse(book.image_urls) as string[]
return render(
<Layout>
<div mix={[css({ display: 'grid', gridTemplateColumns: '300px 1fr', gap: '2rem' })]}>
<div
mix={[
css({
height: '400px',
borderRadius: '8px',
boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
overflow: 'hidden',
}),
]}
>
<ImageCarousel images={imageUrls} />
</div>
<div class="card">
<h1>{book.title}</h1>
<p class="author" mix={[css({ fontSize: '1.2rem', margin: '0.5rem 0' })]}>
by {book.author}
</p>
<p mix={[css({ margin: '1rem 0' })]}>
<span class="badge badge-info">{book.genre}</span>
<span
class={`badge ${book.in_stock ? 'badge-success' : 'badge-warning'}`}
mix={[css({ marginLeft: '0.5rem' })]}
>
{book.in_stock ? 'In Stock' : 'Out of Stock'}
</span>
</p>
<p class="price" mix={[css({ fontSize: '2rem', margin: '1rem 0' })]}>
${book.price.toFixed(2)}
</p>
<p mix={[css({ margin: '1.5rem 0', lineHeight: 1.8 })]}>{book.description}</p>
<div
mix={[
css({
margin: '1.5rem 0',
padding: '1rem',
background: '#f8f9fa',
borderRadius: '4px',
}),
]}
>
<p>
<strong>ISBN:</strong> {book.isbn}
</p>
<p>
<strong>Published:</strong> {book.published_year}
</p>
</div>
{book.in_stock ? (
<div mix={[
gitextract_a33wb0o_/
├── .agents/
│ └── skills/
│ ├── add-package/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-change-file/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-demo/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── make-pr/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── publish-placeholder-package/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── supersede-pr/
│ │ ├── SKILL.md
│ │ ├── agents/
│ │ │ └── openai.yaml
│ │ ├── scripts/
│ │ │ └── close_superseded_pr.ts
│ │ └── tsconfig.json
│ ├── update-pr/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ ├── write-api-docs/
│ │ └── SKILL.md
│ └── write-readme/
│ ├── SKILL.md
│ └── agents/
│ └── openai.yaml
├── .github/
│ └── workflows/
│ ├── build.yaml
│ ├── check.yaml
│ ├── data-table-integration.yaml
│ ├── file-storage-integration.yaml
│ ├── format.yml
│ ├── generate-remix.yaml
│ ├── preview.yml
│ ├── publish.yaml
│ ├── release-pr.yaml
│ ├── session-integration.yaml
│ └── test.yaml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode/
│ ├── settings.json
│ └── task.json
├── AGENTS.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cspell.yml
├── decisions/
│ ├── 001-route-pattern-vs-url-pattern.md
│ └── 002-branching-and-releasing.md
├── demos/
│ ├── bookstore/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── account.test.ts
│ │ │ ├── account.tsx
│ │ │ ├── admin.books.test.ts
│ │ │ ├── admin.books.tsx
│ │ │ ├── admin.orders.tsx
│ │ │ ├── admin.test.ts
│ │ │ ├── admin.tsx
│ │ │ ├── admin.users.tsx
│ │ │ ├── assets/
│ │ │ │ ├── cart-button.tsx
│ │ │ │ ├── cart-items.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── image-carousel.tsx
│ │ │ ├── auth.test.ts
│ │ │ ├── auth.tsx
│ │ │ ├── books.test.ts
│ │ │ ├── books.tsx
│ │ │ ├── cart.test.ts
│ │ │ ├── cart.tsx
│ │ │ ├── checkout.test.ts
│ │ │ ├── checkout.tsx
│ │ │ ├── components/
│ │ │ │ ├── book-card.tsx
│ │ │ │ └── restful-form.tsx
│ │ │ ├── data/
│ │ │ │ ├── cart.ts
│ │ │ │ ├── schema.ts
│ │ │ │ ├── setup.test.ts
│ │ │ │ └── setup.ts
│ │ │ ├── fragments.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── marketing.test.ts
│ │ │ ├── marketing.tsx
│ │ │ ├── middleware/
│ │ │ │ ├── admin.ts
│ │ │ │ ├── auth.ts
│ │ │ │ └── database.ts
│ │ │ ├── router.test.ts
│ │ │ ├── router.ts
│ │ │ ├── routes.ts
│ │ │ ├── uploads.test.ts
│ │ │ ├── uploads.tsx
│ │ │ └── utils/
│ │ │ ├── context.ts
│ │ │ ├── ids.ts
│ │ │ ├── render.ts
│ │ │ ├── session.ts
│ │ │ └── uploads.ts
│ │ ├── data/
│ │ │ └── migrations/
│ │ │ └── 20260228090000_create_bookstore_schema.ts
│ │ ├── package.json
│ │ ├── public/
│ │ │ └── app.css
│ │ ├── server.ts
│ │ ├── test/
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── frame-navigation/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── dashboard-stat-grid.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── fake.tsx
│ │ │ ├── auth/
│ │ │ │ ├── controller.tsx
│ │ │ │ └── session.ts
│ │ │ ├── lib/
│ │ │ │ ├── Layout.tsx
│ │ │ │ └── NavLink.tsx
│ │ │ ├── main/
│ │ │ │ ├── account.tsx
│ │ │ │ ├── calendar.tsx
│ │ │ │ ├── controller.tsx
│ │ │ │ ├── courses.tsx
│ │ │ │ └── index.tsx
│ │ │ └── settings/
│ │ │ ├── controller.tsx
│ │ │ ├── grading.tsx
│ │ │ ├── index.tsx
│ │ │ ├── integrations.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── notifications.tsx
│ │ │ ├── privacy.tsx
│ │ │ └── profile.tsx
│ │ ├── config/
│ │ │ ├── render.tsx
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── server.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── frames/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── client-frame-example.tsx
│ │ │ │ ├── client-mounted-page-example.tsx
│ │ │ │ ├── counter.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── reload-scope.tsx
│ │ │ │ ├── reload-time.tsx
│ │ │ │ └── state-search-page.tsx
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── us-states.ts
│ │ ├── package.json
│ │ ├── server.ts
│ │ └── tsconfig.json
│ ├── sse/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── message-stream.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── router.test.ts
│ │ │ ├── router.tsx
│ │ │ ├── routes.ts
│ │ │ └── utils/
│ │ │ └── render.ts
│ │ ├── package.json
│ │ ├── server.ts
│ │ └── tsconfig.json
│ └── unpkg/
│ ├── README.md
│ ├── app/
│ │ ├── breadcrumb.ts
│ │ ├── directory.ts
│ │ ├── error.ts
│ │ ├── file-content.ts
│ │ ├── router.test.ts
│ │ ├── router.ts
│ │ ├── routes.ts
│ │ └── utils/
│ │ ├── cache.ts
│ │ ├── npm.test.ts
│ │ ├── npm.ts
│ │ ├── render.test.ts
│ │ └── render.ts
│ ├── package.json
│ ├── server.ts
│ ├── test/
│ │ ├── fixtures/
│ │ │ ├── is-number-7.0.0.tgz
│ │ │ └── is-number-metadata.json
│ │ └── mock-fetch.ts
│ └── tsconfig.json
├── docs/
│ ├── .gitignore
│ ├── package.json
│ ├── public/
│ │ └── docs.css
│ ├── src/
│ │ ├── client/
│ │ │ └── entry.tsx
│ │ ├── generate/
│ │ │ ├── documented-api.ts
│ │ │ ├── index.ts
│ │ │ ├── markdown.ts
│ │ │ ├── symbols.ts
│ │ │ ├── typedoc.ts
│ │ │ └── utils.ts
│ │ └── server/
│ │ ├── components.tsx
│ │ ├── index.ts
│ │ ├── markdown.ts
│ │ ├── prerender.ts
│ │ ├── router.tsx
│ │ └── routes.ts
│ └── tsconfig.json
├── eslint.config.js
├── package.json
├── packages/
│ ├── async-context-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── async-context.test.ts
│ │ │ └── async-context.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── component/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.01-add-mixin-system-and-core-helpers.md
│ │ │ ├── minor.02-add-press-and-keyboard-mixins.md
│ │ │ ├── minor.03-add-animation-mixins.md
│ │ │ ├── minor.04-remove-legacy-on-prop.md
│ │ │ ├── minor.05-remove-legacy-css-prop.md
│ │ │ ├── minor.06-remove-legacy-animate-prop.md
│ │ │ ├── minor.07-remove-legacy-connect-prop.md
│ │ │ ├── minor.08-interaction-package-removed.md
│ │ │ ├── minor.09-allow-single-mix-values.md
│ │ │ ├── minor.13-allow-remix-node-frame-content.md
│ │ │ ├── minor.frame-navigation-link-attributes.md
│ │ │ ├── minor.frame-navigation-runtime.md
│ │ │ ├── minor.remove-head-hoisting.md
│ │ │ ├── minor.resolve-frame-target.md
│ │ │ ├── minor.ssr-frame-src-context.md
│ │ │ ├── patch.10-preserve-live-dom-state.md
│ │ │ ├── patch.11-forward-client-entry-root-errors.md
│ │ │ ├── patch.defer-mixin-lifecycle-events.md
│ │ │ ├── patch.fix-adjacent-hydration-markers.md
│ │ │ ├── patch.fix-svg-classname-mapping.md
│ │ │ ├── patch.resolve-svg-link-targets.md
│ │ │ └── patch.skip-download-link-interception.md
│ │ ├── AGENTS.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── .gitignore
│ │ │ ├── frameworks/
│ │ │ │ ├── preact/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── preact-signals/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── react/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── remix/
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── shared.ts
│ │ │ │ ├── solid/
│ │ │ │ │ ├── build.js
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ └── styles.css
│ │ │ ├── package.json
│ │ │ ├── runner.ts
│ │ │ ├── server.ts
│ │ │ └── tsconfig.json
│ │ ├── demos/
│ │ │ ├── .gitignore
│ │ │ ├── animation/
│ │ │ │ ├── aspect-ratio.tsx
│ │ │ │ ├── bouncy-switch.tsx
│ │ │ │ ├── color-interpolation.tsx
│ │ │ │ ├── cube.tsx
│ │ │ │ ├── default-animate.tsx
│ │ │ │ ├── enter.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── exit.tsx
│ │ │ │ ├── flip-toggle.tsx
│ │ │ │ ├── hold-to-confirm.tsx
│ │ │ │ ├── html-content.tsx
│ │ │ │ ├── index.html
│ │ │ │ ├── interruptible-keyframes.tsx
│ │ │ │ ├── keyframes.tsx
│ │ │ │ ├── material-ripple.tsx
│ │ │ │ ├── mixin-presence-list.tsx
│ │ │ │ ├── mixin-reclaim.tsx
│ │ │ │ ├── multi-state-badge.tsx
│ │ │ │ ├── press.tsx
│ │ │ │ ├── reordering.tsx
│ │ │ │ ├── rolling-square.tsx
│ │ │ │ ├── rotate.tsx
│ │ │ │ ├── shared-layout.tsx
│ │ │ │ └── transition-options.tsx
│ │ │ ├── basic/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── controlled-uncontrolled-values/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── draggable/
│ │ │ │ ├── draggable.tsx
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── drummer/
│ │ │ │ ├── app.tsx
│ │ │ │ ├── components.tsx
│ │ │ │ ├── drummer.ts
│ │ │ │ ├── entry.tsx
│ │ │ │ ├── index.html
│ │ │ │ ├── tempo-interaction.tsx
│ │ │ │ └── voice-looper.ts
│ │ │ ├── keyed-list/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── package.json
│ │ │ ├── readme/
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ ├── server.ts
│ │ │ ├── spring/
│ │ │ │ ├── drag-release.ts
│ │ │ │ ├── entry.tsx
│ │ │ │ └── index.html
│ │ │ └── tsconfig.json
│ │ ├── docs/
│ │ │ ├── components.md
│ │ │ ├── composition.md
│ │ │ ├── context.md
│ │ │ ├── events.md
│ │ │ ├── frames.md
│ │ │ ├── getting-started.md
│ │ │ ├── handle.md
│ │ │ ├── hydration.md
│ │ │ ├── interactions.md
│ │ │ ├── patterns.md
│ │ │ ├── server-rendering.md
│ │ │ ├── spring.md
│ │ │ ├── styling.md
│ │ │ ├── testing.md
│ │ │ └── tween.md
│ │ ├── package.json
│ │ ├── skills/
│ │ │ ├── animate-elements/
│ │ │ │ └── SKILL.md
│ │ │ └── create-mixins/
│ │ │ └── SKILL.md
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── jsx-dev-runtime.ts
│ │ │ ├── jsx-runtime.ts
│ │ │ ├── lib/
│ │ │ │ ├── client-entries.ts
│ │ │ │ ├── component.ts
│ │ │ │ ├── create-element.ts
│ │ │ │ ├── diff-dom.ts
│ │ │ │ ├── diff-props.ts
│ │ │ │ ├── document-state.ts
│ │ │ │ ├── dom.ts
│ │ │ │ ├── error-event.ts
│ │ │ │ ├── event-listeners.ts
│ │ │ │ ├── frame.ts
│ │ │ │ ├── invariant.ts
│ │ │ │ ├── jsx.ts
│ │ │ │ ├── mixin.ts
│ │ │ │ ├── mixins/
│ │ │ │ │ ├── animate-layout-mixin.test.tsx
│ │ │ │ │ ├── animate-layout-mixin.tsx
│ │ │ │ │ ├── animate-mixins.test.tsx
│ │ │ │ │ ├── animate-mixins.tsx
│ │ │ │ │ ├── css-mixin.test.tsx
│ │ │ │ │ ├── css-mixin.tsx
│ │ │ │ │ ├── keys-mixin.test.tsx
│ │ │ │ │ ├── keys-mixin.tsx
│ │ │ │ │ ├── link-mixin.test.tsx
│ │ │ │ │ ├── link-mixin.tsx
│ │ │ │ │ ├── on-mixin.test.tsx
│ │ │ │ │ ├── on-mixin.tsx
│ │ │ │ │ ├── press-mixin.test.tsx
│ │ │ │ │ ├── press-mixin.tsx
│ │ │ │ │ ├── ref-mixin.test.tsx
│ │ │ │ │ └── ref-mixin.tsx
│ │ │ │ ├── navigation.ts
│ │ │ │ ├── reconcile.ts
│ │ │ │ ├── run.ts
│ │ │ │ ├── scheduler.ts
│ │ │ │ ├── spring.ts
│ │ │ │ ├── stream.ts
│ │ │ │ ├── style/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib/
│ │ │ │ │ ├── style.ts
│ │ │ │ │ └── stylesheet.ts
│ │ │ │ ├── svg-attributes.ts
│ │ │ │ ├── to-vnode.ts
│ │ │ │ ├── tween.ts
│ │ │ │ ├── typed-event-target.ts
│ │ │ │ ├── vdom.ts
│ │ │ │ └── vnode.ts
│ │ │ ├── server.ts
│ │ │ └── test/
│ │ │ ├── client-entry.test.tsx
│ │ │ ├── create-element.test.ts
│ │ │ ├── diff-dom.test.tsx
│ │ │ ├── document-state.test.ts
│ │ │ ├── event-listeners.test.tsx
│ │ │ ├── frame.test.tsx
│ │ │ ├── hydration.attributes.test.tsx
│ │ │ ├── hydration.boolean-attrs.test.tsx
│ │ │ ├── hydration.components.test.tsx
│ │ │ ├── hydration.css.test.tsx
│ │ │ ├── hydration.extra-nodes.test.tsx
│ │ │ ├── hydration.forms.test.tsx
│ │ │ ├── hydration.mismatch.test.tsx
│ │ │ ├── hydration.text.test.tsx
│ │ │ ├── hydration.void-elements.test.tsx
│ │ │ ├── jsx.test.tsx
│ │ │ ├── navigation.test.ts
│ │ │ ├── spring.test.ts
│ │ │ ├── stream.test.tsx
│ │ │ ├── style.test.ts
│ │ │ ├── stylesheet.test.ts
│ │ │ ├── utils.ts
│ │ │ ├── vdom.components.test.tsx
│ │ │ ├── vdom.connect.test.tsx
│ │ │ ├── vdom.context.test.tsx
│ │ │ ├── vdom.controlled-props.test.tsx
│ │ │ ├── vdom.dom-order.test.tsx
│ │ │ ├── vdom.elements-fragments.test.tsx
│ │ │ ├── vdom.errors.test.tsx
│ │ │ ├── vdom.events.test.tsx
│ │ │ ├── vdom.insert-remove.test.tsx
│ │ │ ├── vdom.keys.test.tsx
│ │ │ ├── vdom.mixins.test.tsx
│ │ │ ├── vdom.props.test.tsx
│ │ │ ├── vdom.range-root.test.tsx
│ │ │ ├── vdom.replacements.test.tsx
│ │ │ ├── vdom.scheduler.test.tsx
│ │ │ ├── vdom.signals.test.tsx
│ │ │ ├── vdom.svg.test.tsx
│ │ │ └── vdom.tasks.test.tsx
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── compression-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── global.d.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── compression.test.ts
│ │ │ └── compression.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cookie/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cookie-signing.ts
│ │ │ ├── cookie.test.ts
│ │ │ └── cookie.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cop-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cop.test.ts
│ │ │ └── cop.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── cors-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── cors.test.ts
│ │ │ └── cors.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── csrf-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.initial-release.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── csrf.test.ts
│ │ │ └── csrf.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-schema/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.form-data.md
│ │ │ └── patch.remove-unnecessary-as-const-from-enum.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── checks.ts
│ │ │ ├── coerce.ts
│ │ │ ├── form-data.ts
│ │ │ ├── index.ts
│ │ │ ├── lazy.ts
│ │ │ └── lib/
│ │ │ ├── checks.test.ts
│ │ │ ├── checks.ts
│ │ │ ├── coerce.test.ts
│ │ │ ├── coerce.ts
│ │ │ ├── form-data.test.ts
│ │ │ ├── form-data.ts
│ │ │ ├── lazy.test.ts
│ │ │ ├── lazy.ts
│ │ │ ├── parse.test.ts
│ │ │ ├── pipe.test.ts
│ │ │ ├── schema.test.ts
│ │ │ ├── schema.ts
│ │ │ └── variant.test.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.database-class-export.md
│ │ │ ├── minor.migration-system-features.md
│ │ │ ├── minor.operation-contract-split.md
│ │ │ ├── minor.query-object-api.md
│ │ │ ├── minor.sql-root-api.md
│ │ │ ├── minor.table-column-cutover.md
│ │ │ └── minor.table-lifecycle-callbacks.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── adapter.ts
│ │ │ │ ├── column.ts
│ │ │ │ ├── database/
│ │ │ │ │ ├── execution-context.ts
│ │ │ │ │ ├── helpers.ts
│ │ │ │ │ ├── query-execution.ts
│ │ │ │ │ ├── relations.ts
│ │ │ │ │ └── write-lifecycle.ts
│ │ │ │ ├── database.test.ts
│ │ │ │ ├── database.ts
│ │ │ │ ├── errors.test.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── inflection.test.ts
│ │ │ │ ├── inflection.ts
│ │ │ │ ├── migrations/
│ │ │ │ │ ├── filename.ts
│ │ │ │ │ ├── helpers.ts
│ │ │ │ │ ├── journal-store.ts
│ │ │ │ │ ├── registry.ts
│ │ │ │ │ ├── runner.ts
│ │ │ │ │ └── schema-api.ts
│ │ │ │ ├── migrations-node.test.ts
│ │ │ │ ├── migrations-node.ts
│ │ │ │ ├── migrations.test.ts
│ │ │ │ ├── migrations.ts
│ │ │ │ ├── operators.test.ts
│ │ │ │ ├── operators.ts
│ │ │ │ ├── query.ts
│ │ │ │ ├── references.test.ts
│ │ │ │ ├── references.ts
│ │ │ │ ├── sql-helpers.ts
│ │ │ │ ├── sql.test.ts
│ │ │ │ ├── sql.ts
│ │ │ │ ├── table.test.ts
│ │ │ │ ├── table.ts
│ │ │ │ ├── type-safety.test.ts
│ │ │ │ └── types.ts
│ │ │ ├── migrations/
│ │ │ │ └── node.ts
│ │ │ ├── migrations.ts
│ │ │ ├── operators.ts
│ │ │ └── sql-helpers.ts
│ │ ├── test/
│ │ │ ├── adapter-integration-contract.ts
│ │ │ ├── adapter-integration-schema.ts
│ │ │ ├── sqlite-adapter.ts
│ │ │ └── sqlite-test-database.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-mysql/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-postgres/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── data-table-sqlite/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.ddl-migration-contract.md
│ │ │ └── minor.introspection-migration-transaction-tokens.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── adapter.integration.test.ts
│ │ │ ├── adapter.test.ts
│ │ │ ├── adapter.ts
│ │ │ ├── sql-compiler.test.ts
│ │ │ └── sql-compiler.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fetch-proxy/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fetch-proxy.test.ts
│ │ │ └── fetch-proxy.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fetch-router/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.request-context-storage-methods.md
│ │ │ ├── minor.simplify-controller-shape.md
│ │ │ └── patch.optional-action-middleware.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ ├── bun/
│ │ │ │ ├── README.md
│ │ │ │ ├── app/
│ │ │ │ │ ├── data.ts
│ │ │ │ │ ├── router.ts
│ │ │ │ │ └── routes.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── package.json
│ │ │ │ └── tsconfig.json
│ │ │ ├── cf-workers/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── app/
│ │ │ │ │ ├── data.ts
│ │ │ │ │ ├── router.ts
│ │ │ │ │ └── routes.ts
│ │ │ │ ├── migrations/
│ │ │ │ │ └── 0001_initial.sql
│ │ │ │ ├── package.json
│ │ │ │ ├── tsconfig.json
│ │ │ │ ├── worker-configuration.d.ts
│ │ │ │ ├── worker.ts
│ │ │ │ └── wrangler.jsonc
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── app/
│ │ │ │ ├── data.ts
│ │ │ │ ├── router.ts
│ │ │ │ └── routes.ts
│ │ │ ├── package.json
│ │ │ ├── server.ts
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── controller.ts
│ │ │ │ ├── middleware.test.ts
│ │ │ │ ├── middleware.ts
│ │ │ │ ├── request-abort.test.ts
│ │ │ │ ├── request-abort.ts
│ │ │ │ ├── request-context.test.ts
│ │ │ │ ├── request-context.ts
│ │ │ │ ├── request-methods.ts
│ │ │ │ ├── route-helpers/
│ │ │ │ │ ├── form.test.ts
│ │ │ │ │ ├── form.ts
│ │ │ │ │ ├── method.test.ts
│ │ │ │ │ ├── method.ts
│ │ │ │ │ ├── resource.test.ts
│ │ │ │ │ ├── resource.ts
│ │ │ │ │ ├── resources.test.ts
│ │ │ │ │ └── resources.ts
│ │ │ │ ├── route-map.test.ts
│ │ │ │ ├── route-map.ts
│ │ │ │ ├── router-abort.test.ts
│ │ │ │ ├── router.test.ts
│ │ │ │ ├── router.ts
│ │ │ │ └── type-utils.ts
│ │ │ └── routes.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── file-storage/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── fs.ts
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── backends/
│ │ │ │ │ ├── fs.test.ts
│ │ │ │ │ ├── fs.ts
│ │ │ │ │ ├── memory.test.ts
│ │ │ │ │ └── memory.ts
│ │ │ │ └── file-storage.ts
│ │ │ └── memory.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── file-storage-s3/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── s3.integration.test.ts
│ │ │ ├── s3.test.ts
│ │ │ └── s3.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── form-data-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.context-form-data-key.md
│ │ │ └── patch.no-op-if-already-parsed.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── form-data.test.ts
│ │ │ └── form-data.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── form-data-parser/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.aggregate-multipart-limits.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── form-data.test.ts
│ │ │ └── form-data.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── fs/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fs.test.ts
│ │ │ └── fs.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── headers/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── accept-encoding.test.ts
│ │ │ ├── accept-encoding.ts
│ │ │ ├── accept-language.test.ts
│ │ │ ├── accept-language.ts
│ │ │ ├── accept.test.ts
│ │ │ ├── accept.ts
│ │ │ ├── cache-control.test.ts
│ │ │ ├── cache-control.ts
│ │ │ ├── content-disposition.test.ts
│ │ │ ├── content-disposition.ts
│ │ │ ├── content-range.test.ts
│ │ │ ├── content-range.ts
│ │ │ ├── content-type.test.ts
│ │ │ ├── content-type.ts
│ │ │ ├── cookie.test.ts
│ │ │ ├── cookie.ts
│ │ │ ├── header-names.test.ts
│ │ │ ├── header-names.ts
│ │ │ ├── header-value.ts
│ │ │ ├── if-match.test.ts
│ │ │ ├── if-match.ts
│ │ │ ├── if-none-match.test.ts
│ │ │ ├── if-none-match.ts
│ │ │ ├── if-range.test.ts
│ │ │ ├── if-range.ts
│ │ │ ├── param-values.test.ts
│ │ │ ├── param-values.ts
│ │ │ ├── range.test.ts
│ │ │ ├── range.ts
│ │ │ ├── raw-headers.test.ts
│ │ │ ├── raw-headers.ts
│ │ │ ├── set-cookie.test.ts
│ │ │ ├── set-cookie.ts
│ │ │ ├── utils.ts
│ │ │ ├── vary.test.ts
│ │ │ └── vary.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── html-template/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── safe-html.test.ts
│ │ │ └── safe-html.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lazy-file/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── globals.ts
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── byte-range.test.ts
│ │ │ ├── byte-range.ts
│ │ │ ├── lazy-file.test.ts
│ │ │ └── lazy-file.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── logger-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── logger.test.ts
│ │ │ └── logger.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── method-override-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── method-override.test.ts
│ │ │ └── method-override.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── mime/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ ├── codegen.test.ts
│ │ │ └── codegen.ts
│ │ ├── src/
│ │ │ ├── generated/
│ │ │ │ ├── compressible-mime-types.ts
│ │ │ │ └── mime-types.ts
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── define-mime-type.test.ts
│ │ │ ├── define-mime-type.ts
│ │ │ ├── detect-content-type.test.ts
│ │ │ ├── detect-content-type.ts
│ │ │ ├── detect-mime-type.test.ts
│ │ │ ├── detect-mime-type.ts
│ │ │ ├── is-compressible-mime-type.test.ts
│ │ │ ├── is-compressible-mime-type.ts
│ │ │ ├── mime-type-to-content-type.test.ts
│ │ │ └── mime-type-to-content-type.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── multipart-parser/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.aggregate-multipart-limits.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── messages.ts
│ │ │ ├── package.json
│ │ │ ├── parsers/
│ │ │ │ ├── busboy.ts
│ │ │ │ ├── fastify-busboy.ts
│ │ │ │ ├── multipart-parser.ts
│ │ │ │ └── multipasta.ts
│ │ │ ├── runner.ts
│ │ │ └── utils.ts
│ │ ├── demos/
│ │ │ ├── bun/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── server.ts
│ │ │ │ └── tsconfig.json
│ │ │ ├── cf-workers/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── tsconfig.json
│ │ │ │ ├── worker-configuration.d.ts
│ │ │ │ └── wrangler.toml
│ │ │ ├── deno/
│ │ │ │ ├── README.md
│ │ │ │ ├── main.ts
│ │ │ │ └── package.json
│ │ │ └── node/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── buffer-search.test.ts
│ │ │ │ ├── buffer-search.ts
│ │ │ │ ├── multipart-request.test.ts
│ │ │ │ ├── multipart-request.ts
│ │ │ │ ├── multipart.node.test.ts
│ │ │ │ ├── multipart.node.ts
│ │ │ │ ├── multipart.test.ts
│ │ │ │ ├── multipart.ts
│ │ │ │ └── read-stream.ts
│ │ │ └── node.ts
│ │ ├── test/
│ │ │ ├── utils.node.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── node-fetch-server/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── package.json
│ │ │ ├── runner.sh
│ │ │ └── servers/
│ │ │ ├── express.ts
│ │ │ ├── node-fetch-server.ts
│ │ │ └── node-http.ts
│ │ ├── demos/
│ │ │ └── http2/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.crt
│ │ │ ├── server.csr
│ │ │ ├── server.js
│ │ │ ├── server.key
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── fetch-handler.ts
│ │ │ ├── read-stream.ts
│ │ │ ├── request-listener.test.ts
│ │ │ └── request-listener.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── remix/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── config.json
│ │ │ ├── minor.remix.add-cors-middleware-export.md
│ │ │ ├── minor.remix.component-exports.md
│ │ │ ├── minor.remix.update-exports.md
│ │ │ └── minor.request-protection-middlewares.md
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── async-context-middleware.ts
│ │ │ ├── component/
│ │ │ │ ├── jsx-dev-runtime.ts
│ │ │ │ ├── jsx-runtime.ts
│ │ │ │ └── server.ts
│ │ │ ├── component.ts
│ │ │ ├── compression-middleware.ts
│ │ │ ├── cookie.ts
│ │ │ ├── cop-middleware.ts
│ │ │ ├── cors-middleware.ts
│ │ │ ├── csrf-middleware.ts
│ │ │ ├── data-schema/
│ │ │ │ ├── checks.ts
│ │ │ │ ├── coerce.ts
│ │ │ │ ├── form-data.ts
│ │ │ │ └── lazy.ts
│ │ │ ├── data-schema.ts
│ │ │ ├── data-table/
│ │ │ │ ├── migrations/
│ │ │ │ │ └── node.ts
│ │ │ │ ├── migrations.ts
│ │ │ │ ├── operators.ts
│ │ │ │ └── sql-helpers.ts
│ │ │ ├── data-table-mysql.ts
│ │ │ ├── data-table-postgres.ts
│ │ │ ├── data-table-sqlite.ts
│ │ │ ├── data-table.ts
│ │ │ ├── fetch-proxy.ts
│ │ │ ├── fetch-router/
│ │ │ │ └── routes.ts
│ │ │ ├── fetch-router.ts
│ │ │ ├── file-storage/
│ │ │ │ ├── fs.ts
│ │ │ │ └── memory.ts
│ │ │ ├── file-storage-s3.ts
│ │ │ ├── file-storage.ts
│ │ │ ├── form-data-middleware.ts
│ │ │ ├── form-data-parser.ts
│ │ │ ├── fs.ts
│ │ │ ├── headers.ts
│ │ │ ├── html-template.ts
│ │ │ ├── lazy-file.ts
│ │ │ ├── logger-middleware.ts
│ │ │ ├── method-override-middleware.ts
│ │ │ ├── mime.ts
│ │ │ ├── multipart-parser/
│ │ │ │ └── node.ts
│ │ │ ├── multipart-parser.ts
│ │ │ ├── node-fetch-server.ts
│ │ │ ├── response/
│ │ │ │ ├── compress.ts
│ │ │ │ ├── file.ts
│ │ │ │ ├── html.ts
│ │ │ │ └── redirect.ts
│ │ │ ├── route-pattern/
│ │ │ │ └── specificity.ts
│ │ │ ├── route-pattern.ts
│ │ │ ├── session/
│ │ │ │ ├── cookie-storage.ts
│ │ │ │ ├── fs-storage.ts
│ │ │ │ └── memory-storage.ts
│ │ │ ├── session-middleware.ts
│ │ │ ├── session-storage-memcache.ts
│ │ │ ├── session-storage-redis.ts
│ │ │ ├── session.ts
│ │ │ ├── static-middleware.ts
│ │ │ └── tar-parser.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── response/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── compress.ts
│ │ │ ├── file.ts
│ │ │ ├── html.ts
│ │ │ ├── lib/
│ │ │ │ ├── compress.test.ts
│ │ │ │ ├── compress.ts
│ │ │ │ ├── file.test.ts
│ │ │ │ ├── file.ts
│ │ │ │ ├── html.test.ts
│ │ │ │ ├── html.ts
│ │ │ │ ├── redirect.test.ts
│ │ │ │ └── redirect.ts
│ │ │ └── redirect.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── route-pattern/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ ├── minor.readonly-ast.md
│ │ │ └── patch.type-inference-perf.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── patterns/
│ │ │ │ ├── mediarss.ts
│ │ │ │ └── shopify.ts
│ │ │ ├── src/
│ │ │ │ ├── comparison.bench.ts
│ │ │ │ ├── href.bench.ts
│ │ │ │ ├── pathological.bench.ts
│ │ │ │ ├── shopify.bench.ts
│ │ │ │ └── simple.bench.ts
│ │ │ └── types/
│ │ │ ├── href.ts
│ │ │ ├── join.ts
│ │ │ ├── match.ts
│ │ │ ├── new.ts
│ │ │ └── params.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── array-matcher.ts
│ │ │ │ ├── matcher.test.ts
│ │ │ │ ├── matcher.ts
│ │ │ │ ├── regexp.ts
│ │ │ │ ├── route-pattern/
│ │ │ │ │ ├── AGENTS.md
│ │ │ │ │ ├── href.test.ts
│ │ │ │ │ ├── href.ts
│ │ │ │ │ ├── join.ts
│ │ │ │ │ ├── match.ts
│ │ │ │ │ ├── params.test.ts
│ │ │ │ │ ├── params.ts
│ │ │ │ │ ├── parse.test.ts
│ │ │ │ │ ├── parse.ts
│ │ │ │ │ ├── part-pattern.test.ts
│ │ │ │ │ ├── part-pattern.ts
│ │ │ │ │ ├── serialize.ts
│ │ │ │ │ ├── split.test.ts
│ │ │ │ │ └── split.ts
│ │ │ │ ├── route-pattern.test.ts
│ │ │ │ ├── route-pattern.ts
│ │ │ │ ├── specificity.test.ts
│ │ │ │ ├── specificity.ts
│ │ │ │ ├── trie-matcher/
│ │ │ │ │ ├── variant.test.ts
│ │ │ │ │ └── variant.ts
│ │ │ │ ├── trie-matcher.ts
│ │ │ │ ├── types/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── join.test.ts
│ │ │ │ │ ├── join.ts
│ │ │ │ │ ├── parse.test.ts
│ │ │ │ │ ├── parse.ts
│ │ │ │ │ ├── split.test.ts
│ │ │ │ │ ├── split.ts
│ │ │ │ │ ├── stringify.test.ts
│ │ │ │ │ ├── stringify.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ └── unreachable.ts
│ │ │ └── specificity.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── cookie-storage.ts
│ │ │ ├── fs-storage.ts
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── session-storage/
│ │ │ │ │ ├── cookie.test.ts
│ │ │ │ │ ├── cookie.ts
│ │ │ │ │ ├── fs.test.ts
│ │ │ │ │ ├── fs.ts
│ │ │ │ │ ├── memory.test.ts
│ │ │ │ │ └── memory.ts
│ │ │ │ ├── session-storage.ts
│ │ │ │ ├── session.test.ts
│ │ │ │ └── session.ts
│ │ │ └── memory-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-middleware/
│ │ ├── .changes/
│ │ │ ├── README.md
│ │ │ └── minor.session-context-key.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── session.test.ts
│ │ │ └── session.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-storage-memcache/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── memcache-client.test.ts
│ │ │ ├── memcache-client.ts
│ │ │ ├── memcache-storage.integration.test.ts
│ │ │ ├── memcache-storage.test.ts
│ │ │ └── memcache-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── session-storage-redis/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── redis-storage.integration.test.ts
│ │ │ ├── redis-storage.test.ts
│ │ │ └── redis-storage.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── static-middleware/
│ │ ├── .changes/
│ │ │ └── README.md
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── demos/
│ │ │ └── list-files/
│ │ │ ├── README.md
│ │ │ ├── package.json
│ │ │ ├── server.js
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── directory-listing.ts
│ │ │ ├── static.test.ts
│ │ │ └── static.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── tar-parser/
│ ├── .changes/
│ │ └── README.md
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── bench/
│ │ ├── package.json
│ │ ├── parsers/
│ │ │ ├── node-tar.ts
│ │ │ ├── tar-parser.ts
│ │ │ └── tar-stream.ts
│ │ └── runner.ts
│ ├── package.json
│ ├── src/
│ │ ├── globals.ts
│ │ ├── index.ts
│ │ └── lib/
│ │ ├── read-stream.ts
│ │ ├── tar.test.ts
│ │ ├── tar.ts
│ │ └── utils.ts
│ ├── test/
│ │ ├── fixtures/
│ │ │ ├── express-4.21.1.tgz
│ │ │ ├── lodash-4.17.21.tgz
│ │ │ └── npm-11.0.0.tgz
│ │ └── utils.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── pnpm-workspace.yaml
└── scripts/
├── changes-preview.ts
├── changes-validate.ts
├── changes-version.ts
├── detect-changed-packages.ts
├── generate-remix.ts
├── package.json
├── pr-preview.ts
├── publish.ts
├── release-pr.ts
├── setup-installable-branch.ts
├── tsconfig.json
└── utils/
├── changes.test.ts
├── changes.ts
├── color.ts
├── fs.ts
├── git.test.ts
├── git.ts
├── github.ts
├── packages.ts
├── process.ts
├── release-pr.test.ts
├── release-pr.ts
└── semver.ts
Showing preview only (394K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4616 symbols across 465 files)
FILE: .agents/skills/supersede-pr/scripts/close_superseded_pr.ts
type ParsedArgs (line 5) | type ParsedArgs = {
function main (line 12) | function main(): void {
function parseArgs (line 87) | function parseArgs(argv: string[]): ParsedArgs {
function printUsage (line 126) | function printUsage(): void {
function ensureNumericPrNumber (line 137) | function ensureNumericPrNumber(value: string, label: string): void {
function ghCapture (line 143) | function ghCapture(args: string[]): string {
function ghInherit (line 153) | function ghInherit(args: string[]): void {
function fail (line 160) | function fail(message: string): never {
FILE: demos/bookstore/app/account.tsx
method index (line 27) | index() {
method index (line 77) | index() {
method update (line 122) | async update({ get }) {
method index (line 143) | async index({ get }) {
method show (line 206) | async show({ get, params }) {
FILE: demos/bookstore/app/admin.books.tsx
method index (line 40) | async index({ get }) {
method show (line 116) | async show({ get, params }) {
method new (line 185) | new() {
method create (line 268) | async create({ get }) {
method edit (line 291) | async edit({ get, params }) {
method update (line 418) | async update({ get, params }) {
method destroy (line 450) | async destroy({ get, params }) {
FILE: demos/bookstore/app/admin.orders.tsx
method index (line 13) | async index({ get }) {
method show (line 70) | async show({ get, params }) {
FILE: demos/bookstore/app/admin.tsx
method index (line 17) | index() {
FILE: demos/bookstore/app/admin.users.tsx
method index (line 28) | async index({ get }) {
method show (line 98) | async show({ get, params }) {
method edit (line 152) | async edit({ get, params }) {
method update (line 215) | async update({ get, params }) {
method destroy (line 233) | async destroy({ get, params }) {
FILE: demos/bookstore/app/assets/cart-items.tsx
type CartItem (line 7) | type CartItem = {
type CartItemsProps (line 15) | type CartItemsProps = {
type PendingAction (line 21) | type PendingAction = {
FILE: demos/bookstore/app/assets/entry.tsx
method loadModule (line 4) | async loadModule(moduleUrl: string, exportName: string) {
method resolveFrame (line 12) | async resolveFrame(src, signal) {
FILE: demos/bookstore/app/auth.tsx
method index (line 38) | index({ get, url }) {
method action (line 109) | async action({ get, url }) {
method index (line 133) | index() {
method action (line 173) | async action({ get }) {
method logout (line 221) | logout({ get }) {
method index (line 229) | index() {
method action (line 255) | async action({ get }) {
method index (line 316) | index({ params, get }) {
method action (line 365) | async action({ get, params }) {
function normalizeEmail (line 418) | function normalizeEmail(email: string): string {
FILE: demos/bookstore/app/books.tsx
method index (line 18) | async index({ get }) {
method genre (line 72) | async genre({ get, params }) {
method show (line 118) | async show({ get, params }) {
FILE: demos/bookstore/app/cart.tsx
method index (line 38) | index() {
method add (line 52) | async add({ get }) {
method update (line 80) | async update({ get }) {
method remove (line 104) | async remove({ get }) {
function toggleCart (line 133) | async function toggleCart({ get }: RequestContext) {
FILE: demos/bookstore/app/checkout.tsx
method index (line 29) | index() {
method action (line 124) | async action({ get }) {
method confirmation (line 177) | async confirmation({ get, params }) {
FILE: demos/bookstore/app/components/book-card.tsx
type BookCardProps (line 5) | interface BookCardProps {
function BookCard (line 10) | function BookCard() {
FILE: demos/bookstore/app/components/restful-form.tsx
type RestfulFormProps (line 3) | interface RestfulFormProps extends Props<'form'> {
function RestfulForm (line 18) | function RestfulForm() {
FILE: demos/bookstore/app/data/cart.ts
type CartItem (line 1) | interface CartItem {
type Cart (line 9) | interface Cart {
function isCart (line 13) | function isCart(value: unknown): value is Cart {
function getCart (line 19) | function getCart(value: unknown): Cart {
function addToCart (line 23) | function addToCart(
function updateCartItem (line 41) | function updateCartItem(cart: Cart, bookId: number, quantity: number): C...
function removeFromCart (line 55) | function removeFromCart(cart: Cart, bookId: number): Cart {
function clearCart (line 60) | function clearCart(cart: Cart): Cart {
function getCartTotal (line 64) | function getCartTotal(cart: Cart): number {
FILE: demos/bookstore/app/data/schema.ts
method beforeWrite (line 20) | beforeWrite({ value }) {
method validate (line 53) | validate({ operation, value }) {
method afterRead (line 90) | afterRead({ value }) {
method beforeWrite (line 114) | beforeWrite({ operation, value }) {
method validate (line 139) | validate({ operation, value }) {
method afterRead (line 172) | afterRead({ value }) {
method beforeWrite (line 204) | beforeWrite({ operation, value }) {
method validate (line 217) | validate({ value }) {
method beforeWrite (line 248) | beforeWrite({ value }) {
method validate (line 257) | validate({ value }) {
type Book (line 297) | type Book = TableRow<typeof books>
type User (line 298) | type User = TableRow<typeof users>
type Order (line 299) | type Order = TableRowWith<typeof orders, { items: OrderItem[] }>
type OrderItem (line 300) | type OrderItem = TableRowWith<
function normalizeEmail (line 305) | function normalizeEmail(email: string): string {
function normalizeText (line 309) | function normalizeText(value: string): string {
function normalizeSlug (line 313) | function normalizeSlug(value: string): string {
function isValidEmail (line 322) | function isValidEmail(email: string): boolean {
function isJsonObject (line 326) | function isJsonObject(value: string): boolean {
FILE: demos/bookstore/app/data/setup.test.ts
function getRows (line 9) | function getRows(result: { rows?: Record<string, unknown>[] }): Record<s...
function readRowString (line 13) | function readRowString(row: Record<string, unknown>, key: string): string {
function readRowCount (line 23) | function readRowCount(row: Record<string, unknown>, key: string): number {
FILE: demos/bookstore/app/data/setup.ts
function initializeBookstoreDatabase (line 29) | async function initializeBookstoreDatabase(): Promise<void> {
function initialize (line 37) | async function initialize(): Promise<void> {
function getDatabaseFilePath (line 180) | function getDatabaseFilePath(): string {
FILE: demos/bookstore/app/fragments.tsx
method cartButton (line 18) | async cartButton({ get, params }) {
method cartItems (line 33) | cartItems() {
FILE: demos/bookstore/app/layout.tsx
function Document (line 6) | function Document() {
function Layout (line 21) | function Layout() {
FILE: demos/bookstore/app/marketing.tsx
method action (line 13) | async action({ get }) {
method action (line 55) | action() {
method index (line 116) | index() {
method action (line 150) | async action() {
method action (line 170) | async action({ get, url }) {
FILE: demos/bookstore/app/middleware/admin.ts
function requireAdmin (line 10) | function requireAdmin(): Middleware {
FILE: demos/bookstore/app/middleware/auth.ts
function loadAuth (line 17) | function loadAuth(): Middleware {
type RequireAuthOptions (line 32) | interface RequireAuthOptions {
function requireAuth (line 45) | function requireAuth(options?: RequireAuthOptions): Middleware {
FILE: demos/bookstore/app/middleware/database.ts
function loadDatabase (line 6) | function loadDatabase(): Middleware {
FILE: demos/bookstore/app/utils/context.ts
function getCurrentUser (line 15) | function getCurrentUser(): User {
function getCurrentUserSafely (line 23) | function getCurrentUserSafely(): User | null {
function setCurrentUser (line 34) | function setCurrentUser(user: User): void {
function getCurrentCart (line 41) | function getCurrentCart(): Cart {
FILE: demos/bookstore/app/utils/ids.ts
function parseId (line 1) | function parseId(value: unknown): number | undefined {
FILE: demos/bookstore/app/utils/render.ts
function render (line 6) | function render(node: RemixNode, init?: ResponseInit) {
function resolveFrame (line 26) | async function resolveFrame(router: Router, request: Request, src: strin...
function renderFragment (line 52) | function renderFragment(node: RemixNode, init?: ResponseInit) {
FILE: demos/bookstore/app/utils/uploads.ts
function uploadHandler (line 14) | async function uploadHandler(file: FileUpload): Promise<string> {
FILE: demos/bookstore/data/migrations/20260228090000_create_bookstore_schema.ts
method up (line 5) | async up({ schema }) {
method down (line 97) | async down({ schema }) {
FILE: demos/bookstore/server.ts
function shutdown (line 30) | function shutdown() {
FILE: demos/bookstore/test/helpers.ts
function getCookie (line 6) | function getCookie(response: Response, name: string): string | null {
function getSessionCookie (line 22) | function getSessionCookie(response: Response): string | null {
function requestWithSession (line 29) | function requestWithSession(
function assertContains (line 48) | function assertContains(html: string, text: string): void {
function assertNotContains (line 57) | function assertNotContains(html: string, text: string): void {
function login (line 66) | async function login(router: any, email: string, password: string): Prom...
function loginAsAdmin (line 84) | function loginAsAdmin(router: any): Promise<string> {
function loginAsCustomer (line 91) | function loginAsCustomer(router: any): Promise<string> {
FILE: demos/frame-navigation/app/assets/dashboard-stat-grid.tsx
type StatCard (line 3) | type StatCard = {
type DashboardStatGridProps (line 9) | type DashboardStatGridProps = {
FILE: demos/frame-navigation/app/assets/entry.tsx
method loadModule (line 7) | async loadModule(moduleUrl, exportName) {
method resolveFrame (line 15) | async resolveFrame(src, signal, target) {
function resolveFrameResponse (line 20) | async function resolveFrameResponse(
function fadeOutBody (line 88) | async function fadeOutBody() {
type ErrorCardProps (line 101) | type ErrorCardProps = {
function ErrorCard (line 109) | function ErrorCard() {
FILE: demos/frame-navigation/app/auth/controller.tsx
method index (line 13) | async index() {
method action (line 47) | async action() {
method logout (line 56) | async logout() {
FILE: demos/frame-navigation/app/auth/session.ts
function hasAuthCookie (line 10) | async function hasAuthCookie(cookieHeader: string | null) {
function isAuthenticated (line 15) | async function isAuthenticated() {
FILE: demos/frame-navigation/app/lib/Layout.tsx
type MainNavItem (line 7) | type MainNavItem = 'dashboard' | 'courses' | 'calendar' | 'account' | 's...
type LayoutProps (line 9) | type LayoutProps = {
function Layout (line 23) | function Layout() {
FILE: demos/frame-navigation/app/lib/NavLink.tsx
type NavLinkProps (line 4) | type NavLinkProps = {
function NavLink (line 12) | function NavLink() {
FILE: demos/frame-navigation/app/main/account.tsx
function MainAccountPage (line 3) | function MainAccountPage() {
FILE: demos/frame-navigation/app/main/calendar.tsx
function MainCalendarPage (line 3) | function MainCalendarPage() {
FILE: demos/frame-navigation/app/main/controller.tsx
function renderMainPage (line 12) | function renderMainPage(title: string, activeNav: MainNavItem, content: ...
method index (line 22) | index() {
method courses (line 25) | courses() {
method calendar (line 28) | calendar() {
method account (line 31) | account() {
FILE: demos/frame-navigation/app/main/courses.tsx
function MainCoursesPage (line 3) | function MainCoursesPage() {
FILE: demos/frame-navigation/app/main/index.tsx
function MainIndexPage (line 24) | function MainIndexPage() {
FILE: demos/frame-navigation/app/settings/controller.tsx
method index (line 20) | index() {
method profile (line 23) | profile() {
method notifications (line 26) | notifications() {
method privacy (line 29) | privacy() {
method grading (line 32) | grading() {
method integrations (line 35) | integrations() {
type SettingsPageProps (line 41) | type SettingsPageProps = {
function renderSettingsPage (line 46) | function renderSettingsPage(activeItem: SettingsNavItem, content: RemixN...
function SettingsShellOrFragment (line 53) | function SettingsShellOrFragment() {
function isFrameRequest (line 67) | function isFrameRequest() {
FILE: demos/frame-navigation/app/settings/grading.tsx
function Grading (line 3) | function Grading() {
FILE: demos/frame-navigation/app/settings/index.tsx
function Index (line 3) | function Index() {
FILE: demos/frame-navigation/app/settings/integrations.tsx
function Integrations (line 3) | function Integrations() {
FILE: demos/frame-navigation/app/settings/layout.tsx
type SettingsNavItem (line 7) | type SettingsNavItem =
type SettingsLayoutProps (line 15) | type SettingsLayoutProps = {
function SettingsLayout (line 53) | function SettingsLayout() {
FILE: demos/frame-navigation/app/settings/notifications.tsx
function Notifications (line 3) | function Notifications() {
FILE: demos/frame-navigation/app/settings/privacy.tsx
function Privacy (line 3) | function Privacy() {
FILE: demos/frame-navigation/app/settings/profile.tsx
function Profile (line 3) | function Profile() {
FILE: demos/frame-navigation/config/render.tsx
function render (line 6) | function render(node: RemixNode, init?: ResponseInit) {
function resolveFrame (line 27) | async function resolveFrame(
function followFrameRedirects (line 55) | async function followFrameRedirects(router: Router, request: Request, ur...
FILE: demos/frame-navigation/config/server.ts
function shutdown (line 25) | function shutdown() {
FILE: demos/frames/app/assets/entry.tsx
method loadModule (line 4) | async loadModule(moduleUrl, exportName) {
method resolveFrame (line 12) | async resolveFrame(src, signal) {
FILE: demos/frames/app/router.tsx
function App (line 33) | function App() {
method onError (line 131) | onError(error) {
function ClientMountedPage (line 145) | function ClientMountedPage() {
method onError (line 184) | onError(error) {
function TimePage (line 198) | function TimePage() {
method onError (line 251) | onError(error) {
function ReloadScopePage (line 265) | function ReloadScopePage() {
method onError (line 324) | onError(error) {
function StateSearchRoutePage (line 341) | function StateSearchRoutePage() {
method onError (line 380) | onError(error) {
function resolveFrameViaRouter (line 715) | async function resolveFrameViaRouter(request: Request, src: string) {
FILE: demos/frames/app/us-states.ts
function searchUnitedStates (line 54) | function searchUnitedStates(query: string): string[] {
FILE: demos/frames/server.ts
function shutdown (line 25) | function shutdown() {
FILE: demos/sse/app/assets/entry.tsx
method loadModule (line 4) | async loadModule(moduleUrl: string, name: string) {
FILE: demos/sse/app/layout.tsx
function Layout (line 7) | function Layout() {
FILE: demos/sse/app/router.tsx
method home (line 41) | home(context) {
method messages (line 189) | messages(context) {
function getMessageLimit (line 243) | function getMessageLimit(url: URL): number | null {
FILE: demos/sse/app/utils/render.ts
function render (line 5) | function render(node: RemixNode, init?: ResponseInit) {
FILE: demos/unpkg/app/breadcrumb.ts
function renderBreadcrumb (line 3) | function renderBreadcrumb(packageName: string, version: string, dirPath:...
FILE: demos/unpkg/app/directory.ts
function renderDirectoryListing (line 7) | function renderDirectoryListing(
function getParentPath (line 75) | function getParentPath(dirPath: string): string {
FILE: demos/unpkg/app/error.ts
function renderError (line 3) | function renderError(title: string, message: string): Response {
FILE: demos/unpkg/app/file-content.ts
constant TEXT_EXTENSIONS (line 5) | const TEXT_EXTENSIONS = new Set([
constant IMAGE_EXTENSIONS (line 78) | const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.web...
constant IMAGE_MIME_TYPES (line 80) | const IMAGE_MIME_TYPES: Record<string, string> = {
function getExtension (line 90) | function getExtension(filename: string): string {
function isTextFile (line 96) | function isTextFile(filename: string): boolean {
function isImageFile (line 101) | function isImageFile(filename: string): boolean {
function isLikelyText (line 106) | function isLikelyText(data: Uint8Array): boolean {
function bytesToBase64 (line 117) | function bytesToBase64(bytes: Uint8Array): string {
function renderFileContent (line 125) | function renderFileContent(
FILE: demos/unpkg/app/router.ts
method home (line 25) | home() {
method browse (line 55) | async browse({ params }) {
FILE: demos/unpkg/app/utils/cache.ts
function getTarballCacheKey (line 12) | function getTarballCacheKey(packageName: string, version: string): string {
FILE: demos/unpkg/app/utils/npm.ts
constant NPM_REGISTRY (line 7) | const NPM_REGISTRY = 'https://registry.npmjs.org'
type PackageMetadata (line 9) | interface PackageMetadata {
type PackageVersionMetadata (line 15) | interface PackageVersionMetadata {
type PackageFile (line 24) | interface PackageFile {
type PackageContents (line 31) | interface PackageContents {
function fetchPackageMetadata (line 40) | async function fetchPackageMetadata(packageName: string): Promise<Packag...
function isFullyResolvedVersion (line 59) | function isFullyResolvedVersion(metadata: PackageMetadata, specifier: st...
function resolveVersion (line 71) | function resolveVersion(metadata: PackageMetadata, specifier: string): s...
function fetchPackageContents (line 108) | async function fetchPackageContents(
function fetchTarball (line 135) | async function fetchTarball(
function gunzip (line 171) | function gunzip(data: Uint8Array): Promise<Uint8Array> {
function parseTarball (line 183) | async function parseTarball(
function getFilesAtPath (line 237) | function getFilesAtPath(files: Map<string, PackageFile>, dirPath: string...
function parsePackagePath (line 290) | function parsePackagePath(path: string): {
class PackageNotFoundError (line 327) | class PackageNotFoundError extends Error {
method constructor (line 330) | constructor(packageName: string) {
class VersionNotFoundError (line 337) | class VersionNotFoundError extends Error {
method constructor (line 341) | constructor(packageName: string, version: string) {
class InvalidPathError (line 349) | class InvalidPathError extends Error {
method constructor (line 352) | constructor(path: string) {
FILE: demos/unpkg/app/utils/render.ts
function layout (line 254) | function layout(title: string, content: SafeHtml): SafeHtml {
function render (line 272) | function render(title: string, content: SafeHtml, init?: ResponseInit): ...
function formatBytes (line 276) | function formatBytes(bytes: number): string {
FILE: demos/unpkg/server.ts
function shutdown (line 25) | function shutdown() {
FILE: demos/unpkg/test/mock-fetch.ts
type MockResponse (line 7) | interface MockResponse {
type FetchMockHandler (line 13) | type FetchMockHandler = (url: string, init?: RequestInit) => MockRespons...
function createMockResponse (line 17) | function createMockResponse(mockResponse: MockResponse): Response {
function mockedFetch (line 38) | function mockedFetch(input: RequestInfo | URL, init?: RequestInit): Prom...
function installFetchMock (line 55) | function installFetchMock(): void {
function restoreFetchMock (line 62) | function restoreFetchMock(): void {
function addFetchHandler (line 70) | function addFetchHandler(handler: FetchMockHandler): void {
function clearFetchHandlers (line 77) | function clearFetchHandlers(): void {
function loadFixture (line 84) | function loadFixture(filename: string): Uint8Array {
function loadFixtureJson (line 92) | function loadFixtureJson<T = unknown>(filename: string): T {
function createNpmRegistryMock (line 100) | function createNpmRegistryMock(
FILE: docs/src/client/entry.tsx
method loadModule (line 4) | async loadModule(moduleUrl: string, exportName: string) {
method resolveFrame (line 12) | async resolveFrame(src, signal) {
FILE: docs/src/generate/documented-api.ts
type DocumentedAPI (line 5) | type DocumentedAPI =
type ParameterOrProperty (line 13) | type ParameterOrProperty = {
type Method (line 20) | type Method = {
type BaseDocumentedAPI (line 29) | type BaseDocumentedAPI = {
type DocumentedFunction (line 39) | type DocumentedFunction = BaseDocumentedAPI & {
type DocumentedClass (line 48) | type DocumentedClass = BaseDocumentedAPI & {
type DocumentedInterface (line 59) | type DocumentedInterface = BaseDocumentedAPI & {
type DocumentedInterfaceFunction (line 67) | type DocumentedInterfaceFunction = BaseDocumentedAPI & {
type DocumentedType (line 75) | type DocumentedType = BaseDocumentedAPI & {
constant WEBSITE_DOCS_PATH (line 81) | const WEBSITE_DOCS_PATH = '/api'
function getDocumentedAPI (line 84) | function getDocumentedAPI(fullName: string, node: typedoc.Reflection): D...
function getDocumentedFunction (line 120) | function getDocumentedFunction(
function getDocumentedInterfaceFunction (line 164) | function getDocumentedInterfaceFunction(
function getDocumentedClass (line 174) | function getDocumentedClass(
function getClassBodySignature (line 219) | function getClassBodySignature(node: typedoc.DeclarationReflection): str...
function getChildrenSignature (line 241) | function getChildrenSignature(
function getDocumentedInterface (line 276) | function getDocumentedInterface(
function getDocumentedType (line 302) | function getDocumentedType(fullName: string, node: typedoc.DeclarationRe...
function getApiAliases (line 340) | function getApiAliases(typedocComment: typedoc.Comment): string[] | unde...
function getApiFilePath (line 356) | function getApiFilePath(fullName: string, type: DocumentedAPI['type']): ...
function getApiDescription (line 362) | function getApiDescription(typedocComment: typedoc.Comment): string {
function getApiPropertiesAndMethods (line 370) | function getApiPropertiesAndMethods(
function getApiMethod (line 412) | function getApiMethod(fullName: string, node: typedoc.SignatureReflectio...
function getApiParameters (line 474) | function getApiParameters(
function getApiParameterOrProperty (line 525) | function getApiParameterOrProperty(
function processApiComment (line 542) | function processApiComment(parts: typedoc.CommentDisplayPart[]): string {
function warnOnInvalidImportSyntax (line 588) | function warnOnInvalidImportSyntax(api: DocumentedAPI) {
FILE: docs/src/generate/index.ts
constant DOCS_DIR (line 9) | const DOCS_DIR = path.join('build', 'md')
constant TYPEDOC_DIR (line 10) | const TYPEDOC_DIR = path.join('build', 'typedoc')
FILE: docs/src/generate/markdown.ts
function writeMarkdownFiles (line 14) | async function writeMarkdownFiles(comments: DocumentedAPI[], docsDir: st...
function frontmatter (line 61) | function frontmatter(comment: DocumentedAPI) {
function name (line 65) | function name(comment: DocumentedAPI) {
function source (line 69) | function source(comment: DocumentedAPI) {
function summary (line 75) | function summary(comment: DocumentedAPI) {
function aliases (line 79) | function aliases(comment: DocumentedAPI) {
function getFunctionMarkdown (line 82) | async function getFunctionMarkdown(comment: DocumentedFunction): Promise...
function getClassMarkdown (line 108) | async function getClassMarkdown(comment: DocumentedClass): Promise<strin...
function getInterfaceMarkdown (line 152) | async function getInterfaceMarkdown(comment: DocumentedInterface): Promi...
function getInterfaceFunctionMarkdown (line 188) | async function getInterfaceFunctionMarkdown(comment: DocumentedInterface...
function getTypeMarkdown (line 208) | async function getTypeMarkdown(comment: DocumentedType): Promise<string> {
FILE: docs/src/generate/symbols.ts
constant IGNORE_SYMBOLS (line 2) | const IGNORE_SYMBOLS = new Set([
constant MDN_SYMBOLS (line 15) | const MDN_SYMBOLS = {
FILE: docs/src/generate/typedoc.ts
type Maps (line 5) | type Maps = {
function loadTypeDoc (line 10) | async function loadTypeDoc(opts: {
function loadTypedocJson (line 33) | async function loadTypedocJson(opts: {
function createLookupMaps (line 88) | function createLookupMaps(reflection: typedoc.ProjectReflection): Maps {
function getDuplicateAPIs (line 162) | function getDuplicateAPIs(apisToDocument: Set<string>): Set<string> {
function getAliasedAPIs (line 211) | function getAliasedAPIs(comments: Map<string, typedoc.Reflection>): Set<...
FILE: docs/src/generate/utils.ts
function getApiNameFromFullName (line 1) | function getApiNameFromFullName(fullName: string): string {
function verbose (line 7) | function verbose(...args: unknown[]) {
function debug (line 13) | function debug(...args: unknown[]) {
function info (line 19) | function info(...args: unknown[]) {
function warn (line 23) | function warn(...args: unknown[]) {
function unimplemented (line 27) | function unimplemented(...args: unknown[]) {
function invariant (line 31) | function invariant(condition: unknown, message?: string): asserts condit...
function padStart (line 37) | function padStart(str: string, fill: string = ' '): string {
FILE: docs/src/server/components.tsx
function Home (line 6) | function Home() {
function NotFound (line 17) | function NotFound() {
type ServerContext (line 29) | type ServerContext = {
function ServerPage (line 36) | function ServerPage(handle: Handle<ServerContext>, setup: ServerContext) {
function Document (line 45) | function Document(handle: Handle) {
function Layout (line 75) | function Layout() {
function VersionDropdown (line 113) | function VersionDropdown(handle: Handle) {
type ApiTypes (line 155) | type ApiTypes = {
function Nav (line 162) | function Nav(handle: Handle) {
function NavDropdown (line 202) | function NavDropdown(handle: Handle) {
function NavDropdownSection (line 219) | function NavDropdownSection(handle: Handle) {
function MarkdownContent (line 248) | function MarkdownContent() {
function RemixLogoLight (line 252) | function RemixLogoLight(handle: Handle) {
function RemixLogoDark (line 270) | function RemixLogoDark(handle: Handle) {
FILE: docs/src/server/index.ts
function shutdown (line 26) | function shutdown() {
FILE: docs/src/server/markdown.ts
type DocFile (line 16) | type DocFile = {
function discoverMarkdownFiles (line 24) | async function discoverMarkdownFiles(
function renderMarkdownFile (line 63) | async function renderMarkdownFile(
function getShikiExtension (line 84) | function getShikiExtension(
FILE: docs/src/server/prerender.ts
function spider (line 44) | async function spider(router: Router, outputDir: string, urlQueue = new ...
function crawl (line 63) | async function crawl(router: Router, urlPath: string, outputDir: string) {
function isAbsoluteUrl (line 112) | function isAbsoluteUrl(href: string): boolean {
function resolveRelativeLink (line 116) | function resolveRelativeLink(link: string, url: string): string {
function getVersionsToBuild (line 126) | async function getVersionsToBuild(): Promise<ServerContext['versions']> {
FILE: docs/src/server/router.tsx
constant DOCS_DIR (line 14) | const DOCS_DIR = path.resolve(import.meta.dirname, '..', '..')
constant REPO_DIR (line 15) | const REPO_DIR = path.resolve(DOCS_DIR, '..')
constant BUILD_DIR (line 16) | const BUILD_DIR = path.join(REPO_DIR, 'docs', 'build')
constant MD_DIR (line 17) | const MD_DIR = path.join(BUILD_DIR, 'md')
constant ASSETS_DIR (line 18) | const ASSETS_DIR = path.join(BUILD_DIR, 'assets')
constant DEV_CSS_DIR (line 19) | const DEV_CSS_DIR = path.join(DOCS_DIR, 'public')
constant REMIX_PKG_JSON (line 20) | const REMIX_PKG_JSON = path.join(REPO_DIR, 'packages', 'remix', 'package...
function createRouter (line 29) | function createRouter(versions: ServerContext['versions']) {
function stream (line 113) | function stream(router: Router, request: Request, node: RemixNode, init?...
FILE: packages/async-context-middleware/src/lib/async-context.ts
function asyncContext (line 13) | function asyncContext(): Middleware {
function getContext (line 22) | function getContext(): RequestContext {
FILE: packages/component/bench/frameworks/preact-signals/index.tsx
type Row (line 9) | type Row = { id: number; label: Signal<string> }
function buildSignalData (line 11) | function buildSignalData(count: number): Row[] {
function MetricCard (line 115) | function MetricCard({
function ChartBar (line 164) | function ChartBar({ value, index }: { value: number; index: number }) {
function ActivityItem (line 196) | function ActivityItem({
function DropdownMenu (line 254) | function DropdownMenu({ rowId }: { rowId: number }) {
function DashboardTableRow (line 330) | function DashboardTableRow({ row }: { row: PlainRow }) {
function SearchInput (line 361) | function SearchInput() {
function FormWidgets (line 387) | function FormWidgets() {
function Row (line 570) | function Row({ id, label }: { id: number; label: Signal<string> }) {
function Dashboard (line 602) | function Dashboard({ onSwitchToTable }: { onSwitchToTable: () => void }) {
function App (line 788) | function App() {
FILE: packages/component/bench/frameworks/preact/index.tsx
function MetricCard (line 18) | function MetricCard({
function ChartBar (line 67) | function ChartBar({ value, index }: { value: number; index: number }) {
function ActivityItem (line 99) | function ActivityItem({
function DropdownMenu (line 153) | function DropdownMenu({ rowId }: { rowId: number }) {
function DashboardTableRow (line 229) | function DashboardTableRow({ row }: { row: Row }) {
function SearchInput (line 260) | function SearchInput() {
function FormWidgets (line 286) | function FormWidgets() {
function Dashboard (line 469) | function Dashboard({ onSwitchToTable }: { onSwitchToTable: () => void }) {
function App (line 655) | function App() {
FILE: packages/component/bench/frameworks/react/index.tsx
function MetricCard (line 18) | function MetricCard({
function ChartBar (line 67) | function ChartBar({ value, index }: { value: number; index: number }) {
function ActivityItem (line 99) | function ActivityItem({
function DropdownMenu (line 153) | function DropdownMenu({ rowId }: { rowId: number }) {
function DashboardTableRow (line 229) | function DashboardTableRow({ row }: { row: Row }) {
function SearchInput (line 260) | function SearchInput() {
function FormWidgets (line 286) | function FormWidgets() {
function Dashboard (line 469) | function Dashboard({ onSwitchToTable }: { onSwitchToTable: () => void }) {
function App (line 655) | function App() {
FILE: packages/component/bench/frameworks/remix/index.tsx
function Button (line 16) | function Button() {
function MetricCard (line 27) | function MetricCard(handle: Handle) {
function ChartBar (line 87) | function ChartBar(handle: Handle) {
function ActivityItem (line 127) | function ActivityItem(handle: Handle) {
function DropdownMenu (line 182) | function DropdownMenu(handle: Handle) {
function DashboardTableRow (line 275) | function DashboardTableRow(handle: Handle) {
function SearchInput (line 317) | function SearchInput(handle: Handle) {
function FormWidgets (line 354) | function FormWidgets(handle: Handle) {
function Dashboard (line 553) | function Dashboard(handle: Handle) {
function App (line 734) | function App(handle: Handle) {
FILE: packages/component/bench/frameworks/shared.ts
type Benchmark (line 1) | interface Benchmark {
type Framework (line 6) | interface Framework {
type Row (line 14) | type Row = { id: number; label: string }
function buildData (line 74) | function buildData(count: number) {
function get1000Rows (line 88) | function get1000Rows(): Row[] {
function get10000Rows (line 92) | function get10000Rows(): Row[] {
function updatedEvery10thRow (line 96) | function updatedEvery10thRow(data: Row[]): Row[] {
function swapRows (line 104) | function swapRows(data: Row[]): Row[] {
function remove (line 114) | function remove(data: Row[], id: number): Row[] {
function sortRows (line 118) | function sortRows(data: Row[], ascending: boolean = true): Row[] {
FILE: packages/component/bench/frameworks/solid/index.tsx
function MetricCard (line 17) | function MetricCard(props: { id: number; label: string; value: string; c...
function ChartBar (line 62) | function ChartBar(props: { value: number; index: number }) {
function ActivityItem (line 94) | function ActivityItem(props: { id: number; title: string; time: string; ...
function DropdownMenu (line 138) | function DropdownMenu(props: { rowId: number }) {
function DashboardTableRow (line 215) | function DashboardTableRow(props: { row: Row }) {
function SearchInput (line 246) | function SearchInput() {
function FormWidgets (line 272) | function FormWidgets() {
function Dashboard (line 466) | function Dashboard(props: { onSwitchToTable: () => void }) {
function App (line 678) | function App() {
FILE: packages/component/bench/runner.ts
constant PORT (line 8) | const PORT = 44100
constant BASE_URL (line 9) | const BASE_URL = `http://localhost:${PORT}`
constant REMIX_RESULTS_FILE (line 10) | const REMIX_RESULTS_FILE = path.join(import.meta.dirname, '.remix-prev-r...
constant LAST_ARGS_FILE (line 11) | const LAST_ARGS_FILE = path.join(import.meta.dirname, '.last-args.json')
type SavedArgs (line 13) | interface SavedArgs {
function saveArgs (line 25) | function saveArgs(args: SavedArgs): void {
function loadLastArgs (line 29) | function loadLastArgs(): SavedArgs | null {
type FunctionProfile (line 102) | interface FunctionProfile {
type AllocationProfile (line 108) | interface AllocationProfile {
type TimingResult (line 114) | interface TimingResult {
type BenchmarkResult (line 121) | interface BenchmarkResult {
type Operation (line 128) | interface Operation {
constant EVENT_TIMING_TIMEOUT_MS (line 135) | const EVENT_TIMING_TIMEOUT_MS = 5000
function clickAndMeasure (line 139) | async function clickAndMeasure(
function waitForIdle (line 282) | async function waitForIdle(page: Page): Promise<void> {
function click (line 300) | async function click(page: Page, selector: string): Promise<void> {
function clear (line 307) | async function clear(page: Page): Promise<void> {
function create1k (line 312) | async function create1k(page: Page): Promise<void> {
function startServer (line 435) | function startServer(): Promise<ChildProcess> {
function stopServer (line 471) | function stopServer(server: ChildProcess): Promise<void> {
function getFrameworks (line 479) | function getFrameworks(): string[] {
function saveRemixResults (line 489) | function saveRemixResults(results: BenchmarkResult[]): void {
function loadPreviousRemixResults (line 497) | function loadPreviousRemixResults(): BenchmarkResult[] {
function measureOperation (line 512) | async function measureOperation(page: Page, operation: Operation): Promi...
function calcStats (line 536) | function calcStats(times: number[]) {
function aggregateProfiles (line 548) | function aggregateProfiles(
function aggregateAllocationProfiles (line 594) | function aggregateAllocationProfiles(profiles: AllocationProfile[][]): A...
function printProfileTable (line 633) | function printProfileTable(profile: FunctionProfile[], operationName: st...
function printAllocationProfileTable (line 650) | function printAllocationProfileTable(profile: AllocationProfile[], opera...
function benchmarkFramework (line 671) | async function benchmarkFramework(
constant RESET (line 740) | const RESET = '\x1b[0m'
constant RED (line 741) | const RED = '\x1b[31m'
constant GREEN (line 742) | const GREEN = '\x1b[32m'
constant YELLOW (line 743) | const YELLOW = '\x1b[33m'
constant WHITE (line 744) | const WHITE = '\x1b[97m'
constant BG_GRAY (line 745) | const BG_GRAY = '\x1b[48;5;240m'
constant BOLD (line 746) | const BOLD = '\x1b[1m'
constant DIM (line 747) | const DIM = '\x1b[2m'
function printBarGraph (line 750) | function printBarGraph(allResults: BenchmarkResult[]): void {
function printTable (line 821) | function printTable(allResults: BenchmarkResult[]): void {
function printResults (line 894) | function printResults(allResults: BenchmarkResult[]): void {
function main (line 903) | async function main(): Promise<void> {
function buildAllocationProfile (line 1017) | function buildAllocationProfile(rawProfile: any): AllocationProfile[] {
FILE: packages/component/bench/server.ts
function shutdown (line 90) | function shutdown() {
FILE: packages/component/demos/animation/aspect-ratio.tsx
function AspectRatio (line 4) | function AspectRatio(handle: Handle) {
FILE: packages/component/demos/animation/bouncy-switch.tsx
function BouncySwitch (line 6) | function BouncySwitch(handle: Handle) {
FILE: packages/component/demos/animation/color-interpolation.tsx
function ColorInterpolation (line 12) | function ColorInterpolation() {
FILE: packages/component/demos/animation/cube.tsx
function Cube (line 4) | function Cube(handle: Handle) {
FILE: packages/component/demos/animation/default-animate.tsx
function createItem (line 4) | function createItem() {
function DefaultAnimate (line 8) | function DefaultAnimate(handle: Handle) {
FILE: packages/component/demos/animation/enter.tsx
function EnterAnimation (line 3) | function EnterAnimation() {
FILE: packages/component/demos/animation/entry.tsx
function Tile (line 23) | function Tile(handle: Handle) {
FILE: packages/component/demos/animation/exit.tsx
function ExitAnimation (line 4) | function ExitAnimation(handle: Handle) {
FILE: packages/component/demos/animation/flip-toggle.tsx
function FlipToggle (line 3) | function FlipToggle(handle: Handle) {
FILE: packages/component/demos/animation/hold-to-confirm.tsx
function HoldToConfirm (line 27) | function HoldToConfirm(handle: Handle) {
function HoldButton (line 64) | function HoldButton(handle: Handle) {
function Confirmation (line 144) | function Confirmation() {
function TrashIcon (line 198) | function TrashIcon() {
function CheckIcon (line 216) | function CheckIcon() {
constant PRESS_CONFIRM_TIME (line 233) | const PRESS_CONFIRM_TIME = 2000
type HTMLElementEventMap (line 239) | interface HTMLElementEventMap {
type ConfirmPressMixin (line 288) | type ConfirmPressMixin = typeof baseConfirmPress & {
FILE: packages/component/demos/animation/html-content.tsx
function HTMLContent (line 4) | function HTMLContent(handle: Handle) {
FILE: packages/component/demos/animation/interruptible-keyframes.tsx
function InterruptibleKeyframes (line 4) | function InterruptibleKeyframes(handle: Handle) {
FILE: packages/component/demos/animation/keyframes.tsx
function Keyframes (line 3) | function Keyframes() {
FILE: packages/component/demos/animation/material-ripple.tsx
type Ripple (line 4) | type Ripple = {
function MaterialRipple (line 11) | function MaterialRipple(handle: Handle) {
FILE: packages/component/demos/animation/mixin-presence-list.tsx
function createItem (line 5) | function createItem() {
function MixinPresenceList (line 9) | function MixinPresenceList(handle: Handle) {
FILE: packages/component/demos/animation/mixin-reclaim.tsx
function MixinReclaim (line 4) | function MixinReclaim(handle: Handle) {
FILE: packages/component/demos/animation/multi-state-badge.tsx
constant STATES (line 5) | const STATES = {
type State (line 12) | type State = keyof typeof STATES
function getNextState (line 14) | function getNextState(state: State): State {
constant ICON_SIZE (line 20) | const ICON_SIZE = 20
constant STROKE_WIDTH (line 21) | const STROKE_WIDTH = 1.5
constant VIEW_BOX_SIZE (line 22) | const VIEW_BOX_SIZE = 24
function MultiStateBadge (line 38) | function MultiStateBadge(handle: Handle) {
function Badge (line 74) | function Badge(handle: Handle) {
function Icon (line 132) | function Icon() {
function Loader (line 188) | function Loader() {
function Check (line 223) | function Check() {
function X (line 253) | function X() {
function Label (line 303) | function Label(handle: Handle) {
FILE: packages/component/demos/animation/press.tsx
function Press (line 3) | function Press(handle: Handle) {
FILE: packages/component/demos/animation/reordering.tsx
function shuffle (line 5) | function shuffle<T>(array: T[]): T[] {
function Reordering (line 14) | function Reordering(handle: Handle) {
FILE: packages/component/demos/animation/rolling-square.tsx
function RollingSquare (line 4) | function RollingSquare(handle: Handle) {
FILE: packages/component/demos/animation/rotate.tsx
function Rotate (line 3) | function Rotate() {
FILE: packages/component/demos/animation/shared-layout.tsx
function OverlapExample (line 13) | function OverlapExample(handle: Handle) {
function WaitExample (line 62) | function WaitExample(handle: Handle) {
function SharedLayout (line 112) | function SharedLayout(handle: Handle) {
function Circle (line 162) | function Circle() {
function FilledIcon (line 185) | function FilledIcon() {
function OutlineIcon (line 204) | function OutlineIcon() {
FILE: packages/component/demos/animation/transition-options.tsx
function TransitionOptions (line 3) | function TransitionOptions() {
FILE: packages/component/demos/basic/entry.tsx
function App (line 3) | function App(handle: Handle) {
FILE: packages/component/demos/controlled-uncontrolled-values/entry.tsx
function App (line 3) | function App(handle: Handle) {
FILE: packages/component/demos/draggable/draggable.tsx
type DragDetail (line 3) | type DragDetail = {
type DraggableProps (line 11) | type DraggableProps = {
function onPointerDown (line 39) | function onPointerDown(event: PointerEvent) {
function onPointerMove (line 66) | function onPointerMove(event: PointerEvent) {
function onPointerDone (line 77) | function onPointerDone(event: PointerEvent) {
function stopDrag (line 85) | function stopDrag() {
function dispatchDragEvent (line 96) | function dispatchDragEvent(node: HTMLElement, type: string) {
function readPx (line 108) | function readPx(value: string) {
type DraggableMixin (line 115) | type DraggableMixin = typeof baseDraggable & {
type HTMLElementEventMap (line 126) | interface HTMLElementEventMap {
FILE: packages/component/demos/draggable/entry.tsx
function App (line 5) | function App(_handle: Handle) {
FILE: packages/component/demos/drummer/app.tsx
function App (line 17) | function App(handle: Handle<Drummer>) {
function Equalizer (line 44) | function Equalizer(handle: Handle) {
function DrumControls (line 90) | function DrumControls(handle: Handle) {
function TempoDisplay (line 150) | function TempoDisplay(handle: Handle) {
FILE: packages/component/demos/drummer/components.tsx
function Layout (line 4) | function Layout() {
function EqualizerLayout (line 62) | function EqualizerLayout() {
function TempoLayout (line 84) | function TempoLayout() {
function TempoButtons (line 102) | function TempoButtons() {
function BPMDisplay (line 121) | function BPMDisplay() {
function EqualizerBar (line 171) | function EqualizerBar(handle: Handle) {
function ControlGroup (line 218) | function ControlGroup() {
function Button (line 243) | function Button() {
function Triangle (line 280) | function Triangle() {
type TempoButtonProps (line 299) | interface TempoButtonProps extends Props<'button'> {
function TempoButton (line 303) | function TempoButton() {
function Logo (line 339) | function Logo() {
FILE: packages/component/demos/drummer/drummer.ts
type DrummerEventMap (line 3) | interface DrummerEventMap {
class DrumEvent (line 13) | class DrumEvent extends Event {
method constructor (line 16) | constructor(type: keyof DrummerEventMap, tempo: number) {
class Drummer (line 22) | class Drummer extends TypedEventTarget<DrummerEventMap> {
method constructor (line 37) | constructor(tempoBpm: number = 90) {
method isPlaying (line 42) | get isPlaying() {
method bpm (line 46) | get bpm() {
method toggle (line 50) | async toggle() {
method setTempo (line 58) | setTempo(bpm: number) {
method play (line 64) | async play(bpm?: number) {
method stop (line 81) | async stop() {
method #ensureContext (line 94) | #ensureContext() {
method #secondsPer16th (line 106) | #secondsPer16th(): number {
method #createNoiseBuffer (line 110) | #createNoiseBuffer(ctx: AudioContext): AudioBuffer {
method #playKick (line 118) | #playKick(time: number) {
method #playSnare (line 134) | #playSnare(time: number) {
method #playHiHat (line 163) | #playHiHat(time: number) {
method #scheduleStep (line 184) | #scheduleStep(step: number, time: number) {
method #advanceNote (line 191) | #advanceNote() {
FILE: packages/component/demos/drummer/tempo-interaction.tsx
type HTMLElementEventMap (line 4) | interface HTMLElementEventMap {
class TempoEvent (line 9) | class TempoEvent extends Event {
method constructor (line 12) | constructor(type: typeof tempoEventType, bpm: number) {
type TempoEventsMixin (line 64) | type TempoEventsMixin = typeof baseTempoEvents & {
FILE: packages/component/demos/drummer/voice-looper.ts
type DecayGenerator (line 1) | type DecayGenerator = Generator<number, number, number>
function createExponentialDecayGenerator (line 3) | function createExponentialDecayGenerator(
function createVoiceLooper (line 25) | function createVoiceLooper(render: () => void, epsilon: number = 0.001) {
FILE: packages/component/demos/keyed-list/entry.tsx
type ListItem (line 3) | type ListItem = {
function App (line 8) | function App(handle: Handle) {
FILE: packages/component/demos/readme/entry.tsx
function App (line 15) | function App(handle: Handle) {
function Counter (line 34) | function Counter(handle: Handle) {
function Greeting (line 57) | function Greeting(handle: Handle) {
function CounterWithSetup (line 64) | function CounterWithSetup(handle: Handle, setup: number) {
function CounterWithLabel (line 89) | function CounterWithLabel(handle: Handle, setup: number) {
function SearchInput (line 113) | function SearchInput(handle: Handle) {
function SlugForm (line 155) | function SlugForm(handle: Handle) {
function KeyboardTracker (line 198) | function KeyboardTracker(handle: Handle) {
function ButtonBasic (line 215) | function ButtonBasic(handle: Handle) {
function ButtonAdvanced (line 243) | function ButtonAdvanced(handle: Handle) {
function FormBasic (line 290) | function FormBasic(handle: Handle) {
function ResizeComponent (line 319) | function ResizeComponent(handle: Handle) {
function Player (line 362) | function Player(handle: Handle) {
function FormWithScroll (line 412) | function FormWithScroll(handle: Handle) {
function Clock (line 461) | function Clock(handle: Handle) {
function LabeledInput (line 476) | function LabeledInput(handle: Handle) {
function ThemeProvider (line 498) | function ThemeProvider(handle: Handle<{ theme: string }>) {
function ThemedHeader (line 508) | function ThemedHeader(handle: Handle) {
class Theme (line 529) | class Theme extends TypedEventTarget<{ change: Event }> {
method value (line 532) | get value() {
method setValue (line 536) | setValue(value: 'light' | 'dark') {
function ThemeProviderAdvanced (line 542) | function ThemeProviderAdvanced(handle: Handle<Theme>) {
function ThemedContent (line 564) | function ThemedContent(handle: Handle) {
function ListWithFragment (line 593) | function ListWithFragment(handle: Handle) {
function Example (line 608) | function Example(handle: Handle) {
function DemoApp (line 620) | function DemoApp(handle: Handle) {
FILE: packages/component/demos/server.ts
function shutdown (line 86) | function shutdown() {
FILE: packages/component/demos/spring/drag-release.ts
type HTMLElementEventMap (line 6) | interface HTMLElementEventMap {
class DragVelocityEvent (line 11) | class DragVelocityEvent extends Event {
method constructor (line 17) | constructor(
type DragVelocityEventsMixin (line 112) | type DragVelocityEventsMixin = typeof baseDragVelocityEvents & {
FILE: packages/component/demos/spring/entry.tsx
type TrailPoint (line 6) | interface TrailPoint {
function PointerTrail (line 12) | function PointerTrail(handle: Handle) {
function SpringDemo (line 147) | function SpringDemo(handle: Handle) {
FILE: packages/component/src/lib/client-entries.ts
type SerializablePrimitive (line 6) | type SerializablePrimitive = string | number | boolean | null | undefined
type SerializableObject (line 11) | type SerializableObject = {
type SerializableArray (line 18) | type SerializableArray = SerializableValue[]
type SerializableValue (line 24) | type SerializableValue =
type SerializableProps (line 33) | type SerializableProps = {
type EntryMetadata (line 40) | type EntryMetadata = {
type EntryComponent (line 49) | type EntryComponent<context = NoContext, setup = undefined, props = {}> ...
function clientEntry (line 96) | function clientEntry(href: string, component: any): any {
function isEntry (line 127) | function isEntry(component: unknown): component is EntryComponent {
function logHydrationMismatch (line 136) | function logHydrationMismatch(...msg: any[]) {
function skipComments (line 146) | function skipComments(cursor: Node | null): Node | null {
FILE: packages/component/src/lib/component.ts
type Task (line 7) | type Task = (signal: AbortSignal) => void
type Handle (line 12) | interface Handle<C = Record<string, never>> {
type NoContext (line 94) | type NoContext = Record<string, never>
type Component (line 99) | type Component<Context = NoContext, Setup = undefined, Props = ElementPr...
type ContextFrom (line 107) | type ContextFrom<ComponentType> =
type Context (line 117) | interface Context<C> {
type FrameContent (line 129) | type FrameContent = ReadableStream<Uint8Array> | string | RemixNode
type FrameHandleEventMap (line 134) | type FrameHandleEventMap = {
type FrameHandle (line 142) | type FrameHandle = TypedEventTarget<FrameHandleEventMap> & {
type FrameProps (line 153) | interface FrameProps {
type ComponentFn (line 167) | type ComponentFn<Context = NoContext, Setup = undefined, Props = Record<...
type RenderFn (line 175) | type RenderFn<P = {}> = (props: P) => RemixNode
type FragmentProps (line 184) | interface FragmentProps {
type BuiltinElements (line 192) | interface BuiltinElements {
type Key (line 202) | type Key = string | number | bigint
type ComponentConfig (line 204) | type ComponentConfig = {
type ComponentHandle (line 216) | type ComponentHandle = ReturnType<typeof createComponent>
function createComponent (line 224) | function createComponent<C = NoContext>(config: ComponentConfig) {
function Frame (line 337) | function Frame(handle: Handle<FrameHandle>) {
function Fragment (line 346) | function Fragment() {
function createFrameHandle (line 356) | function createFrameHandle(
function notImplemented (line 375) | function notImplemented(msg: string) {
FILE: packages/component/src/lib/create-element.ts
function createElement (line 12) | function createElement(
FILE: packages/component/src/lib/diff-dom.ts
function diffNodes (line 4) | function diffNodes(curr: Node[], next: Node[], context: FrameContext) {
function diffNode (line 51) | function diffNode(current: Node, next: Node, context: FrameContext): Chi...
function diffElementAttributes (line 99) | function diffElementAttributes(current: Element, next: Element): void {
function shouldPreserveLiveAttribute (line 124) | function shouldPreserveLiveAttribute(current: Element, next: Element, na...
function shouldPreserveElementChildren (line 164) | function shouldPreserveElementChildren(current: Element, next: Element):...
function shouldPreserveInputValue (line 172) | function shouldPreserveInputValue(input: HTMLInputElement): boolean {
function isPopoverOpen (line 184) | function isPopoverOpen(element: Element): boolean {
function diffElementChildren (line 192) | function diffElementChildren(current: Element, next: Element, context: F...
function nodeTypesComparable (line 324) | function nodeTypesComparable(a: Node, b: Node): boolean {
function isHydrationEndComment (line 333) | function isHydrationEndComment(node: Node): node is Comment {
function findHydrationEndMarker (line 337) | function findHydrationEndMarker(start: Comment): Comment {
function findHydrationEndIndex (line 355) | function findHydrationEndIndex(nodes: Node[], startIdx: number): number {
function isTextNode (line 362) | function isTextNode(node: Node): node is Text {
function isElement (line 366) | function isElement(node: Node): node is Element {
function isCommentNode (line 370) | function isCommentNode(node: Node): node is Comment {
function isFrameStartMarker (line 374) | function isFrameStartMarker(node: Node): node is Comment {
function disposeRemovedSubFrames (line 378) | function disposeRemovedSubFrames(node: Node, context: FrameContext): void {
function isVirtualRootStartMarker (line 398) | function isVirtualRootStartMarker(node: Node): node is Comment {
function isVirtualRootEndMarker (line 402) | function isVirtualRootEndMarker(node: Node): node is Comment {
FILE: packages/component/src/lib/diff-props.ts
constant SVG_NS (line 7) | const SVG_NS = 'http://www.w3.org/2000/svg'
constant ATTRIBUTE_FALLBACK_NAMES (line 17) | const ATTRIBUTE_FALLBACK_NAMES = new Set([
function canUseProperty (line 33) | function canUseProperty(
function isFrameworkProp (line 43) | function isFrameworkProp(name: string): boolean {
function serializeStyleObject (line 56) | function serializeStyleObject(style: Record<string, unknown>): string {
function normalizePropName (line 74) | function normalizePropName(name: string, isSvg: boolean): { ns?: string;...
function toLocalName (line 91) | function toLocalName(attrName: string): string {
function clearRuntimePropertyOnRemoval (line 97) | function clearRuntimePropertyOnRemoval(dom: Element & Record<string, unk...
function getMergedClassName (line 113) | function getMergedClassName(props: ElementProps): string | undefined {
function diffHostProps (line 121) | function diffHostProps(curr: ElementProps, next: ElementProps, dom: Elem...
function resetStyleState (line 202) | function resetStyleState() {
FILE: packages/component/src/lib/document-state.ts
type SelectionInformation (line 9) | type SelectionInformation = {
function createDocumentState (line 14) | function createDocumentState(_doc?: Document) {
FILE: packages/component/src/lib/dom.ts
type Booleanish (line 11) | type Booleanish = boolean | 'true' | 'false'
type LayoutAnimationConfig (line 17) | interface LayoutAnimationConfig {
type HostProps (line 27) | interface HostProps<eventTarget extends EventTarget> {
type Trackable (line 45) | type Trackable<T> = T
type SVGProps (line 50) | interface SVGProps<eventTarget extends EventTarget = SVGElement>
type PathProps (line 749) | interface PathProps {
type AriaProps (line 758) | interface AriaProps {
type WAIAriaRole (line 996) | type WAIAriaRole =
type DPubAriaRole (line 1093) | type DPubAriaRole =
type AriaRole (line 1134) | type AriaRole = WAIAriaRole | DPubAriaRole
type AllHTMLProps (line 1139) | interface AllHTMLProps<eventTarget extends EventTarget = EventTarget>
type HTMLProps (line 1598) | interface HTMLProps<eventTarget extends EventTarget = EventTarget>
type HTMLAttributeReferrerPolicy (line 1732) | type HTMLAttributeReferrerPolicy =
type HTMLAttributeAnchorTarget (line 1743) | type HTMLAttributeAnchorTarget = '_self' | '_blank' | '_parent' | '_top'...
type PartialAnchorHTMLProps (line 1748) | interface PartialAnchorHTMLProps<eventTarget extends EventTarget>
type AnchorAriaRoles (line 1780) | type AnchorAriaRoles =
type AccessibleAnchorHTMLProps (line 1810) | type AccessibleAnchorHTMLProps<eventTarget extends EventTarget = HTMLAnc...
type AnchorHTMLProps (line 1819) | interface AnchorHTMLProps<eventTarget extends EventTarget = HTMLAnchorEl...
type PartialAreaHTMLProps (line 1830) | interface PartialAreaHTMLProps<eventTarget extends EventTarget>
type AreaAriaRoles (line 1856) | type AreaAriaRoles =
type AccessibleAreaHTMLProps (line 1869) | type AccessibleAreaHTMLProps<eventTarget extends EventTarget = HTMLAreaE...
type AreaHTMLProps (line 1878) | interface AreaHTMLProps<eventTarget extends EventTarget = HTMLAreaElement>
type ArticleHTMLProps (line 1889) | interface ArticleHTMLProps<eventTarget extends EventTarget = HTMLElement>
type AsideHTMLProps (line 1908) | interface AsideHTMLProps<eventTarget extends EventTarget = HTMLElement>
type AudioHTMLProps (line 1932) | interface AudioHTMLProps<eventTarget extends EventTarget = HTMLAudioElem...
type BaseHTMLProps (line 1941) | interface BaseHTMLProps<eventTarget extends EventTarget = HTMLBaseElement>
type BlockquoteHTMLProps (line 1954) | interface BlockquoteHTMLProps<eventTarget extends EventTarget = HTMLQuot...
type BrHTMLProps (line 1963) | interface BrHTMLProps<eventTarget extends EventTarget = HTMLBRElement>
type ButtonHTMLProps (line 1972) | interface ButtonHTMLProps<eventTarget extends EventTarget = HTMLButtonEl...
type CanvasHTMLProps (line 2042) | interface CanvasHTMLProps<eventTarget extends EventTarget = HTMLCanvasEl...
type CaptionHTMLProps (line 2053) | interface CaptionHTMLProps<eventTarget extends EventTarget = HTMLElement>
type ColHTMLProps (line 2062) | interface ColHTMLProps<eventTarget extends EventTarget = HTMLTableColEle...
type ColgroupHTMLProps (line 2075) | interface ColgroupHTMLProps<eventTarget extends EventTarget = HTMLTableC...
type DataHTMLProps (line 2086) | interface DataHTMLProps<eventTarget extends EventTarget = HTMLDataElement>
type DataListHTMLProps (line 2095) | interface DataListHTMLProps<eventTarget extends EventTarget = HTMLDataLi...
type DdHTMLProps (line 2104) | interface DdHTMLProps<eventTarget extends EventTarget = HTMLElement>
type DelHTMLProps (line 2113) | interface DelHTMLProps<eventTarget extends EventTarget = HTMLModElement>
type DetailsHTMLProps (line 2126) | interface DetailsHTMLProps<eventTarget extends EventTarget = HTMLDetails...
type DialogHTMLProps (line 2139) | interface DialogHTMLProps<eventTarget extends EventTarget = HTMLDialogEl...
type DlHTMLProps (line 2154) | interface DlHTMLProps<eventTarget extends EventTarget = HTMLDListElement>
type DtHTMLProps (line 2163) | interface DtHTMLProps<eventTarget extends EventTarget = HTMLElement>
type EmbedHTMLProps (line 2172) | interface EmbedHTMLProps<eventTarget extends EventTarget = HTMLEmbedElem...
type FieldsetHTMLProps (line 2189) | interface FieldsetHTMLProps<eventTarget extends EventTarget = HTMLFieldS...
type FigcaptionHTMLProps (line 2204) | interface FigcaptionHTMLProps<eventTarget extends EventTarget = HTMLElem...
type FooterHTMLProps (line 2213) | interface FooterHTMLProps<eventTarget extends EventTarget = HTMLElement>
type FormHTMLProps (line 2222) | interface FormHTMLProps<eventTarget extends EventTarget = HTMLFormElement>
type HeadingHTMLProps (line 2257) | interface HeadingHTMLProps<eventTarget extends EventTarget = HTMLHeading...
type HeadHTMLProps (line 2266) | interface HeadHTMLProps<eventTarget extends EventTarget = HTMLHeadElement>
type HeaderHTMLProps (line 2275) | interface HeaderHTMLProps<eventTarget extends EventTarget = HTMLElement>
type HrHTMLProps (line 2284) | interface HrHTMLProps<eventTarget extends EventTarget = HTMLHRElement>
type HtmlHTMLProps (line 2293) | interface HtmlHTMLProps<eventTarget extends EventTarget = HTMLHtmlElement>
type IframeHTMLProps (line 2302) | interface IframeHTMLProps<eventTarget extends EventTarget = HTMLIFrameEl...
type HTMLAttributeCrossOrigin (line 2346) | type HTMLAttributeCrossOrigin = 'anonymous' | 'use-credentials'
type PartialImgHTMLProps (line 2351) | interface PartialImgHTMLProps<eventTarget extends EventTarget>
type ImgAriaRolesAccessibleName (line 2387) | type ImgAriaRolesAccessibleName = Trackable<
type ImgAriaRoles (line 2409) | type ImgAriaRoles =
type AccessibleImgHTMLProps (line 2437) | type AccessibleImgHTMLProps<eventTarget extends EventTarget = HTMLImageE...
type ImgHTMLProps (line 2446) | interface ImgHTMLProps<eventTarget extends EventTarget = HTMLImageElement>
type HTMLInputTypeAttribute (line 2462) | type HTMLInputTypeAttribute =
type PartialInputHTMLProps (line 2489) | interface PartialInputHTMLProps<eventTarget extends EventTarget>
type InputAriaRoles (line 2577) | type InputAriaRoles =
type AccessibleInputHTMLProps (line 2724) | type AccessibleInputHTMLProps<eventTarget extends EventTarget = HTMLInpu...
type InputHTMLProps (line 2733) | interface InputHTMLProps<eventTarget extends EventTarget = HTMLInputElem...
type InsHTMLProps (line 2764) | interface InsHTMLProps<eventTarget extends EventTarget = HTMLModElement>
type KeygenHTMLProps (line 2777) | interface KeygenHTMLProps<eventTarget extends EventTarget = HTMLUnknownE...
type LabelHTMLProps (line 2796) | interface LabelHTMLProps<eventTarget extends EventTarget = HTMLLabelElem...
type LegendHTMLProps (line 2811) | interface LegendHTMLProps<eventTarget extends EventTarget = HTMLLegendEl...
type LiHTMLProps (line 2820) | interface LiHTMLProps<eventTarget extends EventTarget = HTMLLIElement>
type LinkHTMLProps (line 2829) | interface LinkHTMLProps<eventTarget extends EventTarget = HTMLLinkElement>
type MainHTMLProps (line 2874) | interface MainHTMLProps<eventTarget extends EventTarget = HTMLElement>
type MapHTMLProps (line 2883) | interface MapHTMLProps<eventTarget extends EventTarget = HTMLMapElement>
type MarqueeHTMLProps (line 2894) | interface MarqueeHTMLProps<eventTarget extends EventTarget = HTMLMarquee...
type MediaHTMLProps (line 2923) | interface MediaHTMLProps<eventTarget extends EventTarget = HTMLMediaElem...
type MenuHTMLProps (line 2972) | interface MenuHTMLProps<eventTarget extends EventTarget = HTMLMenuElement>
type MetaHTMLProps (line 2994) | interface MetaHTMLProps<eventTarget extends EventTarget = HTMLMetaElement>
type MeterHTMLProps (line 3017) | interface MeterHTMLProps<eventTarget extends EventTarget = HTMLMeterElem...
type NavHTMLProps (line 3040) | interface NavHTMLProps<eventTarget extends EventTarget = HTMLElement>
type NoScriptHTMLProps (line 3051) | interface NoScriptHTMLProps<eventTarget extends EventTarget = HTMLElement>
type ObjectHTMLProps (line 3060) | interface ObjectHTMLProps<eventTarget extends EventTarget = HTMLObjectEl...
type OlHTMLProps (line 3089) | interface OlHTMLProps<eventTarget extends EventTarget = HTMLOListElement>
type OptgroupHTMLProps (line 3117) | interface OptgroupHTMLProps<eventTarget extends EventTarget = HTMLOptGro...
type OptionHTMLProps (line 3130) | interface OptionHTMLProps<eventTarget extends EventTarget = HTMLOptionEl...
type OutputHTMLProps (line 3147) | interface OutputHTMLProps<eventTarget extends EventTarget = HTMLOutputEl...
type ParamHTMLProps (line 3162) | interface ParamHTMLProps<eventTarget extends EventTarget = HTMLParamElem...
type PictureHTMLProps (line 3175) | interface PictureHTMLProps<eventTarget extends EventTarget = HTMLPicture...
type ProgressHTMLProps (line 3184) | interface ProgressHTMLProps<eventTarget extends EventTarget = HTMLProgre...
type QuoteHTMLProps (line 3197) | interface QuoteHTMLProps<eventTarget extends EventTarget = HTMLQuoteElem...
type ScriptHTMLProps (line 3206) | interface ScriptHTMLProps<eventTarget extends EventTarget = HTMLScriptEl...
type SearchHTMLProps (line 3241) | interface SearchHTMLProps<eventTarget extends EventTarget = HTMLElement>
type PartialSelectHTMLProps (line 3250) | interface PartialSelectHTMLProps<eventTarget extends EventTarget>
type SelectAriaRoles (line 3272) | type SelectAriaRoles =
type AccessibleSelectHTMLProps (line 3289) | type AccessibleSelectHTMLProps<eventTarget extends EventTarget = HTMLSel...
type SelectHTMLProps (line 3298) | interface SelectHTMLProps<eventTarget extends EventTarget = HTMLSelectEl...
type SlotHTMLProps (line 3313) | interface SlotHTMLProps<eventTarget extends EventTarget = HTMLSlotElement>
type SourceHTMLProps (line 3324) | interface SourceHTMLProps<eventTarget extends EventTarget = HTMLSourceEl...
type StyleHTMLProps (line 3349) | interface StyleHTMLProps<eventTarget extends EventTarget = HTMLStyleElem...
type TableHTMLProps (line 3364) | interface TableHTMLProps<eventTarget extends EventTarget = HTMLTableElem...
type TdHTMLProps (line 3379) | interface TdHTMLProps<eventTarget extends EventTarget = HTMLTableCellEle...
type TemplateHTMLProps (line 3408) | interface TemplateHTMLProps<eventTarget extends EventTarget = HTMLTempla...
type TextareaHTMLProps (line 3417) | interface TextareaHTMLProps<eventTarget extends EventTarget = HTMLTextAr...
type ThHTMLProps (line 3462) | interface ThHTMLProps<eventTarget extends EventTarget = HTMLTableCellEle...
type TimeHTMLProps (line 3485) | interface TimeHTMLProps<eventTarget extends EventTarget = HTMLTimeElement>
type TitleHTMLProps (line 3496) | interface TitleHTMLProps<eventTarget extends EventTarget = HTMLTitleElem...
type TrackHTMLProps (line 3505) | interface TrackHTMLProps<eventTarget extends EventTarget = HTMLTrackElem...
type UlHTMLProps (line 3524) | interface UlHTMLProps<eventTarget extends EventTarget = HTMLUListElement>
type VideoHTMLProps (line 3546) | interface VideoHTMLProps<eventTarget extends EventTarget = HTMLVideoElem...
type WbrHTMLProps (line 3567) | interface WbrHTMLProps<eventTarget extends EventTarget = HTMLElement>
type DetailedHTMLProps (line 3576) | type DetailedHTMLProps<
type MathMLProps (line 3584) | interface MathMLProps<eventTarget extends EventTarget = MathMLElement>
type AnnotationMathMLProps (line 3607) | interface AnnotationMathMLProps<eventTarget extends EventTarget>
type AnnotationXmlMathMLProps (line 3618) | interface AnnotationXmlMathMLProps<eventTarget extends EventTarget>
type MActionMathMLProps (line 3629) | interface MActionMathMLProps<eventTarget extends EventTarget>
type MathMathMLProps (line 3640) | interface MathMathMLProps<eventTarget extends EventTarget> extends MathM...
type MEncloseMathMLProps (line 3648) | interface MEncloseMathMLProps<eventTarget extends EventTarget>
type MErrorMathMLProps (line 3657) | interface MErrorMathMLProps<eventTarget extends EventTarget>
type MFencedMathMLProps (line 3663) | interface MFencedMathMLProps<eventTarget extends EventTarget>
type MFracMathMLProps (line 3676) | interface MFracMathMLProps<eventTarget extends EventTarget>
type MiMathMLProps (line 3689) | interface MiMathMLProps<eventTarget extends EventTarget> extends MathMLP...
type MmultiScriptsMathMLProps (line 3720) | interface MmultiScriptsMathMLProps<eventTarget extends EventTarget>
type MNMathMLProps (line 3731) | interface MNMathMLProps<eventTarget extends EventTarget> extends MathMLP...
type MOMathMLProps (line 3736) | interface MOMathMLProps<eventTarget extends EventTarget> extends MathMLP...
type MOverMathMLProps (line 3764) | interface MOverMathMLProps<eventTarget extends EventTarget>
type MPaddedMathMLProps (line 3773) | interface MPaddedMathMLProps<eventTarget extends EventTarget>
type MPhantomMathMLProps (line 3790) | interface MPhantomMathMLProps<eventTarget extends EventTarget>
type MPrescriptsMathMLProps (line 3796) | interface MPrescriptsMathMLProps<eventTarget extends EventTarget>
type MRootMathMLProps (line 3802) | interface MRootMathMLProps<eventTarget extends EventTarget>
type MRowMathMLProps (line 3808) | interface MRowMathMLProps<eventTarget extends EventTarget>
type MSMathMLProps (line 3814) | interface MSMathMLProps<eventTarget extends EventTarget> extends MathMLP...
type MSpaceMathMLProps (line 3824) | interface MSpaceMathMLProps<eventTarget extends EventTarget>
type MSqrtMathMLProps (line 3837) | interface MSqrtMathMLProps<eventTarget extends EventTarget>
type MStyleMathMLProps (line 3843) | interface MStyleMathMLProps<eventTarget extends EventTarget>
type MSubMathMLProps (line 3864) | interface MSubMathMLProps<eventTarget extends EventTarget> extends MathM...
type MSubsupMathMLProps (line 3872) | interface MSubsupMathMLProps<eventTarget extends EventTarget>
type MSupMathMLProps (line 3883) | interface MSupMathMLProps<eventTarget extends EventTarget> extends MathM...
type MTableMathMLProps (line 3891) | interface MTableMathMLProps<eventTarget extends EventTarget>
type MTdMathMLProps (line 3918) | interface MTdMathMLProps<eventTarget extends EventTarget> extends MathML...
type MTextMathMLProps (line 3932) | interface MTextMathMLProps<eventTarget extends EventTarget>
type MTrMathMLProps (line 3938) | interface MTrMathMLProps<eventTarget extends EventTarget> extends MathML...
type MUnderMathMLProps (line 3948) | interface MUnderMathMLProps<eventTarget extends EventTarget>
type MUnderoverMathMLProps (line 3957) | interface MUnderoverMathMLProps<eventTarget extends EventTarget>
type SemanticsMathMLProps (line 3968) | interface SemanticsMathMLProps<eventTarget extends EventTarget>
FILE: packages/component/src/lib/error-event.ts
type ComponentErrorEvent (line 4) | type ComponentErrorEvent = ErrorEvent & {
function createComponentErrorEvent (line 14) | function createComponentErrorEvent(error: unknown): ComponentErrorEvent {
function getComponentError (line 24) | function getComponentError(event: Event): unknown {
FILE: packages/component/src/lib/event-listeners.ts
type Dispatched (line 4) | type Dispatched<event extends Event, target extends EventTarget> = Omit<
type EnsureEvent (line 14) | type EnsureEvent<event, target extends EventTarget> = event extends Event
type EventType (line 21) | type EventType<target extends EventTarget> = target extends { __eventMap...
type NavigationTarget (line 25) | type NavigationTarget = Window extends { navigation: infer navigation } ...
type NavigationTargetEvent (line 26) | type NavigationTargetEvent = NavigationTarget extends {
type NavigationTargetEventMap (line 31) | type NavigationTargetEventMap = {
type ListenerFor (line 38) | type ListenerFor<target extends EventTarget, type extends EventType<targ...
type EventListeners (line 46) | type EventListeners<target extends EventTarget> = Partial<{
type EventMap (line 54) | type EventMap<target extends EventTarget> = (
function addEventListeners (line 137) | function addEventListeners<target extends EventTarget>(
FILE: packages/component/src/lib/frame.ts
type FrameRoot (line 12) | type FrameRoot = [Comment, Comment] | Element | Document | DocumentFragment
type FrameData (line 14) | type FrameData = {
type HydrationData (line 20) | type HydrationData = {
type RmxData (line 26) | type RmxData = {
type VirtualRootMarker (line 31) | type VirtualRootMarker = Comment & {
type FrameMarkerData (line 35) | type FrameMarkerData = FrameData & {
type PendingClientEntries (line 39) | type PendingClientEntries = Map<Comment, [Comment, RemixElement]>
type LoadModule (line 44) | type LoadModule = (moduleUrl: string, exportName: string) => Promise<Fun...
type ResolveFrame (line 49) | type ResolveFrame = (
type InternalFrameContent (line 55) | type InternalFrameContent = FrameContent | DocumentFragment
type FrameTemplateListener (line 57) | type FrameTemplateListener = (fragment: DocumentFragment) => void
function syncElementAttributes (line 62) | function syncElementAttributes(target: Element, source: Element) {
type FrameRuntime (line 76) | type FrameRuntime = {
type FrameContext (line 91) | type FrameContext = {
type FrameInit (line 109) | type FrameInit = {
type Frame (line 127) | type Frame = {
type RenderOptions (line 135) | type RenderOptions = {
function createFrame (line 140) | function createFrame(root: FrameRoot, init: FrameInit): Frame {
function createFrameRuntime (line 407) | function createFrameRuntime(init: {
type InitialHydrationTracker (line 437) | type InitialHydrationTracker = {
function createInitialHydrationTracker (line 443) | function createInitialHydrationTracker(): InitialHydrationTracker {
function mergeRmxDataFromDocument (line 480) | function mergeRmxDataFromDocument(into: RmxData, doc: Document): void {
function mergeRmxDataFromFragment (line 489) | function mergeRmxDataFromFragment(into: RmxData, fragment: DocumentFragm...
function moveServerStylesToHead (line 498) | function moveServerStylesToHead(doc: Document, fragment: DocumentFragmen...
function parseRmxDataScript (line 517) | function parseRmxDataScript(script: HTMLScriptElement): RmxData {
function mergeRmxData (line 526) | function mergeRmxData(into: RmxData, from: RmxData): void {
function copyOwnRmxEntries (line 538) | function copyOwnRmxEntries<T>(target: Record<string, T>, source: Record<...
function scheduleHydrationInContainer (line 546) | function scheduleHydrationInContainer(
function scheduleHydrationMarker (line 564) | function scheduleHydrationMarker(
function getOrStartModuleLoad (line 598) | function getOrStartModuleLoad(
function createElement (line 627) | function createElement(component: Function, props: Record<string, unknow...
function reviveSerializedValue (line 632) | function reviveSerializedValue(value: unknown): unknown {
function reviveSerializedObject (line 661) | function reviveSerializedObject(value: unknown): Record<string, unknown> {
function hydrateRegion (line 668) | function hydrateRegion(
function createSubFrames (line 698) | function createSubFrames(nodes: Node[], context: FrameContext) {
function isHydrationMarkerLive (line 740) | function isHydrationMarkerLive(marker: HydrationMarker, context: FrameCo...
function removeVirtualRoots (line 762) | function removeVirtualRoots(nodes: Node[]): void {
function disposeSubFrames (line 779) | function disposeSubFrames(nodes: Node[], context: FrameContext): void {
function getEarlyFrameContent (line 800) | function getEarlyFrameContent(id: string): DocumentFragment | null {
function setupTemplateObserver (line 810) | function setupTemplateObserver(): MutationObserver {
function collectAndPublishTemplates (line 824) | function collectAndPublishTemplates(node: Node): void {
function publishFrameTemplateElement (line 838) | function publishFrameTemplateElement(template: HTMLTemplateElement): void {
function publishFrameTemplate (line 844) | function publishFrameTemplate(id: string, fragment: DocumentFragment): v...
function consumeFrameTemplate (line 861) | function consumeFrameTemplate(id: string): DocumentFragment | null {
function subscribeFrameTemplate (line 873) | function subscribeFrameTemplate(id: string, listener: FrameTemplateListe...
type StreamTemplateParseResult (line 890) | type StreamTemplateParseResult = {
constant COMPLETE_TEMPLATE_WITH_ID_PATTERN (line 895) | const COMPLETE_TEMPLATE_WITH_ID_PATTERN =
function extractTemplatesFromBuffer (line 898) | function extractTemplatesFromBuffer(
function renderFrameStream (line 952) | async function renderFrameStream(
type FrameContainer (line 1009) | type FrameContainer = {
function createContainer (line 1017) | function createContainer(root: FrameRoot): FrameContainer {
function createElementContainer (line 1021) | function createElementContainer(root: Document | Element | DocumentFragm...
function createCommentContainer (line 1032) | function createCommentContainer([start, end]: [Comment, Comment]): Frame...
function createFragmentFromString (line 1059) | function createFragmentFromString(doc: Document, content: string): Docum...
function isRemixNodeFrameContent (line 1065) | function isRemixNodeFrameContent(content: InternalFrameContent): content...
function isFullDocumentHtml (line 1073) | function isFullDocumentHtml(content: string): boolean {
type HydrationMarker (line 1078) | type HydrationMarker = {
function findHydrationMarkers (line 1084) | function findHydrationMarkers(container: FrameContainer): HydrationMarke...
function forEachComment (line 1099) | function forEachComment(container: FrameContainer, cb: (comment: Comment...
function walkCommentsInNodes (line 1103) | function walkCommentsInNodes(nodes: Node[], cb: (comment: Comment) => vo...
function isHydrationStart (line 1122) | function isHydrationStart(node: Comment): boolean {
function isHydrationEnd (line 1126) | function isHydrationEnd(node: Comment): boolean {
function isHydratedVirtualRootMarker (line 1130) | function isHydratedVirtualRootMarker(node: Node): node is VirtualRootMar...
function isFrameStart (line 1134) | function isFrameStart(node: Node): node is Comment {
function isFrameEnd (line 1138) | function isFrameEnd(node: Comment): boolean {
function getFrameId (line 1142) | function getFrameId(start: Comment): string {
function findEndMarker (line 1148) | function findEndMarker(
function collectHtmlMarkerSummary (line 1171) | function collectHtmlMarkerSummary(html: string): Record<string, number> {
function hasBalancedMarkerSummary (line 1180) | function hasBalancedMarkerSummary(summary: Record<string, number>): bool...
FILE: packages/component/src/lib/invariant.ts
function invariant (line 8) | function invariant(assertion: any, message?: string): asserts assertion {
function ensure (line 28) | function ensure(id: number, assertion: boolean): asserts assertion {
FILE: packages/component/src/lib/jsx.ts
type ElementType (line 9) | type ElementType = string | Function
type ElementProps (line 16) | type ElementProps = Record<string, any>
type RemixElement (line 22) | interface RemixElement {
type Renderable (line 36) | type Renderable = RemixElement | string | number | bigint | boolean | nu...
type RemixNode (line 49) | type RemixNode = Renderable | RemixNode[]
type MixItem (line 51) | type MixItem<mix> = mix extends ReadonlyArray<infer descriptor> ? descri...
type NormalizeMixProp (line 53) | type NormalizeMixProp<props> = props extends { mix?: infer mix }
type ExpandMixProp (line 59) | type ExpandMixProp<props> = props extends { mix?: infer mix }
type Props (line 73) | type Props<T extends keyof JSX.IntrinsicElements> = NormalizeMixProp<
function jsx (line 95) | function jsx(type: any, props: any, key?: any): RemixElement {
type IntrinsicAttributes (line 103) | interface IntrinsicAttributes {
type Element (line 107) | type Element = RemixElement
type ElementType (line 109) | type ElementType =
type ElementChildrenAttribute (line 115) | type ElementChildrenAttribute = {
type ElementAttributesProperty (line 119) | interface ElementAttributesProperty {
type LibraryManagedAttributes (line 123) | type LibraryManagedAttributes<component, props> = component extends (
type IntrinsicSVGElements (line 133) | interface IntrinsicSVGElements {
type IntrinsicMathMLElements (line 195) | interface IntrinsicMathMLElements {
type IntrinsicHTMLElements (line 233) | interface IntrinsicHTMLElements {
type IntrinsicElements (line 353) | interface IntrinsicElements
function normalizeElementProps (line 360) | function normalizeElementProps(props: ElementProps | null | undefined): ...
function normalizeMixValue (line 369) | function normalizeMixValue(mix: unknown): unknown[] | undefined {
FILE: packages/component/src/lib/mixin.ts
type RebindNode (line 8) | type RebindNode<value, baseNode, boundNode> = value extends (
type RebindTuple (line 18) | type RebindTuple<args extends unknown[], baseNode, boundNode> = {
type MixinProps (line 22) | type MixinProps<
type MixinElement (line 29) | type MixinElement<
type MixinInsertEvent (line 39) | type MixinInsertEvent<node extends EventTarget = Element> = Event & {
type MixinReclaimedEvent (line 45) | type MixinReclaimedEvent<node extends EventTarget = Element> = Event & {
type MixinUpdateEvent (line 51) | type MixinUpdateEvent<node extends EventTarget = Element> = Event & {
type MixinBeforeRemoveEvent (line 55) | type MixinBeforeRemoveEvent = Event & {
type MixinHandleEventMap (line 59) | type MixinHandleEventMap<node extends EventTarget = Element> = {
type MixinHandle (line 71) | type MixinHandle<
type MixinRuntimeType (line 83) | type MixinRuntimeType<
type MixinType (line 99) | type MixinType<
type MixinDescriptor (line 115) | type MixinDescriptor<
type MixValue (line 128) | type MixValue<
type AnyMixinType (line 133) | type AnyMixinType = MixinRuntimeType<unknown[], Element, ElementProps>
type AnyMixinDescriptor (line 134) | type AnyMixinDescriptor = MixinDescriptor<Element, unknown[], ElementProps>
type AnyMixinRunner (line 135) | type AnyMixinRunner = (
type AnyMixinRunnerResult (line 138) | type AnyMixinRunnerResult = ReturnType<AnyMixinRunner>
type AnyMixinSetupResult (line 139) | type AnyMixinSetupResult = ReturnType<AnyMixinType> | AnyMixinRunnerResult
type AnyMixinHandle (line 140) | type AnyMixinHandle = MixinHandle<Element, ElementProps>
type ScopedAnyMixinHandle (line 141) | type ScopedAnyMixinHandle = AnyMixinHandle & {
type RunnerEntry (line 148) | type RunnerEntry = {
type MixinHandleFactoryOptions (line 154) | type MixinHandleFactoryOptions = {
type MixinRuntimeBinding (line 163) | type MixinRuntimeBinding = {
type ResolveMixedPropsInput (line 173) | type ResolveMixedPropsInput = {
type ResolveMixedPropsOutput (line 181) | type ResolveMixedPropsOutput = {
type MixinRuntimeState (line 186) | type MixinRuntimeState = {
function createMixin (line 209) | function createMixin<
function resolveMixedProps (line 222) | function resolveMixedProps(input: ResolveMixedPropsInput): ResolveMixedP...
function teardownMixins (line 319) | function teardownMixins(state?: MixinRuntimeState) {
function bindMixinRuntime (line 332) | function bindMixinRuntime(
function prepareMixinRemoval (line 354) | function prepareMixinRemoval(state?: MixinRuntimeState) {
function cancelPendingMixinRemoval (line 388) | function cancelPendingMixinRemoval(
function createMixinRuntimeState (line 398) | function createMixinRuntimeState(): MixinRuntimeState {
function createMixinHandle (line 406) | function createMixinHandle(options: {
class MixinHandleImpl (line 417) | class MixinHandleImpl
method constructor (line 439) | constructor(options: MixinHandleFactoryOptions) {
method signal (line 456) | get signal() {
method addEventListener (line 460) | addEventListener(
method removeEventListener (line 486) | removeEventListener(
method update (line 512) | update(): Promise<AbortSignal> {
method queueTask (line 528) | queueTask(task: (node: Element, signal: AbortSignal) => void): void {
method queueCommitTask (line 538) | queueCommitTask(task: () => void): void {
method setActiveScope (line 542) | setActiveScope(scope?: symbol): void {
method dispatchScopedEvent (line 550) | dispatchScopedEvent(scope: symbol, event: Event): void {
method releaseScope (line 557) | releaseScope(scope: symbol): void {
method #dispatchSchedulerPhaseToHandle (line 570) | #dispatchSchedulerPhaseToHandle(type: 'beforeUpdate' | 'commit', event...
method #getActiveScopeTarget (line 581) | #getActiveScopeTarget(): TypedEventTarget<MixinHandleEventMap<Element>> {
method #decrementGlobalPhaseCount (line 589) | #decrementGlobalPhaseCount(type: 'beforeUpdate' | 'commit', amount: nu...
function getMixinRuntimeSignal (line 601) | function getMixinRuntimeSignal(state: MixinRuntimeState): AbortSignal {
function dispatchMixinBeforeUpdate (line 613) | function dispatchMixinBeforeUpdate(state?: MixinRuntimeState) {
function dispatchMixinCommit (line 617) | function dispatchMixinCommit(state?: MixinRuntimeState) {
function dispatchMixinInsert (line 621) | function dispatchMixinInsert(
function dispatchMixinReclaimed (line 635) | function dispatchMixinReclaimed(
function dispatchMixinBeforeRemove (line 649) | function dispatchMixinBeforeRemove(
function queueMixinInsert (line 659) | function queueMixinInsert(
function queueMixinReclaimed (line 671) | function queueMixinReclaimed(
function queueMixinRemove (line 683) | function queueMixinRemove(handle: ScopedAnyMixinHandle, scope: symbol) {
function dispatchMixinRemoveEvent (line 690) | function dispatchMixinRemoveEvent(state?: MixinRuntimeState) {
function finalizeMixinTeardown (line 700) | function finalizeMixinTeardown(state: MixinRuntimeState) {
function dispatchMixinUpdateEvent (line 716) | function dispatchMixinUpdateEvent(
function isSchedulerPhaseType (line 733) | function isSchedulerPhaseType(type: string): type is 'beforeUpdate' | 'c...
function isBindingInUpdateScope (line 737) | function isBindingInUpdateScope(binding: MixinRuntimeBinding, parents: P...
function resolveMixDescriptors (line 748) | function resolveMixDescriptors(props: ElementProps): AnyMixinDescriptor[] {
function withoutMix (line 758) | function withoutMix(props: ElementProps): ElementProps {
function composeMixinProps (line 765) | function composeMixinProps(previous: ElementProps, next: ElementProps): ...
function isRemixElement (line 769) | function isRemixElement(value: unknown): value is RemixElement {
function isMixinElement (line 774) | function isMixinElement(value: unknown): value is MixinElement<Element, ...
function normalizeMixinRunner (line 779) | function normalizeMixinRunner(result: AnyMixinSetupResult, handle: AnyMi...
FILE: packages/component/src/lib/mixins/animate-layout-mixin.test.tsx
type MockAnimation (line 6) | interface MockAnimation {
function createMockAnimation (line 18) | function createMockAnimation(
function mockBoundingRect (line 38) | function mockBoundingRect(
function mockBoundingRectSequence (line 58) | function mockBoundingRectSequence(
FILE: packages/component/src/lib/mixins/animate-layout-mixin.tsx
type LayoutConfig (line 6) | type LayoutConfig = true | false | null | undefined | LayoutAnimationConfig
type Axis (line 8) | type Axis = { min: number; max: number }
type Box (line 9) | type Box = { x: Axis; y: Axis }
type AxisDelta (line 10) | type AxisDelta = { translate: number; scale: number; origin: number; ori...
type Delta (line 11) | type Delta = { x: AxisDelta; y: AxisDelta }
constant DEFAULT_DURATION (line 13) | const DEFAULT_DURATION = 200
constant DEFAULT_EASING (line 14) | const DEFAULT_EASING = 'ease-out'
constant SCALE_PRECISION (line 15) | const SCALE_PRECISION = 0.0001
constant TRANSLATE_PRECISION (line 16) | const TRANSLATE_PRECISION = 0.01
function createAxisDelta (line 18) | function createAxisDelta(): AxisDelta {
function createDelta (line 22) | function createDelta(): Delta {
function mix (line 26) | function mix(from: number, to: number, progress: number): number {
function isNear (line 30) | function isNear(value: number, target: number, threshold: number): boole...
function calcLength (line 34) | function calcLength(axis: Axis): number {
function calcAxisDelta (line 38) | function calcAxisDelta(delta: AxisDelta, source: Axis, target: Axis, ori...
function calcBoxDelta (line 57) | function calcBoxDelta(delta: Delta, source: Box, target: Box): void {
function mixAxisDelta (line 62) | function mixAxisDelta(output: AxisDelta, delta: AxisDelta, progress: num...
function mixDelta (line 69) | function mixDelta(output: Delta, delta: Delta, progress: number): void {
function copyAxisDeltaInto (line 74) | function copyAxisDeltaInto(target: AxisDelta, source: AxisDelta): void {
function copyDeltaInto (line 81) | function copyDeltaInto(target: Delta, source: Delta): void {
function isDeltaZero (line 86) | function isDeltaZero(delta: Delta): boolean {
function buildProjectionTransform (line 95) | function buildProjectionTransform(delta: Delta): string {
function buildTransformOrigin (line 107) | function buildTransformOrigin(delta: Delta): string {
function rectToBox (line 111) | function rectToBox(rect: DOMRect): Box {
function measureNaturalBox (line 118) | function measureNaturalBox(node: HTMLElement): Box {
function resolveLayoutConfig (line 129) | function resolveLayoutConfig(config: LayoutConfig): LayoutAnimationConfi...
function animateLayout (line 257) | function animateLayout<target extends EventTarget = Element>(
FILE: packages/component/src/lib/mixins/animate-mixins.tsx
type AnimateTiming (line 6) | type AnimateTiming = {
type AnimateStyleProps (line 14) | type AnimateStyleProps = {
type AnimateMixinConfig (line 18) | type AnimateMixinConfig = AnimateTiming & AnimateStyleProps
type AnimationConfig (line 20) | type AnimationConfig = true | false | null | undefined | AnimateMixinConfig
constant DEFAULT_ENTER (line 22) | const DEFAULT_ENTER: AnimateMixinConfig = {
constant DEFAULT_EXIT (line 28) | const DEFAULT_EXIT: AnimateMixinConfig = {
type AnimationState (line 34) | type AnimationState = {
function extractStyleProps (line 42) | function extractStyleProps(config: AnimateMixinConfig): Keyframe {
function buildEnterKeyframes (line 62) | function buildEnterKeyframes(config: AnimateMixinConfig): Keyframe[] {
function buildExitKeyframes (line 67) | function buildExitKeyframes(config: AnimateMixinConfig): Keyframe[] {
function resolveEnterConfig (line 72) | function resolveEnterConfig(config: AnimationConfig): AnimateMixinConfig...
function resolveExitConfig (line 78) | function resolveExitConfig(config: AnimationConfig): AnimateMixinConfig ...
function createAnimationOptions (line 84) | function createAnimationOptions(
function collectAnimatedProperties (line 97) | function collectAnimatedProperties(keyframes: Keyframe[]): string[] {
function toCssPropertyName (line 108) | function toCssPropertyName(property: string): string {
function readInlineStyle (line 114) | function readInlineStyle(style: CSSStyleDeclaration, property: string): ...
function writeInlineStyle (line 118) | function writeInlineStyle(style: CSSStyleDeclaration, property: string, ...
function trackAnimation (line 127) | function trackAnimation(node: Element, animation: Animation, keyframes: ...
function waitForAnimationOrAbort (line 139) | function waitForAnimationOrAbort(animation: Animation, signal: AbortSign...
function shouldSkipInitialEntrance (line 154) | function shouldSkipInitialEntrance(
function animateEntrance (line 268) | function animateEntrance<target extends EventTarget = Element>(
function animateExit (line 284) | function animateExit<target extends EventTarget = Element>(
FILE: packages/component/src/lib/mixins/css-mixin.test.tsx
function readAdoptedCssTexts (line 164) | function readAdoptedCssTexts(): string[] {
FILE: packages/component/src/lib/mixins/css-mixin.tsx
type StyleEntry (line 10) | type StyleEntry = { selector: string; css: string }
type StyleCache (line 11) | type StyleCache = Map<string, StyleEntry>
type StyleManagerLike (line 12) | type StyleManagerLike = {
function resolveStyleTarget (line 71) | function resolveStyleTarget(runtime: {
FILE: packages/component/src/lib/mixins/keys-mixin.tsx
type HTMLElementEventMap (line 22) | interface HTMLElementEventMap {
type KeysEventsMixin (line 73) | type KeysEventsMixin = typeof baseKeysEvents & {
FILE: packages/component/src/lib/mixins/link-mixin.test.tsx
function render (line 8) | function render(node: RemixNode) {
FILE: packages/component/src/lib/mixins/link-mixin.tsx
type LinkCurrentProps (line 11) | type LinkCurrentProps = ElementProps & {
function isDisabledElement (line 134) | function isDisabledElement(node: Element) {
FILE: packages/component/src/lib/mixins/on-mixin.test.tsx
type inferredEvent (line 200) | type inferredEvent = Assert<
type inferredTarget (line 203) | type inferredTarget = Assert<Equal<typeof event.currentTarget, HTMLButto...
type inferredSignal (line 204) | type inferredSignal = Assert<Equal<typeof signal, AbortSignal>>
FILE: packages/component/src/lib/mixins/on-mixin.tsx
type SignaledListener (line 11) | type SignaledListener<event extends Event> = (
type EventType (line 16) | type EventType<target extends Element> = Extract<AddEventType<target>, s...
type ListenerFor (line 17) | type ListenerFor<target extends Element, type extends EventType<target>>...
function on (line 74) | function on<
FILE: packages/component/src/lib/mixins/press-mixin.test.tsx
function dispatchPointer (line 422) | function dispatchPointer(
FILE: packages/component/src/lib/mixins/press-mixin.tsx
type HTMLElementEventMap (line 10) | interface HTMLElementEventMap {
class PressEvent (line 22) | class PressEvent extends Event {
method constructor (line 33) | constructor(
type PressEventsMixin (line 189) | type PressEventsMixin = typeof basePressEvents & {
FILE: packages/component/src/lib/mixins/ref-mixin.test.tsx
function Toggle (line 72) | function Toggle(handle: Handle) {
function Example (line 101) | function Example(handle: Handle) {
type inferredNode (line 130) | type inferredNode = Assert<Equal<typeof node, HTMLButtonElement>>
type inferredSignal (line 131) | type inferredSignal = Assert<Equal<typeof signal, AbortSignal>>
FILE: packages/component/src/lib/mixins/ref-mixin.tsx
type RefCallback (line 7) | type RefCallback<node extends EventTarget> = (node: node, signal: AbortS...
FILE: packages/component/src/lib/navigation.ts
type NavigationState (line 3) | type NavigationState = {
type SourceElementNavigateEvent (line 10) | type SourceElementNavigateEvent = NavigateEvent & {
type NavigationOptions (line 17) | type NavigationOptions = {
function navigate (line 30) | async function navigate(href: string, options?: NavigationOptions) {
function startNavigationListener (line 46) | function startNavigationListener(signal: AbortSignal) {
function isRuntimeNavigation (line 85) | function isRuntimeNavigation(info: unknown): info is NavigationState {
function getRuntimeNavigationState (line 89) | function getRuntimeNavigationState(event: NavigateEvent): NavigationStat...
function getTraverseNavigationState (line 101) | function getTraverseNavigationState(event: NavigateEvent): NavigationSta...
function getSourceElementNavigationState (line 121) | function getSourceElementNavigationState(event: NavigateEvent): Navigati...
FILE: packages/component/src/lib/reconcile.ts
constant SVG_NS (line 47) | const SVG_NS = 'http://www.w3.org/2000/svg'
constant INSERT_VNODE (line 50) | const INSERT_VNODE = 1 << 0
constant MATCHED (line 51) | const MATCHED = 1 << 1
function getSvgContext (line 60) | function getSvgContext(vParent: VNode, nodeType: VNodeType): boolean {
function getHostProps (line 72) | function getHostProps(node: HostNode | CommittedHostNode): ElementProps {
function markNodePersistedByMixins (line 76) | function markNodePersistedByMixins(node: CommittedHostNode, domParent: P...
function unmarkNodePersistedByMixins (line 84) | function unmarkNodePersistedByMixins(node: CommittedHostNode) {
function findMatchingPersistedMixinNode (line 91) | function findMatchingPersistedMixinNode(
type ControlledReflectionState (line 106) | type ControlledReflectionState = {
function ensureControlledReflection (line 120) | function ensureControlledReflection(
function syncControlledReflection (line 157) | function syncControlledReflection(node: CommittedHostNode, props: Elemen...
function shouldTrackControlledReflection (line 170) | function shouldTrackControlledReflection(props: ElementProps): boolean {
function scheduleControlledRestore (line 174) | function scheduleControlledRestore(
function restoreControlledReflections (line 187) | function restoreControlledReflections(
function teardownControlledReflection (line 200) | function teardownControlledReflection(node: CommittedHostNode): void {
function canManageValue (line 212) | function canManageValue(type: string, element: Element): boolean {
function hasControlledValueProp (line 217) | function hasControlledValueProp(props: ElementProps): boolean {
function hasControlledCheckedProp (line 221) | function hasControlledCheckedProp(props: ElementProps): boolean {
function canReflectProperty (line 225) | function canReflectProperty(
function readDomProp (line 232) | function readDomProp(element: Element, key: string): unknown {
function setPropertyReflection (line 237) | function setPropertyReflection(element: Element, key: string, value: unk...
function resolveNodeMixProps (line 242) | function resolveNodeMixProps(
function enqueueMixinBindingUpdate (line 267) | function enqueueMixinBindingUpdate(
function bindNodeMixRuntime (line 291) | function bindNodeMixRuntime(
function isHeadHostNode (line 315) | function isHeadHostNode(node: HostNode): boolean {
function getDocumentHead (line 319) | function getDocumentHead(domParent: ParentNode): HTMLHeadElement | null {
function diffVNodes (line 329) | function diffVNodes(
function replace (line 403) | function replace(
function diffHost (line 432) | function diffHost(
function setupHostNode (line 491) | function setupHostNode(node: HostNode, dom: Element, scheduler: Schedule...
function diffText (line 502) | function diffText(curr: CommittedTextNode, next: TextNode, vParent: VNod...
function insert (line 510) | function insert(
function diffFrame (line 752) | function diffFrame(
function insertFrame (line 809) | function insertFrame(
function resolveClientFrame (line 916) | function resolveClientFrame(node: VNode, runtime: FrameRuntime, rootTarg...
function disposeFrameResources (line 945) | function disposeFrameResources(node: VNode): void {
function asAbortableFrameContent (line 959) | function asAbortableFrameContent(content: FrameContent, signal: AbortSig...
function createAbortableReadableStream (line 964) | function createAbortableReadableStream(
function removeFrameDomRange (line 1015) | function removeFrameDomRange(node: VNode, domParent: ParentNode): void {
function getFrameRuntime (line 1034) | function getFrameRuntime(frame: FrameHandle): FrameRuntime | undefined {
function getFrameSrc (line 1038) | function getFrameSrc(node: VNode): string {
function getFrameName (line 1044) | function getFrameName(node: VNode): string | undefined {
function randomFrameId (line 1049) | function randomFrameId(): string {
function skipCommentsExceptFrameStart (line 1053) | function skipCommentsExceptFrameStart(cursor: Node | null): Node | null {
function isFrameStartComment (line 1061) | function isFrameStartComment(node: Node | null | undefined): node is Com...
function isFrameEndComment (line 1065) | function isFrameEndComment(node: Node | null | undefined): node is Comme...
function getFrameIdFromComment (line 1069) | function getFrameIdFromComment(comment: Comment): string | undefined {
function findFrameEndComment (line 1075) | function findFrameEndComment(start: Comment): Comment | null {
function renderComponent (line 1091) | function renderComponent(
function diffComponent (line 1132) | function diffComponent(
function cleanupDescendants (line 1198) | function cleanupDescendants(node: VNode, scheduler: Scheduler, styles: S...
function remove (line 1234) | function remove(
function performHostNodeRemoval (line 1288) | function performHostNodeRemoval(
function diffChildren (line 1314) | function diffChildren(
function findFirstDomAnchor (line 1579) | function findFirstDomAnchor(node: VNode | null | undefined): Node | null {
function findLastDomAnchor (line 1594) | function findLastDomAnchor(node: VNode | null | undefined): Node | null {
function domRangeContainsNode (line 1609) | function domRangeContainsNode(first: Node, last: Node, node: Node): bool...
function moveDomRange (line 1619) | function moveDomRange(domParent: ParentNode, first: Node, last: Node, be...
function setActiveSchedulerUpdateParents (line 1629) | function setActiveSchedulerUpdateParents(parents: ParentNode[] | undefin...
function shouldDispatchInlineMixinLifecycle (line 1633) | function shouldDispatchInlineMixinLifecycle(node: Node): boolean {
function findNextSiblingDomAnchor (line 1644) | function findNextSiblingDomAnchor(curr: VNode, vParent?: VNode): Node | ...
function reclaimPersistedMixinNode (line 1656) | function reclaimPersistedMixinNode(
FILE: packages/component/src/lib/run.ts
type RunInit (line 14) | type RunInit = {
type AppRuntimeEventMap (line 22) | type AppRuntimeEventMap = {
type AppRuntime (line 29) | type AppRuntime = TypedEventTarget<AppRuntimeEventMap> & {
function getTopFrame (line 41) | function getTopFrame(): FrameHandle {
function getNamedFrame (line 53) | function getNamedFrame(name: string): FrameHandle {
function run (line 63) | function run(init: RunInit): AppRuntime {
FILE: packages/component/src/lib/scheduler.ts
type EmptyFn (line 13) | type EmptyFn = () => void
type SchedulerPhaseType (line 14) | type SchedulerPhaseType = 'beforeUpdate' | 'commit'
type SchedulerPhaseListener (line 15) | type SchedulerPhaseListener = EventListenerOrEventListenerObject | null
type Scheduler (line 20) | type Scheduler = ReturnType<typeof createScheduler>
constant MAX_CASCADING_UPDATES (line 23) | const MAX_CASCADING_UPDATES = 50
type SchedulerPhaseEvent (line 25) | type SchedulerPhaseEvent = Event & {
function createScheduler (line 37) | function createScheduler(
FILE: packages/component/src/lib/spring.ts
type SpringPreset (line 15) | type SpringPreset = 'smooth' | 'snappy' | 'bouncy'
type SpringOptions (line 20) | interface SpringOptions {
type SpringIterator (line 32) | interface SpringIterator extends IterableIterator<number> {
function spring (line 88) | function spring(
function resolveOptions (line 140) | function resolveOptions(
type ComputedSpring (line 159) | interface ComputedSpring {
function computeSpring (line 166) | function computeSpring(options: SpringOptions): ComputedSpring {
function generateEasing (line 245) | function generateEasing(position: (t: number) => number, settlingTime: n...
function adaptiveSample (line 262) | function adaptiveSample(
FILE: packages/component/src/lib/stream.ts
type VNode (line 7) | interface VNode {
function createVNode (line 15) | function createVNode(type: ElementType, props: ElementProps, key?: Key):...
type RenderToStreamOptions (line 22) | interface RenderToStreamOptions {
type ResolveFrameContext (line 40) | interface ResolveFrameContext {
type HydrationData (line 47) | interface HydrationData {
type FrameData (line 53) | interface FrameData {
type RenderContext (line 59) | interface RenderContext {
type ResolvedFrameHtml (line 77) | interface ResolvedFrameHtml {
type SsrFrameState (line 82) | interface SsrFrameState {
type Segment (line 87) | type Segment =
constant SELF_CLOSING_TAGS (line 97) | const SELF_CLOSING_TAGS = new Set([
constant NUMERIC_CSS_PROPS (line 114) | const NUMERIC_CSS_PROPS = new Set([
constant FRAMEWORK_PROPS (line 133) | const FRAMEWORK_PROPS = new Set(['children', 'innerHTML', 'on', 'key', '...
constant SSR_MIXIN_SIGNAL (line 134) | const SSR_MIXIN_SIGNAL = createSsrThrowingSignal()
function createSsrSignalError (line 136) | function createSsrSignalError() {
function createSsrThrowingSignal (line 140) | function createSsrThrowingSignal(): AbortSignal {
function renderToStream (line 163) | function renderToStream(
function defaultResolveFrame (line 221) | function defaultResolveFrame(): never {
function normalizeFrameSrc (line 225) | function normalizeFrameSrc(value?: string | URL): string {
function createSsrFrameState (line 229) | function createSsrFrameState(frameSrc: string, topFrameSrc = frameSrc): ...
function getResolveFrameContext (line 235) | function getResolveFrameContext(frameState: SsrFrameState): ResolveFrame...
function randomId (line 242) | function randomId(prefix: string): string {
function createServerComponentId (line 246) | function createServerComponentId(context: RenderContext): string {
function splitFirstChunk (line 251) | async function splitFirstChunk(
function resolveFrameHtml (line 299) | async function resolveFrameHtml(
function isRemixElement (line 309) | function isRemixElement(node: unknown): node is RemixElement {
function staticSeg (line 313) | function staticSeg(html: string): Segment {
function compositeSeg (line 317) | function compositeSeg(parts: Segment[]): Segment {
function buildSegment (line 321) | function buildSegment(node: RemixNode, context: RenderContext, frameStat...
function buildFrameSegment (line 377) | function buildFrameSegment(props: any, context: RenderContext, frameStat...
function buildElementSegment (line 416) | function buildElementSegment(
function buildHeadElementSegment (line 447) | function buildHeadElementSegment(
function renderAttributes (line 464) | function renderAttributes(props: any, isSvg: boolean): string {
function resolveSsrMixedProps (line 485) | function resolveSsrMixedProps(
function resolveSsrMixinRunner (line 548) | function resolveSsrMixinRunner(
function createSsrMixinHandle (line 566) | function createSsrMixinHandle(hostType: string, context: RenderContext, ...
function resolveSsrMixDescriptors (line 602) | function resolveSsrMixDescriptors(props: ElementProps): Array<{ type: an...
function withoutSsrMix (line 612) | function withoutSsrMix(props: ElementProps): ElementProps {
function isSsrMixinElement (line 619) | function isSsrMixinElement(
function buildComponentSegment (line 626) | function buildComponentSegment(
function createHydrationPropsReplacer (line 671) | function createHydrationPropsReplacer(context: RenderContext, frameState...
function buildEntrySegment (line 775) | function buildEntrySegment(
function resolveBlocking (line 798) | async function resolveBlocking(segment: Segment): Promise<void> {
function serializeSegment (line 815) | function serializeSegment(seg: Segment): string {
function escapeHtml (line 825) | function escapeHtml(str: string): string {
function escapeTextContent (line 834) | function escapeTextContent(str: string): string {
function escapeTemplateContent (line 838) | function escapeTemplateContent(html: string): string {
function transformAttributeName (line 842) | function transformAttributeName(name: string, isSvg: boolean): string {
function finalizeHtml (line 859) | function finalizeHtml(html: string, context: RenderContext): string {
function processStyleProps (line 910) | function processStyleProps(props: any): any {
function collectAllStyles (line 928) | function collectAllStyles(context: RenderContext): string {
function buildRmxDataScript (line 938) | function buildRmxDataScript(context: RenderContext): string {
function escapeScriptJson (line 960) | function escapeScriptJson(json: string): string {
function serializeStyleObject (line 965) | function serializeStyleObject(style: Record<string, any>): string {
function streamPendingFrames (line 999) | async function streamPendingFrames(
function streamByteStreams (line 1032) | async function streamByteStreams(
function drain (line 1055) | async function drain(stream: ReadableStream<Uint8Array>): Promise<string> {
function renderToString (line 1075) | async function renderToString(node: RemixNode): Promise<string> {
FILE: packages/component/src/lib/style/index.ts
type StyleManager (line 10) | type StyleManager = ReturnType<typeof createStyleManager>
FILE: packages/component/src/lib/style/lib/style.ts
type DOMStyleProperties (line 1) | type DOMStyleProperties = {
type AllStyleProperties (line 7) | type AllStyleProperties = {
type StyleProps (line 10) | interface StyleProps extends AllStyleProperties, DOMStyleProperties {
type CSSProps (line 15) | interface CSSProps extends DOMStyleProperties {
type StyleObject (line 19) | type StyleObject = Record<string, unknown>
function camelToKebab (line 22) | function camelToKebab(str: string): string {
constant NUMERIC_CSS_PROPS (line 27) | const NUMERIC_CSS_PROPS = new Set([
function normalizeCssValue (line 50) | function normalizeCssValue(key: string, value: unknown): string {
function isComplexSelector (line 62) | function isComplexSelector(key: string): boolean {
function isKeyframesAtRule (line 73) | function isKeyframesAtRule(key: string): boolean {
function hashStyle (line 85) | function hashStyle(obj: any): string {
function styleToCss (line 99) | function styleToCss(styles: StyleObject, selector: string = ''): string {
function indent (line 192) | function indent(text: string, spaces: number): string {
function isRecord (line 201) | function isRecord(value: unknown): value is Record<string, unknown> {
function toRecord (line 205) | function toRecord(value: unknown): Record<string, unknown> | null {
function keyframesBodyToCss (line 210) | function keyframesBodyToCss(frames: StyleObject): string {
function atRuleBodyToCss (line 239) | function atRuleBodyToCss(styles: StyleObject): string {
function processStyleClass (line 278) | function processStyleClass(
function clearStyleCache (line 310) | function clearStyleCache(styleCache: Map<string, { selector: string; css...
FILE: packages/component/src/lib/style/lib/stylesheet.ts
type RuleEntry (line 4) | type RuleEntry = { count: number; index: number }
type ActiveManager (line 5) | type ActiveManager = { layer: string; ruleMap: Map<string, RuleEntry> }
type ServerStyleState (line 7) | type ServerStyleState = {
function isHtmlStyleElement (line 20) | function isHtmlStyleElement(node: unknown): node is HTMLStyleElement {
function getLayerName (line 24) | function getLayerName(rule: CSSRule): string | null {
function isCssStyleRule (line 31) | function isCssStyleRule(rule: CSSRule): rule is CSSStyleRule {
function walkRulesForSelectors (line 36) | function walkRulesForSelectors(
function seedManagersWithServerSelectors (line 66) | function seedManagersWithServerSelectors(layerName: string, selectors: S...
function ensureServerStyleState (line 78) | function ensureServerStyleState(): ServerStyleState {
function adoptAllServerStyleTags (line 100) | function adoptAllServerStyleTags() {
function startServerStyleObserver (line 110) | function startServerStyleObserver() {
function adoptServerStyleTag (line 141) | function adoptServerStyleTag(styleEl: HTMLStyleElement) {
function teardownServerStyleStateIfUnused (line 212) | function teardownServerStyleStateIfUnused() {
function createStyleManager (line 227) | function createStyleManager(layer: string = 'rmx') {
FILE: packages/component/src/lib/svg-attributes.ts
constant XLINK_NS (line 1) | const XLINK_NS = 'http://www.w3.org/1999/xlink'
constant XML_NS (line 2) | const XML_NS = 'http://www.w3.org/XML/1998/namespace'
constant CANONICAL_CAMEL_SVG_ATTRS (line 4) | const CANONICAL_CAMEL_SVG_ATTRS = new Set([
constant SVG_ATTR_ALIASES (line 66) | const SVG_ATTR_ALIASES = new Map<string, string>()
constant NAMESPACED_SVG_ALIASES (line 71) | const NAMESPACED_SVG_ALIASES = new Map([
function normalizeSvgAttributeName (line 107) | function normalizeSvgAttributeName(name: string): string {
function normalizeSvgAttribute (line 116) | function normalizeSvgAttribute(name: string): {
function camelToKebab (line 128) | function camelToKebab(input: string): string {
FILE: packages/component/src/lib/to-vnode.ts
function flatMapChildrenToVNodes (line 6) | function flatMapChildrenToVNodes(node: RemixElement): VNode[] {
function flattenRemixNodeArray (line 14) | function flattenRemixNodeArray(nodes: RemixNode[], out: RemixNode[] = []...
function toVNode (line 25) | function toVNode(node: RemixNode): VNode {
FILE: packages/component/src/lib/tween.ts
function solveCubicBezierX (line 9) | function solveCubicBezierX(x1: number, x2: number, targetX: number): num...
function cubicBezier (line 38) | function cubicBezier(t: number, p1: number, p2: number): number {
function cubicBezierDerivative (line 51) | function cubicBezierDerivative(t: number, p1: number, p2: number): number {
type BezierCurve (line 60) | interface BezierCurve {
type TweenOptions (line 86) | interface TweenOptions {
FILE: packages/component/src/lib/typed-event-target.ts
class TypedEventTarget (line 4) | class TypedEventTarget<eventMap> extends EventTarget {
type TypedEventTarget (line 14) | interface TypedEventTarget<eventMap> {
type TypedEventListener (line 65) | type TypedEventListener<eventMap> = {
FILE: packages/component/src/lib/vdom.ts
type VirtualRootEventMap (line 21) | type VirtualRootEventMap = {
type VirtualRoot (line 28) | type VirtualRoot = TypedEventTarget<VirtualRootEventMap> & {
type VirtualRootOptions (line 37) | type VirtualRootOptions = {
function getHydrationComponentIdFromRangeStart (line 56) | function getHydrationComponentIdFromRangeStart(start: Node): string | un...
function createRangeRoot (line 71) | function createRangeRoot(
function createRoot (line 172) | function createRoot(container: HTMLElement, options: VirtualRootOptions ...
function createRootFrameHandle (line 250) | function createRootFrameHandle(init: {
FILE: packages/component/src/lib/vnode.ts
constant TEXT_NODE (line 5) | const TEXT_NODE = Symbol('TEXT_NODE')
constant ROOT_VNODE (line 6) | const ROOT_VNODE = Symbol('ROOT_VNODE')
type VNodeType (line 8) | type VNodeType =
type VNode (line 16) | type VNode<T extends VNodeType = VNodeType> = {
type FragmentNode (line 58) | type FragmentNode = VNode & {
type TextNode (line 63) | type TextNode = VNode & {
type CommittedTextNode (line 68) | type CommittedTextNode = TextNode & {
type HostNode (line 72) | type HostNode = VNode & {
type CommittedHostNode (line 78) | type CommittedHostNode = HostNode & {
type ComponentNode (line 83) | type ComponentNode = VNode & {
type CommittedComponentNode (line 89) | type CommittedComponentNode = VNode & {
function isFragmentNode (line 96) | function isFragmentNode(node: VNode): node is FragmentNode {
function isTextNode (line 100) | function isTextNode(node: VNode): node is TextNode {
function isCommittedTextNode (line 104) | function isCommittedTextNode(node: VNode): node is CommittedTextNode {
function isHostNode (line 108) | function isHostNode(node: VNode): node is HostNode {
function isCommittedHostNode (line 112) | function isCommittedHostNode(node: VNode): node is CommittedHostNode {
function isComponentNode (line 116) | function isComponentNode(node: VNode): node is ComponentNode {
function isCommittedComponentNode (line 120) | function isCommittedComponentNode(node: VNode): node is CommittedCompone...
function isRemixElement (line 124) | function isRemixElement(node: RemixNode): node is RemixElement {
function findContextFromAncestry (line 128) | function findContextFromAncestry(node: VNode, type: Component): unknown {
FILE: packages/component/src/test/client-entry.test.tsx
function Input (line 8) | function Input(handle: Handle, props: { defaultValue?: string }) {
function Input (line 28) | function Input(handle: Handle, props: { defaultValue?: string; func: () ...
function Input2 (line 40) | function Input2(handle: Handle, props: { defaultValue?: string }) {
function TestComponent (line 58) | function TestComponent(handle: Handle, props: { count: number }) {
function MyComponent (line 70) | function MyComponent() {
function NamedComponent (line 81) | function NamedComponent() {
function TestComponent (line 92) | function TestComponent(handle: Handle, props: { initialCount: number }) {
function TestComponent (line 130) | function TestComponent() {
function TestComponent (line 153) | function TestComponent() {
function ValidComponent (line 169) | function ValidComponent(
function InvalidComponent (line 189) | function InvalidComponent(handle: Handle, props: { func: () => void }) {
function Counter (line 200) | function Counter(handle: Handle, setup: number) {
function NullSetup (line 210) | function NullSetup(handle: Handle, setup: null) {
function UndefinedSetup (line 214) | function UndefinedSetup(handle: Handle, setup: undefined) {
function ArraySetup (line 226) | function ArraySetup(handle: Handle, setup: string[]) {
function TestComponent (line 237) | function TestComponent() {
function RegularComponent (line 246) | function RegularComponent() {
function normalFunction (line 262) | function normalFunction() {}
function Counter (line 269) | function Counter(handle: Handle, setupProps: { initialCount: number }) {
function SimpleComponent (line 287) | function SimpleComponent(handle: Handle, props: { message: string }) {
FILE: packages/component/src/test/diff-dom.test.tsx
function diffDom (line 5) | function diffDom(container: HTMLElement, next: string) {
FILE: packages/component/src/test/event-listeners.test.tsx
function App (line 48) | function App(handle: Handle) {
method click (line 75) | click(event) {
method click (line 90) | click(_event, signal) {
function App (line 110) | function App(handle: Handle) {
function App (line 121) | function App(handle: Handle) {
type PingEventMap (line 133) | type PingEventMap = {
class PingTarget (line 137) | class PingTarget extends TypedEventTarget<PingEventMap> {}
type test (line 144) | type test = Assert<
FILE: packages/component/src/test/frame.test.tsx
function getCommentMarkerId (line 12) | function getCommentMarkerId(html: string, prefix: 'rmx:f:' | 'rmx:h:'): ...
function streamFromChunks (line 19) | function streamFromChunks(chunks: Array<string | Promise<string>>): Read...
function renderInitialBody (line 193) | async function renderInitialBody() {
function renderReloadDocument (line 204) | async function renderReloadDocument() {
method resolveFrame (line 222) | async resolveFrame(src: string) {
function A (line 551) | function A(handle: Handle) {
function B (line 568) | function B(handle: Handle) {
function A (line 626) | function A() {
function renderTimeFragment (line 665) | async function renderTimeFragment() {
method loadModule (line 701) | loadModule(moduleUrl, exportName) {
function renderInitial (line 759) | async function renderInitial(): Promise<string> {
method loadModule (line 781) | loadModule(moduleUrl, exportName) {
method resolveFrame (line 787) | resolveFrame(src: string) {
function resolveFrame (line 826) | async function resolveFrame(src: string) {
method loadModule (line 851) | loadModule(moduleUrl, exportName) {
function renderInner (line 885) | async function renderInner() {
method resolveFrame (line 895) | resolveFrame(src: string) {
method loadModule (line 904) | loadModule(moduleUrl, exportName) {
method resolveFrame (line 910) | async resolveFrame(src: string) {
function resolveFrame (line 975) | async function resolveFrame(src: string) {
method loadModule (line 998) | loadModule(moduleUrl, exportName) {
function renderTimeFragmentWithCss (line 1038) | async function renderTimeFragmentWithCss() {
method loadModule (line 1068) | loadModule(moduleUrl, exportName) {
function resolveFrame (line 1112) | async function resolveFrame(src: string) {
method loadModule (line 1139) | loadModule(moduleUrl, exportName) {
function renderInitial (line 1182) | async function renderInitial() {
method loadModule (line 1204) | loadModule(moduleUrl, exportName) {
method resolveFrame (line 1210) | resolveFrame(src: string, signal?: AbortSignal) {
function renderHeadFragment (line 1270) | async function renderHeadFragment() {
method loadModule (line 1302) | loadModule(moduleUrl, exportName) {
function renderInner (line 1361) | async function renderInner(): Promise<string> {
method resolveFrame (line 1475) | resolveFrame(src: string) {
function Counter (line 1527) | function Counter() {
function renderInner (line 1776) | async function renderInner() {
function CaptureRuntime (line 1936) | function CaptureRuntime(handle: Handle) {
method loadModule (line 2100) | loadModule(moduleUrl, exportName) {
method resolveFrame (line 2148) | resolveFrame(src: string) {
method loadModule (line 2167) | loadModule(moduleUrl, exportName) {
method loadModule (line 2224) | loadModule(moduleUrl, exportName) {
function Shell (line 2247) | function Shell(handle: Handle) {
function renderInitial (line 2312) | async function renderInitial(): Promise<string> {
method resolveFrame (line 2331) | resolveFrame(src: string) {
method loadModule (line 2359) | loadModule(moduleUrl, exportName) {
method resolveFrame (line 2365) | resolveFrame(src: string) {
function Shell (line 2396) | function Shell(handle: Handle) {
method resolveFrame (line 2419) | resolveFrame(src: string) {
method resolveFrame (line 2457) | resolveFrame(src: string) {
FILE: packages/component/src/test/hydration.components.test.tsx
function NullComponent (line 26) | function NullComponent() {
function FragmentComponent (line 55) | function FragmentComponent() {
function Counter (line 126) | function Counter(handle: Handle, setup: number) {
function Provider (line 166) | function Provider(handle: Handle<{ value: string }>) {
function Consumer (line 171) | function Consumer(handle: Handle) {
function WithConnect (line 279) | function WithConnect() {
function Clickable (line 310) | function Clickable() {
FILE: packages/component/src/test/hydration.text.test.tsx
function render (line 53) | function render() {
FILE: packages/component/src/test/jsx.test.tsx
type MixItem (line 7) | type MixItem<mix> = mix extends ReadonlyArray<infer descriptor> ? descri...
type NormalizedMix (line 8) | type NormalizedMix<mix> = Array<MixItem<Exclude<mix, undefined>>> | unde...
type dispatchedEvent (line 36) | type dispatchedEvent = Assert<
type eventTarget (line 39) | type eventTarget = Assert<Equal<typeof event.currentTarget, HTMLButtonEl...
function Counter (line 51) | function Counter(handle: Handle, setup: number) {
function Counter (line 78) | function Counter(handle: Handle<number>, setup: number) {
function Button (line 106) | function Button() {
type inferredButtonProps (line 130) | type inferredButtonProps = Assert<Equal<typeof props, Props<'button'>>>
type inferredInsertNode (line 151) | type inferredInsertNode = Assert<Equal<typeof event.node, HTMLInputEleme...
type inferredEvent (line 163) | type inferredEvent = Assert<
type inferredTarget (line 166) | type inferredTarget = Assert<Equal<typeof event.currentTarget, HTMLButto...
type inferredSignal (line 167) | type inferredSignal = Assert<Equal<typeof signal, AbortSignal>>
type inferredEvent (line 178) | type inferredEvent = Assert<
type inferredTarget (line 181) | type inferredTarget = Assert<Equal<typeof event.currentTarget, HTMLEleme...
type inferredSignal (line 182) | type inferredSignal = Assert<Equal<typeof signal, AbortSignal>>
type inferredNode (line 196) | type inferredNode = Assert<Equal<typeof node, HTMLButtonElement>>
type inferredSignal (line 197) | type inferredSignal = Assert<Equal<typeof signal, AbortSignal>>
FILE: packages/component/src/test/navigation.test.ts
method getTopFrame (line 4) | getTopFrame() {
method getNamedFrame (line 10) | getNamedFrame() {
method addEventListener (line 113) | addEventListener(type: string, listener: EventListener) {
FILE: packages/component/src/test/stream.test.tsx
function getLatestRmxDataScript (line 14) | function getLatestRmxDataScript(root: ParentNode): HTMLScriptElement {
function parseRmxDataFromHtml (line 21) | function parseRmxDataFromHtml(html: string): any {
function getSingleEntry (line 28) | function getSingleEntry(obj: Record<string, any>): [string, any] {
function getCommentMarkerId (line 34) | function getCommentMarkerId(html: string, prefix: 'rmx:f:' | 'rmx:h:'): ...
function Greeting (line 134) | function Greeting(handle: Handle) {
function Test (line 143) | function Test(handle: Handle) {
function Stateful (line 153) | function Stateful(handle: Handle) {
type ThemeContext (line 162) | type ThemeContext = { color: string; size: number }
function ThemeProvider (line 164) | function ThemeProvider(handle: Handle<ThemeContext>) {
function ThemedText (line 169) | function ThemedText(handle: Handle) {
function App (line 174) | function App(handle: Handle) {
type ThemeContext (line 190) | type ThemeContext = { color: string }
type UserContext (line 191) | type UserContext = { name: string }
function ThemeProvider (line 193) | function ThemeProvider(handle: Handle<ThemeContext>) {
function UserProvider (line 198) | function UserProvider(handle: Handle<UserContext>) {
function Display (line 203) | function Display(handle: Handle) {
function App (line 209) | function App(handle: Handle) {
type CountContext (line 227) | type CountContext = { count: number }
function CountProvider (line 229) | function CountProvider(handle: Handle<CountContext>) {
function CountDisplay (line 234) | function CountDisplay(handle: Handle) {
function DoubleDisplay (line 239) | function DoubleDisplay(handle: Handle) {
function App (line 244) | function App(handle: Handle) {
function Inspect (line 270) | function Inspect(handle: Handle) {
function StyledButton (line 662) | function StyledButton(handle: Handle) {
function BadComponent (line 783) | function BadComponent() {
function BadComponent (line 799) | function BadComponent() {
function SEO (line 882) | function SEO(handle: Handle) {
function UnwrappedChild (line 1169) | function UnwrappedChild(handle: Handle) {
function DeepChild (line 1177) | function DeepChild(handle: Handle) {
function resolveFrame (line 1381) | async function resolveFrame(src: string) {
function resolveFrame (line 1424) | async function resolveFrame(src: string) {
function resolveFrame (line 1605) | async function resolveFrame(src: string) {
function Inspect (line 1694) | function Inspect(handle: Handle) {
function resolveFrame (line 1706) | async function resolveFrame(
function resolveFrame (line 1787) | async function resolveFrame(src: string) {
function resolveFrame (line 1838) | async function resolveFrame(src: string) {
function resolveFrame (line 1981) | async function resolveFrame(src: string) {
FILE: packages/component/src/test/utils.ts
type Assert (line 1) | type Assert<T extends true> = T
type Equal (line 3) | type Equal<X, Y> =
function drain (line 6) | async function drain(stream: ReadableStream<Uint8Array>): Promise<string> {
function readChunks (line 20) | function readChunks(stream: ReadableStream<Uint8Array>): AsyncGenerator<...
function withResolvers (line 33) | function withResolvers<T = unknown>(): [
FILE: packages/component/src/test/vdom.components.test.tsx
function App (line 19) | function App() {
function App (line 31) | function App(handle: Handle) {
function App (line 61) | function App(handle: Handle) {
function App (line 100) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.connect.test.tsx
function App (line 14) | function App(handle: Handle) {
function App (line 48) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.context.test.tsx
function App (line 10) | function App(handle: Handle<{ value: string }>) {
function Child (line 15) | function Child(handle: Handle) {
function App (line 33) | function App(handle: Handle<{ value: string }>) {
function Child (line 42) | function Child(handle: Handle) {
function Listbox (line 68) | function Listbox(handle: Handle<{ registerOption: (option: string) => vo...
function Option (line 83) | function Option(handle: Handle) {
function App (line 91) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.controlled-props.test.tsx
function App (line 37) | function App(handle: Handle) {
function App (line 81) | function App(handle: Handle) {
function App (line 139) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.dom-order.test.tsx
function A (line 14) | function A(handle: Handle) {
function B (line 45) | function B() {
function A (line 49) | function A(handle: Handle) {
function Loading (line 80) | function Loading() {
function PageB (line 87) | function PageB(handle: Handle) {
function PageA (line 92) | function PageA() {
function App (line 98) | function App(handle: Handle) {
function Loading (line 126) | function Loading() {
function A (line 133) | function A(handle: Handle) {
function Inner (line 161) | function Inner() {
function Middle (line 165) | function Middle() {
function Outer (line 172) | function Outer(handle: Handle) {
function LoadingA (line 200) | function LoadingA() {
function LoadingB (line 204) | function LoadingB() {
function CompA (line 213) | function CompA(handle: Handle) {
function CompB (line 218) | function CompB(handle: Handle) {
function PageB (line 256) | function PageB(handle: Handle) {
function PageA (line 261) | function PageA() {
function App (line 267) | function App(handle: Handle) {
function List (line 316) | function List(handle: Handle) {
FILE: packages/component/src/test/vdom.errors.test.tsx
function BadComponent (line 60) | function BadComponent() {
function BadChild (line 78) | function BadChild() {
function Parent (line 83) | function Parent() {
function BadComponent (line 106) | function BadComponent() {
function Component (line 128) | function Component(handle: Handle) {
function Component (line 213) | function Component(handle: Handle) {
function Component (line 236) | function Component(handle: Handle) {
function Bad (line 267) | function Bad(handle: Handle) {
function Good (line 274) | function Good(handle: Handle) {
function Bad (line 300) | function Bad() {
function Component (line 318) | function Component(handle: Handle) {
function InfiniteLoop (line 375) | function InfiniteLoop(handle: Handle) {
function Counter (line 411) | function Counter(handle: Handle) {
FILE: packages/component/src/test/vdom.events.test.tsx
function App (line 39) | function App() {
FILE: packages/component/src/test/vdom.insert-remove.test.tsx
function App (line 141) | function App() {
FILE: packages/component/src/test/vdom.keys.test.tsx
type CardData (line 12) | type CardData = { id: string; title: string }
function Card (line 14) | function Card() {
function Column (line 18) | function Column() {
function List (line 64) | function List() {
function List (line 92) | function List() {
function List (line 127) | function List() {
function List (line 163) | function List() {
function List (line 198) | function List() {
function List (line 232) | function List() {
function List (line 269) | function List() {
function Item (line 339) | function Item() {
function List (line 372) | function List() {
function Item (line 480) | function Item() {
FILE: packages/component/src/test/vdom.mixins.test.tsx
function Button (line 54) | function Button() {
function App (line 414) | function App(_handle: Handle) {
FILE: packages/component/src/test/vdom.range-root.test.tsx
function Greeting (line 118) | function Greeting() {
function Counter (line 496) | function Counter(handle: Handle, setup: number) {
FILE: packages/component/src/test/vdom.replacements.test.tsx
function App (line 74) | function App(handle: Handle) {
function App (line 94) | function App(handle: Handle) {
function App (line 137) | function App() {
function App (line 147) | function App() {
function App (line 168) | function App() {
function App2 (line 173) | function App2() {
function App (line 183) | function App() {
function App (line 206) | function App() {
function App (line 244) | function App() {
function Frag (line 310) | function Frag() {
function App (line 341) | function App() {
function App2 (line 351) | function App2() {
FILE: packages/component/src/test/vdom.scheduler.test.tsx
function Parent (line 13) | function Parent(handle: Handle) {
function Child (line 25) | function Child(handle: Handle) {
function App (line 66) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.signals.test.tsx
function App (line 13) | function App(handle: Handle) {
function App (line 33) | function App(handle: Handle) {
function App (line 61) | function App(handle: Handle) {
function App (line 90) | function App(handle: Handle) {
function App (line 113) | function App(handle: Handle) {
FILE: packages/component/src/test/vdom.svg.test.tsx
function SvgGroup (line 238) | function SvgGroup() {
FILE: packages/component/src/test/vdom.tasks.test.tsx
function App (line 12) | function App(handle: Handle) {
function App (line 39) | function App(handle: Handle) {
FILE: packages/compression-middleware/global.d.ts
type ReadableStream (line 3) | interface ReadableStream<R = any> {
FILE: packages/compression-middleware/src/lib/compression.ts
type Encoding (line 6) | type Encoding = 'br' | 'gzip' | 'deflate'
type CompressionOptions (line 11) | interface CompressionOptions {
function compression (line 63) | function compression(options?: CompressionOptions): Middleware {
FILE: packages/cookie/src/lib/cookie-signing.ts
function sign (line 3) | async function sign(value: string, secret: string): Promise<string> {
function unsign (line 12) | async function unsign(cookie: string, secret: string): Promise<string | ...
function createKey (line 37) | function createKey(secret: string, usages: CryptoKey['usages']): Promise...
function byteStringToArray (line 47) | function byteStringToArray(byteString: string): Uint8Array<ArrayBuffer> {
FILE: packages/cookie/src/lib/cookie.test.ts
function getCookieFromSetCookie (line 8) | function getCookieFromSetCookie(setCookie: string): string {
FILE: packages/cookie/src/lib/cookie.ts
type CookieOptions (line 12) | interface CookieOptions extends CookieProperties {
type SameSiteValue (line 42) | type SameSiteValue = 'Strict' | 'Lax' | 'None'
type Coder (line 43) | type Coder = (value: string) => string
class Cookie (line 53) | class Cookie implements CookieProperties {
method constructor (line 71) | constructor(name: string, options?: CookieOptions) {
method domain (line 111) | get domain(): string | undefined {
method expires (line 120) | get expires(): Date | undefined {
method httpOnly (line 131) | get httpOnly(): boolean {
method maxAge (line 140) | get maxAge(): number | undefined {
method name (line 149) | get name(): string {
method parse (line 159) | async parse(headerValue: string | null): Promise<string | null> {
method partitioned (line 179) | get partitioned(): boolean {
method path (line 190) | get path(): string {
method sameSite (line 201) | get sameSite(): SameSiteValue {
method secure (line 212) | get secure(): boolean {
method serialize (line 223) | async serialize(value: string, props?: CookieProperties): Promise<stri...
method signed (line 244) | get signed(): boolean {
function createCookie (line 256) | function createCookie(name: string, options?: CookieOptions): Cookie {
function decodeCookieValue (line 260) | async function decodeCookieValue(
function decodeValue (line 279) | function decodeValue(value: string, decode: Coder): string | null {
function myEscape (line 288) | function myEscape(value: string): string {
function hex (line 309) | function hex(code: number, length: number): string {
function encodeCookieValue (line 315) | async function encodeCookieValue(value: string, secrets: string[], encod...
function encodeValue (line 321) | function encodeValue(value: string, encode: Coder): string {
function myUnescape (line 326) | function myUnescape(value: string): string {
FILE: packages/cop-middleware/src/lib/cop.test.ts
function createRequest (line 8) | function createRequest(pathname: string, init?: RequestInit): Request {
function createTestRouter (line 12) | function createTestRouter(middleware: ReturnType<typeof cop>[]): ReturnT...
method onDeny (line 270) | onDeny() {
FILE: packages/cop-middleware/src/lib/cop.ts
type BypassSegment (line 6) | type BypassSegment = { type: 'static'; value: string } | { type: 'wildca...
type BypassPattern (line 8) | interface BypassPattern {
type CopFailureReason (line 18) | type CopFailureReason = 'cross-origin-request' | 'cross-origin-request-f...
type CopDenyHandler (line 23) | interface CopDenyHandler {
type CopOptions (line 33) | interface CopOptions {
class CrossOriginProtection (line 50) | class CrossOriginProtection {
method constructor (line 55) | constructor(options: CopOptions = {}) {
method addTrustedOrigin (line 67) | addTrustedOrigin(origin: string): void {
method addInsecureBypassPattern (line 71) | addInsecureBypassPattern(pattern: string): void {
method setDenyHandler (line 75) | setDenyHandler(onDeny?: CopDenyHandler): void {
method check (line 79) | check(context: RequestContext): CopFailureReason | null {
method deny (line 108) | deny(reason: CopFailureReason, context: RequestContext): Response | Pr...
method #isRequestExempt (line 116) | #isRequestExempt(context: RequestContext): boolean {
function cop (line 139) | function cop(options: CopOptions = {}): Middleware {
function getDefaultErrorMessage (line 152) | function getDefaultErrorMessage(reason: CopFailureReason): string {
function getHeaderValue (line 160) | function getHeaderValue(headers: Headers, name: string): string | null {
function validateTrustedOrigin (line 170) | function validateTrustedOrigin(origin: string): string {
function normalizeOrigin (line 194) | function normalizeOrigin(origin: string): string | null {
function parseOrigin (line 199) | function parseOrigin(origin: string): URL | null {
function serializeOrigin (line 216) | function serializeOrigin(origin: URL): string {
function parseBypassPattern (line 220) | function parseBypassPattern(pattern: string): BypassPattern {
function parseBypassSegment (line 261) | function parseBypassSegment(
function matchesBypassPattern (line 308) | function matchesBypassPattern(pattern: BypassPattern, context: RequestCo...
FILE: packages/cors-middleware/src/lib/cors.ts
type OriginMatcher (line 6) | type OriginMatcher = string | RegExp | ReadonlyArray<string | RegExp>
type CorsOriginResolverResult (line 11) | type CorsOriginResolverResult = '*' | string | boolean | null | undefined
type CorsOriginResolver (line 16) | interface CorsOriginResolver {
type CorsOrigin (line 29) | type CorsOrigin = OriginMatcher | boolean | CorsOriginResolver
type CorsAllowedHeadersResolverResult (line 34) | type CorsAllowedHeadersResolverResult = readonly string[] | null | undef...
type CorsAllowedHeadersResolver (line 39) | interface CorsAllowedHeadersResolver {
type CorsOptions (line 52) | interface CorsOptions {
type ResolvedAllowedHeaders (line 117) | type ResolvedAllowedHeaders = {
function cors (line 128) | function cors(options: CorsOptions = {}): Middleware {
function isPreflightRequest (line 220) | function isPreflightRequest(context: RequestContext): boolean {
function normalizeMethodList (line 224) | function normalizeMethodList(methods: readonly string[]): string {
function normalizeHeaderList (line 243) | function normalizeHeaderList(headerNames: readonly string[]): string {
function resolveAllowedOrigin (line 263) | async function resolveAllowedOrigin(
function matchesOriginPattern (line 312) | function matchesOriginPattern(pattern: RegExp, requestOrigin: string): b...
function normalizeResolvedOrigin (line 317) | function normalizeResolvedOrigin(
function resolveAllowedHeaders (line 336) | async function resolveAllowedHeaders(
function withCorsHeaders (line 388) | function withCorsHeaders(response: Response, corsHeaders: Headers, vary:...
FILE: packages/csrf-middleware/src/lib/csrf.test.ts
function createRequest (line 12) | function createRequest(fromResponse?: Response, init?: RequestInit): Req...
method value (line 220) | value(context) {
FILE: packages/csrf-middleware/src/lib/csrf.ts
type OriginMatcher (line 7) | type OriginMatcher = string | RegExp | ReadonlyArray<string | RegExp>
type CsrfOriginResolverResult (line 12) | type CsrfOriginResolverResult = boolean | null | undefined
type CsrfOriginResolver (line 17) | interface CsrfOriginResolver {
type CsrfOrigin (line 30) | type CsrfOrigin = OriginMatcher | CsrfOriginResolver
type CsrfTokenResolverResult (line 35) | type CsrfTokenResolverResult = string | null | undefined
type CsrfTokenResolver (line 40) | interface CsrfTokenResolver {
type CsrfFailureReason (line 50) | type CsrfFailureReason = 'invalid-origin' | 'missing-token' | 'invalid-t...
type CsrfOptions (line 55) | interface CsrfOptions {
function csrf (line 117) | function csrf(options: CsrfOptions = {}): Middleware {
function getCsrfToken (line 166) | function getCsrfToken(context: RequestContext, tokenKey = '_csrf'): stri...
function createCsrfToken (line 183) | function createCsrfToken(): string {
function getErrorResponse (line 195) | function getErrorResponse(
function resolveSubmittedToken (line 215) | async function resolveSubmittedToken(
function validateRequestOrigin (line 260) | async function validateRequestOrigin(
function getRequestOrigin (line 301) | function getRequestOrigin(context: RequestContext): string | null {
function constantTimeEqual (line 319) | function constantTimeEqual(left: string, right: string): boolean {
FILE: packages/data-schema/src/lib/checks.test.ts
function assertSuccess (line 8) | function assertSuccess<output>(
function assertFailure (line 14) | function assertFailure<output>(
FILE: packages/data-schema/src/lib/checks.ts
function minLength (line 9) | function minLength(length: number): Check<string> {
function maxLength (line 26) | function maxLength(length: number): Check<string> {
function email (line 42) | function email(): Check<string> {
function url (line 57) | function url(): Check<string> {
function min (line 78) | function min(limit: number): Check<number> {
function max (line 95) | function max(limit: number): Check<number> {
FILE: packages/data-schema/src/lib/coerce.test.ts
function assertSuccess (line 7) | function assertSuccess<output>(
function assertFailure (line 13) | function assertFailure<output>(
FILE: packages/data-schema/src/lib/coerce.ts
function coerceNumber (line 13) | function coerceNumber(): Schema<unknown, number> {
function coerceBoolean (line 54) | function coerceBoolean(): Schema<unknown, boolean> {
function coerceDate (line 89) | function coerceDate(): Schema<unknown, Date> {
function coerceBigint (line 121) | function coerceBigint(): Schema<unknown, bigint> {
function coerceString (line 170) | function coerceString(): Schema<unknown, string> {
FILE: packages/data-schema/src/lib/form-data.test.ts
type Equal (line 9) | type Equal<left, right> =
function expectType (line 14) | function expectType<condition extends true>(_value?: condition): void {}
FILE: packages/data-schema/src/lib/form-data.ts
type FormDataEntryKind (line 4) | type FormDataEntryKind = 'field' | 'fields' | 'file' | 'files'
type FormDataSource (line 9) | type FormDataSource = FormData | URLSearchParams
type FormDataParseResult (line 11) | type FormDataParseResult<output> = { value: output } | { issues: Readonl...
type FormDataValidationContext (line 13) | type FormDataValidationContext = {
type FormDataEntrySchema (line 22) | interface FormDataEntrySchema<output> {
type FormDataFieldOptions (line 34) | interface FormDataFieldOptions {
type FormDataFieldsOptions (line 42) | interface FormDataFieldsOptions {
type FormDataFileOptions (line 50) | interface FormDataFileOptions {
type FormDataFilesOptions (line 58) | interface FormDataFilesOptions {
type FormDataSchema (line 66) | type FormDataSchema = Record<string, FormDataEntrySchema<any>>
type FormDataObjectSchema (line 71) | type FormDataObjectSchema<schema extends FormDataSchema> = Schema<
type ParsedFormData (line 79) | type ParsedFormData<schema extends FormDataSchema> = {
function object (line 93) | function object<schema extends FormDataSchema>(
function field (line 142) | function field<schema extends Schema<any, any>>(
function fields (line 160) | function fields<schema extends Schema<any, any>>(
function file (line 178) | function file<schema extends Schema<any, any>>(
function files (line 196) | function files<schema extends Schema<any, any>>(
function parseField (line 207) | function parseField(
function validateParsedValue (line 280) | function validateParsedValue(
function shouldAbortEarly (line 297) | function shouldAbortEarly(options?: ParseOptions): boolean {
function withPath (line 304) | function withPath(path: NonNullable<Issue['path']>, key: PropertyKey): N...
function isFormDataSource (line 308) | function isFormDataSource(value: unknown): value is FormDataSource {
FILE: packages/data-schema/src/lib/lazy.test.ts
type NodeOutput (line 8) | type NodeOutput = {
function assertSuccess (line 13) | function assertSuccess<output>(
function assertFailure (line 19) | function assertFailure<output>(
FILE: packages/data-schema/src/lib/lazy.ts
function lazy (line 12) | function lazy<schema extends Schema<any, any>>(
FILE: packages/data-schema/src/lib/parse.test.ts
method errorMap (line 65) | errorMap(context) {
method errorMap (line 86) | errorMap(context) {
method errorMap (line 113) | errorMap() {
FILE: packages/data-schema/src/lib/pipe.test.ts
function assertSuccess (line 8) | function assertSuccess<output>(
function assertFailure (line 14) | function assertFailure<output>(
FILE: packages/data-schema/src/lib/schema.test.ts
type Equal (line 30) | type Equal<left, right> =
function expectType (line 35) | function expectType<condition extends true>(_value?: condition): void {}
function assertSuccess (line 37) | function assertSuccess<output>(
function assertFailure (line 43) | function assertFailure<output>(
class MyClass (line 726) | class MyClass {
class Base (line 737) | class Base {}
class Child (line 738) | class Child extends Base {}
FILE: packages/data-schema/src/lib/schema.ts
type Issue (line 9) | type Issue = StandardSchemaV1.Issue
type ValidationResult (line 16) | type ValidationResult<output> = StandardSchemaV1.Result<output>
type ValidationOptions (line 21) | type ValidationOptions = StandardSchemaV1.Options
type ErrorMapContext (line 26) | type ErrorMapContext = {
type ErrorMap (line 40) | type ErrorMap = (context: ErrorMapContext) => string | undefined
type ParseOptions (line 48) | type ParseOptions = StandardSchemaV1.Options & {
type SyncStandardSchemaProps (line 54) | type SyncStandardSchemaProps<input, output> = Omit<
type SyncStandardSchema (line 64) | type SyncStandardSchema<input, output = input> = {
type Check (line 71) | type Check<output> = {
type Schema (line 81) | type Schema<input, output = input> = SyncStandardSchema<input, output> & {
type InferInput (line 112) | type InferInput<schema> = schema extends StandardSchemaV1<infer input, a...
type InferOutput (line 117) | type InferOutput<schema> =
type ValidationContext (line 120) | type ValidationContext = {
type IssueDescriptor (line 125) | type IssueDescriptor = {
function createSchema (line 139) | function createSchema<input, output>(
function shouldAbortEarly (line 222) | function shouldAbortEarly(options?: ParseOptions): boolean {
function withPath (line 229) | function withPath(path: NonNullable<Issue['path']>, key: PropertyKey): N...
function getErrorMap (line 233) | function getErrorMap(options?: ParseOptions): ErrorMap | undefined {
function getLocale (line 245) | function getLocale(options?: ParseOptions): string | undefined {
function resolveIssueMessage (line 257) | function resolveIssueMessage(options: ParseOptions | undefined, context:...
function createIssueFromContext (line 268) | function createIssueFromContext(context: ValidationContext, descriptor: ...
function createIssue (line 289) | function createIssue(message: string, path: Issue['path']): Issue {
function fail (line 305) | function fail(
function any (line 336) | function any(): Schema<any, unknown> {
function array (line 348) | function array<input, output>(
function bigint (line 397) | function bigint(): Schema<unknown, bigint> {
function boolean (line 416) | function boolean(): Schema<unknown, boolean> {
function defaulted (line 437) | function defaulted<input, output>(
function enum_ (line 459) | function enum_<const values extends readonly [unknown, ...unknown[]]>(
function instanceof_ (line 484) | function instanceof_<constructor extends abstract new (...args: any[]) =...
function literal (line 507) | function literal<value>(literalValue: value): Schema<unknown, value> {
function map (line 529) | function map<keyInput, keyOutput, valueInput, valueOutput>(
function null_ (line 591) | function null_(): Schema<unknown, null> {
function nullable (line 611) | function nullable<input, output>(
function number (line 628) | function number(): Schema<unknown, number> {
type ObjectShape (line 642) | type ObjectShape = Record<string, Schema<any, any>>
type ObjectOptions (line 644) | type ObjectOptions = {
function object (line 657) | function object<shape extends ObjectShape>(
function optional (line 739) | function optional<input, output>(
function record (line 758) | function record<keyInput, keyOutput extends PropertyKey, valueInput, val...
function set (line 826) | function set<valueInput, valueOutput>(
function string (line 875) | function string(): Schema<unknown, string> {
function symbol (line 894) | function symbol(): Schema<unknown, symbol> {
function tuple (line 914) | function tuple<items extends Schema<any, any>[]>(
function undefined_ (line 980) | function undefined_(): Schema<unknown, undefined> {
function variant (line 1004) | function variant<
function union (line 1060) | function union<schemas extends Schema<any, any>[]>(
class ValidationError (line 1098) | class ValidationError extends Error {
method constructor (line 1108) | constructor(issues: ReadonlyArray<Issue>, message = 'Validation failed...
function parse (line 1124) | function parse<input, output>(
function parseSafe (line 1146) | function parseSafe<input, output>(
FILE: packages/data-schema/src/lib/variant.test.ts
function assertSuccess (line 7) | function assertSuccess<output>(
function assertFailure (line 13) | function assertFailure<output>(
FILE: packages/data-table-mysql/src/lib/adapter.test.ts
method query (line 47) | async query() {
method beginTransaction (line 50) | async beginTransaction() {}
method commit (line 51) | async commit() {}
method rollback (line 52) | async rollback() {}
method query (line 78) | async query(text: string, values?: unknown[]) {
method beginTransaction (line 82) | async beginTransaction() {}
method commit (line 83) | async commit() {}
method rollback (line 84) | async rollback() {}
method query (line 110) | async query(text: string) {
method beginTransaction (line 114) | async beginTransaction() {
method commit (line 117) | async commit() {
method rollback (line 120) | async rollback() {
method release (line 123) | release() {}
method query (line 127) | async query() {
method getConnection (line 131) | async getConnection() {
method query (line 156) | async query() {
method beginTransaction (line 160) | async beginTransaction() {}
method commit (line 161) | async commit() {}
method rollback (line 162) | async rollback() {}
method query (line 189) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 194) | async beginTransaction() {}
method commit (line 195) | async commit() {}
method rollback (line 196) | async rollback() {}
method query (line 212) | async query() {
method beginTransaction (line 215) | async beginTransaction() {
method commit (line 218) | async commit() {
method rollback (line 221) | async rollback() {
method release (line 224) | release() {
method query (line 230) | async query() {
method getConnection (line 233) | async getConnection() {
method query (line 252) | async query() {
method beginTransaction (line 255) | async beginTransaction() {
method commit (line 258) | async commit() {
method rollback (line 261) | async rollback() {
method query (line 267) | async query() {
method getConnection (line 270) | async getConnection() {
method query (line 289) | async query(text: string) {
method beginTransaction (line 293) | async beginTransaction() {
method commit (line 296) | async commit() {
method rollback (line 299) | async rollback() {
method query (line 323) | async query(text: string) {
method beginTransaction (line 327) | async beginTransaction() {
method commit (line 330) | async commit() {
method rollback (line 333) | async rollback() {
method query (line 349) | async query() {
method beginTransaction (line 352) | async beginTransaction() {
method commit (line 355) | async commit() {
method rollback (line 358) | async rollback() {
method release (line 361) | release() {
method query (line 367) | async query() {
method getConnection (line 370) | async getConnection() {
method query (line 393) | async query() {
method beginTransaction (line 396) | async beginTransaction() {
method commit (line 399) | async commit() {
method rollback (line 402) | async rollback() {
method query (line 408) | async query() {
method getConnection (line 411) | async getConnection() {
method query (line 434) | async query(text: string) {
method beginTransaction (line 438) | async beginTransaction() {}
method commit (line 439) | async commit() {}
method rollback (line 440) | async rollback() {}
method query (line 460) | async query() {
method beginTransaction (line 463) | async beginTransaction() {}
method commit (line 464) | async commit() {}
method rollback (line 465) | async rollback() {}
method query (line 488) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 492) | async beginTransaction() {}
method commit (line 493) | async commit() {}
method rollback (line 494) | async rollback() {}
method query (line 514) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 518) | async beginTransaction() {}
method commit (line 519) | async commit() {}
method rollback (line 520) | async rollback() {}
method query (line 536) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 540) | async beginTransaction() {}
method commit (line 541) | async commit() {}
method rollback (line 542) | async rollback() {}
method query (line 556) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 560) | async beginTransaction() {}
method commit (line 561) | async commit() {}
method rollback (line 562) | async rollback() {}
method query (line 581) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 595) | async beginTransaction() {}
method commit (line 596) | async commit() {}
method rollback (line 597) | async rollback() {}
method query (line 621) | async query() {
method beginTransaction (line 624) | async beginTransaction() {}
method commit (line 625) | async commit() {}
method rollback (line 626) | async rollback() {}
method query (line 637) | async query() {
method beginTransaction (line 640) | async beginTransaction() {}
method commit (line 641) | async commit() {}
method rollback (line 642) | async rollback() {}
method query (line 654) | async query() {
method beginTransaction (line 657) | async beginTransaction() {}
method commit (line 658) | async commit() {}
method rollback (line 659) | async rollback() {}
method query (line 675) | async query() {
method beginTransaction (line 678) | async beginTransaction() {}
method commit (line 679) | async commit() {}
method rollback (line 680) | async rollback() {}
method query (line 691) | async query() {
method beginTransaction (line 694) | async beginTransaction() {}
method commit (line 695) | async commit() {}
method rollback (line 696) | async rollback() {}
method query (line 714) | async query(text: string, values: unknown[] = []) {
method beginTransaction (line 718) | async beginTransaction() {}
method commit (line 719) | async commit() {}
method rollback (line 720) | async rollback() {}
method query (line 753) | async query() {
method beginTransaction (line 756) | async beginTransaction() {}
method commit (line 757) | async commit() {}
method rollback (line 758) | async rollback() {}
method query (line 968) | async query() {
method beginTransaction (line 971) | async beginTransaction() {}
method commit (line 972) | async commit() {}
method rollback (line 973) | async rollback() {}
method query (line 984) | async query() {
method beginTransaction (line 987) | async beginTransaction() {}
method commit (line 988) | async commit() {}
method rollback (line 989) | async rollback() {}
FILE: packages/data-table-mysql/src/lib/adapter.ts
type MysqlDatabaseAdapterOptions (line 35) | type MysqlDatabaseAdapterOptions = {
type TransactionState (line 39) | type TransactionState = {
type MysqlQueryRows (line 44) | type MysqlQueryRows = RowDataPacket[]
type MysqlQueryResultHeader (line 45) | type MysqlQueryResultHeader = {
type MysqlTransactionConnection (line 49) | type MysqlTransactionConnection = MysqlConnection | MysqlPoolConnection
type MysqlQueryable (line 50) | type MysqlQueryable = MysqlPool | MysqlTransactionConnection
class MysqlDatabaseAdapter (line 55) | class MysqlDatabaseAdapter implements DatabaseAdapter {
method constructor (line 70) | constructor(client: MysqlQueryable, options?: MysqlDatabaseAdapterOpti...
method compileSql (line 86) | compileSql(operation: DataManipulationOperation | DataMigrationOperati...
method execute (line 100) | async execute(request: DataManipulationRequest): Promise<DataManipulat...
method migrate (line 137) | async migrate(request: DataMigrationRequest): Promise<DataMigrationRes...
method hasTable (line 156) | async hasTable(table: TableRef, transaction?: TransactionToken): Promi...
method hasColumn (line 179) | async hasColumn(
method beginTransaction (line 204) | async beginTransaction(options?: TransactionOptions): Promise<Transact...
method commitTransaction (line 243) | async commitTransaction(token: TransactionToken): Promise<void> {
method rollbackTransaction (line 266) | async rollbackTransaction(token: TransactionToken): Promise<void> {
method createSavepoint (line 290) | async createSavepoint(token: TransactionToken, name: string): Promise<...
method rollbackToSavepoint (line 301) | async rollbackToSavepoint(token: TransactionToken, name: string): Prom...
method releaseSavepoint (line 312) | async releaseSavepoint(token: TransactionToken, name: string): Promise...
method acquireMigrationLock (line 321) | async acquireMigrationLock(): Promise<void> {
method releaseMigrationLock (line 329) | async releaseMigrationLock(): Promise<void> {
method #resolveClient (line 333) | #resolveClient(token: TransactionToken | undefined): MysqlQueryable {
method #transactionConnection (line 341) | #transactionConnection(token: TransactionToken): MysqlTransactionConne...
function createMysqlDatabaseAdapter (line 368) | function createMysqlDatabaseAdapter(
function isMysqlPool (line 375) | function isMysqlPool(client: MysqlQueryable): client is MysqlPool {
function isMysqlPoolConnection (line 379) | function isMysqlPoolConnection(
function isRowsResult (line 385) | function isRowsResult(result: unknown): result is MysqlQueryRows {
function toBooleanExists (line 389) | function toBooleanExists(value: unknown): boolean {
function normalizeRows (line 409) | function normalizeRows(rows: MysqlQueryRows): Record<string, unknown>[] {
function normalizeHeader (line 413) | function normalizeHeader(result: unknown): MysqlQueryResultHeader {
function normalizeCountRows (line 429) | function normalizeCountRows(rows: Record<string, unknown>[]): Record<str...
function normalizeInsertId (line 455) | function normalizeInsertId(
function quoteIdentifier (line 471) | function quoteIdentifier(value: string): string {
function quoteTableRef (line 475) | function quoteTableRef(table: TableRef): string {
function quoteLiteral (line 479) | function quoteLiteral(value: unknown): string {
function isInsertOperationKind (line 483) | function isInsertOperationKind(kind: DataManipulationRequest['operation'...
function isInsertOperation (line 487) | function isInsertOperation(
function isDataManipulationOperation (line 498) | function isDataManipulationOperation(
function compileMysqlMigrationOperations (line 504) | function compileMysqlMigrationOperations(operation: DataMigrationOperati...
function compileMysqlColumn (line 807) | function compileMysqlColumn(definition: ColumnDefinition): string {
function compileMysqlColumnType (line 869) | function compileMysqlColumnType(definition: ColumnDefinition): string {
FILE: packages/data-table-mysql/src/lib/sql-compiler.ts
type JoinClause (line 9) | type JoinClause = Extract<DataManipulationOperation, { kind: 'select' }>...
type UpsertOperation (line 10) | type UpsertOperation = Extract<DataManipulationOperation, { kind: 'upser...
type OperationTable (line 11) | type OperationTable = Extract<DataManipulationOperation, { kind: 'select...
type CompileContext (line 13) | type CompileContext = {
function compileMysqlOperation (line 17) | function compileMysqlOperation(operation: DataManipulationOperation): Sq...
function compileInsertOperation (line 115) | function compileInsertOperation(
function compileInsertManyOperation (line 142) | function compileInsertManyOperation(
function compileUpsertOperation (line 187) | function compileUpsertOperation(operation: UpsertOperation, context: Com...
function compileFromClause (line 219) | function compileFromClause(
function compileWhereClause (line 239) | function compileWhereClause(predicates: Predicate[], context: CompileCon...
function compileGroupByClause (line 250) | function compileGroupByClause(columns: string[]): string {
function compileHavingClause (line 258) | function compileHavingClause(predicates: Predicate[], context: CompileCo...
function compileOrderByClause (line 269) | function compileOrderByClause(orderBy: { column: string; direction: 'asc...
function compileLimitClause (line 282) | function compileLimitClause(limit: number | undefined): string {
function compileOffsetClause (line 290) | function compileOffsetClause(offset: number | undefined): string {
function compilePredicate (line 298) | function compilePredicate(predicate: Predicate, context: CompileContext)...
function compileComparisonValue (line 407) | function compileComparisonValue(
function normalizeJoinType (line 418) | function normalizeJoinType(type: string): string {
function quoteIdentifier (line 422) | function quoteIdentifier(value: string): string {
function quotePath (line 426) | function quotePath(path: string): string {
function pushValue (line 430) | function pushValue(context: CompileContext, value: unknown): string {
function collectColumns (line 435) | function collectColumns(rows: Record<string, unknown>[]): string[] {
FILE: packages/data-table-postgres/src/lib/adapter.test.ts
method query (line 47) | async query() {
method query (line 81) | async query(text: string, values?: unknown[]) {
method query (line 119) | async query(text: string) {
method release (line 129) | release() {}
method query (line 133) | async query() {
method connect (line 143) | async connect() {
method query (line 168) | async query() {
method query (line 203) | async query(text: string, values?: unknown[]) {
method query (line 240) | async query(text: string) {
method query (line 276) | async query(text: string) {
method query (line 307) | async query(text: string) {
method query (line 330) | async query(text: string) {
method release (line 340) | release() {
method query (line 346) | async query() {
method connect (line 349) | async connect() {
method query (line 366) | async query(text: string) {
method query (line 379) | async query() {
method connect (line 382) | async connect() {
method query (line 399) | async query(text: string) {
method release (line 409) | release() {
method query (line 415) | async query() {
method connect (line 418) | async connect() {
method query (line 441) | async query(text: string) {
method query (line 454) | async query() {
method connect (line 457) | async connect() {
method query (line 480) | async query(text: string) {
method query (line 511) | async query() {
method query (line 542) | async query(text: string, values?: unknown[]) {
method query (line 572) | async query(text: string, values?: unknown[]) {
method query (line 598) | async query(text: string, values?: unknown[]) {
method query (line 622) | async query(text: string, values?: unknown[]) {
method query (line 648) | async query() {
method query (line 667) | async query() {
method query (line 686) | async query() {
method query (line 708) | async query() {
method query (line 735) | async query() {
method query (line 765) | async query(text: string, values?: unknown[]) {
method query (line 813) | async query() {
method query (line 920) | async query() {
method query (line 1066) | async query() {
method query (line 1102) | async query() {
FILE: packages/data-table-postgres/src/lib/adapter.ts
type PostgresDatabaseAdapterOptions (line 29) | type PostgresDatabaseAdapterOptions = {
type TransactionState (line 33) | type TransactionState = {
type PostgresQueryable (line 38) | type PostgresQueryable = PostgresPool | PostgresPoolClient
class PostgresDatabaseAdapter (line 43) | class PostgresDatabaseAdapter implements DatabaseAdapter {
method constructor (line 58) | constructor(client: PostgresQueryable, options?: PostgresDatabaseAdapt...
method compileSql (line 74) | compileSql(operation: DataManipulationOperation | DataMigrationOperati...
method execute (line 88) | async execute(request: DataManipulationRequest): Promise<DataManipulat...
method migrate (line 118) | async migrate(request: DataMigrationRequest): Promise<DataMigrationRes...
method hasTable (line 137) | async hasTable(table: TableRef, transaction?: TransactionToken): Promi...
method hasColumn (line 152) | async hasColumn(
method beginTransaction (line 172) | async beginTransaction(options?: TransactionOptions): Promise<Transact...
method commitTransaction (line 205) | async commitTransaction(token: TransactionToken): Promise<void> {
method rollbackTransaction (line 228) | async rollbackTransaction(token: TransactionToken): Promise<void> {
method createSavepoint (line 252) | async createSavepoint(token: TransactionToken, name: string): Promise<...
method rollbackToSavepoint (line 263) | async rollbackToSavepoint(token: TransactionToken, name: string): Prom...
method releaseSavepoint (line 274) | async releaseSavepoint(token: TransactionToken, name: string): Promise...
method acquireMigrationLock (line 283) | async acquireMigrationLock(): Promise<void> {
method releaseMigrationLock (line 291) | async releaseMigrationLock(): Promise<void> {
method #resolveClient (line 295) | #resolveClient(token: TransactionToken | undefined): PostgresQueryable {
method #transactionClient (line 303) | #transactionClient(token: TransactionToken): PostgresPoolClient {
function createPostgresDatabaseAdapter (line 330) | function createPostgresDatabaseAdapter(
function isPostgresPool (line 337) | function isPostgresPool(client: PostgresQueryable): client is PostgresPo...
function releasePostgresClient (line 341) | function releasePostgresClient(client: PostgresPoolClient): void {
function buildSetTransactionStatement (line 346) | function buildSetTransactionStatement(options: TransactionOptions): stri...
function normalizeRows (line 360) | function normalizeRows(rows: unknown[]): Record<string, unknown>[] {
function normalizeCountRows (line 370) | function normalizeCountRows(rows: Record<string, unknown>[]): Record<str...
function normalizeAffectedRows (line 396) | function normalizeAffectedRows(
function normalizeInsertId (line 416) | function normalizeInsertId(
function quoteIdentifier (line 437) | function quoteIdentifier(value: string): string {
function toPostgresRelationName (line 441) | function toPostgresRelationName(table: TableRef): string {
function toBooleanExists (line 449) | function toBooleanExists(value: unknown): boolean {
function isInsertOperationKind (line 465) | function isInsertOperationKind(kind: DataManipulationRequest['operation'...
function isInsertOperation (line 469) | function isInsertOperation(
function isDataManipulationOperation (line 480) | function isDataManipulationOperation(
function compilePostgresMigrationOperations (line 486) | function compilePostgresMigrationOperations(operation: DataMigrationOper...
function compilePostgresColumn (line 805) | function compilePostgresColumn(definition: ColumnDefinition): string {
function compilePostgresColumnType (line 866) | function compilePostgresColumnType(definition: ColumnDefinition): string {
function quoteTableRef (line 926) | function quoteTableRef(table: TableRef): string {
function quoteLiteral (line 930) | function quoteLiteral(value: unknown): string {
FILE: packages/data-table-postgres/src/lib/sql-compiler.ts
type JoinClause (line 9) | type JoinClause = Extract<DataManipulationOperation, { kind: 'select' }>...
type UpsertOperation (line 10) | type UpsertOperation = Extract<DataManipulationOperation, { kind: 'upser...
type OperationTable (line 11) | type OperationTable = Extract<DataManipulationOperation, { kind: 'select...
type CompileContext (line 13) | type CompileContext = {
function compilePostgresOperation (line 17) | function compilePostgresOperation(operation: DataManipulationOperation):...
function compileInsertOperation (line 120) | function compileInsertOperation(
function compileInsertManyOperation (line 156) | function compileInsertManyOperation(
function compileUpsertOperation (line 206) | function compileUpsertOperation(operation: UpsertOperation, context: Com...
function compileRawOperation (line 253) | function compileRawOperation(statement: SqlStatement): SqlStatement {
function compileFromClause (line 274) | function compileFromClause(
function compileWhereClause (line 294) | function compileWhereClause(predicates: Predicate[], context: CompileCon...
function compileGroupByClause (line 306) | function compileGroupByClause(columns: string[]): string {
function compileHavingClause (line 314) | function compileHavingClause(predicates: Predicate[], context: CompileCo...
function compileOrderByClause (line 326) | function compileOrderByClause(orderBy: { column: string; direction: 'asc...
function compileLimitClause (line 339) | function compileLimitClause(limit: number | undefined): string {
function compileOffsetClause (line 347) | function compileOffsetClause(offset: number | undefined): string {
function compileReturningClause (line 355) | function compileReturningClause(returning: '*' | string[] | undefined): ...
function compilePredicate (line 367) | function compilePredicate(predicate: Predicate, context: CompileContext)...
function compileComparisonValue (line 471) | function compileComparisonValue(
function normalizeJoinType (line 482) | function normalizeJoinType(type: string): string {
function quoteIdentifier (line 486) | function quoteIdentifier(value: string): string {
function quotePath (line 490) | function quotePath(path: string): string {
function pushValue (line 494) | function pushValue(context: CompileContext, value: unknown): string {
function collectColumns (line 499) | function collectColumns(rows: Record<string, unknown>[]): string[] {
FILE: packages/data-table-sqlite/src/lib/adapter.integration.test.ts
function canOpenSqliteDatabase (line 52) | function canOpenSqliteDatabase(): boolean {
FILE: packages/data-table-sqlite/src/lib/adapter.test.ts
method prepare (line 43) | prepare() {
method exec (line 55) | exec() {}
method pragma (line 56) | pragma() {}
method prepare (line 82) | prepare(statement: string) {
method exec (line 111) | exec() {}
method pragma (line 112) | pragma() {}
method prepare (line 133) | prepare() {
method pragma (line 136) | pragma(statement: string) {
method exec (line 139) | exec(statement: string) {
method prepare (line 156) | prepare() {
method pragma (line 159) | pragma() {}
method exec (line 160) | exec(statement: string) {
method prepare (line 184) | prepare() {
method pragma (line 187) | pragma() {}
method exec (line 188) | exec() {}
method prepare (line 217) | prepare() {
method exec (line 228) | exec() {}
method pragma (line 229) | pragma() {}
method prepare (line 252) | prepare() {
method exec (line 263) | exec() {}
method pragma (line 264) | pragma() {}
method prepare (line 291) | prepare() {
method exec (line 302) | exec() {}
method pragma (line 303) | pragma() {}
method prepare (line 326) | prepare() {
method exec (line 337) | exec() {}
method pragma (line 338) | pragma() {}
method prepare (line 362) | prepare() {
method exec (line 373) | exec() {}
method pragma (line 374) | pragma() {}
method prepare (line 388) | prepare(text: string) {
method exec (line 400) | exec() {}
method pragma (line 401) | pragma() {}
method prepare (line 422) | prepare() {
method exec (line 433) | exec() {}
method pragma (line 434) | pragma() {}
method prepare (line 620) | prepare() {
method exec (line 631) | exec() {}
method pragma (line 632) | pragma() {}
method prepare (line 760) | prepare() {
method exec (line 771) | exec() {}
method pragma (line 772) | pragma() {}
function canOpenSqliteDatabase (line 867) | function canOpenSqliteDatabase(): boolean {
FILE: packages/data-table-sqlite/src/lib/adapter.ts
type SqliteDatabaseAdapterOptions (line 29) | type SqliteDatabaseAdapterOptions = {
class SqliteDatabaseAdapter (line 36) | class SqliteDatabaseAdapter implements DatabaseAdapter {
method constructor (line 51) | constructor(database: BetterSqliteDatabase, options?: SqliteDatabaseAd...
method compileSql (line 67) | compileSql(operation: DataManipulationOperation | DataMigrationOperati...
method execute (line 81) | async execute(request: DataManipulationRequest): Promise<DataManipulat...
method migrate (line 120) | async migrate(request: DataMigrationRequest): Promise<DataMigrationRes...
method hasTable (line 139) | async hasTable(table: TableRef, transaction?: TransactionToken): Promi...
method hasColumn (line 161) | async hasColumn(
method beginTransaction (line 184) | async beginTransaction(options?: TransactionOptions): Promise<Transact...
method commitTransaction (line 203) | async commitTransaction(token: TransactionToken): Promise<void> {
method rollbackTransaction (line 214) | async rollbackTransaction(token: TransactionToken): Promise<void> {
method createSavepoint (line 226) | async createSavepoint(token: TransactionToken, name: string): Promise<...
method rollbackToSavepoint (line 237) | async rollbackToSavepoint(token: TransactionToken, name: string): Prom...
method releaseSavepoint (line 248) | async releaseSavepoint(token: TransactionToken, name: string): Promise...
method #assertTransaction (line 253) | #assertTransaction(token: TransactionToken): void {
function createSqliteDatabaseAdapter (line 276) | function createSqliteDatabaseAdapter(
function normalizeRows (line 283) | function normalizeRows(rows: unknown[]): Record<string, unknown>[] {
function normalizeCountRows (line 293) | function normalizeCountRows(rows: Record<string, unknown>[]): Record<str...
function normalizeAffectedRowsForReader (line 319) | function normalizeAffectedRowsForReader(
function normalizeInsertIdForReader (line 330) | function normalizeInsertIdForReader(
function normalizeAffectedRowsForRun (line 351) | function normalizeAffectedRowsForRun(
function normalizeInsertIdForRun (line 362) | function normalizeInsertIdForRun(
function quoteIdentifier (line 378) | function quoteIdentifier(value: string): string {
function quoteTableRef (line 382) | function quoteTableRef(table: TableRef): string {
function quoteLiteral (line 386) | function quoteLiteral(value: unknown): string {
function isWriteOperationKind (line 390) | function isWriteOperationKind(kind: DataManipulationRequest['operation']...
function isInsertOperationKind (line 400) | function isInsertOperationKind(kind: DataManipulationRequest['operation'...
function isInsertOperation (line 404) | function isInsertOperation(
function isDataManipulationOperation (line 415) | function isDataManipulationOperation(
function compileSqliteMigrationOperations (line 421) | function compileSqliteMigrationOperations(operation: DataMigrationOperat...
function compileSqliteColumn (line 719) | function compileSqliteColumn(definition: ColumnDefinition): string {
function compileSqliteColumnType (line 777) | function compileSqliteColumnType(definition: ColumnDefinition): string {
FILE: packages/data-table-sqlite/src/lib/sql-compiler.ts
type JoinClause (line 9) | type JoinClause = Extract<DataManipulationOperation, { kind: 'select' }>...
type UpsertOperation (line 10) | type UpsertOperation = Extract<DataManipulationOperation, { kind: 'upser...
type OperationTable (line 11) | type OperationTable = Extract<DataManipulationOperation, { kind: 'select...
type CompileContext (line 13) | type CompileContext = {
function compileSqliteOperation (line 17) | function compileSqliteOperation(operation: DataManipulationOperation): S...
function compileInsertOperation (line 122) | function compileInsertOperation(
function compileInsertManyOperation (line 155) | function compileInsertManyOperation(
function compileUpsertOperation (line 206) | function compileUpsertOperation(operation: UpsertOperation, context: Com...
function compileFromClause (line 249) | function compileFromClause(
function compileWhereClause (line 269) | function compileWhereClause(predicates: Predicate[], context: CompileCon...
function compileGroupByClause (line 280) | function compileGroupByClause(columns: string[]): string {
function compileHavingClause (line 288) | function compileHavingClause(predicates: Predicate[], context: CompileCo...
function compileOrderByClause (line 299) | function compileOrderByClause(orderBy: { column: string; direction: 'asc...
function compileLimitClause (line 312) | function compileLimitClause(limit: number | undefined): string {
function compileOffsetClause (line 320) | function compileOffsetClause(offset: number | undefined): string {
function compileReturningClause (line 328) | function compileReturningClause(returning: '*' | string[] | undefined): ...
function compilePredicate (line 340) | function compilePredicate(predicate: Predicate, context: CompileContext)...
function compileComparisonValue (line 449) | function compileComparisonValue(
function normalizeJoinType (line 460) | function normalizeJoinType(type: string): string {
function quoteIdentifier (line 464) | function quoteIdentifier(value: string): string {
function quotePath (line 468) | function quotePath(path: string): string {
function pushValue (line 472) | function pushValue(context: CompileContext, value: unknown): string {
function normalizeBoundValue (line 477) | function normalizeBoundValue(value: unknown): unknown {
function collectColumns (line 485) | function collectColumns(rows: Record<string, unknown>[]): string[] {
FILE: packages/data-table/src/lib/adapter.ts
type JoinType (line 9) | type JoinType = 'inner' | 'left' | 'right'
type JoinClause (line 14) | type JoinClause = {
type SelectColumn (line 23) | type SelectColumn = {
type ReturningSelection (line 31) | type ReturningSelection = '*' | string[]
type SelectOperation (line 36) | type SelectOperation<table extends AnyTable = AnyTable> = {
type CountOperation (line 53) | type CountOperation<table extends AnyTable = AnyTable> = {
type ExistsOperation (line 65) | type ExistsOperation<table extends AnyTable = AnyTable> = {
type InsertOperation (line 77) | type InsertOperation<table extends AnyTable = AnyTable> = {
type InsertManyOperation (line 87) | type InsertManyOperation<table extends AnyTable = AnyTable> = {
type UpdateOperation (line 97) | type UpdateOperation<table extends AnyTable = AnyTable> = {
type DeleteOperation (line 108) | type DeleteOperation<table extends AnyTable = AnyTable> = {
type UpsertOperation (line 118) | type UpsertOperation<table extends AnyTable = AnyTable> = {
type RawOperation (line 130) | type RawOperation = {
type DataManipulationOperation (line 138) | type DataManipulationOperation =
type TableRef (line 152) | type TableRef = {
type ForeignKeyAction (line 160) | type ForeignKeyAction = 'cascade' | 'restrict' | 'set null' | 'set defau...
type ColumnTypeName (line 165) | type ColumnTypeName =
type ColumnDefault (line 183) | type ColumnDefault =
type ColumnComputed (line 191) | type ColumnComputed = {
type IdentityOptions (line 197) | type IdentityOptions = {
type ColumnReference (line 204) | type ColumnReference = {
type ColumnCheck (line 215) | type ColumnCheck = {
type ColumnDefinition (line 223) | type ColumnDefinition = {
type PrimaryKeyConstraint (line 248) | type PrimaryKeyConstraint = {
type UniqueConstraint (line 256) | type UniqueConstraint = {
type CheckConstraint (line 264) | type CheckConstraint = {
type ForeignKeyConstraint (line 272) | type ForeignKeyConstraint = {
type IndexMethod (line 286) | type IndexMethod = 'btree' | 'hash' | 'gin' | 'gist' | 'fulltext' | (str...
type IndexDefinition (line 291) | type IndexDefinition = {
type CreateTableOperation (line 303) | type CreateTableOperation = {
type AddColumnChange (line 318) | type AddColumnChange = {
type ChangeColumnChange (line 327) | type ChangeColumnChange = {
type RenameColumnChange (line 336) | type RenameColumnChange = {
type DropColumnChange (line 345) | type DropColumnChange = {
type AddPrimaryKeyChange (line 354) | type AddPrimaryKeyChange = {
type DropPrimaryKeyChange (line 362) | type DropPrimaryKeyChange = {
type AddUniqueChange (line 370) | type AddUniqueChange = {
type DropUniqueChange (line 378) | type DropUniqueChange = {
type AddForeignKeyChange (line 386) | type AddForeignKeyChange = {
type DropForeignKeyChange (line 394) | type DropForeignKeyChange = {
type AddCheckChange (line 402) | type AddCheckChange = {
type DropCheckChange (line 410) | type DropCheckChange = {
type SetTableCommentChange (line 418) | type SetTableCommentChange = {
type AlterTableChange (line 426) | type AlterTableChange =
type AlterTableOperation (line 444) | type AlterTableOperation = {
type RenameTableOperation (line 454) | type RenameTableOperation = {
type DropTableOperation (line 463) | type DropTableOperation = {
type CreateIndexOperation (line 473) | type CreateIndexOperation = {
type DropIndexOperation (line 482) | type DropIndexOperation = {
type RenameIndexOperation (line 492) | type RenameIndexOperation = {
type AddForeignKeyOperation (line 502) | type AddForeignKeyOperation = {
type DropForeignKeyOperation (line 511) | type DropForeignKeyOperation = {
type AddCheckOperation (line 520) | type AddCheckOperation = {
type DropCheckOperation (line 529) | type DropCheckOperation = {
type DataMigrationOperation (line 538) | type DataMigrationOperation =
type TransactionToken (line 555) | type TransactionToken = {
type TransactionOptions (line 563) | type TransactionOptions = {
type DataManipulationRequest (line 571) | type DataManipulationRequest = {
type DataMigrationRequest (line 579) | type DataMigrationRequest = {
type DataManipulationResult (line 587) | type DataManipulationResult = {
type DataMigrationResult (line 596) | type DataMigrationResult = {
type AdapterCapabilities (line 606) | type AdapterCapabilities = {
type AdapterCapabilityOverrides (line 617) | type AdapterCapabilityOverrides = Pretty<Partial<AdapterCapabilities>>
type DatabaseAdapter (line 622) | interface DatabaseAdapter {
FILE: packages/data-table/src/lib/column.ts
class ColumnBuilder (line 7) | class ColumnBuilder<output = unknown> {
method constructor (line 10) | constructor(definition: ColumnDefinition) {
method nullable (line 18) | nullable(): ColumnBuilder<output | null> {
method notNull (line 27) | notNull(): ColumnBuilder<Exclude<output, null>> {
method default (line 37) | default(value: unknown): ColumnBuilder<output> {
method defaultNow (line 49) | defaultNow(): ColumnBuilder<output> {
method defaultSql (line 61) | defaultSql(expression: string): ColumnBuilder<output> {
method primaryKey (line 73) | primaryKey(): ColumnBuilder<output> {
method unique (line 83) | unique(name?: string): ColumnBuilder<output> {
method references (line 103) | references(
method onDelete (line 126) | onDelete(action: ForeignKeyAction): ColumnBuilder<output> {
method onUpdate (line 140) | onUpdate(action: ForeignKeyAction): ColumnBuilder<output> {
method check (line 155) | check(expression: string, name: string): ColumnBuilder<output> {
method comment (line 167) | comment(text: string): ColumnBuilder<output> {
method computed (line 179) | computed(expression: string, options?: { stored?: boolean }): ColumnBu...
method unsigned (line 191) | unsigned(): ColumnBuilder<output> {
method autoIncrement (line 200) | autoIncrement(): ColumnBuilder<output> {
method identity (line 210) | identity(options?: IdentityOptions): ColumnBuilder<output> {
method collate (line 220) | collate(name: string): ColumnBuilder<output> {
method charset (line 230) | charset(name: string): ColumnBuilder<output> {
method length (line 240) | length(value: number): ColumnBuilder<output> {
method precision (line 251) | precision(value: number, scale?: number): ColumnBuilder<output> {
method scale (line 266) | scale(value: number): ColumnBuilder<output> {
method timezone (line 276) | timezone(enabled = true): ColumnBuilder<output> {
method build (line 285) | build(): ColumnDefinition {
type ColumnOutput (line 294) | type ColumnOutput<column extends ColumnBuilder<any>> =
type ColumnInput (line 298) | type ColumnInput<column extends ColumnBuilder<any>> = ColumnOutput<column>
type ColumnNamespace (line 303) | type ColumnNamespace = {
function createColumnBuilder (line 319) | function createColumnBuilder<output = unknown>(
method varchar (line 335) | varchar(length: number) {
method text (line 338) | text() {
method integer (line 341) | integer() {
method bigint (line 344) | bigint() {
method decimal (line 347) | decimal(precision: number, scale: number) {
method boolean (line 350) | boolean() {
method uuid (line 353) | uuid() {
method date (line 356) | date() {
method time (line 359) | time(options?: { precision?: number; withTimezone?: boolean }) {
method timestamp (line 366) | timestamp(options?: { precision?: number; withTimezone?: boolean }) {
method json (line 373) | json() {
method binary (line 376) | binary(length?: number) {
method enum (line 379) | enum<values extends readonly string[]>(values: values) {
FILE: packages/data-table/src/lib/database.test.ts
method validate (line 24) | validate({ value }) {
method now (line 115) | now() {
method now (line 120) | now() {
method execute (line 541) | async execute(request) {
method compileSql (line 559) | compileSql() {
method migrate (line 562) | async migrate() {
method beginTransaction (line 565) | async beginTransaction() {
method commitTransaction (line 568) | async commitTransaction() {}
method rollbackTransaction (line 569) | async rollbackTransaction() {}
method createSavepoint (line 570) | async createSavepoint() {}
method rollbackToSavepoint (line 571) | async rollbackToSavepoint() {}
method releaseSavepoint (line 572) | async releaseSavepoint() {}
method hasTable (line 573) | async hasTable() {
method hasColumn (line 576) | async hasColumn() {
method execute (line 600) | async execute(request) {
method compileSql (line 623) | compileSql() {
method migrate (line 626) | async migrate() {
method beginTransaction (line 629) | async beginTransaction() {
method commitTransaction (line 632) | async commitTransaction() {}
method rollbackTransaction (line 633) | async rollbackTransaction() {}
method createSavepoint (line 634) | async createSavepoint() {}
method rollbackToSavepoint (line 635) | async rollbackToSavepoint() {}
method releaseSavepoint (line 636) | async releaseSavepoint() {}
method hasTable (line 637) | async hasTable() {
method hasColumn (line 640) | async hasColumn() {
method execute (line 869) | async execute(request) {
method compileSql (line 880) | compileSql() {
method migrate (line 883) | async migrate() {
method beginTransaction (line 886) | async beginTransaction() {
method commitTransaction (line 889) | async commitTransaction() {}
method rollbackTransaction (line 890) | async rollbackTransaction() {}
method createSavepoint (line 891) | async createSavepoint() {}
method rollbackToSavepoint (line 892) | async rollbackToSavepoint() {}
method releaseSavepoint (line 893) | async releaseSavepoint() {}
method hasTable (line 894) | async hasTable() {
method hasColumn (line 897) | async hasColumn() {
method now (line 1070) | now() {
method validate (line 1123) | validate({ operation, value }) {
method validate (line 1161) | validate({ operation, value }) {
method compileSql (line 1176) | compileSql() {
method execute (line 1179) | async execute() {
method migrate (line 1182) | async migrate() {
method beginTransaction (line 1185) | async beginTransaction() {
method commitTransaction (line 1188) | async commitTransaction() {}
method rollbackTransaction (line 1189) | async rollbackTransaction() {}
method createSavepoint (line 1190) | async createSavepoint() {}
method rollbackToSavepoint (line 1191) | async rollbackToSavepoint() {}
method releaseSavepoint (line 1192) | async releaseSavepoint() {}
method hasTable (line 1193) | async hasTable() {
method hasColumn (line 1196) | async hasColumn() {
method validate (line 1222) | validate({ value }) {
method beforeWrite (line 1276) | beforeWrite({ value }) {
method validate (line 1285) | validate({ value }) {
method afterWrite (line 1290) | afterWrite({ values }) {
method beforeDelete (line 1337) | beforeDelete(context) {
method afterDelete (line 1346) | afterDelete(context) {
method beforeDelete (line 1392) | beforeDelete() {
method afterRead (line 1428) | afterRead() {
method afterRead (line 1465) | afterRead({ value }) {
method afterRead (line 1495) | afterRead({ value }) {
method afterRead (line 1512) | afterRead({ value }) {
method beforeWrite (line 1555) | beforeWrite({ value }) {
method afterRead (line 1566) | afterRead({ value }) {
method beforeDelete (line 1609) | beforeDelete() {
method execute (line 2126) | async execute() {
method compileSql (line 2129) | compileSql() {
method migrate (line 2132) | async migrate() {
method beginTransaction (line 2135) | async beginTransaction() {
method commitTransaction (line 2139) | async commitTransaction() {}
method rollbackTransaction (line 2140) | async rollbackTransaction() {}
method createSavepoint (line 2141) | async createSavepoint() {}
method rollbackToSavepoint (line 2142) | async rollbackToSavepoint() {}
method releaseSavepoint (line 2143) | async releaseSavepoint() {}
method hasTable (line 2144) | async hasTable() {
method hasColumn (line 2147) | async hasColumn() {
function createAdapter (line 2174) | function createAdapter(
function createTestDatabase (line 2183) | function createTestDatabase(adapter: DatabaseAdapter) {
FILE: packages/data-table/src/lib/database.ts
type TableColumnName (line 49) | type TableColumnName<table extends AnyTable> = keyof TableRow<table> & s...
type QualifiedTableColumnName (line 50) | type QualifiedTableColumnName<table extends AnyTable> =
type QueryColumnName (line 52) | type QueryColumnName<table extends AnyTable> =
type RowColumnName (line 56) | type RowColumnName<row extends Record<string, unknown>> = keyof row & st...
type QualifiedRowColumnName (line 57) | type QualifiedRowColumnName<
type QueryColumnTypeMapFromRow (line 62) | type QueryColumnTypeMapFromRow<
type QueryColumnTypeMap (line 75) | type QueryColumnTypeMap<table extends AnyTable> = Pretty<
type MergeColumnTypeMaps (line 79) | type MergeColumnTypeMaps<
type QueryColumns (line 92) | type QueryColumns<columnTypes extends Record<string, unknown>> = Extract<
type QueryColumnInput (line 97) | type QueryColumnInput<columnTypes extends Record<string, unknown>> = Col...
type SelectedAliasRow (line 101) | type SelectedAliasRow<
type RelationMapForSourceName (line 110) | type RelationMapForSourceName<tableName extends string> = Record<
type PrimaryKeyInputForRow (line 121) | type PrimaryKeyInputForRow<
type ReturningInput (line 130) | type ReturningInput<row extends Record<string, unknown>> = '*' | (keyof ...
type QueryTableInput (line 135) | type QueryTableInput<
type WriteResult (line 162) | type WriteResult = {
type WriteRowsResult (line 170) | type WriteRowsResult<row> = {
type WriteRowResult (line 179) | type WriteRowResult<row> = {
type QueryColumnTypesForTable (line 188) | type QueryColumnTypesForTable<table extends AnyTable> = QueryColumnTypeM...
type QueryForTable (line 193) | type QueryForTable<
type SingleTableColumn (line 207) | type SingleTableColumn<table extends AnyTable> = QueryColumns<QueryColum...
type SingleTableWhere (line 212) | type SingleTableWhere<table extends AnyTable> = WhereInput<SingleTableCo...
type OrderByTuple (line 217) | type OrderByTuple<table extends AnyTable> = [
type OrderByInput (line 225) | type OrderByInput<table extends AnyTable> = OrderByTuple<table> | OrderB...
type FindManyOptions (line 230) | type FindManyOptions<
type FindOneOptions (line 244) | type FindOneOptions<
type UpdateOptions (line 254) | type UpdateOptions<
type UpdateManyOptions (line 265) | type UpdateManyOptions<table extends AnyTable> = {
type DeleteManyOptions (line 276) | type DeleteManyOptions<table extends AnyTable> = {
type CountOptions (line 286) | type CountOptions<table extends AnyTable> = {
type CreateResultOptions (line 293) | type CreateResultOptions = {
type CreateRowOptions (line 301) | type CreateRowOptions<
type CreateManyResultOptions (line 313) | type CreateManyResultOptions = {
type CreateManyRowsOptions (line 321) | type CreateManyRowsOptions = {
type SavepointCounter (line 326) | type SavepointCounter = {
type DatabaseOptions (line 330) | type DatabaseOptions = {
type DatabaseInternalState (line 334) | type DatabaseInternalState = {
class Database (line 347) | class Database implements QueryExecutionContext {
method constructor (line 353) | constructor(adapter: DatabaseAdapter, options?: DatabaseOptions) {
method [createInternalDatabase] (line 359) | static [createInternalDatabase](
method adapter (line 370) | get adapter(): DatabaseAdapter {
method now (line 374) | now(): unknown {
method query (line 378) | query<
method create (line 410) | async create<
method createMany (line 480) | async createMany<table extends AnyTable>(
method find (line 509) | async find<
method findOne (line 536) | async findOne<
method findMany (line 560) | async findMany<
method count (line 595) | async count<table extends AnyTable>(
method update (line 608) | async update<
method updateMany (line 665) | async updateMany<table extends AnyTable>(
method delete (line 689) | async delete<table extends AnyTable>(
method deleteMany (line 698) | async deleteMany<table extends AnyTable>(
method exec (line 723) | async exec<input extends AnyQuery>(
method transaction (line 741) | async transaction<result>(
method [executeOperation] (line 786) | async [executeOperation](operation: DataManipulationOperation): Promis...
function createDatabase (line 827) | function createDatabase(
function createDatabaseWithTransaction (line 843) | function createDatabaseWithTransaction(
function defaultNow (line 854) | function defaultNow(): Date {
FILE: packages/data-table/src/lib/database/execution-context.ts
type QueryExecutionContext (line 10) | type QueryExecutionContext = {
FILE: packages/data-table/src/lib/database/helpers.ts
function asQueryTableInput (line 23) | function asQueryTableInput<table extends AnyTable>(
function getPrimaryKeyWhere (line 33) | function getPrimaryKeyWhere<table extends AnyTable>(
function getPrimaryKeyWhereFromRow (line 40) | function getPrimaryKeyWhereFromRow<table extends AnyTable>(
function resolveCreateRowWhere (line 53) | function resolveCreateRowWhere<table extends AnyTable>(
function normalizeOrderByInput (line 93) | function normalizeOrderByInput<table extends AnyTable>(
function toWriteResult (line 111) | function toWriteResult(result: WriteResult | WriteRowsResult<unknown>): ...
function hasScopedWriteModifiers (line 118) | function hasScopedWriteModifiers(state: {
function loadPrimaryKeyRowsForScope (line 126) | async function loadPrimaryKeyRowsForScope<table extends AnyTable>(
function buildPrimaryKeyPredicate (line 176) | function buildPrimaryKeyPredicate<table extends AnyTable>(
function rowKeys (line 206) | function rowKeys(row: Record<string, unknown>, keys: string[]): string[] {
FILE: packages/data-table/src/lib/database/query-execution.ts
function executeQuery (line 40) | async function executeQuery<input extends AnyQuery>(
function loadRowsWithRelationsForQuery (line 119) | async function loadRowsWithRelationsForQuery(
function loadRowsWithRelationsForState (line 127) | async function loadRowsWithRelationsForState(
function executeAll (line 143) | async function executeAll(
function executeFirst (line 152) | async function executeFirst(
function executeFind (line 165) | async fu
Condensed preview — 1168 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,299K chars).
[
{
"path": ".agents/skills/add-package/SKILL.md",
"chars": 6380,
"preview": "---\nname: add-package\ndescription: Create or align a package in the Remix monorepo to match existing package conventions"
},
{
"path": ".agents/skills/add-package/agents/openai.yaml",
"chars": 270,
"preview": "interface:\n display_name: 'Add Package'\n short_description: 'Create new Remix monorepo packages consistently'\n defaul"
},
{
"path": ".agents/skills/make-change-file/SKILL.md",
"chars": 3552,
"preview": "---\nname: make-change-file\ndescription: Create or update Remix repo change files under `packages/*/.changes`. Use when a"
},
{
"path": ".agents/skills/make-change-file/agents/openai.yaml",
"chars": 230,
"preview": "interface:\n display_name: 'Make Change File'\n short_description: 'Create or update Remix change files using repo conve"
},
{
"path": ".agents/skills/make-demo/SKILL.md",
"chars": 3392,
"preview": "---\nname: make-demo\ndescription: Create or revise demos in the Remix repository. Use when adding a new demo under demos/"
},
{
"path": ".agents/skills/make-demo/agents/openai.yaml",
"chars": 259,
"preview": "interface:\n display_name: 'Make Demo'\n short_description: 'Create high-quality Remix demos'\n default_prompt: 'Use $ma"
},
{
"path": ".agents/skills/make-pr/SKILL.md",
"chars": 3389,
"preview": "---\nname: make-pr\ndescription: Create GitHub pull requests with clear, reviewer-friendly descriptions. Use when asked to"
},
{
"path": ".agents/skills/make-pr/agents/openai.yaml",
"chars": 209,
"preview": "interface:\n display_name: 'Make PR'\n short_description: 'Create high-quality GitHub pull requests'\n default_prompt: '"
},
{
"path": ".agents/skills/publish-placeholder-package/SKILL.md",
"chars": 2616,
"preview": "---\nname: publish-placeholder-package\ndescription: Publish a placeholder npm package at version 0.0.0 so package names a"
},
{
"path": ".agents/skills/publish-placeholder-package/agents/openai.yaml",
"chars": 288,
"preview": "interface:\n display_name: 'Publish Placeholder Package'\n short_description: 'Publish npm placeholder package at 0.0.0'"
},
{
"path": ".agents/skills/supersede-pr/SKILL.md",
"chars": 1824,
"preview": "---\nname: supersede-pr\ndescription: Safely replace one GitHub pull request with another. Use when a user says a PR super"
},
{
"path": ".agents/skills/supersede-pr/agents/openai.yaml",
"chars": 193,
"preview": "interface:\n display_name: 'Supersede PR'\n short_description: 'Close superseded pull requests safely'\n default_prompt:"
},
{
"path": ".agents/skills/supersede-pr/scripts/close_superseded_pr.ts",
"chars": 3809,
"preview": "#!/usr/bin/env node\nimport { spawnSync } from 'node:child_process'\nimport * as process from 'node:process'\n\ntype ParsedA"
},
{
"path": ".agents/skills/supersede-pr/tsconfig.json",
"chars": 246,
"preview": "{\n \"compilerOptions\": {\n \"allowJs\": false,\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"noEmit"
},
{
"path": ".agents/skills/update-pr/SKILL.md",
"chars": 1846,
"preview": "---\nname: update-pr\ndescription: Update an existing GitHub pull request title and description so they accurately describ"
},
{
"path": ".agents/skills/update-pr/agents/openai.yaml",
"chars": 250,
"preview": "interface:\n display_name: 'Update PR'\n short_description: 'Refresh an existing pull request title and body.'\n default"
},
{
"path": ".agents/skills/write-api-docs/SKILL.md",
"chars": 5240,
"preview": "---\nname: write-api-docs\ndescription: Write or audit public API docs for Remix packages. Use when adding or tightening J"
},
{
"path": ".agents/skills/write-readme/SKILL.md",
"chars": 2927,
"preview": "---\nname: write-readme\ndescription: Write or rewrite package README files in the style used by the Remix repository. Use"
},
{
"path": ".agents/skills/write-readme/agents/openai.yaml",
"chars": 233,
"preview": "interface:\n display_name: 'Write Readme'\n short_description: 'Write Remix package READMEs in the repo style.'\n defaul"
},
{
"path": ".github/workflows/build.yaml",
"chars": 742,
"preview": "name: Build\n\non:\n push:\n branches:\n - main\n pull_request:\n branches-ignore:\n - release-v2\n - v2\n\n"
},
{
"path": ".github/workflows/check.yaml",
"chars": 1528,
"preview": "name: Type check, lint, validate change files\n\non:\n push:\n branches:\n - main\n pull_request:\n branches-ignor"
},
{
"path": ".github/workflows/data-table-integration.yaml",
"chars": 5098,
"preview": "name: Data Table Integration Tests\n\non:\n push:\n branches:\n - main\n paths:\n - '.github/workflows/data-ta"
},
{
"path": ".github/workflows/file-storage-integration.yaml",
"chars": 2687,
"preview": "name: File Storage Integration Tests\n\non:\n push:\n branches:\n - main\n paths:\n - '.github/workflows/file-"
},
{
"path": ".github/workflows/format.yml",
"chars": 2788,
"preview": "name: Format\n\non:\n push:\n branches:\n - main\n\nconcurrency:\n group: ${{ github.workflow }}-${{ github.ref }}\n c"
},
{
"path": ".github/workflows/generate-remix.yaml",
"chars": 3981,
"preview": "# Update the `remix` package by auto-generating from the `@remix-run/*` packages in the repo\n# runs on any pushes/PRs to"
},
{
"path": ".github/workflows/preview.yml",
"chars": 4300,
"preview": "# Create \"installable\" preview branches\n#\n# Commits to `main` push builds to a `preview/main` branch:\n# pnpm install \""
},
{
"path": ".github/workflows/publish.yaml",
"chars": 3140,
"preview": "name: Publish\n\non:\n push:\n branches:\n - main\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n ch"
},
{
"path": ".github/workflows/release-pr.yaml",
"chars": 780,
"preview": "name: Update \"Release\" PR\n\non:\n push:\n branches:\n - main\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}"
},
{
"path": ".github/workflows/session-integration.yaml",
"chars": 2386,
"preview": "name: Session Integration Tests\n\non:\n push:\n branches:\n - main\n paths:\n - '.github/workflows/session-in"
},
{
"path": ".github/workflows/test.yaml",
"chars": 3856,
"preview": "name: Test\n\non:\n push:\n branches:\n - main\n pull_request:\n branches-ignore:\n - release-v2\n - v2\n\nc"
},
{
"path": ".gitignore",
"chars": 147,
"preview": "dist/\nnode_modules/\npnpm-publish-summary.json\nreference/\n.tmp/\n/demos/tmp/\n*.db\n*.db-*\n*.sqlite\n*.sqlite-*\n*.sqlite3\n*.s"
},
{
"path": ".prettierignore",
"chars": 131,
"preview": "node_modules/\ndist/\ntmp/\nreference/\n**/*.bundled.*\n**/public/assets/\n**/test/fixtures/\n**/worker-configuration.d.ts\npnpm"
},
{
"path": ".prettierrc",
"chars": 61,
"preview": "printWidth: 100\nsemi: false\nsingleQuote: true\nuseTabs: false\n"
},
{
"path": ".vscode/settings.json",
"chars": 251,
"preview": "{\n \"deno.enablePaths\": [\"./packages/multipart-parser/examples/deno\"],\n \"nodejs-testing.extensions\": [\n {\n \"ext"
},
{
"path": ".vscode/task.json",
"chars": 106,
"preview": "{\n \"version\": \"0.1.0\",\n \"command\": \"./node_modules/.bin/tsc\",\n \"args\": [\"-v\"],\n \"echoCommand\": true\n}\n"
},
{
"path": "AGENTS.md",
"chars": 8356,
"preview": "# Remix 3 Development Guide\n\n## Commands\n\n- **Build**: `pnpm run build` (all packages) or `pnpm --filter @remix-run/<pac"
},
{
"path": "CONTRIBUTING.md",
"chars": 7347,
"preview": "Welcome to Remix! We're excited to have you contribute.\n\nThis guide will help you get started.\n\n## Setting Up Your Envir"
},
{
"path": "LICENSE",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2025 Shopify Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "README.md",
"chars": 8030,
"preview": "# Welcome to Remix 3!\n\nThis is the source repository for Remix 3. It is under active development.\n\nWe published [a blog "
},
{
"path": "cspell.yml",
"chars": 2437,
"preview": "version: '0.2'\nlanguage: en\nwords:\n - accentunder\n - actiontype\n - activedescendant\n - aftertoggle\n - arcrole\n - A"
},
{
"path": "decisions/001-route-pattern-vs-url-pattern.md",
"chars": 3255,
"preview": "# RoutePattern vs. URLPattern\n\nThe web has a built-in URL matcher called [`URLPattern`](https://developer.mozilla.org/en"
},
{
"path": "decisions/002-branching-and-releasing.md",
"chars": 2011,
"preview": "# Branching and Releasing in Remix 3\n\nBeginning in Remix 3 the `remix` package is an umbrella package for everything in "
},
{
"path": "demos/bookstore/.gitignore",
"chars": 34,
"preview": "public/assets/\ntmp/\ndata/*.sqlite\n"
},
{
"path": "demos/bookstore/README.md",
"chars": 3791,
"preview": "# Bookstore Demo\n\nA full-featured e-commerce bookstore demonstrating the most powerful patterns and features of Remix. T"
},
{
"path": "demos/bookstore/app/account.test.ts",
"chars": 1922,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/account.tsx",
"chars": 9773,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport * as s from 'remix/dat"
},
{
"path": "demos/bookstore/app/admin.books.test.ts",
"chars": 1061,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/admin.books.tsx",
"chars": 14920,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport * as s from 'remix/dat"
},
{
"path": "demos/bookstore/app/admin.orders.tsx",
"chars": 4880,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport { Database } from 'rem"
},
{
"path": "demos/bookstore/app/admin.test.ts",
"chars": 802,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/admin.tsx",
"chars": 2159,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\n\nimport { routes } from './ro"
},
{
"path": "demos/bookstore/app/admin.users.tsx",
"chars": 7634,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport * as s from 'remix/dat"
},
{
"path": "demos/bookstore/app/assets/cart-button.tsx",
"chars": 1113,
"preview": "import { type Handle, clientEntry, on } from 'remix/component'\n\nimport { routes } from '../routes.ts'\n\nlet moduleUrl = r"
},
{
"path": "demos/bookstore/app/assets/cart-items.tsx",
"chars": 6314,
"preview": "import { css, type Handle, clientEntry, on } from 'remix/component'\n\nimport { routes } from '../routes.ts'\n\nlet moduleUr"
},
{
"path": "demos/bookstore/app/assets/entry.tsx",
"chars": 808,
"preview": "import { run } from 'remix/component'\n\nlet app = run({\n async loadModule(moduleUrl: string, exportName: string) {\n l"
},
{
"path": "demos/bookstore/app/assets/image-carousel.tsx",
"chars": 4411,
"preview": "import { css, type Handle, clientEntry, on } from 'remix/component'\n\nimport { routes } from '../routes.ts'\n\nexport const"
},
{
"path": "demos/bookstore/app/auth.test.ts",
"chars": 8721,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/auth.tsx",
"chars": 14334,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport * as s from 'remix/dat"
},
{
"path": "demos/bookstore/app/books.test.ts",
"chars": 1234,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/books.tsx",
"chars": 6736,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { Frame, css } from 'remix/component'\n\nimport { routes } fro"
},
{
"path": "demos/bookstore/app/cart.test.ts",
"chars": 5683,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/cart.tsx",
"chars": 4941,
"preview": "import type { Controller, RequestContext } from 'remix/fetch-router'\nimport { Frame } from 'remix/component'\nimport { Da"
},
{
"path": "demos/bookstore/app/checkout.test.ts",
"chars": 2236,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/checkout.tsx",
"chars": 7569,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport * as s from 'remix/dat"
},
{
"path": "demos/bookstore/app/components/book-card.tsx",
"chars": 839,
"preview": "import { routes } from '../routes.ts'\nimport type { Book } from '../data/schema.ts'\nimport { Frame, css } from 'remix/co"
},
{
"path": "demos/bookstore/app/components/restful-form.tsx",
"chars": 1075,
"preview": "import type { Props, RemixNode } from 'remix/component'\n\nexport interface RestfulFormProps extends Props<'form'> {\n /**"
},
{
"path": "demos/bookstore/app/data/cart.ts",
"chars": 1501,
"preview": "export interface CartItem {\n bookId: number\n slug: string\n title: string\n price: number\n quantity: number\n}\n\nexport"
},
{
"path": "demos/bookstore/app/data/schema.ts",
"chars": 8927,
"preview": "import { belongsTo, column as c, table, hasMany } from 'remix/data-table'\nimport type { TableRow, TableRowWith } from 'r"
},
{
"path": "demos/bookstore/app/data/setup.test.ts",
"chars": 3417,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\nimport { fileURLToPath } from 'nod"
},
{
"path": "demos/bookstore/app/data/setup.ts",
"chars": 5246,
"preview": "import * as fs from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport BetterSqlite3 from 'better-sqlite3'\nimport"
},
{
"path": "demos/bookstore/app/fragments.tsx",
"chars": 1851,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport { Database } from 'rem"
},
{
"path": "demos/bookstore/app/layout.tsx",
"chars": 2397,
"preview": "import type { RemixNode } from 'remix/component'\n\nimport { routes } from './routes.ts'\nimport { getCurrentUserSafely } f"
},
{
"path": "demos/bookstore/app/marketing.test.ts",
"chars": 1034,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/marketing.tsx",
"chars": 7215,
"preview": "import type { BuildAction, Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\n\nimport { routes"
},
{
"path": "demos/bookstore/app/middleware/admin.ts",
"chars": 463,
"preview": "import type { Middleware } from 'remix/fetch-router'\n\nimport { getCurrentUser } from '../utils/context.ts'\n\n/**\n * Middl"
},
{
"path": "demos/bookstore/app/middleware/auth.ts",
"chars": 1809,
"preview": "import type { Middleware } from 'remix/fetch-router'\nimport type { Route } from 'remix/fetch-router/routes'\nimport { Dat"
},
{
"path": "demos/bookstore/app/middleware/database.ts",
"chars": 272,
"preview": "import type { Middleware } from 'remix/fetch-router'\nimport { Database } from 'remix/data-table'\n\nimport { db } from '.."
},
{
"path": "demos/bookstore/app/router.test.ts",
"chars": 663,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/bookstore/app/router.ts",
"chars": 2339,
"preview": "import { createRouter } from 'remix/fetch-router'\nimport { asyncContext } from 'remix/async-context-middleware'\nimport {"
},
{
"path": "demos/bookstore/app/routes.ts",
"chars": 2108,
"preview": "import { del, get, post, put, route, form, resources } from 'remix/fetch-router/routes'\n\nexport let routes = route({\n a"
},
{
"path": "demos/bookstore/app/uploads.test.ts",
"chars": 4287,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { loginAsAdmin, requestWit"
},
{
"path": "demos/bookstore/app/uploads.tsx",
"chars": 545,
"preview": "import type { BuildAction } from 'remix/fetch-router'\nimport { createFileResponse as sendFile } from 'remix/response/fil"
},
{
"path": "demos/bookstore/app/utils/context.ts",
"chars": 1146,
"preview": "import { createContextKey } from 'remix/fetch-router'\nimport { getContext } from 'remix/async-context-middleware'\n\nimpor"
},
{
"path": "demos/bookstore/app/utils/ids.ts",
"chars": 312,
"preview": "export function parseId(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isSaf"
},
{
"path": "demos/bookstore/app/utils/render.ts",
"chars": 1581,
"preview": "import type { RemixNode } from 'remix/component'\nimport { renderToStream } from 'remix/component/server'\nimport { getCon"
},
{
"path": "demos/bookstore/app/utils/session.ts",
"chars": 814,
"preview": "import * as path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { createCookie } from 'remix/cookie'\ni"
},
{
"path": "demos/bookstore/app/utils/uploads.ts",
"chars": 890,
"preview": "import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { FileUpload } from 'r"
},
{
"path": "demos/bookstore/data/migrations/20260228090000_create_bookstore_schema.ts",
"chars": 3419,
"preview": "import { column as c, createMigration } from 'remix/data-table/migrations'\nimport { table } from 'remix/data-table'\n\nexp"
},
{
"path": "demos/bookstore/package.json",
"chars": 853,
"preview": "{\n \"name\": \"bookstore-demo\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"better-sqlite3\": \"^12.6.2\""
},
{
"path": "demos/bookstore/public/app.css",
"chars": 4226,
"preview": "@layer app, rmx;\n\n/* CSS Reset */\n@layer app {\n *,\n *::before,\n *::after {\n margin: 0;\n padding: 0;\n box-siz"
},
{
"path": "demos/bookstore/server.ts",
"chars": 972,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\n\nimport { router } fro"
},
{
"path": "demos/bookstore/test/helpers.ts",
"chars": 2233,
"preview": "import { SetCookie, Cookie } from 'remix/headers'\n\n/**\n * Extract a specific cookie value from Set-Cookie headers\n */\nex"
},
{
"path": "demos/bookstore/tsconfig.json",
"chars": 708,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\", \"DOM.AsyncIterable\"],\n \"typ"
},
{
"path": "demos/frame-navigation/.gitignore",
"chars": 15,
"preview": "public/assets/\n"
},
{
"path": "demos/frame-navigation/app/assets/dashboard-stat-grid.tsx",
"chars": 1448,
"preview": "import { clientEntry, css, link, type Handle } from 'remix/component'\n\ntype StatCard = {\n label: string\n value: string"
},
{
"path": "demos/frame-navigation/app/assets/entry.tsx",
"chars": 4422,
"preview": "import type { FrameContent, RemixNode } from 'remix/component'\nimport { animateEntrance, createRoot, css, on, run, sprin"
},
{
"path": "demos/frame-navigation/app/assets/fake.tsx",
"chars": 125,
"preview": "// don't know why we need this, but esbuild won't build without, just waiting\n// for the JS asset handler\nconsole.log('l"
},
{
"path": "demos/frame-navigation/app/auth/controller.tsx",
"chars": 3434,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport { css } from 'remix/component'\nimport { redirect } from 'rem"
},
{
"path": "demos/frame-navigation/app/auth/session.ts",
"chars": 480,
"preview": "import { createCookie } from 'remix/cookie'\nimport { getContext } from 'remix/async-context-middleware'\n\nexport let auth"
},
{
"path": "demos/frame-navigation/app/lib/Layout.tsx",
"chars": 3951,
"preview": "import type { RemixNode } from 'remix/component'\nimport { css } from 'remix/component'\n\nimport { routes } from '../../co"
},
{
"path": "demos/frame-navigation/app/lib/NavLink.tsx",
"chars": 587,
"preview": "import type { RemixNode } from 'remix/component'\nimport type { Route } from 'remix/fetch-router/routes'\n\ntype NavLinkPro"
},
{
"path": "demos/frame-navigation/app/main/account.tsx",
"chars": 1148,
"preview": "import { css } from 'remix/component'\n\nexport function MainAccountPage() {\n return () => (\n <section>\n <h1 mix="
},
{
"path": "demos/frame-navigation/app/main/calendar.tsx",
"chars": 1083,
"preview": "import { css } from 'remix/component'\n\nexport function MainCalendarPage() {\n return () => (\n <section>\n <h1 mix"
},
{
"path": "demos/frame-navigation/app/main/controller.tsx",
"chars": 1132,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport type { RemixNode } from 'remix/component'\n\nimport type { rou"
},
{
"path": "demos/frame-navigation/app/main/courses.tsx",
"chars": 952,
"preview": "import { css } from 'remix/component'\n\nexport function MainCoursesPage() {\n return () => (\n <section>\n <h1 mix="
},
{
"path": "demos/frame-navigation/app/main/index.tsx",
"chars": 1173,
"preview": "import { css } from 'remix/component'\n\nimport { DashboardStatGrid } from '../assets/dashboard-stat-grid.tsx'\nimport { ro"
},
{
"path": "demos/frame-navigation/app/settings/controller.tsx",
"chars": 2085,
"preview": "import type { Controller } from 'remix/fetch-router'\nimport type { RemixNode } from 'remix/component'\nimport { Frame } f"
},
{
"path": "demos/frame-navigation/app/settings/grading.tsx",
"chars": 1247,
"preview": "import { css } from 'remix/component'\n\nexport function Grading() {\n return () => (\n <section>\n <h2 mix={titleSt"
},
{
"path": "demos/frame-navigation/app/settings/index.tsx",
"chars": 1098,
"preview": "import { css } from 'remix/component'\n\nexport function Index() {\n return () => (\n <section>\n <h2 mix={titleStyl"
},
{
"path": "demos/frame-navigation/app/settings/integrations.tsx",
"chars": 1258,
"preview": "import { css } from 'remix/component'\n\nexport function Integrations() {\n return () => (\n <section>\n <h2 mix={ti"
},
{
"path": "demos/frame-navigation/app/settings/layout.tsx",
"chars": 2556,
"preview": "import type { RemixNode } from 'remix/component'\nimport { css } from 'remix/component'\n\nimport { frames, routes } from '"
},
{
"path": "demos/frame-navigation/app/settings/notifications.tsx",
"chars": 1285,
"preview": "import { css } from 'remix/component'\n\nexport function Notifications() {\n return () => (\n <section>\n <h2 mix={t"
},
{
"path": "demos/frame-navigation/app/settings/privacy.tsx",
"chars": 1228,
"preview": "import { css } from 'remix/component'\n\nexport function Privacy() {\n return () => (\n <section>\n <h2 mix={titleSt"
},
{
"path": "demos/frame-navigation/app/settings/profile.tsx",
"chars": 981,
"preview": "import { css } from 'remix/component'\n\nexport function Profile() {\n return () => (\n <section>\n <h2 mix={titleSt"
},
{
"path": "demos/frame-navigation/config/render.tsx",
"chars": 2166,
"preview": "import type { RemixNode } from 'remix/component'\nimport { renderToStream, type ResolveFrameContext } from 'remix/compone"
},
{
"path": "demos/frame-navigation/config/router.tsx",
"chars": 1697,
"preview": "import { createRouter } from 'remix/fetch-router'\nimport { asyncContext } from 'remix/async-context-middleware'\nimport {"
},
{
"path": "demos/frame-navigation/config/routes.ts",
"chars": 597,
"preview": "import { form, get, post, route } from 'remix/fetch-router/routes'\n\nexport let frames = {\n settings: 'settings',\n} as c"
},
{
"path": "demos/frame-navigation/config/server.ts",
"chars": 798,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\n\nimport { router } fro"
},
{
"path": "demos/frame-navigation/package.json",
"chars": 740,
"preview": "{\n \"name\": \"frame-navigation-demo\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"remix\": \"workspace:"
},
{
"path": "demos/frame-navigation/tsconfig.json",
"chars": 646,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\"],\n \"module\": \"ES2022\",\n \""
},
{
"path": "demos/frames/.gitignore",
"chars": 16,
"preview": "public/assets/\n\n"
},
{
"path": "demos/frames/app/assets/client-frame-example.tsx",
"chars": 2014,
"preview": "import { clientEntry, Frame, css, on, type Handle } from 'remix/component'\n\nexport let ClientFrameExample = clientEntry("
},
{
"path": "demos/frames/app/assets/client-mounted-page-example.tsx",
"chars": 2092,
"preview": "import { clientEntry, Frame, css, on, type Handle } from 'remix/component'\nimport { routes } from '../routes.ts'\n\nexport"
},
{
"path": "demos/frames/app/assets/counter.tsx",
"chars": 1682,
"preview": "import { clientEntry, css, on, type Handle } from 'remix/component'\n\nexport let Counter = clientEntry(\n '/assets/counte"
},
{
"path": "demos/frames/app/assets/entry.tsx",
"chars": 665,
"preview": "import { run } from 'remix/component'\n\nlet app = run({\n async loadModule(moduleUrl, exportName) {\n let mod = await i"
},
{
"path": "demos/frames/app/assets/reload-scope.tsx",
"chars": 2377,
"preview": "import { clientEntry, css, on, type Handle } from 'remix/component'\n\nexport let ReloadScope = clientEntry(\n '/assets/re"
},
{
"path": "demos/frames/app/assets/reload-time.tsx",
"chars": 1103,
"preview": "import { clientEntry, css, on, type Handle } from 'remix/component'\n\nexport let ReloadTime = clientEntry(\n '/assets/rel"
},
{
"path": "demos/frames/app/assets/state-search-page.tsx",
"chars": 2491,
"preview": "import { clientEntry, Frame, css, on, ref, type Handle } from 'remix/component'\nimport { routes } from '../routes.ts'\n\nl"
},
{
"path": "demos/frames/app/router.tsx",
"chars": 22491,
"preview": "import { createRouter } from 'remix/fetch-router'\nimport { logger } from 'remix/logger-middleware'\nimport { staticFiles "
},
{
"path": "demos/frames/app/routes.ts",
"chars": 731,
"preview": "import { get, route } from 'remix/fetch-router/routes'\n\nexport let routes = route({\n home: get('/'),\n time: get('/time"
},
{
"path": "demos/frames/app/us-states.ts",
"chars": 984,
"preview": "export let unitedStates = [\n 'Alabama',\n 'Alaska',\n 'Arizona',\n 'Arkansas',\n 'California',\n 'Colorado',\n 'Connect"
},
{
"path": "demos/frames/package.json",
"chars": 737,
"preview": "{\n \"name\": \"frames-demo\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"remix\": \"workspace:*\"\n },\n "
},
{
"path": "demos/frames/server.ts",
"chars": 792,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\n\nimport { router } fro"
},
{
"path": "demos/frames/tsconfig.json",
"chars": 708,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\", \"DOM.AsyncIterable\"],\n \"typ"
},
{
"path": "demos/sse/.gitignore",
"chars": 15,
"preview": "public/assets/\n"
},
{
"path": "demos/sse/app/assets/entry.tsx",
"chars": 465,
"preview": "import { run } from 'remix/component'\n\nlet app = run({\n async loadModule(moduleUrl: string, name: string) {\n let mod"
},
{
"path": "demos/sse/app/assets/message-stream.tsx",
"chars": 3820,
"preview": "import { addEventListeners, clientEntry, css, type Handle } from 'remix/component'\n\nimport { routes } from '../routes.ts"
},
{
"path": "demos/sse/app/layout.tsx",
"chars": 1536,
"preview": "import { css, type RemixNode } from 'remix/component'\n\nimport { routes } from './routes.ts'\n\nconst rawCss = String.raw\n\n"
},
{
"path": "demos/sse/app/router.test.ts",
"chars": 2636,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { router } from './router."
},
{
"path": "demos/sse/app/router.tsx",
"chars": 7155,
"preview": "import { createRouter } from 'remix/fetch-router'\nimport { compression } from 'remix/compression-middleware'\nimport { lo"
},
{
"path": "demos/sse/app/routes.ts",
"chars": 162,
"preview": "import { get, route } from 'remix/fetch-router/routes'\n\nexport let routes = route({\n assets: '/assets/*path',\n home: g"
},
{
"path": "demos/sse/app/utils/render.ts",
"chars": 284,
"preview": "import type { RemixNode } from 'remix/component'\nimport { renderToStream } from 'remix/component/server'\nimport { create"
},
{
"path": "demos/sse/package.json",
"chars": 835,
"preview": "{\n \"name\": \"sse-demo\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"@remix-run/component\": \"workspac"
},
{
"path": "demos/sse/server.ts",
"chars": 583,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\n\nimport { router } fro"
},
{
"path": "demos/sse/tsconfig.json",
"chars": 708,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\", \"DOM.AsyncIterable\"],\n \"typ"
},
{
"path": "demos/unpkg/README.md",
"chars": 1570,
"preview": "# UNPKG Demo\n\nA lo-fi clone of [unpkg.com](https://unpkg.com) that lets you browse the contents of any npm package. This"
},
{
"path": "demos/unpkg/app/breadcrumb.ts",
"chars": 895,
"preview": "import { html } from './utils/render.ts'\n\nexport function renderBreadcrumb(packageName: string, version: string, dirPath"
},
{
"path": "demos/unpkg/app/directory.ts",
"chars": 2464,
"preview": "import { detectMimeType } from 'remix/mime'\n\nimport { renderBreadcrumb } from './breadcrumb.ts'\nimport type { PackageFil"
},
{
"path": "demos/unpkg/app/error.ts",
"chars": 367,
"preview": "import { html, render } from './utils/render.ts'\n\nexport function renderError(title: string, message: string): Response "
},
{
"path": "demos/unpkg/app/file-content.ts",
"chars": 3386,
"preview": "import { renderBreadcrumb } from './breadcrumb.ts'\nimport type { PackageFile } from './utils/npm.ts'\nimport { html, rend"
},
{
"path": "demos/unpkg/app/router.test.ts",
"chars": 6621,
"preview": "import * as assert from 'node:assert/strict'\nimport { after, before, describe, it } from 'node:test'\n\nimport {\n install"
},
{
"path": "demos/unpkg/app/router.ts",
"chars": 3674,
"preview": "import { createRouter } from 'remix/fetch-router'\nimport { createRedirectResponse as redirect } from 'remix/response/red"
},
{
"path": "demos/unpkg/app/routes.ts",
"chars": 373,
"preview": "import { get, route } from 'remix/fetch-router/routes'\n\nexport let routes = route({\n // Home page with instructions\n h"
},
{
"path": "demos/unpkg/app/utils/cache.ts",
"chars": 546,
"preview": "import * as os from 'node:os'\nimport * as path from 'node:path'\nimport { createFsFileStorage } from 'remix/file-storage/"
},
{
"path": "demos/unpkg/app/utils/npm.test.ts",
"chars": 11260,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport {\n parsePackagePath,\n re"
},
{
"path": "demos/unpkg/app/utils/npm.ts",
"chars": 9927,
"preview": "import * as zlib from 'node:zlib'\nimport { parseTar, type TarEntry } from 'remix/tar-parser'\nimport * as semver from 'se"
},
{
"path": "demos/unpkg/app/utils/render.test.ts",
"chars": 867,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { formatBytes } from './re"
},
{
"path": "demos/unpkg/app/utils/render.ts",
"chars": 5379,
"preview": "import { html, type SafeHtml } from 'remix/html-template'\nimport { createHtmlResponse } from 'remix/response/html'\n\nexpo"
},
{
"path": "demos/unpkg/package.json",
"chars": 443,
"preview": "{\n \"name\": \"unpkg-demo\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"remix\": \"workspace:*\",\n \"se"
},
{
"path": "demos/unpkg/server.ts",
"chars": 781,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\n\nimport { router } fro"
},
{
"path": "demos/unpkg/test/fixtures/is-number-metadata.json",
"chars": 36169,
"preview": "{\"_id\":\"is-number\",\"_rev\":\"43-3fe877efbeead423ee53a12d5ca44395\",\"name\":\"is-number\",\"description\":\"Returns true if a numb"
},
{
"path": "demos/unpkg/test/mock-fetch.ts",
"chars": 3659,
"preview": "import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { mock } from 'node:test'\n\nlet fixturesDir = path"
},
{
"path": "demos/unpkg/tsconfig.json",
"chars": 347,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\"],\n \"module\": \"ES2022\",\n \""
},
{
"path": "docs/.gitignore",
"chars": 7,
"preview": "build/\n"
},
{
"path": "docs/package.json",
"chars": 1259,
"preview": "{\n \"name\": \"remix-the-docs\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"front-matter\": \"^4.0.2\",\n "
},
{
"path": "docs/public/docs.css",
"chars": 10208,
"preview": ":root {\n --spacing-xxs: 4px;\n --spacing-xs: 8px;\n --spacing-small: 12px;\n --spacing-medium: 24px;\n --spacing-large:"
},
{
"path": "docs/src/client/entry.tsx",
"chars": 1152,
"preview": "import { run } from 'remix/component'\n\nlet app = run({\n async loadModule(moduleUrl: string, exportName: string) {\n l"
},
{
"path": "docs/src/generate/documented-api.ts",
"chars": 19665,
"preview": "import * as typedoc from 'typedoc'\nimport { getApiNameFromFullName, invariant, unimplemented, warn } from './utils.ts'\ni"
},
{
"path": "docs/src/generate/index.ts",
"chars": 2001,
"preview": "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport util from 'node:util'\nimport { getDocumentedAPI } "
},
{
"path": "docs/src/generate/markdown.ts",
"chars": 7104,
"preview": "import * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as prettier from 'prettier'\nimport {\n "
},
{
"path": "docs/src/generate/symbols.ts",
"chars": 1952,
"preview": "// Ignore auto-linking for APIs of ours that conflict with built in symbols\nexport const IGNORE_SYMBOLS = new Set([\n 'a"
},
{
"path": "docs/src/generate/typedoc.ts",
"chars": 9286,
"preview": "import * as path from 'node:path'\nimport * as typedoc from 'typedoc'\nimport { debug, getApiNameFromFullName, info, invar"
},
{
"path": "docs/src/generate/utils.ts",
"chars": 1060,
"preview": "export function getApiNameFromFullName(fullName: string): string {\n return fullName.split('.').slice(-1)[0]\n}\n\nconst is"
},
{
"path": "docs/src/server/components.tsx",
"chars": 8253,
"preview": "import type { RemixNode } from 'remix/component/jsx-runtime'\nimport type { DocFile } from './markdown.ts'\nimport { route"
},
{
"path": "docs/src/server/index.ts",
"chars": 859,
"preview": "import * as http from 'node:http'\nimport { createRequestListener } from 'remix/node-fetch-server'\nimport { createRouter,"
},
{
"path": "docs/src/server/markdown.ts",
"chars": 6169,
"preview": "import * as frontmatter from 'front-matter'\nimport type { Element } from 'hast'\nimport { Marked, type MarkedExtension } "
},
{
"path": "docs/src/server/prerender.ts",
"chars": 4864,
"preview": "import * as cp from 'node:child_process'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport"
},
{
"path": "docs/src/server/router.tsx",
"chars": 4723,
"preview": "import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as semver from 'semver'\nimport { type RemixNode"
},
{
"path": "docs/src/server/routes.ts",
"chars": 221,
"preview": "import { route } from 'remix/fetch-router/routes'\n\nexport const routes = route({\n assets: '/(:version/)assets/*asset',\n"
},
{
"path": "docs/tsconfig.json",
"chars": 614,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\"],\n \"module\": \"ES2022\",\n \""
},
{
"path": "eslint.config.js",
"chars": 6770,
"preview": "import tseslint from 'typescript-eslint'\nimport importPlugin from 'eslint-plugin-import'\nimport jsdoc from 'eslint-plugi"
},
{
"path": "package.json",
"chars": 1527,
"preview": "{\n \"name\": \"remix-the-web\",\n \"type\": \"module\",\n \"private\": true,\n \"packageManager\": \"pnpm@10.32.1\",\n \"dependencies\""
},
{
"path": "packages/async-context-middleware/.changes/README.md",
"chars": 143,
"preview": "# Changes Directory\n\nSee the [Contributing Guide](../../../CONTRIBUTING.md#adding-a-change-file) for documentation on ho"
},
{
"path": "packages/async-context-middleware/CHANGELOG.md",
"chars": 888,
"preview": "# `async-context-middleware` CHANGELOG\n\nThis is the changelog for [`async-context-middleware`](https://github.com/remix-"
},
{
"path": "packages/async-context-middleware/LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2025 Shopify Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "packages/async-context-middleware/README.md",
"chars": 1487,
"preview": "# async-context-middleware\n\nRequest-scoped async context middleware for Remix. It stores each request context in [`Async"
},
{
"path": "packages/async-context-middleware/package.json",
"chars": 1408,
"preview": "{\n \"name\": \"@remix-run/async-context-middleware\",\n \"version\": \"0.1.3\",\n \"description\": \"Middleware for storing reques"
},
{
"path": "packages/async-context-middleware/src/index.ts",
"chars": 66,
"preview": "export { asyncContext, getContext } from './lib/async-context.ts'\n"
},
{
"path": "packages/async-context-middleware/src/lib/async-context.test.ts",
"chars": 669,
"preview": "import * as assert from 'node:assert/strict'\nimport { describe, it } from 'node:test'\n\nimport { createRouter } from '@re"
},
{
"path": "packages/async-context-middleware/src/lib/async-context.ts",
"chars": 863,
"preview": "import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport type { Middleware, RequestContext } from '@remix-run/fetch-"
},
{
"path": "packages/async-context-middleware/tsconfig.build.json",
"chars": 204,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"declaration\": true,\n \"declarationMap\": true,\n \"outDi"
},
{
"path": "packages/async-context-middleware/tsconfig.json",
"chars": 321,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"lib\": [\"ES2024\", \"DOM\", \"DOM.Iterable\"],\n \"module\": \"ES2022\",\n \""
},
{
"path": "packages/component/.changes/README.md",
"chars": 143,
"preview": "# Changes Directory\n\nSee the [Contributing Guide](../../../CONTRIBUTING.md#adding-a-change-file) for documentation on ho"
},
{
"path": "packages/component/.changes/minor.01-add-mixin-system-and-core-helpers.md",
"chars": 319,
"preview": "Add the new host `mix` prop and mixin authoring APIs in `@remix-run/component`.\n\nNew exports include:\n\n- `createMixin`\n-"
},
{
"path": "packages/component/.changes/minor.02-add-press-and-keyboard-mixins.md",
"chars": 261,
"preview": "Add new interaction mixins for normalized user input events:\n\n- `pressEvents(...)` for pointer/keyboard \"press\" interact"
},
{
"path": "packages/component/.changes/minor.03-add-animation-mixins.md",
"chars": 246,
"preview": "Add mixin-first animation APIs for host elements:\n\n- `animateEntrance(...)`\n- `animateExit(...)`\n- `animateLayout(...)`\n"
},
{
"path": "packages/component/.changes/minor.04-remove-legacy-on-prop.md",
"chars": 361,
"preview": "BREAKING CHANGE: remove legacy host-element `on` prop support in `@remix-run/component`.\n\nUse the `on()` mixin instead:\n"
},
{
"path": "packages/component/.changes/minor.05-remove-legacy-css-prop.md",
"chars": 289,
"preview": "BREAKING CHANGE: remove legacy host-element `css` prop runtime support in `@remix-run/component`.\n\nUse the `css(...)` mi"
},
{
"path": "packages/component/.changes/minor.06-remove-legacy-animate-prop.md",
"chars": 348,
"preview": "BREAKING CHANGE: remove legacy host-element `animate` prop runtime support in `@remix-run/component`.\n\nUse animation mix"
},
{
"path": "packages/component/.changes/minor.07-remove-legacy-connect-prop.md",
"chars": 316,
"preview": "BREAKING CHANGE: remove legacy host-element `connect` prop support in `@remix-run/component`.\n\nUse the `ref(...)` mixin "
}
]
// ... and 968 more files (download for full content)
About this extraction
This page contains the full source code of the remix-run/remix GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1168 files (4.8 MB), approximately 1.3M tokens, and a symbol index with 4616 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.