remix 3.0.0-beta.4 → 3.0.0-beta.5
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/ui/accordion/primitives.d.ts +2 -0
- package/dist/ui/accordion/primitives.d.ts.map +1 -0
- package/dist/ui/accordion/primitives.js +2 -0
- package/dist/ui/button.d.ts +1 -0
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +1 -0
- package/dist/ui/checkbox.d.ts +3 -0
- package/dist/ui/checkbox.d.ts.map +1 -0
- package/dist/ui/checkbox.js +3 -0
- package/dist/ui/combobox/primitives.d.ts +2 -0
- package/dist/ui/combobox/primitives.d.ts.map +1 -0
- package/dist/ui/combobox/primitives.js +2 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.d.ts.map +1 -0
- package/dist/ui/input.js +3 -0
- package/dist/ui/menu/primitives.d.ts +2 -0
- package/dist/ui/menu/primitives.d.ts.map +1 -0
- package/dist/ui/{glyph.js → menu/primitives.js} +1 -1
- package/dist/ui/radio.d.ts +3 -0
- package/dist/ui/radio.d.ts.map +1 -0
- package/dist/ui/radio.js +3 -0
- package/dist/ui/select/primitives.d.ts +2 -0
- package/dist/ui/select/primitives.d.ts.map +1 -0
- package/dist/ui/select/primitives.js +2 -0
- package/dist/ui/tabs/primitives.d.ts +2 -0
- package/dist/ui/tabs/primitives.d.ts.map +1 -0
- package/dist/ui/{theme.js → tabs/primitives.js} +1 -1
- package/dist/ui/tabs.d.ts +2 -0
- package/dist/ui/tabs.d.ts.map +1 -0
- package/{src/ui/glyph.ts → dist/ui/tabs.js} +1 -1
- package/dist/ui/toggle/primitives.d.ts +2 -0
- package/dist/ui/toggle/primitives.d.ts.map +1 -0
- package/dist/ui/toggle/primitives.js +2 -0
- package/dist/ui/toggle.d.ts +3 -0
- package/dist/ui/toggle.d.ts.map +1 -0
- package/dist/ui/toggle.js +3 -0
- package/package.json +73 -45
- package/src/data-table-mysql/README.md +25 -0
- package/src/data-table-postgres/README.md +26 -0
- package/src/form-data-parser/README.md +4 -4
- package/src/mime/README.md +8 -1
- package/src/node-fetch-server/README.md +28 -2
- package/src/route-pattern/README.md +51 -6
- package/src/session/README.md +1 -1
- package/src/ui/README.md +50 -164
- package/src/ui/accordion/README.md +50 -14
- package/src/ui/accordion/primitives/README.md +202 -0
- package/src/ui/accordion/primitives.ts +2 -0
- package/src/ui/anchor/README.md +37 -2
- package/src/ui/breadcrumbs/README.md +4 -4
- package/src/ui/button/README.md +26 -26
- package/src/ui/button.ts +1 -0
- package/src/ui/checkbox/README.md +59 -0
- package/src/ui/checkbox.ts +3 -0
- package/src/ui/combobox/README.md +58 -9
- package/src/ui/combobox/primitives/README.md +194 -0
- package/src/ui/combobox/primitives.ts +2 -0
- package/src/ui/input/README.md +52 -0
- package/src/ui/input.ts +3 -0
- package/src/ui/listbox/README.md +9 -41
- package/src/ui/menu/README.md +55 -14
- package/src/ui/menu/primitives/README.md +161 -0
- package/{dist/ui/scroll-lock.js → src/ui/menu/primitives.ts} +1 -1
- package/src/ui/popover/README.md +20 -39
- package/src/ui/radio/README.md +53 -0
- package/src/ui/radio.ts +3 -0
- package/src/ui/select/README.md +29 -19
- package/src/ui/select/primitives/README.md +117 -0
- package/src/ui/select/primitives.ts +2 -0
- package/src/ui/tabs/README.md +141 -0
- package/src/ui/tabs/primitives/README.md +141 -0
- package/{dist/ui/separator.js → src/ui/tabs/primitives.ts} +1 -1
- package/src/ui/{theme.ts → tabs.ts} +1 -1
- package/src/ui/toggle/README.md +56 -0
- package/src/ui/toggle/primitives/README.md +56 -0
- package/src/ui/toggle/primitives.ts +2 -0
- package/src/ui/toggle.ts +3 -0
- package/dist/ui/glyph.d.ts +0 -2
- package/dist/ui/glyph.d.ts.map +0 -1
- package/dist/ui/scroll-lock.d.ts +0 -2
- package/dist/ui/scroll-lock.d.ts.map +0 -1
- package/dist/ui/separator.d.ts +0 -2
- package/dist/ui/separator.d.ts.map +0 -1
- package/dist/ui/theme.d.ts +0 -2
- package/dist/ui/theme.d.ts.map +0 -1
- package/src/ui/glyph/README.md +0 -72
- package/src/ui/scroll-lock/README.md +0 -33
- package/src/ui/scroll-lock.ts +0 -2
- package/src/ui/separator.ts +0 -2
- package/src/ui/theme/README.md +0 -103
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/accordion/primitives.ts"],"names":[],"mappings":"AACA,cAAc,oCAAoC,CAAA"}
|
package/dist/ui/button.d.ts
CHANGED
package/dist/ui/button.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/ui/button.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/ui/button.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/ui/button.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../src/ui/checkbox.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/combobox/primitives.ts"],"names":[],"mappings":"AACA,cAAc,mCAAmC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/ui/input.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/ui/input.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/primitives.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// IMPORTANT: This file is auto-generated, please do not edit manually.
|
|
2
|
-
export * from '@remix-run/ui/
|
|
2
|
+
export * from '@remix-run/ui/menu/primitives';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../../src/ui/radio.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/ui/radio.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/select/primitives.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/tabs/primitives.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// IMPORTANT: This file is auto-generated, please do not edit manually.
|
|
2
|
-
export * from '@remix-run/ui/
|
|
2
|
+
export * from '@remix-run/ui/tabs/primitives';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/ui/tabs.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// IMPORTANT: This file is auto-generated, please do not edit manually.
|
|
2
|
-
export * from '@remix-run/ui/
|
|
2
|
+
export * from '@remix-run/ui/tabs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/toggle/primitives.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toggle.d.ts","sourceRoot":"","sources":["../../src/ui/toggle.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remix",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.5",
|
|
4
4
|
"description": "The Remix web framework",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -453,6 +453,10 @@
|
|
|
453
453
|
"types": "./dist/ui/accordion.d.ts",
|
|
454
454
|
"default": "./dist/ui/accordion.js"
|
|
455
455
|
},
|
|
456
|
+
"./ui/accordion/primitives": {
|
|
457
|
+
"types": "./dist/ui/accordion/primitives.d.ts",
|
|
458
|
+
"default": "./dist/ui/accordion/primitives.js"
|
|
459
|
+
},
|
|
456
460
|
"./ui/anchor": {
|
|
457
461
|
"types": "./dist/ui/anchor.d.ts",
|
|
458
462
|
"default": "./dist/ui/anchor.js"
|
|
@@ -469,13 +473,21 @@
|
|
|
469
473
|
"types": "./dist/ui/button.d.ts",
|
|
470
474
|
"default": "./dist/ui/button.js"
|
|
471
475
|
},
|
|
476
|
+
"./ui/checkbox": {
|
|
477
|
+
"types": "./dist/ui/checkbox.d.ts",
|
|
478
|
+
"default": "./dist/ui/checkbox.js"
|
|
479
|
+
},
|
|
472
480
|
"./ui/combobox": {
|
|
473
481
|
"types": "./dist/ui/combobox.d.ts",
|
|
474
482
|
"default": "./dist/ui/combobox.js"
|
|
475
483
|
},
|
|
476
|
-
"./ui/
|
|
477
|
-
"types": "./dist/ui/
|
|
478
|
-
"default": "./dist/ui/
|
|
484
|
+
"./ui/combobox/primitives": {
|
|
485
|
+
"types": "./dist/ui/combobox/primitives.d.ts",
|
|
486
|
+
"default": "./dist/ui/combobox/primitives.js"
|
|
487
|
+
},
|
|
488
|
+
"./ui/input": {
|
|
489
|
+
"types": "./dist/ui/input.d.ts",
|
|
490
|
+
"default": "./dist/ui/input.js"
|
|
479
491
|
},
|
|
480
492
|
"./ui/jsx-dev-runtime": {
|
|
481
493
|
"types": "./dist/ui/jsx-dev-runtime.d.ts",
|
|
@@ -493,33 +505,49 @@
|
|
|
493
505
|
"types": "./dist/ui/menu.d.ts",
|
|
494
506
|
"default": "./dist/ui/menu.js"
|
|
495
507
|
},
|
|
508
|
+
"./ui/menu/primitives": {
|
|
509
|
+
"types": "./dist/ui/menu/primitives.d.ts",
|
|
510
|
+
"default": "./dist/ui/menu/primitives.js"
|
|
511
|
+
},
|
|
496
512
|
"./ui/popover": {
|
|
497
513
|
"types": "./dist/ui/popover.d.ts",
|
|
498
514
|
"default": "./dist/ui/popover.js"
|
|
499
515
|
},
|
|
500
|
-
"./ui/
|
|
501
|
-
"types": "./dist/ui/
|
|
502
|
-
"default": "./dist/ui/
|
|
516
|
+
"./ui/radio": {
|
|
517
|
+
"types": "./dist/ui/radio.d.ts",
|
|
518
|
+
"default": "./dist/ui/radio.js"
|
|
503
519
|
},
|
|
504
520
|
"./ui/select": {
|
|
505
521
|
"types": "./dist/ui/select.d.ts",
|
|
506
522
|
"default": "./dist/ui/select.js"
|
|
507
523
|
},
|
|
508
|
-
"./ui/
|
|
509
|
-
"types": "./dist/ui/
|
|
510
|
-
"default": "./dist/ui/
|
|
524
|
+
"./ui/select/primitives": {
|
|
525
|
+
"types": "./dist/ui/select/primitives.d.ts",
|
|
526
|
+
"default": "./dist/ui/select/primitives.js"
|
|
511
527
|
},
|
|
512
528
|
"./ui/server": {
|
|
513
529
|
"types": "./dist/ui/server.d.ts",
|
|
514
530
|
"default": "./dist/ui/server.js"
|
|
515
531
|
},
|
|
532
|
+
"./ui/tabs": {
|
|
533
|
+
"types": "./dist/ui/tabs.d.ts",
|
|
534
|
+
"default": "./dist/ui/tabs.js"
|
|
535
|
+
},
|
|
536
|
+
"./ui/tabs/primitives": {
|
|
537
|
+
"types": "./dist/ui/tabs/primitives.d.ts",
|
|
538
|
+
"default": "./dist/ui/tabs/primitives.js"
|
|
539
|
+
},
|
|
516
540
|
"./ui/test": {
|
|
517
541
|
"types": "./dist/ui/test.d.ts",
|
|
518
542
|
"default": "./dist/ui/test.js"
|
|
519
543
|
},
|
|
520
|
-
"./ui/
|
|
521
|
-
"types": "./dist/ui/
|
|
522
|
-
"default": "./dist/ui/
|
|
544
|
+
"./ui/toggle": {
|
|
545
|
+
"types": "./dist/ui/toggle.d.ts",
|
|
546
|
+
"default": "./dist/ui/toggle.js"
|
|
547
|
+
},
|
|
548
|
+
"./ui/toggle/primitives": {
|
|
549
|
+
"types": "./dist/ui/toggle/primitives.d.ts",
|
|
550
|
+
"default": "./dist/ui/toggle/primitives.js"
|
|
523
551
|
},
|
|
524
552
|
"./package.json": "./package.json"
|
|
525
553
|
},
|
|
@@ -529,50 +557,50 @@
|
|
|
529
557
|
"typescript": "^5.9.3"
|
|
530
558
|
},
|
|
531
559
|
"dependencies": {
|
|
532
|
-
"@remix-run/assets": "^0.4.
|
|
533
|
-
"@remix-run/auth": "^0.2.
|
|
534
|
-
"@remix-run/async-context-middleware": "^0.3.
|
|
535
|
-
"@remix-run/
|
|
536
|
-
"@remix-run/
|
|
537
|
-
"@remix-run/
|
|
538
|
-
"@remix-run/cop-middleware": "^0.1.
|
|
539
|
-
"@remix-run/
|
|
540
|
-
"@remix-run/
|
|
560
|
+
"@remix-run/assets": "^0.4.4",
|
|
561
|
+
"@remix-run/auth": "^0.2.6",
|
|
562
|
+
"@remix-run/async-context-middleware": "^0.3.4",
|
|
563
|
+
"@remix-run/auth-middleware": "^0.2.4",
|
|
564
|
+
"@remix-run/ui": "^0.4.0",
|
|
565
|
+
"@remix-run/compression-middleware": "^0.1.12",
|
|
566
|
+
"@remix-run/cop-middleware": "^0.1.7",
|
|
567
|
+
"@remix-run/cors-middleware": "^0.1.7",
|
|
568
|
+
"@remix-run/csrf-middleware": "^0.1.7",
|
|
569
|
+
"@remix-run/cookie": "^0.5.4",
|
|
541
570
|
"@remix-run/data-schema": "^0.3.0",
|
|
542
571
|
"@remix-run/data-table": "^0.3.0",
|
|
543
|
-
"@remix-run/cookie": "^0.5.4",
|
|
544
|
-
"@remix-run/data-table-postgres": "^0.4.0",
|
|
545
572
|
"@remix-run/data-table-mysql": "^0.4.0",
|
|
546
573
|
"@remix-run/data-table-sqlite": "^0.5.1",
|
|
547
|
-
"@remix-run/
|
|
548
|
-
"@remix-run/fetch-
|
|
549
|
-
"@remix-run/file-storage": "^0.13.
|
|
550
|
-
"@remix-run/form-data-middleware": "^0.3.
|
|
551
|
-
"@remix-run/file-storage-s3": "^0.1.
|
|
552
|
-
"@remix-run/
|
|
553
|
-
"@remix-run/
|
|
554
|
-
"@remix-run/form-data-parser": "^0.17.3",
|
|
574
|
+
"@remix-run/data-table-postgres": "^0.4.0",
|
|
575
|
+
"@remix-run/fetch-proxy": "^0.8.4",
|
|
576
|
+
"@remix-run/file-storage": "^0.13.7",
|
|
577
|
+
"@remix-run/form-data-middleware": "^0.3.4",
|
|
578
|
+
"@remix-run/file-storage-s3": "^0.1.4",
|
|
579
|
+
"@remix-run/fs": "^0.4.6",
|
|
580
|
+
"@remix-run/form-data-parser": "^0.17.4",
|
|
555
581
|
"@remix-run/html-template": "^0.3.1",
|
|
556
|
-
"@remix-run/
|
|
557
|
-
"@remix-run/logger-middleware": "^0.3.
|
|
558
|
-
"@remix-run/
|
|
582
|
+
"@remix-run/lazy-file": "^5.0.6",
|
|
583
|
+
"@remix-run/logger-middleware": "^0.3.4",
|
|
584
|
+
"@remix-run/fetch-router": "^0.20.1",
|
|
585
|
+
"@remix-run/method-override-middleware": "^0.1.12",
|
|
586
|
+
"@remix-run/mime": "^0.4.2",
|
|
587
|
+
"@remix-run/headers": "^0.21.1",
|
|
559
588
|
"@remix-run/multipart-parser": "^0.16.3",
|
|
560
|
-
"@remix-run/lazy-file": "^5.0.5",
|
|
561
|
-
"@remix-run/node-fetch-server": "^0.13.3",
|
|
562
589
|
"@remix-run/node-tsx": "^0.1.1",
|
|
563
|
-
"@remix-run/
|
|
564
|
-
"@remix-run/
|
|
590
|
+
"@remix-run/node-fetch-server": "^0.14.0",
|
|
591
|
+
"@remix-run/route-pattern": "^0.23.0",
|
|
565
592
|
"@remix-run/session": "^0.4.2",
|
|
593
|
+
"@remix-run/response": "^0.3.7",
|
|
594
|
+
"@remix-run/session-middleware": "^0.3.4",
|
|
566
595
|
"@remix-run/session-storage-redis": "^0.1.1",
|
|
567
|
-
"@remix-run/
|
|
596
|
+
"@remix-run/tar-parser": "^0.7.1",
|
|
568
597
|
"@remix-run/session-storage-memcache": "^0.1.2",
|
|
569
|
-
"@remix-run/static-middleware": "^0.4.
|
|
598
|
+
"@remix-run/static-middleware": "^0.4.13",
|
|
570
599
|
"@remix-run/assert": "^0.3.0",
|
|
571
|
-
"@remix-run/
|
|
572
|
-
"@remix-run/cli": "^0.3.3",
|
|
573
|
-
"@remix-run/test": "^0.5.0",
|
|
600
|
+
"@remix-run/cli": "^0.3.4",
|
|
574
601
|
"@remix-run/terminal": "^0.1.1",
|
|
575
|
-
"@remix-run/
|
|
602
|
+
"@remix-run/test": "^0.5.0",
|
|
603
|
+
"@remix-run/render-middleware": "^0.1.4"
|
|
576
604
|
},
|
|
577
605
|
"bin": {
|
|
578
606
|
"remix": "./dist/cli-entry.js"
|
|
@@ -79,6 +79,31 @@ try {
|
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
## Running integration tests locally
|
|
83
|
+
|
|
84
|
+
To start a local MySQL container matching CI:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
podman run --name mysql \
|
|
88
|
+
-e MYSQL_ROOT_PASSWORD=root \
|
|
89
|
+
-e MYSQL_DATABASE=remix \
|
|
90
|
+
-p 3306:3306 \
|
|
91
|
+
-d mysql:8
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Then run:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
REMIX_DATA_TABLE_MYSQL_TEST_URL=mysql://root:root@127.0.0.1:3306/remix \
|
|
98
|
+
pnpm test src/lib/adapter.integration.test.ts
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Remove the container when you are done:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
podman rm -f mysql
|
|
105
|
+
```
|
|
106
|
+
|
|
82
107
|
## Related Packages
|
|
83
108
|
|
|
84
109
|
- [`data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table) - Core query/relations API
|
|
@@ -60,6 +60,32 @@ await db.transaction(async (txDb) => txDb.exec('select 1'), {
|
|
|
60
60
|
})
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Running integration tests locally
|
|
64
|
+
|
|
65
|
+
To start a local Postgres container matching CI:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
podman run --name postgres \
|
|
69
|
+
-e POSTGRES_USER=postgres \
|
|
70
|
+
-e POSTGRES_PASSWORD=postgres \
|
|
71
|
+
-e POSTGRES_DB=remix \
|
|
72
|
+
-p 5432:5432 \
|
|
73
|
+
-d postgres:16
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then run:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
REMIX_DATA_TABLE_POSTGRES_TEST_URL=postgres://postgres:postgres@127.0.0.1:5432/remix \
|
|
80
|
+
pnpm test src/lib/adapter.integration.test.ts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Remove the container when you are done:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
podman rm -f postgres
|
|
87
|
+
```
|
|
88
|
+
|
|
63
89
|
## Related Packages
|
|
64
90
|
|
|
65
91
|
- [`data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table) - Core query/relations API
|
|
@@ -7,7 +7,7 @@ A streaming `multipart/form-data` parser that solves memory issues with file upl
|
|
|
7
7
|
- **Drop-in replacement** for `request.formData()` with streaming file upload support
|
|
8
8
|
- **Minimal buffering** - processes file upload streams with minimal memory footprint
|
|
9
9
|
- **Standards-based** - built on the [web Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) and [File API](https://developer.mozilla.org/en-US/docs/Web/API/File)
|
|
10
|
-
- **Smart fallback** -
|
|
10
|
+
- **Smart fallback** - parses `application/x-www-form-urlencoded` requests with the same total size and field count limits, and uses native `request.formData()` for other form requests
|
|
11
11
|
- **Storage agnostic** - works with any storage backend (local disk, S3, R2, etc.)
|
|
12
12
|
|
|
13
13
|
## Why You Need This
|
|
@@ -74,7 +74,7 @@ async function requestHandler(request: Request) {
|
|
|
74
74
|
|
|
75
75
|
To validate the resulting `FormData` object with `remix/data-schema`, use the `remix/data-schema/form-data` helpers.
|
|
76
76
|
|
|
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`.
|
|
77
|
+
To limit the overall shape of multipart requests, use the `maxHeaderSize`, `maxFileSize`, `maxFiles`, `maxParts`, and `maxTotalSize` options. For `application/x-www-form-urlencoded` requests, `maxParts` limits the number of form fields and `maxTotalSize` limits the raw request body size. By default, `parseFormData()` uses `maxFiles = 20`, `maxParts = 1000`, and `maxTotalSize = maxFiles * maxFileSize + 1 MiB`.
|
|
78
78
|
|
|
79
79
|
Known limit errors are thrown directly so you can handle them with `instanceof` checks. Other failures while parsing the request body are wrapped in `FormDataParseError`, with the original error available as `error.cause`. Errors thrown or rejected by your `uploadHandler` are not wrapped.
|
|
80
80
|
|
|
@@ -106,9 +106,9 @@ try {
|
|
|
106
106
|
} else if (error instanceof MaxFileSizeExceededError) {
|
|
107
107
|
console.error(`Files may not be larger than 10 MiB`)
|
|
108
108
|
} else if (error instanceof MaxPartsExceededError) {
|
|
109
|
-
console.error(`Request may not contain more than 25 multipart parts`)
|
|
109
|
+
console.error(`Request may not contain more than 25 form fields or multipart parts`)
|
|
110
110
|
} else if (error instanceof MaxTotalSizeExceededError) {
|
|
111
|
-
console.error(`
|
|
111
|
+
console.error(`Form data request may not exceed 12 MiB of total content`)
|
|
112
112
|
} else if (error instanceof FormDataParseError) {
|
|
113
113
|
console.error(`Could not parse form data:`, error.cause ?? error)
|
|
114
114
|
} else {
|
package/src/mime/README.md
CHANGED
|
@@ -86,12 +86,19 @@ mimeTypeToContentType('image/png') // 'image/png'
|
|
|
86
86
|
Registers or overrides a MIME type for one or more file extensions.
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
|
-
import { defineMimeType } from 'remix/mime'
|
|
89
|
+
import { defineMimeType, detectMimeType } from 'remix/mime'
|
|
90
90
|
|
|
91
91
|
defineMimeType({
|
|
92
92
|
extensions: ['myformat'],
|
|
93
93
|
mimeType: 'application/x-myformat',
|
|
94
94
|
})
|
|
95
|
+
|
|
96
|
+
defineMimeType({
|
|
97
|
+
extensions: ['be.pit'],
|
|
98
|
+
mimeType: 'application/x-be-pit-document',
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
detectMimeType('filename.be.pit') // 'application/x-be-pit-document'
|
|
95
102
|
```
|
|
96
103
|
|
|
97
104
|
You can also optionally configure the charset and whether the MIME type is compressible:
|
|
@@ -8,7 +8,8 @@ Build Node.js servers with web-standard Fetch API primitives. `node-fetch-server
|
|
|
8
8
|
- **Node.js HTTP Integration** - Works directly with `node:http`, `node:https`, and `node:http2`
|
|
9
9
|
- **Streaming Support** - Response support with `ReadableStream`
|
|
10
10
|
- **Custom Hostname** - Configuration for deployment flexibility
|
|
11
|
-
- **
|
|
11
|
+
- **Proxy Header Support** - Opt in to trusted `Forwarded`, `X-Forwarded-Host`, `X-Forwarded-Proto`, and `X-Forwarded-For` headers
|
|
12
|
+
- **Client Info** - Access to client connection info (IP address, port, and trusted proxy address)
|
|
12
13
|
- **TypeScript** - Full TypeScript support with type definitions
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
@@ -146,9 +147,34 @@ let server = http.createServer(createRequestListener(handler, { host: hostname }
|
|
|
146
147
|
server.listen(3000)
|
|
147
148
|
```
|
|
148
149
|
|
|
150
|
+
### Trusted Proxy Headers
|
|
151
|
+
|
|
152
|
+
If your app runs behind a trusted reverse proxy, Node.js sees the proxy connection instead of the original client connection. Enable `trustProxy` to use trusted proxy headers when constructing `request.url` and client information:
|
|
153
|
+
|
|
154
|
+
- `Forwarded: proto` and `X-Forwarded-Proto` can provide the original request protocol.
|
|
155
|
+
- `Forwarded: host` and `X-Forwarded-Host` can provide the original request host.
|
|
156
|
+
- `Forwarded: for` and `X-Forwarded-For` can provide the original client address.
|
|
157
|
+
|
|
158
|
+
Only enable this option when your server is reachable exclusively through a trusted proxy that overwrites these headers. Otherwise, clients can spoof the host, protocol, and client address.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import * as http from 'node:http'
|
|
162
|
+
import { createRequestListener } from 'remix/node-fetch-server'
|
|
163
|
+
|
|
164
|
+
let server = http.createServer(
|
|
165
|
+
createRequestListener(handler, {
|
|
166
|
+
trustProxy: true,
|
|
167
|
+
}),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
server.listen(3000)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
When `host` or `protocol` are set, those fixed options take precedence over trusted proxy headers.
|
|
174
|
+
|
|
149
175
|
### Accessing Client Information
|
|
150
176
|
|
|
151
|
-
Get client connection details (IP address, port) for logging or security:
|
|
177
|
+
Get client connection details (IP address, port) for logging or security. When `trustProxy` is enabled, `client.address` uses trusted `Forwarded` or `X-Forwarded-For` values when present:
|
|
152
178
|
|
|
153
179
|
```ts
|
|
154
180
|
import { type FetchHandler } from 'remix/node-fetch-server'
|
|
@@ -65,6 +65,8 @@ createHref('http(s)://:region.cdn.com/assets/*file.:ext', {
|
|
|
65
65
|
|
|
66
66
|
For in-depth reference, visit the [`route-pattern` API docs](https://api.remix.run/api/remix/route-pattern)
|
|
67
67
|
|
|
68
|
+
Examples in this README use `remix/route-pattern/*` imports. The same APIs are also available from the direct package entrypoints: `@remix-run/route-pattern`, `@remix-run/route-pattern/href`, `@remix-run/route-pattern/match`, `@remix-run/route-pattern/join`, and `@remix-run/route-pattern/specificity`.
|
|
69
|
+
|
|
68
70
|
## Pattern syntax
|
|
69
71
|
|
|
70
72
|
### Protocol
|
|
@@ -149,6 +151,8 @@ docsMatcher.match(url)?.params
|
|
|
149
151
|
// Type safe params ^? { tenant: string | undefined, path: string, ext: string } | undefined
|
|
150
152
|
```
|
|
151
153
|
|
|
154
|
+
Matchers accept absolute URL strings or `URL` objects. Relative strings such as `/blog/v3` are not accepted because matching uses the platform `URL` parser; wrap relative paths with a known origin before matching them.
|
|
155
|
+
|
|
152
156
|
### Match against multiple patterns
|
|
153
157
|
|
|
154
158
|
Use `createMultiMatcher` when you need to match many patterns and attach your own data to each match.
|
|
@@ -172,17 +176,38 @@ matcher.match('https://example.com/api/v2/users/profile')
|
|
|
172
176
|
|
|
173
177
|
The matched pattern is only known at runtime, so matched `params` are not inferred when matching with `createMultiMatcher`.
|
|
174
178
|
|
|
179
|
+
Each match returns:
|
|
180
|
+
|
|
181
|
+
- `url`: the `URL` object that was matched
|
|
182
|
+
- `pattern`: the matched `RoutePattern`
|
|
183
|
+
- `data`: the data attached with `matcher.add(pattern, data)`
|
|
184
|
+
- `params`: captured param values
|
|
185
|
+
- `paramsMeta`: hostname and pathname param metadata
|
|
186
|
+
|
|
187
|
+
`paramsMeta.hostname` and `paramsMeta.pathname` are arrays of `{ type, name, value, begin, end }` entries. The offsets are measured after URL normalization. A pattern with no hostname matches any hostname, represented in `paramsMeta.hostname` as an unnamed wildcard entry.
|
|
188
|
+
|
|
189
|
+
Set `ignoreCase: true` to make pathname matching case-insensitive. Hostname matching is always case-insensitive, and search constraints are always case-sensitive.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
let matcher = createMatcher('/Docs/:slug', { ignoreCase: true })
|
|
193
|
+
|
|
194
|
+
matcher.match('https://example.com/docs/Intro')?.params
|
|
195
|
+
// { slug: 'Intro' }
|
|
196
|
+
```
|
|
197
|
+
|
|
175
198
|
### Ranking matches by specificity
|
|
176
199
|
|
|
177
200
|
When multiple patterns match the same URL, `route-pattern` chooses the most specific match deterministically. Matches are ranked left-to-right, character-by-character:
|
|
178
201
|
|
|
202
|
+
- Explicit protocol and port constraints are more specific than omitted constraints.
|
|
203
|
+
- Static hostnames are more specific than dynamic hostnames, which are more specific than omitted hostnames.
|
|
179
204
|
- Static characters are more specific than variables.
|
|
180
205
|
- Variables are more specific than wildcards.
|
|
181
206
|
- Earliest difference decides the winner.
|
|
182
207
|
|
|
183
208
|
This is the same ranking used by `createMultiMatcher`.
|
|
184
209
|
|
|
185
|
-
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`. For example:
|
|
210
|
+
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`. `lessThan(a, b)` returns `true` when match `a` is less specific than match `b`. For example:
|
|
186
211
|
|
|
187
212
|
```ts
|
|
188
213
|
import { createMultiMatcher } from 'remix/route-pattern/match'
|
|
@@ -226,6 +251,10 @@ createHref('blog/:slug?ref=docs', { slug: 'v3' }, { utm_source: 'newsletter' })
|
|
|
226
251
|
// '/blog/v3?utm_source=newsletter&ref=docs'
|
|
227
252
|
```
|
|
228
253
|
|
|
254
|
+
`createHref()` throws `CreateHrefError` when it cannot safely generate an href. The error exposes stable structured details on `error.details`; the string message is for humans.
|
|
255
|
+
|
|
256
|
+
Common failures include missing required params, nameless wildcards, invalid hostname params, empty pathname variables, and origin patterns that specify a protocol or port without a concrete hostname.
|
|
257
|
+
|
|
229
258
|
**Note:** optional groups without params are included in the generated href:
|
|
230
259
|
|
|
231
260
|
```ts
|
|
@@ -238,25 +267,41 @@ createHref('products(.json)')
|
|
|
238
267
|
|
|
239
268
|
## Parse & stringify patterns
|
|
240
269
|
|
|
241
|
-
You can explicitly parse and stringify patterns
|
|
270
|
+
You can explicitly parse and stringify patterns. Create a `RoutePattern` with `RoutePattern.parse` and use the methods and helpers below instead of reading parsed token internals.
|
|
242
271
|
|
|
243
272
|
```ts
|
|
244
|
-
import { RoutePattern } from 'remix/route-pattern'
|
|
273
|
+
import { getRoutePatternCaptures, RoutePattern } from 'remix/route-pattern'
|
|
245
274
|
|
|
246
|
-
let pattern = RoutePattern.parse('
|
|
275
|
+
let pattern = RoutePattern.parse('://:tenant.example.com/blog/:slug(/*path)')
|
|
247
276
|
// ^? RoutePattern
|
|
248
277
|
|
|
249
278
|
pattern.toString()
|
|
250
|
-
// '
|
|
279
|
+
// '://:tenant.example.com/blog/:slug(/*path)'
|
|
251
280
|
|
|
252
281
|
pattern.toJSON()
|
|
253
|
-
// { hostname: 'example.com', pathname: 'blog/:slug', ... }
|
|
282
|
+
// { hostname: ':tenant.example.com', pathname: 'blog/:slug(/*path)', ... }
|
|
283
|
+
|
|
284
|
+
getRoutePatternCaptures(pattern)
|
|
285
|
+
// [
|
|
286
|
+
// { part: 'hostname', type: ':', name: 'tenant', optional: false },
|
|
287
|
+
// { part: 'pathname', type: ':', name: 'slug', optional: false },
|
|
288
|
+
// { part: 'pathname', type: '*', name: 'path', optional: true },
|
|
289
|
+
// ]
|
|
254
290
|
```
|
|
255
291
|
|
|
256
292
|
All APIs that take a `pattern` arg accept `string` or a parsed `RoutePattern`.
|
|
257
293
|
|
|
258
294
|
**TIP:** For high-performance scenarios, you can parse patterns ahead of time to avoid reparsing them on every call.
|
|
259
295
|
|
|
296
|
+
`RoutePattern.toJSON()` returns a `RoutePatternJSON` object with serialized `protocol`, `hostname`, `port`, `pathname`, and `search` fields. `RoutePattern.parse()` throws `ParseError` for malformed sources; the error exposes stable `type`, `source`, and `index` fields.
|
|
297
|
+
|
|
298
|
+
The public support types are:
|
|
299
|
+
|
|
300
|
+
- `RoutePatternCapture` from `remix/route-pattern`
|
|
301
|
+
- `RoutePatternJSON` from `remix/route-pattern`
|
|
302
|
+
- `CreateHrefErrorDetails` from `remix/route-pattern/href`
|
|
303
|
+
- `MatchParamMeta` from `remix/route-pattern/match`
|
|
304
|
+
|
|
260
305
|
## Combine patterns
|
|
261
306
|
|
|
262
307
|
`joinPatterns` builds a new pattern from a base pattern.
|