@visulima/crud 1.0.1 → 1.0.3

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 (54) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +46 -16
  3. package/dist/chunk-SH6A4KBC.mjs +28 -0
  4. package/dist/{chunk-UBXIGP5H.mjs.map → chunk-SH6A4KBC.mjs.map} +1 -1
  5. package/dist/chunk-ZY3WOLEP.js +28 -0
  6. package/dist/chunk-ZY3WOLEP.js.map +1 -0
  7. package/dist/index.d.ts +9 -15
  8. package/dist/index.js +57 -37
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +54 -34
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/next/index.js +10 -281
  13. package/dist/next/index.js.map +1 -1
  14. package/dist/next/index.mjs +9 -280
  15. package/dist/next/index.mjs.map +1 -1
  16. package/next/package.json +18 -0
  17. package/package.json +16 -14
  18. package/dist/chunk-FJWRITBO.js +0 -52
  19. package/dist/chunk-FJWRITBO.js.map +0 -1
  20. package/dist/chunk-UBXIGP5H.mjs +0 -52
  21. package/src/adapter/prisma/index.ts +0 -241
  22. package/src/adapter/prisma/types.d.ts +0 -46
  23. package/src/adapter/prisma/utils/models-to-route-names.ts +0 -12
  24. package/src/adapter/prisma/utils/parse-cursor.ts +0 -26
  25. package/src/adapter/prisma/utils/parse-order-by.ts +0 -21
  26. package/src/adapter/prisma/utils/parse-recursive.ts +0 -26
  27. package/src/adapter/prisma/utils/parse-where.ts +0 -197
  28. package/src/base-crud-handler.ts +0 -181
  29. package/src/handler/create.ts +0 -21
  30. package/src/handler/delete.ts +0 -27
  31. package/src/handler/list.ts +0 -62
  32. package/src/handler/read.ts +0 -27
  33. package/src/handler/update.ts +0 -29
  34. package/src/index.ts +0 -27
  35. package/src/next/api/edge/index.ts +0 -23
  36. package/src/next/api/node/index.ts +0 -27
  37. package/src/next/index.ts +0 -2
  38. package/src/query-parser.ts +0 -94
  39. package/src/swagger/adapter/prisma/index.ts +0 -95
  40. package/src/swagger/json-schema-parser.ts +0 -456
  41. package/src/swagger/parameters.ts +0 -83
  42. package/src/swagger/types.d.ts +0 -53
  43. package/src/swagger/utils/format-example-ref.ts +0 -4
  44. package/src/swagger/utils/format-schema-ref.ts +0 -4
  45. package/src/swagger/utils/get-models-accessible-routes.ts +0 -23
  46. package/src/swagger/utils/get-swagger-paths.ts +0 -244
  47. package/src/swagger/utils/get-swagger-tags.ts +0 -13
  48. package/src/types.d.ts +0 -124
  49. package/src/utils/format-resource-id.ts +0 -3
  50. package/src/utils/get-accessible-routes.ts +0 -18
  51. package/src/utils/get-resource-name-from-url.ts +0 -23
  52. package/src/utils/get-route-type.ts +0 -99
  53. package/src/utils/is-primitive.ts +0 -5
  54. package/src/utils/validate-adapter-methods.ts +0 -15
@@ -1,244 +0,0 @@
1
- import type { ModelOption, ModelsOptions } from "../../types.d";
2
- import { RouteType } from "../../types.d";
3
- import { getQueryParameters } from "../parameters";
4
- import type { Routes, SwaggerModelsConfig } from "../types.d";
5
- import formatExampleReference from "./format-example-ref";
6
- import formatSchemaReference from "./format-schema-ref";
7
-
8
- interface GenerateSwaggerPathObjectParameters<M extends string> {
9
- tag: string;
10
- routeTypes: RouteType[];
11
- modelsConfig?: SwaggerModelsConfig<M>;
12
- modelName: M;
13
- hasId?: boolean;
14
- }
15
-
16
- type HttpMethod = "get" | "post" | "put" | "delete";
17
-
18
- const generateContentForSchema = (schemaName: string, isArray?: boolean) => {
19
- if (isArray) {
20
- return {
21
- type: "array",
22
- items: {
23
- $ref: formatSchemaReference(schemaName),
24
- },
25
- };
26
- }
27
-
28
- return {
29
- $ref: formatSchemaReference(schemaName),
30
- };
31
- };
32
-
33
- const generateSwaggerResponse = (routeType: RouteType, modelName: string): { statusCode: number; content: any } | undefined => {
34
- if (routeType === RouteType.CREATE) {
35
- return {
36
- statusCode: 201,
37
- content: {
38
- description: `${modelName} created`,
39
- content: {
40
- "application/json": {
41
- schema: generateContentForSchema(modelName),
42
- },
43
- },
44
- },
45
- };
46
- }
47
-
48
- if (routeType === RouteType.DELETE) {
49
- return {
50
- statusCode: 200,
51
- content: {
52
- description: `${modelName} item deleted`,
53
- content: {
54
- "application/json": {
55
- schema: generateContentForSchema(modelName),
56
- },
57
- },
58
- },
59
- };
60
- }
61
-
62
- if (routeType === RouteType.READ_ALL) {
63
- return {
64
- statusCode: 200,
65
- content: {
66
- description: `${modelName} list retrieved`,
67
- content: {
68
- "application/json": {
69
- schema: {
70
- oneOf: [generateContentForSchema(modelName, true), generateContentForSchema(`${modelName}Page`, false)],
71
- },
72
- examples: {
73
- Default: {
74
- $ref: formatExampleReference(`${modelName}`),
75
- },
76
- Pagination: {
77
- $ref: formatExampleReference(`${modelName}Page`),
78
- },
79
- },
80
- },
81
- },
82
- },
83
- };
84
- }
85
-
86
- if (routeType === RouteType.READ_ONE) {
87
- return {
88
- statusCode: 200,
89
- content: {
90
- description: `${modelName} item retrieved`,
91
- content: {
92
- "application/json": {
93
- schema: generateContentForSchema(modelName),
94
- },
95
- },
96
- },
97
- };
98
- }
99
-
100
- if (routeType === RouteType.UPDATE) {
101
- return {
102
- statusCode: 200,
103
- content: {
104
- description: `${modelName} item updated`,
105
- content: {
106
- "application/json": {
107
- schema: generateContentForSchema(modelName),
108
- },
109
- },
110
- },
111
- };
112
- }
113
-
114
- return undefined;
115
- };
116
-
117
- const generateRequestBody = (schemaStartName: string, modelName: string) => {
118
- return {
119
- content: {
120
- "application/json": {
121
- schema: {
122
- $ref: formatSchemaReference(`${schemaStartName}${modelName}`),
123
- },
124
- },
125
- },
126
- };
127
- };
128
-
129
- const getRouteTypeMethod = (routeType: RouteType): HttpMethod => {
130
- switch (routeType) {
131
- case RouteType.CREATE: {
132
- return "post";
133
- }
134
- case RouteType.READ_ALL:
135
- case RouteType.READ_ONE: {
136
- return "get";
137
- }
138
- case RouteType.UPDATE: {
139
- return "put";
140
- }
141
- case RouteType.DELETE: {
142
- return "delete";
143
- }
144
- default: {
145
- throw new TypeError(`Method for route type ${routeType} was not found.`);
146
- }
147
- }
148
- };
149
-
150
- const generateSwaggerPathObject = <M extends string>({
151
- tag, routeTypes, modelName, modelsConfig, hasId,
152
- }: GenerateSwaggerPathObjectParameters<M>) => {
153
- const methods: { [key: string]: any } = {};
154
-
155
- routeTypes.forEach((routeType) => {
156
- if (routeTypes.includes(routeType)) {
157
- const returnType = modelsConfig?.[modelName]?.routeTypes?.[routeType]?.response?.name ?? modelsConfig?.[modelName]?.type?.name ?? modelName;
158
- const method: HttpMethod = getRouteTypeMethod(routeType);
159
- const response = generateSwaggerResponse(routeType, returnType);
160
-
161
- if (typeof response === "undefined") {
162
- throw new TypeError(`Route type ${routeType}; response config was not found.`);
163
- }
164
-
165
- methods[method as HttpMethod] = {
166
- tags: [tag],
167
- summary: modelsConfig?.[modelName]?.routeTypes?.[routeType]?.summary,
168
- parameters: getQueryParameters(routeType).map((queryParameter) => {
169
- return { ...queryParameter, in: "query" };
170
- }),
171
- responses: {
172
- [response.statusCode]: response.content,
173
- ...modelsConfig?.[modelName]?.routeTypes?.[routeType]?.responses,
174
- },
175
- };
176
-
177
- if (hasId) {
178
- methods[method as HttpMethod].parameters.push({
179
- in: "path",
180
- name: "id",
181
- description: `ID of the ${modelName}`,
182
- required: true,
183
- schema: {
184
- type: "string",
185
- },
186
- });
187
- }
188
-
189
- if (routeType === RouteType.UPDATE || routeType === RouteType.CREATE) {
190
- if (routeType === RouteType.UPDATE) {
191
- methods[method as HttpMethod].requestBody = generateRequestBody("Update", returnType);
192
- } else if (routeType === RouteType.CREATE) {
193
- methods[method as HttpMethod].requestBody = generateRequestBody("Create", returnType);
194
- }
195
- }
196
- }
197
- });
198
-
199
- return methods;
200
- };
201
-
202
- interface GetSwaggerPathsParameters<M extends string> {
203
- routes: Routes<M>;
204
- modelsConfig?: SwaggerModelsConfig<M>;
205
- models?: ModelsOptions<M>;
206
- routesMap?: { [key in M]?: string };
207
- }
208
-
209
- const getSwaggerPaths = <M extends string>({
210
- routes, models, modelsConfig, routesMap,
211
- }: GetSwaggerPathsParameters<M>) => Object.keys(routes).reduce((accumulator: { [key: string]: any }, value: string | M) => {
212
- const routeTypes = routes[value] as RouteType[];
213
- const resourceName = models?.[value]?.name ? (models[value] as ModelOption).name : routesMap?.[value as M] || value;
214
- const tag = modelsConfig?.[value]?.tag?.name || value;
215
-
216
- if (routeTypes.includes(RouteType.CREATE) || routeTypes.includes(RouteType.READ_ALL)) {
217
- const path = `/${resourceName}`;
218
- const routeTypesToUse = [RouteType.READ_ALL, RouteType.CREATE].filter((routeType) => routeTypes.includes(routeType));
219
-
220
- accumulator[path] = generateSwaggerPathObject({
221
- tag,
222
- modelName: value as M,
223
- modelsConfig,
224
- routeTypes: routeTypesToUse,
225
- });
226
- }
227
-
228
- if (routeTypes.includes(RouteType.READ_ONE) || routeTypes.includes(RouteType.UPDATE) || routeTypes.includes(RouteType.DELETE)) {
229
- const path = `/${resourceName}/{id}`;
230
- const routeTypesToUse = [RouteType.READ_ONE, RouteType.UPDATE, RouteType.DELETE].filter((routeType) => routeTypes.includes(routeType));
231
-
232
- accumulator[path] = generateSwaggerPathObject({
233
- tag,
234
- modelName: value as M,
235
- modelsConfig,
236
- routeTypes: routeTypesToUse,
237
- hasId: true,
238
- });
239
- }
240
-
241
- return accumulator;
242
- }, {});
243
-
244
- export default getSwaggerPaths;
@@ -1,13 +0,0 @@
1
- import type { ModelsConfig, SwaggerModelsConfig, SwaggerTag } from "../types.d";
2
-
3
- const getSwaggerTags = <M extends string>(modelNames: M[], modelsConfig?: SwaggerModelsConfig<M>): SwaggerTag[] => modelNames.map((modelName) => {
4
- if (modelsConfig?.[modelName]?.tag) {
5
- return (modelsConfig[modelName as M] as ModelsConfig).tag;
6
- }
7
-
8
- return {
9
- name: modelName,
10
- };
11
- });
12
-
13
- export default getSwaggerTags;
package/src/types.d.ts DELETED
@@ -1,124 +0,0 @@
1
- import type { Handler as CreateHandler } from "./handler/create";
2
- import type { Handler as DeleteHandler } from "./handler/delete";
3
- import type { Handler as ListHandler } from "./handler/list";
4
- import type { Handler as GetHandler } from "./handler/read";
5
- import type { Handler as UpdateHandler } from "./handler/update";
6
-
7
- export enum RouteType {
8
- CREATE = "CREATE",
9
- READ_ALL = "READ_ALL",
10
- READ_ONE = "READ_ONE",
11
- UPDATE = "UPDATE",
12
- DELETE = "DELETE",
13
- }
14
-
15
- export type ModelOption = {
16
- name?: string
17
- only?: RouteType[]
18
- exclude?: RouteType[]
19
- formatResourceId?: (resourceId: string) => string | number
20
- };
21
-
22
- export type ModelsOptions<M extends string = string> = {
23
- [key in M]?: ModelOption
24
- };
25
-
26
- export type HandlerOptions<M extends string = string> = {
27
- formatResourceId?: (resourceId: string) => string | number;
28
- models?: ModelsOptions<M>;
29
- exposeStrategy?: "all" | "none";
30
- pagination?: PaginationConfig,
31
- handlers?: {
32
- create?: CreateHandler;
33
- delete?: DeleteHandler;
34
- get?: GetHandler;
35
- list?: ListHandler;
36
- update?: UpdateHandler;
37
- },
38
- };
39
-
40
- export type PaginationConfig = {
41
- perPage: number
42
- };
43
-
44
- export interface HandlerParameters<T, Q> {
45
- adapter: Adapter<T, Q>;
46
- query: Q;
47
- resourceName: string;
48
- }
49
-
50
- export interface UniqueResourceHandlerParameters<T, Q> {
51
- adapter: Adapter<T, Q>;
52
- query: Q;
53
- resourceName: string;
54
- resourceId: string | number;
55
- }
56
-
57
- export interface Adapter<T, Q, M extends string = string> {
58
- models?: M[];
59
- init?: () => Promise<void>;
60
- parseQuery(resourceName: M, query: ParsedQueryParameters): Q;
61
- getAll(resourceName: M, query: Q): Promise<T[]>;
62
- getOne(resourceName: M, resourceId: string | number, query: Q): Promise<T>;
63
- create(resourceName: M, data: any, query: Q): Promise<T>;
64
- update(resourceName: M, resourceId: string | number, data: any, query: Q): Promise<T>;
65
- delete(resourceName: M, resourceId: string | number, query: Q): Promise<T>;
66
- getPaginationData(resourceName: M, query: Q): Promise<PaginationData>;
67
- getModels(): M[];
68
- connect?: () => Promise<void>;
69
- disconnect?: () => Promise<void>;
70
- handleError?: (error: Error) => void;
71
- mapModelsToRouteNames?: () => Promise<{ [key in M]?: string }>;
72
- }
73
-
74
- export type PaginationData = {
75
- total: number
76
- pageCount: number
77
- page: number
78
- };
79
-
80
- export type RecursiveField = {
81
- [key: string]: boolean | TRecursiveField;
82
- };
83
-
84
- export type WhereOperator = "$eq" | "$neq" | "$in" | "$notin" | "$lt" | "$lte" | "$gt" | "$gte" | "$cont" | "$starts" | "$ends" | "$isnull";
85
-
86
- export type SearchCondition = string | boolean | number | Date | null;
87
-
88
- export type WhereCondition = {
89
- [key in TWhereOperator]?: TSearchCondition;
90
- };
91
-
92
- export type Condition = {
93
- [key: string]: TSearchCondition | TWhereCondition | TCondition;
94
- };
95
-
96
- export type WhereField = Condition & {
97
- $and?: TCondition | TCondition[];
98
- $or?: TCondition | TCondition[];
99
- $not?: TCondition | TCondition[];
100
- };
101
-
102
- export type OrderByOperator = "$asc" | "$desc";
103
-
104
- export type OrderByField = {
105
- [key: string]: TOrderByOperator;
106
- };
107
-
108
- export interface ParsedQueryParameters {
109
- select?: RecursiveField;
110
- include?: RecursiveField;
111
- where?: WhereField;
112
- orderBy?: OrderByField;
113
- limit?: number;
114
- skip?: number;
115
- distinct?: string;
116
- page?: number;
117
- originalQuery?: {
118
- [key: string]: any;
119
- };
120
- }
121
-
122
- export interface ExecuteHandler<Request, Response> {
123
- (request: Request, response: Response): Promise<void>;
124
- }
@@ -1,3 +0,0 @@
1
- const formatResourceId = (resourceId: string): string | number => (Number.isSafeInteger(+resourceId) ? +resourceId : resourceId);
2
-
3
- export default formatResourceId;
@@ -1,18 +0,0 @@
1
- import { RouteType } from "../types.d";
2
-
3
- const getAccessibleRoutes = (only?: RouteType[], exclude?: RouteType[], defaultExposeStrategy: "all" | "none" = "all"): RouteType[] => {
4
- // eslint-disable-next-line max-len
5
- let accessibleRoutes: RouteType[] = defaultExposeStrategy === "none" ? [] : [RouteType.READ_ALL, RouteType.READ_ONE, RouteType.UPDATE, RouteType.DELETE, RouteType.CREATE];
6
-
7
- if (Array.isArray(only)) {
8
- accessibleRoutes = only;
9
- }
10
-
11
- if (exclude?.length) {
12
- accessibleRoutes = accessibleRoutes.filter((element) => !exclude.includes(element));
13
- }
14
-
15
- return accessibleRoutes;
16
- };
17
-
18
- export default getAccessibleRoutes;
@@ -1,23 +0,0 @@
1
- export const ensureCamelCase = (string_: string) => `${string_.charAt(0).toLowerCase()}${string_.slice(1)}`;
2
-
3
- export const getResourceNameFromUrl = <M extends string = string>(url: string, models: { [key in M]?: string }) => {
4
- // Exclude the query params from the path
5
- const realPath = url.split("?")[0];
6
-
7
- if (typeof realPath === "undefined") {
8
- throw new TypeError("Path is undefined");
9
- }
10
-
11
- const modelName = (Object.keys(models) as M[]).find((name) => {
12
- const routeName = models[name] as string;
13
- const camelCaseModel = ensureCamelCase(routeName);
14
-
15
- // eslint-disable-next-line @rushstack/security/no-unsafe-regexp
16
- return new RegExp(`(${routeName}|${camelCaseModel}$)|(${routeName}|${camelCaseModel}/)`, "g").test(realPath);
17
- });
18
-
19
- return {
20
- modelName,
21
- resourceName: models[modelName] as string,
22
- };
23
- };
@@ -1,99 +0,0 @@
1
- import { match } from "path-to-regexp";
2
-
3
- import { RouteType } from "../types.d";
4
-
5
- type PathMatch = { id: string };
6
-
7
- const getRouteType: (
8
- method: string,
9
- url: string,
10
- resourceName: string,
11
- ) => GetRouteType = (method, url, resourceName) => {
12
- // Exclude the query params from the path
13
- const realPath = url.split("?")[0];
14
-
15
- if (typeof realPath === "undefined") {
16
- throw new TypeError("Path is undefined");
17
- }
18
-
19
- if (!realPath.includes(`/${resourceName}`)) {
20
- throw new Error(`invalid resource name '${resourceName}' for route '${realPath}'`);
21
- }
22
-
23
- const entityMatcher = match<PathMatch>([`/(.*)/${resourceName}`, `/(.*)/${resourceName}/:id`], { decode: decodeURIComponent });
24
- const simpleMatcher = match(`/(.*)/${resourceName}`, {
25
- decode: decodeURIComponent,
26
- });
27
-
28
- switch (method) {
29
- case "GET": {
30
- const pathMatch = entityMatcher(realPath);
31
-
32
- // If we got a /something after the resource name, we are reading 1 entity
33
- if (pathMatch && pathMatch.params.id) {
34
- return {
35
- routeType: RouteType.READ_ONE,
36
- resourceId: pathMatch.params.id,
37
- };
38
- }
39
-
40
- return {
41
- routeType: RouteType.READ_ALL,
42
- };
43
- }
44
- case "POST": {
45
- const pathMatch = simpleMatcher(realPath);
46
-
47
- if (pathMatch) {
48
- return {
49
- routeType: RouteType.CREATE,
50
- };
51
- }
52
-
53
- return {
54
- routeType: null,
55
- };
56
- }
57
- case "PUT":
58
- case "PATCH": {
59
- const pathMatch = entityMatcher(realPath);
60
-
61
- if (pathMatch && pathMatch.params.id) {
62
- return {
63
- routeType: RouteType.UPDATE,
64
- resourceId: pathMatch.params.id,
65
- };
66
- }
67
-
68
- return {
69
- routeType: null,
70
- };
71
- }
72
- case "DELETE": {
73
- const pathMatch = entityMatcher(realPath);
74
-
75
- if (pathMatch && pathMatch.params.id) {
76
- return {
77
- routeType: RouteType.DELETE,
78
- resourceId: pathMatch.params.id,
79
- };
80
- }
81
-
82
- return {
83
- routeType: null,
84
- };
85
- }
86
- default: {
87
- return {
88
- routeType: null,
89
- };
90
- }
91
- }
92
- };
93
-
94
- export type GetRouteType = {
95
- routeType: RouteType | null;
96
- resourceId?: string;
97
- };
98
-
99
- export default getRouteType;
@@ -1,5 +0,0 @@
1
- const primitiveTypes = new Set(["string", "boolean", "number"]);
2
-
3
- const isPrimitive = (value: any): boolean => primitiveTypes.has(typeof value);
4
-
5
- export default isPrimitive;
@@ -1,15 +0,0 @@
1
- import createHttpError from "http-errors";
2
-
3
- import type { Adapter } from "../types.d";
4
-
5
- const adapterMethods = ["create" || "delete" || "getAll" || "getOne" || "parseQuery" || "update" || "getPaginationData" || "getModels"];
6
-
7
- const validateAdapterMethods = <T, Q>(adapter: Adapter<T, Q>) => {
8
- adapterMethods.forEach((method) => {
9
- if (!adapter[method as keyof Adapter<T, Q>]) {
10
- throw createHttpError(500, `Adapter must implement the "${method}" method.`);
11
- }
12
- });
13
- };
14
-
15
- export default validateAdapterMethods;