dineway 0.1.4 → 0.1.6

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 (193) hide show
  1. package/README.md +6 -3
  2. package/dist/{apply-CAPvMfoU.mjs → apply-iVSqz2qs.mjs} +132 -39
  3. package/dist/astro/index.d.mts +18 -9
  4. package/dist/astro/index.mjs +238 -16
  5. package/dist/astro/middleware/auth.d.mts +16 -5
  6. package/dist/astro/middleware/auth.mjs +74 -37
  7. package/dist/astro/middleware/redirect.mjs +24 -8
  8. package/dist/astro/middleware/request-context.mjs +18 -5
  9. package/dist/astro/middleware/setup.mjs +1 -1
  10. package/dist/astro/middleware.mjs +411 -169
  11. package/dist/astro/types.d.mts +25 -8
  12. package/dist/{byline-DeWCMU_i.mjs → byline-OhH2dlRu.mjs} +6 -21
  13. package/dist/{bylines-DyqBV9EQ.mjs → bylines-BGpD9_hy.mjs} +16 -6
  14. package/dist/cache-BdSY-gQN.mjs +42 -0
  15. package/dist/chunks--4F8ddV4.mjs +18 -0
  16. package/dist/cli/index.mjs +935 -15
  17. package/dist/client/external-auth-headers.d.mts +1 -1
  18. package/dist/client/index.d.mts +11 -3
  19. package/dist/client/index.mjs +4 -3
  20. package/dist/{connection-C9pxzuag.mjs → connection-BCNICDWN.mjs} +22 -5
  21. package/dist/{content-zSgdNmnt.mjs → content-DWi4d0rT.mjs} +41 -2
  22. package/dist/database/instrumentation.d.mts +34 -0
  23. package/dist/database/instrumentation.mjs +53 -0
  24. package/dist/db/index.d.mts +3 -3
  25. package/dist/db/index.mjs +2 -2
  26. package/dist/db/libsql.d.mts +1 -1
  27. package/dist/db/libsql.mjs +11 -5
  28. package/dist/db/postgres.d.mts +1 -1
  29. package/dist/db/sqlite.d.mts +1 -1
  30. package/dist/db/sqlite.mjs +7 -1
  31. package/dist/db-errors-CEqD7qH9.mjs +23 -0
  32. package/dist/{default-WYlzADZL.mjs → default-VjJyuuG9.mjs} +2 -0
  33. package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +3 -0
  34. package/dist/{error-DrxtnGPg.mjs → error-BmL6QipT.mjs} +7 -3
  35. package/dist/{index-C-jx21qs.d.mts → index-yvc6E_17.d.mts} +157 -30
  36. package/dist/index.d.mts +11 -11
  37. package/dist/index.mjs +24 -22
  38. package/dist/{loader-qKmo0wAY.mjs → loader-sMG4TZ-u.mjs} +9 -3
  39. package/dist/media/index.d.mts +1 -1
  40. package/dist/media/index.mjs +1 -1
  41. package/dist/media/local-runtime.d.mts +7 -7
  42. package/dist/page/index.d.mts +10 -2
  43. package/dist/page/index.mjs +22 -1
  44. package/dist/patterns-CrCYkMBb.mjs +92 -0
  45. package/dist/{placeholder-bOx1xCTY.d.mts → placeholder--wOi4TbO.d.mts} +1 -1
  46. package/dist/{placeholder-B3knXwNc.mjs → placeholder-Cp8g5Emj.mjs} +1 -1
  47. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  48. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  49. package/dist/{query-BiaPl_g2.mjs → query-kDmwCsHh.mjs} +118 -50
  50. package/dist/{redirect-JPqLAbxa.mjs → redirect-DnEWAkVg.mjs} +43 -99
  51. package/dist/{registry-DSd1GWB8.mjs → registry-C0zjeB9P.mjs} +191 -123
  52. package/dist/request-cache-Dk5qPSOx.mjs +66 -0
  53. package/dist/request-context.d.mts +4 -16
  54. package/dist/{runner-B5l1JfOj.d.mts → runner-CFI6B6J2.d.mts} +1 -1
  55. package/dist/{runner-BGUGywgG.mjs → runner-DWZm2KQm.mjs} +589 -137
  56. package/dist/runtime.d.mts +6 -6
  57. package/dist/runtime.mjs +2 -2
  58. package/dist/{search-BNruJHDL.mjs → search-BApX1xhM.mjs} +570 -424
  59. package/dist/seed/index.d.mts +2 -2
  60. package/dist/seed/index.mjs +11 -10
  61. package/dist/seo/index.d.mts +1 -1
  62. package/dist/storage/local.d.mts +1 -1
  63. package/dist/storage/local.mjs +1 -1
  64. package/dist/storage/s3.d.mts +11 -3
  65. package/dist/storage/s3.mjs +78 -15
  66. package/dist/taxonomies-1s5PaS_8.mjs +266 -0
  67. package/dist/transaction-Cn2rjY78.mjs +27 -0
  68. package/dist/{types-BgQeVaPj.d.mts → types-BuMDPy5C.d.mts} +52 -3
  69. package/dist/{types-DuNbGKjF.mjs → types-COeOq9nK.mjs} +6 -1
  70. package/dist/{types-ju-_ORz7.d.mts → types-CWbdtiux.d.mts} +13 -5
  71. package/dist/{types-D38djUXv.d.mts → types-Cj0KMIZV.d.mts} +16 -3
  72. package/dist/{types-DkvMXalq.d.mts → types-DOrVigru.d.mts} +159 -0
  73. package/dist/{validate-CXnRKfJK.mjs → validate-BZ5wnLLp.mjs} +2 -1
  74. package/dist/{validate-DVKJJ-M_.d.mts → validate-IPf8n4Fj.d.mts} +4 -51
  75. package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +10 -10
  76. package/dist/version-hmtC3Cmv.mjs +6 -0
  77. package/package.json +49 -38
  78. package/src/astro/routes/admin.astro +25 -9
  79. package/src/astro/routes/api/admin/api-tokens/[id].ts +4 -0
  80. package/src/astro/routes/api/admin/api-tokens/index.ts +24 -2
  81. package/src/astro/routes/api/admin/briefing.ts +76 -0
  82. package/src/astro/routes/api/admin/bylines/[id]/index.ts +3 -0
  83. package/src/astro/routes/api/admin/bylines/index.ts +2 -0
  84. package/src/astro/routes/api/admin/context/[id]/history.ts +35 -0
  85. package/src/astro/routes/api/admin/context/[id]/index.ts +35 -0
  86. package/src/astro/routes/api/admin/context/[id]/review.ts +57 -0
  87. package/src/astro/routes/api/admin/context/[id]/supersede.ts +58 -0
  88. package/src/astro/routes/api/admin/context/diff.ts +35 -0
  89. package/src/astro/routes/api/admin/context/index.ts +69 -0
  90. package/src/astro/routes/api/admin/context/stale.ts +35 -0
  91. package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +38 -0
  92. package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +54 -0
  93. package/src/astro/routes/api/admin/hitl-requests/index.ts +38 -0
  94. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +58 -17
  95. package/src/astro/routes/api/admin/oauth-clients/[id].ts +28 -1
  96. package/src/astro/routes/api/admin/oauth-clients/index.ts +25 -1
  97. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +54 -2
  98. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +54 -2
  99. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +51 -1
  100. package/src/astro/routes/api/admin/plugins/[id]/update.ts +98 -3
  101. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +72 -1
  102. package/src/astro/routes/api/admin/review-requests/[id]/index.ts +35 -0
  103. package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +52 -0
  104. package/src/astro/routes/api/admin/review-requests/index.ts +35 -0
  105. package/src/astro/routes/api/admin/users/[id]/disable.ts +26 -23
  106. package/src/astro/routes/api/admin/users/[id]/index.ts +41 -21
  107. package/src/astro/routes/api/auth/invite/register-options.ts +73 -0
  108. package/src/astro/routes/api/auth/magic-link/send.ts +2 -1
  109. package/src/astro/routes/api/auth/passkey/options.ts +2 -1
  110. package/src/astro/routes/api/auth/passkey/verify.ts +5 -1
  111. package/src/astro/routes/api/auth/signup/request.ts +20 -8
  112. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +3 -4
  113. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +1 -1
  114. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +16 -2
  115. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +16 -0
  116. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +9 -0
  117. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +1 -1
  118. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +45 -1
  119. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +12 -2
  120. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +1 -1
  121. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +24 -0
  122. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +3 -0
  123. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +20 -0
  124. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +13 -0
  125. package/src/astro/routes/api/content/[collection]/[id].ts +36 -0
  126. package/src/astro/routes/api/content/[collection]/index.ts +48 -4
  127. package/src/astro/routes/api/content/[collection]/trash.ts +1 -1
  128. package/src/astro/routes/api/health.ts +54 -0
  129. package/src/astro/routes/api/import/wordpress/analyze.ts +2 -10
  130. package/src/astro/routes/api/import/wordpress/execute.ts +40 -6
  131. package/src/astro/routes/api/import/wordpress/prepare.ts +36 -5
  132. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +33 -1
  133. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +3 -3
  134. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +57 -15
  135. package/src/astro/routes/api/manifest.ts +13 -1
  136. package/src/astro/routes/api/mcp.ts +1 -0
  137. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +7 -2
  138. package/src/astro/routes/api/media/upload-url.ts +11 -2
  139. package/src/astro/routes/api/media.ts +9 -7
  140. package/src/astro/routes/api/menus/[name]/items.ts +124 -5
  141. package/src/astro/routes/api/menus/[name]/reorder.ts +47 -1
  142. package/src/astro/routes/api/menus/[name].ts +84 -4
  143. package/src/astro/routes/api/menus/index.ts +46 -2
  144. package/src/astro/routes/api/oauth/authorize.ts +21 -8
  145. package/src/astro/routes/api/oauth/device/code.ts +2 -1
  146. package/src/astro/routes/api/oauth/device/token.ts +2 -1
  147. package/src/astro/routes/api/oauth/register.ts +182 -0
  148. package/src/astro/routes/api/oauth/token.ts +18 -7
  149. package/src/astro/routes/api/openapi.json.ts +3 -2
  150. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +21 -4
  151. package/src/astro/routes/api/redirects/[id].ts +103 -4
  152. package/src/astro/routes/api/redirects/index.ts +50 -2
  153. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +28 -0
  154. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +15 -0
  155. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +13 -0
  156. package/src/astro/routes/api/schema/collections/[slug]/index.ts +27 -0
  157. package/src/astro/routes/api/schema/collections/index.ts +14 -0
  158. package/src/astro/routes/api/search/index.ts +1 -0
  159. package/src/astro/routes/api/search/suggest.ts +1 -0
  160. package/src/astro/routes/api/sections/[slug].ts +123 -4
  161. package/src/astro/routes/api/sections/index.ts +57 -2
  162. package/src/astro/routes/api/settings.ts +51 -2
  163. package/src/astro/routes/api/setup/admin-verify.ts +25 -5
  164. package/src/astro/routes/api/setup/admin.ts +16 -8
  165. package/src/astro/routes/api/setup/index.ts +3 -2
  166. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +141 -4
  167. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +64 -2
  168. package/src/astro/routes/api/taxonomies/index.ts +57 -2
  169. package/src/astro/routes/api/well-known/auth.ts +3 -1
  170. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +8 -5
  171. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +3 -2
  172. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +58 -16
  173. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +124 -38
  174. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +66 -20
  175. package/src/astro/routes/api/widget-areas/[name].ts +55 -7
  176. package/src/astro/routes/api/widget-areas/index.ts +56 -6
  177. package/src/components/DinewayHead.astro +15 -7
  178. package/src/components/DinewayMedia.astro +1 -1
  179. package/src/components/InlinePortableTextEditor.tsx +1 -1
  180. package/src/components/Table.astro +68 -41
  181. package/src/components/index.ts +2 -12
  182. package/src/components/marks.ts +19 -0
  183. package/LICENSE +0 -9
  184. /package/dist/{adapters-BlzWJG82.d.mts → adapters-C2ypTrZZ.d.mts} +0 -0
  185. /package/dist/{config-Cq8H0SfX.mjs → config-BXwuX8Bx.mjs} +0 -0
  186. /package/dist/{load-C6FCD1FU.mjs → load-Coc9HpHH.mjs} +0 -0
  187. /package/dist/{manifest-schema-CTSEyIJ3.mjs → manifest-schema-D1MSVnoI.mjs} +0 -0
  188. /package/dist/{mode-BlyYtIFO.mjs → mode-47goXBBK.mjs} +0 -0
  189. /package/dist/{tokens-4vgYuXsZ.mjs → tokens-CJz9ubV6.mjs} +0 -0
  190. /package/dist/{transport-C5FYnid7.mjs → transport-DB5eDN4x.mjs} +0 -0
  191. /package/dist/{transport-gIL-e43D.d.mts → transport-Wge_IzKl.d.mts} +0 -0
  192. /package/dist/{types-CLLdsG3g.d.mts → types-BzcUjoqg.d.mts} +0 -0
  193. /package/dist/{types-DShnjzb6.mjs → types-griIBQOQ.mjs} +0 -0
@@ -1,7 +1,9 @@
1
- import { t as defaultSeed } from "../default-WYlzADZL.mjs";
1
+ import { n as VERSION, t as COMMIT } from "../version-hmtC3Cmv.mjs";
2
+ import { t as defaultSeed } from "../default-VjJyuuG9.mjs";
2
3
  import { createRequire } from "node:module";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  import { dirname, resolve } from "node:path";
6
+ import { fontProviders } from "astro/config";
5
7
  import { fileURLToPath } from "node:url";
6
8
 
7
9
  //#region src/astro/storage/adapters.ts
@@ -9,19 +11,28 @@ import { fileURLToPath } from "node:url";
9
11
  * S3-compatible storage adapter
10
12
  *
11
13
  * Works with AWS S3, MinIO, and other S3-compatible object stores.
14
+ * Any omitted field is resolved from the matching `S3_*` environment
15
+ * variable when the Node process starts. Explicit values take precedence.
12
16
  *
13
17
  * @example
14
18
  * ```ts
19
+ * // All fields from runtime env
20
+ * storage: s3()
21
+ *
22
+ * // Mix explicit CDN config with endpoint/bucket/credentials from env
23
+ * storage: s3({ publicUrl: "https://cdn.example.com" })
24
+ *
25
+ * // All explicit values
15
26
  * storage: s3({
16
27
  * endpoint: process.env.S3_ENDPOINT!,
17
28
  * bucket: "media",
18
- * accessKeyId: process.env.S3_ACCESS_KEY_ID!,
19
- * secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
29
+ * accessKeyId: process.env.S3_ACCESS_KEY_ID,
30
+ * secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
20
31
  * publicUrl: "https://cdn.example.com", // optional CDN
21
32
  * })
22
33
  * ```
23
34
  */
24
- function s3(config) {
35
+ function s3(config = {}) {
25
36
  return {
26
37
  entrypoint: "dineway/storage/s3",
27
38
  config
@@ -48,6 +59,118 @@ function local(config) {
48
59
  };
49
60
  }
50
61
 
62
+ //#endregion
63
+ //#region src/astro/integration/font-provider.ts
64
+ /**
65
+ * Dineway Noto Sans font provider.
66
+ *
67
+ * Wraps Google Fonts so the base Noto Sans family and optional
68
+ * script-specific Noto Sans families share one logical font entry.
69
+ * The browser then chooses the right file per character via unicode-range.
70
+ */
71
+ /**
72
+ * All subset names used by Google Fonts CSS responses.
73
+ * Passed when resolving extra script families so the unifont provider does
74
+ * not filter out faces for those families.
75
+ */
76
+ const ALL_GOOGLE_SUBSETS = [
77
+ "arabic",
78
+ "armenian",
79
+ "bengali",
80
+ "chinese-simplified",
81
+ "chinese-traditional",
82
+ "chinese-hongkong",
83
+ "cyrillic",
84
+ "cyrillic-ext",
85
+ "devanagari",
86
+ "ethiopic",
87
+ "farsi",
88
+ "georgian",
89
+ "greek",
90
+ "greek-ext",
91
+ "gujarati",
92
+ "gurmukhi",
93
+ "hebrew",
94
+ "japanese",
95
+ "kannada",
96
+ "khmer",
97
+ "korean",
98
+ "lao",
99
+ "latin",
100
+ "latin-ext",
101
+ "malayalam",
102
+ "math",
103
+ "myanmar",
104
+ "oriya",
105
+ "sinhala",
106
+ "symbols",
107
+ "tamil",
108
+ "telugu",
109
+ "thai",
110
+ "tibetan",
111
+ "vietnamese"
112
+ ];
113
+ /**
114
+ * Known Noto Sans and compatible script families on Google Fonts.
115
+ */
116
+ const NOTO_SCRIPT_FAMILIES = {
117
+ arabic: "Noto Sans Arabic",
118
+ armenian: "Noto Sans Armenian",
119
+ bengali: "Noto Sans Bengali",
120
+ "chinese-simplified": "Noto Sans SC",
121
+ "chinese-traditional": "Noto Sans TC",
122
+ "chinese-hongkong": "Noto Sans HK",
123
+ devanagari: "Noto Sans Devanagari",
124
+ ethiopic: "Noto Sans Ethiopic",
125
+ farsi: "Vazirmatn",
126
+ georgian: "Noto Sans Georgian",
127
+ gujarati: "Noto Sans Gujarati",
128
+ gurmukhi: "Noto Sans Gurmukhi",
129
+ hebrew: "Noto Sans Hebrew",
130
+ japanese: "Noto Sans JP",
131
+ kannada: "Noto Sans Kannada",
132
+ khmer: "Noto Sans Khmer",
133
+ korean: "Noto Sans KR",
134
+ lao: "Noto Sans Lao",
135
+ malayalam: "Noto Sans Malayalam",
136
+ myanmar: "Noto Sans Myanmar",
137
+ oriya: "Noto Sans Oriya",
138
+ sinhala: "Noto Sans Sinhala",
139
+ tamil: "Noto Sans Tamil",
140
+ telugu: "Noto Sans Telugu",
141
+ thai: "Noto Sans Thai",
142
+ tibetan: "Noto Sans Tibetan"
143
+ };
144
+ function notoSans(options) {
145
+ const googleProvider = fontProviders.google();
146
+ return {
147
+ name: "dineway-noto",
148
+ async init(context) {
149
+ await googleProvider.init?.(context);
150
+ },
151
+ async resolveFont(resolveFontOptions) {
152
+ const base = await googleProvider.resolveFont(resolveFontOptions);
153
+ const baseFonts = base?.fonts ?? [];
154
+ if (!options?.scripts?.length) return base;
155
+ const baseSubsets = new Set(baseFonts.map((font) => font.meta?.subset).filter(Boolean));
156
+ const extraFaces = (await Promise.all(options.scripts.map(async (script) => {
157
+ const family = NOTO_SCRIPT_FAMILIES[script];
158
+ if (!family) {
159
+ if (ALL_GOOGLE_SUBSETS.includes(script)) return;
160
+ console.warn(`[dineway] Unknown Noto Sans script "${script}". Available: ${Object.keys(NOTO_SCRIPT_FAMILIES).join(", ")}`);
161
+ return;
162
+ }
163
+ return googleProvider.resolveFont({
164
+ ...resolveFontOptions,
165
+ familyName: family,
166
+ subsets: ALL_GOOGLE_SUBSETS
167
+ });
168
+ }))).flatMap((result) => (result?.fonts ?? []).filter((font) => !font.meta?.subset || !baseSubsets.has(font.meta.subset)));
169
+ return { fonts: [...baseFonts, ...extraFaces] };
170
+ }
171
+ };
172
+ }
173
+
51
174
  //#endregion
52
175
  //#region src/astro/integration/routes.ts
53
176
  /**
@@ -84,6 +207,10 @@ function injectCoreRoutes(injectRoute) {
84
207
  pattern: "/_dineway/api/dashboard",
85
208
  entrypoint: resolveRoute("api/dashboard.ts")
86
209
  });
210
+ injectRoute({
211
+ pattern: "/_dineway/api/health",
212
+ entrypoint: resolveRoute("api/health.ts")
213
+ });
87
214
  injectRoute({
88
215
  pattern: "/_dineway/api/content/[collection]",
89
216
  entrypoint: resolveRoute("api/content/[collection]/index.ts")
@@ -360,6 +487,62 @@ function injectCoreRoutes(injectRoute) {
360
487
  pattern: "/_dineway/api/admin/bylines/[id]",
361
488
  entrypoint: resolveRoute("api/admin/bylines/[id]/index.ts")
362
489
  });
490
+ injectRoute({
491
+ pattern: "/_dineway/api/admin/briefing",
492
+ entrypoint: resolveRoute("api/admin/briefing.ts")
493
+ });
494
+ injectRoute({
495
+ pattern: "/_dineway/api/admin/review-requests",
496
+ entrypoint: resolveRoute("api/admin/review-requests/index.ts")
497
+ });
498
+ injectRoute({
499
+ pattern: "/_dineway/api/admin/review-requests/[id]",
500
+ entrypoint: resolveRoute("api/admin/review-requests/[id]/index.ts")
501
+ });
502
+ injectRoute({
503
+ pattern: "/_dineway/api/admin/review-requests/[id]/resolve",
504
+ entrypoint: resolveRoute("api/admin/review-requests/[id]/resolve.ts")
505
+ });
506
+ injectRoute({
507
+ pattern: "/_dineway/api/admin/hitl-requests",
508
+ entrypoint: resolveRoute("api/admin/hitl-requests/index.ts")
509
+ });
510
+ injectRoute({
511
+ pattern: "/_dineway/api/admin/hitl-requests/[id]",
512
+ entrypoint: resolveRoute("api/admin/hitl-requests/[id]/index.ts")
513
+ });
514
+ injectRoute({
515
+ pattern: "/_dineway/api/admin/hitl-requests/[id]/resolve",
516
+ entrypoint: resolveRoute("api/admin/hitl-requests/[id]/resolve.ts")
517
+ });
518
+ injectRoute({
519
+ pattern: "/_dineway/api/admin/context",
520
+ entrypoint: resolveRoute("api/admin/context/index.ts")
521
+ });
522
+ injectRoute({
523
+ pattern: "/_dineway/api/admin/context/stale",
524
+ entrypoint: resolveRoute("api/admin/context/stale.ts")
525
+ });
526
+ injectRoute({
527
+ pattern: "/_dineway/api/admin/context/diff",
528
+ entrypoint: resolveRoute("api/admin/context/diff.ts")
529
+ });
530
+ injectRoute({
531
+ pattern: "/_dineway/api/admin/context/[id]",
532
+ entrypoint: resolveRoute("api/admin/context/[id]/index.ts")
533
+ });
534
+ injectRoute({
535
+ pattern: "/_dineway/api/admin/context/[id]/history",
536
+ entrypoint: resolveRoute("api/admin/context/[id]/history.ts")
537
+ });
538
+ injectRoute({
539
+ pattern: "/_dineway/api/admin/context/[id]/supersede",
540
+ entrypoint: resolveRoute("api/admin/context/[id]/supersede.ts")
541
+ });
542
+ injectRoute({
543
+ pattern: "/_dineway/api/admin/context/[id]/review",
544
+ entrypoint: resolveRoute("api/admin/context/[id]/review.ts")
545
+ });
363
546
  injectRoute({
364
547
  pattern: "/_dineway/api/admin/users/[id]",
365
548
  entrypoint: resolveRoute("api/admin/users/[id]/index.ts")
@@ -420,6 +603,10 @@ function injectCoreRoutes(injectRoute) {
420
603
  pattern: "/_dineway/api/oauth/token",
421
604
  entrypoint: resolveRoute("api/oauth/token.ts")
422
605
  });
606
+ injectRoute({
607
+ pattern: "/_dineway/api/oauth/register",
608
+ entrypoint: resolveRoute("api/oauth/register.ts")
609
+ });
423
610
  injectRoute({
424
611
  pattern: "/_dineway/oauth/authorize",
425
612
  entrypoint: resolveRoute("api/oauth/authorize.ts")
@@ -429,7 +616,7 @@ function injectCoreRoutes(injectRoute) {
429
616
  entrypoint: resolveRoute("api/well-known/oauth-protected-resource.ts")
430
617
  });
431
618
  injectRoute({
432
- pattern: "/_dineway/.well-known/oauth-authorization-server",
619
+ pattern: "/.well-known/oauth-authorization-server/_dineway",
433
620
  entrypoint: resolveRoute("api/well-known/oauth-authorization-server.ts")
434
621
  });
435
622
  injectRoute({
@@ -591,7 +778,7 @@ function injectCoreRoutes(injectRoute) {
591
778
  }
592
779
  /**
593
780
  * Injects the MCP (Model Context Protocol) server route.
594
- * Only injected when `mcp: true` is set in the Dineway config.
781
+ * Injected by default unless `mcp: false` is set in the Dineway config.
595
782
  */
596
783
  function injectMcpRoute(injectRoute) {
597
784
  injectRoute({
@@ -640,6 +827,10 @@ function injectBuiltinAuthRoutes(injectRoute) {
640
827
  pattern: "/_dineway/api/auth/invite/accept",
641
828
  entrypoint: resolveRoute("api/auth/invite/accept.ts")
642
829
  });
830
+ injectRoute({
831
+ pattern: "/_dineway/api/auth/invite/register-options",
832
+ entrypoint: resolveRoute("api/auth/invite/register-options.ts")
833
+ });
643
834
  injectRoute({
644
835
  pattern: "/_dineway/api/auth/invite/complete",
645
836
  entrypoint: resolveRoute("api/auth/invite/complete.ts")
@@ -1106,10 +1297,16 @@ const NODE_NATIVE_EXTERNALS = [
1106
1297
  */
1107
1298
  function createViteConfig(options, command) {
1108
1299
  const adminDistPath = resolveAdminDist();
1109
- const adminSourcePath = command === "dev" ? resolveAdminSource() : void 0;
1300
+ const isDev = command === "dev";
1301
+ const adminSourcePath = isDev ? resolveAdminSource() : void 0;
1110
1302
  const useSource = adminSourcePath !== void 0;
1111
1303
  const devPlugins = adminSourcePath ? [linguiMacroPlugin(adminSourcePath, adminDistPath)] : [];
1112
1304
  return {
1305
+ define: {
1306
+ __DINEWAY_VERSION__: JSON.stringify(VERSION),
1307
+ __DINEWAY_COMMIT__: JSON.stringify(COMMIT),
1308
+ __DINEWAY_PSEUDO_LOCALE__: JSON.stringify(isDev && process.env["DINEWAY_PSEUDO_LOCALE"] === "1")
1309
+ },
1113
1310
  resolve: {
1114
1311
  dedupe: [
1115
1312
  "@dineway-ai/admin",
@@ -1219,7 +1416,10 @@ function dineway(config = {}) {
1219
1416
  storage: resolvedConfig.storage,
1220
1417
  auth: resolvedConfig.auth,
1221
1418
  marketplace: resolvedConfig.marketplace,
1222
- siteUrl: resolvedConfig.siteUrl
1419
+ maxUploadSize: resolvedConfig.maxUploadSize,
1420
+ admin: resolvedConfig.admin,
1421
+ siteUrl: resolvedConfig.siteUrl,
1422
+ trustedProxyHeaders: resolvedConfig.trustedProxyHeaders
1223
1423
  };
1224
1424
  const useExternalAuth = !!(resolvedConfig.auth && "entrypoint" in resolvedConfig.auth);
1225
1425
  return {
@@ -1236,11 +1436,36 @@ function dineway(config = {}) {
1236
1436
  prefixDefaultLocale: typeof routing === "object" ? routing.prefixDefaultLocale ?? false : false
1237
1437
  };
1238
1438
  }
1439
+ const securityConfig = {
1440
+ checkOrigin: false,
1441
+ ...resolvedConfig.siteUrl ? { allowedDomains: [{ hostname: new URL(resolvedConfig.siteUrl).hostname }] } : {}
1442
+ };
1443
+ const fontsConfig = resolvedConfig.fonts;
1444
+ const fontScripts = fontsConfig && typeof fontsConfig === "object" ? fontsConfig.scripts : void 0;
1239
1445
  updateConfig({
1240
- security: {
1241
- checkOrigin: false,
1242
- ...resolvedConfig.siteUrl ? { allowedDomains: [{ hostname: new URL(resolvedConfig.siteUrl).hostname }] } : {}
1243
- },
1446
+ security: securityConfig,
1447
+ fonts: fontsConfig === false ? [] : [{
1448
+ provider: notoSans({ scripts: fontScripts }),
1449
+ name: "Noto Sans",
1450
+ cssVariable: "--font-dineway",
1451
+ weights: ["100 900"],
1452
+ styles: ["normal", "italic"],
1453
+ subsets: [
1454
+ "latin",
1455
+ "latin-ext",
1456
+ "cyrillic",
1457
+ "cyrillic-ext",
1458
+ "devanagari",
1459
+ "greek",
1460
+ "greek-ext",
1461
+ "vietnamese"
1462
+ ],
1463
+ fallbacks: [
1464
+ "ui-sans-serif",
1465
+ "system-ui",
1466
+ "sans-serif"
1467
+ ]
1468
+ }],
1244
1469
  vite: createViteConfig({
1245
1470
  serializableConfig,
1246
1471
  resolvedConfig,
@@ -1250,10 +1475,7 @@ function dineway(config = {}) {
1250
1475
  });
1251
1476
  injectCoreRoutes(injectRoute);
1252
1477
  if (!useExternalAuth) injectBuiltinAuthRoutes(injectRoute);
1253
- if (resolvedConfig.mcp) {
1254
- injectMcpRoute(injectRoute);
1255
- logger.info("MCP server enabled at /_dineway/api/mcp");
1256
- }
1478
+ if (resolvedConfig.mcp !== false) injectMcpRoute(injectRoute);
1257
1479
  if (resolvedConfig.playground) addMiddleware({
1258
1480
  entrypoint: resolvedConfig.playground.middlewareEntrypoint,
1259
1481
  order: "pre"
@@ -1,12 +1,21 @@
1
- import "../../types-DkvMXalq.mjs";
2
- import "../../index-C-jx21qs.mjs";
3
- import "../../runner-B5l1JfOj.mjs";
4
- import "../../types-D38djUXv.mjs";
5
- import "../../validate-DVKJJ-M_.mjs";
1
+ import "../../types-DOrVigru.mjs";
2
+ import "../../index-yvc6E_17.mjs";
3
+ import "../../runner-CFI6B6J2.mjs";
4
+ import "../../types-Cj0KMIZV.mjs";
5
+ import "../../validate-IPf8n4Fj.mjs";
6
6
  import { DinewayHandlers, DinewayManifest } from "../types.mjs";
7
+ import { Kysely } from "kysely";
7
8
  import { User } from "@dineway-ai/auth";
8
9
  import * as astro from "astro";
9
10
 
11
+ //#region src/site-context/actor.d.ts
12
+ type SiteAuthTokenType = "api_token" | "oauth_token";
13
+ interface SiteAuthTokenContext {
14
+ type: SiteAuthTokenType;
15
+ /** Non-secret persistent token id. Present for API tokens; absent for OAuth access tokens today. */
16
+ id?: string | null;
17
+ }
18
+ //#endregion
10
19
  //#region src/astro/middleware/auth.d.ts
11
20
  declare global {
12
21
  namespace App {
@@ -14,6 +23,8 @@ declare global {
14
23
  user?: User;
15
24
  /** Token scopes when authenticated via API token or OAuth token. Undefined for session auth. */
16
25
  tokenScopes?: string[];
26
+ /** Non-secret bearer token metadata for activity attribution. Undefined for session auth. */
27
+ authToken?: SiteAuthTokenContext;
17
28
  dineway?: DinewayHandlers;
18
29
  dinewayManifest?: DinewayManifest;
19
30
  }
@@ -1,5 +1,5 @@
1
- import { t as apiError } from "../../error-DrxtnGPg.mjs";
2
- import { t as getAuthMode } from "../../mode-BlyYtIFO.mjs";
1
+ import { t as apiError } from "../../error-BmL6QipT.mjs";
2
+ import { t as getAuthMode } from "../../mode-47goXBBK.mjs";
3
3
  import { ulid } from "ulidx";
4
4
  import { defineMiddleware } from "astro:middleware";
5
5
  import { createKyselyAdapter } from "@dineway-ai/auth/adapters/kysely";
@@ -58,7 +58,7 @@ function checkPublicCsrf(request, url, publicOrigin) {
58
58
  * the build. Container deployments set env vars at runtime, so we must read
59
59
  * process.env which Vite leaves untouched.
60
60
  *
61
- * On non-Node runtimes process.env may be unavailable, so the fallback chain
61
+ * On runtimes without `process.env`, the fallback chain
62
62
  * continues to url.origin.
63
63
  *
64
64
  * Caches after first call.
@@ -117,7 +117,8 @@ async function resolveApiToken(db, rawToken) {
117
117
  db.updateTable("_dineway_api_tokens").set({ last_used_at: (/* @__PURE__ */ new Date()).toISOString() }).where("id", "=", row.id).execute().catch(() => {});
118
118
  return {
119
119
  userId: row.user_id,
120
- scopes: JSON.parse(row.scopes)
120
+ scopes: JSON.parse(row.scopes),
121
+ tokenId: row.id
121
122
  };
122
123
  }
123
124
  /**
@@ -174,6 +175,35 @@ function buildDinewayCsp() {
174
175
  /** Cache headers for middleware error responses (matches API_CACHE_HEADERS in api/error.ts) */
175
176
  const MW_CACHE_HEADERS = { "Cache-Control": "private, no-store" };
176
177
  const ROLE_ADMIN = 50;
178
+ const MCP_ENDPOINT_PATH = "/_dineway/api/mcp";
179
+ function isUnsafeMethod(method) {
180
+ return method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
181
+ }
182
+ function csrfRejectedResponse() {
183
+ return new Response(JSON.stringify({ error: {
184
+ code: "CSRF_REJECTED",
185
+ message: "Missing required header"
186
+ } }), {
187
+ status: 403,
188
+ headers: {
189
+ "Content-Type": "application/json",
190
+ ...MW_CACHE_HEADERS
191
+ }
192
+ });
193
+ }
194
+ function mcpUnauthorizedResponse(url, config) {
195
+ const origin = getPublicOrigin(url, config);
196
+ return Response.json({ error: {
197
+ code: "NOT_AUTHENTICATED",
198
+ message: "Not authenticated"
199
+ } }, {
200
+ status: 401,
201
+ headers: {
202
+ "WWW-Authenticate": `Bearer resource_metadata="${origin}/.well-known/oauth-protected-resource"`,
203
+ ...MW_CACHE_HEADERS
204
+ }
205
+ });
206
+ }
177
207
  /**
178
208
  * API routes that skip auth — each handles its own access control.
179
209
  *
@@ -187,29 +217,39 @@ const PUBLIC_API_PREFIXES = [
187
217
  "/_dineway/api/auth/dev-bypass",
188
218
  "/_dineway/api/auth/signup/",
189
219
  "/_dineway/api/auth/magic-link/",
190
- "/_dineway/api/auth/invite/accept",
191
- "/_dineway/api/auth/invite/complete",
220
+ "/_dineway/api/auth/invite/",
192
221
  "/_dineway/api/auth/oauth/",
193
222
  "/_dineway/api/oauth/device/token",
194
223
  "/_dineway/api/oauth/device/code",
195
224
  "/_dineway/api/oauth/token",
225
+ "/_dineway/api/oauth/register",
196
226
  "/_dineway/api/comments/",
197
227
  "/_dineway/api/media/file/",
198
228
  "/_dineway/.well-known/"
199
229
  ];
200
230
  const PUBLIC_API_EXACT = new Set([
231
+ "/_dineway/api/health",
201
232
  "/_dineway/api/auth/passkey/options",
202
233
  "/_dineway/api/auth/passkey/verify",
203
234
  "/_dineway/api/oauth/token",
204
235
  "/_dineway/api/snapshot",
205
236
  "/_dineway/api/search"
206
237
  ]);
238
+ const CSRF_EXEMPT_PUBLIC_ROUTES = new Set([
239
+ "/_dineway/api/oauth/token",
240
+ "/_dineway/api/oauth/register",
241
+ "/_dineway/api/oauth/device/code",
242
+ "/_dineway/api/oauth/device/token"
243
+ ]);
207
244
  function isPublicDinewayRoute(pathname) {
208
245
  if (PUBLIC_API_EXACT.has(pathname)) return true;
209
246
  if (PUBLIC_API_PREFIXES.some((p) => pathname.startsWith(p))) return true;
210
247
  if (import.meta.env.DEV && pathname === "/_dineway/api/typegen") return true;
211
248
  return false;
212
249
  }
250
+ function isCsrfExemptPublicRoute(pathname) {
251
+ return CSRF_EXEMPT_PUBLIC_ROUTES.has(pathname);
252
+ }
213
253
  const onRequest = defineMiddleware(async (context, next) => {
214
254
  const { url } = context;
215
255
  const isAdminRoute = url.pathname.startsWith("/_dineway/admin");
@@ -218,8 +258,7 @@ const onRequest = defineMiddleware(async (context, next) => {
218
258
  const isPublicApiRoute = isPublicDinewayRoute(url.pathname);
219
259
  const isPublicRoute = !isAdminRoute && !isApiRoute;
220
260
  if (isPublicApiRoute) {
221
- const method = context.request.method.toUpperCase();
222
- if (method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
261
+ if (isUnsafeMethod(context.request.method.toUpperCase()) && !isCsrfExemptPublicRoute(url.pathname)) {
223
262
  const publicOrigin = getPublicOrigin(url, context.locals.dineway?.config);
224
263
  const csrfError = checkPublicCsrf(context.request, url, publicOrigin);
225
264
  if (csrfError) return csrfError;
@@ -258,7 +297,7 @@ const onRequest = defineMiddleware(async (context, next) => {
258
297
  "Content-Type": "application/json",
259
298
  ...MW_CACHE_HEADERS
260
299
  };
261
- if (url.pathname === "/_dineway/api/mcp") headers["WWW-Authenticate"] = `Bearer resource_metadata="${getPublicOrigin(url, context.locals.dineway?.config)}/.well-known/oauth-protected-resource"`;
300
+ if (url.pathname === MCP_ENDPOINT_PATH) headers["WWW-Authenticate"] = `Bearer resource_metadata="${getPublicOrigin(url, context.locals.dineway?.config)}/.well-known/oauth-protected-resource"`;
262
301
  return new Response(JSON.stringify({ error: {
263
302
  code: "INVALID_TOKEN",
264
303
  message: "Invalid or expired token"
@@ -269,18 +308,10 @@ const onRequest = defineMiddleware(async (context, next) => {
269
308
  }
270
309
  const isTokenAuth = bearerResult === "authenticated";
271
310
  const method = context.request.method.toUpperCase();
311
+ if (url.pathname === MCP_ENDPOINT_PATH && !isTokenAuth) return mcpUnauthorizedResponse(url, context.locals.dineway?.config);
272
312
  const isOAuthConsent = url.pathname.startsWith("/_dineway/oauth/authorize");
273
- if (isApiRoute && !isTokenAuth && !isOAuthConsent && method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && !isPublicApiRoute) {
274
- if (context.request.headers.get("X-Dineway-Request") !== "1") return new Response(JSON.stringify({ error: {
275
- code: "CSRF_REJECTED",
276
- message: "Missing required header"
277
- } }), {
278
- status: 403,
279
- headers: {
280
- "Content-Type": "application/json",
281
- ...MW_CACHE_HEADERS
282
- }
283
- });
313
+ if (isApiRoute && !isTokenAuth && !isOAuthConsent && isUnsafeMethod(method) && !isPublicApiRoute) {
314
+ if (context.request.headers.get("X-Dineway-Request") !== "1") return csrfRejectedResponse();
284
315
  }
285
316
  if (isTokenAuth) {
286
317
  const scopeError = enforceTokenScope(url.pathname, method, context.locals.tokenScopes);
@@ -300,18 +331,18 @@ const onRequest = defineMiddleware(async (context, next) => {
300
331
  async function handleDinewayAuth(context, next) {
301
332
  const { url, locals } = context;
302
333
  const { dineway } = locals;
303
- const isLoginRoute = url.pathname.startsWith("/_dineway/admin/login");
334
+ const isPublicAdminRoute = url.pathname.startsWith("/_dineway/admin/login") || url.pathname.startsWith("/_dineway/admin/invite/accept");
304
335
  const isApiRoute = url.pathname.startsWith("/_dineway/api");
305
336
  if (!dineway?.db) return next();
306
337
  const authMode = getAuthMode(dineway.config);
307
338
  if (authMode.type === "external") {
308
339
  if (import.meta.env.DEV) {
309
- if (isLoginRoute) return next();
340
+ if (isPublicAdminRoute) return next();
310
341
  return handlePasskeyAuth(context, next, isApiRoute);
311
342
  }
312
343
  return handleExternalAuth(context, next, authMode, isApiRoute);
313
344
  }
314
- if (isLoginRoute) return next();
345
+ if (isPublicAdminRoute) return next();
315
346
  return handlePasskeyAuth(context, next, isApiRoute);
316
347
  }
317
348
  /**
@@ -471,14 +502,24 @@ async function handleBearerAuth(context) {
471
502
  const { dineway } = locals;
472
503
  if (!dineway?.db) return "none";
473
504
  let resolved = null;
474
- if (token.startsWith("ec_pat_")) resolved = await resolveApiToken(dineway.db, token);
475
- else if (token.startsWith("ec_oat_")) resolved = await resolveOAuthToken(dineway.db, token);
476
- else return "invalid";
505
+ let authToken;
506
+ if (token.startsWith("ec_pat_")) {
507
+ const apiToken = await resolveApiToken(dineway.db, token);
508
+ resolved = apiToken;
509
+ if (apiToken) authToken = {
510
+ type: "api_token",
511
+ id: apiToken.tokenId
512
+ };
513
+ } else if (token.startsWith("ec_oat_")) {
514
+ resolved = await resolveOAuthToken(dineway.db, token);
515
+ if (resolved) authToken = { type: "oauth_token" };
516
+ } else return "invalid";
477
517
  if (!resolved) return "invalid";
478
518
  const user = await createKyselyAdapter(dineway.db).getUserById(resolved.userId);
479
519
  if (!user || user.disabled) return "invalid";
480
520
  locals.user = user;
481
521
  locals.tokenScopes = resolved.scopes;
522
+ locals.authToken = authToken;
482
523
  return "authenticated";
483
524
  }
484
525
  /**
@@ -490,17 +531,13 @@ async function handlePasskeyAuth(context, next, isApiRoute) {
490
531
  try {
491
532
  const sessionUser = await session?.get("user");
492
533
  if (!sessionUser?.id) {
493
- if (isApiRoute) {
494
- const headers = { ...MW_CACHE_HEADERS };
495
- if (url.pathname === "/_dineway/api/mcp") headers["WWW-Authenticate"] = `Bearer resource_metadata="${getPublicOrigin(url, dineway?.config)}/.well-known/oauth-protected-resource"`;
496
- return Response.json({ error: {
497
- code: "NOT_AUTHENTICATED",
498
- message: "Not authenticated"
499
- } }, {
500
- status: 401,
501
- headers
502
- });
503
- }
534
+ if (isApiRoute) return Response.json({ error: {
535
+ code: "NOT_AUTHENTICATED",
536
+ message: "Not authenticated"
537
+ } }, {
538
+ status: 401,
539
+ headers: MW_CACHE_HEADERS
540
+ });
504
541
  const loginUrl = new URL("/_dineway/admin/login", getPublicOrigin(url, dineway?.config));
505
542
  loginUrl.searchParams.set("redirect", url.pathname);
506
543
  return context.redirect(loginUrl.toString());
@@ -1,7 +1,9 @@
1
- import "../../dialect-helpers-B9uSp2GJ.mjs";
1
+ import { getRequestContext } from "../../request-context.mjs";
2
+ import "../../dialect-helpers-DhTzaUxP.mjs";
2
3
  import "../../base64-F8-DUraK.mjs";
3
4
  import "../../types-BawVha09.mjs";
4
- import { t as RedirectRepository } from "../../redirect-JPqLAbxa.mjs";
5
+ import { t as RedirectRepository } from "../../redirect-DnEWAkVg.mjs";
6
+ import { a as matchCachedPatterns, n as compileRedirectRules, o as setCachedPatternRules, r as getCachedPatternRules } from "../../cache-BdSY-gQN.mjs";
5
7
  import { defineMiddleware } from "astro:middleware";
6
8
 
7
9
  //#region src/astro/middleware/redirect.ts
@@ -35,12 +37,26 @@ const onRequest = defineMiddleware(async (context, next) => {
35
37
  if (!dineway?.db) return next();
36
38
  try {
37
39
  const repo = new RedirectRepository(dineway.db);
38
- const match = await repo.matchPath(pathname);
39
- if (match) {
40
- if (match.resolvedDestination.startsWith("//") || match.resolvedDestination.startsWith("/\\")) return next();
41
- repo.recordHit(match.redirect.id).catch(() => {});
42
- const code = isRedirectCode(match.redirect.type) ? match.redirect.type : 301;
43
- return context.redirect(match.resolvedDestination, code);
40
+ const exact = await repo.findExactMatch(pathname);
41
+ if (exact) {
42
+ if (exact.destination.startsWith("//") || exact.destination.startsWith("/\\")) return next();
43
+ repo.recordHit(exact.id).catch(() => {});
44
+ const code = isRedirectCode(exact.type) ? exact.type : 301;
45
+ return context.redirect(exact.destination, code);
46
+ }
47
+ const hasDbOverride = !!getRequestContext()?.db;
48
+ let rules = !hasDbOverride ? getCachedPatternRules() : null;
49
+ if (!rules) {
50
+ const patternRedirects = await repo.findEnabledPatternRules();
51
+ rules = hasDbOverride ? compileRedirectRules(patternRedirects) : setCachedPatternRules(patternRedirects);
52
+ }
53
+ const patternMatch = matchCachedPatterns(rules, pathname);
54
+ if (patternMatch) {
55
+ const { redirect, destination } = patternMatch;
56
+ if (destination.startsWith("//") || destination.startsWith("/\\")) return next();
57
+ repo.recordHit(redirect.id).catch(() => {});
58
+ const code = isRedirectCode(redirect.type) ? redirect.type : 301;
59
+ return context.redirect(destination, code);
44
60
  }
45
61
  const response = await next();
46
62
  if (response.status === 404) {