@restura/core 1.5.0 → 1.6.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.
package/dist/index.d.ts CHANGED
@@ -81,143 +81,6 @@ declare class RsError<T extends Record<string, unknown> = Record<string, unknown
81
81
  static isRsError(error: unknown): error is RsError;
82
82
  }
83
83
 
84
- interface SchemaChangeValue {
85
- name: string;
86
- changeType: 'NEW' | 'MODIFIED' | 'DELETED';
87
- }
88
- interface SchemaPreview {
89
- commands: string;
90
- endPoints: SchemaChangeValue[];
91
- globalParams: SchemaChangeValue[];
92
- roles: SchemaChangeValue[];
93
- customTypes: boolean;
94
- }
95
- type StandardOrderTypes = 'ASC' | 'DESC' | 'RAND' | 'NONE';
96
- type ConjunctionTypes = 'AND' | 'OR';
97
- type MatchTypes = 'exact' | 'fuzzy' | 'like' | 'greaterThan' | 'greaterThanEqual' | 'lessThan' | 'lessThanEqual';
98
- interface RsResponseData<T> {
99
- data: T;
100
- }
101
- interface RsErrorData {
102
- err: string;
103
- msg: string;
104
- stack?: string;
105
- }
106
- interface RsPagedResponseData<T> extends RsResponseData<T> {
107
- total: number;
108
- }
109
- interface PageQuery {
110
- page: number;
111
- perPage: number;
112
- sortBy: string;
113
- sortOrder: StandardOrderTypes;
114
- filter?: string;
115
- [key: string]: string | number | boolean | object | null | undefined;
116
- }
117
- interface AuthenticatedRequesterDetails {
118
- role: string;
119
- scopes: string[];
120
- userId?: number;
121
- [key: string]: string | number | boolean | object | null | undefined;
122
- }
123
- type OnValidAuthenticationCallback = (authenticatedRequesterDetails: AuthenticatedRequesterDetails) => void;
124
- type AuthenticateHandler = (req: RsRequest<unknown>, res: RsResponse<unknown>, onValid: OnValidAuthenticationCallback) => Promise<void>;
125
-
126
- interface RsHeaders extends IncomingHttpHeaders {
127
- 'x-auth-token'?: string;
128
- }
129
- type ApiMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
130
- type RequesterDetails<T extends object = {}> = {
131
- role: string;
132
- scopes: string[];
133
- host: string;
134
- ipAddress: string;
135
- userId?: number;
136
- isSystemUser?: boolean;
137
- } & T;
138
- interface RsRequest<T = unknown, U extends object = Record<string, unknown>> extends express.Request {
139
- requesterDetails: RequesterDetails<U>;
140
- data: T;
141
- }
142
- type DynamicObject<T = unknown> = {
143
- [key: string]: T;
144
- };
145
- interface RsResponse<T = unknown> extends express.Response {
146
- sendData: (data: T, statusCode?: number) => void;
147
- sendNoWrap: (data: T, statusCode?: number) => void;
148
- sendError: (err: ErrorCode, msg: string, htmlStatusCode?: HtmlStatusCodes, stack?: string) => void;
149
- sendPaginated: (pagedData: RsPagedResponseData<T>, statusCode?: number) => void;
150
- _contentLength?: number;
151
- }
152
- type RsRouteHandler<T = unknown, U = unknown> = (req: RsRequest<T>, res: RsResponse<U>, next?: express.NextFunction) => Promise<void>;
153
- interface AsyncExpressApplication {
154
- get: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
155
- post: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
156
- put: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
157
- patch: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
158
- delete: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
159
- }
160
-
161
- type EventType = 'DATABASE_ROW_DELETE' | 'DATABASE_ROW_INSERT' | 'DATABASE_COLUMN_UPDATE' | 'WEBHOOK';
162
- type MutationType = 'INSERT' | 'UPDATE' | 'DELETE';
163
- interface SqlMutationData {
164
- mutationType: MutationType;
165
- queryMetadata: QueryMetadata;
166
- }
167
- interface DatabaseActionData {
168
- tableName: string;
169
- queryMetadata: QueryMetadata;
170
- }
171
- interface ActionRowInsertData<T = DynamicObject> extends DatabaseActionData {
172
- insertedId: number;
173
- insertObject: T;
174
- }
175
- interface ActionRowDeleteData<T = DynamicObject> extends DatabaseActionData {
176
- deletedId: number;
177
- deletedRow: T;
178
- }
179
- interface ActionColumnChangeData<T = DynamicObject> extends DatabaseActionData {
180
- tableName: string;
181
- changedId: number;
182
- newData: T;
183
- oldData: T;
184
- }
185
- interface ActionRowInsertFilter {
186
- tableName: string;
187
- }
188
- interface ActionRowDeleteFilter {
189
- tableName: string;
190
- }
191
- interface ActionColumnChangeFilter {
192
- tableName: string;
193
- columns: string[];
194
- }
195
- type TriggerResult = {
196
- table: string;
197
- insertedId?: number;
198
- changedId?: number;
199
- deletedId?: number;
200
- queryMetadata: QueryMetadata;
201
- record: DynamicObject;
202
- previousRecord: DynamicObject;
203
- requesterId: number;
204
- };
205
- type QueryMetadata = RequesterDetails & {
206
- connectionInstanceId: UUID;
207
- };
208
- declare class EventManager {
209
- private actionHandlers;
210
- addRowInsertHandler<T>(onInsert: (data: ActionRowInsertData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter?: ActionRowInsertFilter): void;
211
- addColumnChangeHandler<T>(onUpdate: (data: ActionColumnChangeData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter: ActionColumnChangeFilter): void;
212
- addRowDeleteHandler<T>(onDelete: (data: ActionRowDeleteData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter?: ActionRowDeleteFilter): void;
213
- fireActionFromDbTrigger(sqlMutationData: SqlMutationData, result: TriggerResult): Promise<void>;
214
- private fireInsertActions;
215
- private fireDeleteActions;
216
- private fireUpdateActions;
217
- private hasHandlersForEventType;
218
- }
219
- declare const eventManager: EventManager;
220
-
221
84
  declare const whereDataSchema: z.ZodObject<{
222
85
  tableName: z.ZodOptional<z.ZodString>;
223
86
  columnName: z.ZodOptional<z.ZodString>;
@@ -333,6 +196,10 @@ declare const standardRouteSchema: z.ZodObject<{
333
196
  name: z.ZodString;
334
197
  description: z.ZodString;
335
198
  path: z.ZodString;
199
+ deprecation: z.ZodOptional<z.ZodObject<{
200
+ date: z.ZodISODateTime;
201
+ message: z.ZodOptional<z.ZodString>;
202
+ }, z.core.$strip>>;
336
203
  roles: z.ZodArray<z.ZodString>;
337
204
  scopes: z.ZodArray<z.ZodString>;
338
205
  type: z.ZodEnum<{
@@ -485,6 +352,10 @@ declare const customRouteSchema: z.ZodObject<{
485
352
  name: z.ZodString;
486
353
  description: z.ZodString;
487
354
  path: z.ZodString;
355
+ deprecation: z.ZodOptional<z.ZodObject<{
356
+ date: z.ZodISODateTime;
357
+ message: z.ZodOptional<z.ZodString>;
358
+ }, z.core.$strip>>;
488
359
  roles: z.ZodArray<z.ZodString>;
489
360
  scopes: z.ZodArray<z.ZodString>;
490
361
  type: z.ZodEnum<{
@@ -596,6 +467,7 @@ declare const tableDataSchema: z.ZodObject<{
596
467
  ASC: "ASC";
597
468
  DESC: "DESC";
598
469
  }>;
470
+ where: z.ZodOptional<z.ZodString>;
599
471
  }, z.core.$strict>>;
600
472
  foreignKeys: z.ZodArray<z.ZodObject<{
601
473
  name: z.ZodString;
@@ -704,6 +576,7 @@ declare const resturaSchema: z.ZodObject<{
704
576
  ASC: "ASC";
705
577
  DESC: "DESC";
706
578
  }>;
579
+ where: z.ZodOptional<z.ZodString>;
707
580
  }, z.core.$strict>>;
708
581
  foreignKeys: z.ZodArray<z.ZodObject<{
709
582
  name: z.ZodString;
@@ -748,6 +621,10 @@ declare const resturaSchema: z.ZodObject<{
748
621
  name: z.ZodString;
749
622
  description: z.ZodString;
750
623
  path: z.ZodString;
624
+ deprecation: z.ZodOptional<z.ZodObject<{
625
+ date: z.ZodISODateTime;
626
+ message: z.ZodOptional<z.ZodString>;
627
+ }, z.core.$strip>>;
751
628
  roles: z.ZodArray<z.ZodString>;
752
629
  scopes: z.ZodArray<z.ZodString>;
753
630
  type: z.ZodEnum<{
@@ -898,6 +775,10 @@ declare const resturaSchema: z.ZodObject<{
898
775
  name: z.ZodString;
899
776
  description: z.ZodString;
900
777
  path: z.ZodString;
778
+ deprecation: z.ZodOptional<z.ZodObject<{
779
+ date: z.ZodISODateTime;
780
+ message: z.ZodOptional<z.ZodString>;
781
+ }, z.core.$strip>>;
901
782
  roles: z.ZodArray<z.ZodString>;
902
783
  scopes: z.ZodArray<z.ZodString>;
903
784
  type: z.ZodEnum<{
@@ -939,6 +820,144 @@ declare const resturaSchema: z.ZodObject<{
939
820
  type ResturaSchema = z.infer<typeof resturaSchema>;
940
821
  declare function isSchemaValid(schemaToCheck: unknown): Promise<boolean>;
941
822
 
823
+ interface SchemaChangeValue {
824
+ name: string;
825
+ changeType: 'NEW' | 'MODIFIED' | 'DELETED';
826
+ }
827
+ interface SchemaPreview {
828
+ commands: string;
829
+ endPoints: SchemaChangeValue[];
830
+ globalParams: SchemaChangeValue[];
831
+ roles: SchemaChangeValue[];
832
+ customTypes: boolean;
833
+ }
834
+ type StandardOrderTypes = 'ASC' | 'DESC' | 'RAND' | 'NONE';
835
+ type ConjunctionTypes = 'AND' | 'OR';
836
+ type MatchTypes = 'exact' | 'fuzzy' | 'like' | 'greaterThan' | 'greaterThanEqual' | 'lessThan' | 'lessThanEqual';
837
+ interface RsResponseData<T> {
838
+ data: T;
839
+ }
840
+ interface RsErrorData {
841
+ err: string;
842
+ msg: string;
843
+ stack?: string;
844
+ }
845
+ interface RsPagedResponseData<T> extends RsResponseData<T> {
846
+ total: number;
847
+ }
848
+ interface PageQuery {
849
+ page: number;
850
+ perPage: number;
851
+ sortBy: string;
852
+ sortOrder: StandardOrderTypes;
853
+ filter?: string;
854
+ [key: string]: string | number | boolean | object | null | undefined;
855
+ }
856
+ interface AuthenticatedRequesterDetails {
857
+ role: string;
858
+ scopes: string[];
859
+ userId?: number;
860
+ [key: string]: string | number | boolean | object | null | undefined;
861
+ }
862
+ type OnValidAuthenticationCallback = (authenticatedRequesterDetails: AuthenticatedRequesterDetails) => void;
863
+ type AuthenticateHandler = (req: RsRequest<unknown>, res: RsResponse<unknown>, onValid: OnValidAuthenticationCallback) => Promise<void>;
864
+
865
+ interface RsHeaders extends IncomingHttpHeaders {
866
+ 'x-auth-token'?: string;
867
+ }
868
+ type ApiMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
869
+ type RequesterDetails<T extends object = {}> = {
870
+ role: string;
871
+ scopes: string[];
872
+ host: string;
873
+ ipAddress: string;
874
+ userId?: number;
875
+ isSystemUser?: boolean;
876
+ } & T;
877
+ interface RsRequest<T = unknown, U extends object = Record<string, unknown>> extends express.Request {
878
+ requesterDetails: RequesterDetails<U>;
879
+ data: T;
880
+ routeData?: RouteData;
881
+ }
882
+ type DynamicObject<T = unknown> = {
883
+ [key: string]: T;
884
+ };
885
+ interface RsResponse<T = unknown> extends express.Response {
886
+ sendData: (data: T, statusCode?: number) => void;
887
+ sendNoWrap: (data: T, statusCode?: number) => void;
888
+ sendError: (err: ErrorCode, msg: string, htmlStatusCode?: HtmlStatusCodes, stack?: string) => void;
889
+ sendPaginated: (pagedData: RsPagedResponseData<T>, statusCode?: number) => void;
890
+ _contentLength?: number;
891
+ }
892
+ type RsRouteHandler<T = unknown, U = unknown> = (req: RsRequest<T>, res: RsResponse<U>, next?: express.NextFunction) => Promise<void>;
893
+ interface AsyncExpressApplication {
894
+ get: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
895
+ post: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
896
+ put: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
897
+ patch: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
898
+ delete: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise<void> | void;
899
+ }
900
+
901
+ type EventType = 'DATABASE_ROW_DELETE' | 'DATABASE_ROW_INSERT' | 'DATABASE_COLUMN_UPDATE' | 'WEBHOOK';
902
+ type MutationType = 'INSERT' | 'UPDATE' | 'DELETE';
903
+ interface SqlMutationData {
904
+ mutationType: MutationType;
905
+ queryMetadata: QueryMetadata;
906
+ }
907
+ interface DatabaseActionData {
908
+ tableName: string;
909
+ queryMetadata: QueryMetadata;
910
+ }
911
+ interface ActionRowInsertData<T = DynamicObject> extends DatabaseActionData {
912
+ insertedId: number;
913
+ insertObject: T;
914
+ }
915
+ interface ActionRowDeleteData<T = DynamicObject> extends DatabaseActionData {
916
+ deletedId: number;
917
+ deletedRow: T;
918
+ }
919
+ interface ActionColumnChangeData<T = DynamicObject> extends DatabaseActionData {
920
+ tableName: string;
921
+ changedId: number;
922
+ newData: T;
923
+ oldData: T;
924
+ }
925
+ interface ActionRowInsertFilter {
926
+ tableName: string;
927
+ }
928
+ interface ActionRowDeleteFilter {
929
+ tableName: string;
930
+ }
931
+ interface ActionColumnChangeFilter {
932
+ tableName: string;
933
+ columns: string[];
934
+ }
935
+ type TriggerResult = {
936
+ table: string;
937
+ insertedId?: number;
938
+ changedId?: number;
939
+ deletedId?: number;
940
+ queryMetadata: QueryMetadata;
941
+ record: DynamicObject;
942
+ previousRecord: DynamicObject;
943
+ requesterId: number;
944
+ };
945
+ type QueryMetadata = RequesterDetails & {
946
+ connectionInstanceId: UUID;
947
+ };
948
+ declare class EventManager {
949
+ private actionHandlers;
950
+ addRowInsertHandler<T>(onInsert: (data: ActionRowInsertData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter?: ActionRowInsertFilter): void;
951
+ addColumnChangeHandler<T>(onUpdate: (data: ActionColumnChangeData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter: ActionColumnChangeFilter): void;
952
+ addRowDeleteHandler<T>(onDelete: (data: ActionRowDeleteData<T>, queryMetadata: QueryMetadata) => Promise<void>, filter?: ActionRowDeleteFilter): void;
953
+ fireActionFromDbTrigger(sqlMutationData: SqlMutationData, result: TriggerResult): Promise<void>;
954
+ private fireInsertActions;
955
+ private fireDeleteActions;
956
+ private fireUpdateActions;
957
+ private hasHandlersForEventType;
958
+ }
959
+ declare const eventManager: EventManager;
960
+
942
961
  declare function apiGenerator(schema: ResturaSchema): Promise<string>;
943
962
 
944
963
  declare function modelGenerator(schema: ResturaSchema): Promise<string>;
@@ -1049,6 +1068,7 @@ declare class ResturaEngine {
1049
1068
  private getSchema;
1050
1069
  private getSchemaAndTypes;
1051
1070
  private getMulterFilesIfAny;
1071
+ private attachRouteData;
1052
1072
  private executeRouteLogic;
1053
1073
  private isCustomRoute;
1054
1074
  private runCustomRouteLogic;
package/dist/index.js CHANGED
@@ -1300,6 +1300,18 @@ function addApiResponseFunctions(req, res, next) {
1300
1300
  next();
1301
1301
  }
1302
1302
 
1303
+ // src/restura/middleware/addDeprecationResponse.ts
1304
+ function addDeprecationResponse(req, res, next) {
1305
+ const deprecation = req.routeData?.deprecation;
1306
+ if (deprecation) {
1307
+ const { date, message } = deprecation;
1308
+ const dateObject = new Date(date);
1309
+ res.set("Deprecation", `@${dateObject.getTime().toString()}`);
1310
+ res.set("Deprecation-Message", message ?? "This endpoint is deprecated and will be removed in the future.");
1311
+ }
1312
+ next();
1313
+ }
1314
+
1303
1315
  // src/restura/middleware/authenticateRequester.ts
1304
1316
  function authenticateRequester(applicationAuthenticateHandler) {
1305
1317
  return (req, res, next) => {
@@ -1422,6 +1434,10 @@ var routeDataBaseSchema = z3.object({
1422
1434
  name: z3.string(),
1423
1435
  description: z3.string(),
1424
1436
  path: z3.string(),
1437
+ deprecation: z3.object({
1438
+ date: z3.iso.datetime(),
1439
+ message: z3.string().optional()
1440
+ }).optional(),
1425
1441
  roles: z3.array(z3.string()),
1426
1442
  scopes: z3.array(z3.string())
1427
1443
  }).strict();
@@ -1574,7 +1590,8 @@ var indexDataSchema = z3.object({
1574
1590
  columns: z3.array(z3.string()),
1575
1591
  isUnique: z3.boolean(),
1576
1592
  isPrimaryKey: z3.boolean(),
1577
- order: z3.enum(["ASC", "DESC"])
1593
+ order: z3.enum(["ASC", "DESC"]),
1594
+ where: z3.string().optional()
1578
1595
  }).strict();
1579
1596
  var foreignKeyActionsSchema = z3.enum([
1580
1597
  "CASCADE",
@@ -1969,8 +1986,7 @@ var PsqlConnection = class {
1969
1986
  if ("isSystemUser" in queryMetadata && queryMetadata.isSystemUser) initiator = "SYSTEM";
1970
1987
  logger.silly(`${prefix}query by ${initiator}, Query ->
1971
1988
  ${formattedSql}`, {
1972
- duration: `${durationMs.toFixed(2)}ms`,
1973
- _meta: { durationNs: nanoseconds }
1989
+ durationMs
1974
1990
  });
1975
1991
  }
1976
1992
  };
@@ -2410,11 +2426,11 @@ var PsqlEngine = class extends SqlEngine {
2410
2426
  if (!index.isPrimaryKey) {
2411
2427
  let unique = " ";
2412
2428
  if (index.isUnique) unique = "UNIQUE ";
2413
- indexes.push(
2414
- ` CREATE ${unique}INDEX "${index.name}" ON "${table.name}" (${index.columns.map((item) => {
2415
- return `"${item}" ${index.order}`;
2416
- }).join(", ")});`
2417
- );
2429
+ let indexSQL = ` CREATE ${unique}INDEX "${index.name}" ON "${table.name}"`;
2430
+ indexSQL += ` (${index.columns.map((item) => `"${item}" ${index.order}`).join(", ")})`;
2431
+ indexSQL += index.where ? ` WHERE ${index.where}` : "";
2432
+ indexSQL += ";";
2433
+ indexes.push(indexSQL);
2418
2434
  }
2419
2435
  }
2420
2436
  sql += "\n);";
@@ -3290,6 +3306,8 @@ var ResturaEngine = class {
3290
3306
  this.resturaRouter[route.method.toLowerCase()](
3291
3307
  route.path,
3292
3308
  // <-- Notice we only use path here since the baseUrl is already added to the router.
3309
+ this.attachRouteData,
3310
+ addDeprecationResponse,
3293
3311
  this.executeRouteLogic
3294
3312
  );
3295
3313
  routeCount++;
@@ -3367,9 +3385,17 @@ var ResturaEngine = class {
3367
3385
  });
3368
3386
  });
3369
3387
  }
3388
+ attachRouteData(req, _res, next) {
3389
+ try {
3390
+ req.routeData = this.getRouteData(req.method, req.baseUrl, req.path);
3391
+ next();
3392
+ } catch (e) {
3393
+ next(e);
3394
+ }
3395
+ }
3370
3396
  async executeRouteLogic(req, res, next) {
3371
3397
  try {
3372
- const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
3398
+ const routeData = req.routeData ?? this.getRouteData(req.method, req.baseUrl, req.path);
3373
3399
  this.validateAuthorization(req, routeData);
3374
3400
  await this.getMulterFilesIfAny(req, res, routeData);
3375
3401
  requestValidator(
@@ -3491,6 +3517,9 @@ __decorateClass([
3491
3517
  __decorateClass([
3492
3518
  boundMethod
3493
3519
  ], ResturaEngine.prototype, "getMulterFilesIfAny", 1);
3520
+ __decorateClass([
3521
+ boundMethod
3522
+ ], ResturaEngine.prototype, "attachRouteData", 1);
3494
3523
  __decorateClass([
3495
3524
  boundMethod
3496
3525
  ], ResturaEngine.prototype, "executeRouteLogic", 1);