remix 3.0.0-beta.0 → 3.0.0-beta.10

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 (258) hide show
  1. package/README.md +4 -2
  2. package/dist/assets/types/hmr.d.ts +2 -0
  3. package/dist/cli-entry.js +1 -1
  4. package/dist/data-table/cli.d.ts +2 -0
  5. package/dist/data-table/cli.d.ts.map +1 -0
  6. package/dist/{ui/scroll-lock.js → data-table/cli.js} +1 -1
  7. package/dist/fetch-router.d.ts +7 -0
  8. package/dist/fetch-router.d.ts.map +1 -1
  9. package/dist/headers/accept-encoding.d.ts +2 -0
  10. package/dist/headers/accept-encoding.d.ts.map +1 -0
  11. package/dist/headers/accept-encoding.js +2 -0
  12. package/dist/headers/accept-language.d.ts +2 -0
  13. package/dist/headers/accept-language.d.ts.map +1 -0
  14. package/dist/headers/accept-language.js +2 -0
  15. package/dist/headers/accept.d.ts +2 -0
  16. package/dist/headers/accept.d.ts.map +1 -0
  17. package/dist/headers/accept.js +2 -0
  18. package/dist/headers/cache-control.d.ts +2 -0
  19. package/dist/headers/cache-control.d.ts.map +1 -0
  20. package/dist/headers/cache-control.js +2 -0
  21. package/dist/headers/content-disposition.d.ts +2 -0
  22. package/dist/headers/content-disposition.d.ts.map +1 -0
  23. package/dist/headers/content-disposition.js +2 -0
  24. package/dist/headers/content-range.d.ts +2 -0
  25. package/dist/headers/content-range.d.ts.map +1 -0
  26. package/dist/headers/content-range.js +2 -0
  27. package/dist/headers/content-type.d.ts +2 -0
  28. package/dist/headers/content-type.d.ts.map +1 -0
  29. package/dist/headers/content-type.js +2 -0
  30. package/dist/headers/cookie.d.ts +2 -0
  31. package/dist/headers/cookie.d.ts.map +1 -0
  32. package/dist/headers/cookie.js +2 -0
  33. package/dist/headers/if-match.d.ts +2 -0
  34. package/dist/headers/if-match.d.ts.map +1 -0
  35. package/dist/headers/if-match.js +2 -0
  36. package/dist/headers/if-none-match.d.ts +2 -0
  37. package/dist/headers/if-none-match.d.ts.map +1 -0
  38. package/dist/headers/if-none-match.js +2 -0
  39. package/dist/headers/if-range.d.ts +2 -0
  40. package/dist/headers/if-range.d.ts.map +1 -0
  41. package/dist/headers/if-range.js +2 -0
  42. package/dist/headers/range.d.ts +2 -0
  43. package/dist/headers/range.d.ts.map +1 -0
  44. package/dist/headers/range.js +2 -0
  45. package/dist/headers/raw-headers.d.ts +2 -0
  46. package/dist/headers/raw-headers.d.ts.map +1 -0
  47. package/dist/headers/raw-headers.js +2 -0
  48. package/dist/headers/set-cookie.d.ts +2 -0
  49. package/dist/headers/set-cookie.d.ts.map +1 -0
  50. package/dist/headers/set-cookie.js +2 -0
  51. package/dist/headers/vary.d.ts +2 -0
  52. package/dist/headers/vary.d.ts.map +1 -0
  53. package/dist/{ui/separator.js → headers/vary.js} +1 -1
  54. package/dist/node-hmr/runtime.d.ts +2 -0
  55. package/dist/node-hmr/runtime.d.ts.map +1 -0
  56. package/dist/node-hmr/runtime.js +2 -0
  57. package/dist/node-hmr/types.d.ts +2 -0
  58. package/dist/node-hmr.d.ts +2 -0
  59. package/dist/node-hmr.d.ts.map +1 -0
  60. package/dist/{ui/glyph.js → node-hmr.js} +1 -1
  61. package/dist/node-tsx/load-module.d.ts +2 -0
  62. package/dist/node-tsx/load-module.d.ts.map +1 -0
  63. package/dist/node-tsx/load-module.js +2 -0
  64. package/dist/node-tsx.d.ts +3 -0
  65. package/dist/node-tsx.d.ts.map +1 -0
  66. package/dist/node-tsx.js +3 -0
  67. package/dist/render-middleware.d.ts +2 -0
  68. package/dist/render-middleware.d.ts.map +1 -0
  69. package/dist/render-middleware.js +2 -0
  70. package/dist/route-pattern/href.d.ts +2 -0
  71. package/dist/route-pattern/href.d.ts.map +1 -0
  72. package/dist/route-pattern/href.js +2 -0
  73. package/dist/route-pattern/join.d.ts +2 -0
  74. package/dist/route-pattern/join.d.ts.map +1 -0
  75. package/dist/route-pattern/join.js +2 -0
  76. package/dist/route-pattern/match.d.ts +2 -0
  77. package/dist/route-pattern/match.d.ts.map +1 -0
  78. package/dist/route-pattern/match.js +2 -0
  79. package/dist/ui/accordion/primitives.d.ts +2 -0
  80. package/dist/ui/accordion/primitives.d.ts.map +1 -0
  81. package/dist/ui/accordion/primitives.js +2 -0
  82. package/dist/ui/button.d.ts +1 -0
  83. package/dist/ui/button.d.ts.map +1 -1
  84. package/dist/ui/button.js +1 -0
  85. package/dist/ui/checkbox.d.ts +3 -0
  86. package/dist/ui/checkbox.d.ts.map +1 -0
  87. package/dist/ui/checkbox.js +3 -0
  88. package/dist/ui/combobox/primitives.d.ts +2 -0
  89. package/dist/ui/combobox/primitives.d.ts.map +1 -0
  90. package/dist/ui/combobox/primitives.js +2 -0
  91. package/dist/ui/dev/refresh.d.ts +2 -0
  92. package/dist/ui/dev/refresh.d.ts.map +1 -0
  93. package/dist/ui/dev/refresh.js +2 -0
  94. package/dist/ui/input.d.ts +3 -0
  95. package/dist/ui/input.d.ts.map +1 -0
  96. package/dist/ui/input.js +3 -0
  97. package/dist/ui/menu/primitives.d.ts +2 -0
  98. package/dist/ui/menu/primitives.d.ts.map +1 -0
  99. package/dist/ui/menu/primitives.js +2 -0
  100. package/dist/ui/radio.d.ts +3 -0
  101. package/dist/ui/radio.d.ts.map +1 -0
  102. package/dist/ui/radio.js +3 -0
  103. package/dist/ui/select/primitives.d.ts +2 -0
  104. package/dist/ui/select/primitives.d.ts.map +1 -0
  105. package/dist/ui/select/primitives.js +2 -0
  106. package/dist/ui/tabs/primitives.d.ts +2 -0
  107. package/dist/ui/tabs/primitives.d.ts.map +1 -0
  108. package/dist/ui/tabs/primitives.js +2 -0
  109. package/dist/ui/tabs.d.ts +2 -0
  110. package/dist/ui/tabs.d.ts.map +1 -0
  111. package/{src/ui/theme.ts → dist/ui/tabs.js} +1 -1
  112. package/dist/ui/toggle/primitives.d.ts +2 -0
  113. package/dist/ui/toggle/primitives.d.ts.map +1 -0
  114. package/dist/ui/toggle/primitives.js +2 -0
  115. package/dist/ui/toggle.d.ts +3 -0
  116. package/dist/ui/toggle.d.ts.map +1 -0
  117. package/dist/ui/toggle.js +3 -0
  118. package/dist/ui-hmr/assets.d.ts +2 -0
  119. package/dist/ui-hmr/assets.d.ts.map +1 -0
  120. package/dist/ui-hmr/assets.js +2 -0
  121. package/dist/ui-hmr/node.d.ts +3 -0
  122. package/dist/ui-hmr/node.d.ts.map +1 -0
  123. package/dist/ui-hmr/node.js +3 -0
  124. package/dist/ui-hmr/runtime/browser.d.ts +2 -0
  125. package/dist/ui-hmr/runtime/browser.d.ts.map +1 -0
  126. package/dist/ui-hmr/runtime/browser.js +2 -0
  127. package/dist/ui-hmr/runtime/server.d.ts +2 -0
  128. package/dist/ui-hmr/runtime/server.d.ts.map +1 -0
  129. package/dist/ui-hmr/runtime/server.js +2 -0
  130. package/dist/ui-hmr.d.ts +2 -0
  131. package/dist/ui-hmr.d.ts.map +1 -0
  132. package/{src/ui/glyph.ts → dist/ui-hmr.js} +1 -1
  133. package/package.json +284 -129
  134. package/src/assert/README.md +116 -0
  135. package/src/assets/README.md +805 -0
  136. package/src/assets/types/hmr.d.ts +2 -0
  137. package/src/async-context-middleware/README.md +100 -0
  138. package/src/auth/README.md +445 -0
  139. package/src/auth-middleware/README.md +246 -0
  140. package/src/cli/README.md +182 -0
  141. package/src/compression-middleware/README.md +176 -0
  142. package/src/cookie/README.md +106 -0
  143. package/src/cop-middleware/README.md +117 -0
  144. package/src/cors-middleware/README.md +174 -0
  145. package/src/csrf-middleware/README.md +92 -0
  146. package/src/data-schema/README.md +416 -0
  147. package/src/data-table/README.md +678 -0
  148. package/src/data-table/cli.ts +2 -0
  149. package/src/data-table-mysql/README.md +120 -0
  150. package/src/data-table-postgres/README.md +98 -0
  151. package/src/data-table-sqlite/README.md +100 -0
  152. package/src/fetch-proxy/README.md +70 -0
  153. package/src/fetch-router/README.md +982 -0
  154. package/src/fetch-router.ts +7 -0
  155. package/src/file-storage/README.md +57 -0
  156. package/src/file-storage-s3/README.md +46 -0
  157. package/src/form-data-middleware/README.md +108 -0
  158. package/src/form-data-parser/README.md +158 -0
  159. package/src/fs/README.md +60 -0
  160. package/src/headers/README.md +639 -0
  161. package/src/headers/accept-encoding.ts +2 -0
  162. package/src/headers/accept-language.ts +2 -0
  163. package/src/headers/accept.ts +2 -0
  164. package/src/headers/cache-control.ts +2 -0
  165. package/src/headers/content-disposition.ts +2 -0
  166. package/src/headers/content-range.ts +2 -0
  167. package/src/headers/content-type.ts +2 -0
  168. package/src/headers/cookie.ts +2 -0
  169. package/src/headers/if-match.ts +2 -0
  170. package/src/headers/if-none-match.ts +2 -0
  171. package/src/headers/if-range.ts +2 -0
  172. package/src/headers/range.ts +2 -0
  173. package/src/headers/raw-headers.ts +2 -0
  174. package/src/headers/set-cookie.ts +2 -0
  175. package/src/{node-serve.ts → headers/vary.ts} +1 -1
  176. package/src/html-template/README.md +101 -0
  177. package/src/lazy-file/README.md +109 -0
  178. package/src/logger-middleware/README.md +132 -0
  179. package/src/method-override-middleware/README.md +71 -0
  180. package/src/mime/README.md +117 -0
  181. package/src/multipart-parser/README.md +241 -0
  182. package/src/node-fetch-server/README.md +377 -0
  183. package/src/node-hmr/README.md +307 -0
  184. package/src/node-hmr/runtime.ts +2 -0
  185. package/src/node-hmr/types.d.ts +2 -0
  186. package/{dist/ui/theme.js → src/node-hmr.ts} +1 -1
  187. package/src/node-tsx/README.md +74 -0
  188. package/src/node-tsx/load-module.ts +2 -0
  189. package/{dist/node-serve.js → src/node-tsx.ts} +2 -1
  190. package/src/render-middleware/README.md +99 -0
  191. package/src/render-middleware.ts +2 -0
  192. package/src/route-pattern/README.md +424 -0
  193. package/src/route-pattern/href.ts +2 -0
  194. package/src/route-pattern/join.ts +2 -0
  195. package/src/route-pattern/match.ts +2 -0
  196. package/src/session/README.md +171 -0
  197. package/src/session-middleware/README.md +111 -0
  198. package/src/session-storage-memcache/README.md +37 -0
  199. package/src/session-storage-redis/README.md +36 -0
  200. package/src/static-middleware/README.md +89 -0
  201. package/src/tar-parser/README.md +74 -0
  202. package/src/terminal/README.md +92 -0
  203. package/src/test/README.md +500 -0
  204. package/src/ui/README.md +216 -0
  205. package/src/ui/accordion/README.md +202 -0
  206. package/src/ui/accordion/primitives/README.md +202 -0
  207. package/src/ui/accordion/primitives.ts +2 -0
  208. package/src/ui/anchor/README.md +196 -0
  209. package/src/ui/animation/README.md +316 -0
  210. package/src/ui/breadcrumbs/README.md +55 -0
  211. package/src/ui/button/README.md +44 -0
  212. package/src/ui/button.ts +1 -0
  213. package/src/ui/checkbox/README.md +59 -0
  214. package/src/ui/checkbox.ts +3 -0
  215. package/src/ui/combobox/README.md +194 -0
  216. package/src/ui/combobox/primitives/README.md +194 -0
  217. package/src/ui/combobox/primitives.ts +2 -0
  218. package/src/ui/dev/refresh.ts +2 -0
  219. package/src/ui/input/README.md +52 -0
  220. package/src/ui/input.ts +3 -0
  221. package/src/ui/listbox/README.md +83 -0
  222. package/src/ui/menu/README.md +161 -0
  223. package/src/ui/menu/primitives/README.md +161 -0
  224. package/src/ui/menu/primitives.ts +2 -0
  225. package/src/ui/popover/README.md +103 -0
  226. package/src/ui/radio/README.md +53 -0
  227. package/src/ui/radio.ts +3 -0
  228. package/src/ui/select/README.md +117 -0
  229. package/src/ui/select/primitives/README.md +117 -0
  230. package/src/ui/select/primitives.ts +2 -0
  231. package/src/ui/server/README.md +103 -0
  232. package/src/ui/tabs/README.md +141 -0
  233. package/src/ui/tabs/primitives/README.md +141 -0
  234. package/src/ui/tabs/primitives.ts +2 -0
  235. package/src/ui/tabs.ts +2 -0
  236. package/src/ui/test/README.md +198 -0
  237. package/src/ui/toggle/README.md +56 -0
  238. package/src/ui/toggle/primitives/README.md +56 -0
  239. package/src/ui/toggle/primitives.ts +2 -0
  240. package/src/ui/toggle.ts +3 -0
  241. package/src/ui-hmr/README.md +119 -0
  242. package/src/ui-hmr/assets.ts +2 -0
  243. package/src/ui-hmr/node.ts +3 -0
  244. package/src/ui-hmr/runtime/browser.ts +2 -0
  245. package/src/ui-hmr/runtime/server.ts +2 -0
  246. package/src/ui-hmr.ts +2 -0
  247. package/dist/node-serve.d.ts +0 -2
  248. package/dist/node-serve.d.ts.map +0 -1
  249. package/dist/ui/glyph.d.ts +0 -2
  250. package/dist/ui/glyph.d.ts.map +0 -1
  251. package/dist/ui/scroll-lock.d.ts +0 -2
  252. package/dist/ui/scroll-lock.d.ts.map +0 -1
  253. package/dist/ui/separator.d.ts +0 -2
  254. package/dist/ui/separator.d.ts.map +0 -1
  255. package/dist/ui/theme.d.ts +0 -2
  256. package/dist/ui/theme.d.ts.map +0 -1
  257. package/src/ui/scroll-lock.ts +0 -2
  258. package/src/ui/separator.ts +0 -2
@@ -0,0 +1,246 @@
1
+ # auth-middleware
2
+
3
+ Request-time authentication and route protection for Remix. Use this package to resolve identity into `context.auth` from sessions, bearer tokens, API keys, or your own schemes. Pair it with [`remix/auth`](https://github.com/remix-run/remix/tree/main/packages/auth) when you need browser login routes that call `verifyCredentials()` or `finishExternalAuth()`, then rotate the session id with `completeAuth()` before writing the auth record.
4
+
5
+ ## Features
6
+
7
+ - Request auth resolution without mutating request objects
8
+ - Route protection with `requireAuth()` and configurable failure behavior
9
+ - Built-in auth schemes for sessions, bearer tokens, and API keys
10
+ - Ordered fallback across multiple auth schemes
11
+ - Public and private route support with the same resolved auth state
12
+ - Read auth state with `context.auth` (or `context.get(Auth)`)
13
+ - Designed to pair with browser login flows that persist session auth records earlier in the request lifecycle
14
+
15
+ ## Installation
16
+
17
+ ```sh
18
+ npm i remix
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ The following example shows the request-time half of a session-backed browser login flow:
24
+
25
+ - another part of the app has already called `completeAuth()` and written `{ userId }` into the returned session
26
+ - `remix/middleware/auth` reads that value, resolves the current user, and protects the dashboard route
27
+
28
+ ```ts
29
+ import { auth, createSessionAuthScheme, requireAuth } from 'remix/middleware/auth'
30
+ import { createRouter } from 'remix/router'
31
+ import { route } from 'remix/routes'
32
+ import { session } from 'remix/middleware/session'
33
+
34
+ let routes = route({
35
+ app: {
36
+ dashboard: '/dashboard',
37
+ },
38
+ })
39
+
40
+ type User = { id: string; email: string }
41
+
42
+ let router = createRouter({
43
+ middleware: [
44
+ session(sessionCookie, sessionStorage),
45
+ auth({
46
+ schemes: [
47
+ createSessionAuthScheme<User, { userId: string }>({
48
+ read(session) {
49
+ return session.get('auth') as { userId: string } | null
50
+ },
51
+ verify(value) {
52
+ return users.getById(value.userId)
53
+ },
54
+ invalidate(session) {
55
+ session.unset('auth')
56
+ },
57
+ }),
58
+ ],
59
+ }),
60
+ ],
61
+ })
62
+
63
+ router.get(routes.app.dashboard, {
64
+ middleware: [requireAuth<User>()],
65
+ handler(context) {
66
+ let auth = context.auth
67
+ if (!auth.ok) {
68
+ return new Response('Unauthorized', { status: 401 })
69
+ }
70
+
71
+ return Response.json({
72
+ id: auth.identity.id,
73
+ email: auth.identity.email,
74
+ method: auth.method,
75
+ })
76
+ },
77
+ })
78
+ ```
79
+
80
+ In this example, `createSessionAuthScheme()` turns a persisted session auth record back into request auth state, `auth()` stores that state at `context.auth` (or `context.get(Auth)`), and `requireAuth()` rejects anonymous requests.
81
+
82
+ If you need to create the login route, start an OAuth redirect, finish a provider callback, or write the session auth record in the first place, use [`remix/auth`](https://github.com/remix-run/remix/tree/main/packages/auth):
83
+
84
+ - `verifyCredentials()` for direct credentials flows
85
+ - `startExternalAuth()` and `finishExternalAuth()` for OAuth and OIDC flows
86
+ - `completeAuth()` to rotate the session id before writing the auth record that this package reads later
87
+
88
+ ## Route Protection
89
+
90
+ This package includes two middlewares:
91
+
92
+ - `auth()` to resolve auth state and store it in `context.auth` (or `context.get(Auth)`)
93
+ - `requireAuth()` to reject requests that aren't authenticated
94
+
95
+ That separation is intentional so the same auth resolution can support public routes, API routes, and browser routes with different failure behavior.
96
+
97
+ `auth()` resolves auth state and stores either `{ ok: true, identity, method }` or `{ ok: false, error? }` in `context.auth`.
98
+
99
+ Use `requireAuth()` after `auth()` when a route must be authenticated. If `auth()` did not run first, `requireAuth()` throws. Otherwise it returns `401 Unauthorized` by default, or you can replace that with `onFailure(context, auth)` to return JSON, redirects, or any other custom response.
100
+
101
+ Handlers whose context contract includes `auth()` and `requireAuth<Identity>()` can read `context.auth.identity` without a manual lookup or cast. You can also use `context.get(Auth)`.
102
+
103
+ Auth challenges are forwarded to `WWW-Authenticate` automatically when the auth failure included a `challenge`, so clients that honor those challenges can react without custom header handling.
104
+
105
+ ## Auth Schemes
106
+
107
+ An `AuthScheme` is any object with a `name` and an `authenticate(context)` method. The `auth()` middleware tries each scheme in order until one returns a success or failure result. If no scheme returns success or failure, the request is treated as anonymous.
108
+
109
+ This package ships with three built-in auth schemes:
110
+
111
+ - `createBearerTokenAuthScheme()` for bearer tokens in the [HTTP `Authorization: Bearer <token>` header](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1)
112
+ - `createAPIAuthScheme()` for API keys in a custom request header
113
+ - `createSessionAuthScheme()` for session-backed auth loaded by [a `session()` middleware](https://github.com/remix-run/remix/tree/main/packages/session-middleware)
114
+
115
+ ## Custom Auth Schemes
116
+
117
+ If none of the built-in auth schemes match your environment, you can create your own auth scheme easily. A custom scheme usually wraps one auth mechanism behind a small `create*` factory function and returns an `AuthScheme`. For example, apps behind a trusted access proxy can authenticate requests from forwarded identity headers instead of sessions or bearer tokens.
118
+
119
+ ```ts
120
+ import type { RequestContext } from 'remix/router'
121
+ import type { AuthScheme } from 'remix/middleware/auth'
122
+
123
+ type User = {
124
+ id: string
125
+ role: 'admin' | 'user'
126
+ }
127
+
128
+ function createTrustedProxyAuthScheme(): AuthScheme<User> {
129
+ return {
130
+ name: 'trusted-proxy',
131
+ async authenticate(context: RequestContext) {
132
+ let email = context.headers.get('X-Forwarded-Email')
133
+
134
+ if (email == null) {
135
+ return
136
+ }
137
+
138
+ let user = await users.getByEmail(email)
139
+
140
+ if (user == null) {
141
+ return {
142
+ status: 'failure',
143
+ code: 'invalid_credentials',
144
+ message: 'Unknown forwarded user',
145
+ }
146
+ }
147
+
148
+ return {
149
+ status: 'success',
150
+ identity: user,
151
+ }
152
+ },
153
+ }
154
+ }
155
+ ```
156
+
157
+ Only use a scheme like this when the app is reachable exclusively through infrastructure you trust to set the headers you rely on. In this case, the `X-Forwarded-Email` header.
158
+
159
+ `authenticate(context)` can return:
160
+
161
+ - `null`, `undefined`, or no return value to skip this scheme
162
+ - `{ status: 'success', identity }` to authenticate the request
163
+ - `{ status: 'failure', code?, message?, challenge? }` to stop with an auth error
164
+
165
+ The scheme `name` becomes `auth.method` when authentication succeeds.
166
+
167
+ ## Simple Auth Cookies
168
+
169
+ If your app already has an auth cookie and you do not need a session-backed identity lookup, you can use a small custom auth scheme and still rely on `requireAuth()` for route protection.
170
+
171
+ ```ts
172
+ import { auth, requireAuth } from 'remix/middleware/auth'
173
+ import type { AuthScheme } from 'remix/middleware/auth'
174
+ import { createCookie } from 'remix/cookie'
175
+ import { createRouter } from 'remix/router'
176
+ import { redirect } from 'remix/response/redirect'
177
+
178
+ let authCookie = createCookie('__auth', {
179
+ httpOnly: true,
180
+ sameSite: 'lax',
181
+ path: '/',
182
+ })
183
+
184
+ let authCookieScheme: AuthScheme<'demo-user'> = {
185
+ name: 'auth-cookie',
186
+ async authenticate(context) {
187
+ let value = await authCookie.parse(context.headers.get('Cookie'))
188
+ if (value !== '1') {
189
+ return
190
+ }
191
+
192
+ return {
193
+ status: 'success',
194
+ identity: 'demo-user',
195
+ }
196
+ },
197
+ }
198
+
199
+ let requireAuthCookie = requireAuth<'demo-user'>({
200
+ onFailure(context) {
201
+ let isFrameRequest = context.request.headers.get('X-Remix-Frame') === 'true'
202
+ if (isFrameRequest) {
203
+ return new Response('<p>Not authorized</p>', {
204
+ status: 401,
205
+ headers: {
206
+ 'Content-Type': 'text/html; charset=utf-8',
207
+ },
208
+ })
209
+ }
210
+
211
+ return redirect('/login')
212
+ },
213
+ })
214
+
215
+ let router = createRouter({
216
+ middleware: [
217
+ auth({
218
+ schemes: [authCookieScheme],
219
+ }),
220
+ ],
221
+ })
222
+
223
+ router.get('/dashboard', {
224
+ middleware: [requireAuthCookie],
225
+ handler() {
226
+ return new Response('ok')
227
+ },
228
+ })
229
+ ```
230
+
231
+ This pattern keeps the auth check app-owned. Use [`remix/middleware/session`](https://github.com/remix-run/remix/tree/main/packages/session-middleware) and [`remix/auth`](https://github.com/remix-run/remix/tree/main/packages/auth) when you need server-managed session data, credential verification helpers, or OAuth/OIDC flows.
232
+
233
+ ## Related Packages
234
+
235
+ - [`auth`](https://github.com/remix-run/remix/tree/main/packages/auth) - Browser auth primitives for credentials, OAuth, and OIDC flows
236
+ - [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Router and middleware runtime
237
+ - [`response`](https://github.com/remix-run/remix/tree/main/packages/response) - Response helpers like redirects
238
+
239
+ ## Related Work
240
+
241
+ - [HTTP Authentication Framework](https://datatracker.ietf.org/doc/html/rfc7235)
242
+ - [OAuth 2.0 Bearer Token Usage](https://datatracker.ietf.org/doc/html/rfc6750)
243
+
244
+ ## License
245
+
246
+ See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
@@ -0,0 +1,182 @@
1
+ # cli
2
+
3
+ Command-line interface for creating and managing Remix projects.
4
+
5
+ ## Features
6
+
7
+ - Create new Remix projects with `npx remix@next new` or installed `remix new`
8
+ - Print shell completion scripts with `remix completion`
9
+ - Check project environment and Remix app conventions with `remix doctor`
10
+ - Apply available low-risk project fixes with `remix doctor --fix`
11
+ - Manage the current app database with `remix db`
12
+ - Inspect the current app route tree with `remix routes`
13
+ - Run project tests with `remix test`
14
+ - Configure commands with a static, commented `remix.json` file
15
+ - Print the current Remix version with `remix version`
16
+ - Use the same CLI through the `remix` package or the `remix/cli` API
17
+ - Scaffold a starter app that matches the Remix project layout conventions
18
+
19
+ ## Installation
20
+
21
+ Use `npx remix@next new <target-dir>` to scaffold a new Remix app. Install `remix` when you want the local `remix` command:
22
+
23
+ ```sh
24
+ npm i remix
25
+ ```
26
+
27
+ ## Shell completion
28
+
29
+ Install bash completion:
30
+
31
+ ```sh
32
+ remix completion bash >> ~/.bashrc
33
+ ```
34
+
35
+ Install zsh completion:
36
+
37
+ ```sh
38
+ remix completion zsh >> ~/.zshrc
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ Use `npx remix@next new my-remix-app` to scaffold a new Remix app. After installing Remix, the equivalent local command is `remix new my-remix-app`.
44
+
45
+ The rest of the CLI is available through the installed `remix` command:
46
+
47
+ ```sh
48
+ remix new my-remix-app
49
+ remix completion bash >> ~/.bashrc
50
+ remix doctor
51
+ remix doctor --fix
52
+ remix db migrate
53
+ remix db status
54
+ remix db reset --force
55
+ remix routes
56
+ remix routes --table
57
+ remix routes --table --no-headers
58
+ remix test
59
+ remix version
60
+ remix --no-color doctor
61
+ ```
62
+
63
+ You can also run the CLI programmatically:
64
+
65
+ ```ts
66
+ import { runRemix } from 'remix/cli'
67
+
68
+ await runRemix(['new', 'my-remix-app'])
69
+ await runRemix(['completion', 'bash'])
70
+ await runRemix(['doctor'])
71
+ await runRemix(['doctor', '--fix'])
72
+ await runRemix(['db', 'migrate'])
73
+ await runRemix(['db', 'status'])
74
+ await runRemix(['db', 'reset', '--force'])
75
+ await runRemix(['routes'])
76
+ await runRemix(['routes', '--table'])
77
+ await runRemix(['routes', '--table', '--no-headers'])
78
+ await runRemix(['test'])
79
+ await runRemix(['version'])
80
+ ```
81
+
82
+ Destructive database commands (`remix db wipe` and `remix db reset`) refuse to run without `--force`.
83
+
84
+ `runRemix()` returns the CLI exit code as a promise.
85
+
86
+ ## Configuration
87
+
88
+ The CLI loads an optional `remix.json`. The file is parsed as JSONC, so it may contain comments and
89
+ trailing commas. Every top-level field is optional:
90
+
91
+ ```jsonc
92
+ {
93
+ "$schema": "https://remix.run/schemas/remix.json",
94
+
95
+ "db": {
96
+ "adapter": {
97
+ "type": "sqlite",
98
+ "filename": { "env": "DATABASE_URL", "default": "./db/app.sqlite" },
99
+ "foreignKeys": true,
100
+ "busyTimeout": 5000,
101
+ },
102
+ "migrations": {
103
+ "directory": "./db/migrations",
104
+ "journalTable": "data_table_migrations",
105
+ },
106
+ "seed": "./db/seed.sql",
107
+ },
108
+
109
+ "doctor": {
110
+ "strict": true,
111
+ },
112
+
113
+ "test": {
114
+ // Test discovery
115
+ "files": ["**/*.test{,.browser,.e2e}.{ts,tsx}"],
116
+ "browserFiles": ["**/*.test.browser.{ts,tsx}"],
117
+ "e2eFiles": ["**/*.test.e2e.{ts,tsx}"],
118
+ "exclude": ["node_modules/**", "dist/**"],
119
+ "type": ["server", "browser", "e2e"],
120
+ "only": ["/checkout/i"],
121
+
122
+ // Test execution
123
+ "concurrency": 4,
124
+ "pool": "forks",
125
+ "setup": "./test/setup.ts",
126
+ "watch": false,
127
+
128
+ // Playwright
129
+ "playwright": {
130
+ "echo": false,
131
+ "open": false,
132
+ "configFile": "./playwright.config.ts",
133
+ "projects": ["chromium", "firefox"],
134
+ },
135
+
136
+ // Output
137
+ "reporter": "spec",
138
+ "quiet": false,
139
+
140
+ // Coverage
141
+ "coverage": {
142
+ "enabled": true,
143
+ "dir": ".coverage",
144
+ "include": ["app/**"],
145
+ "exclude": ["**/*.test.*"],
146
+ "branches": 80,
147
+ "functions": 80,
148
+ "lines": 80,
149
+ "statements": 80,
150
+ },
151
+ },
152
+ }
153
+ ```
154
+
155
+ Explicit command flags and positional arguments override configured values. Repeated flags replace
156
+ configured arrays, while nested Playwright and coverage settings merge by field. Relative paths and
157
+ globs are resolved from the directory containing the config file. Use `remix doctor --no-strict` to
158
+ disable configured strict mode for one run.
159
+
160
+ `remix db` requires `db.adapter`. Adapters use `type: "sqlite"`, `type: "postgres"`, or
161
+ `type: "mysql"`; PostgreSQL uses `connectionString` and MySQL uses `uri`. A connection value may be
162
+ a string or an object naming an environment variable with an optional default. `db.seed` names a
163
+ SQL file that `remix db seed` and `remix db reset` run against the database. Database flags such as
164
+ `--migrations`, `--seed`, `--journal-table`, and `--connection-env` override the corresponding
165
+ config for one invocation. When no global `--config` is provided, database commands find the
166
+ nearest `remix.json` by walking up from the working directory.
167
+
168
+ Use the global `--config` option to select another JSONC file. The option itself is resolved from the
169
+ CLI working directory and may appear before or after the command:
170
+
171
+ ```sh
172
+ remix --config ./config/remix.ci.json test
173
+ remix test --config ./config/remix.ci.json
174
+ ```
175
+
176
+ A missing default `remix.json` is ignored. A missing explicitly selected file, malformed JSONC,
177
+ unknown property, or invalid value is reported as a CLI error. The optional `$schema` field enables
178
+ editor completion and validation; it has no runtime effect.
179
+
180
+ ## License
181
+
182
+ See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
@@ -0,0 +1,176 @@
1
+ # compression-middleware
2
+
3
+ Response compression middleware for Remix. It negotiates `br`, `gzip`, and `deflate` from `Accept-Encoding` and applies sensible defaults for when compression is useful.
4
+
5
+ ## Features
6
+
7
+ - **Encoding Negotiation** - Selects the best supported encoding from `Accept-Encoding`
8
+ - **Compression Guards** - Skips already-compressed responses and range-enabled responses
9
+ - **Size Thresholds** - Configurable minimum response size for compression
10
+ - **MIME Filtering** - Compresses only content types likely to benefit
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ npm i remix
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { createRouter } from 'remix/router'
22
+ import { compression } from 'remix/middleware/compression'
23
+
24
+ let router = createRouter({
25
+ middleware: [compression()],
26
+ })
27
+ ```
28
+
29
+ The middleware will automatically compress responses for compressible MIME types when:
30
+
31
+ - The client supports compression (`Accept-Encoding` header with a supported encoding)
32
+ - The response is large enough to benefit from compression (≥1024 bytes if `Content-Length` is present, by default)
33
+ - The response hasn't already been compressed
34
+ - The response doesn't advertise range support (`Accept-Ranges: bytes`)
35
+
36
+ ### Threshold
37
+
38
+ **Default:** `1024` (only enforced if `Content-Length` is present)
39
+
40
+ Set the minimum response size in bytes to compress:
41
+
42
+ ```ts
43
+ import { createRouter } from 'remix/router'
44
+ import { compression } from 'remix/middleware/compression'
45
+
46
+ let router = createRouter({
47
+ middleware: [
48
+ compression({
49
+ threshold: 2048, // Only compress responses ≥2KB
50
+ }),
51
+ ],
52
+ })
53
+ ```
54
+
55
+ ### Encodings
56
+
57
+ **Default:** `['br', 'gzip', 'deflate']`
58
+
59
+ Customize which compression algorithms to support:
60
+
61
+ ```ts
62
+ import { createRouter } from 'remix/router'
63
+ import { compression } from 'remix/middleware/compression'
64
+
65
+ let router = createRouter({
66
+ middleware: [
67
+ compression({
68
+ encodings: ['br', 'gzip'], // Only use Brotli and Gzip
69
+ }),
70
+ ],
71
+ })
72
+ ```
73
+
74
+ The `encodings` option can also be a function that receives the response:
75
+
76
+ ```ts
77
+ import { createRouter } from 'remix/router'
78
+ import { compression } from 'remix/middleware/compression'
79
+
80
+ let router = createRouter({
81
+ middleware: [
82
+ compression({
83
+ encodings: (response) => {
84
+ // Use different encodings for server-sent events
85
+ let contentType = response.headers.get('Content-Type')
86
+ return contentType?.startsWith('text/event-stream;')
87
+ ? ['gzip', 'deflate']
88
+ : ['br', 'gzip', 'deflate']
89
+ },
90
+ }),
91
+ ],
92
+ })
93
+ ```
94
+
95
+ ### Filter Media Type
96
+
97
+ **Default:** Uses `isCompressibleMimeType()` from [`remix/mime`](https://github.com/remix-run/remix/tree/main/packages/mime)
98
+
99
+ You can customize this behavior with the `filterMediaType` option:
100
+
101
+ ```ts
102
+ import { createRouter } from 'remix/router'
103
+ import { compression } from 'remix/middleware/compression'
104
+ import { isCompressibleMimeType } from 'remix/mime'
105
+
106
+ let router = createRouter({
107
+ middleware: [
108
+ compression({
109
+ filterMediaType(mediaType) {
110
+ // Add a custom media type to the default compressible list
111
+ return isCompressibleMimeType(mediaType) || mediaType === 'application/vnd.example+data'
112
+ },
113
+ }),
114
+ ],
115
+ })
116
+ ```
117
+
118
+ ### Compression Options
119
+
120
+ **Default:** Uses Node.js defaults for [zlib](https://nodejs.org/api/zlib.html#class-options) and [Brotli](https://nodejs.org/api/zlib.html#class-brotlioptions), with automatic flush handling for server-sent events.
121
+
122
+ You can pass options options to the underlying Node.js `zlib` and `brotli` compressors for fine-grained control:
123
+
124
+ ```ts
125
+ import { createRouter } from 'remix/router'
126
+ import { compression } from 'remix/middleware/compression'
127
+ import { zlib } from 'node:zlib'
128
+
129
+ let router = createRouter({
130
+ middleware: [
131
+ compression({
132
+ zlib: {
133
+ level: 6,
134
+ },
135
+ brotli: {
136
+ params: {
137
+ [zlib.constants.BROTLI_PARAM_QUALITY]: 4,
138
+ },
139
+ },
140
+ }),
141
+ ],
142
+ })
143
+ ```
144
+
145
+ Like `encodings`, both `zlib` and `brotli` options can also be functions that receive the response:
146
+
147
+ ```ts
148
+ import zlib from 'node:zlib'
149
+ import { createRouter } from 'remix/router'
150
+ import { compression } from 'remix/middleware/compression'
151
+
152
+ let router = createRouter({
153
+ middleware: [
154
+ compression({
155
+ brotli: (response) => {
156
+ let contentType = response.headers.get('Content-Type')
157
+ return {
158
+ params: {
159
+ [zlib.constants.BROTLI_PARAM_QUALITY]: contentType?.startsWith('text/html;') ? 4 : 11,
160
+ },
161
+ }
162
+ },
163
+ }),
164
+ ],
165
+ })
166
+ ```
167
+
168
+ ## Related Packages
169
+
170
+ - [`remix/router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Router for the web Fetch API
171
+ - [`remix/mime`](https://github.com/remix-run/remix/tree/main/packages/mime) - MIME type utilities
172
+ - [`remix/response`](https://github.com/remix-run/remix/tree/main/packages/response) - Response helpers
173
+
174
+ ## License
175
+
176
+ MIT