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.
Files changed (90) hide show
  1. package/dist/ui/accordion/primitives.d.ts +2 -0
  2. package/dist/ui/accordion/primitives.d.ts.map +1 -0
  3. package/dist/ui/accordion/primitives.js +2 -0
  4. package/dist/ui/button.d.ts +1 -0
  5. package/dist/ui/button.d.ts.map +1 -1
  6. package/dist/ui/button.js +1 -0
  7. package/dist/ui/checkbox.d.ts +3 -0
  8. package/dist/ui/checkbox.d.ts.map +1 -0
  9. package/dist/ui/checkbox.js +3 -0
  10. package/dist/ui/combobox/primitives.d.ts +2 -0
  11. package/dist/ui/combobox/primitives.d.ts.map +1 -0
  12. package/dist/ui/combobox/primitives.js +2 -0
  13. package/dist/ui/input.d.ts +3 -0
  14. package/dist/ui/input.d.ts.map +1 -0
  15. package/dist/ui/input.js +3 -0
  16. package/dist/ui/menu/primitives.d.ts +2 -0
  17. package/dist/ui/menu/primitives.d.ts.map +1 -0
  18. package/dist/ui/{glyph.js → menu/primitives.js} +1 -1
  19. package/dist/ui/radio.d.ts +3 -0
  20. package/dist/ui/radio.d.ts.map +1 -0
  21. package/dist/ui/radio.js +3 -0
  22. package/dist/ui/select/primitives.d.ts +2 -0
  23. package/dist/ui/select/primitives.d.ts.map +1 -0
  24. package/dist/ui/select/primitives.js +2 -0
  25. package/dist/ui/tabs/primitives.d.ts +2 -0
  26. package/dist/ui/tabs/primitives.d.ts.map +1 -0
  27. package/dist/ui/{theme.js → tabs/primitives.js} +1 -1
  28. package/dist/ui/tabs.d.ts +2 -0
  29. package/dist/ui/tabs.d.ts.map +1 -0
  30. package/{src/ui/glyph.ts → dist/ui/tabs.js} +1 -1
  31. package/dist/ui/toggle/primitives.d.ts +2 -0
  32. package/dist/ui/toggle/primitives.d.ts.map +1 -0
  33. package/dist/ui/toggle/primitives.js +2 -0
  34. package/dist/ui/toggle.d.ts +3 -0
  35. package/dist/ui/toggle.d.ts.map +1 -0
  36. package/dist/ui/toggle.js +3 -0
  37. package/package.json +73 -45
  38. package/src/data-table-mysql/README.md +25 -0
  39. package/src/data-table-postgres/README.md +26 -0
  40. package/src/form-data-parser/README.md +4 -4
  41. package/src/mime/README.md +8 -1
  42. package/src/node-fetch-server/README.md +28 -2
  43. package/src/route-pattern/README.md +51 -6
  44. package/src/session/README.md +1 -1
  45. package/src/ui/README.md +50 -164
  46. package/src/ui/accordion/README.md +50 -14
  47. package/src/ui/accordion/primitives/README.md +202 -0
  48. package/src/ui/accordion/primitives.ts +2 -0
  49. package/src/ui/anchor/README.md +37 -2
  50. package/src/ui/breadcrumbs/README.md +4 -4
  51. package/src/ui/button/README.md +26 -26
  52. package/src/ui/button.ts +1 -0
  53. package/src/ui/checkbox/README.md +59 -0
  54. package/src/ui/checkbox.ts +3 -0
  55. package/src/ui/combobox/README.md +58 -9
  56. package/src/ui/combobox/primitives/README.md +194 -0
  57. package/src/ui/combobox/primitives.ts +2 -0
  58. package/src/ui/input/README.md +52 -0
  59. package/src/ui/input.ts +3 -0
  60. package/src/ui/listbox/README.md +9 -41
  61. package/src/ui/menu/README.md +55 -14
  62. package/src/ui/menu/primitives/README.md +161 -0
  63. package/{dist/ui/scroll-lock.js → src/ui/menu/primitives.ts} +1 -1
  64. package/src/ui/popover/README.md +20 -39
  65. package/src/ui/radio/README.md +53 -0
  66. package/src/ui/radio.ts +3 -0
  67. package/src/ui/select/README.md +29 -19
  68. package/src/ui/select/primitives/README.md +117 -0
  69. package/src/ui/select/primitives.ts +2 -0
  70. package/src/ui/tabs/README.md +141 -0
  71. package/src/ui/tabs/primitives/README.md +141 -0
  72. package/{dist/ui/separator.js → src/ui/tabs/primitives.ts} +1 -1
  73. package/src/ui/{theme.ts → tabs.ts} +1 -1
  74. package/src/ui/toggle/README.md +56 -0
  75. package/src/ui/toggle/primitives/README.md +56 -0
  76. package/src/ui/toggle/primitives.ts +2 -0
  77. package/src/ui/toggle.ts +3 -0
  78. package/dist/ui/glyph.d.ts +0 -2
  79. package/dist/ui/glyph.d.ts.map +0 -1
  80. package/dist/ui/scroll-lock.d.ts +0 -2
  81. package/dist/ui/scroll-lock.d.ts.map +0 -1
  82. package/dist/ui/separator.d.ts +0 -2
  83. package/dist/ui/separator.d.ts.map +0 -1
  84. package/dist/ui/theme.d.ts +0 -2
  85. package/dist/ui/theme.d.ts.map +0 -1
  86. package/src/ui/glyph/README.md +0 -72
  87. package/src/ui/scroll-lock/README.md +0 -33
  88. package/src/ui/scroll-lock.ts +0 -2
  89. package/src/ui/separator.ts +0 -2
  90. package/src/ui/theme/README.md +0 -103
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/accordion/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/ui/accordion/primitives.ts"],"names":[],"mappings":"AACA,cAAc,oCAAoC,CAAA"}
@@ -0,0 +1,2 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/accordion/primitives';
@@ -1,2 +1,3 @@
1
1
  export * from '@remix-run/ui/button';
2
+ export { default } from '@remix-run/ui/button';
2
3
  //# sourceMappingURL=button.d.ts.map
@@ -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
@@ -1,2 +1,3 @@
1
1
  // IMPORTANT: This file is auto-generated, please do not edit manually.
2
2
  export * from '@remix-run/ui/button';
3
+ export { default } from '@remix-run/ui/button';
@@ -0,0 +1,3 @@
1
+ export * from '@remix-run/ui/checkbox';
2
+ export { default } from '@remix-run/ui/checkbox';
3
+ //# sourceMappingURL=checkbox.d.ts.map
@@ -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,3 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/checkbox';
3
+ export { default } from '@remix-run/ui/checkbox';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/combobox/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -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,2 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/combobox/primitives';
@@ -0,0 +1,3 @@
1
+ export * from '@remix-run/ui/input';
2
+ export { default } from '@remix-run/ui/input';
3
+ //# sourceMappingURL=input.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/input';
3
+ export { default } from '@remix-run/ui/input';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/menu/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -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/glyph';
2
+ export * from '@remix-run/ui/menu/primitives';
@@ -0,0 +1,3 @@
1
+ export * from '@remix-run/ui/radio';
2
+ export { default } from '@remix-run/ui/radio';
3
+ //# sourceMappingURL=radio.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/radio';
3
+ export { default } from '@remix-run/ui/radio';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/select/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -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,2 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/select/primitives';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/tabs/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -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/theme';
2
+ export * from '@remix-run/ui/tabs/primitives';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/tabs';
2
+ //# sourceMappingURL=tabs.d.ts.map
@@ -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/glyph'
2
+ export * from '@remix-run/ui/tabs';
@@ -0,0 +1,2 @@
1
+ export * from '@remix-run/ui/toggle/primitives';
2
+ //# sourceMappingURL=primitives.d.ts.map
@@ -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,2 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/toggle/primitives';
@@ -0,0 +1,3 @@
1
+ export * from '@remix-run/ui/toggle';
2
+ export { default } from '@remix-run/ui/toggle';
3
+ //# sourceMappingURL=toggle.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/toggle';
3
+ export { default } from '@remix-run/ui/toggle';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix",
3
- "version": "3.0.0-beta.4",
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/glyph": {
477
- "types": "./dist/ui/glyph.d.ts",
478
- "default": "./dist/ui/glyph.js"
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/scroll-lock": {
501
- "types": "./dist/ui/scroll-lock.d.ts",
502
- "default": "./dist/ui/scroll-lock.js"
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/separator": {
509
- "types": "./dist/ui/separator.d.ts",
510
- "default": "./dist/ui/separator.js"
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/theme": {
521
- "types": "./dist/ui/theme.d.ts",
522
- "default": "./dist/ui/theme.js"
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.3",
533
- "@remix-run/auth": "^0.2.5",
534
- "@remix-run/async-context-middleware": "^0.3.3",
535
- "@remix-run/ui": "^0.3.0",
536
- "@remix-run/compression-middleware": "^0.1.11",
537
- "@remix-run/auth-middleware": "^0.2.3",
538
- "@remix-run/cop-middleware": "^0.1.6",
539
- "@remix-run/csrf-middleware": "^0.1.6",
540
- "@remix-run/cors-middleware": "^0.1.6",
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/fetch-proxy": "^0.8.3",
548
- "@remix-run/fetch-router": "^0.20.0",
549
- "@remix-run/file-storage": "^0.13.6",
550
- "@remix-run/form-data-middleware": "^0.3.3",
551
- "@remix-run/file-storage-s3": "^0.1.3",
552
- "@remix-run/headers": "^0.21.1",
553
- "@remix-run/fs": "^0.4.5",
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/method-override-middleware": "^0.1.11",
557
- "@remix-run/logger-middleware": "^0.3.3",
558
- "@remix-run/mime": "^0.4.1",
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/route-pattern": "^0.22.1",
564
- "@remix-run/response": "^0.3.6",
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/session-middleware": "^0.3.3",
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.12",
598
+ "@remix-run/static-middleware": "^0.4.13",
570
599
  "@remix-run/assert": "^0.3.0",
571
- "@remix-run/tar-parser": "^0.7.1",
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/render-middleware": "^0.1.3"
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** - automatically uses native `request.formData()` for non-`multipart/form-data` requests
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(`Multipart request may not exceed 12 MiB of total content`)
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 {
@@ -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
- - **Client Info** - Access to client connection info (IP address, port)
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('://example.com/blog/:slug')
275
+ let pattern = RoutePattern.parse('://:tenant.example.com/blog/:slug(/*path)')
247
276
  // ^? RoutePattern
248
277
 
249
278
  pattern.toString()
250
- // '://example.com/blog/:slug'
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.