appflare 0.2.25 → 0.2.27

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 (140) hide show
  1. package/Documentation.md +758 -758
  2. package/cli/commands/index.ts +238 -238
  3. package/cli/generate.ts +178 -178
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1183 -1183
  7. package/cli/templates/auth/README.md +156 -156
  8. package/cli/templates/auth/config.ts +61 -61
  9. package/cli/templates/auth/route-config.ts +1 -1
  10. package/cli/templates/auth/route-handler.ts +1 -1
  11. package/cli/templates/auth/route-request-utils.ts +5 -5
  12. package/cli/templates/auth/route.config.ts +18 -18
  13. package/cli/templates/auth/route.handler.ts +18 -18
  14. package/cli/templates/auth/route.request-utils.ts +55 -55
  15. package/cli/templates/auth/route.ts +14 -14
  16. package/cli/templates/core/README.md +266 -266
  17. package/cli/templates/core/app-creation.ts +19 -19
  18. package/cli/templates/core/client/appflare.ts +112 -112
  19. package/cli/templates/core/client/handlers/index.ts +748 -748
  20. package/cli/templates/core/client/handlers.ts +1 -1
  21. package/cli/templates/core/client/index.ts +7 -7
  22. package/cli/templates/core/client/storage.ts +205 -180
  23. package/cli/templates/core/client/types.ts +186 -184
  24. package/cli/templates/core/client-modules/appflare.ts +1 -1
  25. package/cli/templates/core/client-modules/handlers.ts +1 -1
  26. package/cli/templates/core/client-modules/index.ts +1 -1
  27. package/cli/templates/core/client-modules/storage.ts +1 -1
  28. package/cli/templates/core/client-modules/types.ts +1 -1
  29. package/cli/templates/core/client.artifacts.ts +39 -39
  30. package/cli/templates/core/client.ts +4 -4
  31. package/cli/templates/core/drizzle.ts +15 -15
  32. package/cli/templates/core/export.ts +14 -14
  33. package/cli/templates/core/handlers.route.ts +24 -24
  34. package/cli/templates/core/handlers.ts +1 -1
  35. package/cli/templates/core/imports.ts +9 -9
  36. package/cli/templates/core/server.ts +38 -38
  37. package/cli/templates/core/types.ts +6 -6
  38. package/cli/templates/core/wrangler.ts +109 -109
  39. package/cli/templates/dashboard/builders/functions/index.ts +17 -17
  40. package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -20
  41. package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -33
  42. package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +171 -171
  43. package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
  44. package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +554 -554
  45. package/cli/templates/dashboard/builders/navigation.ts +122 -122
  46. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  47. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  48. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  49. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  50. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  51. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  52. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  53. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  55. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  56. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  57. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  58. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  59. package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
  60. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  61. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  62. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  63. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  64. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  65. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  66. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  67. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  68. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  69. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  70. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  71. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  72. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  73. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  74. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  75. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  76. package/cli/templates/dashboard/components/layout.ts +388 -388
  77. package/cli/templates/dashboard/components/login-page.ts +65 -65
  78. package/cli/templates/dashboard/index.ts +61 -61
  79. package/cli/templates/dashboard/types.ts +9 -9
  80. package/cli/templates/handlers/README.md +353 -353
  81. package/cli/templates/handlers/auth.ts +37 -37
  82. package/cli/templates/handlers/execution.ts +42 -42
  83. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  84. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  85. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  86. package/cli/templates/handlers/generators/context/storage-api.ts +82 -112
  87. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  88. package/cli/templates/handlers/generators/context/types.ts +18 -18
  89. package/cli/templates/handlers/generators/context.ts +43 -43
  90. package/cli/templates/handlers/generators/execution.ts +15 -15
  91. package/cli/templates/handlers/generators/handlers.ts +13 -13
  92. package/cli/templates/handlers/generators/registration/modules/cron.ts +26 -26
  93. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  94. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  95. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  96. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  97. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  98. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  99. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +516 -516
  100. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +56 -56
  101. package/cli/templates/handlers/generators/registration/modules/storage.ts +192 -194
  102. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  103. package/cli/templates/handlers/generators/types/context.ts +67 -66
  104. package/cli/templates/handlers/generators/types/core.ts +106 -106
  105. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  106. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +259 -259
  107. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  108. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1031 -1031
  109. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +246 -246
  110. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  111. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +121 -121
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +676 -676
  116. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  117. package/cli/templates/handlers/index.ts +43 -43
  118. package/cli/templates/handlers/operations.ts +116 -116
  119. package/cli/templates/handlers/registration.ts +91 -91
  120. package/cli/templates/handlers/types.ts +15 -15
  121. package/cli/templates/handlers/utils.ts +48 -48
  122. package/cli/types.ts +110 -110
  123. package/cli/utils/handler-discovery.ts +466 -466
  124. package/cli/utils/json-utils.ts +24 -24
  125. package/cli/utils/path-utils.ts +19 -19
  126. package/cli/utils/schema-discovery.ts +399 -399
  127. package/dist/cli/index.js +95 -99
  128. package/dist/cli/index.mjs +95 -99
  129. package/index.ts +18 -18
  130. package/package.json +58 -58
  131. package/react/index.ts +5 -5
  132. package/react/use-infinite-query.ts +252 -252
  133. package/react/use-mutation.ts +89 -89
  134. package/react/use-query.ts +207 -207
  135. package/schema.ts +415 -415
  136. package/test-better-auth-hash.ts +2 -2
  137. package/tsconfig.json +6 -6
  138. package/tsup.config.ts +82 -82
  139. package/dist/cli/index.d.mts +0 -2
  140. package/dist/cli/index.d.ts +0 -2
@@ -1,80 +1,80 @@
1
- import { DiscoveredTable } from "../../../types";
2
- import {
3
- buildColumnHeaders,
4
- buildFieldInput,
5
- buildPayloadAssignments,
6
- buildRowCells,
7
- buildSearchConditions,
8
- } from "../fragments";
9
- import {
10
- resolvePrimaryKey,
11
- shouldIncludeCreateField,
12
- shouldIncludeEditField,
13
- } from "../helpers";
14
- import { buildActionsCell } from "./actions-cell";
15
- import { buildTableGetRoute } from "./get-route";
16
- import { buildTablePostRoutes } from "./post-routes";
17
-
18
- /**
19
- * Builds the complete set of Hono route handlers for a discovered table.
20
- * Delegates to focused builder functions for GET and POST handlers.
21
- */
22
- export function buildTableRoute(table: DiscoveredTable): string {
23
- const primaryKey = resolvePrimaryKey(table);
24
- const hasPrimaryKey = Boolean(primaryKey);
25
- const columns = table.columns.map((column) => column.name);
26
- const createColumns = columns.filter((columnName) =>
27
- shouldIncludeCreateField(table, columnName),
28
- );
29
- const editColumns = columns.filter((columnName) =>
30
- shouldIncludeEditField(table, columnName),
31
- );
32
- const searchConditions = buildSearchConditions(table);
33
- const headers = buildColumnHeaders(table, columns);
34
- const rowCells = buildRowCells(columns, primaryKey);
35
- const createInputs = createColumns
36
- .map((columnName) => buildFieldInput(table, columnName, "create"))
37
- .join("");
38
- const editInputs = editColumns
39
- .map((columnName) => buildFieldInput(table, columnName, "edit"))
40
- .join("");
41
- const createAssignments = buildPayloadAssignments(table, createColumns);
42
- const editAssignments = buildPayloadAssignments(table, editColumns);
43
- const defaultSort = hasPrimaryKey ? primaryKey : columns[0] || "id";
44
- const primaryKeyType = table.columns.find(
45
- (column) => column.name === primaryKey,
46
- )?.type;
47
-
48
- const actionsCell = buildActionsCell(
49
- table,
50
- hasPrimaryKey,
51
- primaryKey,
52
- editInputs,
53
- );
54
-
55
- return (
56
- buildTableGetRoute(
57
- table,
58
- defaultSort,
59
- primaryKey,
60
- hasPrimaryKey,
61
- columns,
62
- searchConditions,
63
- headers,
64
- rowCells,
65
- actionsCell,
66
- createInputs,
67
- ) +
68
- "\n" +
69
- buildTablePostRoutes(
70
- table.exportName,
71
- defaultSort,
72
- primaryKey,
73
- primaryKeyType,
74
- hasPrimaryKey,
75
- searchConditions,
76
- createAssignments,
77
- editAssignments,
78
- )
79
- );
80
- }
1
+ import { DiscoveredTable } from "../../../types";
2
+ import {
3
+ buildColumnHeaders,
4
+ buildFieldInput,
5
+ buildPayloadAssignments,
6
+ buildRowCells,
7
+ buildSearchConditions,
8
+ } from "../fragments";
9
+ import {
10
+ resolvePrimaryKey,
11
+ shouldIncludeCreateField,
12
+ shouldIncludeEditField,
13
+ } from "../helpers";
14
+ import { buildActionsCell } from "./actions-cell";
15
+ import { buildTableGetRoute } from "./get-route";
16
+ import { buildTablePostRoutes } from "./post-routes";
17
+
18
+ /**
19
+ * Builds the complete set of Hono route handlers for a discovered table.
20
+ * Delegates to focused builder functions for GET and POST handlers.
21
+ */
22
+ export function buildTableRoute(table: DiscoveredTable): string {
23
+ const primaryKey = resolvePrimaryKey(table);
24
+ const hasPrimaryKey = Boolean(primaryKey);
25
+ const columns = table.columns.map((column) => column.name);
26
+ const createColumns = columns.filter((columnName) =>
27
+ shouldIncludeCreateField(table, columnName),
28
+ );
29
+ const editColumns = columns.filter((columnName) =>
30
+ shouldIncludeEditField(table, columnName),
31
+ );
32
+ const searchConditions = buildSearchConditions(table);
33
+ const headers = buildColumnHeaders(table, columns);
34
+ const rowCells = buildRowCells(columns, primaryKey);
35
+ const createInputs = createColumns
36
+ .map((columnName) => buildFieldInput(table, columnName, "create"))
37
+ .join("");
38
+ const editInputs = editColumns
39
+ .map((columnName) => buildFieldInput(table, columnName, "edit"))
40
+ .join("");
41
+ const createAssignments = buildPayloadAssignments(table, createColumns);
42
+ const editAssignments = buildPayloadAssignments(table, editColumns);
43
+ const defaultSort = hasPrimaryKey ? primaryKey : columns[0] || "id";
44
+ const primaryKeyType = table.columns.find(
45
+ (column) => column.name === primaryKey,
46
+ )?.type;
47
+
48
+ const actionsCell = buildActionsCell(
49
+ table,
50
+ hasPrimaryKey,
51
+ primaryKey,
52
+ editInputs,
53
+ );
54
+
55
+ return (
56
+ buildTableGetRoute(
57
+ table,
58
+ defaultSort,
59
+ primaryKey,
60
+ hasPrimaryKey,
61
+ columns,
62
+ searchConditions,
63
+ headers,
64
+ rowCells,
65
+ actionsCell,
66
+ createInputs,
67
+ ) +
68
+ "\n" +
69
+ buildTablePostRoutes(
70
+ table.exportName,
71
+ defaultSort,
72
+ primaryKey,
73
+ primaryKeyType,
74
+ hasPrimaryKey,
75
+ searchConditions,
76
+ createAssignments,
77
+ editAssignments,
78
+ )
79
+ );
80
+ }
@@ -1,163 +1,163 @@
1
- /**
2
- * Builds the POST route handlers code string for create/edit/delete operations
3
- * on a generic table (/admin/table/:tableName/create|edit|delete).
4
- */
5
- export function buildTablePostRoutes(
6
- exportName: string,
7
- defaultSort: string,
8
- primaryKey: string,
9
- primaryKeyType: string | undefined,
10
- hasPrimaryKey: boolean,
11
- searchConditions: string,
12
- createAssignments: string,
13
- editAssignments: string,
14
- ): string {
15
- const numericIdGuard =
16
- primaryKeyType === "number"
17
- ? `
18
- \t\tconst parsedId = Number(rawId);
19
- \t\tif (Number.isNaN(parsedId)) return c.text('${primaryKey} must be a valid number', 400);
20
- \t\tidValue = parsedId;
21
- \t\t`
22
- : "";
23
-
24
- const parseBulkIds =
25
- primaryKeyType === "number"
26
- ? "idValues.map((value) => Number(value)).filter((value) => !Number.isNaN(value))"
27
- : "idValues";
28
-
29
- const editRoute = hasPrimaryKey
30
- ? `
31
- \tadminApp.post('/table/${exportName}/edit', async (c) => {
32
- \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
33
- \t\tconst tableSchema = (schema as any).${exportName};
34
- \t\tif (!tableSchema) return c.text('Table missing', 404);
35
-
36
- \t\tconst body = await c.req.parseBody();
37
- \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
38
- \t\tconst rawId = getValue(body['${primaryKey}']);
39
- \t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
40
-
41
- \t\tconst payload: Record<string, unknown> = {};
42
-
43
- \t\t${editAssignments}
44
-
45
- \t\tlet idValue: unknown = rawId;
46
- \t\t${numericIdGuard}
47
-
48
- \t\tif (Object.keys(payload).length > 0) {
49
- \t\t\tawait db
50
- \t\t\t\t.update(tableSchema)
51
- \t\t\t\t.set(payload as any)
52
- \t\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
53
- \t\t\t\t.execute();
54
- \t\t}
55
-
56
- \t\tconst query = new URLSearchParams({
57
- \t\t\tpage: getValue(body.page) || '1',
58
- \t\t\tsort: getValue(body.sort) || '${defaultSort}',
59
- \t\t\torder: getValue(body.order) || 'desc',
60
- \t\t\tsearch: getValue(body.search) || '',
61
- \t\t});
62
- \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
63
- \t});
64
-
65
- \tadminApp.post('/table/${exportName}/delete', async (c) => {
66
- \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
67
- \t\tconst tableSchema = (schema as any).${exportName};
68
- \t\tif (!tableSchema) return c.text('Table missing', 404);
69
-
70
- \t\tconst body = await c.req.parseBody();
71
- \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
72
- \t\tconst rawId = getValue(body['${primaryKey}']);
73
- \t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
74
-
75
- \t\tlet idValue: unknown = rawId;
76
- \t\t${numericIdGuard}
77
-
78
- \t\tawait db
79
- \t\t\t.delete(tableSchema)
80
- \t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
81
- \t\t\t.execute();
82
-
83
- \t\tconst query = new URLSearchParams({
84
- \t\t\tpage: getValue(body.page) || '1',
85
- \t\t\tsort: getValue(body.sort) || '${defaultSort}',
86
- \t\t\torder: getValue(body.order) || 'desc',
87
- \t\t\tsearch: getValue(body.search) || '',
88
- \t\t});
89
- \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
90
- \t});
91
-
92
- \tadminApp.post('/table/${exportName}/delete-bulk', async (c) => {
93
- \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
94
- \t\tconst tableSchema = (schema as any).${exportName};
95
- \t\tif (!tableSchema) return c.text('Table missing', 404);
96
-
97
- \t\tconst body = await c.req.parseBody();
98
- \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
99
- \t\tconst mode = getValue(body.bulkMode);
100
- \t\tconst selectedIdsRaw = getValue(body.selectedIds);
101
- \t\tconst search = getValue(body.search);
102
-
103
- \t\tif (mode === 'all-matching') {
104
- \t\t\tlet deleteQuery = db.delete(tableSchema);
105
- \t\t\tif (search) {
106
- const searchConditions: any[] = [];
107
- \t\t\t\t${searchConditions}
108
- \t\t\t\tif (searchConditions.length > 0) {
109
- \t\t\t\t\tdeleteQuery = deleteQuery.where(or(...searchConditions)) as any;
110
- \t\t\t\t}
111
- \t\t\t}
112
- \t\t\tawait deleteQuery.execute();
113
- \t\t} else {
114
- \t\t\tconst idValues = selectedIdsRaw
115
- \t\t\t\t.split(',')
116
- \t\t\t\t.map((value) => value.trim())
117
- \t\t\t\t.filter((value) => value.length > 0);
118
- \t\t\tif (idValues.length === 0) return c.text('No rows selected', 400);
119
-
120
- \t\t\tconst parsedIds: unknown[] = ${parseBulkIds};
121
- \t\t\tif (parsedIds.length === 0) return c.text('No valid selected rows', 400);
122
-
123
- \t\t\tawait db
124
- \t\t\t\t.delete(tableSchema)
125
- \t\t\t\t.where(inArray(tableSchema.${primaryKey}, parsedIds as any))
126
- \t\t\t\t.execute();
127
- \t\t}
128
-
129
- \t\tconst query = new URLSearchParams({
130
- \t\t\tpage: getValue(body.page) || '1',
131
- \t\t\tsort: getValue(body.sort) || '${defaultSort}',
132
- \t\t\torder: getValue(body.order) || 'desc',
133
- \t\t\tsearch: getValue(body.search) || '',
134
- \t\t});
135
- \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
136
- \t});`
137
- : "";
138
-
139
- return `
140
- \tadminApp.post('/table/${exportName}/create', async (c) => {
141
- \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
142
- \t\tconst tableSchema = (schema as any).${exportName};
143
- \t\tif (!tableSchema) return c.text('Table missing', 404);
144
-
145
- \t\tconst body = await c.req.parseBody();
146
- \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
147
- \t\tconst payload: Record<string, unknown> = {};
148
-
149
- \t\t${createAssignments}
150
-
151
- \t\tawait db.insert(tableSchema).values(payload as any).execute();
152
-
153
- \t\tconst query = new URLSearchParams({
154
- \t\t\tpage: getValue(body.page) || '1',
155
- \t\t\tsort: getValue(body.sort) || '${defaultSort}',
156
- \t\t\torder: getValue(body.order) || 'desc',
157
- \t\t\tsearch: getValue(body.search) || '',
158
- \t\t});
159
- \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
160
- \t});
161
- ${editRoute}
162
- \t`;
163
- }
1
+ /**
2
+ * Builds the POST route handlers code string for create/edit/delete operations
3
+ * on a generic table (/admin/table/:tableName/create|edit|delete).
4
+ */
5
+ export function buildTablePostRoutes(
6
+ exportName: string,
7
+ defaultSort: string,
8
+ primaryKey: string,
9
+ primaryKeyType: string | undefined,
10
+ hasPrimaryKey: boolean,
11
+ searchConditions: string,
12
+ createAssignments: string,
13
+ editAssignments: string,
14
+ ): string {
15
+ const numericIdGuard =
16
+ primaryKeyType === "number"
17
+ ? `
18
+ \t\tconst parsedId = Number(rawId);
19
+ \t\tif (Number.isNaN(parsedId)) return c.text('${primaryKey} must be a valid number', 400);
20
+ \t\tidValue = parsedId;
21
+ \t\t`
22
+ : "";
23
+
24
+ const parseBulkIds =
25
+ primaryKeyType === "number"
26
+ ? "idValues.map((value) => Number(value)).filter((value) => !Number.isNaN(value))"
27
+ : "idValues";
28
+
29
+ const editRoute = hasPrimaryKey
30
+ ? `
31
+ \tadminApp.post('/table/${exportName}/edit', async (c) => {
32
+ \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
33
+ \t\tconst tableSchema = (schema as any).${exportName};
34
+ \t\tif (!tableSchema) return c.text('Table missing', 404);
35
+
36
+ \t\tconst body = await c.req.parseBody();
37
+ \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
38
+ \t\tconst rawId = getValue(body['${primaryKey}']);
39
+ \t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
40
+
41
+ \t\tconst payload: Record<string, unknown> = {};
42
+
43
+ \t\t${editAssignments}
44
+
45
+ \t\tlet idValue: unknown = rawId;
46
+ \t\t${numericIdGuard}
47
+
48
+ \t\tif (Object.keys(payload).length > 0) {
49
+ \t\t\tawait db
50
+ \t\t\t\t.update(tableSchema)
51
+ \t\t\t\t.set(payload as any)
52
+ \t\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
53
+ \t\t\t\t.execute();
54
+ \t\t}
55
+
56
+ \t\tconst query = new URLSearchParams({
57
+ \t\t\tpage: getValue(body.page) || '1',
58
+ \t\t\tsort: getValue(body.sort) || '${defaultSort}',
59
+ \t\t\torder: getValue(body.order) || 'desc',
60
+ \t\t\tsearch: getValue(body.search) || '',
61
+ \t\t});
62
+ \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
63
+ \t});
64
+
65
+ \tadminApp.post('/table/${exportName}/delete', async (c) => {
66
+ \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
67
+ \t\tconst tableSchema = (schema as any).${exportName};
68
+ \t\tif (!tableSchema) return c.text('Table missing', 404);
69
+
70
+ \t\tconst body = await c.req.parseBody();
71
+ \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
72
+ \t\tconst rawId = getValue(body['${primaryKey}']);
73
+ \t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
74
+
75
+ \t\tlet idValue: unknown = rawId;
76
+ \t\t${numericIdGuard}
77
+
78
+ \t\tawait db
79
+ \t\t\t.delete(tableSchema)
80
+ \t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
81
+ \t\t\t.execute();
82
+
83
+ \t\tconst query = new URLSearchParams({
84
+ \t\t\tpage: getValue(body.page) || '1',
85
+ \t\t\tsort: getValue(body.sort) || '${defaultSort}',
86
+ \t\t\torder: getValue(body.order) || 'desc',
87
+ \t\t\tsearch: getValue(body.search) || '',
88
+ \t\t});
89
+ \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
90
+ \t});
91
+
92
+ \tadminApp.post('/table/${exportName}/delete-bulk', async (c) => {
93
+ \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
94
+ \t\tconst tableSchema = (schema as any).${exportName};
95
+ \t\tif (!tableSchema) return c.text('Table missing', 404);
96
+
97
+ \t\tconst body = await c.req.parseBody();
98
+ \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
99
+ \t\tconst mode = getValue(body.bulkMode);
100
+ \t\tconst selectedIdsRaw = getValue(body.selectedIds);
101
+ \t\tconst search = getValue(body.search);
102
+
103
+ \t\tif (mode === 'all-matching') {
104
+ \t\t\tlet deleteQuery = db.delete(tableSchema);
105
+ \t\t\tif (search) {
106
+ const searchConditions: any[] = [];
107
+ \t\t\t\t${searchConditions}
108
+ \t\t\t\tif (searchConditions.length > 0) {
109
+ \t\t\t\t\tdeleteQuery = deleteQuery.where(or(...searchConditions)) as any;
110
+ \t\t\t\t}
111
+ \t\t\t}
112
+ \t\t\tawait deleteQuery.execute();
113
+ \t\t} else {
114
+ \t\t\tconst idValues = selectedIdsRaw
115
+ \t\t\t\t.split(',')
116
+ \t\t\t\t.map((value) => value.trim())
117
+ \t\t\t\t.filter((value) => value.length > 0);
118
+ \t\t\tif (idValues.length === 0) return c.text('No rows selected', 400);
119
+
120
+ \t\t\tconst parsedIds: unknown[] = ${parseBulkIds};
121
+ \t\t\tif (parsedIds.length === 0) return c.text('No valid selected rows', 400);
122
+
123
+ \t\t\tawait db
124
+ \t\t\t\t.delete(tableSchema)
125
+ \t\t\t\t.where(inArray(tableSchema.${primaryKey}, parsedIds as any))
126
+ \t\t\t\t.execute();
127
+ \t\t}
128
+
129
+ \t\tconst query = new URLSearchParams({
130
+ \t\t\tpage: getValue(body.page) || '1',
131
+ \t\t\tsort: getValue(body.sort) || '${defaultSort}',
132
+ \t\t\torder: getValue(body.order) || 'desc',
133
+ \t\t\tsearch: getValue(body.search) || '',
134
+ \t\t});
135
+ \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
136
+ \t});`
137
+ : "";
138
+
139
+ return `
140
+ \tadminApp.post('/table/${exportName}/create', async (c) => {
141
+ \t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
142
+ \t\tconst tableSchema = (schema as any).${exportName};
143
+ \t\tif (!tableSchema) return c.text('Table missing', 404);
144
+
145
+ \t\tconst body = await c.req.parseBody();
146
+ \t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
147
+ \t\tconst payload: Record<string, unknown> = {};
148
+
149
+ \t\t${createAssignments}
150
+
151
+ \t\tawait db.insert(tableSchema).values(payload as any).execute();
152
+
153
+ \t\tconst query = new URLSearchParams({
154
+ \t\t\tpage: getValue(body.page) || '1',
155
+ \t\t\tsort: getValue(body.sort) || '${defaultSort}',
156
+ \t\t\torder: getValue(body.order) || 'desc',
157
+ \t\t\tsearch: getValue(body.search) || '',
158
+ \t\t});
159
+ \t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
160
+ \t});
161
+ ${editRoute}
162
+ \t`;
163
+ }
@@ -1,7 +1,7 @@
1
- // Re-exported from the modular table/ directory.
2
- // The actual implementation has been split into focused files:
3
- // - table/actions-cell.ts — edit drawer + delete dialog cell
4
- // - table/get-route.ts — GET /admin/table/:tableName handler
5
- // - table/post-routes.ts — POST create/edit/delete handlers
6
- // - table/index.ts — orchestrating buildTableRoute()
7
- export { buildTableRoute } from "./table/index";
1
+ // Re-exported from the modular table/ directory.
2
+ // The actual implementation has been split into focused files:
3
+ // - table/actions-cell.ts — edit drawer + delete dialog cell
4
+ // - table/get-route.ts — GET /admin/table/:tableName handler
5
+ // - table/post-routes.ts — POST create/edit/delete handlers
6
+ // - table/index.ts — orchestrating buildTableRoute()
7
+ export { buildTableRoute } from "./table/index";
@@ -1,69 +1,69 @@
1
- import { buildUsersTableHtml } from "./html/table";
2
- import { buildUsersPageHtml } from "./html/page";
3
-
4
- /**
5
- * Builds the GET /admin/users route handler (runtime template string).
6
- * Handles search, sort, pagination, and renders the users page.
7
- */
8
- export function buildUsersGetRoute(): string {
9
- const tableHtml = buildUsersTableHtml();
10
- const pageHtml = buildUsersPageHtml();
11
-
12
- return `
13
- \tadminApp.get('/users', async (c) => {
14
- \t\tconst db = drizzle(c.env[options.databaseBinding]);
15
- \t\tconst auth = createAuth({ DATABASE: c.env[options.databaseBinding] } as any, c.req.raw.cf as any);
16
- \t\tconst session = await auth.api.getSession({ headers: c.req.raw.headers });
17
- \t\tconst page = parseInt(c.req.query('page') || '1');
18
- \t\tconst limit = 20;
19
- \t\tconst offset = (page - 1) * limit;
20
- \t\tconst sort = c.req.query('sort') || 'createdAt';
21
- \t\tconst order = c.req.query('order') || 'desc';
22
- \t\tconst search = c.req.query('search') || '';
23
- \t\tconst currentUserId = session?.user?.id || '';
24
-
25
- \t\tlet query = db.select().from(users);
26
- \t\tlet countQuery = db.select({ count: sql\`count(*)\` }).from(users);
27
-
28
- \t\tif (search) {
29
- \t\t\tconst searchConditions = [
30
- \t\t\t\tlike(users.name, \`%\${search}%\`),
31
- \t\t\t\tlike(users.email, \`%\${search}%\`),
32
- \t\t\t\tlike(users.role, \`%\${search}%\`),
33
- \t\t\t];
34
- \t\t\tquery = query.where(or(...searchConditions)) as any;
35
- \t\t\tcountQuery = countQuery.where(or(...searchConditions)) as any;
36
- \t\t}
37
-
38
- \t\tconst sortColumns: Record<string, any> = {
39
- \t\t\tid: users.id,
40
- \t\t\tname: users.name,
41
- \t\t\temail: users.email,
42
- \t\t\trole: users.role,
43
- \t\t\tcreatedAt: users.createdAt,
44
- \t\t\tbanned: users.banned,
45
- \t\t};
46
-
47
- \t\tif (sortColumns[sort]) {
48
- \t\t\tquery = query.orderBy(order === 'asc' ? asc(sortColumns[sort]) : desc(sortColumns[sort])) as any;
49
- \t\t}
50
-
51
- \t\tconst data = await query.limit(limit).offset(offset).execute();
52
- \t\tconst totalResult = await countQuery.execute();
53
- \t\tconst total = Number(totalResult[0]?.count || 0);
54
- \t\tconst totalPages = Math.ceil(total / limit);
55
-
56
- \t\t${tableHtml}
57
-
58
- \t\t${pageHtml}
59
-
60
- \t\tif (c.req.header('hx-request')) {
61
- \t\t\treturn c.html(content);
62
- \t\t}
63
-
64
- \t\treturn c.html(Layout({
65
- \t\t\ttitle: "users - Admin Dashboard",
66
- \t\t\tchildren: content,
67
- \t\t}));
68
- \t});`;
69
- }
1
+ import { buildUsersTableHtml } from "./html/table";
2
+ import { buildUsersPageHtml } from "./html/page";
3
+
4
+ /**
5
+ * Builds the GET /admin/users route handler (runtime template string).
6
+ * Handles search, sort, pagination, and renders the users page.
7
+ */
8
+ export function buildUsersGetRoute(): string {
9
+ const tableHtml = buildUsersTableHtml();
10
+ const pageHtml = buildUsersPageHtml();
11
+
12
+ return `
13
+ \tadminApp.get('/users', async (c) => {
14
+ \t\tconst db = drizzle(c.env[options.databaseBinding]);
15
+ \t\tconst auth = createAuth({ DATABASE: c.env[options.databaseBinding] } as any, c.req.raw.cf as any);
16
+ \t\tconst session = await auth.api.getSession({ headers: c.req.raw.headers });
17
+ \t\tconst page = parseInt(c.req.query('page') || '1');
18
+ \t\tconst limit = 20;
19
+ \t\tconst offset = (page - 1) * limit;
20
+ \t\tconst sort = c.req.query('sort') || 'createdAt';
21
+ \t\tconst order = c.req.query('order') || 'desc';
22
+ \t\tconst search = c.req.query('search') || '';
23
+ \t\tconst currentUserId = session?.user?.id || '';
24
+
25
+ \t\tlet query = db.select().from(users);
26
+ \t\tlet countQuery = db.select({ count: sql\`count(*)\` }).from(users);
27
+
28
+ \t\tif (search) {
29
+ \t\t\tconst searchConditions = [
30
+ \t\t\t\tlike(users.name, \`%\${search}%\`),
31
+ \t\t\t\tlike(users.email, \`%\${search}%\`),
32
+ \t\t\t\tlike(users.role, \`%\${search}%\`),
33
+ \t\t\t];
34
+ \t\t\tquery = query.where(or(...searchConditions)) as any;
35
+ \t\t\tcountQuery = countQuery.where(or(...searchConditions)) as any;
36
+ \t\t}
37
+
38
+ \t\tconst sortColumns: Record<string, any> = {
39
+ \t\t\tid: users.id,
40
+ \t\t\tname: users.name,
41
+ \t\t\temail: users.email,
42
+ \t\t\trole: users.role,
43
+ \t\t\tcreatedAt: users.createdAt,
44
+ \t\t\tbanned: users.banned,
45
+ \t\t};
46
+
47
+ \t\tif (sortColumns[sort]) {
48
+ \t\t\tquery = query.orderBy(order === 'asc' ? asc(sortColumns[sort]) : desc(sortColumns[sort])) as any;
49
+ \t\t}
50
+
51
+ \t\tconst data = await query.limit(limit).offset(offset).execute();
52
+ \t\tconst totalResult = await countQuery.execute();
53
+ \t\tconst total = Number(totalResult[0]?.count || 0);
54
+ \t\tconst totalPages = Math.ceil(total / limit);
55
+
56
+ \t\t${tableHtml}
57
+
58
+ \t\t${pageHtml}
59
+
60
+ \t\tif (c.req.header('hx-request')) {
61
+ \t\t\treturn c.html(content);
62
+ \t\t}
63
+
64
+ \t\treturn c.html(Layout({
65
+ \t\t\ttitle: "users - Admin Dashboard",
66
+ \t\t\tchildren: content,
67
+ \t\t}));
68
+ \t});`;
69
+ }