create-jen-app 1.2.3 → 1.2.4
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/colors.js +0 -17
- package/dist/create.js +5 -17
- package/dist/generator.js +14 -31
- package/dist/index.js +6 -1
- package/package.json +1 -1
- package/templates/ssr-isr/README.md +77 -0
- package/templates/ssr-isr/build.js +118 -0
- package/templates/ssr-isr/jen.config.ts +109 -0
- package/templates/ssr-isr/jenjs.d.ts +22 -0
- package/templates/ssr-isr/lib/api/(hello).js +9 -0
- package/templates/ssr-isr/lib/auth/cookie-utils.js +79 -0
- package/templates/ssr-isr/lib/auth/index.js +2 -0
- package/templates/ssr-isr/lib/auth/jwt.js +57 -0
- package/templates/ssr-isr/lib/auth/session.js +92 -0
- package/templates/ssr-isr/lib/build/asset-hashing.d.ts +10 -0
- package/templates/ssr-isr/lib/build/asset-hashing.js +25 -0
- package/templates/ssr-isr/lib/build/asset-manifest.d.ts +11 -0
- package/templates/ssr-isr/lib/build/asset-manifest.js +21 -0
- package/templates/{static → ssr-isr}/lib/build/build.d.ts +1 -1
- package/templates/ssr-isr/lib/build/build.js +141 -0
- package/templates/{static → ssr-isr}/lib/build/island-hydration.d.ts +8 -5
- package/templates/ssr-isr/lib/build/island-hydration.js +44 -0
- package/templates/ssr-isr/lib/build/minifier.d.ts +20 -0
- package/templates/ssr-isr/lib/build/minifier.js +46 -0
- package/templates/ssr-isr/lib/build/page-renderer.d.ts +17 -0
- package/templates/ssr-isr/lib/build/page-renderer.js +28 -0
- package/templates/ssr-isr/lib/build/production-build.d.ts +10 -0
- package/templates/ssr-isr/lib/build/production-build.js +13 -0
- package/templates/ssr-isr/lib/build/ssg-pipeline.d.ts +15 -0
- package/templates/ssr-isr/lib/build/ssg-pipeline.js +113 -0
- package/templates/ssr-isr/lib/build-tools/build-site.js +36 -0
- package/templates/ssr-isr/lib/cache/index.js +10 -0
- package/templates/ssr-isr/lib/cache/memory.js +40 -0
- package/templates/ssr-isr/lib/cache/redis.js +61 -0
- package/templates/ssr-isr/lib/cli/banner.js +28 -0
- package/templates/ssr-isr/lib/cli/templates/ssg/jen.config.js +32 -0
- package/templates/ssr-isr/lib/cli/templates/ssg/site/index.js +9 -0
- package/templates/ssr-isr/lib/cli/templates/ssr/jen.config.js +32 -0
- package/templates/ssr-isr/lib/cli/templates/ssr/site/index.js +9 -0
- package/templates/ssr-isr/lib/compilers/esbuild-plugins.js +111 -0
- package/templates/ssr-isr/lib/compilers/svelte.js +44 -0
- package/templates/ssr-isr/lib/compilers/vue.js +90 -0
- package/templates/ssr-isr/lib/core/http.js +71 -0
- package/templates/ssr-isr/lib/core/middleware-hooks.js +97 -0
- package/templates/ssr-isr/lib/core/paths.js +39 -0
- package/templates/ssr-isr/lib/core/routes/match.js +47 -0
- package/templates/ssr-isr/lib/core/routes/scan.js +190 -0
- package/templates/ssr-isr/lib/core/types.js +1 -0
- package/templates/ssr-isr/lib/css/compiler.js +74 -0
- package/templates/ssr-isr/lib/db/connector.js +42 -0
- package/templates/ssr-isr/lib/db/drivers/jdb.js +44 -0
- package/templates/ssr-isr/lib/db/drivers/sql.js +182 -0
- package/templates/ssr-isr/lib/db/index.js +48 -0
- package/templates/ssr-isr/lib/db/types.js +1 -0
- package/templates/ssr-isr/lib/graphql/index.js +52 -0
- package/templates/ssr-isr/lib/graphql/resolvers.js +25 -0
- package/templates/ssr-isr/lib/graphql/schema.js +35 -0
- package/templates/ssr-isr/lib/i18n/en.json +4 -0
- package/templates/ssr-isr/lib/i18n/es.json +4 -0
- package/templates/ssr-isr/lib/i18n/index.js +15 -0
- package/templates/ssr-isr/lib/import/jen-import.js +161 -0
- package/templates/ssr-isr/lib/index.js +116 -0
- package/templates/ssr-isr/lib/jdb/engine.js +275 -0
- package/templates/ssr-isr/lib/jdb/index.js +34 -0
- package/templates/ssr-isr/lib/jdb/types.js +1 -0
- package/templates/ssr-isr/lib/jdb/utils.js +176 -0
- package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.js +0 -17
- package/templates/ssr-isr/lib/middleware/builtins/cors.js +54 -0
- package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.js +0 -17
- package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.js +0 -17
- package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.js +0 -17
- package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.js +0 -17
- package/templates/ssr-isr/lib/middleware/context.js +124 -0
- package/templates/{static → ssr-isr}/lib/middleware/decorators.js +0 -17
- package/templates/{static → ssr-isr}/lib/middleware/errors/handler.js +0 -17
- package/templates/ssr-isr/lib/middleware/errors/http-error.js +10 -0
- package/templates/ssr-isr/lib/middleware/kernel.js +85 -0
- package/templates/ssr-isr/lib/middleware/pipeline.js +148 -0
- package/templates/ssr-isr/lib/middleware/registry.js +85 -0
- package/templates/ssr-isr/lib/middleware/response.js +107 -0
- package/templates/ssr-isr/lib/middleware/types.d.ts +1 -0
- package/templates/ssr-isr/lib/middleware/types.js +1 -0
- package/templates/ssr-isr/lib/middleware/utils/matcher.js +13 -0
- package/templates/{static → ssr-isr}/lib/native/bundle.js +0 -17
- package/templates/{static → ssr-isr}/lib/native/dev-server.js +0 -17
- package/templates/{static → ssr-isr}/lib/native/index.js +0 -17
- package/templates/{static → ssr-isr}/lib/native/optimizer.js +0 -17
- package/templates/ssr-isr/lib/native/style-compiler.js +19 -0
- package/templates/ssr-isr/lib/plugin/loader.js +36 -0
- package/templates/ssr-isr/lib/runtime/client-runtime.js +25 -0
- package/templates/ssr-isr/lib/runtime/hmr.js +59 -0
- package/templates/ssr-isr/lib/runtime/hydrate.js +55 -0
- package/templates/ssr-isr/lib/runtime/island-hydration-client.js +146 -0
- package/templates/ssr-isr/lib/runtime/islands.js +110 -0
- package/templates/ssr-isr/lib/runtime/render.js +244 -0
- package/templates/ssr-isr/lib/server/api-routes.js +237 -0
- package/templates/ssr-isr/lib/server/api.js +108 -0
- package/templates/ssr-isr/lib/server/app.js +438 -0
- package/templates/ssr-isr/lib/server/runtimeServe.js +169 -0
- package/templates/ssr-isr/lib/server/ssr.js +202 -0
- package/templates/ssr-isr/lib/shared/log.js +64 -0
- package/templates/ssr-isr/package.json +23 -0
- package/templates/ssr-isr/server.js +128 -0
- package/templates/ssr-isr/site/pages/(index).tsx +11 -0
- package/templates/ssr-isr/site/styles/global.scss +37 -0
- package/templates/ssr-isr/tsconfig.json +39 -0
- package/templates/static/build.js +30 -18
- package/templates/static/jen.config.ts +0 -18
- package/templates/static/jenjs.d.ts +0 -18
- package/templates/static/lib/api/(hello).js +0 -17
- package/templates/static/lib/api/examples/files/[...slug].js +22 -0
- package/templates/static/lib/api/examples/hello.js +11 -0
- package/templates/static/lib/api/examples/posts/[id].js +37 -0
- package/templates/static/lib/api/examples/posts.js +37 -0
- package/templates/static/lib/api/examples/search.js +23 -0
- package/templates/static/lib/api/index.js +41 -0
- package/templates/static/lib/api/loader.js +234 -0
- package/templates/static/lib/api/router.js +259 -0
- package/templates/static/lib/assets/types.js +1 -0
- package/templates/static/lib/auth/cookie-utils.js +3 -16
- package/templates/static/lib/auth/index.js +0 -17
- package/templates/static/lib/auth/jwt.js +0 -17
- package/templates/static/lib/auth/session.js +0 -17
- package/templates/static/lib/build/asset-hashing.js +44 -36
- package/templates/static/lib/build/asset-manifest.js +16 -33
- package/templates/static/lib/build/build.js +270 -125
- package/templates/static/lib/build/bundle-analyzer-ui.js +417 -0
- package/templates/static/lib/build/bundle-analyzer.js +945 -0
- package/templates/static/lib/build/code-splitter.js +194 -0
- package/templates/static/lib/build/feature-analyzer.js +190 -0
- package/templates/static/lib/build/feature-gate.js +257 -0
- package/templates/static/lib/build/island-hydration.js +17 -35
- package/templates/static/lib/build/lazy-loader.js +322 -0
- package/templates/static/lib/build/minifier.js +40 -59
- package/templates/static/lib/build/page-renderer.js +23 -40
- package/templates/static/lib/build/production-build.js +9 -26
- package/templates/static/lib/build/rust-hashing.js +71 -0
- package/templates/static/lib/build/script-optimizer.js +285 -0
- package/templates/static/lib/build/ssg-pipeline.js +100 -106
- package/templates/static/lib/build/vercel-output.js +298 -0
- package/templates/static/lib/build-tools/build-site.js +0 -17
- package/templates/static/lib/cache/index.js +0 -17
- package/templates/static/lib/cache/memory.js +0 -17
- package/templates/static/lib/cache/redis.js +0 -17
- package/templates/static/lib/cli/banner.js +0 -17
- package/templates/static/lib/cli/templates/ssg/jen.config.js +0 -17
- package/templates/static/lib/cli/templates/ssr/jen.config.js +0 -17
- package/templates/static/lib/client/Image.js +42 -0
- package/templates/static/lib/client/Link.js +190 -0
- package/templates/static/lib/client/PWA.js +46 -0
- package/templates/static/lib/client/Seo.js +97 -0
- package/templates/static/lib/client/index.js +9 -0
- package/templates/static/lib/client/useNavigation.js +25 -0
- package/templates/static/lib/client/useRouter.js +64 -0
- package/templates/static/lib/client-routing/Link.js +17 -0
- package/templates/static/lib/client-routing/index.js +19 -0
- package/templates/static/lib/client-routing/router.js +151 -0
- package/templates/static/lib/client-routing/signal.js +147 -0
- package/templates/static/lib/compilers/esbuild-plugins.js +0 -17
- package/templates/static/lib/compilers/svelte.js +0 -17
- package/templates/static/lib/compilers/vue.js +0 -17
- package/templates/static/lib/core/config.js +0 -17
- package/templates/static/lib/core/feature-guard.js +136 -0
- package/templates/static/lib/core/features.js +99 -0
- package/templates/static/lib/core/http.js +0 -17
- package/templates/static/lib/core/layouts/index.js +10 -0
- package/templates/static/lib/core/layouts/render.js +158 -0
- package/templates/static/lib/core/layouts/scan.js +112 -0
- package/templates/static/lib/core/layouts/types.js +1 -0
- package/templates/static/lib/core/lifecycle.js +129 -0
- package/templates/static/lib/core/loader-schema.js +81 -0
- package/templates/static/lib/core/middleware-hooks.js +0 -17
- package/templates/static/lib/core/paths.js +0 -17
- package/templates/static/lib/core/routes/advanced.js +114 -0
- package/templates/static/lib/core/routes/handlers.js +181 -0
- package/templates/static/lib/core/routes/match.js +89 -17
- package/templates/static/lib/core/routes/orchestrator.js +171 -0
- package/templates/static/lib/core/routes/rendering-config.js +131 -0
- package/templates/static/lib/core/routes/scan.js +0 -17
- package/templates/static/lib/core/types.js +0 -17
- package/templates/static/lib/css/compiler.js +1 -18
- package/templates/static/lib/data-fetching/cache.js +223 -0
- package/templates/static/lib/data-fetching/client.js +202 -0
- package/templates/static/lib/data-fetching/feature-guard.js +29 -0
- package/templates/static/lib/data-fetching/graphql.js +265 -0
- package/templates/static/lib/data-fetching/index.js +57 -0
- package/templates/static/lib/data-fetching/rest.js +256 -0
- package/templates/static/lib/data-fetching/server.js +182 -0
- package/templates/static/lib/data-fetching/types.js +5 -0
- package/templates/static/lib/db/connector.js +0 -17
- package/templates/static/lib/db/drivers/jdb.js +0 -17
- package/templates/static/lib/db/drivers/sql.js +0 -17
- package/templates/static/lib/db/index.js +0 -17
- package/templates/static/lib/db/types.js +0 -17
- package/templates/static/lib/devtools/component-tree.js +106 -0
- package/templates/static/lib/devtools/devtools.js +638 -0
- package/templates/static/lib/devtools/event-bus.js +29 -0
- package/templates/static/lib/devtools/event-logger.js +67 -0
- package/templates/static/lib/devtools/index.js +9 -0
- package/templates/static/lib/devtools/integration.js +149 -0
- package/templates/static/lib/devtools/performance.js +84 -0
- package/templates/static/lib/devtools/persistence.js +57 -0
- package/templates/static/lib/devtools/plugins.js +97 -0
- package/templates/static/lib/devtools/search.js +89 -0
- package/templates/static/lib/devtools/ui.js +769 -0
- package/templates/static/lib/features/api/handler.js +10 -0
- package/templates/static/lib/features/api/index.js +5 -0
- package/templates/static/lib/features/api/types.js +4 -0
- package/templates/static/lib/features/middleware/compiled.js +7 -0
- package/templates/static/lib/features/middleware/index.js +5 -0
- package/templates/static/lib/features/middleware/types.js +4 -0
- package/templates/static/lib/fonts/index.js +46 -0
- package/templates/static/lib/fonts/inject.js +125 -0
- package/templates/static/lib/fonts/loader.js +196 -0
- package/templates/static/lib/fonts/types.js +1 -0
- package/templates/static/lib/graphql/index.js +1 -18
- package/templates/static/lib/graphql/resolvers.js +20 -13
- package/templates/static/lib/graphql/schema.js +0 -17
- package/templates/static/lib/i18n/index.js +7 -19
- package/templates/static/lib/import/jen-import.js +1 -18
- package/templates/static/lib/index.js +79 -125
- package/templates/static/lib/jdb/engine.js +0 -17
- package/templates/static/lib/jdb/index.js +1 -18
- package/templates/static/lib/jdb/types.js +0 -17
- package/templates/static/lib/jdb/utils.js +0 -17
- package/templates/static/lib/middleware/builtins/cors.js +3 -16
- package/templates/static/lib/middleware/context.js +0 -17
- package/templates/static/lib/middleware/kernel.js +117 -25
- package/templates/static/lib/middleware/pipeline.js +0 -17
- package/templates/static/lib/middleware/registry.js +0 -17
- package/templates/static/lib/middleware/response.js +0 -17
- package/templates/static/lib/plugin/examples/analytics-plugin.js +183 -0
- package/templates/static/lib/plugin/examples/cdn-upload-plugin.js +94 -0
- package/templates/static/lib/plugin/loader.js +0 -17
- package/templates/static/lib/plugin/plugin-manager.js +177 -0
- package/templates/static/lib/plugin/types.js +28 -0
- package/templates/static/lib/runtime/client-runtime.js +0 -17
- package/templates/static/lib/runtime/hmr.js +0 -17
- package/templates/static/lib/runtime/hydrate.js +0 -17
- package/templates/static/lib/runtime/island-hydration-client.js +0 -17
- package/templates/static/lib/runtime/islands.js +0 -17
- package/templates/static/lib/runtime/render.js +208 -50
- package/templates/static/lib/security/security-config.js +60 -0
- package/templates/static/lib/security/security-middleware.js +229 -0
- package/templates/static/lib/server/api-routes.js +153 -43
- package/templates/static/lib/server/api.js +0 -17
- package/templates/static/lib/server/app.js +539 -223
- package/templates/static/lib/server/isr.js +365 -0
- package/templates/static/lib/server/runtimeServe.js +31 -24
- package/templates/static/lib/server/ssr.js +98 -22
- package/templates/static/lib/server-actions/handler.js +180 -0
- package/templates/static/lib/server-actions/index.js +19 -0
- package/templates/static/lib/server-actions/middleware.js +146 -0
- package/templates/static/lib/server-actions/scan.js +152 -0
- package/templates/static/lib/server-actions/types.js +1 -0
- package/templates/static/lib/server-actions/validators.js +156 -0
- package/templates/static/lib/shared/log.js +19 -20
- package/templates/static/lib/telemetry/api/rate-limiter.js +32 -0
- package/templates/static/lib/telemetry/api/validator.js +67 -0
- package/templates/static/lib/telemetry/client.js +121 -0
- package/templates/static/lib/telemetry/tests/rate-limiter.test.js +46 -0
- package/templates/static/lib/telemetry/tests/validator.test.js +62 -0
- package/templates/static/lib/vendor/glob/glob.js +4766 -0
- package/templates/static/lib/vendor/preact/LICENSE +21 -0
- package/templates/static/lib/vendor/preact/preact.module.js +797 -0
- package/templates/static/lib/vendor/sass/sass.node.mjs +212 -0
- package/templates/static/package.json +4 -0
- package/templates/static/server.js +22 -22
- package/templates/static/site/(home).tsx +0 -18
- package/templates/static/tsconfig.json +5 -1
- package/templates/static/.esbuild/jen.config.js +0 -19
- package/templates/static/lib/build/asset-hashing.d.ts +0 -10
- package/templates/static/lib/build/asset-manifest.d.ts +0 -11
- package/templates/static/lib/build/minifier.d.ts +0 -20
- package/templates/static/lib/build/page-renderer.d.ts +0 -17
- package/templates/static/lib/build/production-build.d.ts +0 -10
- package/templates/static/lib/build/ssg-pipeline.d.ts +0 -15
- package/templates/static/lib/middleware/errors/http-error.js +0 -27
- package/templates/static/lib/middleware/types.js +0 -18
- package/templates/static/lib/middleware/utils/matcher.js +0 -30
- package/templates/static/lib/native/style-compiler.js +0 -36
- /package/templates/{static → ssr-isr}/lib/api/(hello).d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/auth/cookie-utils.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/auth/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/auth/jwt.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/auth/session.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/build-tools/build-site.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cache/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cache/memory.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cache/redis.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cli/banner.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/jen.config.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/site/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/jen.config.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/site/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/compilers/esbuild-plugins.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/compilers/svelte.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/compilers/vue.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/config.d.ts +0 -0
- /package/templates/{static/lib/middleware/types.d.ts → ssr-isr/lib/core/config.js} +0 -0
- /package/templates/{static → ssr-isr}/lib/core/http.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/middleware-hooks.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/paths.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/routes/match.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/routes/scan.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/core/types.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/css/compiler.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/db/connector.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/db/drivers/jdb.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/db/drivers/sql.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/db/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/db/types.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/graphql/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/graphql/resolvers.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/graphql/schema.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/i18n/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/import/jen-import.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/jdb/engine.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/jdb/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/jdb/types.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/jdb/utils.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/cors.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/context.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/decorators.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/errors/handler.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/errors/http-error.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/kernel.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/pipeline.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/registry.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/response.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/middleware/utils/matcher.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/native/bundle.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/native/dev-server.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/native/index.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/native/optimizer.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/native/style-compiler.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/plugin/loader.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/client-runtime.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/hmr.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/hydrate.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/island-hydration-client.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/islands.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/runtime/render.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/server/api-routes.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/server/api.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/server/app.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/server/runtimeServe.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/server/ssr.d.ts +0 -0
- /package/templates/{static → ssr-isr}/lib/shared/log.d.ts +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { log } from "../shared/log.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a ServerActionContext from request/response objects.
|
|
4
|
+
* Handles body parsing, header extraction, and cookie parsing.
|
|
5
|
+
*/
|
|
6
|
+
export async function createServerActionContext(opts) {
|
|
7
|
+
const { req, res, url, params = {}, headers, cookies } = opts;
|
|
8
|
+
// Parse request body based on content type
|
|
9
|
+
const body = await parseRequestBody(req);
|
|
10
|
+
// Extract method
|
|
11
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
12
|
+
// Extract query parameters
|
|
13
|
+
const query = {};
|
|
14
|
+
for (const [k, v] of url.searchParams.entries()) {
|
|
15
|
+
query[k] = v;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
req,
|
|
19
|
+
res,
|
|
20
|
+
url,
|
|
21
|
+
method,
|
|
22
|
+
body,
|
|
23
|
+
query,
|
|
24
|
+
params,
|
|
25
|
+
headers,
|
|
26
|
+
cookies,
|
|
27
|
+
data: {},
|
|
28
|
+
validate(input, schema) {
|
|
29
|
+
const errors = {};
|
|
30
|
+
let success = true;
|
|
31
|
+
for (const [fieldName, rules] of Object.entries(schema)) {
|
|
32
|
+
const value = input[fieldName];
|
|
33
|
+
for (const rule of rules) {
|
|
34
|
+
const error = rule.validate(value);
|
|
35
|
+
if (error) {
|
|
36
|
+
errors[fieldName] = error;
|
|
37
|
+
success = false;
|
|
38
|
+
break; // Stop at first error for this field
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { success, errors };
|
|
43
|
+
},
|
|
44
|
+
stream() {
|
|
45
|
+
// Set headers for streaming response
|
|
46
|
+
if (!res.headersSent) {
|
|
47
|
+
res.statusCode = 200;
|
|
48
|
+
res.setHeader("content-type", "application/x-ndjson; charset=utf-8");
|
|
49
|
+
res.setHeader("transfer-encoding", "chunked");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
write(data) {
|
|
53
|
+
const line = typeof data === "string" ? data : JSON.stringify(data);
|
|
54
|
+
res.write(line + "\n");
|
|
55
|
+
},
|
|
56
|
+
writeJSON(data) {
|
|
57
|
+
res.write(JSON.stringify(data) + "\n");
|
|
58
|
+
},
|
|
59
|
+
close() {
|
|
60
|
+
if (!res.writableEnded) {
|
|
61
|
+
res.end();
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse request body from IncomingMessage.
|
|
70
|
+
* Handles JSON and form data content types.
|
|
71
|
+
*/
|
|
72
|
+
async function parseRequestBody(req) {
|
|
73
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
74
|
+
// Methods without bodies per HTTP spec
|
|
75
|
+
if (method === "GET" || method === "HEAD") {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
// Read all body chunks
|
|
79
|
+
const chunks = [];
|
|
80
|
+
for await (const chunk of req) {
|
|
81
|
+
chunks.push(Buffer.from(chunk));
|
|
82
|
+
}
|
|
83
|
+
if (chunks.length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
87
|
+
const contentType = (req.headers["content-type"] ?? "").toString();
|
|
88
|
+
// Parse JSON
|
|
89
|
+
if (contentType.includes("application/json")) {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(raw);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
log.warn(`Failed to parse JSON body: ${err}`);
|
|
94
|
+
return { __raw: raw };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Parse form data
|
|
98
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
99
|
+
const data = {};
|
|
100
|
+
const params = new URLSearchParams(raw);
|
|
101
|
+
for (const [k, v] of params.entries()) {
|
|
102
|
+
data[k] = v;
|
|
103
|
+
}
|
|
104
|
+
return data;
|
|
105
|
+
}
|
|
106
|
+
// Parse multipart form data (basic support)
|
|
107
|
+
if (contentType.includes("multipart/form-data")) {
|
|
108
|
+
// For now, return raw. Full multipart parsing requires a library
|
|
109
|
+
return { __raw: raw };
|
|
110
|
+
}
|
|
111
|
+
// Default: return as raw
|
|
112
|
+
return { __raw: raw };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Execute a server action handler with the given context.
|
|
116
|
+
* Handles validation, error handling, and response serialization.
|
|
117
|
+
*/
|
|
118
|
+
export async function executeServerAction(opts) {
|
|
119
|
+
const { handler, ctx, validation } = opts;
|
|
120
|
+
try {
|
|
121
|
+
// Run validation if schema provided
|
|
122
|
+
if (validation) {
|
|
123
|
+
const validationResult = ctx.validate(ctx.body, validation);
|
|
124
|
+
if (!validationResult.success) {
|
|
125
|
+
ctx.res.statusCode = 400;
|
|
126
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
127
|
+
ctx.res.end(
|
|
128
|
+
JSON.stringify({
|
|
129
|
+
success: false,
|
|
130
|
+
errors: validationResult.errors,
|
|
131
|
+
message: "Validation failed",
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Execute the handler
|
|
138
|
+
const result = await handler(ctx);
|
|
139
|
+
// If response already sent (streaming), don't double-send
|
|
140
|
+
if (ctx.res.writableEnded) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Handle Response object
|
|
144
|
+
if (result instanceof Response) {
|
|
145
|
+
ctx.res.statusCode = result.status;
|
|
146
|
+
result.headers.forEach((v, k) => ctx.res.setHeader(k, v));
|
|
147
|
+
const buf = Buffer.from(await result.arrayBuffer());
|
|
148
|
+
ctx.res.end(buf);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Serialize as JSON
|
|
152
|
+
ctx.res.statusCode = 200;
|
|
153
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
154
|
+
if (result === null || result === undefined) {
|
|
155
|
+
ctx.res.end(JSON.stringify({ success: true, data: null }));
|
|
156
|
+
} else if (typeof result === "string") {
|
|
157
|
+
ctx.res.end(JSON.stringify({ success: true, data: result }));
|
|
158
|
+
} else {
|
|
159
|
+
ctx.res.end(JSON.stringify({ success: true, ...result }));
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
// Error response
|
|
163
|
+
log.error(`Server action error: ${err.message}`);
|
|
164
|
+
if (!ctx.res.headersSent) {
|
|
165
|
+
ctx.res.statusCode = 500;
|
|
166
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
167
|
+
}
|
|
168
|
+
if (!ctx.res.writableEnded) {
|
|
169
|
+
ctx.res.end(
|
|
170
|
+
JSON.stringify({
|
|
171
|
+
success: false,
|
|
172
|
+
message: "Internal server error",
|
|
173
|
+
...(process.env.NODE_ENV === "development" && {
|
|
174
|
+
error: err.message,
|
|
175
|
+
}),
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Validators
|
|
2
|
+
export {
|
|
3
|
+
required,
|
|
4
|
+
minLength,
|
|
5
|
+
maxLength,
|
|
6
|
+
email,
|
|
7
|
+
url,
|
|
8
|
+
pattern,
|
|
9
|
+
range,
|
|
10
|
+
custom,
|
|
11
|
+
enumValue,
|
|
12
|
+
type,
|
|
13
|
+
} from "./validators.js";
|
|
14
|
+
// Scanning and routing
|
|
15
|
+
export { scanServerActions, matchServerAction } from "./scan.js";
|
|
16
|
+
// Handler and context creation
|
|
17
|
+
export { createServerActionContext, executeServerAction } from "./handler.js";
|
|
18
|
+
// Middleware factory
|
|
19
|
+
export { createServerActionsMiddleware } from "./middleware.js";
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import esbuild from "esbuild";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { log } from "../shared/log.js";
|
|
5
|
+
import { headersToObject, parseCookies } from "../core/http.js";
|
|
6
|
+
import { scanServerActions, matchServerAction } from "./scan.js";
|
|
7
|
+
import { createServerActionContext, executeServerAction } from "./handler.js";
|
|
8
|
+
/** Cache directory for transpiled server actions. */
|
|
9
|
+
const actionsCacheDir = join(
|
|
10
|
+
process.cwd(),
|
|
11
|
+
"node_modules",
|
|
12
|
+
".jen",
|
|
13
|
+
"actions-cache",
|
|
14
|
+
);
|
|
15
|
+
/**
|
|
16
|
+
* Transpile a TypeScript server action file to JavaScript.
|
|
17
|
+
* Uses esbuild to convert TS → JS and output as ESM.
|
|
18
|
+
*/
|
|
19
|
+
async function transpileServerAction(filePath) {
|
|
20
|
+
const outfile = join(
|
|
21
|
+
actionsCacheDir,
|
|
22
|
+
basename(filePath).replace(/\.ts$/, `.${Date.now()}.mjs`),
|
|
23
|
+
);
|
|
24
|
+
await esbuild.build({
|
|
25
|
+
entryPoints: [filePath],
|
|
26
|
+
outfile,
|
|
27
|
+
format: "esm",
|
|
28
|
+
platform: "node",
|
|
29
|
+
target: "es2022",
|
|
30
|
+
bundle: false,
|
|
31
|
+
external: ["preact", "preact-render-to-string", "jenjs"],
|
|
32
|
+
write: true,
|
|
33
|
+
});
|
|
34
|
+
return outfile;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load a server action module and extract its configuration and handler.
|
|
38
|
+
*/
|
|
39
|
+
async function loadServerActionModule(filePath) {
|
|
40
|
+
let moduleUrl = filePath;
|
|
41
|
+
// Transpile TypeScript to JavaScript if needed
|
|
42
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) {
|
|
43
|
+
moduleUrl = await transpileServerAction(filePath);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
// Cache-busting query param for dev mode
|
|
47
|
+
const mod = await import(
|
|
48
|
+
pathToFileURL(moduleUrl).href + `?t=${Date.now()}`
|
|
49
|
+
);
|
|
50
|
+
return mod.default ? { default: mod.default, ...mod } : mod;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
log.error(`Failed to load server action: ${err.message}`);
|
|
53
|
+
throw new Error(`Failed to load server action module: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a middleware that handles server actions.
|
|
58
|
+
* Scans the actions directory and routes requests to action handlers.
|
|
59
|
+
*
|
|
60
|
+
* Routes server action requests to /actions/* endpoints and executes them
|
|
61
|
+
* with validation, error handling, and streaming support.
|
|
62
|
+
*/
|
|
63
|
+
export async function createServerActionsMiddleware(opts) {
|
|
64
|
+
const { config } = opts;
|
|
65
|
+
// Scan actions directory once at startup
|
|
66
|
+
const actions = scanServerActions(config);
|
|
67
|
+
log.info(`Server actions discovered: ${actions.length}`);
|
|
68
|
+
for (const a of actions) {
|
|
69
|
+
log.info(` ${a.actionPath} (${a.name})`);
|
|
70
|
+
}
|
|
71
|
+
// Cache loaded action modules
|
|
72
|
+
const moduleCache = new Map();
|
|
73
|
+
return async (ctx, next) => {
|
|
74
|
+
// Only handle /actions/* requests
|
|
75
|
+
if (!ctx.url.pathname.startsWith("/actions/")) {
|
|
76
|
+
return next();
|
|
77
|
+
}
|
|
78
|
+
// Extract action path (remove /actions prefix)
|
|
79
|
+
const actionPath = ctx.url.pathname.slice("/actions".length);
|
|
80
|
+
// Match against discovered actions
|
|
81
|
+
const match = matchServerAction(actions, actionPath);
|
|
82
|
+
if (!match) {
|
|
83
|
+
ctx.res.statusCode = 404;
|
|
84
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
85
|
+
ctx.res.end(
|
|
86
|
+
JSON.stringify({ success: false, message: "Action not found" }),
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { action, params } = match;
|
|
91
|
+
try {
|
|
92
|
+
// Load action module (use cache to avoid reloading)
|
|
93
|
+
let actionModule = moduleCache.get(action.id);
|
|
94
|
+
if (!actionModule) {
|
|
95
|
+
actionModule = await loadServerActionModule(action.filePath);
|
|
96
|
+
moduleCache.set(action.id, actionModule);
|
|
97
|
+
}
|
|
98
|
+
// Extract configuration
|
|
99
|
+
const metadata = actionModule.metadata || {};
|
|
100
|
+
const validation = actionModule.validation;
|
|
101
|
+
const handler = actionModule.default;
|
|
102
|
+
if (!handler || typeof handler !== "function") {
|
|
103
|
+
ctx.res.statusCode = 500;
|
|
104
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
105
|
+
ctx.res.end(
|
|
106
|
+
JSON.stringify({
|
|
107
|
+
success: false,
|
|
108
|
+
message: "Invalid action: no handler exported",
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Create action context
|
|
114
|
+
const reqHeaders = headersToObject(ctx.req.headers);
|
|
115
|
+
const cookies = parseCookies(ctx.req);
|
|
116
|
+
const actionCtx = await createServerActionContext({
|
|
117
|
+
req: ctx.req,
|
|
118
|
+
res: ctx.res,
|
|
119
|
+
url: ctx.url,
|
|
120
|
+
params,
|
|
121
|
+
headers: reqHeaders,
|
|
122
|
+
cookies,
|
|
123
|
+
});
|
|
124
|
+
// Execute server action
|
|
125
|
+
await executeServerAction({
|
|
126
|
+
handler,
|
|
127
|
+
ctx: actionCtx,
|
|
128
|
+
validation,
|
|
129
|
+
});
|
|
130
|
+
} catch (err) {
|
|
131
|
+
log.error(`Server action error: ${err.message}`);
|
|
132
|
+
if (!ctx.res.headersSent) {
|
|
133
|
+
ctx.res.statusCode = 500;
|
|
134
|
+
ctx.res.setHeader("content-type", "application/json; charset=utf-8");
|
|
135
|
+
}
|
|
136
|
+
if (!ctx.res.writableEnded) {
|
|
137
|
+
ctx.res.end(
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
success: false,
|
|
140
|
+
message: "Internal server error",
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Recursively walks a directory and returns all file paths.
|
|
5
|
+
* Used to discover all action files in the actions directory.
|
|
6
|
+
*/
|
|
7
|
+
function walk(dir) {
|
|
8
|
+
const out = [];
|
|
9
|
+
try {
|
|
10
|
+
for (const name of readdirSync(dir)) {
|
|
11
|
+
const p = join(dir, name);
|
|
12
|
+
const st = statSync(p);
|
|
13
|
+
if (st.isDirectory()) out.push(...walk(p));
|
|
14
|
+
else out.push(p);
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
// Directory doesn't exist yet
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Normalizes filesystem path separators to forward slashes.
|
|
23
|
+
*/
|
|
24
|
+
function normalizeSlashes(p) {
|
|
25
|
+
return p.split(sep).join("/");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Converts kebab-case to camelCase.
|
|
29
|
+
*/
|
|
30
|
+
function kebabToCamel(str) {
|
|
31
|
+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Converts a file path to an action name.
|
|
35
|
+
* - actions/post-comment.ts => postComment
|
|
36
|
+
* - actions/blog/publish.ts => blog.publish
|
|
37
|
+
* - actions/deeply/nested/action.ts => deeply.nested.action
|
|
38
|
+
*/
|
|
39
|
+
function filePathToActionName(filePath) {
|
|
40
|
+
// Remove extension
|
|
41
|
+
const withoutExt = filePath.replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
42
|
+
// Split by directory separator
|
|
43
|
+
const parts = withoutExt.split("/");
|
|
44
|
+
// Convert each part from kebab-case to camelCase, except keep first as-is for namespace
|
|
45
|
+
return parts
|
|
46
|
+
.map((part, idx) => {
|
|
47
|
+
// First part can be namespace (keep as-is)
|
|
48
|
+
// Subsequent parts are action names (convert to camelCase)
|
|
49
|
+
return idx === 0 ? part : kebabToCamel(part);
|
|
50
|
+
})
|
|
51
|
+
.join(".");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Scans the actions directory for server action files.
|
|
55
|
+
* Server action files are TypeScript/JavaScript files in the site/actions directory.
|
|
56
|
+
* Each file should export a default function that is the action handler.
|
|
57
|
+
*
|
|
58
|
+
* Naming conventions:
|
|
59
|
+
* - actions/submit-form.ts => action path /submit-form, name "submitForm"
|
|
60
|
+
* - actions/blog/publish.ts => action path /blog/publish, name "blog.publish"
|
|
61
|
+
* - actions/user/[id]/delete.ts => action path /user/:id/delete, name "user.delete"
|
|
62
|
+
*
|
|
63
|
+
* @param config Framework configuration with siteDir
|
|
64
|
+
* @returns Array of ServerActionEntry objects for all discovered actions
|
|
65
|
+
*/
|
|
66
|
+
export function scanServerActions(config) {
|
|
67
|
+
const actionsDir = join(process.cwd(), config.siteDir, "actions");
|
|
68
|
+
const files = walk(actionsDir);
|
|
69
|
+
const actions = [];
|
|
70
|
+
for (const abs of files) {
|
|
71
|
+
// Skip if not a valid action file
|
|
72
|
+
const ext = abs.split(".").pop()?.toLowerCase();
|
|
73
|
+
if (!ext || !["ts", "js", "tsx", "jsx"].includes(ext)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Get relative path from actions directory
|
|
77
|
+
const rel = normalizeSlashes(relative(actionsDir, abs));
|
|
78
|
+
// Remove extension
|
|
79
|
+
const withoutExt = rel.replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
80
|
+
// Convert file path to action path
|
|
81
|
+
// Handle dynamic segments: [id] => :id
|
|
82
|
+
const actionPath =
|
|
83
|
+
"/" +
|
|
84
|
+
withoutExt
|
|
85
|
+
.split("/")
|
|
86
|
+
.map((part) =>
|
|
87
|
+
part.startsWith("[") && part.endsWith("]")
|
|
88
|
+
? ":" + part.slice(1, -1)
|
|
89
|
+
: part,
|
|
90
|
+
)
|
|
91
|
+
.join("/");
|
|
92
|
+
// Generate action name
|
|
93
|
+
const actionName = filePathToActionName(withoutExt);
|
|
94
|
+
actions.push({
|
|
95
|
+
id: withoutExt.replaceAll("/", "_").replaceAll("-", "_"),
|
|
96
|
+
filePath: abs,
|
|
97
|
+
actionPath,
|
|
98
|
+
name: actionName,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Sort by path specificity (static first, then dynamic)
|
|
102
|
+
actions.sort((a, b) => {
|
|
103
|
+
const aDyn = a.actionPath.includes(":");
|
|
104
|
+
const bDyn = b.actionPath.includes(":");
|
|
105
|
+
if (aDyn !== bDyn) return aDyn ? 1 : -1;
|
|
106
|
+
return a.actionPath.localeCompare(b.actionPath);
|
|
107
|
+
});
|
|
108
|
+
return actions;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Matches an action path against discovered server actions.
|
|
112
|
+
* Handles static and dynamic route matching.
|
|
113
|
+
*
|
|
114
|
+
* @param actions Array of discovered server actions
|
|
115
|
+
* @param requestPath The incoming request path
|
|
116
|
+
* @returns Matched action entry with params if found, null otherwise
|
|
117
|
+
*/
|
|
118
|
+
export function matchServerAction(actions, requestPath) {
|
|
119
|
+
// Normalize path
|
|
120
|
+
const path = requestPath.startsWith("/") ? requestPath : "/" + requestPath;
|
|
121
|
+
for (const action of actions) {
|
|
122
|
+
// Try exact match first
|
|
123
|
+
if (action.actionPath === path) {
|
|
124
|
+
return { action, params: {} };
|
|
125
|
+
}
|
|
126
|
+
// Try dynamic match
|
|
127
|
+
const actionParts = action.actionPath.split("/").filter(Boolean);
|
|
128
|
+
const pathParts = path.split("/").filter(Boolean);
|
|
129
|
+
if (actionParts.length !== pathParts.length) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const params = {};
|
|
133
|
+
let matches = true;
|
|
134
|
+
for (let i = 0; i < actionParts.length; i++) {
|
|
135
|
+
const actionPart = actionParts[i];
|
|
136
|
+
const pathPart = pathParts[i];
|
|
137
|
+
if (actionPart.startsWith(":")) {
|
|
138
|
+
// Dynamic segment
|
|
139
|
+
const paramName = actionPart.slice(1);
|
|
140
|
+
params[paramName] = pathPart;
|
|
141
|
+
} else if (actionPart !== pathPart) {
|
|
142
|
+
// Static segment doesn't match
|
|
143
|
+
matches = false;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (matches) {
|
|
148
|
+
return { action, params };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common validation rules for server actions.
|
|
3
|
+
* Can be composed and chained for complex validations.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a required field validator.
|
|
7
|
+
* Checks that value is not null, undefined, or empty string.
|
|
8
|
+
*/
|
|
9
|
+
export function required(message = "This field is required") {
|
|
10
|
+
return {
|
|
11
|
+
name: "required",
|
|
12
|
+
validate: (value) => {
|
|
13
|
+
if (value === null || value === undefined || value === "") {
|
|
14
|
+
return message;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a minimum length validator for strings.
|
|
22
|
+
*/
|
|
23
|
+
export function minLength(min, message) {
|
|
24
|
+
return {
|
|
25
|
+
name: "minLength",
|
|
26
|
+
validate: (value) => {
|
|
27
|
+
if (typeof value !== "string") return undefined;
|
|
28
|
+
if (value.length < min) {
|
|
29
|
+
return message || `Minimum ${min} characters required`;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a maximum length validator for strings.
|
|
37
|
+
*/
|
|
38
|
+
export function maxLength(max, message) {
|
|
39
|
+
return {
|
|
40
|
+
name: "maxLength",
|
|
41
|
+
validate: (value) => {
|
|
42
|
+
if (typeof value !== "string") return undefined;
|
|
43
|
+
if (value.length > max) {
|
|
44
|
+
return message || `Maximum ${max} characters allowed`;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates an email format validator.
|
|
52
|
+
*/
|
|
53
|
+
export function email(message = "Invalid email address") {
|
|
54
|
+
return {
|
|
55
|
+
name: "email",
|
|
56
|
+
validate: (value) => {
|
|
57
|
+
if (typeof value !== "string" || !value) return undefined;
|
|
58
|
+
// Simple email regex (RFC 5322 simplified)
|
|
59
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
60
|
+
if (!emailRegex.test(value)) {
|
|
61
|
+
return message;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a URL format validator.
|
|
69
|
+
*/
|
|
70
|
+
export function url(message = "Invalid URL") {
|
|
71
|
+
return {
|
|
72
|
+
name: "url",
|
|
73
|
+
validate: (value) => {
|
|
74
|
+
if (typeof value !== "string" || !value) return undefined;
|
|
75
|
+
try {
|
|
76
|
+
new URL(value);
|
|
77
|
+
return undefined;
|
|
78
|
+
} catch {
|
|
79
|
+
return message;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a regex pattern validator.
|
|
86
|
+
*/
|
|
87
|
+
export function pattern(regex, message) {
|
|
88
|
+
return {
|
|
89
|
+
name: "pattern",
|
|
90
|
+
validate: (value) => {
|
|
91
|
+
if (typeof value !== "string" || !value) return undefined;
|
|
92
|
+
if (!regex.test(value)) {
|
|
93
|
+
return message || "Invalid format";
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a number range validator.
|
|
101
|
+
*/
|
|
102
|
+
export function range(min, max, message) {
|
|
103
|
+
return {
|
|
104
|
+
name: "range",
|
|
105
|
+
validate: (value) => {
|
|
106
|
+
const num = Number(value);
|
|
107
|
+
if (isNaN(num)) return undefined;
|
|
108
|
+
if (num < min || num > max) {
|
|
109
|
+
return message || `Must be between ${min} and ${max}`;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a custom validator from a function.
|
|
117
|
+
*/
|
|
118
|
+
export function custom(fn, message = "Invalid value") {
|
|
119
|
+
return {
|
|
120
|
+
name: "custom",
|
|
121
|
+
validate: (value) => {
|
|
122
|
+
const result = fn(value);
|
|
123
|
+
if (result === false) return message;
|
|
124
|
+
if (typeof result === "string") return result;
|
|
125
|
+
return undefined;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates an enum validator.
|
|
131
|
+
*/
|
|
132
|
+
export function enumValue(values, message) {
|
|
133
|
+
return {
|
|
134
|
+
name: "enum",
|
|
135
|
+
validate: (value) => {
|
|
136
|
+
if (!values.includes(value)) {
|
|
137
|
+
return message || `Must be one of: ${values.join(", ")}`;
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Creates a type validator.
|
|
145
|
+
*/
|
|
146
|
+
export function type(expectedType, message) {
|
|
147
|
+
return {
|
|
148
|
+
name: "type",
|
|
149
|
+
validate: (value) => {
|
|
150
|
+
if (typeof value !== expectedType) {
|
|
151
|
+
return message || `Expected ${expectedType}`;
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|