appflare 0.2.43 → 0.2.44

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 (139) hide show
  1. package/Documentation.md +898 -898
  2. package/cli/commands/index.ts +247 -247
  3. package/cli/generate.ts +360 -360
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1366 -1363
  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 +763 -763
  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 +195 -195
  23. package/cli/templates/core/client/types.ts +187 -187
  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/functions/tree-builder.ts +47 -47
  46. package/cli/templates/dashboard/builders/navigation.ts +155 -155
  47. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  48. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  49. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  50. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  51. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  52. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  53. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  55. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  56. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  57. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  58. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  59. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  60. package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
  61. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  62. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  63. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  64. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  65. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  66. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  67. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  68. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  69. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  70. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  71. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  72. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  73. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  74. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  75. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  76. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  77. package/cli/templates/dashboard/components/layout.ts +420 -420
  78. package/cli/templates/dashboard/components/login-page.ts +65 -65
  79. package/cli/templates/dashboard/index.ts +61 -61
  80. package/cli/templates/dashboard/types.ts +9 -9
  81. package/cli/templates/handlers/README.md +353 -353
  82. package/cli/templates/handlers/auth.ts +37 -37
  83. package/cli/templates/handlers/execution.ts +42 -42
  84. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  85. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  86. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  87. package/cli/templates/handlers/generators/context/storage-api.ts +82 -82
  88. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  89. package/cli/templates/handlers/generators/context/types.ts +40 -40
  90. package/cli/templates/handlers/generators/context.ts +43 -43
  91. package/cli/templates/handlers/generators/execution.ts +15 -15
  92. package/cli/templates/handlers/generators/handlers.ts +14 -14
  93. package/cli/templates/handlers/generators/registration/modules/cron.ts +26 -26
  94. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  95. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  96. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  97. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  98. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  99. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  100. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +510 -510
  101. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +56 -56
  102. package/cli/templates/handlers/generators/registration/modules/storage.ts +199 -199
  103. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  104. package/cli/templates/handlers/generators/types/context.ts +121 -121
  105. package/cli/templates/handlers/generators/types/core.ts +106 -106
  106. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  107. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +291 -291
  108. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  109. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1382 -1382
  110. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +278 -278
  111. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +157 -157
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  116. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +958 -958
  117. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  118. package/cli/templates/handlers/index.ts +47 -47
  119. package/cli/templates/handlers/operations.ts +116 -116
  120. package/cli/templates/handlers/registration.ts +91 -91
  121. package/cli/templates/handlers/types.ts +17 -17
  122. package/cli/templates/handlers/utils.ts +48 -48
  123. package/cli/types.ts +110 -110
  124. package/cli/utils/handler-discovery.ts +466 -466
  125. package/cli/utils/json-utils.ts +24 -24
  126. package/cli/utils/path-utils.ts +19 -19
  127. package/cli/utils/schema-discovery.ts +399 -399
  128. package/dist/cli/index.js +1 -1
  129. package/dist/cli/index.mjs +1 -1
  130. package/index.ts +18 -18
  131. package/package.json +58 -58
  132. package/react/index.ts +5 -5
  133. package/react/use-infinite-query.ts +255 -255
  134. package/react/use-mutation.ts +89 -89
  135. package/react/use-query.ts +210 -210
  136. package/schema.ts +641 -641
  137. package/test-better-auth-hash.ts +2 -2
  138. package/tsconfig.json +6 -6
  139. package/tsup.config.ts +82 -82
@@ -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
+ }