@pyreon/zero 0.1.1 → 0.2.0

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 (57) hide show
  1. package/README.md +17 -6
  2. package/lib/fs-router-BkbIWqek.js.map +1 -1
  3. package/lib/{fs-router-jfd1QGLB.js → fs-router-n4VA4lxu.js} +29 -4
  4. package/lib/fs-router-n4VA4lxu.js.map +1 -0
  5. package/lib/image.js +50 -1
  6. package/lib/image.js.map +1 -1
  7. package/lib/index.js +651 -11
  8. package/lib/index.js.map +1 -1
  9. package/lib/link.js +49 -1
  10. package/lib/link.js.map +1 -1
  11. package/lib/script.js +49 -1
  12. package/lib/script.js.map +1 -1
  13. package/lib/theme.js +50 -1
  14. package/lib/theme.js.map +1 -1
  15. package/lib/types/actions.d.ts +57 -0
  16. package/lib/types/actions.d.ts.map +1 -0
  17. package/lib/types/api-routes.d.ts +66 -0
  18. package/lib/types/api-routes.d.ts.map +1 -0
  19. package/lib/types/compression.d.ts +33 -0
  20. package/lib/types/compression.d.ts.map +1 -0
  21. package/lib/types/cors.d.ts +32 -0
  22. package/lib/types/cors.d.ts.map +1 -0
  23. package/lib/types/entry-server.d.ts +10 -2
  24. package/lib/types/entry-server.d.ts.map +1 -1
  25. package/lib/types/error-overlay.d.ts +6 -0
  26. package/lib/types/error-overlay.d.ts.map +1 -0
  27. package/lib/types/fs-router.d.ts +5 -0
  28. package/lib/types/fs-router.d.ts.map +1 -1
  29. package/lib/types/image.d.ts +1 -1
  30. package/lib/types/image.d.ts.map +1 -1
  31. package/lib/types/index.d.ts +12 -2
  32. package/lib/types/index.d.ts.map +1 -1
  33. package/lib/types/rate-limit.d.ts +34 -0
  34. package/lib/types/rate-limit.d.ts.map +1 -0
  35. package/lib/types/script.d.ts +1 -1
  36. package/lib/types/script.d.ts.map +1 -1
  37. package/lib/types/testing.d.ts +85 -0
  38. package/lib/types/testing.d.ts.map +1 -0
  39. package/lib/types/theme.d.ts +1 -1
  40. package/lib/types/theme.d.ts.map +1 -1
  41. package/lib/types/types.d.ts +5 -0
  42. package/lib/types/types.d.ts.map +1 -1
  43. package/lib/types/vite-plugin.d.ts.map +1 -1
  44. package/package.json +40 -9
  45. package/src/actions.ts +168 -0
  46. package/src/api-routes.ts +233 -0
  47. package/src/compression.ts +107 -0
  48. package/src/cors.ts +102 -0
  49. package/src/entry-server.ts +62 -7
  50. package/src/error-overlay.ts +121 -0
  51. package/src/fs-router.ts +34 -2
  52. package/src/index.ts +37 -0
  53. package/src/rate-limit.ts +122 -0
  54. package/src/testing.ts +150 -0
  55. package/src/types.ts +8 -0
  56. package/src/vite-plugin.ts +75 -10
  57. package/lib/fs-router-jfd1QGLB.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAalD,MAAM,WAAW,UAAU;IACzB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAA;IACX,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAA;IAC/B,mFAAmF;IACnF,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB,qGAAqG;IACrG,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC1B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uCAAuC;IACvC,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAA;IAC1D,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,OAsGtC"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAalD,MAAM,WAAW,UAAU;IACzB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAA;IACX,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAA;IAC/B,mFAAmF;IACnF,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB,qGAAqG;IACrG,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC1B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uCAAuC;IACvC,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAA;IAC1D,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,gCAsGtC"}
@@ -3,7 +3,7 @@ export { createApp } from './app';
3
3
  export type { CreateServerOptions } from './entry-server';
4
4
  export { createServer } from './entry-server';
5
5
  export { zeroPlugin as default } from './vite-plugin';
6
- export { filePathToUrlPath, generateRouteModule, parseFileRoutes, scanRouteFiles, } from './fs-router';
6
+ export { filePathToUrlPath, generateMiddlewareModule, generateRouteModule, parseFileRoutes, scanRouteFiles, } from './fs-router';
7
7
  export { defineConfig, resolveConfig } from './config';
8
8
  export { createISRHandler } from './isr';
9
9
  export { bunAdapter, nodeAdapter, resolveAdapter, staticAdapter, } from './adapters';
@@ -23,5 +23,15 @@ export type { Theme } from './theme';
23
23
  export { initTheme, resolvedTheme, setTheme, ThemeToggle, theme, themeScript, toggleTheme, } from './theme';
24
24
  export type { ChangeFreq, JsonLdType, RobotsConfig, RobotsRule, SeoPluginConfig, SitemapConfig, SitemapEntry, } from './seo';
25
25
  export { generateRobots, generateSitemap, jsonLd, seoMiddleware, seoPlugin, } from './seo';
26
- export type { Adapter, AdapterBuildOptions, FileRoute, ISRConfig, LoaderContext, RenderMode, RouteMeta, RouteModule, ZeroConfig, } from './types';
26
+ export type { ApiContext, ApiHandler, ApiRouteEntry, ApiRouteModule, HttpMethod, } from './api-routes';
27
+ export { createApiMiddleware, generateApiRouteModule } from './api-routes';
28
+ export type { CorsConfig } from './cors';
29
+ export { corsMiddleware } from './cors';
30
+ export type { RateLimitConfig } from './rate-limit';
31
+ export { rateLimitMiddleware } from './rate-limit';
32
+ export type { CompressionConfig } from './compression';
33
+ export { compressionMiddleware, compressResponse, isCompressible, } from './compression';
34
+ export type { Action, ActionContext, ActionHandler } from './actions';
35
+ export { createActionMiddleware, defineAction } from './actions';
36
+ export type { Adapter, AdapterBuildOptions, FileRoute, ISRConfig, LoaderContext, RenderMode, RouteMeta, RouteMiddlewareEntry, RouteModule, ZeroConfig, } from './types';
27
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAI7C,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,cAAc,GACf,MAAM,aAAa,CAAA;AAIpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAItD,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAIxC,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAA;AAInB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAClD,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAIjC,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAIxE,YAAY,EACV,eAAe,EACf,UAAU,EACV,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,SAAS,GACV,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AAIlD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAI5C,YAAY,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EACL,SAAS,EACT,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAAK,EACL,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA;AAIhB,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,UAAU,EACV,eAAe,EACf,aAAa,EACb,YAAY,GACb,MAAM,OAAO,CAAA;AACd,OAAO,EACL,cAAc,EACd,eAAe,EACf,MAAM,EACN,aAAa,EACb,SAAS,GACV,MAAM,OAAO,CAAA;AAId,YAAY,EACV,OAAO,EACP,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,aAAa,EACb,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Middleware, MiddlewareContext } from '@pyreon/server';
2
+ export interface RateLimitConfig {
3
+ /** Maximum requests per window. Default: `100` */
4
+ max?: number;
5
+ /** Time window in seconds. Default: `60` */
6
+ window?: number;
7
+ /** Function to extract the client identifier. Default: IP from headers. */
8
+ keyFn?: (ctx: MiddlewareContext) => string;
9
+ /** Custom response when rate limited. */
10
+ onLimit?: (ctx: MiddlewareContext) => Response;
11
+ /** URL patterns to rate limit (glob-style). Default: all paths. */
12
+ include?: string[];
13
+ /** URL patterns to exclude from rate limiting. */
14
+ exclude?: string[];
15
+ }
16
+ /**
17
+ * Rate limiting middleware — limits requests per client within a time window.
18
+ * Uses an in-memory store (suitable for single-instance deployments).
19
+ *
20
+ * @example
21
+ * import { rateLimitMiddleware } from "@pyreon/zero/rate-limit"
22
+ *
23
+ * // 100 requests per minute (default)
24
+ * rateLimitMiddleware()
25
+ *
26
+ * // Strict API rate limiting
27
+ * rateLimitMiddleware({
28
+ * max: 20,
29
+ * window: 60,
30
+ * include: ["/api/*"],
31
+ * })
32
+ */
33
+ export declare function rateLimitMiddleware(config?: RateLimitConfig): Middleware;
34
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAInE,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,MAAM,CAAA;IAC1C,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,QAAQ,CAAA;IAC9C,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,eAAoB,GAAG,UAAU,CAgE5E"}
@@ -30,5 +30,5 @@ export type ScriptStrategy = 'beforeHydration' | 'afterHydration' | 'onIdle' | '
30
30
  * {`console.log("App hydrated!")`}
31
31
  * </Script>
32
32
  */
33
- export declare function Script(props: ScriptProps): any;
33
+ export declare function Script(props: ScriptProps): import("@pyreon/core").VNode | null;
34
34
  //# sourceMappingURL=script.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../src/script.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mCAAmC;IACnC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,gBAAgB,GAChB,QAAQ,GACR,eAAe,GACf,YAAY,CAAA;AAEhB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,OAiFxC"}
1
+ {"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../src/script.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mCAAmC;IACnC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,gBAAgB,GAChB,QAAQ,GACR,eAAe,GACf,YAAY,CAAA;AAEhB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,uCAiFxC"}
@@ -0,0 +1,85 @@
1
+ import type { Middleware, MiddlewareContext } from '@pyreon/server';
2
+ import type { ApiHandler, ApiRouteEntry } from './api-routes';
3
+ /**
4
+ * Create a mock MiddlewareContext for testing middleware.
5
+ *
6
+ * @example
7
+ * import { createTestContext } from "@pyreon/zero/testing"
8
+ *
9
+ * const ctx = createTestContext("/api/posts", { method: "POST", body: { title: "Hello" } })
10
+ * const result = await myMiddleware(ctx)
11
+ */
12
+ export declare function createTestContext(path: string, options?: {
13
+ method?: string;
14
+ headers?: Record<string, string>;
15
+ body?: unknown;
16
+ }): MiddlewareContext;
17
+ /**
18
+ * Test a middleware by running it with a mock context and returning
19
+ * the result along with the response headers it set.
20
+ *
21
+ * @example
22
+ * import { testMiddleware } from "@pyreon/zero/testing"
23
+ *
24
+ * const { response, headers } = await testMiddleware(
25
+ * corsMiddleware({ origin: "*" }),
26
+ * "/api/posts"
27
+ * )
28
+ * expect(headers.get("Access-Control-Allow-Origin")).toBe("*")
29
+ */
30
+ export declare function testMiddleware(middleware: Middleware, path: string, options?: {
31
+ method?: string;
32
+ headers?: Record<string, string>;
33
+ body?: unknown;
34
+ }): Promise<{
35
+ response: Response | undefined;
36
+ headers: Headers;
37
+ }>;
38
+ /**
39
+ * Create a test server for API routes. Returns a function that
40
+ * accepts Request objects and dispatches to the correct handler.
41
+ *
42
+ * @example
43
+ * import { createTestApiServer } from "@pyreon/zero/testing"
44
+ *
45
+ * const server = createTestApiServer([
46
+ * { pattern: "/api/posts", module: postsApi },
47
+ * { pattern: "/api/posts/:id", module: postByIdApi },
48
+ * ])
49
+ *
50
+ * const response = await server.request("/api/posts")
51
+ * expect(response.status).toBe(200)
52
+ *
53
+ * const data = await server.request("/api/posts", { method: "POST", body: { title: "Hi" } })
54
+ * expect(data.status).toBe(201)
55
+ */
56
+ export declare function createTestApiServer(routes: ApiRouteEntry[]): {
57
+ request(path: string, options?: {
58
+ method?: string;
59
+ headers?: Record<string, string>;
60
+ body?: unknown;
61
+ }): Promise<Response>;
62
+ };
63
+ /**
64
+ * Create a mock API handler for testing.
65
+ * Records all calls and returns a configurable response.
66
+ *
67
+ * @example
68
+ * import { createMockHandler } from "@pyreon/zero/testing"
69
+ *
70
+ * const handler = createMockHandler({ status: 200, body: { ok: true } })
71
+ * // ... use handler in your API route module
72
+ * expect(handler.calls).toHaveLength(1)
73
+ * expect(handler.calls[0].params).toEqual({ id: "123" })
74
+ */
75
+ export declare function createMockHandler(responseConfig?: {
76
+ status?: number;
77
+ body?: unknown;
78
+ headers?: Record<string, string>;
79
+ }): ApiHandler & {
80
+ calls: Array<{
81
+ path: string;
82
+ params: Record<string, string>;
83
+ }>;
84
+ };
85
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAK7D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,EAAE,OAAO,CAAA;CACV,GACL,iBAAiB,CAyBnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,EAAE,OAAO,CAAA;CACV,GACL,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAI/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE;kBAK/C,MAAM,YACH;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,IAAI,CAAC,EAAE,OAAO,CAAA;KACf,GACA,OAAO,CAAC,QAAQ,CAAC;EASvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,GAAE;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC5B,GACL,UAAU,GAAG;IACd,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAA;CAC/D,CAaA"}
@@ -22,7 +22,7 @@ export declare function initTheme(): void;
22
22
  export declare function ThemeToggle(props: {
23
23
  class?: string;
24
24
  style?: string;
25
- }): any;
25
+ }): import("@pyreon/core").VNode;
26
26
  /**
27
27
  * Inline script to prevent flash of wrong theme.
28
28
  * Include this in your index.html <head> BEFORE any stylesheets.
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAI/C,6BAA6B;AAC7B,eAAO,MAAM,KAAK,4CAA0B,CAAA;AAE5C,yDAAyD;AACzD,wBAAgB,aAAa,IAAI,OAAO,GAAG,MAAM,CAShD;AAED,qCAAqC;AACrC,wBAAgB,WAAW,SAG1B;AAED,4BAA4B;AAC5B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,QAUhC;AAED;;;GAGG;AACH,wBAAgB,SAAS,SAiCxB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,OAqDpE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,yPAA6O,CAAA"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAI/C,6BAA6B;AAC7B,eAAO,MAAM,KAAK,4CAA0B,CAAA;AAE5C,yDAAyD;AACzD,wBAAgB,aAAa,IAAI,OAAO,GAAG,MAAM,CAShD;AAED,qCAAqC;AACrC,wBAAgB,WAAW,SAG1B;AAED,4BAA4B;AAC5B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,QAUhC;AAED;;;GAGG;AACH,wBAAgB,SAAS,SAiCxB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,gCAqDpE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,yPAA6O,CAAA"}
@@ -87,6 +87,11 @@ export interface FileRoute {
87
87
  /** Resolved rendering mode. */
88
88
  renderMode: RenderMode;
89
89
  }
90
+ /** Entry mapping a URL pattern to its route-level middleware. */
91
+ export interface RouteMiddlewareEntry {
92
+ pattern: string;
93
+ middleware: Middleware | Middleware[];
94
+ }
90
95
  export interface Adapter {
91
96
  name: string;
92
97
  /** Build the production server/output for this adapter. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAGlC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,UAAU,GAAE,UAAe,GAAG,MAAM,CAsE9D"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AASlC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAWzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,UAAU,GAAE,UAAe,GAAG,MAAM,CA2H9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/zero",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Pyreon Zero — zero-config full-stack framework powered by Pyreon and Vite",
5
5
  "license": "MIT",
6
6
  "author": "Vit Bokisch",
@@ -76,6 +76,36 @@
76
76
  "bun": "./src/image-plugin.ts",
77
77
  "import": "./lib/image-plugin.js",
78
78
  "types": "./lib/types/image-plugin.d.ts"
79
+ },
80
+ "./actions": {
81
+ "bun": "./src/actions.ts",
82
+ "import": "./lib/actions.js",
83
+ "types": "./lib/types/actions.d.ts"
84
+ },
85
+ "./api-routes": {
86
+ "bun": "./src/api-routes.ts",
87
+ "import": "./lib/api-routes.js",
88
+ "types": "./lib/types/api-routes.d.ts"
89
+ },
90
+ "./cors": {
91
+ "bun": "./src/cors.ts",
92
+ "import": "./lib/cors.js",
93
+ "types": "./lib/types/cors.d.ts"
94
+ },
95
+ "./rate-limit": {
96
+ "bun": "./src/rate-limit.ts",
97
+ "import": "./lib/rate-limit.js",
98
+ "types": "./lib/types/rate-limit.d.ts"
99
+ },
100
+ "./compression": {
101
+ "bun": "./src/compression.ts",
102
+ "import": "./lib/compression.js",
103
+ "types": "./lib/types/compression.d.ts"
104
+ },
105
+ "./testing": {
106
+ "bun": "./src/testing.ts",
107
+ "import": "./lib/testing.js",
108
+ "types": "./lib/types/testing.d.ts"
79
109
  }
80
110
  },
81
111
  "scripts": {
@@ -85,16 +115,17 @@
85
115
  "typecheck": "tsc --noEmit"
86
116
  },
87
117
  "dependencies": {
88
- "@pyreon/core": "^0.2.1",
89
- "@pyreon/head": "^0.2.1",
90
- "@pyreon/router": "^0.2.1",
91
- "@pyreon/runtime-dom": "^0.2.1",
92
- "@pyreon/runtime-server": "^0.2.1",
93
- "@pyreon/server": "^0.2.1",
94
- "@pyreon/vite-plugin": "^0.2.1",
118
+ "@pyreon/core": "^0.5.4",
119
+ "@pyreon/head": "^0.5.4",
120
+ "@pyreon/meta": "^0.2.0",
121
+ "@pyreon/router": "^0.5.4",
122
+ "@pyreon/runtime-dom": "^0.5.4",
123
+ "@pyreon/runtime-server": "^0.5.4",
124
+ "@pyreon/server": "^0.5.4",
125
+ "@pyreon/vite-plugin": "^0.5.4",
95
126
  "vite": "^8.0.0"
96
127
  },
97
128
  "peerDependencies": {
98
- "@pyreon/reactivity": "^0.2.1"
129
+ "@pyreon/reactivity": "^0.5.4"
99
130
  }
100
131
  }
package/src/actions.ts ADDED
@@ -0,0 +1,168 @@
1
+ import type { MiddlewareContext } from '@pyreon/server'
2
+
3
+ // ─── Types ───────────────────────────────────────────────────────────────────
4
+
5
+ /** Context passed to server action handlers. */
6
+ export interface ActionContext {
7
+ /** The original request. */
8
+ request: Request
9
+ /** Parsed form data (for form submissions). */
10
+ formData: FormData | null
11
+ /** Parsed JSON body (for JSON submissions). */
12
+ json: unknown
13
+ /** Request headers. */
14
+ headers: Headers
15
+ }
16
+
17
+ /** A server action handler function. */
18
+ export type ActionHandler<T = unknown> = (ctx: ActionContext) => T | Promise<T>
19
+
20
+ /** A registered action with its ID and handler. */
21
+ interface RegisteredAction {
22
+ id: string
23
+ handler: ActionHandler
24
+ }
25
+
26
+ /** Client-side callable action returned by defineAction. */
27
+ export interface Action<T = unknown> {
28
+ /** Call the action with JSON data. */
29
+ (data?: unknown): Promise<T>
30
+ /** The action's unique ID. */
31
+ actionId: string
32
+ }
33
+
34
+ // ─── Registry ────────────────────────────────────────────────────────────────
35
+
36
+ const actionRegistry = new Map<string, RegisteredAction>()
37
+ let actionCounter = 0
38
+
39
+ /**
40
+ * Define a server action. Returns a callable function that:
41
+ * - On the **client**: sends a POST request to `/_zero/actions/<id>`
42
+ * - On the **server** (SSR): executes the handler directly (no fetch)
43
+ *
44
+ * @example
45
+ * // In a route file or module:
46
+ * export const createPost = defineAction(async (ctx) => {
47
+ * const data = ctx.json as { title: string; body: string }
48
+ * // ... save to database
49
+ * return { success: true, id: 123 }
50
+ * })
51
+ *
52
+ * // In a component:
53
+ * const result = await createPost({ title: 'Hello', body: '...' })
54
+ */
55
+ export function defineAction<T = unknown>(
56
+ handler: ActionHandler<T>,
57
+ ): Action<T> {
58
+ const id = `action_${actionCounter++}`
59
+
60
+ actionRegistry.set(id, { id, handler: handler as ActionHandler })
61
+
62
+ const callable = async (data?: unknown): Promise<T> => {
63
+ // Server-side: execute handler directly (no network round-trip)
64
+ if (typeof globalThis.window === 'undefined') {
65
+ return handler({
66
+ request: new Request(`http://localhost/_zero/actions/${id}`, {
67
+ method: 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify(data ?? null),
70
+ }),
71
+ formData: null,
72
+ json: data ?? null,
73
+ headers: new Headers({ 'Content-Type': 'application/json' }),
74
+ })
75
+ }
76
+
77
+ // Client-side: POST to the action endpoint
78
+ const response = await fetch(`/_zero/actions/${id}`, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify(data ?? null),
82
+ })
83
+ if (!response.ok) {
84
+ const body = await response.json().catch(() => ({}))
85
+ throw new Error(
86
+ (body as { error?: string }).error ??
87
+ `Action failed: ${response.statusText}`,
88
+ )
89
+ }
90
+ return response.json()
91
+ }
92
+
93
+ callable.actionId = id
94
+ return callable as Action<T>
95
+ }
96
+
97
+ /** Get all registered actions. Useful for testing. */
98
+ export function getRegisteredActions(): Map<string, RegisteredAction> {
99
+ return actionRegistry
100
+ }
101
+
102
+ /**
103
+ * Reset the action registry. Useful for testing.
104
+ * @internal
105
+ */
106
+ export function _resetActions(): void {
107
+ actionRegistry.clear()
108
+ actionCounter = 0
109
+ }
110
+
111
+ // ─── Server handler ──────────────────────────────────────────────────────────
112
+
113
+ /**
114
+ * Create a middleware that handles action requests at `/_zero/actions/*`.
115
+ * Mount this before the SSR handler in the server entry.
116
+ */
117
+ export function createActionMiddleware(): (
118
+ ctx: MiddlewareContext,
119
+ ) => Response | undefined | Promise<Response | undefined> {
120
+ return async (ctx: MiddlewareContext) => {
121
+ if (!ctx.path.startsWith('/_zero/actions/')) return
122
+
123
+ const actionId = ctx.path.slice('/_zero/actions/'.length)
124
+ const action = actionRegistry.get(actionId)
125
+
126
+ if (!action) {
127
+ return Response.json({ error: 'Action not found' }, { status: 404 })
128
+ }
129
+
130
+ if (ctx.req.method !== 'POST') {
131
+ return Response.json({ error: 'Method not allowed' }, { status: 405 })
132
+ }
133
+
134
+ return executeAction(action, ctx.req)
135
+ }
136
+ }
137
+
138
+ async function executeAction(
139
+ action: RegisteredAction,
140
+ req: Request,
141
+ ): Promise<Response> {
142
+ try {
143
+ const contentType = req.headers.get('content-type') ?? ''
144
+ let formData: FormData | null = null
145
+ let json: unknown = null
146
+
147
+ if (contentType.includes('application/json')) {
148
+ json = await req.json()
149
+ } else if (
150
+ contentType.includes('multipart/form-data') ||
151
+ contentType.includes('application/x-www-form-urlencoded')
152
+ ) {
153
+ formData = await req.formData()
154
+ }
155
+
156
+ const result = await action.handler({
157
+ request: req,
158
+ formData,
159
+ json,
160
+ headers: req.headers,
161
+ })
162
+
163
+ return Response.json(result ?? null)
164
+ } catch (err) {
165
+ const message = err instanceof Error ? err.message : 'Internal server error'
166
+ return Response.json({ error: message }, { status: 500 })
167
+ }
168
+ }