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.
- package/dist/lib/$.d.mts +8 -0
- package/dist/lib/$.mjs +5 -0
- package/dist/lib/compileTsModule.d.mts +1 -0
- package/dist/lib/compileTsModule.mjs +27 -0
- package/dist/lib/constants.d.mts +4 -0
- package/dist/lib/constants.mjs +6 -0
- package/dist/lib/findWranglerConfig.d.mts +1 -0
- package/dist/lib/findWranglerConfig.mjs +12 -0
- package/dist/lib/getShortName.d.mts +1 -0
- package/dist/lib/getShortName.mjs +2 -0
- package/dist/lib/getSrcPaths.d.ts +15 -0
- package/dist/lib/getSrcPaths.js +80 -0
- package/dist/lib/hasPkgScript.d.mts +1 -0
- package/dist/lib/hasPkgScript.mjs +9 -0
- package/dist/lib/jsonUtils.d.mts +28 -0
- package/dist/lib/jsonUtils.mjs +167 -0
- package/dist/lib/setupEnvFiles.d.mts +4 -0
- package/dist/lib/setupEnvFiles.mjs +31 -0
- package/dist/lib/smokeTests/artifacts.d.mts +10 -0
- package/dist/lib/smokeTests/artifacts.mjs +164 -0
- package/dist/lib/smokeTests/browser.d.mts +48 -0
- package/dist/lib/smokeTests/browser.mjs +1041 -0
- package/dist/lib/smokeTests/cleanup.d.mts +5 -0
- package/dist/lib/smokeTests/cleanup.mjs +214 -0
- package/dist/lib/smokeTests/codeUpdates.d.mts +8 -0
- package/dist/lib/smokeTests/codeUpdates.mjs +229 -0
- package/dist/lib/smokeTests/constants.d.mts +5 -0
- package/dist/lib/smokeTests/constants.mjs +10 -0
- package/dist/lib/smokeTests/development.d.mts +11 -0
- package/dist/lib/smokeTests/development.mjs +209 -0
- package/dist/lib/smokeTests/environment.d.mts +14 -0
- package/dist/lib/smokeTests/environment.mjs +163 -0
- package/dist/lib/smokeTests/release.d.mts +61 -0
- package/dist/lib/smokeTests/release.mjs +526 -0
- package/dist/lib/smokeTests/reporting.d.mts +13 -0
- package/dist/lib/smokeTests/reporting.mjs +355 -0
- package/dist/lib/smokeTests/runSmokeTests.d.mts +5 -0
- package/dist/lib/smokeTests/runSmokeTests.mjs +144 -0
- package/dist/lib/smokeTests/state.d.mts +48 -0
- package/dist/lib/smokeTests/state.mjs +57 -0
- package/dist/lib/smokeTests/templates/SmokeTest.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/SmokeTest.template.js +81 -0
- package/dist/lib/smokeTests/templates/SmokeTestClient.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/SmokeTestClient.template.js +159 -0
- package/dist/lib/smokeTests/templates/smokeTestFunctions.template.d.ts +1 -0
- package/dist/lib/smokeTests/templates/smokeTestFunctions.template.js +19 -0
- package/dist/lib/smokeTests/types.d.mts +75 -0
- package/dist/lib/smokeTests/types.mjs +1 -0
- package/dist/lib/smokeTests/utils.d.mts +15 -0
- package/dist/lib/smokeTests/utils.mjs +147 -0
- package/dist/llms/index.d.ts +3 -0
- package/dist/llms/index.js +35 -0
- package/dist/llms/rules/interruptors.d.ts +1 -0
- package/dist/llms/rules/interruptors.js +243 -0
- package/dist/llms/rules/middleware.d.ts +1 -0
- package/dist/llms/rules/middleware.js +71 -0
- package/dist/llms/rules/react.d.ts +1 -0
- package/dist/llms/rules/react.js +106 -0
- package/dist/llms/rules/request-response.d.ts +1 -0
- package/dist/llms/rules/request-response.js +209 -0
- package/dist/runtime/client.d.ts +17 -0
- package/dist/runtime/client.js +74 -0
- package/dist/runtime/clientNavigation.d.ts +4 -0
- package/dist/runtime/clientNavigation.js +53 -0
- package/dist/runtime/clientNavigation.test.d.ts +1 -0
- package/dist/runtime/clientNavigation.test.js +55 -0
- package/dist/runtime/constants.d.ts +1 -0
- package/dist/runtime/constants.js +1 -0
- package/dist/runtime/entries/auth.d.ts +1 -0
- package/dist/runtime/entries/auth.js +1 -0
- package/dist/runtime/entries/client.d.ts +4 -0
- package/dist/runtime/entries/client.js +4 -0
- package/dist/runtime/entries/clientSSR.d.ts +1 -0
- package/dist/runtime/entries/clientSSR.js +1 -0
- package/dist/runtime/entries/navigation.d.ts +1 -0
- package/dist/runtime/entries/navigation.js +1 -0
- package/dist/runtime/entries/no-react-server.d.ts +0 -0
- package/dist/runtime/entries/no-react-server.js +2 -0
- package/dist/runtime/entries/react-server-only.d.ts +0 -0
- package/dist/runtime/entries/react-server-only.js +2 -0
- package/dist/runtime/entries/router.d.ts +2 -0
- package/dist/runtime/entries/router.js +2 -0
- package/dist/runtime/entries/ssr.d.ts +1 -0
- package/dist/runtime/entries/ssr.js +1 -0
- package/dist/runtime/entries/worker.d.ts +9 -0
- package/dist/runtime/entries/worker.js +9 -0
- package/dist/runtime/error.d.ts +6 -0
- package/dist/runtime/error.js +8 -0
- package/dist/runtime/imports/ClientOnly.d.ts +3 -0
- package/dist/runtime/imports/ClientOnly.js +8 -0
- package/dist/runtime/imports/client.d.ts +4 -0
- package/dist/runtime/imports/client.js +33 -0
- package/dist/runtime/imports/ssr.d.ts +5 -0
- package/dist/runtime/imports/ssr.js +20 -0
- package/dist/runtime/imports/worker.d.ts +5 -0
- package/dist/runtime/imports/worker.js +22 -0
- package/dist/runtime/lib/auth/index.d.ts +1 -0
- package/dist/runtime/lib/auth/index.js +1 -0
- package/dist/runtime/lib/auth/session.d.ts +50 -0
- package/dist/runtime/lib/auth/session.js +148 -0
- package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
- package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
- package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
- package/dist/runtime/lib/db/createDb.d.ts +2 -0
- package/dist/runtime/lib/db/createDb.js +33 -0
- package/dist/runtime/lib/db/index.d.ts +4 -0
- package/dist/runtime/lib/db/index.js +3 -0
- package/dist/runtime/lib/db/migrations.d.ts +23 -0
- package/dist/runtime/lib/db/migrations.js +34 -0
- package/dist/runtime/lib/db/typeInference/assert.d.ts +2 -0
- package/dist/runtime/lib/db/typeInference/assert.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/alterColumn.d.ts +27 -0
- package/dist/runtime/lib/db/typeInference/builders/alterColumn.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +53 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +26 -0
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +49 -0
- package/dist/runtime/lib/db/typeInference/builders/createTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +17 -0
- package/dist/runtime/lib/db/typeInference/builders/createView.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +11 -0
- package/dist/runtime/lib/db/typeInference/builders/dropTable.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +12 -0
- package/dist/runtime/lib/db/typeInference/builders/dropView.js +1 -0
- package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +24 -0
- package/dist/runtime/lib/db/typeInference/builders/schema.js +1 -0
- package/dist/runtime/lib/db/typeInference/database.d.ts +27 -0
- package/dist/runtime/lib/db/typeInference/database.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +360 -0
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +33 -0
- package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/dropTable.typetest.js +143 -0
- package/dist/runtime/lib/db/typeInference/typetests/print.d.ts +3 -0
- package/dist/runtime/lib/db/typeInference/typetests/print.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +2 -0
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.js +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/typetests/typeInference.typetest.js +17 -0
- package/dist/runtime/lib/db/typeInference/utils.d.ts +82 -0
- package/dist/runtime/lib/db/typeInference/utils.js +2 -0
- package/dist/runtime/lib/debug.d.ts +2 -0
- package/dist/runtime/lib/debug.js +36 -0
- package/dist/runtime/lib/links.d.ts +14 -0
- package/dist/runtime/lib/links.js +38 -0
- package/dist/runtime/lib/realtime/client.d.ts +7 -0
- package/dist/runtime/lib/realtime/client.js +166 -0
- package/dist/runtime/lib/realtime/constants.d.ts +1 -0
- package/dist/runtime/lib/realtime/constants.js +1 -0
- package/dist/runtime/lib/realtime/durableObject.d.ts +29 -0
- package/dist/runtime/lib/realtime/durableObject.js +187 -0
- package/dist/runtime/lib/realtime/renderRealtimeClients.d.ts +7 -0
- package/dist/runtime/lib/realtime/renderRealtimeClients.js +6 -0
- package/dist/runtime/lib/realtime/shared.d.ts +10 -0
- package/dist/runtime/lib/realtime/shared.js +10 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.d.ts +6 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.js +29 -0
- package/dist/runtime/lib/realtime/worker.d.ts +3 -0
- package/dist/runtime/lib/realtime/worker.js +16 -0
- package/dist/runtime/lib/router.d.ts +56 -0
- package/dist/runtime/lib/router.js +210 -0
- package/dist/runtime/lib/router.test.d.ts +1 -0
- package/dist/runtime/lib/router.test.js +58 -0
- package/dist/runtime/lib/streams/consumeEventStream.d.ts +4 -0
- package/dist/runtime/lib/streams/consumeEventStream.js +13 -0
- package/dist/runtime/lib/turnstile/TurnstileScript.d.ts +1 -0
- package/dist/runtime/lib/turnstile/TurnstileScript.js +2 -0
- package/dist/runtime/lib/turnstile/turnstile.d.ts +3 -0
- package/dist/runtime/lib/turnstile/turnstile.js +3 -0
- package/dist/runtime/lib/turnstile/useTurnstile.d.ts +4 -0
- package/dist/runtime/lib/turnstile/useTurnstile.js +23 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +4 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +15 -0
- package/dist/runtime/lib/utils.d.ts +1 -0
- package/dist/runtime/lib/utils.js +1 -0
- package/dist/runtime/register/client.d.ts +1 -0
- package/dist/runtime/register/client.js +5 -0
- package/dist/runtime/register/ssr.d.ts +3 -0
- package/dist/runtime/register/ssr.js +26 -0
- package/dist/runtime/register/worker.d.ts +4 -0
- package/dist/runtime/register/worker.js +42 -0
- package/dist/runtime/render/createClientManifest.d.ts +1 -0
- package/dist/runtime/render/createClientManifest.js +7 -0
- package/dist/runtime/render/createModuleMap.d.ts +1 -0
- package/dist/runtime/render/createModuleMap.js +13 -0
- package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
- package/dist/runtime/render/injectRSCPayload.js +79 -0
- package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +9 -0
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +49 -0
- package/dist/runtime/render/renderToRscStream.d.ts +5 -0
- package/dist/runtime/render/renderToRscStream.js +46 -0
- package/dist/runtime/render/renderToStream.d.ts +9 -0
- package/dist/runtime/render/renderToStream.js +27 -0
- package/dist/runtime/render/renderToString.d.ts +7 -0
- package/dist/runtime/render/renderToString.js +26 -0
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +8 -0
- package/dist/runtime/render/transformRscToHtmlStream.js +19 -0
- package/dist/runtime/requestInfo/types.d.ts +11 -0
- package/dist/runtime/requestInfo/types.js +1 -0
- package/dist/runtime/requestInfo/worker.d.ts +5 -0
- package/dist/runtime/requestInfo/worker.js +33 -0
- package/dist/runtime/script.d.ts +5 -0
- package/dist/runtime/script.js +8 -0
- package/dist/runtime/ssrBridge.d.ts +2 -0
- package/dist/runtime/ssrBridge.js +11 -0
- package/dist/runtime/worker.d.ts +18 -0
- package/dist/runtime/worker.js +173 -0
- package/dist/scripts/__sdk.d.mts +1 -0
- package/dist/scripts/__sdk.mjs +14 -0
- package/dist/scripts/build-vendor-bundles.d.mts +1 -0
- package/dist/scripts/build-vendor-bundles.mjs +92 -0
- package/dist/scripts/debug-sync.d.mts +6 -0
- package/dist/scripts/debug-sync.mjs +224 -0
- package/dist/scripts/dev-init.d.mts +1 -0
- package/dist/scripts/dev-init.mjs +25 -0
- package/dist/scripts/ensure-deploy-env.d.mts +1 -0
- package/dist/scripts/ensure-deploy-env.mjs +271 -0
- package/dist/scripts/ensure-env.d.mts +1 -0
- package/dist/scripts/ensure-env.mjs +9 -0
- package/dist/scripts/migrate-new.d.mts +1 -0
- package/dist/scripts/migrate-new.mjs +51 -0
- package/dist/scripts/smoke-test.d.mts +1 -0
- package/dist/scripts/smoke-test.mjs +166 -0
- package/dist/scripts/worker-run.d.mts +1 -0
- package/dist/scripts/worker-run.mjs +82 -0
- package/dist/vite/aliasByEnvPlugin.d.mts +2 -0
- package/dist/vite/aliasByEnvPlugin.mjs +11 -0
- package/dist/vite/asyncSetupPlugin.d.mts +6 -0
- package/dist/vite/asyncSetupPlugin.mjs +23 -0
- package/dist/vite/checkIsUsingPrisma.d.mts +6 -0
- package/dist/vite/checkIsUsingPrisma.mjs +18 -0
- package/dist/vite/configPlugin.d.mts +9 -0
- package/dist/vite/configPlugin.mjs +169 -0
- package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
- package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
- package/dist/vite/createDirectiveLookupPlugin.d.mts +21 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +231 -0
- package/dist/vite/customReactBuildPlugin.d.mts +4 -0
- package/dist/vite/customReactBuildPlugin.mjs +61 -0
- package/dist/vite/devServerTimingPlugin.d.mts +2 -0
- package/dist/vite/devServerTimingPlugin.mjs +24 -0
- package/dist/vite/directivesPlugin.d.mts +6 -0
- package/dist/vite/directivesPlugin.mjs +200 -0
- package/dist/vite/ensureAliasArray.d.mts +2 -0
- package/dist/vite/ensureAliasArray.mjs +17 -0
- package/dist/vite/findSpecifiers.d.mts +31 -0
- package/dist/vite/findSpecifiers.mjs +230 -0
- package/dist/vite/findSsrSpecifiers.d.mts +11 -0
- package/dist/vite/findSsrSpecifiers.mjs +67 -0
- package/dist/vite/hasDirective.d.mts +7 -0
- package/dist/vite/hasDirective.mjs +54 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
- package/dist/vite/index.d.mts +1 -0
- package/dist/vite/index.mjs +1 -0
- package/dist/vite/injectHmrPreambleJsxPlugin.d.mts +2 -0
- package/dist/vite/injectHmrPreambleJsxPlugin.mjs +22 -0
- package/dist/vite/injectVitePreamblePlugin.d.mts +4 -0
- package/dist/vite/injectVitePreamblePlugin.mjs +23 -0
- package/dist/vite/invalidateCacheIfPrismaClientChanged.d.mts +3 -0
- package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +27 -0
- package/dist/vite/invalidateModule.d.mts +6 -0
- package/dist/vite/invalidateModule.mjs +30 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +10 -0
- package/dist/vite/miniflareHMRPlugin.mjs +209 -0
- package/dist/vite/miniflarePlugin.d.mts +9 -0
- package/dist/vite/miniflarePlugin.mjs +135 -0
- package/dist/vite/moveStaticAssetsPlugin.d.mts +4 -0
- package/dist/vite/moveStaticAssetsPlugin.mjs +12 -0
- package/dist/vite/normalizeModulePath.d.mts +1 -0
- package/dist/vite/normalizeModulePath.mjs +13 -0
- package/dist/vite/prismaPlugin.d.mts +4 -0
- package/dist/vite/prismaPlugin.mjs +43 -0
- package/dist/vite/reactConditionsResolverPlugin.d.mts +16 -0
- package/dist/vite/reactConditionsResolverPlugin.mjs +179 -0
- package/dist/vite/redwoodPlugin.d.mts +12 -0
- package/dist/vite/redwoodPlugin.mjs +105 -0
- package/dist/vite/requestUtils.d.mts +6 -0
- package/dist/vite/requestUtils.mjs +35 -0
- package/dist/vite/setupEnvFiles.d.mts +4 -0
- package/dist/vite/setupEnvFiles.mjs +31 -0
- package/dist/vite/ssrBridgePlugin.d.mts +7 -0
- package/dist/vite/ssrBridgePlugin.mjs +137 -0
- package/dist/vite/transformClientComponents.d.mts +12 -0
- package/dist/vite/transformClientComponents.mjs +116 -0
- package/dist/vite/transformClientComponents.test.d.mts +1 -0
- package/dist/vite/transformClientComponents.test.mjs +264 -0
- package/dist/vite/transformJsxScriptTagsPlugin.d.mts +8 -0
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +315 -0
- package/dist/vite/transformJsxScriptTagsPlugin.test.d.mts +1 -0
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +334 -0
- package/dist/vite/transformServerFunctions.d.mts +16 -0
- package/dist/vite/transformServerFunctions.mjs +296 -0
- package/dist/vite/transformServerFunctions.test.d.mts +1 -0
- package/dist/vite/transformServerFunctions.test.mjs +124 -0
- package/dist/vite/useClientLookupPlugin.d.mts +5 -0
- package/dist/vite/useClientLookupPlugin.mjs +15 -0
- package/dist/vite/useClientPlugin.d.mts +8 -0
- package/dist/vite/useClientPlugin.mjs +295 -0
- package/dist/vite/useClientPlugin.test.d.mts +1 -0
- package/dist/vite/useClientPlugin.test.mjs +1204 -0
- package/dist/vite/useServerLookupPlugin.d.mts +5 -0
- package/dist/vite/useServerLookupPlugin.mjs +15 -0
- package/dist/vite/useServerPlugin.d.mts +1 -0
- package/dist/vite/useServerPlugin.mjs +1 -0
- package/dist/vite/virtualPlugin.d.mts +2 -0
- package/dist/vite/virtualPlugin.mjs +18 -0
- package/dist/vite/vitePreamblePlugin.d.mts +1 -0
- package/dist/vite/vitePreamblePlugin.mjs +11 -0
- package/dist/worker/__ssr_bridge.js +8947 -0
- package/dist/worker/__ssr_bridge.js.map +1 -0
- package/package.json +1 -1
- package/dist/vite/invalidateClientModule.d.mts +0 -2
- package/dist/vite/invalidateClientModule.mjs +0 -8
- package/dist/vite/invalidateModule copy.d.mts +0 -2
- package/dist/vite/invalidateModule copy.mjs +0 -14
- package/dist/vite/invalidateSSRModule.d.mts +0 -2
- package/dist/vite/invalidateSSRModule.mjs +0 -7
- package/dist/vite/mode.d.mts +0 -5
- package/dist/vite/mode.mjs +0 -25
- package/dist/vite/modePlugin.d.mts +0 -2
- package/dist/vite/modePlugin.mjs +0 -10
- /package/dist/vite/{isJsFile.d.ts → isJsFile.d.mts} +0 -0
- /package/dist/vite/{isJsFile.js → isJsFile.mjs} +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
export const interruptors = `
|
|
2
|
+
|
|
3
|
+
# RedwoodSDK: Request Interruptors
|
|
4
|
+
|
|
5
|
+
You'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:
|
|
6
|
+
|
|
7
|
+
## Guidelines
|
|
8
|
+
|
|
9
|
+
1. Create focused, single-responsibility interruptors
|
|
10
|
+
2. Organize interruptors in dedicated files (e.g., \`interruptors.ts\`, \`interceptors.ts\`, or \`middleware.ts\`)
|
|
11
|
+
3. Compose interruptors to create more complex validation chains
|
|
12
|
+
4. Use typed parameters and return values
|
|
13
|
+
5. Include clear error handling and user feedback
|
|
14
|
+
|
|
15
|
+
## What are Interruptors?
|
|
16
|
+
|
|
17
|
+
Interruptors are middleware functions that run before your route handlers. They can:
|
|
18
|
+
|
|
19
|
+
- Validate user authentication and authorization
|
|
20
|
+
- Transform request data
|
|
21
|
+
- Validate inputs
|
|
22
|
+
- Rate limit requests
|
|
23
|
+
- Log activity
|
|
24
|
+
- Redirect users based on conditions
|
|
25
|
+
- Short-circuit request handling with early responses
|
|
26
|
+
|
|
27
|
+
## Example Templates
|
|
28
|
+
|
|
29
|
+
### Basic Interruptor Structure
|
|
30
|
+
|
|
31
|
+
\`\`\`tsx
|
|
32
|
+
async function myInterruptor({ request, params, ctx }) {
|
|
33
|
+
// Perform checks or transformations here
|
|
34
|
+
|
|
35
|
+
// Return modified context to pass to the next interruptor or handler
|
|
36
|
+
ctx.someAddedData = "value";
|
|
37
|
+
|
|
38
|
+
// OR return a Response to short-circuit the request
|
|
39
|
+
// return new Response('Unauthorized', { status: 401 });
|
|
40
|
+
}
|
|
41
|
+
\`\`\`
|
|
42
|
+
|
|
43
|
+
### Authentication Interruptors
|
|
44
|
+
|
|
45
|
+
\`\`\`tsx
|
|
46
|
+
export async function requireAuth({ request, ctx }) {
|
|
47
|
+
if (!ctx.user) {
|
|
48
|
+
return new Response(null, {
|
|
49
|
+
status: 302,
|
|
50
|
+
headers: { Location: "/user/login" },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function requireAdmin({ request, ctx }) {
|
|
56
|
+
if (!ctx?.user?.isAdmin) {
|
|
57
|
+
return new Response(null, {
|
|
58
|
+
status: 302,
|
|
59
|
+
headers: { Location: "/user/login" },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
### Input Validation Interruptor
|
|
66
|
+
|
|
67
|
+
\`\`\`tsx
|
|
68
|
+
import { z } from "zod";
|
|
69
|
+
|
|
70
|
+
// Create a reusable validator interruptor
|
|
71
|
+
export function validateInput(schema) {
|
|
72
|
+
return async function validateInputInterruptor({ request, ctx }) {
|
|
73
|
+
try {
|
|
74
|
+
const data = await request.json();
|
|
75
|
+
const validated = (ctx.data = schema.parse(data));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return Response.json(
|
|
78
|
+
{ error: "Validation failed", details: error.errors },
|
|
79
|
+
{ status: 400 },
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Usage example with a Zod schema
|
|
86
|
+
const userSchema = z.object({
|
|
87
|
+
name: z.string().min(2),
|
|
88
|
+
email: z.string().email(),
|
|
89
|
+
age: z.number().min(18).optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const validateUser = validateInput(userSchema);
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
### Logging Interruptor
|
|
96
|
+
|
|
97
|
+
\`\`\`tsx
|
|
98
|
+
export async function logRequests({ request, ctx }) {
|
|
99
|
+
const start = Date.now();
|
|
100
|
+
|
|
101
|
+
// Add a function to the context that will log when called
|
|
102
|
+
ctx.logCompletion: (response) => {
|
|
103
|
+
const duration = Date.now() - start;
|
|
104
|
+
const status = response.status;
|
|
105
|
+
console.log(
|
|
106
|
+
\`\${request.method} \${request.url} - \${status} (\${duration}ms)\`,
|
|
107
|
+
);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Usage in a route handler
|
|
113
|
+
route('/', [
|
|
114
|
+
logRequests,
|
|
115
|
+
async ({request, ctx}) => {
|
|
116
|
+
// Call the logging function
|
|
117
|
+
ctx.logCompletion(response);
|
|
118
|
+
return Response.json({ success: true });;
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
### Composing Multiple Interruptors
|
|
124
|
+
|
|
125
|
+
\`\`\`tsx
|
|
126
|
+
import { route } from "rwsdk/router";
|
|
127
|
+
import {
|
|
128
|
+
requireAuth,
|
|
129
|
+
validateUser,
|
|
130
|
+
apiRateLimit,
|
|
131
|
+
logRequests,
|
|
132
|
+
} from "@/app/interruptors";
|
|
133
|
+
|
|
134
|
+
// Combine multiple interruptors
|
|
135
|
+
route("/api/users", [
|
|
136
|
+
logRequests, // Log all requests
|
|
137
|
+
requireAuth, // Ensure user is authenticated
|
|
138
|
+
validateUser, // Validate user input
|
|
139
|
+
async ({ request, ctx }) => {
|
|
140
|
+
// Handler receives validated data and session from interruptors
|
|
141
|
+
const newUser = await db.user.create({
|
|
142
|
+
data: {
|
|
143
|
+
/* ... */,
|
|
144
|
+
createdBy: ctx.user.userId,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return Response.json(newUser, { status: 201 });
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
### Role-Based Access Control
|
|
155
|
+
|
|
156
|
+
\`\`\`tsx
|
|
157
|
+
import { getSession } from "rwsdk/auth";
|
|
158
|
+
|
|
159
|
+
// Create a function that generates role-based interruptors
|
|
160
|
+
export function hasRole(allowedRoles) {
|
|
161
|
+
return async function hasRoleInterruptor({ request, ctx }) {
|
|
162
|
+
const session = await getSession(request);
|
|
163
|
+
|
|
164
|
+
if (!session) {
|
|
165
|
+
return Response.redirect("/login");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!allowedRoles.includes(session.role)) {
|
|
169
|
+
return Response.json({ error: "Unauthorized" }, { status: 403 });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { ...ctx, session };
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Create specific role-based interruptors
|
|
177
|
+
export const isAdmin = hasRole(["ADMIN"]);
|
|
178
|
+
export const isEditor = hasRole(["ADMIN", "EDITOR"]);
|
|
179
|
+
export const isUser = hasRole(["ADMIN", "EDITOR", "USER"]);
|
|
180
|
+
\`\`\`
|
|
181
|
+
|
|
182
|
+
### Organization with Co-located Interruptors
|
|
183
|
+
|
|
184
|
+
Create a file at \`./src/app/interruptors.ts\`:
|
|
185
|
+
|
|
186
|
+
\`\`\`tsx
|
|
187
|
+
import { getSession } from "rwsdk/auth";
|
|
188
|
+
|
|
189
|
+
// Authentication interruptors
|
|
190
|
+
export async function requireAuth({ request, ctx }) {
|
|
191
|
+
const session = await getSession(request);
|
|
192
|
+
|
|
193
|
+
if (!session) {
|
|
194
|
+
return Response.redirect("/login");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { ...ctx, session };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Role-based interruptors
|
|
201
|
+
export function hasRole(allowedRoles) {
|
|
202
|
+
return async function hasRoleInterruptor({ request, ctx }) {
|
|
203
|
+
const session = await getSession(request);
|
|
204
|
+
|
|
205
|
+
if (!session) {
|
|
206
|
+
return Response.redirect("/login");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!allowedRoles.includes(session.role)) {
|
|
210
|
+
return Response.json({ error: "Unauthorized" }, { status: 403 });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { ...ctx, session };
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export const isAdmin = hasRole(["ADMIN"]);
|
|
218
|
+
export const isEditor = hasRole(["ADMIN", "EDITOR"]);
|
|
219
|
+
|
|
220
|
+
// Other common interruptors
|
|
221
|
+
export async function logRequests({ request, ctx }) {
|
|
222
|
+
console.log(\`\${request.method} \${request.url}\`);
|
|
223
|
+
return ctx;
|
|
224
|
+
}
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
Then import these interruptors in your route files:
|
|
228
|
+
|
|
229
|
+
\`\`\`tsx
|
|
230
|
+
// src/app/pages/admin/routes.ts
|
|
231
|
+
import { route } from "rwsdk/router";
|
|
232
|
+
import { isAdmin, logRequests } from "@/app/interruptors";
|
|
233
|
+
|
|
234
|
+
import { AdminDashboard } from "./AdminDashboard";
|
|
235
|
+
import { UserManagement } from "./UserManagement";
|
|
236
|
+
|
|
237
|
+
export const routes = [
|
|
238
|
+
route("/", [isAdmin, logRequests, AdminDashboard]),
|
|
239
|
+
route("/users", [isAdmin, logRequests, UserManagement]),
|
|
240
|
+
];
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const middleware = "\n\n# RedwoodSDK: Middleware\n\nYou're an expert at Cloudflare, TypeScript, and building web apps with RedwoodSDK. Generate high quality **RedwoodSDK middleware** that adhere to the following best practices:\n\n## Guidelines\n\n1. Create focused, single-responsibility middleware functions\n2. Organize middleware in dedicated files (e.g., `middleware.ts`, `middleware.tsx`)\n3. Use typed parameters and return values\n4. Include clear error handling and logging\n5. Follow the principle of least privilege\n6. Implement proper security headers and CORS policies\n7. Optimize for performance with caching strategies\n\n## What is Middleware?\n\nMiddleware functions in RedwoodSDK are functions that run on every request before your route handlers. They can:\n\n- Add security headers\n- Handle CORS\n- Implement caching strategies\n- Add request/response logging\n- Transform request/response data\n- Implement rate limiting\n- Add performance monitoring\n- Handle error boundaries\n- Setup sessions\n- Authenticate users\n\n## Example Templates\n\n### Basic Middleware Structure\n\n```tsx\nexport default defineApp([\n setCommonHeaders(),\n async ({ ctx, request, headers }) => {\n await setupDb(env);\n setupSessionStore(env);\n try {\n // Grab the session's data.\n ctx.session = await sessions.load(request);\n } catch (error) {\n if (error instanceof ErrorResponse && error.code === 401) {\n await sessions.remove(request, headers);\n headers.set(\"Location\", \"/user/login\");\n\n return new Response(null, {\n status: 302,\n headers,\n });\n }\n\n throw error;\n }\n\n // Populate the ctx with the user's data\n if (ctx.session?.userId) {\n ctx.user = await db.user.findUnique({\n where: {\n id: ctx.session.userId,\n },\n });\n }\n },\n // Route handlers\n]);\n```\n";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const middleware = `
|
|
2
|
+
|
|
3
|
+
# RedwoodSDK: Middleware
|
|
4
|
+
|
|
5
|
+
You're an expert at Cloudflare, TypeScript, and building web apps with RedwoodSDK. Generate high quality **RedwoodSDK middleware** that adhere to the following best practices:
|
|
6
|
+
|
|
7
|
+
## Guidelines
|
|
8
|
+
|
|
9
|
+
1. Create focused, single-responsibility middleware functions
|
|
10
|
+
2. Organize middleware in dedicated files (e.g., \`middleware.ts\`, \`middleware.tsx\`)
|
|
11
|
+
3. Use typed parameters and return values
|
|
12
|
+
4. Include clear error handling and logging
|
|
13
|
+
5. Follow the principle of least privilege
|
|
14
|
+
6. Implement proper security headers and CORS policies
|
|
15
|
+
7. Optimize for performance with caching strategies
|
|
16
|
+
|
|
17
|
+
## What is Middleware?
|
|
18
|
+
|
|
19
|
+
Middleware functions in RedwoodSDK are functions that run on every request before your route handlers. They can:
|
|
20
|
+
|
|
21
|
+
- Add security headers
|
|
22
|
+
- Handle CORS
|
|
23
|
+
- Implement caching strategies
|
|
24
|
+
- Add request/response logging
|
|
25
|
+
- Transform request/response data
|
|
26
|
+
- Implement rate limiting
|
|
27
|
+
- Add performance monitoring
|
|
28
|
+
- Handle error boundaries
|
|
29
|
+
- Setup sessions
|
|
30
|
+
- Authenticate users
|
|
31
|
+
|
|
32
|
+
## Example Templates
|
|
33
|
+
|
|
34
|
+
### Basic Middleware Structure
|
|
35
|
+
|
|
36
|
+
\`\`\`tsx
|
|
37
|
+
export default defineApp([
|
|
38
|
+
setCommonHeaders(),
|
|
39
|
+
async ({ ctx, request, headers }) => {
|
|
40
|
+
await setupDb(env);
|
|
41
|
+
setupSessionStore(env);
|
|
42
|
+
try {
|
|
43
|
+
// Grab the session's data.
|
|
44
|
+
ctx.session = await sessions.load(request);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error instanceof ErrorResponse && error.code === 401) {
|
|
47
|
+
await sessions.remove(request, headers);
|
|
48
|
+
headers.set("Location", "/user/login");
|
|
49
|
+
|
|
50
|
+
return new Response(null, {
|
|
51
|
+
status: 302,
|
|
52
|
+
headers,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Populate the ctx with the user's data
|
|
60
|
+
if (ctx.session?.userId) {
|
|
61
|
+
ctx.user = await db.user.findUnique({
|
|
62
|
+
where: {
|
|
63
|
+
id: ctx.session.userId,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
// Route handlers
|
|
69
|
+
]);
|
|
70
|
+
\`\`\`
|
|
71
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const react = "\n# React, React Server Components, and React Server Functions Rules\n\n## React Server Components (RSC)\n\n1. By default, all components are server components unless explicitly marked as client components.\n2. Server components are rendered on the server as HTML and streamed to the browser.\n3. Server components cannot include client-side interactivity (state, effects, event handlers).\n4. Server components can directly fetch data and include it in the initial payload.\n5. Server components can be async and can be wrapped in Suspense boundaries.\n\nExample:\n\n```tsx\nexport default function MyServerComponent() {\n return <div>Hello, from the server!</div>;\n}\n```\n\n## Client Components\n\n1. Must be explicitly marked with the \"use client\" directive at the top of the file.\n2. Required when the component needs:\n - Interactivity (click handlers, state management)\n - Browser APIs\n - Event listeners\n - Client-side effects\n - Client-side routing\n3. Will be hydrated by React in the browser.\n\nExample:\n\n```tsx\n\"use client\";\n\nexport default function MyClientComponent() {\n return <button onClick={() => console.log(\"clicked\")}>Click me</button>;\n}\n```\n\n## Data Fetching in Server Components\n\n1. Server components can directly fetch data without useEffect or other client-side data fetching methods.\n2. Use Suspense boundaries to handle loading states for async server components.\n3. Pass context (ctx) through props to child components that need it.\n\nExample:\n\n```tsx\nexport async function TodoList({ ctx }) {\n const todos = await db.todo.findMany({ where: { userId: ctx.user.id } });\n\n return (\n <ol>\n {todos.map((todo) => (\n <li key={todo.id}>{todo.title}</li>\n ))}\n </ol>\n );\n}\n```\n\n## Server Functions\n\n1. Must be marked with the \"use server\" directive at the top of the file.\n2. Can be imported and used in client components.\n3. Execute on the server when called from client components.\n4. Have access to the request context via requestInfo.ctx.\n5. Can handle form submissions and other server-side operations.\n\nExample:\n\n```tsx\n\"use server\";\n\nimport { requestInfo } from \"rwsdk/worker\";\n\nexport async function addTodo(formData: FormData) {\n const { ctx } = requestInfo;\n const title = formData.get(\"title\");\n await db.todo.create({ data: { title, userId: ctx.user.id } });\n}\n```\n\n## Context Usage\n\n1. Context is available to all server components and server functions.\n2. Access context via:\n - requestInfo in server functions:\n ```\n import { requestInfo } from \"rwsdk/worker\";\n const { ctx } = requestInfo\n ```\n3. Context is populated by middleware and interruptors and is request-scoped.\n\n## Best Practices\n\n1. Keep server components as the default choice unless client-side interactivity is needed.\n2. Use client components only when necessary to minimize the JavaScript bundle size.\n3. Leverage server components for data fetching and initial rendering.\n4. Use Suspense boundaries appropriately for loading states.\n5. Keep client components as small as possible, moving server-side logic to server components or server functions.\n6. Always mark client components with \"use client\" directive.\n7. Always mark server functions with \"use server\" directive.\n\n";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export const react = `
|
|
2
|
+
# React, React Server Components, and React Server Functions Rules
|
|
3
|
+
|
|
4
|
+
## React Server Components (RSC)
|
|
5
|
+
|
|
6
|
+
1. By default, all components are server components unless explicitly marked as client components.
|
|
7
|
+
2. Server components are rendered on the server as HTML and streamed to the browser.
|
|
8
|
+
3. Server components cannot include client-side interactivity (state, effects, event handlers).
|
|
9
|
+
4. Server components can directly fetch data and include it in the initial payload.
|
|
10
|
+
5. Server components can be async and can be wrapped in Suspense boundaries.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
|
|
14
|
+
\`\`\`tsx
|
|
15
|
+
export default function MyServerComponent() {
|
|
16
|
+
return <div>Hello, from the server!</div>;
|
|
17
|
+
}
|
|
18
|
+
\`\`\`
|
|
19
|
+
|
|
20
|
+
## Client Components
|
|
21
|
+
|
|
22
|
+
1. Must be explicitly marked with the "use client" directive at the top of the file.
|
|
23
|
+
2. Required when the component needs:
|
|
24
|
+
- Interactivity (click handlers, state management)
|
|
25
|
+
- Browser APIs
|
|
26
|
+
- Event listeners
|
|
27
|
+
- Client-side effects
|
|
28
|
+
- Client-side routing
|
|
29
|
+
3. Will be hydrated by React in the browser.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
|
|
33
|
+
\`\`\`tsx
|
|
34
|
+
"use client";
|
|
35
|
+
|
|
36
|
+
export default function MyClientComponent() {
|
|
37
|
+
return <button onClick={() => console.log("clicked")}>Click me</button>;
|
|
38
|
+
}
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
## Data Fetching in Server Components
|
|
42
|
+
|
|
43
|
+
1. Server components can directly fetch data without useEffect or other client-side data fetching methods.
|
|
44
|
+
2. Use Suspense boundaries to handle loading states for async server components.
|
|
45
|
+
3. Pass context (ctx) through props to child components that need it.
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
\`\`\`tsx
|
|
50
|
+
export async function TodoList({ ctx }) {
|
|
51
|
+
const todos = await db.todo.findMany({ where: { userId: ctx.user.id } });
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<ol>
|
|
55
|
+
{todos.map((todo) => (
|
|
56
|
+
<li key={todo.id}>{todo.title}</li>
|
|
57
|
+
))}
|
|
58
|
+
</ol>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
## Server Functions
|
|
64
|
+
|
|
65
|
+
1. Must be marked with the "use server" directive at the top of the file.
|
|
66
|
+
2. Can be imported and used in client components.
|
|
67
|
+
3. Execute on the server when called from client components.
|
|
68
|
+
4. Have access to the request context via requestInfo.ctx.
|
|
69
|
+
5. Can handle form submissions and other server-side operations.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
|
|
73
|
+
\`\`\`tsx
|
|
74
|
+
"use server";
|
|
75
|
+
|
|
76
|
+
import { requestInfo } from "rwsdk/worker";
|
|
77
|
+
|
|
78
|
+
export async function addTodo(formData: FormData) {
|
|
79
|
+
const { ctx } = requestInfo;
|
|
80
|
+
const title = formData.get("title");
|
|
81
|
+
await db.todo.create({ data: { title, userId: ctx.user.id } });
|
|
82
|
+
}
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
## Context Usage
|
|
86
|
+
|
|
87
|
+
1. Context is available to all server components and server functions.
|
|
88
|
+
2. Access context via:
|
|
89
|
+
- requestInfo in server functions:
|
|
90
|
+
\`\`\`
|
|
91
|
+
import { requestInfo } from "rwsdk/worker";
|
|
92
|
+
const { ctx } = requestInfo
|
|
93
|
+
\`\`\`
|
|
94
|
+
3. Context is populated by middleware and interruptors and is request-scoped.
|
|
95
|
+
|
|
96
|
+
## Best Practices
|
|
97
|
+
|
|
98
|
+
1. Keep server components as the default choice unless client-side interactivity is needed.
|
|
99
|
+
2. Use client components only when necessary to minimize the JavaScript bundle size.
|
|
100
|
+
3. Leverage server components for data fetching and initial rendering.
|
|
101
|
+
4. Use Suspense boundaries appropriately for loading states.
|
|
102
|
+
5. Keep client components as small as possible, moving server-side logic to server components or server functions.
|
|
103
|
+
6. Always mark client components with "use client" directive.
|
|
104
|
+
7. Always mark server functions with "use server" directive.
|
|
105
|
+
|
|
106
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const requestResponse = "\n# RedwoodSDK: Request handling and responses\n\nYou're an expert at Cloudflare, TypeScript, and building web apps in React. Generate high quality **RedwoodSDK route handlers** that adhere to the following best practices:\n\n## Guidelines\n\n1. Try to use Web APIs instead of external dependencies (e.g. use fetch instead of Axios, use WebSockets API instead of node-ws)\n2. Co-locate related routes into a separate `routes.ts` file in `./src/app/pages/<section>` (e.g. keep all \"user\" routes in `./src/app/pages/user/routes.ts`, all \"blog\" routes in `./src/app/pages/blog/routes.ts`), and then import them into `defineApp` with the `prefix` function\n4. Structure response data consistently with proper status codes\n5. Handle errors gracefully and return appropriate error responses\n\n## Example Templates\n\n### Basic Routing\n\nRoutes are matched in the order they are defined. Define routes using the `route` function. Trailing slashes are optional and normalized internally.\n\n#### Static Path Matching\n\n```tsx\n// Match exact pathnames\nroute(\"/\", function handler() {\n return <>Home Page</>\n})\n\nroute(\"/about\", function handler() {\n return <>About Page</>\n})\n\nroute(\"/contact\", function handler() {\n return <>Contact Page</>\n})\n```\n\n#### Dynamic Path Parameters\n\n```tsx\n// Match dynamic segments marked with a colon (:)\nroute(\"/users/:id\", function handler({ params }) {\n // params.id contains the value from the URL\n return <>User profile for {params.id}</>\n})\n\nroute(\"/posts/:postId/comments/:commentId\", function handler({ params }) {\n // Access multiple parameters\n return <>Comment {params.commentId} on Post {params.postId}</>\n})\n```\n\n#### Wildcard Path Matching\n\n```tsx\n// Match all remaining segments after the prefix\nroute(\"/files/*\", function handler({ params }) {\n // params.$0 contains the wildcard value\n return <>File: {params.$0}</>\n})\n\nroute(\"/docs/*/version/*\", function handler({ params }) {\n // Multiple wildcards available as params.$0, params.$1, etc.\n return <>Document: {params.$0}, Version: {params.$1}</>\n})\n```\n\n### Response Types\n\n#### Plain Text Response\n\n```tsx\nimport { route } from \"rwsdk/router\";\n\nroute(\"/api/status\", function handler() {\n return new Response(\"OK\", {\n status: 200,\n headers: { \"Content-Type\": \"text/plain\" }\n })\n})\n```\n\n#### JSON Response\n\n```tsx\nimport { route } from \"rwsdk/router\";\n\nroute(\"/api/users/:id\", function handler({ params }) {\n const userData = { id: params.id, name: \"John Doe\", email: \"john@example.com\" }\n\n return Response.json(userData, {\n status: 200,\n headers: {\n \"Cache-Control\": \"max-age=60\"\n }\n })\n})\n```\n\n#### JSX/React Components Response\n\n```tsx\nimport { route } from \"rwsdk/router\";\nimport { UserProfile } from '@/app/components/UserProfile'\n\nroute(\"/users/:id\", function handler({ params }) {\n return <UserProfile userId={params.id} />\n})\n```\n\n#### Custom Document Template\n\n```tsx\nimport { render, route } from \"rwsdk/router\";\nimport { Document } from '@/app/Document'\n\nrender(Document, [\n route(\"/\", function handler() {\n return <>Home Page</>\n }),\n route(\"/about\", function handler() {\n return <>About Page</>\n })\n])\n```\n\n### Error Handling\n\n```tsx\nimport { route } from \"rwsdk/router\";\n\nroute(\"/api/posts/:id\", async function handler({ params }) {\n try {\n const post = await db.post.findUnique({ where: { id: params.id } })\n\n if (!post) {\n return Response.json(\n { error: \"Post not found\" },\n { status: 404 }\n )\n }\n\n return Response.json(post)\n } catch (error) {\n console.error(error)\n return Response.json(\n { error: \"Failed to retrieve post\" },\n { status: 500 }\n )\n }\n})\n```\n\n### Organization with Co-located Routes\n\nCreate a file at `./src/app/pages/blog/routes.ts`:\n\n```tsx\nimport { route } from \"rwsdk/router\";\nimport { isAdminUser } from '@/app/interceptors'\n\nimport { BlogLandingPage } from './BlogLandingPage'\nimport { BlogPostPage } from './BlogPostPage'\nimport { BlogAdminPage } from './BlogAdminPage'\n\nexport const routes = [\n route('/', BlogLandingPage),\n route('/post/:postId', BlogPostPage),\n route('/post/:postId/edit', [isAdminUser, BlogAdminPage])\n]\n```\n\nThen import these routes in your main worker file:\n\n```tsx\n// src/worker.tsx\nimport { defineApp, render, route, prefix } from \"rwsdk/router\";\nimport { Document } from '@/app/Document'\nimport { HomePage } from '@/app/pages/home/HomePage'\nimport { routes as blogRoutes } from '@/app/pages/blog/routes'\n\nexport default defineApp([\n /* middleware */\n render(Document, [\n route('/', HomePage),\n prefix('/blog', blogRoutes)\n ]),\n])\n```\n\n### Advanced: Route with Query Parameters\n\n```tsx\nimport { route } from \"rwsdk/router\";\n\nroute(\"/api/search\", function handler({ request }) {\n const url = new URL(request.url)\n const query = url.searchParams.get('q') || ''\n const page = parseInt(url.searchParams.get('page') || '1')\n const limit = parseInt(url.searchParams.get('limit') || '10')\n\n return Response.json({\n query,\n page,\n limit,\n results: [] // Your search results would go here\n })\n})\n```\n\n";
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
export const requestResponse = `
|
|
2
|
+
# RedwoodSDK: Request handling and responses
|
|
3
|
+
|
|
4
|
+
You're an expert at Cloudflare, TypeScript, and building web apps in React. Generate high quality **RedwoodSDK route handlers** that adhere to the following best practices:
|
|
5
|
+
|
|
6
|
+
## Guidelines
|
|
7
|
+
|
|
8
|
+
1. Try to use Web APIs instead of external dependencies (e.g. use fetch instead of Axios, use WebSockets API instead of node-ws)
|
|
9
|
+
2. Co-locate related routes into a separate \`routes.ts\` file in \`./src/app/pages/<section>\` (e.g. keep all "user" routes in \`./src/app/pages/user/routes.ts\`, all "blog" routes in \`./src/app/pages/blog/routes.ts\`), and then import them into \`defineApp\` with the \`prefix\` function
|
|
10
|
+
4. Structure response data consistently with proper status codes
|
|
11
|
+
5. Handle errors gracefully and return appropriate error responses
|
|
12
|
+
|
|
13
|
+
## Example Templates
|
|
14
|
+
|
|
15
|
+
### Basic Routing
|
|
16
|
+
|
|
17
|
+
Routes are matched in the order they are defined. Define routes using the \`route\` function. Trailing slashes are optional and normalized internally.
|
|
18
|
+
|
|
19
|
+
#### Static Path Matching
|
|
20
|
+
|
|
21
|
+
\`\`\`tsx
|
|
22
|
+
// Match exact pathnames
|
|
23
|
+
route("/", function handler() {
|
|
24
|
+
return <>Home Page</>
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
route("/about", function handler() {
|
|
28
|
+
return <>About Page</>
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
route("/contact", function handler() {
|
|
32
|
+
return <>Contact Page</>
|
|
33
|
+
})
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
#### Dynamic Path Parameters
|
|
37
|
+
|
|
38
|
+
\`\`\`tsx
|
|
39
|
+
// Match dynamic segments marked with a colon (:)
|
|
40
|
+
route("/users/:id", function handler({ params }) {
|
|
41
|
+
// params.id contains the value from the URL
|
|
42
|
+
return <>User profile for {params.id}</>
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
route("/posts/:postId/comments/:commentId", function handler({ params }) {
|
|
46
|
+
// Access multiple parameters
|
|
47
|
+
return <>Comment {params.commentId} on Post {params.postId}</>
|
|
48
|
+
})
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
#### Wildcard Path Matching
|
|
52
|
+
|
|
53
|
+
\`\`\`tsx
|
|
54
|
+
// Match all remaining segments after the prefix
|
|
55
|
+
route("/files/*", function handler({ params }) {
|
|
56
|
+
// params.$0 contains the wildcard value
|
|
57
|
+
return <>File: {params.$0}</>
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
route("/docs/*/version/*", function handler({ params }) {
|
|
61
|
+
// Multiple wildcards available as params.$0, params.$1, etc.
|
|
62
|
+
return <>Document: {params.$0}, Version: {params.$1}</>
|
|
63
|
+
})
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
### Response Types
|
|
67
|
+
|
|
68
|
+
#### Plain Text Response
|
|
69
|
+
|
|
70
|
+
\`\`\`tsx
|
|
71
|
+
import { route } from "rwsdk/router";
|
|
72
|
+
|
|
73
|
+
route("/api/status", function handler() {
|
|
74
|
+
return new Response("OK", {
|
|
75
|
+
status: 200,
|
|
76
|
+
headers: { "Content-Type": "text/plain" }
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
#### JSON Response
|
|
82
|
+
|
|
83
|
+
\`\`\`tsx
|
|
84
|
+
import { route } from "rwsdk/router";
|
|
85
|
+
|
|
86
|
+
route("/api/users/:id", function handler({ params }) {
|
|
87
|
+
const userData = { id: params.id, name: "John Doe", email: "john@example.com" }
|
|
88
|
+
|
|
89
|
+
return Response.json(userData, {
|
|
90
|
+
status: 200,
|
|
91
|
+
headers: {
|
|
92
|
+
"Cache-Control": "max-age=60"
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
#### JSX/React Components Response
|
|
99
|
+
|
|
100
|
+
\`\`\`tsx
|
|
101
|
+
import { route } from "rwsdk/router";
|
|
102
|
+
import { UserProfile } from '@/app/components/UserProfile'
|
|
103
|
+
|
|
104
|
+
route("/users/:id", function handler({ params }) {
|
|
105
|
+
return <UserProfile userId={params.id} />
|
|
106
|
+
})
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
#### Custom Document Template
|
|
110
|
+
|
|
111
|
+
\`\`\`tsx
|
|
112
|
+
import { render, route } from "rwsdk/router";
|
|
113
|
+
import { Document } from '@/app/Document'
|
|
114
|
+
|
|
115
|
+
render(Document, [
|
|
116
|
+
route("/", function handler() {
|
|
117
|
+
return <>Home Page</>
|
|
118
|
+
}),
|
|
119
|
+
route("/about", function handler() {
|
|
120
|
+
return <>About Page</>
|
|
121
|
+
})
|
|
122
|
+
])
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
### Error Handling
|
|
126
|
+
|
|
127
|
+
\`\`\`tsx
|
|
128
|
+
import { route } from "rwsdk/router";
|
|
129
|
+
|
|
130
|
+
route("/api/posts/:id", async function handler({ params }) {
|
|
131
|
+
try {
|
|
132
|
+
const post = await db.post.findUnique({ where: { id: params.id } })
|
|
133
|
+
|
|
134
|
+
if (!post) {
|
|
135
|
+
return Response.json(
|
|
136
|
+
{ error: "Post not found" },
|
|
137
|
+
{ status: 404 }
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return Response.json(post)
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(error)
|
|
144
|
+
return Response.json(
|
|
145
|
+
{ error: "Failed to retrieve post" },
|
|
146
|
+
{ status: 500 }
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
### Organization with Co-located Routes
|
|
153
|
+
|
|
154
|
+
Create a file at \`./src/app/pages/blog/routes.ts\`:
|
|
155
|
+
|
|
156
|
+
\`\`\`tsx
|
|
157
|
+
import { route } from "rwsdk/router";
|
|
158
|
+
import { isAdminUser } from '@/app/interceptors'
|
|
159
|
+
|
|
160
|
+
import { BlogLandingPage } from './BlogLandingPage'
|
|
161
|
+
import { BlogPostPage } from './BlogPostPage'
|
|
162
|
+
import { BlogAdminPage } from './BlogAdminPage'
|
|
163
|
+
|
|
164
|
+
export const routes = [
|
|
165
|
+
route('/', BlogLandingPage),
|
|
166
|
+
route('/post/:postId', BlogPostPage),
|
|
167
|
+
route('/post/:postId/edit', [isAdminUser, BlogAdminPage])
|
|
168
|
+
]
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
Then import these routes in your main worker file:
|
|
172
|
+
|
|
173
|
+
\`\`\`tsx
|
|
174
|
+
// src/worker.tsx
|
|
175
|
+
import { defineApp, render, route, prefix } from "rwsdk/router";
|
|
176
|
+
import { Document } from '@/app/Document'
|
|
177
|
+
import { HomePage } from '@/app/pages/home/HomePage'
|
|
178
|
+
import { routes as blogRoutes } from '@/app/pages/blog/routes'
|
|
179
|
+
|
|
180
|
+
export default defineApp([
|
|
181
|
+
/* middleware */
|
|
182
|
+
render(Document, [
|
|
183
|
+
route('/', HomePage),
|
|
184
|
+
prefix('/blog', blogRoutes)
|
|
185
|
+
]),
|
|
186
|
+
])
|
|
187
|
+
\`\`\`
|
|
188
|
+
|
|
189
|
+
### Advanced: Route with Query Parameters
|
|
190
|
+
|
|
191
|
+
\`\`\`tsx
|
|
192
|
+
import { route } from "rwsdk/router";
|
|
193
|
+
|
|
194
|
+
route("/api/search", function handler({ request }) {
|
|
195
|
+
const url = new URL(request.url)
|
|
196
|
+
const query = url.searchParams.get('q') || ''
|
|
197
|
+
const page = parseInt(url.searchParams.get('page') || '1')
|
|
198
|
+
const limit = parseInt(url.searchParams.get('limit') || '10')
|
|
199
|
+
|
|
200
|
+
return Response.json({
|
|
201
|
+
query,
|
|
202
|
+
page,
|
|
203
|
+
limit,
|
|
204
|
+
results: [] // Your search results would go here
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
\`\`\`
|
|
208
|
+
|
|
209
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CallServerCallback } from "react-server-dom-webpack/client.browser";
|
|
2
|
+
import { type HydrationOptions } from "react-dom/client";
|
|
3
|
+
export type ActionResponse<Result> = {
|
|
4
|
+
node: React.ReactNode;
|
|
5
|
+
actionResult: Result;
|
|
6
|
+
};
|
|
7
|
+
type TransportContext = {
|
|
8
|
+
setRscPayload: <Result>(v: Promise<ActionResponse<Result>>) => void;
|
|
9
|
+
};
|
|
10
|
+
export type Transport = (context: TransportContext) => CallServerCallback;
|
|
11
|
+
export type CreateCallServer = (context: TransportContext) => <Result>(id: null | string, args: null | unknown[]) => Promise<Result>;
|
|
12
|
+
export declare const fetchTransport: Transport;
|
|
13
|
+
export declare const initClient: ({ transport, hydrateRootOptions, }?: {
|
|
14
|
+
transport?: Transport;
|
|
15
|
+
hydrateRootOptions?: HydrationOptions;
|
|
16
|
+
}) => Promise<void>;
|
|
17
|
+
export {};
|