rwsdk 0.1.16 → 0.1.17

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 (327) hide show
  1. package/dist/lib/$.d.mts +8 -0
  2. package/dist/lib/$.mjs +5 -0
  3. package/dist/lib/compileTsModule.d.mts +1 -0
  4. package/dist/lib/compileTsModule.mjs +27 -0
  5. package/dist/lib/constants.d.mts +4 -0
  6. package/dist/lib/constants.mjs +6 -0
  7. package/dist/lib/findWranglerConfig.d.mts +1 -0
  8. package/dist/lib/findWranglerConfig.mjs +12 -0
  9. package/dist/lib/getShortName.d.mts +1 -0
  10. package/dist/lib/getShortName.mjs +2 -0
  11. package/dist/lib/getSrcPaths.d.ts +15 -0
  12. package/dist/lib/getSrcPaths.js +80 -0
  13. package/dist/lib/hasPkgScript.d.mts +1 -0
  14. package/dist/lib/hasPkgScript.mjs +9 -0
  15. package/dist/lib/jsonUtils.d.mts +28 -0
  16. package/dist/lib/jsonUtils.mjs +167 -0
  17. package/dist/lib/setupEnvFiles.d.mts +4 -0
  18. package/dist/lib/setupEnvFiles.mjs +31 -0
  19. package/dist/lib/smokeTests/artifacts.d.mts +10 -0
  20. package/dist/lib/smokeTests/artifacts.mjs +164 -0
  21. package/dist/lib/smokeTests/browser.d.mts +48 -0
  22. package/dist/lib/smokeTests/browser.mjs +1041 -0
  23. package/dist/lib/smokeTests/cleanup.d.mts +5 -0
  24. package/dist/lib/smokeTests/cleanup.mjs +214 -0
  25. package/dist/lib/smokeTests/codeUpdates.d.mts +8 -0
  26. package/dist/lib/smokeTests/codeUpdates.mjs +229 -0
  27. package/dist/lib/smokeTests/constants.d.mts +5 -0
  28. package/dist/lib/smokeTests/constants.mjs +10 -0
  29. package/dist/lib/smokeTests/development.d.mts +11 -0
  30. package/dist/lib/smokeTests/development.mjs +209 -0
  31. package/dist/lib/smokeTests/environment.d.mts +14 -0
  32. package/dist/lib/smokeTests/environment.mjs +163 -0
  33. package/dist/lib/smokeTests/release.d.mts +61 -0
  34. package/dist/lib/smokeTests/release.mjs +526 -0
  35. package/dist/lib/smokeTests/reporting.d.mts +13 -0
  36. package/dist/lib/smokeTests/reporting.mjs +355 -0
  37. package/dist/lib/smokeTests/runSmokeTests.d.mts +5 -0
  38. package/dist/lib/smokeTests/runSmokeTests.mjs +144 -0
  39. package/dist/lib/smokeTests/state.d.mts +48 -0
  40. package/dist/lib/smokeTests/state.mjs +57 -0
  41. package/dist/lib/smokeTests/templates/SmokeTest.template.d.ts +1 -0
  42. package/dist/lib/smokeTests/templates/SmokeTest.template.js +81 -0
  43. package/dist/lib/smokeTests/templates/SmokeTestClient.template.d.ts +1 -0
  44. package/dist/lib/smokeTests/templates/SmokeTestClient.template.js +159 -0
  45. package/dist/lib/smokeTests/templates/smokeTestFunctions.template.d.ts +1 -0
  46. package/dist/lib/smokeTests/templates/smokeTestFunctions.template.js +19 -0
  47. package/dist/lib/smokeTests/types.d.mts +75 -0
  48. package/dist/lib/smokeTests/types.mjs +1 -0
  49. package/dist/lib/smokeTests/utils.d.mts +15 -0
  50. package/dist/lib/smokeTests/utils.mjs +147 -0
  51. package/dist/llms/index.d.ts +3 -0
  52. package/dist/llms/index.js +35 -0
  53. package/dist/llms/rules/interruptors.d.ts +1 -0
  54. package/dist/llms/rules/interruptors.js +243 -0
  55. package/dist/llms/rules/middleware.d.ts +1 -0
  56. package/dist/llms/rules/middleware.js +71 -0
  57. package/dist/llms/rules/react.d.ts +1 -0
  58. package/dist/llms/rules/react.js +106 -0
  59. package/dist/llms/rules/request-response.d.ts +1 -0
  60. package/dist/llms/rules/request-response.js +209 -0
  61. package/dist/runtime/client.d.ts +17 -0
  62. package/dist/runtime/client.js +74 -0
  63. package/dist/runtime/clientNavigation.d.ts +4 -0
  64. package/dist/runtime/clientNavigation.js +53 -0
  65. package/dist/runtime/clientNavigation.test.d.ts +1 -0
  66. package/dist/runtime/clientNavigation.test.js +55 -0
  67. package/dist/runtime/constants.d.ts +1 -0
  68. package/dist/runtime/constants.js +1 -0
  69. package/dist/runtime/entries/auth.d.ts +1 -0
  70. package/dist/runtime/entries/auth.js +1 -0
  71. package/dist/runtime/entries/client.d.ts +4 -0
  72. package/dist/runtime/entries/client.js +4 -0
  73. package/dist/runtime/entries/clientSSR.d.ts +1 -0
  74. package/dist/runtime/entries/clientSSR.js +1 -0
  75. package/dist/runtime/entries/navigation.d.ts +1 -0
  76. package/dist/runtime/entries/navigation.js +1 -0
  77. package/dist/runtime/entries/no-react-server.d.ts +0 -0
  78. package/dist/runtime/entries/no-react-server.js +2 -0
  79. package/dist/runtime/entries/react-server-only.d.ts +0 -0
  80. package/dist/runtime/entries/react-server-only.js +2 -0
  81. package/dist/runtime/entries/router.d.ts +2 -0
  82. package/dist/runtime/entries/router.js +2 -0
  83. package/dist/runtime/entries/ssr.d.ts +1 -0
  84. package/dist/runtime/entries/ssr.js +1 -0
  85. package/dist/runtime/entries/worker.d.ts +9 -0
  86. package/dist/runtime/entries/worker.js +9 -0
  87. package/dist/runtime/error.d.ts +6 -0
  88. package/dist/runtime/error.js +8 -0
  89. package/dist/runtime/imports/ClientOnly.d.ts +3 -0
  90. package/dist/runtime/imports/ClientOnly.js +8 -0
  91. package/dist/runtime/imports/client.d.ts +4 -0
  92. package/dist/runtime/imports/client.js +33 -0
  93. package/dist/runtime/imports/ssr.d.ts +5 -0
  94. package/dist/runtime/imports/ssr.js +20 -0
  95. package/dist/runtime/imports/worker.d.ts +5 -0
  96. package/dist/runtime/imports/worker.js +22 -0
  97. package/dist/runtime/lib/auth/index.d.ts +1 -0
  98. package/dist/runtime/lib/auth/index.js +1 -0
  99. package/dist/runtime/lib/auth/session.d.ts +50 -0
  100. package/dist/runtime/lib/auth/session.js +148 -0
  101. package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
  102. package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
  103. package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
  104. package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
  105. package/dist/runtime/lib/db/createDb.d.ts +2 -0
  106. package/dist/runtime/lib/db/createDb.js +33 -0
  107. package/dist/runtime/lib/db/index.d.ts +4 -0
  108. package/dist/runtime/lib/db/index.js +3 -0
  109. package/dist/runtime/lib/db/migrations.d.ts +23 -0
  110. package/dist/runtime/lib/db/migrations.js +34 -0
  111. package/dist/runtime/lib/db/typeInference/assert.d.ts +2 -0
  112. package/dist/runtime/lib/db/typeInference/assert.js +1 -0
  113. package/dist/runtime/lib/db/typeInference/builders/alterColumn.d.ts +27 -0
  114. package/dist/runtime/lib/db/typeInference/builders/alterColumn.js +1 -0
  115. package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +53 -0
  116. package/dist/runtime/lib/db/typeInference/builders/alterTable.js +1 -0
  117. package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +26 -0
  118. package/dist/runtime/lib/db/typeInference/builders/columnDefinition.js +1 -0
  119. package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +49 -0
  120. package/dist/runtime/lib/db/typeInference/builders/createTable.js +1 -0
  121. package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +17 -0
  122. package/dist/runtime/lib/db/typeInference/builders/createView.js +1 -0
  123. package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +11 -0
  124. package/dist/runtime/lib/db/typeInference/builders/dropTable.js +1 -0
  125. package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +12 -0
  126. package/dist/runtime/lib/db/typeInference/builders/dropView.js +1 -0
  127. package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +24 -0
  128. package/dist/runtime/lib/db/typeInference/builders/schema.js +1 -0
  129. package/dist/runtime/lib/db/typeInference/database.d.ts +27 -0
  130. package/dist/runtime/lib/db/typeInference/database.js +1 -0
  131. package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.d.ts +1 -0
  132. package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +360 -0
  133. package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.d.ts +1 -0
  134. package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +33 -0
  135. package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.d.ts +1 -0
  136. package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.js +143 -0
  137. package/dist/runtime/lib/db/typeInference/typetests/print.d.ts +3 -0
  138. package/dist/runtime/lib/db/typeInference/typetests/print.js +1 -0
  139. package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +2 -0
  140. package/dist/runtime/lib/db/typeInference/typetests/testUtils.js +1 -0
  141. package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.d.ts +1 -0
  142. package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.js +17 -0
  143. package/dist/runtime/lib/db/typeInference/utils.d.ts +82 -0
  144. package/dist/runtime/lib/db/typeInference/utils.js +2 -0
  145. package/dist/runtime/lib/debug.d.ts +2 -0
  146. package/dist/runtime/lib/debug.js +36 -0
  147. package/dist/runtime/lib/links.d.ts +14 -0
  148. package/dist/runtime/lib/links.js +38 -0
  149. package/dist/runtime/lib/realtime/client.d.ts +7 -0
  150. package/dist/runtime/lib/realtime/client.js +166 -0
  151. package/dist/runtime/lib/realtime/constants.d.ts +1 -0
  152. package/dist/runtime/lib/realtime/constants.js +1 -0
  153. package/dist/runtime/lib/realtime/durableObject.d.ts +29 -0
  154. package/dist/runtime/lib/realtime/durableObject.js +187 -0
  155. package/dist/runtime/lib/realtime/renderRealtimeClients.d.ts +7 -0
  156. package/dist/runtime/lib/realtime/renderRealtimeClients.js +6 -0
  157. package/dist/runtime/lib/realtime/shared.d.ts +10 -0
  158. package/dist/runtime/lib/realtime/shared.js +10 -0
  159. package/dist/runtime/lib/realtime/validateUpgradeRequest.d.ts +6 -0
  160. package/dist/runtime/lib/realtime/validateUpgradeRequest.js +29 -0
  161. package/dist/runtime/lib/realtime/worker.d.ts +3 -0
  162. package/dist/runtime/lib/realtime/worker.js +16 -0
  163. package/dist/runtime/lib/router.d.ts +56 -0
  164. package/dist/runtime/lib/router.js +210 -0
  165. package/dist/runtime/lib/router.test.d.ts +1 -0
  166. package/dist/runtime/lib/router.test.js +58 -0
  167. package/dist/runtime/lib/streams/consumeEventStream.d.ts +4 -0
  168. package/dist/runtime/lib/streams/consumeEventStream.js +13 -0
  169. package/dist/runtime/lib/turnstile/TurnstileScript.d.ts +1 -0
  170. package/dist/runtime/lib/turnstile/TurnstileScript.js +2 -0
  171. package/dist/runtime/lib/turnstile/turnstile.d.ts +3 -0
  172. package/dist/runtime/lib/turnstile/turnstile.js +3 -0
  173. package/dist/runtime/lib/turnstile/useTurnstile.d.ts +4 -0
  174. package/dist/runtime/lib/turnstile/useTurnstile.js +23 -0
  175. package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +4 -0
  176. package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +15 -0
  177. package/dist/runtime/lib/utils.d.ts +1 -0
  178. package/dist/runtime/lib/utils.js +1 -0
  179. package/dist/runtime/register/client.d.ts +1 -0
  180. package/dist/runtime/register/client.js +5 -0
  181. package/dist/runtime/register/ssr.d.ts +3 -0
  182. package/dist/runtime/register/ssr.js +26 -0
  183. package/dist/runtime/register/worker.d.ts +4 -0
  184. package/dist/runtime/register/worker.js +42 -0
  185. package/dist/runtime/render/createClientManifest.d.ts +1 -0
  186. package/dist/runtime/render/createClientManifest.js +7 -0
  187. package/dist/runtime/render/createModuleMap.d.ts +1 -0
  188. package/dist/runtime/render/createModuleMap.js +13 -0
  189. package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
  190. package/dist/runtime/render/injectRSCPayload.js +79 -0
  191. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +9 -0
  192. package/dist/runtime/render/renderRscThenableToHtmlStream.js +49 -0
  193. package/dist/runtime/render/renderToRscStream.d.ts +5 -0
  194. package/dist/runtime/render/renderToRscStream.js +46 -0
  195. package/dist/runtime/render/renderToStream.d.ts +9 -0
  196. package/dist/runtime/render/renderToStream.js +27 -0
  197. package/dist/runtime/render/renderToString.d.ts +7 -0
  198. package/dist/runtime/render/renderToString.js +26 -0
  199. package/dist/runtime/render/transformRscToHtmlStream.d.ts +8 -0
  200. package/dist/runtime/render/transformRscToHtmlStream.js +19 -0
  201. package/dist/runtime/requestInfo/types.d.ts +11 -0
  202. package/dist/runtime/requestInfo/types.js +1 -0
  203. package/dist/runtime/requestInfo/worker.d.ts +5 -0
  204. package/dist/runtime/requestInfo/worker.js +33 -0
  205. package/dist/runtime/script.d.ts +5 -0
  206. package/dist/runtime/script.js +8 -0
  207. package/dist/runtime/ssrBridge.d.ts +2 -0
  208. package/dist/runtime/ssrBridge.js +11 -0
  209. package/dist/runtime/worker.d.ts +18 -0
  210. package/dist/runtime/worker.js +173 -0
  211. package/dist/scripts/__sdk.d.mts +1 -0
  212. package/dist/scripts/__sdk.mjs +14 -0
  213. package/dist/scripts/build-vendor-bundles.d.mts +1 -0
  214. package/dist/scripts/build-vendor-bundles.mjs +92 -0
  215. package/dist/scripts/debug-sync.d.mts +6 -0
  216. package/dist/scripts/debug-sync.mjs +224 -0
  217. package/dist/scripts/dev-init.d.mts +1 -0
  218. package/dist/scripts/dev-init.mjs +25 -0
  219. package/dist/scripts/ensure-deploy-env.d.mts +1 -0
  220. package/dist/scripts/ensure-deploy-env.mjs +271 -0
  221. package/dist/scripts/ensure-env.d.mts +1 -0
  222. package/dist/scripts/ensure-env.mjs +9 -0
  223. package/dist/scripts/migrate-new.d.mts +1 -0
  224. package/dist/scripts/migrate-new.mjs +51 -0
  225. package/dist/scripts/smoke-test.d.mts +1 -0
  226. package/dist/scripts/smoke-test.mjs +166 -0
  227. package/dist/scripts/worker-run.d.mts +1 -0
  228. package/dist/scripts/worker-run.mjs +82 -0
  229. package/dist/vite/aliasByEnvPlugin.d.mts +2 -0
  230. package/dist/vite/aliasByEnvPlugin.mjs +11 -0
  231. package/dist/vite/asyncSetupPlugin.d.mts +6 -0
  232. package/dist/vite/asyncSetupPlugin.mjs +23 -0
  233. package/dist/vite/checkIsUsingPrisma.d.mts +6 -0
  234. package/dist/vite/checkIsUsingPrisma.mjs +18 -0
  235. package/dist/vite/configPlugin.d.mts +9 -0
  236. package/dist/vite/configPlugin.mjs +169 -0
  237. package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
  238. package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
  239. package/dist/vite/createDirectiveLookupPlugin.d.mts +21 -0
  240. package/dist/vite/createDirectiveLookupPlugin.mjs +231 -0
  241. package/dist/vite/customReactBuildPlugin.d.mts +4 -0
  242. package/dist/vite/customReactBuildPlugin.mjs +61 -0
  243. package/dist/vite/devServerTimingPlugin.d.mts +2 -0
  244. package/dist/vite/devServerTimingPlugin.mjs +24 -0
  245. package/dist/vite/directivesPlugin.d.mts +6 -0
  246. package/dist/vite/directivesPlugin.mjs +200 -0
  247. package/dist/vite/ensureAliasArray.d.mts +2 -0
  248. package/dist/vite/ensureAliasArray.mjs +17 -0
  249. package/dist/vite/findSpecifiers.d.mts +31 -0
  250. package/dist/vite/findSpecifiers.mjs +230 -0
  251. package/dist/vite/findSsrSpecifiers.d.mts +11 -0
  252. package/dist/vite/findSsrSpecifiers.mjs +67 -0
  253. package/dist/vite/hasDirective.d.mts +7 -0
  254. package/dist/vite/hasDirective.mjs +54 -0
  255. package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
  256. package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
  257. package/dist/vite/index.d.mts +1 -0
  258. package/dist/vite/index.mjs +1 -0
  259. package/dist/vite/injectHmrPreambleJsxPlugin.d.mts +2 -0
  260. package/dist/vite/injectHmrPreambleJsxPlugin.mjs +22 -0
  261. package/dist/vite/injectVitePreamblePlugin.d.mts +4 -0
  262. package/dist/vite/injectVitePreamblePlugin.mjs +23 -0
  263. package/dist/vite/invalidateCacheIfPrismaClientChanged.d.mts +3 -0
  264. package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +27 -0
  265. package/dist/vite/invalidateModule.d.mts +6 -0
  266. package/dist/vite/invalidateModule.mjs +30 -0
  267. package/dist/vite/miniflareHMRPlugin.d.mts +10 -0
  268. package/dist/vite/miniflareHMRPlugin.mjs +209 -0
  269. package/dist/vite/miniflarePlugin.d.mts +9 -0
  270. package/dist/vite/miniflarePlugin.mjs +135 -0
  271. package/dist/vite/moveStaticAssetsPlugin.d.mts +4 -0
  272. package/dist/vite/moveStaticAssetsPlugin.mjs +12 -0
  273. package/dist/vite/normalizeModulePath.d.mts +1 -0
  274. package/dist/vite/normalizeModulePath.mjs +13 -0
  275. package/dist/vite/prismaPlugin.d.mts +4 -0
  276. package/dist/vite/prismaPlugin.mjs +43 -0
  277. package/dist/vite/reactConditionsResolverPlugin.d.mts +16 -0
  278. package/dist/vite/reactConditionsResolverPlugin.mjs +179 -0
  279. package/dist/vite/redwoodPlugin.d.mts +12 -0
  280. package/dist/vite/redwoodPlugin.mjs +105 -0
  281. package/dist/vite/requestUtils.d.mts +6 -0
  282. package/dist/vite/requestUtils.mjs +35 -0
  283. package/dist/vite/setupEnvFiles.d.mts +4 -0
  284. package/dist/vite/setupEnvFiles.mjs +31 -0
  285. package/dist/vite/ssrBridgePlugin.d.mts +7 -0
  286. package/dist/vite/ssrBridgePlugin.mjs +137 -0
  287. package/dist/vite/transformClientComponents.d.mts +12 -0
  288. package/dist/vite/transformClientComponents.mjs +116 -0
  289. package/dist/vite/transformClientComponents.test.d.mts +1 -0
  290. package/dist/vite/transformClientComponents.test.mjs +264 -0
  291. package/dist/vite/transformJsxScriptTagsPlugin.d.mts +8 -0
  292. package/dist/vite/transformJsxScriptTagsPlugin.mjs +315 -0
  293. package/dist/vite/transformJsxScriptTagsPlugin.test.d.mts +1 -0
  294. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +334 -0
  295. package/dist/vite/transformServerFunctions.d.mts +16 -0
  296. package/dist/vite/transformServerFunctions.mjs +296 -0
  297. package/dist/vite/transformServerFunctions.test.d.mts +1 -0
  298. package/dist/vite/transformServerFunctions.test.mjs +124 -0
  299. package/dist/vite/useClientLookupPlugin.d.mts +5 -0
  300. package/dist/vite/useClientLookupPlugin.mjs +15 -0
  301. package/dist/vite/useClientPlugin.d.mts +8 -0
  302. package/dist/vite/useClientPlugin.mjs +295 -0
  303. package/dist/vite/useClientPlugin.test.d.mts +1 -0
  304. package/dist/vite/useClientPlugin.test.mjs +1204 -0
  305. package/dist/vite/useServerLookupPlugin.d.mts +5 -0
  306. package/dist/vite/useServerLookupPlugin.mjs +15 -0
  307. package/dist/vite/useServerPlugin.d.mts +1 -0
  308. package/dist/vite/useServerPlugin.mjs +1 -0
  309. package/dist/vite/virtualPlugin.d.mts +2 -0
  310. package/dist/vite/virtualPlugin.mjs +18 -0
  311. package/dist/vite/vitePreamblePlugin.d.mts +1 -0
  312. package/dist/vite/vitePreamblePlugin.mjs +11 -0
  313. package/dist/worker/__ssr_bridge.js +8947 -0
  314. package/dist/worker/__ssr_bridge.js.map +1 -0
  315. package/package.json +1 -1
  316. package/dist/vite/invalidateClientModule.d.mts +0 -2
  317. package/dist/vite/invalidateClientModule.mjs +0 -8
  318. package/dist/vite/invalidateModule copy.d.mts +0 -2
  319. package/dist/vite/invalidateModule copy.mjs +0 -14
  320. package/dist/vite/invalidateSSRModule.d.mts +0 -2
  321. package/dist/vite/invalidateSSRModule.mjs +0 -7
  322. package/dist/vite/mode.d.mts +0 -5
  323. package/dist/vite/mode.mjs +0 -25
  324. package/dist/vite/modePlugin.d.mts +0 -2
  325. package/dist/vite/modePlugin.mjs +0 -10
  326. /package/dist/vite/{isJsFile.d.ts → isJsFile.d.mts} +0 -0
  327. /package/dist/vite/{isJsFile.js → isJsFile.mjs} +0 -0
@@ -0,0 +1,81 @@
1
+ export function getSmokeTestTemplate(skipClient = false) {
2
+ return `
3
+ import React from "react";
4
+ import { RequestInfo } from "rwsdk/worker";
5
+ ${skipClient ? "" : 'import { SmokeTestClient } from "./__SmokeTestClient";'}
6
+ import { getSmokeTestTimestamp } from "./__smokeTestFunctions";
7
+
8
+ export const SmokeTestInfo: React.FC = async () => {
9
+ const currentTime = Date.now();
10
+ let status = "error";
11
+ let timestamp = 0;
12
+
13
+ try {
14
+ const result = await getSmokeTestTimestamp();
15
+ status = result.status || "error";
16
+ timestamp = result.timestamp;
17
+ } catch (error) {
18
+ console.error("Smoke test failed:", error);
19
+ status = "error";
20
+ }
21
+
22
+ return (
23
+ <div
24
+ id="smoke-test-container"
25
+ data-testid="health-status"
26
+ data-status={status}
27
+ data-timestamp={timestamp}
28
+ data-server-timestamp={currentTime}
29
+ style={{
30
+ fontFamily: "system-ui, -apple-system, sans-serif",
31
+ margin: "20px",
32
+ padding: "15px",
33
+ border: "1px solid #ddd",
34
+ borderRadius: "4px",
35
+ background: "#f9f9f9",
36
+ }}
37
+ >
38
+ <h2
39
+ style={{
40
+ color: status === "ok" ? "#0c9" : "#f44",
41
+ margin: "0 0 10px 0",
42
+ }}
43
+ >
44
+ Smoke Test: {status}
45
+ </h2>
46
+ <div
47
+ id="smoke-test-result"
48
+ >
49
+ Server Timestamp: {timestamp}
50
+ </div>
51
+
52
+ {/* HMR Testing Marker - Do not modify this comment */}
53
+ <div
54
+ id="server-hmr-marker"
55
+ data-testid="server-hmr-marker"
56
+ data-hmr-text="original"
57
+ data-hmr-timestamp={Date.now()}
58
+ >
59
+ Server Component HMR: <span>Original Text</span>
60
+ </div>
61
+
62
+ <details style={{ marginTop: "10px" }}>
63
+ <summary>Details</summary>
64
+ <pre
65
+ style={{
66
+ background: "#f5f5f5",
67
+ padding: "10px",
68
+ borderRadius: "4px",
69
+ fontSize: "12px",
70
+ overflow: "auto",
71
+ }}
72
+ >
73
+ {JSON.stringify({ currentTime, serverTimestamp: timestamp, status }, null, 2)}
74
+ </pre>
75
+ </details>
76
+
77
+ ${!skipClient ? "<SmokeTestClient/>" : ""}
78
+ </div>
79
+ );
80
+ };`;
81
+ }
@@ -0,0 +1 @@
1
+ export declare function getSmokeTestClientTemplate(): string;
@@ -0,0 +1,159 @@
1
+ export function getSmokeTestClientTemplate() {
2
+ return `"use client";
3
+
4
+ import React, { useState } from "react";
5
+ import { smokeTestAction } from "./__smokeTestFunctions";
6
+
7
+ interface SmokeTestStatus {
8
+ status: string;
9
+ verificationPassed: boolean;
10
+ timestamp: number;
11
+ rawResult?: unknown;
12
+ error?: string;
13
+ }
14
+
15
+ interface SmokeTestResponse {
16
+ status: string;
17
+ timestamp?: number;
18
+ [key: string]: unknown;
19
+ }
20
+
21
+ export const SmokeTestClient: React.FC = () => {
22
+ const [loading, setLoading] = useState(false);
23
+ const [lastCheck, setLastCheck] = useState<SmokeTestStatus | null>(null);
24
+
25
+ const runSmokeTest = async () => {
26
+ setLoading(true);
27
+ const clientTimestamp = Date.now();
28
+
29
+ try {
30
+ // Update the server timestamp with our client timestamp
31
+ const result = await smokeTestAction(clientTimestamp);
32
+ const status = result.status || "error";
33
+ const verificationPassed = result.timestamp === clientTimestamp;
34
+
35
+ setLastCheck({
36
+ status,
37
+ verificationPassed,
38
+ timestamp: clientTimestamp,
39
+ rawResult: result,
40
+ });
41
+ } catch (error) {
42
+ setLastCheck({
43
+ status: "error",
44
+ verificationPassed: false,
45
+ timestamp: clientTimestamp,
46
+ error: error instanceof Error ? error.message : String(error),
47
+ });
48
+ } finally {
49
+ setLoading(false);
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div
55
+ className="smoke-test-client"
56
+ style={{
57
+ margin: "20px 0",
58
+ padding: "15px",
59
+ border: "1px solid #ddd",
60
+ borderRadius: "4px",
61
+ background: "#f9f9f9",
62
+ fontFamily: "system-ui, -apple-system, sans-serif",
63
+ }}
64
+ >
65
+ <h3>Manual Smoke Test</h3>
66
+ <button
67
+ data-testid="refresh-health"
68
+ onClick={runSmokeTest}
69
+ disabled={loading}
70
+ style={{
71
+ padding: "8px 16px",
72
+ background: loading ? "#ccc" : "#0070f3",
73
+ color: "white",
74
+ border: "none",
75
+ borderRadius: "4px",
76
+ cursor: loading ? "not-allowed" : "pointer",
77
+ fontWeight: "bold",
78
+ }}
79
+ >
80
+ {loading ? "Checking..." : "Run Smoke Test"}
81
+ </button>
82
+
83
+ {/* HMR Testing Marker - Do not modify this comment */}
84
+ <div
85
+ id="client-hmr-marker"
86
+ data-testid="client-hmr-marker"
87
+ data-hmr-text="original"
88
+ data-hmr-timestamp={Date.now()}
89
+ >
90
+ Client Component HMR: <span>Original Text</span>
91
+ </div>
92
+
93
+ {lastCheck && (
94
+ <div style={{ marginTop: "15px" }}>
95
+ <div
96
+ style={{
97
+ padding: "10px",
98
+ borderRadius: "4px",
99
+ background: lastCheck.status === "ok" ? "#e6f7ee" : "#ffeded",
100
+ border: \`1px solid \${
101
+ lastCheck.status === "ok" ? "#0c9" : "#f44"
102
+ }\`,
103
+ }}
104
+ >
105
+ <h4
106
+ style={{
107
+ margin: "0 0 10px 0",
108
+ color: lastCheck.status === "ok" ? "#0c9" : "#f44",
109
+ }}
110
+ >
111
+ Status: {lastCheck.status}
112
+ </h4>
113
+ <p>
114
+ Server timestamp updated to: {lastCheck.timestamp}
115
+ </p>
116
+ <p>
117
+ Timestamp verification:{" "}
118
+ {lastCheck.verificationPassed ? "Passed ✅" : "Failed ⚠️"}
119
+ </p>
120
+ {lastCheck.error && (
121
+ <p style={{ color: "#f44" }}>Error: {lastCheck.error}</p>
122
+ )}
123
+ <details style={{ marginTop: "10px" }}>
124
+ <summary>Raw Result</summary>
125
+ <pre
126
+ style={{
127
+ background: "#f5f5f5",
128
+ padding: "10px",
129
+ borderRadius: "4px",
130
+ fontSize: "12px",
131
+ overflow: "auto",
132
+ }}
133
+ >
134
+ {JSON.stringify(
135
+ {
136
+ timestamp: lastCheck.timestamp,
137
+ result: lastCheck.rawResult,
138
+ verificationPassed: lastCheck.verificationPassed,
139
+ },
140
+ null,
141
+ 2
142
+ )}
143
+ </pre>
144
+ </details>
145
+ </div>
146
+ </div>
147
+ )}
148
+
149
+ <div
150
+ id="smoke-test-client-timestamp"
151
+ data-client-timestamp={lastCheck?.timestamp ?? ""}
152
+ data-status={lastCheck?.status ?? ""}
153
+ data-verified={lastCheck?.verificationPassed ? "true" : "false"}
154
+ style={{ display: "none" }}
155
+ />
156
+ </div>
157
+ );
158
+ };`;
159
+ }
@@ -0,0 +1 @@
1
+ export declare function getSmokeTestFunctionsTemplate(): string;
@@ -0,0 +1,19 @@
1
+ export function getSmokeTestFunctionsTemplate() {
2
+ return `"use server";
3
+
4
+ // Module-level variable to store the timestamp set by client
5
+ // Initialize with a fixed value to verify initial server render
6
+ let serverTimestamp: number = 23;
7
+
8
+ // Function to retrieve the current server timestamp
9
+ export async function getSmokeTestTimestamp(): Promise<{ timestamp: number, status: string }> {
10
+ return { timestamp: serverTimestamp, status: "ok" };
11
+ }
12
+
13
+ export async function smokeTestAction(timestamp: number): Promise<unknown> {
14
+ await new Promise((resolve) => setTimeout(resolve, 0));
15
+ serverTimestamp = timestamp;
16
+ return { status: "ok", timestamp };
17
+ }
18
+ `;
19
+ }
@@ -0,0 +1,75 @@
1
+ import { WriteStream } from "fs";
2
+ export interface SmokeTestResult {
3
+ status: string;
4
+ verificationPassed: boolean;
5
+ timestamp?: number;
6
+ rawResult?: unknown;
7
+ error?: string;
8
+ serverTimestamp?: number;
9
+ clientTimestamp?: number;
10
+ }
11
+ export interface SmokeTestOptions {
12
+ customPath?: string;
13
+ skipDev?: boolean;
14
+ skipRelease?: boolean;
15
+ skipClient?: boolean;
16
+ projectDir?: string;
17
+ artifactDir?: string;
18
+ keep?: boolean;
19
+ headless?: boolean;
20
+ sync?: boolean;
21
+ ci?: boolean;
22
+ bail?: boolean;
23
+ copyProject?: boolean;
24
+ realtime?: boolean;
25
+ skipHmr?: boolean;
26
+ }
27
+ export interface TestResources {
28
+ tempDirCleanup?: () => Promise<void>;
29
+ workerName?: string;
30
+ originalCwd: string;
31
+ targetDir?: string;
32
+ workerCreatedDuringTest: boolean;
33
+ stopDev?: () => Promise<void>;
34
+ resourceUniqueKey: string;
35
+ }
36
+ export interface StreamCapturer {
37
+ stdoutLogFile: WriteStream | null;
38
+ stderrLogFile: WriteStream | null;
39
+ combinedLogFile: WriteStream | null;
40
+ originalStdoutWrite: (chunk: Uint8Array | string, encoding?: BufferEncoding, callback?: (error?: Error | null) => void) => boolean;
41
+ originalStderrWrite: (chunk: Uint8Array | string, encoding?: BufferEncoding, callback?: (error?: Error | null) => void) => boolean;
42
+ start: (artifactDir: string) => void;
43
+ stop: () => void;
44
+ }
45
+ export interface TestStatus {
46
+ dev: {
47
+ overall: string;
48
+ initialServerSide: string;
49
+ initialClientSide: string;
50
+ initialServerRenderCheck: string;
51
+ realtimeUpgrade: string;
52
+ realtimeServerSide: string;
53
+ realtimeClientSide: string;
54
+ realtimeServerRenderCheck: string;
55
+ initialServerHmr: string;
56
+ initialClientHmr: string;
57
+ realtimeServerHmr: string;
58
+ realtimeClientHmr: string;
59
+ };
60
+ production: {
61
+ overall: string;
62
+ releaseCommand: string;
63
+ initialServerSide: string;
64
+ initialClientSide: string;
65
+ initialServerRenderCheck: string;
66
+ realtimeUpgrade: string;
67
+ realtimeServerSide: string;
68
+ realtimeClientSide: string;
69
+ realtimeServerRenderCheck: string;
70
+ initialServerHmr: string;
71
+ initialClientHmr: string;
72
+ realtimeServerHmr: string;
73
+ realtimeClientHmr: string;
74
+ };
75
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { log } from "./constants.mjs";
2
+ export { log };
3
+ export declare function isRunningInCI(ciFlag?: boolean): boolean;
4
+ /**
5
+ * Handles test failure by logging the error and initiating teardown
6
+ */
7
+ export declare function fail(error: unknown, exitCode?: number, step?: string): Promise<never>;
8
+ /**
9
+ * Handles resource teardown and exits the process with appropriate exit code
10
+ */
11
+ export declare function teardown(): Promise<void>;
12
+ /**
13
+ * Formats the path suffix from a custom path
14
+ */
15
+ export declare function formatPathSuffix(customPath?: string): string;
@@ -0,0 +1,147 @@
1
+ import { setTimeout } from "node:timers/promises";
2
+ import { join } from "path";
3
+ import { log } from "./constants.mjs";
4
+ import { mkdirp } from "fs-extra";
5
+ import { state } from "./state.mjs";
6
+ import { generateFinalReport } from "./reporting.mjs";
7
+ import { cleanupResources } from "./cleanup.mjs";
8
+ // Re-export log from constants
9
+ export { log };
10
+ // Helper function to detect if running in CI environment
11
+ export function isRunningInCI(ciFlag = false) {
12
+ return (ciFlag ||
13
+ !!process.env.CI ||
14
+ !!process.env.GITHUB_ACTIONS ||
15
+ !!process.env.GITLAB_CI ||
16
+ !!process.env.CIRCLECI);
17
+ }
18
+ /**
19
+ * Handles test failure by logging the error and initiating teardown
20
+ */
21
+ export async function fail(error, exitCode = 1, step) {
22
+ state.exitCode = exitCode;
23
+ const msg = error instanceof Error ? error.message : String(error);
24
+ console.error(`❌ Smoke test failed: ${msg}`);
25
+ log("Test failed with error: %O", error);
26
+ // Record the failure if a step is provided
27
+ if (step) {
28
+ // Determine the environment context if not explicitly in the step name
29
+ let enhancedStep = step;
30
+ if (!step.toLowerCase().includes("development") &&
31
+ !step.toLowerCase().includes("production") &&
32
+ !step.toLowerCase().includes("dev server")) {
33
+ // For server/client side tests with phase info, add environment context
34
+ const isInReleasePhase = state.failures.some((f) => f.step.includes("Release Command") ||
35
+ f.step.includes("Release Test")) || state.options.skipDev; // If dev is skipped, we're in release phase
36
+ if (isInReleasePhase) {
37
+ enhancedStep = `Production - ${step}`;
38
+ }
39
+ else {
40
+ enhancedStep = `Development - ${step}`;
41
+ }
42
+ }
43
+ state.failures.push({
44
+ step: enhancedStep,
45
+ error: msg,
46
+ details: error instanceof Error && error.stack ? error.stack : undefined,
47
+ });
48
+ }
49
+ // Ensure artifactDir exists if it's defined but hasn't been created yet
50
+ if (state.options.artifactDir) {
51
+ try {
52
+ // Create the main artifacts directory and reports subdirectory if they don't exist
53
+ await mkdirp(state.options.artifactDir);
54
+ await mkdirp(join(state.options.artifactDir, "reports"));
55
+ log("Ensured artifact directories exist before teardown");
56
+ }
57
+ catch (dirError) {
58
+ log("Error ensuring artifact directories exist: %O", dirError);
59
+ // Non-fatal, continue to teardown
60
+ }
61
+ }
62
+ try {
63
+ // Generate a report before starting teardown to ensure we have at least one report
64
+ await generateFinalReport();
65
+ // Then proceed with teardown
66
+ await teardown();
67
+ }
68
+ catch (teardownError) {
69
+ // If teardown itself fails, log the error
70
+ console.error(`Error during teardown: ${teardownError instanceof Error ? teardownError.message : String(teardownError)}`);
71
+ // Set a short timeout to allow any pending operations to complete
72
+ await setTimeout(500);
73
+ }
74
+ // Set a short delay to allow report to be written
75
+ await setTimeout(500);
76
+ return process.exit(exitCode);
77
+ }
78
+ /**
79
+ * Handles resource teardown and exits the process with appropriate exit code
80
+ */
81
+ export async function teardown() {
82
+ // Prevent multiple teardowns running simultaneously
83
+ if (state.isTearingDown) {
84
+ log("Teardown already in progress, skipping duplicate call");
85
+ return;
86
+ }
87
+ state.isTearingDown = true;
88
+ log("Starting teardown process with exit code: %d", state.exitCode);
89
+ try {
90
+ // First, generate a report, before any cleanup happens
91
+ // This ensures we have at least some report even if cleanup fails
92
+ await generateFinalReport();
93
+ // Then try to cleanup resources
94
+ try {
95
+ await cleanupResources(state.resources, state.options);
96
+ log("Resource cleanup completed successfully");
97
+ }
98
+ catch (cleanupError) {
99
+ log("Error during resource cleanup: %O", cleanupError);
100
+ console.error(`Error during resource cleanup: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
101
+ // Add this error to our failures list
102
+ state.failures.push({
103
+ step: "Resource Cleanup",
104
+ error: cleanupError instanceof Error
105
+ ? cleanupError.message
106
+ : String(cleanupError),
107
+ details: cleanupError instanceof Error && cleanupError.stack
108
+ ? cleanupError.stack
109
+ : undefined,
110
+ });
111
+ // Set exit code to 1 if it wasn't already set
112
+ if (state.exitCode === 0)
113
+ state.exitCode = 1;
114
+ }
115
+ }
116
+ catch (error) {
117
+ log("Error during teardown: %O", error);
118
+ console.error(`Error during teardown: ${error instanceof Error ? error.message : String(error)}`);
119
+ // Set exit code to 1 if it wasn't already set
120
+ if (state.exitCode === 0)
121
+ state.exitCode = 1;
122
+ // Try generating report even if an error occurred
123
+ try {
124
+ await generateFinalReport();
125
+ }
126
+ catch (reportError) {
127
+ console.error("Failed to generate report after teardown error:", reportError);
128
+ }
129
+ }
130
+ finally {
131
+ // Make sure log capturing is stopped before exiting
132
+ log("Log capturing stopped during teardown");
133
+ process.exit(state.exitCode);
134
+ }
135
+ }
136
+ /**
137
+ * Formats the path suffix from a custom path
138
+ */
139
+ export function formatPathSuffix(customPath) {
140
+ const suffix = customPath
141
+ ? customPath.startsWith("/")
142
+ ? customPath
143
+ : `/${customPath}`
144
+ : "";
145
+ log("Formatted path suffix: %s", suffix);
146
+ return suffix;
147
+ }
@@ -0,0 +1,3 @@
1
+ import type { PackageRuleItem } from "vibe-rules";
2
+ declare const rules: PackageRuleItem[];
3
+ export default rules;
@@ -0,0 +1,35 @@
1
+ import { interruptors } from "./rules/interruptors.js";
2
+ import { middleware } from "./rules/middleware.js";
3
+ import { react } from "./rules/react.js";
4
+ import { requestResponse } from "./rules/request-response.js";
5
+ const rules = [
6
+ {
7
+ name: "rwsdk-interruptors",
8
+ description: "RedwoodSDK: Request Interruptors",
9
+ rule: interruptors,
10
+ alwaysApply: false,
11
+ globs: ["worker.tsx", "src/app/**/routes.ts", "src/app/**/*/routes.ts"],
12
+ },
13
+ {
14
+ name: "rwsdk-middleware",
15
+ description: "RedwoodSDK: Middleware",
16
+ rule: middleware,
17
+ alwaysApply: false,
18
+ globs: ["worker.tsx", "middleware.ts", "middleware.tsx"],
19
+ },
20
+ {
21
+ name: "rwsdk-react",
22
+ description: "RedwoodSDK: React, React Server Components, and React Server Functions Rules",
23
+ rule: react,
24
+ alwaysApply: false,
25
+ globs: ["src/app/**/*/*.tsx", "Document.tsx"],
26
+ },
27
+ {
28
+ name: "rwsdk-request-response",
29
+ description: "RedwoodSDK: Request handling and responses",
30
+ rule: requestResponse,
31
+ alwaysApply: false,
32
+ globs: ["worker.tsc", "src/app/**/routes.ts", "src/app/**/*/routes.ts"],
33
+ },
34
+ ];
35
+ export default rules;
@@ -0,0 +1 @@
1
+ export declare const interruptors = "\n\n# RedwoodSDK: Request Interruptors\n\nYou're an expert at Cloudflare, TypeScript, and building web apps with RedwoodSDK. Generate high quality **RedwoodSDK interruptors** (middleware functions) that adhere to the following best practices:\n\n## Guidelines\n\n1. Create focused, single-responsibility interruptors\n2. Organize interruptors in dedicated files (e.g., `interruptors.ts`, `interceptors.ts`, or `middleware.ts`)\n3. Compose interruptors to create more complex validation chains\n4. Use typed parameters and return values\n5. Include clear error handling and user feedback\n\n## What are Interruptors?\n\nInterruptors are middleware functions that run before your route handlers. They can:\n\n- Validate user authentication and authorization\n- Transform request data\n- Validate inputs\n- Rate limit requests\n- Log activity\n- Redirect users based on conditions\n- Short-circuit request handling with early responses\n\n## Example Templates\n\n### Basic Interruptor Structure\n\n```tsx\nasync function myInterruptor({ request, params, ctx }) {\n // Perform checks or transformations here\n\n // Return modified context to pass to the next interruptor or handler\n ctx.someAddedData = \"value\";\n\n // OR return a Response to short-circuit the request\n // return new Response('Unauthorized', { status: 401 });\n}\n```\n\n### Authentication Interruptors\n\n```tsx\nexport async function requireAuth({ request, ctx }) {\n if (!ctx.user) {\n return new Response(null, {\n status: 302,\n headers: { Location: \"/user/login\" },\n });\n }\n}\n\nexport async function requireAdmin({ request, ctx }) {\n if (!ctx?.user?.isAdmin) {\n return new Response(null, {\n status: 302,\n headers: { Location: \"/user/login\" },\n });\n }\n}\n```\n\n### Input Validation Interruptor\n\n```tsx\nimport { z } from \"zod\";\n\n// Create a reusable validator interruptor\nexport function validateInput(schema) {\n return async function validateInputInterruptor({ request, ctx }) {\n try {\n const data = await request.json();\n const validated = (ctx.data = schema.parse(data));\n } catch (error) {\n return Response.json(\n { error: \"Validation failed\", details: error.errors },\n { status: 400 },\n );\n }\n };\n}\n\n// Usage example with a Zod schema\nconst userSchema = z.object({\n name: z.string().min(2),\n email: z.string().email(),\n age: z.number().min(18).optional(),\n});\n\nexport const validateUser = validateInput(userSchema);\n```\n\n### Logging Interruptor\n\n```tsx\nexport async function logRequests({ request, ctx }) {\n const start = Date.now();\n\n // Add a function to the context that will log when called\n ctx.logCompletion: (response) => {\n const duration = Date.now() - start;\n const status = response.status;\n console.log(\n `${request.method} ${request.url} - ${status} (${duration}ms)`,\n );\n },\n };\n}\n\n// Usage in a route handler\nroute('/', [\n logRequests,\n async ({request, ctx}) => {\n // Call the logging function\n ctx.logCompletion(response);\n return Response.json({ success: true });;\n },\n]);\n```\n\n### Composing Multiple Interruptors\n\n```tsx\nimport { route } from \"rwsdk/router\";\nimport {\n requireAuth,\n validateUser,\n apiRateLimit,\n logRequests,\n} from \"@/app/interruptors\";\n\n// Combine multiple interruptors\nroute(\"/api/users\", [\n logRequests, // Log all requests\n requireAuth, // Ensure user is authenticated\n validateUser, // Validate user input\n async ({ request, ctx }) => {\n // Handler receives validated data and session from interruptors\n const newUser = await db.user.create({\n data: {\n /* ... */,\n createdBy: ctx.user.userId,\n },\n });\n\n return Response.json(newUser, { status: 201 });\n },\n ],\n});\n```\n\n### Role-Based Access Control\n\n```tsx\nimport { getSession } from \"rwsdk/auth\";\n\n// Create a function that generates role-based interruptors\nexport function hasRole(allowedRoles) {\n return async function hasRoleInterruptor({ request, ctx }) {\n const session = await getSession(request);\n\n if (!session) {\n return Response.redirect(\"/login\");\n }\n\n if (!allowedRoles.includes(session.role)) {\n return Response.json({ error: \"Unauthorized\" }, { status: 403 });\n }\n\n return { ...ctx, session };\n };\n}\n\n// Create specific role-based interruptors\nexport const isAdmin = hasRole([\"ADMIN\"]);\nexport const isEditor = hasRole([\"ADMIN\", \"EDITOR\"]);\nexport const isUser = hasRole([\"ADMIN\", \"EDITOR\", \"USER\"]);\n```\n\n### Organization with Co-located Interruptors\n\nCreate a file at `./src/app/interruptors.ts`:\n\n```tsx\nimport { getSession } from \"rwsdk/auth\";\n\n// Authentication interruptors\nexport async function requireAuth({ request, ctx }) {\n const session = await getSession(request);\n\n if (!session) {\n return Response.redirect(\"/login\");\n }\n\n return { ...ctx, session };\n}\n\n// Role-based interruptors\nexport function hasRole(allowedRoles) {\n return async function hasRoleInterruptor({ request, ctx }) {\n const session = await getSession(request);\n\n if (!session) {\n return Response.redirect(\"/login\");\n }\n\n if (!allowedRoles.includes(session.role)) {\n return Response.json({ error: \"Unauthorized\" }, { status: 403 });\n }\n\n return { ...ctx, session };\n };\n}\n\nexport const isAdmin = hasRole([\"ADMIN\"]);\nexport const isEditor = hasRole([\"ADMIN\", \"EDITOR\"]);\n\n// Other common interruptors\nexport async function logRequests({ request, ctx }) {\n console.log(`${request.method} ${request.url}`);\n return ctx;\n}\n```\n\nThen import these interruptors in your route files:\n\n```tsx\n// src/app/pages/admin/routes.ts\nimport { route } from \"rwsdk/router\";\nimport { isAdmin, logRequests } from \"@/app/interruptors\";\n\nimport { AdminDashboard } from \"./AdminDashboard\";\nimport { UserManagement } from \"./UserManagement\";\n\nexport const routes = [\n route(\"/\", [isAdmin, logRequests, AdminDashboard]),\n route(\"/users\", [isAdmin, logRequests, UserManagement]),\n];\n```\n\n";