remix 3.0.0-beta.1 → 3.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headers/accept-encoding.d.ts +2 -0
- package/dist/headers/accept-encoding.d.ts.map +1 -0
- package/dist/headers/accept-encoding.js +2 -0
- package/dist/headers/accept-language.d.ts +2 -0
- package/dist/headers/accept-language.d.ts.map +1 -0
- package/dist/headers/accept-language.js +2 -0
- package/dist/headers/accept.d.ts +2 -0
- package/dist/headers/accept.d.ts.map +1 -0
- package/dist/headers/accept.js +2 -0
- package/dist/headers/cache-control.d.ts +2 -0
- package/dist/headers/cache-control.d.ts.map +1 -0
- package/dist/headers/cache-control.js +2 -0
- package/dist/headers/content-disposition.d.ts +2 -0
- package/dist/headers/content-disposition.d.ts.map +1 -0
- package/dist/headers/content-disposition.js +2 -0
- package/dist/headers/content-range.d.ts +2 -0
- package/dist/headers/content-range.d.ts.map +1 -0
- package/dist/headers/content-range.js +2 -0
- package/dist/headers/content-type.d.ts +2 -0
- package/dist/headers/content-type.d.ts.map +1 -0
- package/dist/headers/content-type.js +2 -0
- package/dist/headers/cookie.d.ts +2 -0
- package/dist/headers/cookie.d.ts.map +1 -0
- package/dist/headers/cookie.js +2 -0
- package/dist/headers/if-match.d.ts +2 -0
- package/dist/headers/if-match.d.ts.map +1 -0
- package/dist/headers/if-match.js +2 -0
- package/dist/headers/if-none-match.d.ts +2 -0
- package/dist/headers/if-none-match.d.ts.map +1 -0
- package/dist/headers/if-none-match.js +2 -0
- package/dist/headers/if-range.d.ts +2 -0
- package/dist/headers/if-range.d.ts.map +1 -0
- package/dist/headers/if-range.js +2 -0
- package/dist/headers/range.d.ts +2 -0
- package/dist/headers/range.d.ts.map +1 -0
- package/dist/headers/range.js +2 -0
- package/dist/headers/raw-headers.d.ts +2 -0
- package/dist/headers/raw-headers.d.ts.map +1 -0
- package/dist/headers/raw-headers.js +2 -0
- package/dist/headers/set-cookie.d.ts +2 -0
- package/dist/headers/set-cookie.d.ts.map +1 -0
- package/dist/headers/set-cookie.js +2 -0
- package/dist/headers/vary.d.ts +2 -0
- package/dist/headers/vary.d.ts.map +1 -0
- package/{src/node-serve.ts → dist/headers/vary.js} +1 -1
- package/package.json +102 -47
- package/src/csrf-middleware/README.md +5 -12
- package/src/data-schema/README.md +3 -9
- package/src/data-table/README.md +6 -14
- package/src/data-table-mysql/README.md +5 -11
- package/src/data-table-postgres/README.md +2 -4
- package/src/data-table-sqlite/README.md +2 -4
- package/src/fetch-proxy/README.md +1 -2
- package/src/file-storage/README.md +10 -4
- package/src/file-storage-s3/README.md +2 -3
- package/src/form-data-middleware/README.md +1 -2
- package/src/form-data-parser/README.md +7 -12
- package/src/headers/README.md +33 -1
- package/src/headers/accept-encoding.ts +2 -0
- package/src/headers/accept-language.ts +2 -0
- package/src/headers/accept.ts +2 -0
- package/src/headers/cache-control.ts +2 -0
- package/src/headers/content-disposition.ts +2 -0
- package/src/headers/content-range.ts +2 -0
- package/src/headers/content-type.ts +2 -0
- package/src/headers/cookie.ts +2 -0
- package/src/headers/if-match.ts +2 -0
- package/src/headers/if-none-match.ts +2 -0
- package/src/headers/if-range.ts +2 -0
- package/src/headers/range.ts +2 -0
- package/src/headers/raw-headers.ts +2 -0
- package/src/headers/set-cookie.ts +2 -0
- package/{dist/node-serve.js → src/headers/vary.ts} +1 -1
- package/src/node-fetch-server/README.md +1 -6
- package/src/node-tsx/README.md +3 -8
- package/src/route-pattern/README.md +18 -13
- package/src/session-storage-redis/README.md +1 -2
- package/src/test/README.md +2 -5
- package/src/ui/README.md +9 -7
- package/src/ui/anchor/README.md +11 -3
- package/src/ui/animation/README.md +7 -5
- package/src/ui/combobox/README.md +1 -1
- package/src/ui/menu/README.md +26 -2
- package/src/ui/server/README.md +4 -2
- package/dist/node-serve.d.ts +0 -2
- package/dist/node-serve.d.ts.map +0 -1
- package/src/node-serve/README.md +0 -253
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# file-storage-s3
|
|
2
2
|
|
|
3
|
-
S3 backend for [`remix/file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage).
|
|
4
|
-
Use this package when you want the `FileStorage` API backed by AWS S3 or an S3-compatible provider.
|
|
3
|
+
S3 backend for [`remix/file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage). Use this package when you want the `FileStorage` API backed by AWS S3 or an S3-compatible provider.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
8
7
|
- **S3-Compatible API** - Works with AWS S3 and S3-compatible APIs (e.g. MinIO, LocalStack)
|
|
9
|
-
- **Metadata Preservation** - Preserves `File` metadata (`name`, `type`, `lastModified`)
|
|
8
|
+
- **Metadata Preservation** - Preserves `File` metadata (`name`, `type`, `size`, `lastModified`)
|
|
10
9
|
- **Runtime-Agnostic Signing** - Uses [`aws4fetch`](https://github.com/mhart/aws4fetch) for SigV4 signing
|
|
11
10
|
|
|
12
11
|
## Installation
|
|
@@ -69,8 +69,7 @@ let router = createRouter({
|
|
|
69
69
|
|
|
70
70
|
### Limit Multipart Growth
|
|
71
71
|
|
|
72
|
-
`formData()` forwards multipart limit options to `parseFormData()`, so you can cap uploads with
|
|
73
|
-
`maxHeaderSize`, `maxFiles`, `maxFileSize`, `maxParts`, and `maxTotalSize`.
|
|
72
|
+
`formData()` forwards multipart limit options to `parseFormData()`, so you can cap uploads with `maxHeaderSize`, `maxFiles`, `maxFileSize`, `maxParts`, and `maxTotalSize`.
|
|
74
73
|
|
|
75
74
|
```ts
|
|
76
75
|
let router = createRouter({
|
|
@@ -72,8 +72,7 @@ async function requestHandler(request: Request) {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
To validate the resulting `FormData` object with `remix/data-schema`, use the
|
|
76
|
-
`remix/data-schema/form-data` helpers.
|
|
75
|
+
To validate the resulting `FormData` object with `remix/data-schema`, use the `remix/data-schema/form-data` helpers.
|
|
77
76
|
|
|
78
77
|
To limit the overall shape of multipart requests, use the `maxHeaderSize`, `maxFileSize`, `maxFiles`, `maxParts`, and `maxTotalSize` options. By default, `parseFormData()` uses `maxFiles = 20`, `maxParts = 1000`, and `maxTotalSize = maxFiles * maxFileSize + 1 MiB`.
|
|
79
78
|
|
|
@@ -118,15 +117,15 @@ try {
|
|
|
118
117
|
}
|
|
119
118
|
```
|
|
120
119
|
|
|
121
|
-
If you're looking for a more flexible storage solution for `
|
|
120
|
+
If you're looking for a more flexible storage solution for `FileUpload` objects, this library pairs really well with [the `file-storage` library](https://github.com/remix-run/remix/tree/main/packages/file-storage) for keeping files in various storage backends.
|
|
122
121
|
|
|
123
122
|
```ts
|
|
124
|
-
import {
|
|
123
|
+
import { createFsFileStorage } from 'remix/file-storage/fs'
|
|
125
124
|
import type { FileUpload } from 'remix/form-data-parser'
|
|
126
125
|
import { parseFormData } from 'remix/form-data-parser'
|
|
127
126
|
|
|
128
127
|
// Set up storage for uploaded files
|
|
129
|
-
const fileStorage =
|
|
128
|
+
const fileStorage = createFsFileStorage('/uploads/user-avatars')
|
|
130
129
|
|
|
131
130
|
// Define how to handle incoming file uploads
|
|
132
131
|
async function uploadHandler(fileUpload: FileUpload) {
|
|
@@ -134,11 +133,8 @@ async function uploadHandler(fileUpload: FileUpload) {
|
|
|
134
133
|
if (fileUpload.fieldName === 'user-avatar') {
|
|
135
134
|
let storageKey = `user-${user.id}-avatar`
|
|
136
135
|
|
|
137
|
-
// Put the file in storage
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// Return a lazy File object that can access the stored file when needed
|
|
141
|
-
return fileStorage.get(storageKey)
|
|
136
|
+
// Put the file in storage and return the stored LazyFile
|
|
137
|
+
return fileStorage.put(storageKey, fileUpload)
|
|
142
138
|
}
|
|
143
139
|
|
|
144
140
|
// Ignore unrecognized fields
|
|
@@ -153,8 +149,7 @@ The [`demos` directory](https://github.com/remix-run/remix/tree/main/packages/fo
|
|
|
153
149
|
|
|
154
150
|
## Related Packages
|
|
155
151
|
|
|
156
|
-
- [`data-schema`](https://github.com/remix-run/remix/tree/main/packages/data-schema) - Tiny,
|
|
157
|
-
standards-aligned validation with a `form-data` export for `FormData` and `URLSearchParams`
|
|
152
|
+
- [`data-schema`](https://github.com/remix-run/remix/tree/main/packages/data-schema) - Tiny, standards-aligned validation with a `form-data` export for `FormData` and `URLSearchParams`
|
|
158
153
|
- [`file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage) - A simple key/value interface for storing `FileUpload` objects you get from the parser
|
|
159
154
|
- [`multipart-parser`](https://github.com/remix-run/remix/tree/main/packages/multipart-parser) - The parser used internally for parsing `multipart/form-data` HTTP messages
|
|
160
155
|
|
package/src/headers/README.md
CHANGED
|
@@ -53,6 +53,26 @@ headers.get('Content-Type') // no typed parse needed
|
|
|
53
53
|
headers.contentType.mediaType // parses Content-Type lazily
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
Use `apply()` to apply a `SuperHeadersInit` value to an existing instance with header-aware semantics:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
let headers = new Headers({
|
|
60
|
+
contentType: 'text/html',
|
|
61
|
+
setCookie: { name: 'session', value: 'abc' },
|
|
62
|
+
vary: 'Accept-Encoding',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
headers.apply({
|
|
66
|
+
contentType: 'application/json',
|
|
67
|
+
setCookie: { name: 'theme', value: 'dark' },
|
|
68
|
+
vary: ['Accept-Encoding', 'Accept-Language'],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
headers.get('Content-Type') // 'application/json'
|
|
72
|
+
headers.get('Vary') // 'accept-encoding, accept-language'
|
|
73
|
+
headers.getSetCookie() // ['session=abc', 'theme=dark']
|
|
74
|
+
```
|
|
75
|
+
|
|
56
76
|
## Individual Header Utilities
|
|
57
77
|
|
|
58
78
|
Each supported header has a class that represents the header value. Use the static `from()` method to parse header values. Each class has a `toString()` method that returns the header value as a string, which you can either call manually, or will be called automatically when the header class is used in a context that expects a string.
|
|
@@ -74,6 +94,16 @@ The following headers are currently supported:
|
|
|
74
94
|
- [Set-Cookie](./README.md#set-cookie)
|
|
75
95
|
- [Vary](./README.md#vary)
|
|
76
96
|
|
|
97
|
+
If you only need a specific header parser (for example, just `Content-Type`), import that parser directly from its subpath. This avoids pulling the package barrel and `SuperHeaders`:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { ContentType } from 'remix/headers/content-type'
|
|
101
|
+
import { SetCookie } from 'remix/headers/set-cookie'
|
|
102
|
+
|
|
103
|
+
let contentType = ContentType.from('text/plain; charset=utf-8')
|
|
104
|
+
let setCookie = new SetCookie('session=abc; Path=/')
|
|
105
|
+
```
|
|
106
|
+
|
|
77
107
|
### Accept
|
|
78
108
|
|
|
79
109
|
Parse, manipulate and stringify [`Accept` headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept).
|
|
@@ -325,7 +355,7 @@ headers.set('Content-Type', new ContentType({ mediaType: 'text/html', charset: '
|
|
|
325
355
|
|
|
326
356
|
Parse, manipulate and stringify [`Cookie` headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie).
|
|
327
357
|
|
|
328
|
-
Implements
|
|
358
|
+
Implements an ordered list of name/value pairs. Duplicate cookie names are preserved, such as when cookies with the same name were set for different paths.
|
|
329
359
|
|
|
330
360
|
```ts
|
|
331
361
|
import { Cookie } from 'remix/headers'
|
|
@@ -334,6 +364,7 @@ import { Cookie } from 'remix/headers'
|
|
|
334
364
|
let cookie = Cookie.from(request.headers.get('Cookie'))
|
|
335
365
|
|
|
336
366
|
cookie.get('session_id') // 'abc123'
|
|
367
|
+
cookie.getAll('session_id') // ['abc123']
|
|
337
368
|
cookie.get('theme') // 'dark'
|
|
338
369
|
cookie.has('session_id') // true
|
|
339
370
|
cookie.size // 2
|
|
@@ -345,6 +376,7 @@ for (let [name, value] of cookie) {
|
|
|
345
376
|
|
|
346
377
|
// Modify and set header
|
|
347
378
|
cookie.set('theme', 'light')
|
|
379
|
+
cookie.append('session_id', 'def456')
|
|
348
380
|
cookie.delete('session_id')
|
|
349
381
|
headers.set('Cookie', cookie)
|
|
350
382
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// IMPORTANT: This file is auto-generated, please do not edit manually.
|
|
2
|
-
export * from '@remix-run/
|
|
2
|
+
export * from '@remix-run/headers/vary'
|
|
@@ -124,8 +124,7 @@ async function handler(request: Request) {
|
|
|
124
124
|
|
|
125
125
|
### Custom Hostname Configuration
|
|
126
126
|
|
|
127
|
-
Configure custom hostnames for deployment on VPS or custom environments. `node-fetch-server` uses
|
|
128
|
-
the `host` option when constructing `request.url`.
|
|
127
|
+
Configure custom hostnames for deployment on VPS or custom environments. `node-fetch-server` uses the `host` option when constructing `request.url`.
|
|
129
128
|
|
|
130
129
|
```ts
|
|
131
130
|
import * as http from 'node:http'
|
|
@@ -291,7 +290,6 @@ The [`demos` directory](https://github.com/remix-run/remix/tree/main/packages/no
|
|
|
291
290
|
|
|
292
291
|
## Related Packages
|
|
293
292
|
|
|
294
|
-
- [`node-serve`](https://github.com/remix-run/remix/tree/main/packages/node-serve) - Build high-performance Fetch API servers for Node.js
|
|
295
293
|
- [`fetch-proxy`](https://github.com/remix-run/remix/tree/main/packages/fetch-proxy) - Build HTTP proxy servers using the web fetch API
|
|
296
294
|
|
|
297
295
|
## Benchmarks
|
|
@@ -322,7 +320,6 @@ Simple HTML response benchmarks without inspecting the incoming request.
|
|
|
322
320
|
|
|
323
321
|
| Server | Version | Requests/sec | Avg latency | Transfer/sec |
|
|
324
322
|
| ------------------------- | --------: | -----------: | ----------: | -----------: |
|
|
325
|
-
| `remix/node-serve` | `0.0.0` | `62,225` | `6.45ms` | `9.85MB` |
|
|
326
323
|
| `node:http` | `24.15.0` | `47,110` | `10.66ms` | `9.66MB` |
|
|
327
324
|
| `remix/node-fetch-server` | `0.13.0` | `43,317` | `11.69ms` | `8.80MB` |
|
|
328
325
|
| `express` | `5.2.1` | `39,752` | `13.69ms` | `9.59MB` |
|
|
@@ -333,7 +330,6 @@ POST benchmarks that read and print the request method, headers, and a small bod
|
|
|
333
330
|
|
|
334
331
|
| Server | Version | Requests/sec | Avg latency | Transfer/sec |
|
|
335
332
|
| ------------------------- | --------: | -----------: | ----------: | -----------: |
|
|
336
|
-
| `remix/node-serve` | `0.0.0` | `31,213` | `12.75ms` | `4.94MB` |
|
|
337
333
|
| `remix/node-fetch-server` | `0.13.0` | `25,430` | `24.25ms` | `5.17MB` |
|
|
338
334
|
| `node:http` | `24.15.0` | `25,088` | `23.89ms` | `5.14MB` |
|
|
339
335
|
| `express` | `5.2.1` | `22,845` | `27.16ms` | `5.51MB` |
|
|
@@ -344,7 +340,6 @@ POST benchmarks that read and print the request method, headers, and a 1 MB body
|
|
|
344
340
|
|
|
345
341
|
| Server | Version | Requests/sec | Avg latency | Transfer/sec |
|
|
346
342
|
| ------------------------- | --------: | -----------: | ----------: | -----------: |
|
|
347
|
-
| `remix/node-serve` | `0.0.0` | `1,148` | `327.72ms` | `186.03KB` |
|
|
348
343
|
| `remix/node-fetch-server` | `0.13.0` | `1,086` | `217.69ms` | `225.87KB` |
|
|
349
344
|
| `node:http` | `24.15.0` | `1,079` | `198.67ms` | `226.54KB` |
|
|
350
345
|
| `express` | `5.2.1` | `1,022` | `216.07ms` | `252.51KB` |
|
package/src/node-tsx/README.md
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Run Node.js with TypeScript and JSX syntax support in `.ts`, `.tsx`, and `.jsx` files.
|
|
4
4
|
|
|
5
|
-
`node-tsx` transforms supported source files before Node.js executes them, including
|
|
6
|
-
TypeScript syntax that requires JavaScript code generation such as enums, runtime
|
|
7
|
-
namespaces, and parameter properties. JSX compiler options are read from the nearest
|
|
8
|
-
`tsconfig.json` for each loaded file.
|
|
5
|
+
`node-tsx` transforms supported source files before Node.js executes them, including TypeScript syntax that requires JavaScript code generation such as enums, runtime namespaces, and parameter properties. JSX compiler options are read from the nearest `tsconfig.json` for each loaded file.
|
|
9
6
|
|
|
10
|
-
The loader does not type check, change Node.js module resolution, apply TypeScript
|
|
11
|
-
path aliases, or downlevel JavaScript syntax for older runtimes.
|
|
7
|
+
The loader does not type check, change Node.js module resolution, apply TypeScript path aliases, or downlevel JavaScript syntax for older runtimes.
|
|
12
8
|
|
|
13
9
|
## Installation
|
|
14
10
|
|
|
@@ -47,8 +43,7 @@ Since import resolution still follows Node.js, configure type checking to match
|
|
|
47
43
|
- [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax) requires type-only imports and exports to be marked so runtime imports are unambiguous.
|
|
48
44
|
- [`rewriteRelativeImportExtensions`](https://www.typescriptlang.org/tsconfig/#rewriteRelativeImportExtensions) preserves a `tsc` emit path by rewriting relative `.ts` and `.tsx` imports to JavaScript extensions.
|
|
49
45
|
|
|
50
|
-
Do not enable `erasableSyntaxOnly` if you want TypeScript to accept the same
|
|
51
|
-
transform-only syntax that `node-tsx` can execute.
|
|
46
|
+
Do not enable `erasableSyntaxOnly` if you want TypeScript to accept the same transform-only syntax that `node-tsx` can execute.
|
|
52
47
|
|
|
53
48
|
### Programmatic usage
|
|
54
49
|
|
|
@@ -55,15 +55,13 @@ createHref('http(s)://:region.cdn.com/assets/*file.:ext', {
|
|
|
55
55
|
|
|
56
56
|
## API at a glance
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
**remix/route-pattern/specificity** - Rank matches by [specificity](#ranking-matches-by-specificity).
|
|
58
|
+
| Import | Description |
|
|
59
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `remix/route-pattern` | Parse and stringify patterns. |
|
|
61
|
+
| `remix/route-pattern/href` | Generate hrefs for patterns with type safe params. |
|
|
62
|
+
| `remix/route-pattern/match` | Match against one pattern with type inference for params, or match against many patterns with deterministic ranking and attached data. |
|
|
63
|
+
| `remix/route-pattern/join` | Combine two patterns into one. Override protocol, hostname, port. Join pathnames. Merge search constraints. |
|
|
64
|
+
| `remix/route-pattern/specificity` | Rank matches by [specificity](#ranking-matches-by-specificity). |
|
|
67
65
|
|
|
68
66
|
For in-depth reference, visit the [`route-pattern` API docs](https://api.remix.run/api/remix/route-pattern)
|
|
69
67
|
|
|
@@ -113,6 +111,15 @@ While variables, wilcards, and optionals are most prevalent in pathnames, you ca
|
|
|
113
111
|
'(:locale.)example.com/docs(/:section)' // matches en.example.com/docs, en.example.com/docs/guides
|
|
114
112
|
```
|
|
115
113
|
|
|
114
|
+
**Escape characters** with `\`:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
'time/12\\:30' // matches /time/12:30
|
|
118
|
+
'calculator/2\\*3' // matches /calculator/2*3
|
|
119
|
+
'wiki/Mercury_\\(planet\\)' // matches /wiki/Mercury_(planet)
|
|
120
|
+
'wiki/AC\\/DC' // matches /wiki/AC%2FDC
|
|
121
|
+
```
|
|
122
|
+
|
|
116
123
|
### Search
|
|
117
124
|
|
|
118
125
|
**Search constraints** narrow matches using `?key` or `?key=value`:
|
|
@@ -175,8 +182,7 @@ When multiple patterns match the same URL, `route-pattern` chooses the most spec
|
|
|
175
182
|
|
|
176
183
|
This is the same ranking used by `createMultiMatcher`.
|
|
177
184
|
|
|
178
|
-
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`.
|
|
179
|
-
For example:
|
|
185
|
+
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`. For example:
|
|
180
186
|
|
|
181
187
|
```ts
|
|
182
188
|
import { createMultiMatcher } from 'remix/route-pattern/match'
|
|
@@ -195,8 +201,7 @@ matches.sort(descending).map((match) => match.pattern.toString())
|
|
|
195
201
|
|
|
196
202
|
## Generate hrefs
|
|
197
203
|
|
|
198
|
-
`createHref` turns a pattern and params into a URL string.
|
|
199
|
-
Required variables and wildcards must be provided, while params inside optional groups may be omitted.
|
|
204
|
+
`createHref` turns a pattern and params into a URL string. Required variables and wildcards must be provided, while params inside optional groups may be omitted.
|
|
200
205
|
|
|
201
206
|
```ts
|
|
202
207
|
import { createHref } from 'remix/route-pattern/href'
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# session-storage-redis
|
|
2
2
|
|
|
3
|
-
Redis-backed session storage for [`remix/session`](https://github.com/remix-run/remix/tree/main/packages/session).
|
|
4
|
-
Use this package when app servers need to share session state through Redis.
|
|
3
|
+
Redis-backed session storage for [`remix/session`](https://github.com/remix-run/remix/tree/main/packages/session). Use this package when app servers need to share session state through Redis.
|
|
5
4
|
|
|
6
5
|
## Installation
|
|
7
6
|
|
package/src/test/README.md
CHANGED
|
@@ -209,8 +209,7 @@ suite('My Test Suite', () => {
|
|
|
209
209
|
|
|
210
210
|
### Programmatic runner
|
|
211
211
|
|
|
212
|
-
`remix/test/cli` exports `runRemixTest()` for tools that want to run the test runner without
|
|
213
|
-
exiting the current process:
|
|
212
|
+
`remix/test/cli` exports `runRemixTest()` for tools that want to run the test runner without exiting the current process:
|
|
214
213
|
|
|
215
214
|
```ts
|
|
216
215
|
import { runRemixTest } from 'remix/test/cli'
|
|
@@ -221,9 +220,7 @@ let exitCode = await runRemixTest({
|
|
|
221
220
|
})
|
|
222
221
|
```
|
|
223
222
|
|
|
224
|
-
`runRemixTest()` returns the runner exit code. The `remix test` bin wrapper calls
|
|
225
|
-
`process.exit()` with that code when the run finishes so open workers, browsers, or project handles
|
|
226
|
-
cannot keep the CLI alive.
|
|
223
|
+
`runRemixTest()` returns the runner exit code. The `remix test` bin wrapper calls `process.exit()` with that code when the run finishes so open workers, browsers, or project handles cannot keep the CLI alive.
|
|
227
224
|
|
|
228
225
|
### Test Context
|
|
229
226
|
|
package/src/ui/README.md
CHANGED
|
@@ -135,13 +135,15 @@ let Theme = createTheme({
|
|
|
135
135
|
Render the theme once near the top of your document:
|
|
136
136
|
|
|
137
137
|
```tsx
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
import type { Handle, RemixNode } from 'remix/ui'
|
|
139
|
+
|
|
140
|
+
function Layout(handle: Handle<{ children: RemixNode }>) {
|
|
141
|
+
return () => (
|
|
140
142
|
<html>
|
|
141
143
|
<head>
|
|
142
144
|
<Theme />
|
|
143
145
|
</head>
|
|
144
|
-
<body>{props.children}</body>
|
|
146
|
+
<body>{handle.props.children}</body>
|
|
145
147
|
</html>
|
|
146
148
|
)
|
|
147
149
|
}
|
|
@@ -168,13 +170,13 @@ let card = css({
|
|
|
168
170
|
Render shared glyphs separately from the theme styles:
|
|
169
171
|
|
|
170
172
|
```tsx
|
|
171
|
-
import type { RemixNode } from 'remix/ui'
|
|
173
|
+
import type { Handle, RemixNode } from 'remix/ui'
|
|
172
174
|
import { Button } from 'remix/ui/button'
|
|
173
175
|
import { Glyph } from 'remix/ui/glyph'
|
|
174
176
|
import { RMX_01, RMX_01_GLYPHS } from 'remix/ui/theme'
|
|
175
177
|
|
|
176
|
-
function Layout(
|
|
177
|
-
return (
|
|
178
|
+
function Layout(handle: Handle<{ children: RemixNode }>) {
|
|
179
|
+
return () => (
|
|
178
180
|
<html>
|
|
179
181
|
<head>
|
|
180
182
|
<RMX_01 />
|
|
@@ -184,7 +186,7 @@ function Layout(props: { children: RemixNode }) {
|
|
|
184
186
|
<Button startIcon={<Glyph name="add" />} tone="primary">
|
|
185
187
|
New project
|
|
186
188
|
</Button>
|
|
187
|
-
{props.children}
|
|
189
|
+
{handle.props.children}
|
|
188
190
|
</body>
|
|
189
191
|
</html>
|
|
190
192
|
)
|
package/src/ui/anchor/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# anchor
|
|
2
2
|
|
|
3
|
-
`anchor` positions a floating element against an anchor element and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
|
|
3
|
+
`anchor` positions a floating element against an anchor element or viewport coordinates and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -49,11 +49,19 @@ popover.addEventListener('beforetoggle', (event) => {
|
|
|
49
49
|
})
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
Anchor to coordinates when the surface should open at a pointer location.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
let cleanup = anchor(popover, { x: event.clientX, y: event.clientY }, { placement: 'bottom-start' })
|
|
56
|
+
```
|
|
57
|
+
|
|
52
58
|
## `anchor.*`
|
|
53
59
|
|
|
54
|
-
- `anchor(floatingElement,
|
|
60
|
+
- `anchor(floatingElement, anchorTarget, options)`: positions `floatingElement` against an element or coordinate target, starts animation-frame polling for geometry changes, and returns a cleanup function.
|
|
55
61
|
- `AnchorOptions`: placement, inset, relative alignment, and offset options.
|
|
62
|
+
- `AnchorPoint`: viewport coordinate target with `x`, `y`, and optional `width`/`height`.
|
|
56
63
|
- `AnchorPlacement`: exported placement names for the main sides and top/bottom start/end alignment.
|
|
64
|
+
- `AnchorTarget`: an `HTMLElement` or `AnchorPoint`.
|
|
57
65
|
|
|
58
66
|
## Placements
|
|
59
67
|
|
|
@@ -149,5 +157,5 @@ anchor(listbox, trigger, {
|
|
|
149
157
|
- Oversized inset surfaces with `relativeTo` preserve alignment by scrolling the nearest scrollable descendant when possible.
|
|
150
158
|
- `offset`, `offsetX`, and `offsetY` may be numbers or functions that receive the floating element.
|
|
151
159
|
- `relativeTo` lets a surface align to an inner element, which is useful for selected options inside popovers.
|
|
152
|
-
- `anchor` polls on animation frames for anchor or floating geometry changes and repositions when either changes.
|
|
160
|
+
- `anchor` polls on animation frames for anchor target or floating geometry changes and repositions when either changes.
|
|
153
161
|
- The returned cleanup function cancels animation-frame polling.
|
|
@@ -52,12 +52,13 @@ function Toast() {
|
|
|
52
52
|
`animateExit` keeps a removed keyed element in the DOM long enough to animate from its natural styles to the provided keyframe.
|
|
53
53
|
|
|
54
54
|
```tsx
|
|
55
|
+
import type { Handle } from 'remix/ui'
|
|
55
56
|
import { animateExit } from 'remix/ui/animation'
|
|
56
57
|
|
|
57
|
-
function Item(
|
|
58
|
+
function Item(handle: Handle<{ id: string; label: string }>) {
|
|
58
59
|
return () => (
|
|
59
60
|
<li
|
|
60
|
-
key={id}
|
|
61
|
+
key={handle.props.id}
|
|
61
62
|
mix={[
|
|
62
63
|
animateExit({
|
|
63
64
|
opacity: 0,
|
|
@@ -67,7 +68,7 @@ function Item({ id, label }: { id: string; label: string }) {
|
|
|
67
68
|
}),
|
|
68
69
|
]}
|
|
69
70
|
>
|
|
70
|
-
{label}
|
|
71
|
+
{handle.props.label}
|
|
71
72
|
</li>
|
|
72
73
|
)
|
|
73
74
|
}
|
|
@@ -105,12 +106,13 @@ Exit animations can reclaim a removed keyed node if the same keyed element is re
|
|
|
105
106
|
`animateLayout` animates layout changes with a FLIP-style transform projection. Use it on elements whose position or size can change between renders.
|
|
106
107
|
|
|
107
108
|
```tsx
|
|
109
|
+
import type { Handle } from 'remix/ui'
|
|
108
110
|
import { animateLayout, spring } from 'remix/ui/animation'
|
|
109
111
|
|
|
110
|
-
function Card(
|
|
112
|
+
function Card(handle: Handle<{ expanded: boolean }>) {
|
|
111
113
|
return () => (
|
|
112
114
|
<section
|
|
113
|
-
class={expanded ? 'card expanded' : 'card'}
|
|
115
|
+
class={handle.props.expanded ? 'card expanded' : 'card'}
|
|
114
116
|
mix={[
|
|
115
117
|
animateLayout({
|
|
116
118
|
...spring('smooth'),
|
|
@@ -8,7 +8,7 @@ Use it when the user should type draft text, filter a popup list, and still comm
|
|
|
8
8
|
|
|
9
9
|
```tsx
|
|
10
10
|
import { css, type Handle } from 'remix/ui'
|
|
11
|
-
import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui'
|
|
11
|
+
import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui/combobox'
|
|
12
12
|
|
|
13
13
|
let airports = [
|
|
14
14
|
{
|
package/src/ui/menu/README.md
CHANGED
|
@@ -68,6 +68,29 @@ Use `menuLabel` when the menu surface needs a different accessible label from th
|
|
|
68
68
|
</Menu>
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
Use `menu.contextTrigger()` with `menu.Context` and `MenuList` when a menu should open at the right-click location of an element.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import * as menu from 'remix/ui/menu'
|
|
75
|
+
import { MenuItem, MenuList } from 'remix/ui/menu'
|
|
76
|
+
|
|
77
|
+
export function FileContextMenu(handle: Handle) {
|
|
78
|
+
return () => (
|
|
79
|
+
<menu.Context label="File actions">
|
|
80
|
+
<div mix={menu.contextTrigger()} tabIndex={0}>
|
|
81
|
+
File.txt
|
|
82
|
+
</div>
|
|
83
|
+
<MenuList>
|
|
84
|
+
<MenuItem name="rename">Rename</MenuItem>
|
|
85
|
+
<MenuItem name="delete">Delete</MenuItem>
|
|
86
|
+
</MenuList>
|
|
87
|
+
</menu.Context>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Attach `onMenuSelect(...)` to `MenuList` or a shared ancestor when using lower-level context menu composition.
|
|
93
|
+
|
|
71
94
|
## `menu.*`
|
|
72
95
|
|
|
73
96
|
- `Menu`: composed trigger, popover, and list component for the common menu case.
|
|
@@ -77,13 +100,14 @@ Use `menuLabel` when the menu surface needs a different accessible label from th
|
|
|
77
100
|
- `onMenuSelect(...)`: event mixin for the bubbling `MenuSelectEvent`.
|
|
78
101
|
- `MenuSelectEvent`: bubbling event class whose `item` describes the selected item.
|
|
79
102
|
- `MenuSelectItem`: selected item shape with `checked`, `id`, `label`, `name`, `type`, and `value`.
|
|
80
|
-
- `menu.Context`, `menu.trigger()`, `menu.popover()`, `menu.list()`, `menu.item(...)`, and `menu.submenuTrigger(...)`: lower-level composition primitives.
|
|
103
|
+
- `menu.Context`, `menu.trigger()`, `menu.contextTrigger()`, `menu.popover()`, `menu.list()`, `menu.item(...)`, and `menu.submenuTrigger(...)`: lower-level composition primitives.
|
|
81
104
|
- `buttonStyle`, `popoverStyle`, `listStyle`, `itemStyle`, `itemSlotStyle`, `itemLabelStyle`, `itemGlyphStyle`, and `triggerGlyphStyle`: flat style mixins used by the wrappers.
|
|
82
|
-
- `MenuProps`, `MenuItemProps`, `MenuListProps`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuItemOptions`, and `SubmenuProps`: public TypeScript props and option types.
|
|
105
|
+
- `MenuProps`, `MenuItemProps`, `MenuListProps`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuContextTriggerOptions`, `MenuItemOptions`, and `SubmenuProps`: public TypeScript props and option types.
|
|
83
106
|
|
|
84
107
|
## Behavior Notes
|
|
85
108
|
|
|
86
109
|
- Click opens the root menu and focuses the list; clicking the trigger again closes it and restores focus.
|
|
110
|
+
- `menu.contextTrigger()` opens the root menu from a `contextmenu` event at the pointer coordinates and supports keyboard opening with the Context Menu key or Shift+F10.
|
|
87
111
|
- `ArrowDown` opens from the trigger at the first enabled item. `ArrowUp` opens at the last enabled item. Enter and Space open the menu with focus on the list.
|
|
88
112
|
- Keyboard navigation skips disabled items and does not wrap past the first or last enabled item.
|
|
89
113
|
- `Home` and `End` move to the first and last enabled item. Enter and Space activate the highlighted item.
|
package/src/ui/server/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Server
|
|
2
2
|
|
|
3
|
-
Remix
|
|
3
|
+
Remix UI can render to HTML on the server using two APIs:
|
|
4
4
|
|
|
5
5
|
- `renderToString` - Returns a complete HTML string. Simple, but buffers the entire response.
|
|
6
6
|
- `renderToStream` - Returns a `ReadableStream<Uint8Array>`. Sends the initial HTML immediately and streams frame content as it resolves.
|
|
@@ -26,6 +26,7 @@ import { renderToStream } from 'remix/ui/server'
|
|
|
26
26
|
|
|
27
27
|
let stream = renderToStream(<App />, {
|
|
28
28
|
frameSrc: request.url,
|
|
29
|
+
signal: request.signal,
|
|
29
30
|
resolveFrame(src, _target, context) {
|
|
30
31
|
let frameUrl = new URL(src, context?.currentFrameSrc ?? request.url)
|
|
31
32
|
return fetchHtml(frameUrl)
|
|
@@ -44,6 +45,7 @@ return new Response(stream, {
|
|
|
44
45
|
|
|
45
46
|
- **`frameSrc`** - Seeds SSR frame state for the current render. When provided, server-rendered components can read `handle.frame.src` and `handle.frames.top.src` during SSR.
|
|
46
47
|
- **`topFrameSrc`** - Overrides the root frame URL used for `handle.frames.top.src`. This is mainly useful when calling `renderToStream()` from inside `resolveFrame()` for a nested frame render.
|
|
48
|
+
- **`signal`** - Cancels pending server rendering work. Pass `request.signal` so client disconnects can stop unresolved frame work without invoking `onError` for the disconnect itself.
|
|
47
49
|
- **`resolveFrame(src, target, context)`** - Called when a `<Frame>` needs its content. Return a string of HTML, a `ReadableStream<Uint8Array>`, or a promise of either. `context.currentFrameSrc` is the URL for the frame that contains the `<Frame>`, and `context.topFrameSrc` is the outer document URL. Required if your component tree contains `<Frame>` elements.
|
|
48
50
|
- **`onError(error)`** - Called when a rendering error occurs. If not provided, the stream rejects with the error.
|
|
49
51
|
|
|
@@ -80,7 +82,7 @@ function ProductPage() {
|
|
|
80
82
|
|
|
81
83
|
## CSS
|
|
82
84
|
|
|
83
|
-
Components using the `css`
|
|
85
|
+
Components using the `css(...)` mixin through `mix` have their styles collected during rendering and emitted as a single `<style>` tag in the `<head>`. No client-side style injection is needed for server-rendered content.
|
|
84
86
|
|
|
85
87
|
## See Also
|
|
86
88
|
|
package/dist/node-serve.d.ts
DELETED
package/dist/node-serve.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node-serve.d.ts","sourceRoot":"","sources":["../src/node-serve.ts"],"names":[],"mappings":"AACA,cAAc,uBAAuB,CAAA"}
|