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.
Files changed (355) hide show
  1. package/dist/colors.js +0 -17
  2. package/dist/create.js +5 -17
  3. package/dist/generator.js +14 -31
  4. package/dist/index.js +6 -1
  5. package/package.json +1 -1
  6. package/templates/ssr-isr/README.md +77 -0
  7. package/templates/ssr-isr/build.js +118 -0
  8. package/templates/ssr-isr/jen.config.ts +109 -0
  9. package/templates/ssr-isr/jenjs.d.ts +22 -0
  10. package/templates/ssr-isr/lib/api/(hello).js +9 -0
  11. package/templates/ssr-isr/lib/auth/cookie-utils.js +79 -0
  12. package/templates/ssr-isr/lib/auth/index.js +2 -0
  13. package/templates/ssr-isr/lib/auth/jwt.js +57 -0
  14. package/templates/ssr-isr/lib/auth/session.js +92 -0
  15. package/templates/ssr-isr/lib/build/asset-hashing.d.ts +10 -0
  16. package/templates/ssr-isr/lib/build/asset-hashing.js +25 -0
  17. package/templates/ssr-isr/lib/build/asset-manifest.d.ts +11 -0
  18. package/templates/ssr-isr/lib/build/asset-manifest.js +21 -0
  19. package/templates/{static → ssr-isr}/lib/build/build.d.ts +1 -1
  20. package/templates/ssr-isr/lib/build/build.js +141 -0
  21. package/templates/{static → ssr-isr}/lib/build/island-hydration.d.ts +8 -5
  22. package/templates/ssr-isr/lib/build/island-hydration.js +44 -0
  23. package/templates/ssr-isr/lib/build/minifier.d.ts +20 -0
  24. package/templates/ssr-isr/lib/build/minifier.js +46 -0
  25. package/templates/ssr-isr/lib/build/page-renderer.d.ts +17 -0
  26. package/templates/ssr-isr/lib/build/page-renderer.js +28 -0
  27. package/templates/ssr-isr/lib/build/production-build.d.ts +10 -0
  28. package/templates/ssr-isr/lib/build/production-build.js +13 -0
  29. package/templates/ssr-isr/lib/build/ssg-pipeline.d.ts +15 -0
  30. package/templates/ssr-isr/lib/build/ssg-pipeline.js +113 -0
  31. package/templates/ssr-isr/lib/build-tools/build-site.js +36 -0
  32. package/templates/ssr-isr/lib/cache/index.js +10 -0
  33. package/templates/ssr-isr/lib/cache/memory.js +40 -0
  34. package/templates/ssr-isr/lib/cache/redis.js +61 -0
  35. package/templates/ssr-isr/lib/cli/banner.js +28 -0
  36. package/templates/ssr-isr/lib/cli/templates/ssg/jen.config.js +32 -0
  37. package/templates/ssr-isr/lib/cli/templates/ssg/site/index.js +9 -0
  38. package/templates/ssr-isr/lib/cli/templates/ssr/jen.config.js +32 -0
  39. package/templates/ssr-isr/lib/cli/templates/ssr/site/index.js +9 -0
  40. package/templates/ssr-isr/lib/compilers/esbuild-plugins.js +111 -0
  41. package/templates/ssr-isr/lib/compilers/svelte.js +44 -0
  42. package/templates/ssr-isr/lib/compilers/vue.js +90 -0
  43. package/templates/ssr-isr/lib/core/http.js +71 -0
  44. package/templates/ssr-isr/lib/core/middleware-hooks.js +97 -0
  45. package/templates/ssr-isr/lib/core/paths.js +39 -0
  46. package/templates/ssr-isr/lib/core/routes/match.js +47 -0
  47. package/templates/ssr-isr/lib/core/routes/scan.js +190 -0
  48. package/templates/ssr-isr/lib/core/types.js +1 -0
  49. package/templates/ssr-isr/lib/css/compiler.js +74 -0
  50. package/templates/ssr-isr/lib/db/connector.js +42 -0
  51. package/templates/ssr-isr/lib/db/drivers/jdb.js +44 -0
  52. package/templates/ssr-isr/lib/db/drivers/sql.js +182 -0
  53. package/templates/ssr-isr/lib/db/index.js +48 -0
  54. package/templates/ssr-isr/lib/db/types.js +1 -0
  55. package/templates/ssr-isr/lib/graphql/index.js +52 -0
  56. package/templates/ssr-isr/lib/graphql/resolvers.js +25 -0
  57. package/templates/ssr-isr/lib/graphql/schema.js +35 -0
  58. package/templates/ssr-isr/lib/i18n/en.json +4 -0
  59. package/templates/ssr-isr/lib/i18n/es.json +4 -0
  60. package/templates/ssr-isr/lib/i18n/index.js +15 -0
  61. package/templates/ssr-isr/lib/import/jen-import.js +161 -0
  62. package/templates/ssr-isr/lib/index.js +116 -0
  63. package/templates/ssr-isr/lib/jdb/engine.js +275 -0
  64. package/templates/ssr-isr/lib/jdb/index.js +34 -0
  65. package/templates/ssr-isr/lib/jdb/types.js +1 -0
  66. package/templates/ssr-isr/lib/jdb/utils.js +176 -0
  67. package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.js +0 -17
  68. package/templates/ssr-isr/lib/middleware/builtins/cors.js +54 -0
  69. package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.js +0 -17
  70. package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.js +0 -17
  71. package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.js +0 -17
  72. package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.js +0 -17
  73. package/templates/ssr-isr/lib/middleware/context.js +124 -0
  74. package/templates/{static → ssr-isr}/lib/middleware/decorators.js +0 -17
  75. package/templates/{static → ssr-isr}/lib/middleware/errors/handler.js +0 -17
  76. package/templates/ssr-isr/lib/middleware/errors/http-error.js +10 -0
  77. package/templates/ssr-isr/lib/middleware/kernel.js +85 -0
  78. package/templates/ssr-isr/lib/middleware/pipeline.js +148 -0
  79. package/templates/ssr-isr/lib/middleware/registry.js +85 -0
  80. package/templates/ssr-isr/lib/middleware/response.js +107 -0
  81. package/templates/ssr-isr/lib/middleware/types.d.ts +1 -0
  82. package/templates/ssr-isr/lib/middleware/types.js +1 -0
  83. package/templates/ssr-isr/lib/middleware/utils/matcher.js +13 -0
  84. package/templates/{static → ssr-isr}/lib/native/bundle.js +0 -17
  85. package/templates/{static → ssr-isr}/lib/native/dev-server.js +0 -17
  86. package/templates/{static → ssr-isr}/lib/native/index.js +0 -17
  87. package/templates/{static → ssr-isr}/lib/native/optimizer.js +0 -17
  88. package/templates/ssr-isr/lib/native/style-compiler.js +19 -0
  89. package/templates/ssr-isr/lib/plugin/loader.js +36 -0
  90. package/templates/ssr-isr/lib/runtime/client-runtime.js +25 -0
  91. package/templates/ssr-isr/lib/runtime/hmr.js +59 -0
  92. package/templates/ssr-isr/lib/runtime/hydrate.js +55 -0
  93. package/templates/ssr-isr/lib/runtime/island-hydration-client.js +146 -0
  94. package/templates/ssr-isr/lib/runtime/islands.js +110 -0
  95. package/templates/ssr-isr/lib/runtime/render.js +244 -0
  96. package/templates/ssr-isr/lib/server/api-routes.js +237 -0
  97. package/templates/ssr-isr/lib/server/api.js +108 -0
  98. package/templates/ssr-isr/lib/server/app.js +438 -0
  99. package/templates/ssr-isr/lib/server/runtimeServe.js +169 -0
  100. package/templates/ssr-isr/lib/server/ssr.js +202 -0
  101. package/templates/ssr-isr/lib/shared/log.js +64 -0
  102. package/templates/ssr-isr/package.json +23 -0
  103. package/templates/ssr-isr/server.js +128 -0
  104. package/templates/ssr-isr/site/pages/(index).tsx +11 -0
  105. package/templates/ssr-isr/site/styles/global.scss +37 -0
  106. package/templates/ssr-isr/tsconfig.json +39 -0
  107. package/templates/static/build.js +30 -18
  108. package/templates/static/jen.config.ts +0 -18
  109. package/templates/static/jenjs.d.ts +0 -18
  110. package/templates/static/lib/api/(hello).js +0 -17
  111. package/templates/static/lib/api/examples/files/[...slug].js +22 -0
  112. package/templates/static/lib/api/examples/hello.js +11 -0
  113. package/templates/static/lib/api/examples/posts/[id].js +37 -0
  114. package/templates/static/lib/api/examples/posts.js +37 -0
  115. package/templates/static/lib/api/examples/search.js +23 -0
  116. package/templates/static/lib/api/index.js +41 -0
  117. package/templates/static/lib/api/loader.js +234 -0
  118. package/templates/static/lib/api/router.js +259 -0
  119. package/templates/static/lib/assets/types.js +1 -0
  120. package/templates/static/lib/auth/cookie-utils.js +3 -16
  121. package/templates/static/lib/auth/index.js +0 -17
  122. package/templates/static/lib/auth/jwt.js +0 -17
  123. package/templates/static/lib/auth/session.js +0 -17
  124. package/templates/static/lib/build/asset-hashing.js +44 -36
  125. package/templates/static/lib/build/asset-manifest.js +16 -33
  126. package/templates/static/lib/build/build.js +270 -125
  127. package/templates/static/lib/build/bundle-analyzer-ui.js +417 -0
  128. package/templates/static/lib/build/bundle-analyzer.js +945 -0
  129. package/templates/static/lib/build/code-splitter.js +194 -0
  130. package/templates/static/lib/build/feature-analyzer.js +190 -0
  131. package/templates/static/lib/build/feature-gate.js +257 -0
  132. package/templates/static/lib/build/island-hydration.js +17 -35
  133. package/templates/static/lib/build/lazy-loader.js +322 -0
  134. package/templates/static/lib/build/minifier.js +40 -59
  135. package/templates/static/lib/build/page-renderer.js +23 -40
  136. package/templates/static/lib/build/production-build.js +9 -26
  137. package/templates/static/lib/build/rust-hashing.js +71 -0
  138. package/templates/static/lib/build/script-optimizer.js +285 -0
  139. package/templates/static/lib/build/ssg-pipeline.js +100 -106
  140. package/templates/static/lib/build/vercel-output.js +298 -0
  141. package/templates/static/lib/build-tools/build-site.js +0 -17
  142. package/templates/static/lib/cache/index.js +0 -17
  143. package/templates/static/lib/cache/memory.js +0 -17
  144. package/templates/static/lib/cache/redis.js +0 -17
  145. package/templates/static/lib/cli/banner.js +0 -17
  146. package/templates/static/lib/cli/templates/ssg/jen.config.js +0 -17
  147. package/templates/static/lib/cli/templates/ssr/jen.config.js +0 -17
  148. package/templates/static/lib/client/Image.js +42 -0
  149. package/templates/static/lib/client/Link.js +190 -0
  150. package/templates/static/lib/client/PWA.js +46 -0
  151. package/templates/static/lib/client/Seo.js +97 -0
  152. package/templates/static/lib/client/index.js +9 -0
  153. package/templates/static/lib/client/useNavigation.js +25 -0
  154. package/templates/static/lib/client/useRouter.js +64 -0
  155. package/templates/static/lib/client-routing/Link.js +17 -0
  156. package/templates/static/lib/client-routing/index.js +19 -0
  157. package/templates/static/lib/client-routing/router.js +151 -0
  158. package/templates/static/lib/client-routing/signal.js +147 -0
  159. package/templates/static/lib/compilers/esbuild-plugins.js +0 -17
  160. package/templates/static/lib/compilers/svelte.js +0 -17
  161. package/templates/static/lib/compilers/vue.js +0 -17
  162. package/templates/static/lib/core/config.js +0 -17
  163. package/templates/static/lib/core/feature-guard.js +136 -0
  164. package/templates/static/lib/core/features.js +99 -0
  165. package/templates/static/lib/core/http.js +0 -17
  166. package/templates/static/lib/core/layouts/index.js +10 -0
  167. package/templates/static/lib/core/layouts/render.js +158 -0
  168. package/templates/static/lib/core/layouts/scan.js +112 -0
  169. package/templates/static/lib/core/layouts/types.js +1 -0
  170. package/templates/static/lib/core/lifecycle.js +129 -0
  171. package/templates/static/lib/core/loader-schema.js +81 -0
  172. package/templates/static/lib/core/middleware-hooks.js +0 -17
  173. package/templates/static/lib/core/paths.js +0 -17
  174. package/templates/static/lib/core/routes/advanced.js +114 -0
  175. package/templates/static/lib/core/routes/handlers.js +181 -0
  176. package/templates/static/lib/core/routes/match.js +89 -17
  177. package/templates/static/lib/core/routes/orchestrator.js +171 -0
  178. package/templates/static/lib/core/routes/rendering-config.js +131 -0
  179. package/templates/static/lib/core/routes/scan.js +0 -17
  180. package/templates/static/lib/core/types.js +0 -17
  181. package/templates/static/lib/css/compiler.js +1 -18
  182. package/templates/static/lib/data-fetching/cache.js +223 -0
  183. package/templates/static/lib/data-fetching/client.js +202 -0
  184. package/templates/static/lib/data-fetching/feature-guard.js +29 -0
  185. package/templates/static/lib/data-fetching/graphql.js +265 -0
  186. package/templates/static/lib/data-fetching/index.js +57 -0
  187. package/templates/static/lib/data-fetching/rest.js +256 -0
  188. package/templates/static/lib/data-fetching/server.js +182 -0
  189. package/templates/static/lib/data-fetching/types.js +5 -0
  190. package/templates/static/lib/db/connector.js +0 -17
  191. package/templates/static/lib/db/drivers/jdb.js +0 -17
  192. package/templates/static/lib/db/drivers/sql.js +0 -17
  193. package/templates/static/lib/db/index.js +0 -17
  194. package/templates/static/lib/db/types.js +0 -17
  195. package/templates/static/lib/devtools/component-tree.js +106 -0
  196. package/templates/static/lib/devtools/devtools.js +638 -0
  197. package/templates/static/lib/devtools/event-bus.js +29 -0
  198. package/templates/static/lib/devtools/event-logger.js +67 -0
  199. package/templates/static/lib/devtools/index.js +9 -0
  200. package/templates/static/lib/devtools/integration.js +149 -0
  201. package/templates/static/lib/devtools/performance.js +84 -0
  202. package/templates/static/lib/devtools/persistence.js +57 -0
  203. package/templates/static/lib/devtools/plugins.js +97 -0
  204. package/templates/static/lib/devtools/search.js +89 -0
  205. package/templates/static/lib/devtools/ui.js +769 -0
  206. package/templates/static/lib/features/api/handler.js +10 -0
  207. package/templates/static/lib/features/api/index.js +5 -0
  208. package/templates/static/lib/features/api/types.js +4 -0
  209. package/templates/static/lib/features/middleware/compiled.js +7 -0
  210. package/templates/static/lib/features/middleware/index.js +5 -0
  211. package/templates/static/lib/features/middleware/types.js +4 -0
  212. package/templates/static/lib/fonts/index.js +46 -0
  213. package/templates/static/lib/fonts/inject.js +125 -0
  214. package/templates/static/lib/fonts/loader.js +196 -0
  215. package/templates/static/lib/fonts/types.js +1 -0
  216. package/templates/static/lib/graphql/index.js +1 -18
  217. package/templates/static/lib/graphql/resolvers.js +20 -13
  218. package/templates/static/lib/graphql/schema.js +0 -17
  219. package/templates/static/lib/i18n/index.js +7 -19
  220. package/templates/static/lib/import/jen-import.js +1 -18
  221. package/templates/static/lib/index.js +79 -125
  222. package/templates/static/lib/jdb/engine.js +0 -17
  223. package/templates/static/lib/jdb/index.js +1 -18
  224. package/templates/static/lib/jdb/types.js +0 -17
  225. package/templates/static/lib/jdb/utils.js +0 -17
  226. package/templates/static/lib/middleware/builtins/cors.js +3 -16
  227. package/templates/static/lib/middleware/context.js +0 -17
  228. package/templates/static/lib/middleware/kernel.js +117 -25
  229. package/templates/static/lib/middleware/pipeline.js +0 -17
  230. package/templates/static/lib/middleware/registry.js +0 -17
  231. package/templates/static/lib/middleware/response.js +0 -17
  232. package/templates/static/lib/plugin/examples/analytics-plugin.js +183 -0
  233. package/templates/static/lib/plugin/examples/cdn-upload-plugin.js +94 -0
  234. package/templates/static/lib/plugin/loader.js +0 -17
  235. package/templates/static/lib/plugin/plugin-manager.js +177 -0
  236. package/templates/static/lib/plugin/types.js +28 -0
  237. package/templates/static/lib/runtime/client-runtime.js +0 -17
  238. package/templates/static/lib/runtime/hmr.js +0 -17
  239. package/templates/static/lib/runtime/hydrate.js +0 -17
  240. package/templates/static/lib/runtime/island-hydration-client.js +0 -17
  241. package/templates/static/lib/runtime/islands.js +0 -17
  242. package/templates/static/lib/runtime/render.js +208 -50
  243. package/templates/static/lib/security/security-config.js +60 -0
  244. package/templates/static/lib/security/security-middleware.js +229 -0
  245. package/templates/static/lib/server/api-routes.js +153 -43
  246. package/templates/static/lib/server/api.js +0 -17
  247. package/templates/static/lib/server/app.js +539 -223
  248. package/templates/static/lib/server/isr.js +365 -0
  249. package/templates/static/lib/server/runtimeServe.js +31 -24
  250. package/templates/static/lib/server/ssr.js +98 -22
  251. package/templates/static/lib/server-actions/handler.js +180 -0
  252. package/templates/static/lib/server-actions/index.js +19 -0
  253. package/templates/static/lib/server-actions/middleware.js +146 -0
  254. package/templates/static/lib/server-actions/scan.js +152 -0
  255. package/templates/static/lib/server-actions/types.js +1 -0
  256. package/templates/static/lib/server-actions/validators.js +156 -0
  257. package/templates/static/lib/shared/log.js +19 -20
  258. package/templates/static/lib/telemetry/api/rate-limiter.js +32 -0
  259. package/templates/static/lib/telemetry/api/validator.js +67 -0
  260. package/templates/static/lib/telemetry/client.js +121 -0
  261. package/templates/static/lib/telemetry/tests/rate-limiter.test.js +46 -0
  262. package/templates/static/lib/telemetry/tests/validator.test.js +62 -0
  263. package/templates/static/lib/vendor/glob/glob.js +4766 -0
  264. package/templates/static/lib/vendor/preact/LICENSE +21 -0
  265. package/templates/static/lib/vendor/preact/preact.module.js +797 -0
  266. package/templates/static/lib/vendor/sass/sass.node.mjs +212 -0
  267. package/templates/static/package.json +4 -0
  268. package/templates/static/server.js +22 -22
  269. package/templates/static/site/(home).tsx +0 -18
  270. package/templates/static/tsconfig.json +5 -1
  271. package/templates/static/.esbuild/jen.config.js +0 -19
  272. package/templates/static/lib/build/asset-hashing.d.ts +0 -10
  273. package/templates/static/lib/build/asset-manifest.d.ts +0 -11
  274. package/templates/static/lib/build/minifier.d.ts +0 -20
  275. package/templates/static/lib/build/page-renderer.d.ts +0 -17
  276. package/templates/static/lib/build/production-build.d.ts +0 -10
  277. package/templates/static/lib/build/ssg-pipeline.d.ts +0 -15
  278. package/templates/static/lib/middleware/errors/http-error.js +0 -27
  279. package/templates/static/lib/middleware/types.js +0 -18
  280. package/templates/static/lib/middleware/utils/matcher.js +0 -30
  281. package/templates/static/lib/native/style-compiler.js +0 -36
  282. /package/templates/{static → ssr-isr}/lib/api/(hello).d.ts +0 -0
  283. /package/templates/{static → ssr-isr}/lib/auth/cookie-utils.d.ts +0 -0
  284. /package/templates/{static → ssr-isr}/lib/auth/index.d.ts +0 -0
  285. /package/templates/{static → ssr-isr}/lib/auth/jwt.d.ts +0 -0
  286. /package/templates/{static → ssr-isr}/lib/auth/session.d.ts +0 -0
  287. /package/templates/{static → ssr-isr}/lib/build-tools/build-site.d.ts +0 -0
  288. /package/templates/{static → ssr-isr}/lib/cache/index.d.ts +0 -0
  289. /package/templates/{static → ssr-isr}/lib/cache/memory.d.ts +0 -0
  290. /package/templates/{static → ssr-isr}/lib/cache/redis.d.ts +0 -0
  291. /package/templates/{static → ssr-isr}/lib/cli/banner.d.ts +0 -0
  292. /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/jen.config.d.ts +0 -0
  293. /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/site/index.d.ts +0 -0
  294. /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/jen.config.d.ts +0 -0
  295. /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/site/index.d.ts +0 -0
  296. /package/templates/{static → ssr-isr}/lib/compilers/esbuild-plugins.d.ts +0 -0
  297. /package/templates/{static → ssr-isr}/lib/compilers/svelte.d.ts +0 -0
  298. /package/templates/{static → ssr-isr}/lib/compilers/vue.d.ts +0 -0
  299. /package/templates/{static → ssr-isr}/lib/core/config.d.ts +0 -0
  300. /package/templates/{static/lib/middleware/types.d.ts → ssr-isr/lib/core/config.js} +0 -0
  301. /package/templates/{static → ssr-isr}/lib/core/http.d.ts +0 -0
  302. /package/templates/{static → ssr-isr}/lib/core/middleware-hooks.d.ts +0 -0
  303. /package/templates/{static → ssr-isr}/lib/core/paths.d.ts +0 -0
  304. /package/templates/{static → ssr-isr}/lib/core/routes/match.d.ts +0 -0
  305. /package/templates/{static → ssr-isr}/lib/core/routes/scan.d.ts +0 -0
  306. /package/templates/{static → ssr-isr}/lib/core/types.d.ts +0 -0
  307. /package/templates/{static → ssr-isr}/lib/css/compiler.d.ts +0 -0
  308. /package/templates/{static → ssr-isr}/lib/db/connector.d.ts +0 -0
  309. /package/templates/{static → ssr-isr}/lib/db/drivers/jdb.d.ts +0 -0
  310. /package/templates/{static → ssr-isr}/lib/db/drivers/sql.d.ts +0 -0
  311. /package/templates/{static → ssr-isr}/lib/db/index.d.ts +0 -0
  312. /package/templates/{static → ssr-isr}/lib/db/types.d.ts +0 -0
  313. /package/templates/{static → ssr-isr}/lib/graphql/index.d.ts +0 -0
  314. /package/templates/{static → ssr-isr}/lib/graphql/resolvers.d.ts +0 -0
  315. /package/templates/{static → ssr-isr}/lib/graphql/schema.d.ts +0 -0
  316. /package/templates/{static → ssr-isr}/lib/i18n/index.d.ts +0 -0
  317. /package/templates/{static → ssr-isr}/lib/import/jen-import.d.ts +0 -0
  318. /package/templates/{static → ssr-isr}/lib/index.d.ts +0 -0
  319. /package/templates/{static → ssr-isr}/lib/jdb/engine.d.ts +0 -0
  320. /package/templates/{static → ssr-isr}/lib/jdb/index.d.ts +0 -0
  321. /package/templates/{static → ssr-isr}/lib/jdb/types.d.ts +0 -0
  322. /package/templates/{static → ssr-isr}/lib/jdb/utils.d.ts +0 -0
  323. /package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.d.ts +0 -0
  324. /package/templates/{static → ssr-isr}/lib/middleware/builtins/cors.d.ts +0 -0
  325. /package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.d.ts +0 -0
  326. /package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.d.ts +0 -0
  327. /package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.d.ts +0 -0
  328. /package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.d.ts +0 -0
  329. /package/templates/{static → ssr-isr}/lib/middleware/context.d.ts +0 -0
  330. /package/templates/{static → ssr-isr}/lib/middleware/decorators.d.ts +0 -0
  331. /package/templates/{static → ssr-isr}/lib/middleware/errors/handler.d.ts +0 -0
  332. /package/templates/{static → ssr-isr}/lib/middleware/errors/http-error.d.ts +0 -0
  333. /package/templates/{static → ssr-isr}/lib/middleware/kernel.d.ts +0 -0
  334. /package/templates/{static → ssr-isr}/lib/middleware/pipeline.d.ts +0 -0
  335. /package/templates/{static → ssr-isr}/lib/middleware/registry.d.ts +0 -0
  336. /package/templates/{static → ssr-isr}/lib/middleware/response.d.ts +0 -0
  337. /package/templates/{static → ssr-isr}/lib/middleware/utils/matcher.d.ts +0 -0
  338. /package/templates/{static → ssr-isr}/lib/native/bundle.d.ts +0 -0
  339. /package/templates/{static → ssr-isr}/lib/native/dev-server.d.ts +0 -0
  340. /package/templates/{static → ssr-isr}/lib/native/index.d.ts +0 -0
  341. /package/templates/{static → ssr-isr}/lib/native/optimizer.d.ts +0 -0
  342. /package/templates/{static → ssr-isr}/lib/native/style-compiler.d.ts +0 -0
  343. /package/templates/{static → ssr-isr}/lib/plugin/loader.d.ts +0 -0
  344. /package/templates/{static → ssr-isr}/lib/runtime/client-runtime.d.ts +0 -0
  345. /package/templates/{static → ssr-isr}/lib/runtime/hmr.d.ts +0 -0
  346. /package/templates/{static → ssr-isr}/lib/runtime/hydrate.d.ts +0 -0
  347. /package/templates/{static → ssr-isr}/lib/runtime/island-hydration-client.d.ts +0 -0
  348. /package/templates/{static → ssr-isr}/lib/runtime/islands.d.ts +0 -0
  349. /package/templates/{static → ssr-isr}/lib/runtime/render.d.ts +0 -0
  350. /package/templates/{static → ssr-isr}/lib/server/api-routes.d.ts +0 -0
  351. /package/templates/{static → ssr-isr}/lib/server/api.d.ts +0 -0
  352. /package/templates/{static → ssr-isr}/lib/server/app.d.ts +0 -0
  353. /package/templates/{static → ssr-isr}/lib/server/runtimeServe.d.ts +0 -0
  354. /package/templates/{static → ssr-isr}/lib/server/ssr.d.ts +0 -0
  355. /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
+ }