elysia-rack 0.1.1

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/Dashboard-xpstr7fr.js +172 -0
  4. package/dist/Panel-fbp3wrsw.js +738 -0
  5. package/dist/index-37x76zdn.js +4 -0
  6. package/dist/index-zc7rgvmm.js +13 -0
  7. package/dist/panel-app.js +1 -0
  8. package/dist/panel-theme.js +1 -0
  9. package/dist/panel.css +1 -0
  10. package/dist/rack/adapter.d.ts +5 -0
  11. package/dist/rack/adapters/drizzle.d.ts +2 -0
  12. package/dist/rack/adapters/index.d.ts +59 -0
  13. package/dist/rack/adapters/prisma.d.ts +2 -0
  14. package/dist/rack/authorization.d.ts +23 -0
  15. package/dist/rack/idempotency.d.ts +9 -0
  16. package/dist/rack/index.d.ts +40 -0
  17. package/dist/rack/index.js +1362 -0
  18. package/dist/rack/models/drizzle.d.ts +4 -0
  19. package/dist/rack/models/index.d.ts +24 -0
  20. package/dist/rack/models/prisma.d.ts +4 -0
  21. package/dist/rack/openapi.d.ts +6 -0
  22. package/dist/rack/page.d.ts +33 -0
  23. package/dist/rack/query.d.ts +22 -0
  24. package/dist/rack/registry.d.ts +20 -0
  25. package/dist/rack/types/authorization.d.ts +16 -0
  26. package/dist/rack/types/idempotency.d.ts +46 -0
  27. package/dist/rack/types/index.d.ts +89 -0
  28. package/dist/rack/types/metadata.d.ts +36 -0
  29. package/dist/rack/types/openapi.d.ts +22 -0
  30. package/dist/rack/types/operations.d.ts +14 -0
  31. package/dist/rack/types/page.d.ts +41 -0
  32. package/dist/rack/types/query.d.ts +30 -0
  33. package/dist/rack/types/validation.d.ts +19 -0
  34. package/dist/react/index.d.ts +64 -0
  35. package/dist/react/index.js +126 -0
  36. package/dist/react/page/Dashboard.d.ts +7 -0
  37. package/dist/react/page/Panel.d.ts +36 -0
  38. package/dist/react/page/client/panel.d.ts +1 -0
  39. package/dist/react/page/client/theme.d.ts +1 -0
  40. package/dist/react/page/components/Breadcrumb.d.ts +7 -0
  41. package/dist/react/page/components/DataTable.d.ts +10 -0
  42. package/dist/react/page/components/Masthead.d.ts +7 -0
  43. package/dist/react/page/components/Pagination.d.ts +10 -0
  44. package/dist/react/page/components/Sidebar.d.ts +16 -0
  45. package/dist/react/page/components/Toolbar.d.ts +7 -0
  46. package/dist/react/page/components/href.d.ts +10 -0
  47. package/dist/react/page/components/panelForm.d.ts +9 -0
  48. package/dist/react/render.d.ts +2 -0
  49. package/dist/react/types.d.ts +16 -0
  50. package/package.json +89 -0
@@ -0,0 +1,4 @@
1
+ export interface CrudDrizzleModel {
2
+ db: unknown;
3
+ table: unknown;
4
+ }
@@ -0,0 +1,24 @@
1
+ export type { CrudDrizzleModel } from "./drizzle";
2
+ export type { CrudPrismaModel } from "./prisma";
3
+ import type { CrudDrizzleModel } from "./drizzle";
4
+ import type { CrudPrismaModel } from "./prisma";
5
+ /**
6
+ * ORM model/resource configuration.
7
+ *
8
+ * Union so a single resource cannot accidentally
9
+ * hold two ORMs at once.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * model: {
14
+ * drizzle: { db, table: products }
15
+ * }
16
+ * ```
17
+ */
18
+ export type CrudModel = {
19
+ drizzle: CrudDrizzleModel;
20
+ prisma?: never;
21
+ } | {
22
+ prisma: CrudPrismaModel;
23
+ drizzle?: never;
24
+ };
@@ -0,0 +1,4 @@
1
+ export interface CrudPrismaModel {
2
+ client: unknown;
3
+ model: unknown;
4
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rack } from "./types";
2
+ /**
3
+ * Merge resource-level `options.openapi` with per-operation overrides
4
+ * into an Elysia `detail` hook.
5
+ */
6
+ export declare function detailFor(options: Rack.CrudOptions, operation: Rack.CrudOperation): Record<string, unknown>;
@@ -0,0 +1,33 @@
1
+ import type { FieldDescriptor } from "./adapters/index";
2
+ import type { Rack } from "./types";
3
+ /**
4
+ * Registry key shared with `ReactRack.PAGE` (src/react/types.ts).
5
+ * `Symbol.for` makes both sides resolve to the same symbol
6
+ * without a runtime dependency from rack to react.
7
+ * Must stay in sync.
8
+ */
9
+ export declare const PANEL_PAGE_KEY = "@elysia-panel/react";
10
+ export interface PanelPageProps {
11
+ resource: string;
12
+ metadata?: Rack.CrudMetadata;
13
+ query?: Rack.CrudQueryOptions;
14
+ operations?: Rack.CrudOperationsOptions;
15
+ primaryKey?: string;
16
+ params?: Record<string, unknown>;
17
+ queryUrl?: string;
18
+ fields?: FieldDescriptor[];
19
+ deletedAtField?: string;
20
+ load?: (input: Record<string, unknown>) => Promise<{
21
+ data: unknown[];
22
+ total: number;
23
+ }>;
24
+ }
25
+ /**
26
+ * Build a page descriptor for the React panel,
27
+ * equivalent to `page(path, props)` from `elysia-rack/react`.
28
+ */
29
+ export declare function panelPage(path: string, props: PanelPageProps): {
30
+ [x: symbol]: true;
31
+ path: string;
32
+ props: PanelPageProps;
33
+ };
@@ -0,0 +1,22 @@
1
+ import type { Rack } from "./types";
2
+ /**
3
+ * Normalized list query produced by {@link parseListQuery}.
4
+ */
5
+ export type ParsedListQuery = ReturnType<typeof parseListQuery>;
6
+ /**
7
+ * Parse raw URL/body input into a normalized list query,
8
+ * honoring `CrudQueryOptions` whitelists and pagination.
9
+ */
10
+ export declare function parseListQuery(raw: Record<string, unknown>, queryOptions?: Rack.CrudQueryOptions): {
11
+ search: {
12
+ value: string;
13
+ fields: readonly string[];
14
+ } | undefined;
15
+ filters: Record<string, unknown>;
16
+ sort: {
17
+ field: string;
18
+ direction: import("./types/query").CrudSortDirection;
19
+ } | undefined;
20
+ page: number;
21
+ limit: number;
22
+ };
@@ -0,0 +1,20 @@
1
+ import type { Rack } from "./types";
2
+ export interface RackRegistration {
3
+ /** Mount path, e.g. `/catalog/products`. */
4
+ path: string;
5
+ /** Resolved metadata (`id` defaults to path). */
6
+ metadata: Rack.CrudMetadata & {
7
+ id: string;
8
+ };
9
+ /** Resolved operations (all default `true`). */
10
+ operations: Required<Rack.CrudOperationsOptions>;
11
+ }
12
+ /**
13
+ * Register a rack resource. Called automatically by `rack()`.
14
+ * Re-registering the same path replaces the previous entry.
15
+ */
16
+ export declare function registerRack(reg: RackRegistration): void;
17
+ /** List all registered rack resources in registration order. */
18
+ export declare function listRacks(): RackRegistration[];
19
+ /** Clear the registry (mainly for tests). */
20
+ export declare function clearRacks(): void;
@@ -0,0 +1,16 @@
1
+ export type CrudOperation = "list" | "detail" | "create" | "replace" | "update" | "delete";
2
+ export type CrudAuthorizationRule = boolean | string | ((context: CrudAuthorizationContext) => boolean | Promise<boolean>);
3
+ export interface CrudAuthorizationContext {
4
+ operation: CrudOperation;
5
+ request: Request;
6
+ id?: string;
7
+ resource: unknown;
8
+ }
9
+ export interface CrudAuthorizationOptions {
10
+ list?: CrudAuthorizationRule;
11
+ detail?: CrudAuthorizationRule;
12
+ create?: CrudAuthorizationRule;
13
+ replace?: CrudAuthorizationRule;
14
+ update?: CrudAuthorizationRule;
15
+ delete?: CrudAuthorizationRule;
16
+ }
@@ -0,0 +1,46 @@
1
+ export type Awaitable<T> = T | Promise<T>;
2
+ export interface CrudIdempotencyRecord {
3
+ status: number;
4
+ body: unknown;
5
+ }
6
+ /**
7
+ * Pluggable idempotency store.
8
+ *
9
+ * Keys are already scoped per resource (`<resource>::<key>`).
10
+ * Implementations must honor `ttlSeconds` when provided.
11
+ */
12
+ export interface CrudIdempotencyStore {
13
+ get(key: string): Awaitable<CrudIdempotencyRecord | undefined>;
14
+ set(key: string, record: CrudIdempotencyRecord, ttlSeconds?: number): Awaitable<void>;
15
+ }
16
+ export interface CrudIdempotencyOptions {
17
+ /**
18
+ * Enable idempotency for POST create.
19
+ *
20
+ * Default: true.
21
+ */
22
+ enabled?: boolean;
23
+ /**
24
+ * Reject POST without an idempotency key.
25
+ *
26
+ * Default: true (idempotency key is mandatory for POST).
27
+ */
28
+ required?: boolean;
29
+ /**
30
+ * Header carrying the client-generated key.
31
+ *
32
+ * Default: 'Idempotency-Key'.
33
+ */
34
+ header?: string;
35
+ /**
36
+ * How long a stored response is replayed, in seconds.
37
+ *
38
+ * Default: 86400 (24h).
39
+ */
40
+ ttl?: number;
41
+ /**
42
+ * Custom store. Defaults to an in-memory store (per rack instance).
43
+ * Use a shared store (e.g. Redis) when running multiple instances.
44
+ */
45
+ store?: CrudIdempotencyStore;
46
+ }
@@ -0,0 +1,89 @@
1
+ import type { CrudAuthorizationContext as _CrudAuthorizationContext, CrudAuthorizationOptions as _CrudAuthorizationOptions, CrudAuthorizationRule as _CrudAuthorizationRule, CrudOperation as _CrudOperation } from "./authorization";
2
+ import type { CrudMetadata as _CrudMetadata } from "./metadata";
3
+ import type { CrudOpenAPIOperation as _CrudOpenAPIOperation, CrudOpenAPIOptions as _CrudOpenAPIOptions } from "./openapi";
4
+ import type { CrudOperationsOptions as _CrudOperationsOptions } from "./operations";
5
+ import type { CrudPageOptions as _CrudPageOptions, CrudSettings as _CrudSettings } from "./page";
6
+ import type { CrudPaginationOptions as _CrudPaginationOptions, CrudQueryOptions as _CrudQueryOptions, CrudSortDirection as _CrudSortDirection } from "./query";
7
+ import type { CrudValidationOptions as _CrudValidationOptions } from "./validation";
8
+ import type { Awaitable as _Awaitable, CrudIdempotencyOptions as _CrudIdempotencyOptions, CrudIdempotencyRecord as _CrudIdempotencyRecord, CrudIdempotencyStore as _CrudIdempotencyStore } from "./idempotency";
9
+ import type { CrudDrizzleModel as _CrudDrizzleModel, CrudModel as _CrudModel, CrudPrismaModel as _CrudPrismaModel } from "../models";
10
+ export declare namespace Rack {
11
+ type CrudDrizzleModel = _CrudDrizzleModel;
12
+ type CrudPrismaModel = _CrudPrismaModel;
13
+ type CrudModel = _CrudModel;
14
+ type CrudOperation = _CrudOperation;
15
+ type CrudAuthorizationRule = _CrudAuthorizationRule;
16
+ type CrudAuthorizationContext = _CrudAuthorizationContext;
17
+ type CrudAuthorizationOptions = _CrudAuthorizationOptions;
18
+ type CrudSortDirection = _CrudSortDirection;
19
+ type CrudQueryOptions = _CrudQueryOptions;
20
+ type CrudPaginationOptions = _CrudPaginationOptions;
21
+ type CrudOperationsOptions = _CrudOperationsOptions;
22
+ type CrudMetadata = _CrudMetadata;
23
+ type CrudOpenAPIOptions = _CrudOpenAPIOptions;
24
+ type CrudOpenAPIOperation = _CrudOpenAPIOperation;
25
+ type CrudValidationOptions = _CrudValidationOptions;
26
+ type CrudPageOptions = _CrudPageOptions;
27
+ type CrudSettings = _CrudSettings;
28
+ type Awaitable<T> = _Awaitable<T>;
29
+ type CrudIdempotencyRecord = _CrudIdempotencyRecord;
30
+ type CrudIdempotencyStore = _CrudIdempotencyStore;
31
+ type CrudIdempotencyOptions = _CrudIdempotencyOptions;
32
+ interface CrudOptions<TModel extends CrudModel = CrudModel> {
33
+ /**
34
+ * ORM model/resource configuration.
35
+ *
36
+ * Example:
37
+ * model: {
38
+ * drizzle: { db, table: products }
39
+ * }
40
+ */
41
+ model: TModel;
42
+ /**
43
+ * Validation for CRUD operations.
44
+ */
45
+ validation?: CrudValidationOptions;
46
+ /**
47
+ * Authorization rules for each operation.
48
+ */
49
+ authorization?: CrudAuthorizationOptions;
50
+ /**
51
+ * Configure list/query capabilities.
52
+ */
53
+ query?: CrudQueryOptions;
54
+ /**
55
+ * Enable, disable, or configure CRUD operations.
56
+ */
57
+ operations?: CrudOperationsOptions;
58
+ /**
59
+ * Metadata used by Panel, navigation, breadcrumbs,
60
+ * registry, and generated UI.
61
+ */
62
+ metadata?: CrudMetadata;
63
+ /**
64
+ * OpenAPI metadata and operation overrides.
65
+ */
66
+ openapi?: CrudOpenAPIOptions;
67
+ /**
68
+ * Optional React page configuration.
69
+ */
70
+ page?: CrudPageOptions;
71
+ /**
72
+ * Arbitrary framework/application settings.
73
+ *
74
+ * Avoid using this for core CRUD behavior.
75
+ */
76
+ settings?: CrudSettings;
77
+ /**
78
+ * Idempotency for POST create.
79
+ *
80
+ * The idempotency key header is required by default.
81
+ */
82
+ idempotency?: CrudIdempotencyOptions;
83
+ }
84
+ /**
85
+ * Historical alias for the `rack(path, options)` signature.
86
+ * Same as `CrudOptions`.
87
+ */
88
+ type RackOptions<TModel extends CrudModel = CrudModel> = CrudOptions<TModel>;
89
+ }
@@ -0,0 +1,36 @@
1
+ export interface CrudMetadata {
2
+ /**
3
+ * Internal unique resource ID.
4
+ */
5
+ id?: string;
6
+ /**
7
+ * Human-readable singular label.
8
+ */
9
+ label?: string;
10
+ /**
11
+ * Human-readable plural label.
12
+ */
13
+ pluralLabel?: string;
14
+ /**
15
+ * Navigation group.
16
+ *
17
+ * Example: 'Catalog'
18
+ */
19
+ group?: string;
20
+ /**
21
+ * Parent resource/navigation ID.
22
+ */
23
+ parent?: string;
24
+ /**
25
+ * Icon identifier.
26
+ */
27
+ icon?: string;
28
+ /**
29
+ * Sidebar order.
30
+ */
31
+ order?: number;
32
+ /**
33
+ * Hide from generated sidebar.
34
+ */
35
+ hidden?: boolean;
36
+ }
@@ -0,0 +1,22 @@
1
+ import type { CrudOperation } from "./authorization";
2
+ export interface CrudOpenAPIOptions {
3
+ /**
4
+ * Common metadata applied to all generated operations.
5
+ */
6
+ tags?: readonly string[];
7
+ /**
8
+ * Resource-level description.
9
+ */
10
+ description?: string;
11
+ /**
12
+ * Operation-specific OpenAPI metadata.
13
+ */
14
+ operations?: Partial<Record<CrudOperation, CrudOpenAPIOperation>>;
15
+ }
16
+ export interface CrudOpenAPIOperation {
17
+ summary?: string;
18
+ description?: string;
19
+ deprecated?: boolean;
20
+ tags?: readonly string[];
21
+ operationId?: string;
22
+ }
@@ -0,0 +1,14 @@
1
+ export interface CrudOperationsOptions {
2
+ list?: boolean;
3
+ detail?: boolean;
4
+ create?: boolean;
5
+ /**
6
+ * PUT
7
+ */
8
+ replace?: boolean;
9
+ /**
10
+ * PATCH
11
+ */
12
+ update?: boolean;
13
+ delete?: boolean;
14
+ }
@@ -0,0 +1,41 @@
1
+ export interface CrudPageOptions {
2
+ /**
3
+ * Custom page path handled by react().
4
+ *
5
+ * Example: '/products'
6
+ */
7
+ path?: string;
8
+ /**
9
+ * Disable generated HTML page.
10
+ */
11
+ enabled?: boolean;
12
+ /**
13
+ * Custom page for this resource.
14
+ */
15
+ component?: string;
16
+ }
17
+ export interface CrudSettings {
18
+ /**
19
+ * Primary key field.
20
+ *
21
+ * Default should be resolved by the ORM adapter.
22
+ */
23
+ primaryKey?: string;
24
+ /**
25
+ * Soft delete support.
26
+ *
27
+ * When enabled, deletes set a timestamp column instead of
28
+ * removing the row, and reads skip soft-deleted rows.
29
+ */
30
+ softDelete?: boolean;
31
+ /**
32
+ * Column used for soft deletes.
33
+ *
34
+ * Default: 'deletedAt'.
35
+ */
36
+ deletedAtField?: string;
37
+ /**
38
+ * Return representation after mutation.
39
+ */
40
+ returning?: boolean;
41
+ }
@@ -0,0 +1,30 @@
1
+ export type CrudSortDirection = "asc" | "desc";
2
+ export interface CrudQueryOptions {
3
+ /**
4
+ * Fields allowed for full-text/search query.
5
+ */
6
+ searchable?: readonly string[];
7
+ /**
8
+ * Fields allowed for filtering.
9
+ */
10
+ filterable?: readonly string[];
11
+ /**
12
+ * Fields allowed for sorting.
13
+ */
14
+ sortable?: readonly string[];
15
+ /**
16
+ * Default sort.
17
+ */
18
+ defaultSort?: {
19
+ field: string;
20
+ direction?: CrudSortDirection;
21
+ };
22
+ /**
23
+ * Pagination configuration.
24
+ */
25
+ pagination?: CrudPaginationOptions;
26
+ }
27
+ export interface CrudPaginationOptions {
28
+ default?: number;
29
+ max?: number;
30
+ }
@@ -0,0 +1,19 @@
1
+ export interface CrudValidationOptions {
2
+ create?: unknown;
3
+ /**
4
+ * PUT: full replacement.
5
+ */
6
+ replace?: unknown;
7
+ /**
8
+ * PATCH: partial update.
9
+ */
10
+ update?: unknown;
11
+ /**
12
+ * Validate resource ID.
13
+ */
14
+ params?: unknown;
15
+ /**
16
+ * Validate QUERY body/query definition.
17
+ */
18
+ query?: unknown;
19
+ }
@@ -0,0 +1,64 @@
1
+ import Elysia from "elysia";
2
+ import { ReactRack } from "./types";
3
+ export declare function page<Props = Record<string, never>>(path: string, props?: Props): ReactRack.PageDescriptor<Props>;
4
+ export declare const pages: ReactRack.PageRegistry;
5
+ export declare function reactPlugin(options: ReactRack.ReactPluginOptions): Elysia<"", {
6
+ decorator: {
7
+ react: {
8
+ render: (path: string, props: unknown) => Promise<Response>;
9
+ };
10
+ };
11
+ store: {};
12
+ derive: {};
13
+ resolve: {};
14
+ }, {
15
+ typebox: {};
16
+ error: {};
17
+ }, {
18
+ schema: {};
19
+ standaloneSchema: {};
20
+ macro: {};
21
+ macroFn: {};
22
+ parser: {};
23
+ response: {};
24
+ }, {
25
+ __rack: {
26
+ ":name": {
27
+ get: {
28
+ body: unknown;
29
+ params: {
30
+ name: string;
31
+ } & {};
32
+ query: unknown;
33
+ headers: unknown;
34
+ response: {
35
+ 200: Response;
36
+ 404: "Panel asset not built. Run `bun run build:assets`." | "Panel asset not found";
37
+ 422: {
38
+ type: "validation";
39
+ on: string;
40
+ summary?: string;
41
+ message?: string;
42
+ found?: unknown;
43
+ property?: string;
44
+ expected?: string;
45
+ };
46
+ };
47
+ };
48
+ };
49
+ };
50
+ }, {
51
+ derive: {};
52
+ resolve: {};
53
+ schema: {};
54
+ standaloneSchema: {};
55
+ response: {
56
+ 200: Response;
57
+ };
58
+ }, {
59
+ derive: {};
60
+ resolve: {};
61
+ schema: {};
62
+ standaloneSchema: {};
63
+ response: {};
64
+ }>;
@@ -0,0 +1,126 @@
1
+ import {
2
+ __require
3
+ } from "../index-37x76zdn.js";
4
+
5
+ // src/react/index.ts
6
+ import Elysia from "elysia";
7
+ import { existsSync } from "node:fs";
8
+ import { readFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ // src/react/render.tsx
13
+ import { renderToReadableStream } from "react-dom/server";
14
+ import { jsxDEV } from "react/jsx-dev-runtime";
15
+ function pageTitle(path, props) {
16
+ const metadata = props?.metadata;
17
+ return metadata?.pluralLabel ?? metadata?.label ?? path;
18
+ }
19
+ function createRenderer(pages) {
20
+ return async function render(path, props) {
21
+ const loader = pages[path];
22
+ if (!loader) {
23
+ return new Response("Page Not Found", {
24
+ status: 404
25
+ });
26
+ }
27
+ const module = await loader();
28
+ const Component = module.default;
29
+ const stream = await renderToReadableStream(/* @__PURE__ */ jsxDEV(Component, {
30
+ ...props
31
+ }, undefined, false, undefined, this));
32
+ const head = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${pageTitle(path, props)}</title><link rel="stylesheet" href="/__rack/panel.css"><script type="module" src="/__rack/panel-theme.js"></script></head><body>`;
33
+ const encoder = new TextEncoder;
34
+ const combined = new ReadableStream({
35
+ async start(controller) {
36
+ controller.enqueue(encoder.encode(head));
37
+ const reader = stream.getReader();
38
+ try {
39
+ for (;; ) {
40
+ const { done, value } = await reader.read();
41
+ if (done)
42
+ break;
43
+ controller.enqueue(value);
44
+ }
45
+ } finally {
46
+ reader.releaseLock();
47
+ }
48
+ controller.enqueue(encoder.encode("</body></html>"));
49
+ controller.close();
50
+ }
51
+ });
52
+ return new Response(combined, {
53
+ headers: {
54
+ "Content-Type": "text/html; charset=utf-8"
55
+ }
56
+ });
57
+ };
58
+ }
59
+
60
+ // src/react/types.ts
61
+ var ReactRack;
62
+ ((ReactRack) => {
63
+ ReactRack.PAGE = Symbol.for("@elysia-panel/react");
64
+ })(ReactRack ||= {});
65
+
66
+ // src/react/index.ts
67
+ var PANEL_ASSETS = {
68
+ "panel.css": "text/css; charset=utf-8",
69
+ "panel-theme.js": "text/javascript; charset=utf-8",
70
+ "panel-app.js": "text/javascript; charset=utf-8"
71
+ };
72
+ function resolveDistDir() {
73
+ const candidates = [
74
+ join(process.cwd(), "dist"),
75
+ join(fileURLToPath(new URL(".", import.meta.url)), "..")
76
+ ];
77
+ for (const dir of candidates) {
78
+ try {
79
+ if (existsSync(join(dir, "panel-app.js")))
80
+ return dir;
81
+ } catch {}
82
+ }
83
+ return null;
84
+ }
85
+ function page(path, props) {
86
+ return {
87
+ [ReactRack.PAGE]: true,
88
+ path,
89
+ props
90
+ };
91
+ }
92
+ var pages = {
93
+ "/dashboard": () => import("../Dashboard-xpstr7fr.js"),
94
+ "/panel": () => import("../Panel-fbp3wrsw.js")
95
+ };
96
+ function reactPlugin(options) {
97
+ const render = createRenderer(options.pages);
98
+ return new Elysia({ name: "@elysia-rack/react" }).decorate("react", { render }).get("/__rack/:name", async ({ params, status }) => {
99
+ const type = PANEL_ASSETS[params.name];
100
+ const dir = resolveDistDir();
101
+ if (!type || !dir)
102
+ return status(404, "Panel asset not built. Run `bun run build:assets`.");
103
+ try {
104
+ const data = await readFile(join(dir, params.name));
105
+ return new Response(data, {
106
+ headers: {
107
+ "Content-Type": type,
108
+ "Cache-Control": "public, max-age=3600"
109
+ }
110
+ });
111
+ } catch {
112
+ return status(404, "Panel asset not found");
113
+ }
114
+ }).onAfterHandle({ as: "scoped" }, async ({ responseValue }) => {
115
+ if (typeof responseValue !== "object" || responseValue === null || !(ReactRack.PAGE in responseValue)) {
116
+ return;
117
+ }
118
+ const descriptor = responseValue;
119
+ return render(descriptor.path, descriptor.props);
120
+ });
121
+ }
122
+ export {
123
+ reactPlugin,
124
+ pages,
125
+ page
126
+ };
@@ -0,0 +1,7 @@
1
+ import { type RackRegistration } from "../../rack/registry";
2
+ export interface DashboardProps {
3
+ name?: string;
4
+ resource?: string;
5
+ racks?: RackRegistration[];
6
+ }
7
+ export default function Dashboard(props: DashboardProps): Promise<import("react").JSX.Element>;
@@ -0,0 +1,36 @@
1
+ import type { FieldDescriptor } from "../../rack/adapters/index";
2
+ export interface PanelProps {
3
+ resource?: string;
4
+ metadata?: {
5
+ label?: string;
6
+ pluralLabel?: string;
7
+ group?: string;
8
+ };
9
+ operations?: {
10
+ list?: boolean;
11
+ detail?: boolean;
12
+ create?: boolean;
13
+ replace?: boolean;
14
+ update?: boolean;
15
+ delete?: boolean;
16
+ };
17
+ primaryKey?: string;
18
+ query?: {
19
+ searchable?: readonly string[];
20
+ filterable?: readonly string[];
21
+ sortable?: readonly string[];
22
+ pagination?: {
23
+ default?: number;
24
+ max?: number;
25
+ };
26
+ };
27
+ params?: Record<string, unknown>;
28
+ queryUrl?: string;
29
+ fields?: FieldDescriptor[];
30
+ deletedAtField?: string;
31
+ load?: (input: Record<string, unknown>) => Promise<{
32
+ data: unknown[];
33
+ total: number;
34
+ }>;
35
+ }
36
+ export default function Panel(props: PanelProps): Promise<import("react").JSX.Element>;
@@ -0,0 +1 @@
1
+ export {};