1 hours ago
remix

node-serve v0.2.0

Minor Changes

  • Add a setup(app) option to serve() so managed node-serve apps can register native uWebSockets.js WebSocket routes and connection filters before the Fetch fallback route starts listening.

    import { serve } from 'remix/node-serve'
    
    serve(handler, {
      setup(app) {
        app.ws('/ws/chat', {
          message(ws, message, isBinary) {
            ws.publish('chat', message, isBinary)
          },
        })
    
        app.filter((_res, count) => {
          console.log(`Active uWS connections: ${count}`)
        })
      },
    })

Patch Changes

  • Pass native Request objects to Fetch handlers instead of lazy request facades.

  • Install uWebSockets.js as a required dependency so remix/node-serve works when package managers omit optional dependencies.

1 hours ago
remix

response v0.3.4

Patch Changes

1 hours ago
remix

session-middleware v0.3.0

Minor Changes

  • Added context.session as a direct property installed by the session middleware. context.get(Session) remains supported for keyed context access.

Patch Changes

1 hours ago
remix

node-tsx v0.1.0

Minor Changes

1 hours ago
remix

test v0.4.0

Minor Changes

  • Migrated @remix-run/test from the tsx package to Remix's internal @remix-run/node-tsx module loader.

    BREAKING CHANGE: .ts, .tsx, and .jsx module loading in @remix-run/test now uses Remix's internal @remix-run/node-tsx loader. Test modules are still transformed before execution, including JSX and TypeScript syntax that requires JavaScript output, but the loader is now maintained inside Remix.

Patch Changes

  • Fix browser test runs so large suites can exceed the per-file timeout as long as individual test files keep reporting progress.

  • Fix describe.skip and describe.only so they propagate to nested describe blocks. Previously the skipped/focused state was set only on the outer suite, so tests inside nested describes still ran (or were incorrectly skipped under only).

  • Bumped @remix-run/* dependencies:

1 hours ago
remix

route-pattern v0.21.0

Minor Changes

  • BREAKING CHANGE: Remove the compareFn parameter from match and matchAll methods

    Matches always sort by specificity (most specific first). If you need a different order, sort the result of matchAll yourself.

    import * as Specificity from 'remix/route-pattern/specificity'
    
    // before
    matcher.matchAll(url, Specificity.ascending)
    
    // after
    matcher.matchAll(url).sort(Specificity.ascending)
  • BREAKING CHANGE: New modular APIs and subpath exports

    Previously, this package shipped the default export and a /specificity export. A typical Remix app does not do any client-side matching but all the matching logic would ship to the browser anyway causing JS bloat.

    Now, features are organized into separate subpath exports, so even without a bundler, only the code you need ends up in the browser. For example, this reduced JS from route-pattern in demos/bookstore from 25kB (14.9kB compressed) to 8.8kb (7kB compressed) which amounts to ~65% reduction (~53% reduction compressed).

    To achieve this, we've reworked our core APIs to be simpler and more independently useful. So instead of a single RoutePattern class that does it all (.href, .match, ...), the new RoutePattern class is a thin layer around the parsed pattern that includes RoutePattern.parse static method for parsing and .source, .toString() and .toJSON() for serialization.

    The rest of the functionality comes from dedicated subpath exports:

    • remix/route-pattern/href : Generate hrefs for patterns with type safe params.
    • remix/route-pattern/match : Match against one pattern with type inference for params. Or match against many patterns with deterministic ranking and attached data.
    • remix/route-pattern/join : Combine two patterns into one. Override protocol, hostname, port. Join pathnames. Merge search constraints.

    remix/route-pattern/specificity remains the same as before, providing utilities for ranking matches.

    Additionally, ArrayMatcher and TrieMatcher have been replaced by createMultiMatcher (which is now always backed by trie-based matching). To match against only a single pattern while receiving type safe params from the match, use createMatcher.

    See the new README for details.

Patch Changes

  • Encode href params so pathname params cannot inject URL path, dot segment, query, or hash syntax. Wildcard pathname params now preserve slash-separated structure while encoding each segment, and hostname params are normalized or rejected when they would inject URL authority, path, query, or hash syntax. Matchers now decode generated pathname params so reserved characters like /, ?, and # round-trip as param content.

  • Do not allow partial matches for variables and wildcards in pathname

    let matcher = createMultiMatcher<string>()
    matcher.add('/files/:name.md', 'original')
    matcher.add('/files/:name.md.backup', 'backup')
    
    // before: 'original' included since `:name.md` partially matches `readme.md.backup`
    matcher.matchAll('https://example.com/files/readme.md.backup').map((match) => match.data)
    // ❌ ['backup', 'original']
    
    // after: only matches when the pattern covers the whole segment
    matcher.matchAll('https://example.com/files/readme.md.backup').map((match) => match.data)
    // ✅ ['backup']
  • Fix matcher to match origin-less patterns (e.g. /, /about) against URLs that have an explicit port. Previously a pattern like / would not match http://localhost:44199/.

1 hours ago
remix

ui v0.1.2

Patch Changes

  • Fix a bug in Safari where cross-origin links to a new subdomain incorrectly set event.canIntercept=true and try to opt-into a <Frame> navigation which fails. Cross-origin links now correctly fall through to a document navigation in Safari.

  • Keep streamed frame content in its template when a resolved frame stream starts with a doctype-only chunk.

  • Emit the built-in theme reset in rmx-reset so generated Remix UI component styles can override it. Document where app layers should sit relative to Remix UI layers.

  • Fixed layout animation interruptions so they restart from their current position and don't restart for updates that don't change their final position.

  • Improved type inference for on mixin

    When defining a wrapper for on, use target generic on your handler type:

    import { on, type Dispatched } from '@remix-run/ui'
    
    const ACCORDION_CHANGE_EVENT = 'rmx:accordion-change' as const
    
    type AccordionChangeEvent = Event & {
      accordionType: 'single' | 'multiple'
      itemValue: string
      value: string | null | string[]
    }
    
    declare global {
      interface HTMLElementEventMap {
        [ACCORDION_CHANGE_EVENT]: AccordionChangeEvent
      }
    }
    
    type AccordionChangeHandler<target extends HTMLElement> = (
      event: Dispatched<AccordionChangeEvent, target>,
      signal: AbortSignal,
    ) => void | Promise<void>
    
    export function onAccordionChange<target extends HTMLElement>(
      handler: AccordionChangeHandler<target>,
      captureBoolean?: boolean,
    ) {
      return on(ACCORDION_CHANGE_EVENT, handler, captureBoolean)
    }
    
    let button = (
      <button
        mix={[
          onAccordionChange((event, signal) => {
            event
            // ^? Dispatched<AccordionChangeEvent, HTMLButtonElement>
            event.currentTarget
            //    ^? HTMLButtonElement
          }),
        ]}
      />
    )
  • Preserve hydrated client entry instances and nested frame resolution during full-document root frame reloads.

  • Document the run() loadModule and resolveFrame hooks so editor hints explain how to hydrate client entries and resolve browser-loaded frames.

  • Optimize UI runtime hot paths.

    • Fast path for plain on() mixins that patches host listeners in place.
    • Lazy direct listener closures for event listeners managed by the runtime.
    • Lazy mixin scope signals to avoid unnecessary AbortController work.
    • Faster keyed reconciliation for in-order, append-only, single-removal, and pair-swap lists.
    • Property-level patching for object styles during updates.
    • Bulk clearing for removable child lists, with an innerHTML guard.
  • Fix a flash of unstyled content when navigating between two pages whose hydrated client entries use different css() rules. Style adoption now releases prior-page server styles by refcount instead of resetting the adopted stylesheet, so DOM preserved across a reload (e.g. inside a still-hydrated client-entry boundary) keeps its rules until the new module finishes loading and replaces it.

  • Fix server rendering for <textarea value>, <textarea defaultValue>, <input defaultValue>, and <input defaultChecked> so initial form control content matches client rendering, and disallow textarea children in JSX types.

1 hours ago
remix

static-middleware v0.4.9

Patch Changes

1 hours ago
remix

render-middleware v0.1.0

Minor Changes

  • Initial release of @remix-run/render-middleware, which provides the Renderer context key, Renderer type, and renderWith() middleware for adding request-scoped renderers to fetch-router request context. Renderers are available as both context.render and context.get(Renderer).

Patch Changes

1 hours ago
remix

remix v3.0.0-beta.1

Pre-release Changes

  • BREAKING CHANGE: Removed the ContextWithAuth and ContextWithRequiredAuth helper types from remix/auth-middleware. Derive auth-aware request context from the actual auth middleware tuple with MiddlewareContext, or use the core ContextWithEntry helper from remix/fetch-router when manually composing context types without a middleware tuple.

    import { requireAuth } from 'remix/auth-middleware'
    import type { MiddlewareContext } from 'remix/fetch-router'
    
    let protectedMiddleware = [requireAuth<AuthIdentity>()] as const
    type AppAuthContext = MiddlewareContext<typeof protectedMiddleware, AppContext>
  • BREAKING CHANGE: Remix app scaffolding, remix doctor, and remix routes now use app/actions with controller files only. The old app/controllers directory name has been replaced by app/actions, and root route actions should no longer live in standalone files.

    Move route controllers from app/controllers to app/actions, consolidate root route actions into app/actions/controller.tsx, and map nested route maps explicitly in app/router.ts with one router.map(...) call per route map. Controller middleware applies only to direct actions owned by that controller.

  • BREAKING CHANGE: Removed the ContextWithRenderer helper type from remix/render-middleware. Derive renderer-aware request context from the renderWith() middleware tuple with MiddlewareContext, or use the core ContextWithEntry helper from remix/fetch-router when manually composing context types without a middleware tuple.

    import { renderWith } from 'remix/render-middleware'
    import type { MiddlewareContext } from 'remix/fetch-router'
    
    let render = renderWith(() => (value: string) => new Response(value))
    type AppContext = MiddlewareContext<[typeof render]>
  • BREAKING CHANGE: remix test and remix/test now use Remix's internal node-tsx loader instead of the tsx package.

    Test modules are still transformed before execution, including JSX and TypeScript syntax that requires JavaScript output, but the loader is now maintained inside Remix through remix/node-tsx.

  • BREAKING CHANGE: remix/async-context-middleware no longer exposes AsyncContextTypes. getContext() now derives its type from remix/fetch-router's RouterTypes.context, with route params broadened to AnyParams, so apps only need the router context augmentation.

  • BREAKING CHANGE: Updated the re-exported remix/fetch-router helper types around full request-context types and stored route handlers. Action, Controller, and RequestHandler now take the full request context type, MiddlewareContext accepts middleware values plus an optional base context, and createAction()/createController() are the preferred helpers for stored handlers.

    For most apps, configure RouterTypes.context once and let createController() infer route action context from the route map and controller middleware:

    declare module 'remix/fetch-router' {
      interface RouterTypes {
        context: AppContext
      }
    }
    
    let accountMiddleware = [requireAuth<AuthIdentity>()] as const
    
    let controller = createController(routes, {
      middleware: accountMiddleware,
      actions: {
        account(context) {
          return Response.json(context.auth.identity)
        },
      },
    })

    Low-level context transform helpers such as BuildAction, MiddlewareContextTransform, ContextTransform, ApplyContextTransform, ApplyMiddleware, and ApplyMiddlewareTuple are no longer exported. Use ContextWithParams, ContextWithEntry, ContextWithEntries, MiddlewareContext, and RouteEntry when manually composing request context or custom matcher payloads.

  • BREAKING CHANGE: In remix/route-pattern, remove the compareFn parameter from match and matchAll.

    Matches always sort by specificity (most specific first). If you need a different order, sort the result of matchAll yourself.

    import * as Specificity from 'remix/route-pattern/specificity'
    
    // before
    matcher.matchAll(url, Specificity.ascending)
    
    // after
    matcher.matchAll(url).sort(Specificity.ascending)
  • BREAKING CHANGE: New modular remix/route-pattern APIs and subpath exports

    Previously, remix/route-pattern bundled URL generation, matching, and specificity helpers into one entrypoint. A typical Remix app does not do any client-side matching, but all the matching logic would ship to the browser anyway, causing JS bloat.

    Now, route pattern features are organized into separate subpath exports, so even without a bundler, only the code you need ends up in the browser:

    • remix/route-pattern/href generates hrefs for patterns with type-safe params.
    • remix/route-pattern/match matches against one pattern with type inference for params, or against many patterns with deterministic ranking and attached data.
    • remix/route-pattern/join combines two patterns into one, including protocol, hostname, port, pathname, and search constraints.
    • remix/route-pattern/specificity continues to provide utilities for ranking matches.

    The base remix/route-pattern export now focuses on parsing and serializing route patterns.

  • Expose @remix-run/node-tsx through remix/node-tsx and remix/node-tsx/load-module.

    Use node --import remix/node-tsx to run .ts, .tsx, and .jsx files directly in Node.js with TypeScript and JSX syntax support. The loader transforms TypeScript syntax that requires JavaScript output, including enums, runtime namespaces, and parameter properties, while preserving Node.js module resolution.

  • Updated the remix package with domain-oriented exports, no longer only mapping 1:1 to underlying @remix-run/* packages. Existing 1:1 package exports remain available during the beta migration and will be removed before a Remix 3.0.0 stable release.

    Preferred package mappings:

    • remix/async-context-middlewareremix/middleware/async-context
    • remix/auth-middlewareremix/middleware/auth
    • remix/compression-middlewareremix/middleware/compression
    • remix/cop-middlewareremix/middleware/cop
    • remix/cors-middlewareremix/middleware/cors
    • remix/csrf-middlewareremix/middleware/csrf
    • remix/data-table-mysqlremix/data-table/mysql
    • remix/data-table-postgresremix/data-table/postgres
    • remix/data-table-sqliteremix/data-table/sqlite
    • remix/fetch-routerremix/router
    • remix/fetch-router/routesremix/routes
    • remix/file-storage-s3remix/file-storage/s3
    • remix/form-data-middlewareremix/middleware/form-data
    • remix/logger-middlewareremix/middleware/logger
    • remix/method-override-middlewareremix/middleware/method-override
    • remix/render-middlewareremix/middleware/render
    • remix/session-middlewareremix/middleware/session
    • remix/session-storage-memcacheremix/session-storage/memcache
    • remix/session-storage-redisremix/session-storage/redis
    • remix/session/cookie-storageremix/session-storage/cookie
    • remix/session/fs-storageremix/session-storage/fs
    • remix/session/memory-storageremix/session-storage/memory
    • remix/static-middlewareremix/middleware/static
  • Added support for middleware-installed direct request context properties through remix/fetch-router, including the new ContextEntry type for object-shaped context entries. Built-in middleware now uses this for context.auth, context.formData, context.logger, context.render, and context.session; keyed access with context.get(...) remains supported.

  • Expose the node-serve setup(app) option through remix/node-serve so apps can register native uWebSockets.js WebSocket routes and connection filters before the Fetch fallback route starts listening.

    import { serve } from 'remix/node-serve'
    
    serve(handler, {
      setup(app) {
        app.ws('/ws/chat', {
          message(ws, message, isBinary) {
            ws.publish('chat', message, isBinary)
          },
        })
      },
    })
  • Include source-adjacent README files for generated remix/* exports in the published package so package managers and tooling can discover the relevant module documentation from node_modules/remix.

  • Fix remix/node-fetch-server so streaming responses write the first chunk immediately instead of waiting for another chunk.

  • Fix matching so dynamic pathname segments and wildcard continuations only match when they cover the full pathname range being tested.

    import { createMultiMatcher } from 'remix/route-pattern'
    
    let matcher = createMultiMatcher<string>()
    matcher.add('/files/:name.md', 'markdown')
    matcher.add('/files/:name.md.backup', 'backup')
    
    // before: matched both patterns because `/files/:name.md` matched a prefix of the segment
    matcher.matchAll('https://example.com/files/readme.md.backup').map((match) => match.data)
    // ['backup', 'markdown']
    
    // after: only matches when the pattern covers the whole segment
    matcher.matchAll('https://example.com/files/readme.md.backup').map((match) => match.data)
    // ['backup']
  • Bumped @remix-run/* dependencies: