@flink-app/flink 0.12.1-alpha.9 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cli/build.js +2 -2
  3. package/dist/cli/clean.js +2 -2
  4. package/dist/cli/cli-utils.js +1 -2
  5. package/dist/cli/run.js +2 -2
  6. package/dist/src/FlinkApp.d.ts +6 -1
  7. package/dist/src/FlinkApp.js +28 -14
  8. package/dist/src/FlinkErrors.d.ts +69 -0
  9. package/dist/src/FlinkErrors.js +87 -8
  10. package/dist/src/FlinkHttpHandler.d.ts +14 -1
  11. package/dist/src/FlinkLog.d.ts +2 -2
  12. package/dist/src/FlinkRepo.d.ts +8 -1
  13. package/dist/src/FlinkRepo.js +9 -2
  14. package/dist/src/FlinkResponse.d.ts +0 -3
  15. package/dist/src/FsUtils.js +5 -6
  16. package/dist/src/TypeScriptCompiler.d.ts +10 -0
  17. package/dist/src/TypeScriptCompiler.js +196 -49
  18. package/dist/src/TypeScriptUtils.js +7 -8
  19. package/dist/src/index.d.ts +1 -0
  20. package/dist/src/utils.js +13 -14
  21. package/package.json +67 -69
  22. package/spec/TypeScriptCompiler.spec.ts +11 -1
  23. package/spec/mock-project/dist/src/handlers/GetCar.js +2 -2
  24. package/spec/mock-project/dist/src/handlers/GetCar2.js +2 -2
  25. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +2 -2
  26. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +2 -2
  27. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +2 -2
  28. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +2 -2
  29. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +2 -2
  30. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +2 -2
  31. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +2 -2
  32. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +3 -4
  33. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +3 -4
  34. package/spec/mock-project/dist/src/handlers/PostCar.js +2 -2
  35. package/spec/mock-project/dist/src/handlers/PostLogin.js +2 -2
  36. package/spec/mock-project/dist/src/handlers/{GetCarWithOmitSchema.js → PostLogout.js} +16 -20
  37. package/spec/mock-project/dist/src/handlers/PutCar.js +2 -2
  38. package/spec/mock-project/dist/src/index.js +2 -2
  39. package/spec/mock-project/src/handlers/PostLogout.ts +19 -0
  40. package/spec/mock-project/tsconfig.json +1 -1
  41. package/src/FlinkApp.ts +24 -5
  42. package/src/FlinkErrors.ts +86 -6
  43. package/src/FlinkHttpHandler.ts +95 -96
  44. package/src/FlinkRepo.ts +8 -1
  45. package/src/FlinkResponse.ts +36 -39
  46. package/src/TypeScriptCompiler.ts +172 -39
  47. package/src/index.ts +12 -0
  48. package/tsconfig.json +1 -1
  49. package/dist/cli/generate-schemas.d.ts +0 -2
  50. package/dist/cli/generate-schemas.js +0 -140
  51. package/spec/mock-project/dist/src/handlers/GetCarWithTypeSchema.js +0 -60
  52. package/spec/mock-project/package-lock.json +0 -108
@@ -4,6 +4,17 @@ import { FlinkResponse } from "./FlinkResponse";
4
4
  // A marker for FlinkError, but all it means is that data is undefined
5
5
  export type FlinkError = undefined;
6
6
 
7
+ /**
8
+ * Returns a 404 Not Found error response.
9
+ * Use when a requested resource doesn't exist (e.g., invalid ID, missing entity).
10
+ *
11
+ * @param detail - Optional custom error message
12
+ * @param code - Optional custom error code
13
+ * @example
14
+ * ```ts
15
+ * if (!user) return notFound("User not found");
16
+ * ```
17
+ */
7
18
  export function notFound(detail?: string, code?: string ): FlinkResponse<FlinkError> {
8
19
  return {
9
20
  status: 404,
@@ -11,11 +22,22 @@ export function notFound(detail?: string, code?: string ): FlinkResponse<FlinkEr
11
22
  id: v4(),
12
23
  title: "Not Found",
13
24
  detail: detail || "The requested resource does not exist",
14
- code : code || "notFound"
25
+ code : code || "notFound"
15
26
  },
16
27
  };
17
28
  }
18
29
 
30
+ /**
31
+ * Returns a 409 Conflict error response.
32
+ * Use when a request conflicts with existing data (e.g., duplicate username/email).
33
+ *
34
+ * @param detail - Optional custom error message
35
+ * @param code - Optional custom error code
36
+ * @example
37
+ * ```ts
38
+ * if (existingUser) return conflict("Email already registered");
39
+ * ```
40
+ */
19
41
  export function conflict(detail?: string, code?: string ): FlinkResponse<FlinkError> {
20
42
  return {
21
43
  status: 409,
@@ -23,13 +45,24 @@ export function conflict(detail?: string, code?: string ): FlinkResponse<FlinkEr
23
45
  id: v4(),
24
46
  title: "Conflict",
25
47
  detail: detail || "An identical entity exits",
26
- code : code || "conflict"
48
+ code : code || "conflict"
27
49
  },
28
50
  };
29
51
  }
30
52
 
31
53
 
32
54
 
55
+ /**
56
+ * Returns a 400 Bad Request error response.
57
+ * Use when the request is malformed or contains invalid data (e.g., validation errors).
58
+ *
59
+ * @param detail - Optional custom error message
60
+ * @param code - Optional custom error code
61
+ * @example
62
+ * ```ts
63
+ * if (!email || !password) return badRequest("Email and password are required");
64
+ * ```
65
+ */
33
66
  export function badRequest(detail?: string, code?: string): FlinkResponse<FlinkError> {
34
67
  return {
35
68
  status: 400,
@@ -37,11 +70,23 @@ export function badRequest(detail?: string, code?: string): FlinkResponse<FlinkE
37
70
  id: v4(),
38
71
  title: "Bad Request",
39
72
  detail: detail || "Invalid request",
40
- code : code || "badRequest"
73
+ code : code || "badRequest"
41
74
  },
42
75
  };
43
76
  }
44
77
 
78
+ /**
79
+ * Returns a 401 Unauthorized error response.
80
+ * Use when authentication is required but missing or invalid (e.g., no token, expired token).
81
+ * This means "who are you?" - the user needs to identify themselves first.
82
+ *
83
+ * @param detail - Optional custom error message
84
+ * @param code - Optional custom error code
85
+ * @example
86
+ * ```ts
87
+ * if (!ctx.auth?.user) return unauthorized("Authentication required");
88
+ * ```
89
+ */
45
90
  export function unauthorized(detail?: string, code?: string): FlinkResponse<FlinkError> {
46
91
  return {
47
92
  status: 401,
@@ -49,12 +94,47 @@ export function unauthorized(detail?: string, code?: string): FlinkResponse<Flin
49
94
  id: v4(),
50
95
  title: "Unauthorized",
51
96
  detail:
52
- detail || "User not logged in or or not allowed to access resource",
53
- code : code || "badRequest"
97
+ detail || "Authentication required",
98
+ code : code || "unauthorized"
54
99
  },
55
100
  };
56
101
  }
57
102
 
103
+ /**
104
+ * Returns a 403 Forbidden error response.
105
+ * Use when the user is authenticated but lacks permission to access the resource.
106
+ * This means "I know who you are, but you're not allowed to do this."
107
+ *
108
+ * @param detail - Optional custom error message
109
+ * @param code - Optional custom error code
110
+ * @example
111
+ * ```ts
112
+ * if (ctx.auth?.user?.role !== "admin") return forbidden("Admin access required");
113
+ * ```
114
+ */
115
+ export function forbidden(detail?: string, code?: string): FlinkResponse<FlinkError> {
116
+ return {
117
+ status: 403,
118
+ error: {
119
+ id: v4(),
120
+ title: "Forbidden",
121
+ detail: detail || "You do not have permission to access this resource",
122
+ code : code || "forbidden"
123
+ },
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Returns a 500 Internal Server Error response.
129
+ * Use when an unexpected error occurs on the server side.
130
+ *
131
+ * @param detail - Optional custom error message
132
+ * @param code - Optional custom error code
133
+ * @example
134
+ * ```ts
135
+ * try { ... } catch (error) { return internalServerError("Failed to process request"); }
136
+ * ```
137
+ */
58
138
  export function internalServerError(
59
139
  detail?: string,
60
140
  code?: string
@@ -65,7 +145,7 @@ export function internalServerError(
65
145
  id: v4(),
66
146
  title: "Internal Server Error",
67
147
  detail: detail || "Something unexpected went wrong",
68
- code : code || "internalServerError"
148
+ code : code || "internalServerError"
69
149
  },
70
150
  };
71
151
  }
@@ -5,24 +5,25 @@ import { FlinkError } from "./FlinkErrors";
5
5
  import { FlinkResponse } from "./FlinkResponse";
6
6
 
7
7
  export enum HttpMethod {
8
- get = "get",
9
- post = "post",
10
- put = "put",
11
- delete = "delete",
8
+ get = "get",
9
+ post = "post",
10
+ put = "put",
11
+ delete = "delete",
12
12
  }
13
13
 
14
14
  type Params = Request["params"];
15
- type Query = Request["query"];
15
+
16
+ /**
17
+ * Query type for request query parameters.
18
+ * Does currently not allow nested objects, although
19
+ * underlying express Request does allow it.
20
+ */
21
+ type Query = Record<string, string | string[] | undefined>;
16
22
 
17
23
  /**
18
24
  * Flink request extends express Request but adds reqId and user object.
19
25
  */
20
- export type FlinkRequest<T = any, P = Params, Q = Query> = Request<
21
- P,
22
- any,
23
- T,
24
- Q
25
- > & { reqId: string; user?: any };
26
+ export type FlinkRequest<T = any, P = Params, Q = Query> = Request<P, any, T, Q> & { reqId: string; user?: any };
26
27
 
27
28
  /**
28
29
  * Route props to control routing.
@@ -31,66 +32,69 @@ export type FlinkRequest<T = any, P = Params, Q = Query> = Request<
31
32
  * instructs express web server how to route traffic.
32
33
  */
33
34
  export interface RouteProps {
34
- /**
35
- * HTTP method which this handlers responds to.
36
- *
37
- * Will if not set attempt to extract HTTP method based
38
- * on handler file name prefix, for example `GetFoo.ts` will assume
39
- * HTTP method `GET`.
40
- */
41
- method?: HttpMethod;
42
-
43
- /**
44
- * Route path including any path params.
45
- * Example: `/user/:id`
46
- */
47
- path: string;
48
-
49
- /**
50
- * Generates mock response based on handlers response schema.
51
- *
52
- * Will be ignored if handler does not have any response schema defined.
53
- *
54
- * This should only be used during development 💥
55
- */
56
- mockApi?: boolean;
57
-
58
- /**
59
- * Set permissions needed to access route if route requires authentication.
60
- */
61
- permissions?: string | string[];
62
-
63
- /**
64
- * Optional documentation of endpoint. Can be used for example in API docs.
65
- * Supports markdown strings.
66
- */
67
- docs?: string; // TODO
68
-
69
- /**
70
- * If handler should not be auto registered
71
- */
72
- skipAutoRegister?: boolean;
73
-
74
- /**
75
- * I.e. filename or plugin name that describes where handler origins from
76
- */
77
- origin?: string;
35
+ /**
36
+ * HTTP method which this handlers responds to.
37
+ *
38
+ * Will if not set attempt to extract HTTP method based
39
+ * on handler file name prefix, for example `GetFoo.ts` will assume
40
+ * HTTP method `GET`.
41
+ */
42
+ method?: HttpMethod;
43
+
44
+ /**
45
+ * Route path including any path params.
46
+ * Example: `/user/:id`
47
+ */
48
+ path: string;
49
+
50
+ /**
51
+ * Generates mock response based on handlers response schema.
52
+ *
53
+ * Will be ignored if handler does not have any response schema defined.
54
+ *
55
+ * This should only be used during development 💥
56
+ */
57
+ mockApi?: boolean;
58
+
59
+ /**
60
+ * Set permissions needed to access route if route requires authentication.
61
+ */
62
+ permissions?: string | string[];
63
+
64
+ /**
65
+ * Optional documentation of endpoint. Can be used for example in API docs.
66
+ * Supports markdown strings.
67
+ */
68
+ docs?: string; // TODO
69
+
70
+ /**
71
+ * If handler should not be auto registered
72
+ */
73
+ skipAutoRegister?: boolean;
74
+
75
+ /**
76
+ * I.e. filename or plugin name that describes where handler origins from
77
+ */
78
+ origin?: string;
79
+
80
+ /**
81
+ * Order handler should be registered in.
82
+ *
83
+ * By default all handlers has order 0 and in most cases this is fine,
84
+ * but if for example you want to register a handler before all others
85
+ * to avoid conflicts you can set a negative order.
86
+ */
87
+ order?: number;
78
88
  }
79
89
 
80
90
  /**
81
91
  * Http handler function that handlers implements in order to
82
92
  * handle HTTP requests and return a JSON response.
83
93
  */
84
- export type Handler<
85
- Ctx extends FlinkContext,
86
- ReqSchema = any,
87
- ResSchema = any,
88
- P extends Params = Params,
89
- Q extends Query = Query
90
- > = (props: {
91
- req: FlinkRequest<ReqSchema, P, Q>;
92
- ctx: Ctx;
93
- origin?: string;
94
+ export type Handler<Ctx extends FlinkContext, ReqSchema = any, ResSchema = any, P extends Params = Params, Q extends Query = Query> = (props: {
95
+ req: FlinkRequest<ReqSchema, P, Q>;
96
+ ctx: Ctx;
97
+ origin?: string;
94
98
  }) => Promise<FlinkResponse<ResSchema | FlinkError>>;
95
99
 
96
100
  /**
@@ -99,12 +103,7 @@ export type Handler<
99
103
  *
100
104
  * Just syntactic sugar on top op `HandlerFn`
101
105
  */
102
- export type GetHandler<
103
- Ctx extends FlinkContext,
104
- ResSchema = any,
105
- P extends Params = Params,
106
- Q extends Query = Query
107
- > = Handler<Ctx, any, ResSchema, P, Q>;
106
+ export type GetHandler<Ctx extends FlinkContext, ResSchema = any, P extends Params = Params, Q extends Query = Query> = Handler<Ctx, any, ResSchema, P, Q>;
108
107
 
109
108
  /**
110
109
  * Type for Handler file. Describes shape of exports when using
@@ -113,32 +112,32 @@ export type GetHandler<
113
112
  * `import * as FooHandler from "./src/handlers/FooHandler"
114
113
  */
115
114
  export type HandlerFile = {
116
- default: Handler<any, any, any, any, any>;
117
- Route?: RouteProps;
118
- /**
119
- * Name of schemas, is set at compile time by Flink compiler.
120
- */
121
- __schemas?: {
122
- reqSchema?: JSONSchema;
123
- resSchema?: JSONSchema;
124
- };
125
- /**
126
- * Typescript source file name, is set at compile time by Flink compiler.
127
- */
128
- __file?: string;
129
-
130
- /**
131
- * Description of query params, is set at compile time by Flink compiler.
132
- */
133
- __query?: QueryParamMetadata[];
134
-
135
- /**
136
- * Description of path params, is set at compile time by Flink compiler.
137
- */
138
- __params?: QueryParamMetadata[];
115
+ default: Handler<any, any, any, any, any>;
116
+ Route?: RouteProps;
117
+ /**
118
+ * Name of schemas, is set at compile time by Flink compiler.
119
+ */
120
+ __schemas?: {
121
+ reqSchema?: JSONSchema;
122
+ resSchema?: JSONSchema;
123
+ };
124
+ /**
125
+ * Typescript source file name, is set at compile time by Flink compiler.
126
+ */
127
+ __file?: string;
128
+
129
+ /**
130
+ * Description of query params, is set at compile time by Flink compiler.
131
+ */
132
+ __query?: QueryParamMetadata[];
133
+
134
+ /**
135
+ * Description of path params, is set at compile time by Flink compiler.
136
+ */
137
+ __params?: QueryParamMetadata[];
139
138
  };
140
139
 
141
140
  export type QueryParamMetadata = {
142
- name: string;
143
- description: string;
141
+ name: string;
142
+ description: string;
144
143
  };
package/src/FlinkRepo.ts CHANGED
@@ -82,7 +82,14 @@ export abstract class FlinkRepo<C extends FlinkContext, Model extends Document>
82
82
  return deletedCount || 0;
83
83
  }
84
84
 
85
- private buildId(id: string | ObjectId) {
85
+ /**
86
+ * Helper to ensure the id is always an ObjectId.
87
+ * If a string is passed, it will be converted to an ObjectId.
88
+ * If an ObjectId is passed, it will be returned as is.
89
+ * @param id
90
+ * @returns
91
+ */
92
+ buildId(id: string | ObjectId) {
86
93
  let oid: ObjectId | string;
87
94
 
88
95
  if (typeof id === "string") {
@@ -1,48 +1,45 @@
1
1
  import { Response, Request } from "express";
2
2
 
3
3
  export interface FlinkResponse<T = any> {
4
- /**
5
- * Unique id of request.
6
- * Used to track request in logs.
7
- */
8
- reqId?: string;
4
+ /**
5
+ * Unique id of request.
6
+ * Used to track request in logs.
7
+ */
8
+ reqId?: string;
9
9
 
10
- /**
11
- * HTTP status code, default to 200.
12
- */
13
- status?: number;
10
+ /**
11
+ * HTTP status code, default to 200.
12
+ */
13
+ status?: number;
14
14
 
15
- /**
16
- * Optional redirect. Will trigger a redirect of provided type.
17
- */
18
- redirect?: {
19
- to: string;
20
- type?: "TEMPORARY" | "PERMANENT";
21
- };
15
+ /**
16
+ * Optional redirect. Will trigger a redirect of provided type.
17
+ */
18
+ redirect?: {
19
+ to: string;
20
+ type?: "TEMPORARY" | "PERMANENT";
21
+ };
22
22
 
23
- /**
24
- * Actual payload to return.
25
- */
26
- data?: T;
23
+ /**
24
+ * Actual payload to return.
25
+ */
26
+ data?: T;
27
27
 
28
- /**
29
- * Error object set if error response.
30
- * If set the `status` is set to 4xx or 5xx code.
31
- */
32
- error?: {
33
- id: string;
34
- title: string;
35
- detail?: string;
36
- code?: string;
37
- };
28
+ /**
29
+ * Error object set if error response.
30
+ * If set the `status` is set to 4xx or 5xx code.
31
+ */
32
+ error?: {
33
+ id: string;
34
+ title: string;
35
+ detail?: string;
36
+ code?: string;
37
+ };
38
38
 
39
- /**
40
- * HTTP headers, names are lower cased
41
- */
42
- headers?: {
43
- [x: string]: string;
44
- };
39
+ /**
40
+ * HTTP headers, names are lower cased
41
+ */
42
+ headers?: {
43
+ [x: string]: string;
44
+ };
45
45
  }
46
-
47
- export type ExpressResponse = Response;
48
- export type ExpressRequest = Request;