remix-run/remix
 Watch   
 Star   
 Fork   
18 days ago
remix

remix v3.0.0-alpha.4

Pre-release Changes

  • BREAKING CHANGE: Remove the remix/data-table/sql export. Import SqlStatement, sql, and rawSql from remix/data-table instead.

    remix/data-table/sql-helpers remains available for adapter-facing SQL utilities.

    remix/data-table now exports the Database class as a runtime value. You can construct a database directly with new Database(adapter, options) or keep using createDatabase(adapter, options), which now delegates to the class constructor.

    BREAKING CHANGE: remix/data-table no longer exports QueryBuilder. Import Query and query from remix/data-table, then execute unbound queries with db.exec(...). db.exec(...) now accepts only raw SQL or Query values, and unbound terminal methods like first(), count(), insert(), and update() return Query objects instead of separate command descriptor types. db.query(table) remains available as shorthand and now returns the same bound Query class.

    remix/data-table/migrations no longer exports a separate Database type alias. Import Database from remix/data-table when you need the migration db type directly.

    The incidental QueryMethod type export has also been removed; use Database['query'] or QueryForTable<table> when you need that type shape.

    Added package.json exports:

    • remix/auth-middleware to re-export APIs from @remix-run/auth-middleware
    • remix/auth to re-export APIs from @remix-run/auth
  • Add remix/cors-middleware to re-export the CORS middleware APIs from @remix-run/cors-middleware.

  • Update remix/component and remix/component/server to re-export the latest @remix-run/component frame-navigation APIs.

    remix/component now exposes navigate(href, { src, target, history }), link(href, { src, target, history }), run({ loadModule, resolveFrame }), and the handle.frames.top and handle.frames.get(name) helpers, while remix/component/server re-exports the SSR frame source APIs including frameSrc, topFrameSrc, and ResolveFrameContext.

  • Add browser-origin and CSRF protection middleware APIs to remix.

    • remix/cop-middleware exposes cop(options) for browser-focused cross-origin protection using Sec-Fetch-Site with Origin fallback, trusted origins, and configurable bypasses.
    • remix/csrf-middleware exposes csrf(options) and getCsrfToken(context) for session-backed CSRF tokens plus origin validation.
    • Apps can use either middleware independently or layer cop(), session(), and csrf() together when they want both browser-origin filtering and token-backed protection.
  • Bumped @remix-run/* dependencies:

18 days ago
remix

method-override-middleware v0.1.5

Patch Changes

18 days ago
remix

session-middleware v0.2.0

Minor Changes

  • BREAKING CHANGE: Session middleware no longer reads/writes context.session.

    Session state is now stored on request context using the Session class itself as the context key and accessed with context.get(Session).

  • session() now contributes Session to fetch-router's typed request context, so apps deriving context from middleware can read context.get(Session) without manual type assertions.

Patch Changes

18 days ago
remix

multipart-parser v0.15.0

Minor Changes

  • BREAKING CHANGE: parseMultipart(), parseMultipartStream(), and parseMultipartRequest() now enforce finite default maxParts and maxTotalSize limits, and add MaxPartsExceededError and MaxTotalSizeExceededError for handling multipart envelope limit failures.

    Apps that intentionally accept large multipart requests may need to raise maxParts or maxTotalSize explicitly.

18 days ago
remix

static-middleware v0.4.5

Patch Changes

18 days ago
remix

logger-middleware v0.1.4

Patch Changes

18 days ago
remix

route-pattern v0.20.0

Minor Changes

  • BREAKING CHANGE: Make search param pattern decoding and serialization consistent with URLSearchParams. Affects: RoutePattern.{match,href,search,ast.search}

    Previously, RoutePattern treated ?q and ?q= as different constraints:

    // Before: `?q` and `?q=` are different
    
    let url = new URL('https://example.com?q')
    
    // Matches "key only" constraint?
    new RoutePattern('?q').match(url) // ✅ match
    
    // Matches "key and value" constraint?
    new RoutePattern('?q=').match(url) // ❌ no match (`null`)
    
    // Different constraints serialized to different strings
    new RoutePattern('?q').search // -> 'q'
    new RoutePattern('?q=').search // -> 'q='

    There were two main problems with that approach:

    Unintuitive matching

    // URL search looks like `?q=`
    let url = new URL('https://example.com?q=')
    
    // Pattern search looks like `?q=`
    let pattern = new RoutePattern('?q=')
    
    // But "key and value" constraint doesn't match!
    pattern.match(url) // ❌ no match (`null`)

    Parsing and serialization

    For consistency with URLSearchParams, search param patterns should be parsed according to the WHATWG application/x-www-form-urlencoded parsing spec and should also encode spaces as +.

    Now, we use URLSearchParams to parse search param patterns to guarantee decodings are consistent:

    let url = new URL('https://example.com?q=a+b')
    // Decodes `+` to ` `
    url.searchParams.getAll('q') // -> ['a b']
    
    // Before
    let pattern = new RoutePattern('?q=a+b')
    // Does not decode `+` to ` `
    pattern.ast.search.get('q') // -> ❌ Set { 'a+b' }
    
    // After
    let pattern = new RoutePattern('?q=a+b')
    // Decodes `+` to ` `
    pattern.ast.search.get('q') // -> ✅ Set { 'a b' }

    Similarly, now that ?q and ?q= are semantically equivalent, they should serialize to the same thing:

    new URLSearchParams('q=').toString() // 'q='
    
    // Before
    new RoutePattern('?q=').search // ❌ 'q'
    
    // After
    new RoutePattern('?q=').search // ✅ 'q='

    As a result, RoutePatterns can no longer represent a "key and any value" constraint. In practice, this was a niche use-case so we chose correctness and consistency with URLSearchParams. If the need for "key and any value" constraints arises, we can later introduce a separate syntax for that without the unintuitive shortcoming of ?q=.

    With "key and any value" constraints removed, the missing-search-param error type thrown by RoutePattern.href was made obsolete and was removed.

  • BREAKING CHANGE: RoutePattern.ast is now typed as deeply readonly.

    This was always the intended design; the type system now reflects it:

    // Before
    pattern.ast = { ...pattern.ast, protocol: 'https' }
    pattern.ast.protocol = 'https'
    pattern.ast.port = '443'
    pattern.ast.hostname = null
    pattern.ast.pathname = otherPattern.ast.pathname
    pattern.ast.search.set('q', new Set(['x']))
    pattern.ast.pathname.tokens.push({ type: 'text', text: 'x' })
    pattern.ast.pathname.optionals.set(0, 1)
    
    // After
    pattern.ast = { ...pattern.ast, protocol: 'https' }
    //      ~~~
    // Cannot assign to 'ast' because it is a read-only property. (2703)
    
    pattern.ast.protocol = 'https'
    //          ~~~~~~~~~
    // Cannot assign to 'protocol' because it is a read-only property. (2540)
    
    pattern.ast.port = '443'
    //          ~~~~
    // Cannot assign to 'port' because it is a read-only property. (2540)
    
    pattern.ast.hostname = null
    //          ~~~~~~~~
    // Cannot assign to 'hostname' because it is a read-only property. (2540)
    
    pattern.ast.pathname = otherPattern.ast.pathname
    //          ~~~~~~~~
    // Cannot assign to 'pathname' because it is a read-only property. (2540)
    
    pattern.ast.search.set('q', new Set(['x']))
    //                 ~~~
    // Property 'set' does not exist on type 'ReadonlyMap<string, ReadonlySet<string> | null>'. (2339)
    
    pattern.ast.pathname.tokens.push({ type: 'text', text: 'x' })
    //                          ~~~~
    // Property 'push' does not exist on type 'ReadonlyArray<PartPatternToken>'. (2339)
    
    pattern.ast.pathname.optionals.set(0, 1)
    //                             ~~~
    // Property 'set' does not exist on type 'ReadonlyMap<number, number>'. (2339)
  • Matches return decoded values for params in pathname

    let pattern = new RoutePattern('/posts/:slug')
    
    let url = new URL('https://blog.example.com/posts/💿')
    pattern.match(url)?.params.slug
    // Before -> '%F0%9F%92%BF'
    // After -> '💿'
    
    url = new URL('https://blog.example.com/posts/café-hà-nội')
    pattern.match(url)?.params.slug
    // Before -> 'caf%C3%A9-h%C3%A0-n%E1%BB%99i'
    // After -> 'café-hà-nội'
    
    url = new URL('https://blog.example.com/posts/北京')
    pattern.match(url)?.params.slug
    // Before -> '%E5%8C%97%E4%BA%AC'
    // After -> '北京'
    
    url = new URL('https://blog.example.com/posts/مرحبا')
    pattern.match(url)?.params.slug
    // Before -> '%D9%85%D8%B1%D8%AD%D8%A8%D8%A7'
    // After -> 'مرحبا'

    If you need percent-encoded text again, use encodeURI:

    let url = new URL('https://blog.example.com/posts/💿')
    let slug = pattern.match(url)!.params.slug
    // -> 💿
    
    encodeURI(slug)
    // -> '%F0%9F%92%BF'

Patch Changes

  • Faster TrieMatcher.match: O(m·log(m)) -> O(m)

    Previously, TrieMatcher.match internally called .matchAll, then sorted the result to find the best match. For m matching route patterns, this took O(m·log(m)) operations.

    Now, TrieMatcher.match loops over the m matches, keeping track of the best one, resulting in O(n) operations.

    In our benchmarks, this made our largest workload (~5000 route patterns) 17% faster with negligible or modest improvements to other workloads.

  • Faster type inference for RoutePattern.href, RoutePattern.match, and Params

    Reduced type instantiations for parsing param types, resulting in ~2-5x faster in relevant type benchmarks, but varies depending on your route patterns. May fix "Type instantiation is excessively deep and possibly infinite" (ts2589) for some apps.

18 days ago
remix

async-context-middleware v0.2.0

Minor Changes

  • getContext() can now be typed per app by augmenting AsyncContextTypes, which makes asyncContext() work cleanly with app-specific fetch-router request context contracts.

Patch Changes

18 days ago
remix

form-data-parser v0.16.0

Minor Changes

  • BREAKING CHANGE: parseFormData() now enforces finite default multipart maxParts and maxTotalSize limits and surfaces multipart limit failures directly instead of treating them as generic parse noise.

    Apps that intentionally accept large multipart submissions may need to raise these limits explicitly.

Patch Changes

18 days ago
remix

fetch-router v0.18.0

Minor Changes

  • BREAKING CHANGE: Action objects now use handler instead of action.

    This applies to the object form accepted by router.get(...), router.post(...), and router.map(...), and to BuildAction object definitions.

  • BREAKING CHANGE: Remove context.storage, context.session, context.sessionStarted, context.formData, and context.files from @remix-run/fetch-router, and rename createStorageKey(...) to createContextKey(...).

    RequestContext now provides request-scoped context methods directly (context.get(key), context.set(key, value), and context.has(key)), using keys created with createContextKey(...) or constructors like Session and FormData.

    Session middleware now stores the request session with context.set(Session, session), and form-data middleware now stores parsed form data with context.set(FormData, formData). Uploaded files are read from context.get(FormData) using get(...)/getAll(...).

    RequestContext is now generic over route params and typed context entries (RequestContext<{ id: string }, entries>), and no longer accepts a request-method generic (RequestContext<'GET', ...>).

  • BREAKING CHANGE: router.map() controllers for route maps now require a single shape: an object with an actions property and optional middleware.

    Migration: Wrap existing controller objects in actions. Nested route maps must also use nested controllers with { actions, middleware? }.

  • fetch-router now threads request context types through Router, Controller, and BuildAction, and exports helpers like MiddlewareContext, WithParams, MergeContext, and AnyParams so apps can derive context contracts from installed middleware.

Patch Changes

  • The Action/BuildAction object form accepted by router.get(...), router.post(...), and router.map(...) now uses { handler, middleware? }, so you can omit middleware entirely instead of writing middleware: [] when you do not need route middleware.

  • Bumped @remix-run/* dependencies: