1 hours ago
tldraw

v5.3.2

Generated from commits between v5.3.1 and HEAD

Bug Fixes

[HOTFIX] fix(editor): license fixes and product metadata (#10062)

  • Fixed default UI components (such as DefaultToolbar and DefaultQuickActionsContent) crashing when mounted outside the Tldraw component.

  • Fixed license validation treating native apps served from custom protocols as development environments.

  • Fixed wildcard license host matching so *.example.com matches only that domain's subdomains.

  • Added internal useMaybeLicenseManager(): the license manager from context, falling back to the nearest editor's, or null.

  • Added internal Editor.licenseManager, assigned by <TldrawEditor /> when it creates the editor.

  • Changed internal useLicenseFeatureFlag to accept LicenseManager | null and return false for null.

4 hours ago
officeParser

v7.7.0

v7.7.0: 🎯 Round-Trip Fidelity for Rich-Text Editors + Syntax-Typed Markdown Dialects

I am pleased to announce the release of officeParser v7.7.0! This release hardens the Markdown ↔ HTML round trip for the kind of rich content a ProseMirror/Tiptap-style editor produces. Nested lists, aligned tables, highlights, and link/image titles now survive a full md → HTML → md cycle. It also reworks the Markdown dialect configuration so every capability is named by the syntax it selects instead of a product flavor or a bare boolean.

The fidelity items are corrections: the only output that moves is content that was previously flattened, dropped, or emitted as invalid markup. The dialect rework is backward-compatible: existing boolean/flavor configs still work (they coerce to the new values), and are now deprecated.


✨ What's New

1. Dialect capabilities are typed by syntax, not flavor

Each mdConfig.dialect capability now names the syntax it selects: admonitions: 'blockquote' | 'fence' | 'fence-attribute', and strikethrough/definitionLists/footnotes/citations/wikilinks/attributeLists as '<marker>' | 'none' (e.g. strikethrough: 'tilde', wikilinks: 'double-bracket'). A shared convention is a single value, and a second syntax can be added later without a breaking change. A new highlight: 'equals' | 'none' capability joins them.

2. ==highlight== round-trips through Markdown

In dialects that define it (Obsidian/extended), a highlighted run emits as ==text== and ==text== parses back to a highlight. Other dialects keep the HTML <mark> fallback.

3. Link & image titles are preserved

[text](url "Title") and ![alt](img.png "Title") now keep their title in both directions. Previously an inline destination swallowed url "Title" as one URL.

4. fallbackToHtml.itemLineBreaks

A multi-paragraph list item joins onto its single Markdown line with <br> (default on), mirroring cellLineBreaks for table cells.


🔧 What's Fixed

1. Nested lists survive the round trip

An HTML <li> that wraps its text in <p> (the shape rich-text editors emit) exported as - a\n\n\n - a1, which reparsed flat. List items are now tight, a conservative parser pass rejoins a blank-line-split child, and generated HTML nests spec-validly (<li>a<ul>…</ul></li> instead of the invalid sibling shape), which also makes generated EPUB XHTML valid.

2. GFM table column alignment through HTML

:--- / :---: / ---: alignment now lives on each cell and is emitted as text-align on <th>/<td> (and read back), so per-column alignment survives md → HTML → md, the editor import path, instead of vanishing on the HTML hop.

3. HTML inline & block fidelity

Inline code emits <code> (not a font-family: monospace <span>); a single-line code block with a language stays a <pre><code> block; a plain <blockquote> round-trips to > quoted; an HTML <br> reads back as a hard line break rather than collapsing to a space; and an own-line $$…$$ parses as block math instead of leaking stray $.


⚠️ Deprecated

Boolean dialect toggles (strikethrough: true) and admonition flavor names (admonitions: 'github') still work. They coerce to the new syntax values (true becomes the marker, false becomes 'none'; flavors become 'blockquote'/'fence'/'fence-attribute'), but are deprecated and will be removed in the next major. Prefer the syntax names.


📝 Also

  • Fixed a cold-run npm test flake where a PDF-OCR parity timeout was killed at the 30s cap and misreported as a 0.0% similarity content mismatch. Timeouts now surface distinctly and the OCR run gets a 120s budget. Test tooling only. (#111)

🛠 Getting Started

npm install officeparser@7.7.0

🔗 Full Changelog: View v7.7.0 details 🔗 Documentation & Visualizer: officeparser.harshankur.com

9 hours ago
vuetify

v4.1.10

[!IMPORTANT] Vuetify Needs Your Support! The OpenCollective funds dried up. We are currently unable to compensate our contributors for their continued work on the framework and the ecosystem tools.

If Vuetify is part of your stack, please consider sponsoring the project so we can continue delivering updates and fixes.

Sponsor via Open Collective

Even if you look with optimism into the future (despite the sad economic reality), it is not responsible to keep your largest open source dependency in this poor financial state. Vuetify is on the borrowed time - if anything happens to maintainers primary employment, they are more likely to drop off.

Every contribution helps us keep Vuetify alive and signals to maintainers that their effort is appreciated.

Thank you.


🔧 Bug Fixes

  • VDataTable: key cells to prevent slot reuse (#23124) (a9528b5), closes #23123
  • VDatePicker: restore the hover preview for range selection (6c1dee2)
  • VDialog: avoid error when unmounted while closing (#23054) (8293f4c), closes #23053
  • VNumberInput: snap to min/max when using buttons and value is out of range (#23109) (a35ab72), closes #23098
  • VOverlay: reliably return focus to activator when eager (c879795)
  • VProgressLinear: restore left/right animation in split variant (537a5f7)
  • VProgressLinear: keep indeterminate bars visible in Firefox (694fcdb), closes #23125
  • VTreeviewItem: only attach click handler when interactive (#23118) (a4297d1), closes #23117

🔬 Fixes for audit tools

9 hours ago
vuetify

v3.13.2

🔧 Bug Fixes

9 hours ago
zip.js

v2.8.52

What's Changed in v2.8.52

New features

  • New getExportedSize() method on ZipDirectoryEntry and FS instances. It returns the exact size of the zip file the matching export*() call would write, without writing it. It takes the same options as the export*() methods, so the value it returns is the one the export produces. It is meant for the Content-Length header of a streamed download. It throws the new ERR_UNDETERMINED_SIZE error when the size cannot be known before writing, i.e. when an entry is compressed, when an entry has no known size, when signCentralDirectory is set, and when the bufferedWrite option lets the physical layout depend on the order in which the entries are written. Encryption does not prevent the prediction, its overhead is a fixed number of bytes
  • New onentryprogress option in the export*() methods of the filesystem API. It is called once per written entry with the number of entries written, the total number of entries, and the entry itself. It reports the entries whereas onprogress reports the bytes. It is called after the entry has been written. When bufferedWrite is enabled the entries are written concurrently, so it counts the entries written instead of giving the position of the entry in the zip file
  • New globalComment option in the export*() methods of the filesystem API. It sets the comment of the zip file. The options of these methods are applied to every entry, so setting comment there comments each entry instead of the archive, exactly as lastModDate there sets the date of each entry. The zip file comment therefore needed a name of its own
  • importZip() now accepts a ZipReader instance in addition to the data of a zip file. The caller builds the reader, passes it, and keeps it after the import. This is the way to read prependedData, appendedData, comment, digitalSignature, directoryOffset and directoryLength, which are only filled once the entries have been read. The options of the reader are merged with the options of the import, and the options of the import win
  • New symlink property on entries. It is true when the entry is a symbolic link. The target of the link is the content of the entry, which is read like any other entry, e.g. with getData(new TextWriter()). The target is not validated, it can be an absolute path or escape the directory of the entry, so it must be checked before being used. Writing a symbolic link is done by setting the unixMode option to a mode carrying the S_IFLNK type, e.g. 0o120777, with the path of the target as content
  • exportFileSystemHandle() now accepts the readerOptions option. The password option must be set there to export the entries of an encrypted zip file, since the password option of the export encrypts the written entries instead
  • The readerOptions option of the filesystem export now accepts passThrough. The entries imported from a zip file are then written as-is, without being decompressed and decrypted, exactly as importing them with this option does. The entries added to the filesystem are compressed as usual
  • The signCentralDirectory option is now declared in the export options of the filesystem API. It was already forwarded to the ZipWriter instance, it was simply missing from the TypeScript definitions
  • New ERR_UNDETERMINED_SIZE, ERR_INVALID_PASS_THROUGH and ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH error constants

Behavior changes

  • The export*() methods of the filesystem API now report the progress of the whole archive instead of the progress of each entry. onstart and onend are called once, with the total size of the entries and with the number of bytes written. They used to be called once per entry, while onprogress was already reporting the archive as a whole, so the three callbacks disagreed with each other. onprogress is unchanged. Use the new onentryprogress option to be notified for each entry
  • The export options of the filesystem API now take precedence over the metadata of the entries imported from a zip file. Setting lastModDate, comment, versionMadeBy, uid, gid or the file attributes in the export options used to have no effect on those entries, although unixMode and msdosAttributes did reach them, so the same attribute word was writable one way and not the other. The order is now the metadata of the source entry, then the export options, then the description of the entries copied as-is, then the options of the entry. The description of the entries copied as-is stays above the export options because it describes the bytes being copied, not an intent. A level or a compressionMethod winning over it would write headers that do not match the content. An export using the default options is unchanged, so round trips keep their fidelity
  • ZipReader#close() now cancels the ReadableStream instance passed to the constructor when nothing has been read from it. It used to do nothing at all. The stream of a reader whose entries have been read is left alone, and so is a Reader instance, which belongs to the caller. The entries stay readable after the call
  • The preventClose option is now honored only when the caller owns the writable, i.e. when a WritableWriter instance is passed to exportZip() or exportWritable(). It is ignored by the other export*() methods of the filesystem API, whose Writer instance can only return its data once its writable is closed. Setting it there used to prevent the export from ever resolving
  • Writing an entry with the passThrough option and a password now throws ERR_UNSUPPORTED_ENCRYPTION_PASS_THROUGH. The data is copied as-is, so it cannot be encrypted. The password used to be ignored silently and the entry was written unencrypted although its header announced encryption
  • The passThrough option is now ignored for the entries with no content. add("dir/", undefined, { directory: true, passThrough: true }) used to throw ERR_UNDEFINED_READER, so a ZipWriter instance created with passThrough set to true could not write a single directory
  • Writing an entry with the unixMode option now stamps the type of the file in the external file attributes. zip.js used to write a mode with no type, e.g. 0o000644 where Info-ZIP writes 0o100644, so unzip -l listed the entry as ?rw-r--r--. A mode already carrying a type is left untouched, and the externalFileAttributes option still writes the value verbatim
  • The executable property of an entry is now false for symbolic links. The permission bits of a link are always 0o777, so the flag was meaningless on every link

Bug fixes

  • Reading a range of data no longer emits empty chunks. Reader#createReadable() enqueued an empty chunk at the end of every entry whose size is known, and one extra read was issued for an entry of unknown size. The data was correct, the stream simply contained a chunk of length 0
  • getBlob() and getData64URI() now honor the MIME type they are given. A filesystem entry holding a Blob instance returned it as-is, with the MIME type it was created with, and ignored the requested one
  • The filesystem API now runs the overlapping entry check when checkOverlappingEntryOnly is set in its reader options. The option means "run the check and stop before reading the content" in the core API. The filesystem API needs the content, so it used to drop the option and skip the check altogether. It is now mapped to checkOverlappingEntry, so the check runs and the content is still read
  • The reader options that withhold the content of an entry are now ignored by the filesystem API. checkPasswordOnly, checkOverlappingEntryOnly and preventClose used to be forwarded to the entries read from a zip file, which returned no data
  • Progress is now reported for the filesystem entries that were not imported from a zip file. Reading such an entry pipes the reader to the writer without going through a codec, so onprogress never fired. It is now reported for every entry
  • exportFileSystemHandle() now counts the bytes it writes. It used to report the compressed size of each entry against the uncompressed size of the archive, so the progress stopped around 1% of the total, and it reported nothing at all for the entries that were not imported from a zip file
  • addFileSystemHandle() now applies the entry options to the directories it creates. They were applied to the files only
  • The dates of the Info-ZIP unix extra field are now read as signed timestamps. A date before 1970 was read as a date in 2106
  • Writing an entry whose name ends with a slash no longer corrupts the unixMode option. The type of the file was combined with the type already present in the mode instead of replacing it, so 0o100644 became a socket and 0o120777 became an invalid type. Both were then read back as neither a directory nor a symbolic link
  • The deprecated externalFileAttribute and internalFileAttribute options work again. They were removed when they were renamed to externalFileAttributes and internalFileAttributes. They are back as deprecated aliases, and an option passed to add() now takes precedence over the option of the ZipWriter instance whatever the spelling of each
  • addText() now stores the size of the text in bytes instead of its number of UTF-16 code units. Only getExportedSize() read that value, so the size was under-reported for any text containing non-ASCII characters. The export itself was correct
  • The buffer reserved for the expansion of the deflate algorithm is now allocated only for the entries that are really compressed. A stored entry reserved the same margin as a deflated one

Performance

  • The export*() methods of the filesystem API now keep a running total instead of summing the progress of every entry on every tick. The cost was quadratic in the number of entries. Passing onprogress on an archive of 10,000 entries added 50% to the duration of the export, and now adds nothing measurable

Documentation

  • The useCompressionStream option now states that the native API is used for compression only when level is undefined or equal to 6. CompressionStream does not support compression levels, so any other value compresses the data with the embedded implementation. It also states that the data produced at a given level can vary between platforms, and that useCompressionStream must be set to false to get the same output everywhere
  • The uid and gid options now state which unix extra field carries them
  • ZipReader#close() and the createTempStream option now describe what they do. A temporary stream must be able to hold a whole entry, because the local header written before it holds the size and the CRC-32 of the entry. Its readable side is therefore consumed only once its writable side has been closed. A factory returning new TransformStream() deadlocks, whereas the default buffers everything

Tests and continuous integration

  • The browser test runner uses Selenium instead of Playwright. It accepts --exe-path, --url-search, --build, --headful and --help, and the test-webkit script was renamed to test-safari
  • A workflow runs the test suite on every push, including the native build
  • A workflow checks that the markdown documentation is up to date with index.d.ts
  • The API documentation is published to the GitHub Pages site on each release
  • The web runner accepts a maxParallelTests parameter. Chromium 87 loses the wake-up of the backpressure of a stream when 16 tests run in parallel, so its jobs cap the parallelism at 4
  • The tests that check the abort reason are skipped on the browsers that ignore the signal of pipeTo() instead of being reported as failures

Credits

  • @danny0838 contributed the switch to Selenium, the test workflow and the arguments of the browser runner (#673)
  • Claude (Opus 5) contributed to every change listed above

Full Changelog: https://github.com/gildas-lormeau/zip.js/compare/v2.8.51...v2.8.52

10 hours ago
apexcharts.js

💎 Version 6.10.0

A feature release built around one question: what if the arrangement of a chart's marks were something you could hand it?

The unit chart already drew one dot per thing counted, and 6.9.0 opened a seam for supplying the positions yourself. This release ships the thing that seam was for. apexcharts/unit-shapes is a companion kit of 39 shapes a count can take: a heart, a house, a globe, a checkmark, a heartbeat trace, or the figure 1,024 drawn in 1,024 dots. Each one is a function of the marks and the plot rectangle rather than a picture, so the same shape serves 40 dots in a sparkline and 3,000 in a poster.

Alongside it: outer name labels for those shapes, so a crowd of dots reads without a legend, and a fix worth reading even if you never draw a heart, because it swallowed any update that changed only a callback.

✨ New

apexcharts/unit-shapes, 39 shapes a count can take

A new entry point, tree-shaken per shape. Importing one costs about 4 KB gzipped; importing the catalog is not something you need to do.

import ApexCharts from 'apexcharts'
import { heart } from 'apexcharts/unit-shapes'

new ApexCharts(el, {
  chart: { type: 'unit' },
  series: [57600, 16800, 4200, 3400],
  labels: ['Repeat donors', 'First-time', 'Workplace drives', 'Emergency call-ups'],
  plotOptions: {
    unit: { layout: 'custom', positions: heart, unitValue: 100 },
  },
}).render()

A shape is a plain callable, so positions: heart needs no registration step, and plotOptions.unit.positions already accepted a function: nothing in the chart had to learn about shapes.

The dots are packed, not stamped onto a template. Rows are cut across the outline, each row split into the spans that fall inside it, and the gap between dots is then bisected until the spans hold exactly the number of marks the data asks for. Density follows the shape's own area, which is why one outline covers three orders of magnitude of dot count. A thin limb, fin or tip keeps its single dot rather than dropping out, because that is what stops a shape dissolving as the count falls.

Three kinds, because things in the world are not all areas:

  • Silhouettes (29) fill an outline: heart, house, tree, leaf, flame, droplet, fish, sun, human, group, star, crown, trophy, moneybag, funnel, shield, gear, robot, bulb, flask, car, plane, rocket, battery, pin, mountain, cross, bolt, arrow.
  • Strokes (7) pack a thickened centreline, for a thing with no interior: check, wifi, pulse, xmark, percent, question, spiral. A dotted checkmark still reads as a checkmark, which is how a stroke degrades where a thin silhouette feature would simply vanish.
  • Generated (3) compute their positions from maths and have no outline at all: globe (latitude rings with a tilt), target (concentric bands), pyramid (tiers).

Every shape carries its metadata: which category it belongs to, how it was made, and minUnits, the count below which it stops being recognisable. Ask for fewer and the chart says so in a console warning naming the shape, rather than rendering mush.

Composition, rather than a bigger catalog

outlined(heart)                  // trace the outline instead of filling it
heart.with({ order: 'cols' })    // where each series band lands inside the shape
glyphs('1,024')                  // the number, drawn in that many dots
preview(heart, { series })       // -> an SVG string, no chart and no DOM

outlined() gives all 29 silhouettes a hollow twin for no new artwork, since a stroked closed path is a ring. The fill order is what decides where each category sits: rows bands a shape top to bottom, cols left to right (which is why battery fills like a charge meter), centerOut puts the first series at its heart. preview() renders a shape to standalone SVG, so docs galleries, README images and launch graphics can be generated at build time or on a server from the catalog alone.

Shapes are also registrable by name (ApexCharts.registerUnitLayout), and from a script tag dist/unit-shapes.js exposes the kit as ApexUnitShapes with every shape pre-registered.

On provenance: every outline in the kit was drawn in this repository. No third-party path is admitted, permissively licensed or not, because an outline ships verbatim inside the bundle: a copied path would make its licence notice travel into every consumer's build forever. Brand marks are excluded outright. A test enforces it.

Outer name labels for unit shapes

A shape packed with four categories used to need a legend, which asks the reader to match a swatch to a band. Names can now sit in the margin with a leader line to their own dots, the way a pie names its slices.

plotOptions: {
  unit: { clusterLabels: { external: { show: true } } },
}

The gutter is reserved on both sides before the dot size is chosen, so the shape is sized for the room it will actually get instead of being scaled down afterwards, and it stays centred. Each label anchors on a real dot of its own band, sides are assigned from how the bands are actually arranged, and crowded labels are spaced apart in one pass before they reach the DOM. The implementation is the one pie and donut already use, extracted rather than rewritten.

Samples

Twenty new unit demos, one per shape family, including a gallery that switches between all 39 shapes with the same dots flowing from one arrangement into the next. Palettes use separated hues rather than tints of one colour: at dot size a lightness ramp cannot be read back, and the palest end disappears against the card.

🐛 Fixes

An update that changes only a function no longer vanishes

update() skips a redundant render by comparing the incoming options with the previous ones, and that comparison went through JSON.stringify, which drops function values. Two configs differing only in a callback therefore serialised identically and the update was thrown away.

Any function-valued option was affected: a new dataLabels.formatter, a new custom tooltip, a new plotOptions.unit.positions. The first such update always worked, since there was nothing to compare against yet, which is what made it look like a rendering problem rather than an update problem.

Functions are now compared by identity: passing the same function twice still skips, so the optimisation keeps paying, while a different one gets the render it asked for. A caller who builds a fresh closure on every update now gets a render every time, which is the safe direction to err in, since the closure may capture new state.

Millisecond resolution survives a Date on a datetime axis

A data point whose x is a Date object had its milliseconds truncated, so points inside the same second collapsed onto each other. The type definitions also refused a Date there, despite it being the natural thing to pass. Thanks to @aron-intframe (#5277).

🔧 Internal

  • the unit and unit-shapes sub-entry artifacts are built and published, so apexcharts/unit and apexcharts/unit-shapes resolve for bundlers and script tags alike
  • shape geometry is covered by its own suite: containment against the outline it claims, spacing, the exact-count guarantee at every dot count, provenance, and a cross-check that the catalog, the exports and the type definitions can never disagree
  • two authoring tools ship with the repo rather than the package: a contact sheet that renders every shape at a given count, and a winding checker that catches a subpath which would punch a hole where it meant to fill

Full Changelog: https://github.com/apexcharts/apexcharts.js/compare/v6.9.0...v6.10.0

12 hours ago
next.js

v16.3.1-canary.22

Misc Changes

  • fix(turbo-persistence): allow 32-bit usize conversion: #97288
  • test: restore missing NFT exports* unit fixtures: #97459
  • [ci] Fix backport canary release dispatch: #97383
  • turbo-tasks-backend: Enforce that tasks exist when accessing them: #96043
  • turbo-tasks-backend: add persistence delete/tombstone plumbing for GC: #95975
  • turbo-persistence: add key-value tombstones for MultiValue families: #96929

Credits

Huge thanks to @hardfist, @mischnic, @ztanner, and @lukesandberg for helping!

13 hours ago
remix

remix v3.0.0-beta.10

Pre-release Changes

  • BREAKING CHANGE: Beta.10 supersedes beta.6 through beta.9 and consolidates their changes into one supported prerelease. Legacy package-aligned remix/* aliases have been removed in favor of canonical entrypoints that group related APIs together. Update imports when moving from beta.5 to beta.10:

    - import { requireAuth } from 'remix/auth-middleware'
    - import { createPostgresDatabase } from 'remix/data-table-postgres'
    - import { createRouter } from 'remix/fetch-router'
    + import { requireAuth } from 'remix/middleware/auth'
    + import { createPostgresDatabase } from 'remix/data-table/postgres'
    + import { createRouter } from 'remix/router'

    The same canonical structure applies to the other entrypoints: middleware lives under remix/middleware/*, database dialects under remix/data-table/*, storage adapters under remix/file-storage/* and remix/session-storage/*, and route definitions move from remix/fetch-router/routes to remix/routes.

  • BREAKING CHANGE: createAssetServer() from remix/assets now uses allowFiles and denyFiles instead of allow and deny for file path access rules:

     import { createAssetServer } from 'remix/assets'
    
     export const assetServer = createAssetServer({
    -  allow: ['app/routes.ts', 'app/**/public/**'],
    -  deny: ['app/**/*.test.*'],
    +  allowFiles: ['app/routes.ts', 'app/**/public/**'],
    +  denyFiles: ['app/**/*.test.*'],
     })
  • BREAKING CHANGE: remix/cookie now treats custom encode and decode functions as the complete cookie value codec. Custom encoded values are signed and serialized as-is instead of being wrapped in Remix's default base64 encoding. The default codec keeps the existing base64-safe representation.

  • BREAKING CHANGE: Route patterns now use delimiter-bounded params. Params stop at raw / and ., but not -, so a pattern such as /:year-:month must become one inseparable param such as /:date, or place the captures in separate path segments. Ambiguous adjacent captures are now rejected, and matching work is bounded to prevent pathological patterns (see #11651).

  • BREAKING CHANGE: Href helpers from remix/route-pattern/href and remix/routes now take search parameters in a searchParams options property:

    - const href = userRoute.href({ id: '123' }, { tab: 'settings' })
    + const href = userRoute.href(
    +   { id: '123' },
    +   { searchParams: { tab: 'settings' } },
    + )

    The options object also accepts a baseURL for generating path-relative same-origin hrefs. Route matching accepts the same option for relative URL strings, and searchParams accepts both typed objects and URLSearchParams.

  • BREAKING CHANGE: remix/middleware/session now makes session cookies HTTP-only when httpOnly is omitted. Set httpOnly: false explicitly if client-side JavaScript must read the cookie. Cookie.httpOnly from remix/cookie now returns boolean | undefined so omitted and explicitly disabled settings can be distinguished.

  • BREAKING CHANGE: Run tests with remix test instead of the removed remix-test executable, and move settings from remix-test.config.ts or .js into the test property of remix.json:

    - remix-test --type server --concurrency 1
    + remix test --type server --concurrency 1
    {
      "$schema": "https://remix.run/schemas/remix.json",
      "test": {
        "type": ["server"],
        "concurrency": 1,
        "quiet": true,
      },
    }

    The programmatic runRemixTest() API from remix/test/cli now accepts typed runner options instead of raw command-line arguments. The test runner also adds --only for matching suite or test names, --quiet for omitting skipped tests, and defaults NODE_ENV to test when it is not already set (see #11623, #11628).

  • BREAKING CHANGE: Browser frame resolvers now receive one options object instead of positional signal and target arguments:

    - async function resolveFrame(src, signal, target) {
    + async function resolveFrame(src, options) {
    +   const { signal, target } = options ?? {}
        // ...
      }
  • Add an allowPackages option to createAssetServer() from remix/assets. This allows a package and its dependencies to be served without listing every file the package may load:

    import { createAssetServer } from 'remix/assets'
    
    export const assetServer = createAssetServer({
      allowFiles: ['app/routes.ts', 'app/**/public/**'],
      allowPackages: ['remix'],
    })
  • Added a new remix db workflow for inspecting migration status, migrating, seeding, wiping, and resetting the current app database. Configure a built-in SQLite, PostgreSQL, or MySQL database in remix.json; for example:

    {
      "$schema": "https://remix.run/schemas/remix.json",
      "db": {
        "adapter": {
          "type": "sqlite",
          "filename": "./db/app.sqlite",
        },
        "migrations": {
          "directory": "./db/migrations",
        },
        "seed": "./db/seed.sql",
      },
    }

    The same configuration then powers the complete local database lifecycle:

    remix db status
    remix db migrate
    remix db seed
    remix db reset --force

    Command flags override configured values, database commands work from project subdirectories by finding the nearest remix.json, and destructive wipe and reset commands require --force (see #11608, #11639).

  • Added an optional JSONC remix.json file for shared remix db, remix test, and remix doctor settings. Relative paths and globs resolve from the configuration file, command-line options take precedence, and the global remix --config <path> option selects a different file (see #11628, #11638, #11639).

  • Added complete createPostgresDatabase(), createMysqlDatabase(), and createSqliteDatabase() factories through remix/data-table/postgres, remix/data-table/mysql, and remix/data-table/sqlite. These return the Database used for queries and lifecycle operations such as migrate(), migrationStatus(), reset(), wipe(), and close() (see #11608, #11639).

    BREAKING CHANGE: Replace the removed adapter and migration-runner APIs with a concrete database and its lifecycle methods:

    - import { createDatabase } from 'remix/data-table'
    - import { createPostgresDatabaseAdapter } from 'remix/data-table/postgres'
    + import { createPostgresDatabase } from 'remix/data-table/postgres'
    - const db = createDatabase(
    -   createPostgresDatabaseAdapter({ connectionString: process.env.DATABASE_URL }),
    - )
    + const db = createPostgresDatabase({
    +   connectionString: process.env.DATABASE_URL,
    + })
    - await migrationRunner.migrate()
    + await db.migrate(migrations)

    Applications should use a dialect factory; database integration packages can extend Database with a composed DatabaseDriver from remix/data-table.

  • Beta.10 includes an integrated full-stack hot module replacement workflow. Run npm run hmr in a newly generated project to reload server modules and update compatible UI components in place while preserving their state.

    New projects include the script and a complete hmr.ts runner. The core setup looks like this:

    {
      "scripts": {
        "hmr": "NODE_ENV=development node hmr.ts"
      }
    }
    // hmr.ts (excerpt)
    import { run } from 'remix/node-hmr'
    
    const hmrRunner = run('server.ts', {
      nodeArgs: ['--import', 'remix/node-tsx', '--import', 'remix/ui-hmr/node'],
      browserHmrChannel: { port: 44101 },
    })

    remix/node-hmr, remix/ui-hmr, and remix/assets coordinate server and browser updates through the standard import.meta.hot API. Browser-reachable source is scaffolded in colocated public/ directories so the server/browser boundary remains explicit.

  • Remix UI navigation now progressively enhances same-origin forms into frame navigations. Forms support rmx-target, rmx-document, and rmx-history="push|replace"; resolvers receive native submission data and may return a Response, including redirected responses whose final URL becomes the top frame's canonical location. The new rmx-preserve-dom attribute protects client-owned subtrees such as custom elements during reloads, and run() exposes app.frames for inspecting the current frame collection.

  • run() from remix/ui now fetches frame HTML by default, so apps no longer need a custom resolveFrame just to enable frame reloads and same-origin link and form navigation:

     let app = run({
       loadModule,
    -  resolveFrame: (src, options) => fetch(src, { signal: options?.signal }),
     })

    Keep a custom resolver when the app needs custom request headers, body encoding, response handling, or error UI. Add rmx-document to a link or form to leave that navigation to the browser (see #11693).

  • Allow resolveClientEntry() in remix/ui to return module preload hrefs so client entries can preload their browser module graphs.

  • This beta also improves runtime reliability across the stack. UI hydration, document navigation, and frame reloads no longer stall, lose newly rendered siblings, retain stale server-rendered content, or reload the wrong top-frame URL in the fixed edge cases. Styled components adapt to dark color schemes, and server-rendered CSS safely escapes values that could close a <style> element. Node handlers now receive native Request instances, response compression varies caches correctly by Accept-Encoding, proxied responses discard stale encoding and framing headers, and multipart parsing rejects overlong boundaries without stalling non-Node runtimes.

  • Fixed fresh Remix projects failing type checking when staticFiles() and other router middleware are used together. remix/middleware/static continues to use @remix-run/static-middleware, now with the same @remix-run/fetch-router dependency as remix/router, so package managers do not install incompatible RequestContext types.

  • Bumped @remix-run/* dependencies:

13 hours ago
remix

static-middleware v0.4.14

Patch Changes

  • Fixed staticFiles() middleware types being incompatible with other router middleware by updating @remix-run/fetch-router to ^0.21.0. This keeps the established @remix-run/static-middleware package compatible with Remix 3 projects without package-manager overrides.
14 hours ago
remix

ui v0.7.0

Minor Changes

  • run() now uses a default browser frame resolver when resolveFrame is omitted. Apps that supplied a resolver only to fetch frame HTML can remove it:

     let app = run({
       loadModule,
    -  resolveFrame(src, options) {
    -    return fetch(src, {
    -      headers: { Accept: 'text/html' },
    -      signal: options?.signal,
    -    })
    -  },
     })

    All run() calls now enable frame reloads and same-origin link and form navigation through the Navigation API. The default resolver submits the requested method, encoding, and form data, and rejects non-OK responses. Keep a custom resolveFrame when the app needs custom request headers, body encoding, response handling, or error UI. Add rmx-document to a link or form to leave that navigation to the browser (see #11693).