dn-react-router-toolkit 0.8.0 → 0.9.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 (71) hide show
  1. package/dist/api/index.js +13 -4
  2. package/dist/api/index.mjs +14 -5
  3. package/dist/api/item_api_handler.d.mts +4 -2
  4. package/dist/api/item_api_handler.d.ts +4 -2
  5. package/dist/api/item_api_handler.js +13 -4
  6. package/dist/api/item_api_handler.mjs +14 -5
  7. package/dist/crud/index.d.mts +0 -20
  8. package/dist/crud/index.d.ts +0 -20
  9. package/dist/crud/index.js +12 -8508
  10. package/dist/crud/index.mjs +0 -8516
  11. package/dist/post/index.js +67 -7705
  12. package/dist/post/index.mjs +54 -7717
  13. package/dist/post/post_form_page.js +67 -7705
  14. package/dist/post/post_form_page.mjs +54 -7717
  15. package/dist/table/index.d.mts +1 -3
  16. package/dist/table/index.d.ts +1 -3
  17. package/dist/table/index.js +83 -76
  18. package/dist/table/index.mjs +84 -74
  19. package/dist/table/load_table.d.mts +8 -2
  20. package/dist/table/load_table.d.ts +8 -2
  21. package/dist/table/load_table.js +23 -3
  22. package/dist/table/load_table.mjs +24 -3
  23. package/dist/table/loader.d.mts +3 -0
  24. package/dist/table/loader.d.ts +3 -0
  25. package/dist/table/loader.js +23 -3
  26. package/dist/table/loader.mjs +24 -3
  27. package/dist/table/repository.d.mts +6 -4
  28. package/dist/table/repository.d.ts +6 -4
  29. package/dist/table/repository.js +4 -0
  30. package/dist/table/repository.mjs +4 -0
  31. package/dist/table/table.d.mts +5 -2
  32. package/dist/table/table.d.ts +5 -2
  33. package/dist/table/table.js +38 -6
  34. package/dist/table/table.mjs +38 -6
  35. package/dist/table/table_form.d.mts +2 -2
  36. package/dist/table/table_form.d.ts +2 -2
  37. package/dist/table/table_form.js +52 -19
  38. package/dist/table/table_form.mjs +52 -19
  39. package/dist/table/use_table.d.mts +3 -3
  40. package/dist/table/use_table.d.ts +3 -3
  41. package/dist/table/use_table.js +1 -10
  42. package/dist/table/use_table.mjs +1 -10
  43. package/package.json +1 -1
  44. package/dist/crud/crud_loader.d.mts +0 -26
  45. package/dist/crud/crud_loader.d.ts +0 -26
  46. package/dist/crud/crud_loader.js +0 -322
  47. package/dist/crud/crud_loader.mjs +0 -307
  48. package/dist/crud/crud_page.d.mts +0 -32
  49. package/dist/crud/crud_page.d.ts +0 -32
  50. package/dist/crud/crud_page.js +0 -726
  51. package/dist/crud/crud_page.mjs +0 -708
  52. package/dist/crud/generate_handlers.d.mts +0 -16
  53. package/dist/crud/generate_handlers.d.ts +0 -16
  54. package/dist/crud/generate_handlers.js +0 -39
  55. package/dist/crud/generate_handlers.mjs +0 -14
  56. package/dist/crud/generate_pages.d.mts +0 -19
  57. package/dist/crud/generate_pages.d.ts +0 -19
  58. package/dist/crud/generate_pages.js +0 -55
  59. package/dist/crud/generate_pages.mjs +0 -30
  60. package/dist/crud/generate_routes.d.mts +0 -5
  61. package/dist/crud/generate_routes.d.ts +0 -5
  62. package/dist/crud/generate_routes.js +0 -7639
  63. package/dist/crud/generate_routes.mjs +0 -7627
  64. package/dist/table/item_loader.d.mts +0 -14
  65. package/dist/table/item_loader.d.ts +0 -14
  66. package/dist/table/item_loader.js +0 -43
  67. package/dist/table/item_loader.mjs +0 -18
  68. package/dist/table/page.d.mts +0 -16
  69. package/dist/table/page.d.ts +0 -16
  70. package/dist/table/page.js +0 -325
  71. package/dist/table/page.mjs +0 -300
@@ -1,322 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/crud/crud_loader.tsx
21
- var crud_loader_exports = {};
22
- __export(crud_loader_exports, {
23
- crudHandler: () => crudHandler
24
- });
25
- module.exports = __toCommonJS(crud_loader_exports);
26
-
27
- // src/table/load_table.tsx
28
- var import_drizzle_orm = require("drizzle-orm");
29
- async function loadTable({
30
- request,
31
- repository,
32
- options
33
- }) {
34
- const searchParams = new URL(request.url).searchParams;
35
- const { where, searchKey, defaultOrderBy, defaultDirection } = options;
36
- const query = searchParams.get("query") ?? void 0;
37
- const limit = Number(searchParams.get("limit") ?? "20");
38
- const offset = Number(searchParams.get("offset") ?? "0");
39
- const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
40
- const direction = searchParams.get("direction") ?? defaultDirection;
41
- const whereClauses = (0, import_drizzle_orm.and)(
42
- searchKey && query ? (0, import_drizzle_orm.ilike)(
43
- repository.schema[searchKey],
44
- `%${query}%`
45
- ) : void 0,
46
- ...where ?? []
47
- );
48
- const total = await repository.countTotal({ where: whereClauses });
49
- const items = await repository.findAll({
50
- orderBy,
51
- direction,
52
- limit,
53
- offset,
54
- where: whereClauses
55
- });
56
- return {
57
- items,
58
- total,
59
- limit,
60
- offset,
61
- orderBy,
62
- direction,
63
- searchKey
64
- };
65
- }
66
-
67
- // src/table/loader.tsx
68
- function tableLoader({
69
- repository,
70
- options
71
- }) {
72
- return async ({ request }) => {
73
- const table = await loadTable({
74
- request,
75
- repository,
76
- options
77
- });
78
- return {
79
- table
80
- };
81
- };
82
- }
83
-
84
- // src/table/item_loader.tsx
85
- var tableItemloader = ({
86
- repository
87
- }) => {
88
- return async (args) => {
89
- const { params } = args;
90
- if (params["itemId"] === "new") {
91
- return { item: void 0 };
92
- }
93
- const item = params["itemId"] ? await repository.find(params["itemId"]) : void 0;
94
- return {
95
- item
96
- };
97
- };
98
- };
99
-
100
- // src/api/create_api_handler.ts
101
- var import_http = require("dn-react-toolkit/http");
102
- var import_drizzle_orm2 = require("drizzle-orm");
103
- var import_uuid = require("uuid");
104
-
105
- // src/crud/serialize.ts
106
- function deserialize(data) {
107
- if (data === void 0) {
108
- return void 0;
109
- }
110
- if (typeof data === "object" && data !== null && "type" in data && "value" in data) {
111
- const { type, value } = data;
112
- switch (type) {
113
- case "null":
114
- return null;
115
- case "string":
116
- return value;
117
- case "number":
118
- return value;
119
- case "boolean":
120
- return value;
121
- case "date":
122
- return new Date(value);
123
- case "array":
124
- return value.map((item) => deserialize(item));
125
- case "object":
126
- return Object.entries(value).reduce(
127
- (acc, [key, value2]) => {
128
- return {
129
- ...acc,
130
- [key]: deserialize(value2)
131
- };
132
- },
133
- {}
134
- );
135
- default:
136
- return void 0;
137
- }
138
- }
139
- return void 0;
140
- }
141
-
142
- // src/api/create_api_handler.ts
143
- function apiHandler({
144
- withAuthAction,
145
- repository,
146
- validators,
147
- existingConditions,
148
- injectUserId,
149
- roles
150
- }) {
151
- const loader = async ({ request }) => {
152
- return {};
153
- };
154
- const action = withAuthAction((auth) => async ({ request }) => {
155
- if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
156
- throw (0, import_http.UNAUTHORIZED)();
157
- }
158
- switch (request.method) {
159
- case "POST":
160
- case "PUT": {
161
- try {
162
- const serilaizedParams = await request.json();
163
- const params = deserialize(serilaizedParams);
164
- if (validators) {
165
- const paramsForValidation = Object.keys(validators).filter(
166
- (key) => Object.prototype.hasOwnProperty.call(validators, key)
167
- );
168
- for (const paramKey of paramsForValidation) {
169
- const value = params[paramKey];
170
- const validator = validators[paramKey];
171
- if (validator?.validate && !validator.validate(value)) {
172
- throw (0, import_http.BAD_REQUEST)(
173
- validator.message ? validator.message(value) : void 0
174
- );
175
- }
176
- }
177
- }
178
- const itemId = params.id || (0, import_uuid.v4)();
179
- if (!params.id && existingConditions) {
180
- const paramsForExistenceCheck = Object.keys(
181
- existingConditions
182
- ).filter(
183
- (key) => Object.prototype.hasOwnProperty.call(params, key)
184
- );
185
- if (paramsForExistenceCheck.length > 0) {
186
- const where = (0, import_drizzle_orm2.and)(
187
- ...paramsForExistenceCheck.reduce((acc, key) => {
188
- const condition = existingConditions[key];
189
- if (condition) {
190
- acc.push(condition(params[key]));
191
- }
192
- return acc;
193
- }, [])
194
- );
195
- const existing = await repository.findAll({
196
- limit: 1,
197
- where
198
- });
199
- if (existing.length > 0) {
200
- throw (0, import_http.CONFLICT)("\uC790\uB8CC\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.");
201
- }
202
- }
203
- }
204
- const values = {
205
- id: itemId,
206
- userId: injectUserId ? auth?.userId : void 0,
207
- ...params
208
- };
209
- const item = await repository.save(values);
210
- return (0, import_http.CREATED)(item);
211
- } catch (error) {
212
- if (error instanceof Error) {
213
- throw (0, import_http.INTERNAL_SERVER_ERROR)(error.message);
214
- }
215
- throw error;
216
- }
217
- }
218
- default:
219
- throw (0, import_http.METHOD_NOT_ALLOWED)();
220
- }
221
- });
222
- return {
223
- loader,
224
- action
225
- };
226
- }
227
-
228
- // src/api/item_api_handler.ts
229
- var import_http2 = require("dn-react-toolkit/http");
230
- function itemApiHandler({
231
- withAuthAction,
232
- repository
233
- }) {
234
- const loader = async ({ request }) => {
235
- return {};
236
- };
237
- const action = withAuthAction((auth) => async ({ params, request }) => {
238
- if (!auth || auth.role !== "admin") {
239
- return (0, import_http2.UNAUTHORIZED)();
240
- }
241
- switch (request.method) {
242
- case "DELETE": {
243
- const itemId = params.itemId;
244
- await repository.delete(itemId);
245
- return {};
246
- }
247
- }
248
- });
249
- return {
250
- loader,
251
- action
252
- };
253
- }
254
-
255
- // src/crud/crud_loader.tsx
256
- function crudHandler({
257
- repository,
258
- apiHandlerOptions,
259
- loaderOptions,
260
- itemLoaderOptions
261
- }) {
262
- return (prefix) => async (args) => {
263
- const pattern = args.unstable_pattern;
264
- if (pattern === `/api${prefix}`) {
265
- const { loader, action } = apiHandler({
266
- repository,
267
- ...apiHandlerOptions
268
- });
269
- if (args.request.method === "GET") {
270
- return loader(args);
271
- } else {
272
- return action(args);
273
- }
274
- }
275
- if (pattern.startsWith(`/api${prefix}`)) {
276
- const { loader, action } = itemApiHandler({
277
- repository,
278
- ...apiHandlerOptions
279
- });
280
- if (args.request.method === "GET") {
281
- return loader(args);
282
- } else {
283
- return action(args);
284
- }
285
- }
286
- if (pattern === prefix) {
287
- const body = await tableLoader({
288
- ...loaderOptions,
289
- repository
290
- })(args);
291
- if (loaderOptions.loader) {
292
- const result = await loaderOptions.loader(args);
293
- if (typeof result === "object") {
294
- return {
295
- ...result,
296
- ...body
297
- };
298
- }
299
- }
300
- return body;
301
- }
302
- if (pattern.startsWith(prefix)) {
303
- const body = await tableItemloader({ ...itemLoaderOptions, repository })(
304
- args
305
- );
306
- if (itemLoaderOptions.loader) {
307
- const result = await itemLoaderOptions.loader(args);
308
- if (typeof result === "object") {
309
- return {
310
- ...result,
311
- ...body
312
- };
313
- }
314
- }
315
- return body;
316
- }
317
- };
318
- }
319
- // Annotate the CommonJS export names for ESM import in node:
320
- 0 && (module.exports = {
321
- crudHandler
322
- });
@@ -1,307 +0,0 @@
1
- // src/table/load_table.tsx
2
- import {
3
- and,
4
- ilike
5
- } from "drizzle-orm";
6
- async function loadTable({
7
- request,
8
- repository,
9
- options
10
- }) {
11
- const searchParams = new URL(request.url).searchParams;
12
- const { where, searchKey, defaultOrderBy, defaultDirection } = options;
13
- const query = searchParams.get("query") ?? void 0;
14
- const limit = Number(searchParams.get("limit") ?? "20");
15
- const offset = Number(searchParams.get("offset") ?? "0");
16
- const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
17
- const direction = searchParams.get("direction") ?? defaultDirection;
18
- const whereClauses = and(
19
- searchKey && query ? ilike(
20
- repository.schema[searchKey],
21
- `%${query}%`
22
- ) : void 0,
23
- ...where ?? []
24
- );
25
- const total = await repository.countTotal({ where: whereClauses });
26
- const items = await repository.findAll({
27
- orderBy,
28
- direction,
29
- limit,
30
- offset,
31
- where: whereClauses
32
- });
33
- return {
34
- items,
35
- total,
36
- limit,
37
- offset,
38
- orderBy,
39
- direction,
40
- searchKey
41
- };
42
- }
43
-
44
- // src/table/loader.tsx
45
- function tableLoader({
46
- repository,
47
- options
48
- }) {
49
- return async ({ request }) => {
50
- const table = await loadTable({
51
- request,
52
- repository,
53
- options
54
- });
55
- return {
56
- table
57
- };
58
- };
59
- }
60
-
61
- // src/table/item_loader.tsx
62
- var tableItemloader = ({
63
- repository
64
- }) => {
65
- return async (args) => {
66
- const { params } = args;
67
- if (params["itemId"] === "new") {
68
- return { item: void 0 };
69
- }
70
- const item = params["itemId"] ? await repository.find(params["itemId"]) : void 0;
71
- return {
72
- item
73
- };
74
- };
75
- };
76
-
77
- // src/api/create_api_handler.ts
78
- import {
79
- BAD_REQUEST,
80
- CONFLICT,
81
- CREATED,
82
- INTERNAL_SERVER_ERROR,
83
- METHOD_NOT_ALLOWED,
84
- UNAUTHORIZED
85
- } from "dn-react-toolkit/http";
86
- import {
87
- and as and2
88
- } from "drizzle-orm";
89
- import { v4 } from "uuid";
90
-
91
- // src/crud/serialize.ts
92
- function deserialize(data) {
93
- if (data === void 0) {
94
- return void 0;
95
- }
96
- if (typeof data === "object" && data !== null && "type" in data && "value" in data) {
97
- const { type, value } = data;
98
- switch (type) {
99
- case "null":
100
- return null;
101
- case "string":
102
- return value;
103
- case "number":
104
- return value;
105
- case "boolean":
106
- return value;
107
- case "date":
108
- return new Date(value);
109
- case "array":
110
- return value.map((item) => deserialize(item));
111
- case "object":
112
- return Object.entries(value).reduce(
113
- (acc, [key, value2]) => {
114
- return {
115
- ...acc,
116
- [key]: deserialize(value2)
117
- };
118
- },
119
- {}
120
- );
121
- default:
122
- return void 0;
123
- }
124
- }
125
- return void 0;
126
- }
127
-
128
- // src/api/create_api_handler.ts
129
- function apiHandler({
130
- withAuthAction,
131
- repository,
132
- validators,
133
- existingConditions,
134
- injectUserId,
135
- roles
136
- }) {
137
- const loader = async ({ request }) => {
138
- return {};
139
- };
140
- const action = withAuthAction((auth) => async ({ request }) => {
141
- if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
142
- throw UNAUTHORIZED();
143
- }
144
- switch (request.method) {
145
- case "POST":
146
- case "PUT": {
147
- try {
148
- const serilaizedParams = await request.json();
149
- const params = deserialize(serilaizedParams);
150
- if (validators) {
151
- const paramsForValidation = Object.keys(validators).filter(
152
- (key) => Object.prototype.hasOwnProperty.call(validators, key)
153
- );
154
- for (const paramKey of paramsForValidation) {
155
- const value = params[paramKey];
156
- const validator = validators[paramKey];
157
- if (validator?.validate && !validator.validate(value)) {
158
- throw BAD_REQUEST(
159
- validator.message ? validator.message(value) : void 0
160
- );
161
- }
162
- }
163
- }
164
- const itemId = params.id || v4();
165
- if (!params.id && existingConditions) {
166
- const paramsForExistenceCheck = Object.keys(
167
- existingConditions
168
- ).filter(
169
- (key) => Object.prototype.hasOwnProperty.call(params, key)
170
- );
171
- if (paramsForExistenceCheck.length > 0) {
172
- const where = and2(
173
- ...paramsForExistenceCheck.reduce((acc, key) => {
174
- const condition = existingConditions[key];
175
- if (condition) {
176
- acc.push(condition(params[key]));
177
- }
178
- return acc;
179
- }, [])
180
- );
181
- const existing = await repository.findAll({
182
- limit: 1,
183
- where
184
- });
185
- if (existing.length > 0) {
186
- throw CONFLICT("\uC790\uB8CC\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.");
187
- }
188
- }
189
- }
190
- const values = {
191
- id: itemId,
192
- userId: injectUserId ? auth?.userId : void 0,
193
- ...params
194
- };
195
- const item = await repository.save(values);
196
- return CREATED(item);
197
- } catch (error) {
198
- if (error instanceof Error) {
199
- throw INTERNAL_SERVER_ERROR(error.message);
200
- }
201
- throw error;
202
- }
203
- }
204
- default:
205
- throw METHOD_NOT_ALLOWED();
206
- }
207
- });
208
- return {
209
- loader,
210
- action
211
- };
212
- }
213
-
214
- // src/api/item_api_handler.ts
215
- import { UNAUTHORIZED as UNAUTHORIZED2 } from "dn-react-toolkit/http";
216
- function itemApiHandler({
217
- withAuthAction,
218
- repository
219
- }) {
220
- const loader = async ({ request }) => {
221
- return {};
222
- };
223
- const action = withAuthAction((auth) => async ({ params, request }) => {
224
- if (!auth || auth.role !== "admin") {
225
- return UNAUTHORIZED2();
226
- }
227
- switch (request.method) {
228
- case "DELETE": {
229
- const itemId = params.itemId;
230
- await repository.delete(itemId);
231
- return {};
232
- }
233
- }
234
- });
235
- return {
236
- loader,
237
- action
238
- };
239
- }
240
-
241
- // src/crud/crud_loader.tsx
242
- function crudHandler({
243
- repository,
244
- apiHandlerOptions,
245
- loaderOptions,
246
- itemLoaderOptions
247
- }) {
248
- return (prefix) => async (args) => {
249
- const pattern = args.unstable_pattern;
250
- if (pattern === `/api${prefix}`) {
251
- const { loader, action } = apiHandler({
252
- repository,
253
- ...apiHandlerOptions
254
- });
255
- if (args.request.method === "GET") {
256
- return loader(args);
257
- } else {
258
- return action(args);
259
- }
260
- }
261
- if (pattern.startsWith(`/api${prefix}`)) {
262
- const { loader, action } = itemApiHandler({
263
- repository,
264
- ...apiHandlerOptions
265
- });
266
- if (args.request.method === "GET") {
267
- return loader(args);
268
- } else {
269
- return action(args);
270
- }
271
- }
272
- if (pattern === prefix) {
273
- const body = await tableLoader({
274
- ...loaderOptions,
275
- repository
276
- })(args);
277
- if (loaderOptions.loader) {
278
- const result = await loaderOptions.loader(args);
279
- if (typeof result === "object") {
280
- return {
281
- ...result,
282
- ...body
283
- };
284
- }
285
- }
286
- return body;
287
- }
288
- if (pattern.startsWith(prefix)) {
289
- const body = await tableItemloader({ ...itemLoaderOptions, repository })(
290
- args
291
- );
292
- if (itemLoaderOptions.loader) {
293
- const result = await itemLoaderOptions.loader(args);
294
- if (typeof result === "object") {
295
- return {
296
- ...result,
297
- ...body
298
- };
299
- }
300
- }
301
- return body;
302
- }
303
- };
304
- }
305
- export {
306
- crudHandler
307
- };
@@ -1,32 +0,0 @@
1
- import { FormContextProps, CrudFormProps } from './crud_form_provider.mjs';
2
- import { FC, ReactNode } from 'react';
3
- import { TablePageOptions } from '../table/table_form.mjs';
4
- import 'react/jsx-runtime';
5
- import 'react-store-input';
6
- import '../table/table.mjs';
7
- import '../table/use_table.mjs';
8
-
9
- type CrudPageOptions<TModel> = {
10
- name: string;
11
- primaryKey: keyof TModel;
12
- tablePageOptions: TablePageOptions<TModel>;
13
- formOptions: {
14
- form?: FC<{
15
- form: FormContextProps<TModel>;
16
- }>;
17
- columns?: CrudFormProps<TModel>["columns"];
18
- };
19
- header: FC<{
20
- title: string;
21
- actions?: ReactNode;
22
- className?: string;
23
- children?: ReactNode;
24
- }>;
25
- };
26
- type CrudPage = {
27
- name: string;
28
- create: (prefix: string) => FC;
29
- };
30
- declare function crudPage<TModel>({ name, primaryKey, tablePageOptions, formOptions, header: header, }: CrudPageOptions<TModel>): CrudPage;
31
-
32
- export { type CrudPage, type CrudPageOptions, crudPage };
@@ -1,32 +0,0 @@
1
- import { FormContextProps, CrudFormProps } from './crud_form_provider.js';
2
- import { FC, ReactNode } from 'react';
3
- import { TablePageOptions } from '../table/table_form.js';
4
- import 'react/jsx-runtime';
5
- import 'react-store-input';
6
- import '../table/table.js';
7
- import '../table/use_table.js';
8
-
9
- type CrudPageOptions<TModel> = {
10
- name: string;
11
- primaryKey: keyof TModel;
12
- tablePageOptions: TablePageOptions<TModel>;
13
- formOptions: {
14
- form?: FC<{
15
- form: FormContextProps<TModel>;
16
- }>;
17
- columns?: CrudFormProps<TModel>["columns"];
18
- };
19
- header: FC<{
20
- title: string;
21
- actions?: ReactNode;
22
- className?: string;
23
- children?: ReactNode;
24
- }>;
25
- };
26
- type CrudPage = {
27
- name: string;
28
- create: (prefix: string) => FC;
29
- };
30
- declare function crudPage<TModel>({ name, primaryKey, tablePageOptions, formOptions, header: header, }: CrudPageOptions<TModel>): CrudPage;
31
-
32
- export { type CrudPage, type CrudPageOptions, crudPage };