prostgles-server 2.0.144 → 2.0.147

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 (77) hide show
  1. package/dist/AuthHandler.d.ts +15 -13
  2. package/dist/AuthHandler.d.ts.map +1 -1
  3. package/dist/AuthHandler.js +41 -43
  4. package/dist/AuthHandler.js.map +1 -1
  5. package/dist/DBEventsManager.d.ts +6 -5
  6. package/dist/DBEventsManager.d.ts.map +1 -1
  7. package/dist/DBEventsManager.js +8 -2
  8. package/dist/DBEventsManager.js.map +1 -1
  9. package/dist/DboBuilder.d.ts +62 -50
  10. package/dist/DboBuilder.d.ts.map +1 -1
  11. package/dist/DboBuilder.js +229 -170
  12. package/dist/DboBuilder.js.map +1 -1
  13. package/dist/FileManager.d.ts +5 -5
  14. package/dist/FileManager.d.ts.map +1 -1
  15. package/dist/FileManager.js +48 -21
  16. package/dist/FileManager.js.map +1 -1
  17. package/dist/Filtering.d.ts.map +1 -1
  18. package/dist/Filtering.js +11 -9
  19. package/dist/Filtering.js.map +1 -1
  20. package/dist/PostgresNotifListenManager.d.ts +3 -3
  21. package/dist/PostgresNotifListenManager.d.ts.map +1 -1
  22. package/dist/PostgresNotifListenManager.js +7 -5
  23. package/dist/PostgresNotifListenManager.js.map +1 -1
  24. package/dist/Prostgles.d.ts +6 -9
  25. package/dist/Prostgles.d.ts.map +1 -1
  26. package/dist/Prostgles.js +122 -83
  27. package/dist/Prostgles.js.map +1 -1
  28. package/dist/PubSubManager.d.ts +9 -9
  29. package/dist/PubSubManager.d.ts.map +1 -1
  30. package/dist/PubSubManager.js +10 -9
  31. package/dist/PubSubManager.js.map +1 -1
  32. package/dist/QueryBuilder.d.ts +26 -8
  33. package/dist/QueryBuilder.d.ts.map +1 -1
  34. package/dist/QueryBuilder.js +114 -46
  35. package/dist/QueryBuilder.js.map +1 -1
  36. package/dist/SyncReplication.d.ts +1 -1
  37. package/dist/SyncReplication.d.ts.map +1 -1
  38. package/dist/SyncReplication.js +31 -29
  39. package/dist/SyncReplication.js.map +1 -1
  40. package/dist/TableConfig.d.ts +0 -1
  41. package/dist/TableConfig.d.ts.map +1 -1
  42. package/dist/TableConfig.js +25 -17
  43. package/dist/TableConfig.js.map +1 -1
  44. package/dist/shortestPath.d.ts.map +1 -1
  45. package/dist/shortestPath.js.map +1 -1
  46. package/dist/utils.d.ts +2 -0
  47. package/dist/utils.d.ts.map +1 -1
  48. package/dist/utils.js +6 -0
  49. package/dist/utils.js.map +1 -1
  50. package/lib/AuthHandler.ts +50 -40
  51. package/lib/DBEventsManager.ts +14 -7
  52. package/lib/DboBuilder.ts +265 -199
  53. package/lib/FileManager.ts +30 -21
  54. package/lib/Filtering.ts +19 -16
  55. package/lib/PostgresNotifListenManager.ts +11 -10
  56. package/lib/Prostgles.ts +89 -73
  57. package/lib/PubSubManager.ts +13 -11
  58. package/lib/QueryBuilder.ts +135 -54
  59. package/lib/SyncReplication.ts +10 -10
  60. package/lib/TableConfig.ts +23 -15
  61. package/lib/shortestPath.ts +6 -4
  62. package/lib/utils.ts +7 -1
  63. package/package.json +7 -8
  64. package/tests/client/PID.txt +1 -1
  65. package/tests/client/index.js +10 -7
  66. package/tests/client/index.ts +12 -8
  67. package/tests/client/package-lock.json +14 -14
  68. package/tests/client/package.json +2 -2
  69. package/tests/client/tsconfig.json +2 -2
  70. package/tests/client_only_queries.js +127 -104
  71. package/tests/client_only_queries.ts +43 -17
  72. package/tests/isomorphic_queries.js +44 -6
  73. package/tests/isomorphic_queries.ts +42 -6
  74. package/tests/server/package-lock.json +27 -29
  75. package/tests/server/package.json +2 -2
  76. package/tests/server/tsconfig.json +2 -2
  77. package/tsconfig.json +3 -2
package/lib/DboBuilder.ts CHANGED
@@ -11,7 +11,8 @@ import * as pgPromise from 'pg-promise';
11
11
  const {ParameterizedQuery: PQ} = require('pg-promise');
12
12
  import pg = require('pg-promise/typescript/pg-subset');
13
13
  import {
14
- ColumnInfo, ValidatedColumnInfo, FieldFilter, SelectParams, SubscribeParams, OrderBy, InsertParams, UpdateParams, DeleteParams, SQLOptions,
14
+ ColumnInfo, ValidatedColumnInfo, FieldFilter, SelectParams, SubscribeParams,
15
+ OrderBy, InsertParams, UpdateParams, DeleteParams, SQLOptions,
15
16
  DbJoinMaker,
16
17
  unpatchText,
17
18
  isEmpty,
@@ -21,6 +22,9 @@ import {
21
22
  TableInfo as TInfo,
22
23
  SQLHandler,
23
24
  AnyObject,
25
+ SQLResult,
26
+ Select,
27
+ JoinMaker,
24
28
  } from "prostgles-types";
25
29
 
26
30
  export type Media = {
@@ -69,10 +73,10 @@ export type DbHandler = {
69
73
  // [key: string]: TX
70
74
  // }>
71
75
 
72
- import { get } from "./utils";
73
- import { getNewQuery, makeQuery, COMPUTED_FIELDS, SelectItem, FieldSpec, asNameAlias, SelectItemBuilder, FUNCTIONS } from "./QueryBuilder";
76
+ import { get, isDefined, isObject } from "./utils";
77
+ import { getNewQuery, makeQuery, COMPUTED_FIELDS, SelectItem, FieldSpec, asNameAlias, SelectItemBuilder, FUNCTIONS, parseFunction, parseFunctionObject } from "./QueryBuilder";
74
78
  import {
75
- DB, TableRule, SelectRule, InsertRule, UpdateRule, DeleteRule, SyncRule, Joins, Join, Prostgles, PublishParser, flat, ValidateRow
79
+ DB, TableRule, SelectRule, InsertRule, UpdateRule, DeleteRule, SyncRule, Joins, Join, Prostgles, PublishParser, ValidateRow
76
80
  } from "./Prostgles";
77
81
  import { PubSubManager, filterObj, asValue, BasicCallback } from "./PubSubManager";
78
82
 
@@ -108,8 +112,16 @@ export type PRGLIOSocket = {
108
112
  headers?: { cookie?: string; } // e.g.: "some_arg=dwdaw; otherarg=23232"
109
113
  }
110
114
 
115
+ readonly on: (channel: string, params: any, cb?: (err: any, res?: any) => void) => Promise<void>;
116
+
111
117
  readonly emit: (channel: string, message: any, cb?: BasicCallback) => any;
112
118
 
119
+ readonly once: (channel: string, cb: (_data: any, cb: BasicCallback) => void) => void;
120
+
121
+ readonly removeAllListeners: (channel: string) => void;
122
+
123
+ readonly disconnect: () => void;
124
+
113
125
  /** Used for session caching */
114
126
  __prglCache?: {
115
127
  session: BasicSession;
@@ -117,6 +129,8 @@ export type PRGLIOSocket = {
117
129
  clientUser: AnyObject;
118
130
  }
119
131
 
132
+ _user?: AnyObject
133
+
120
134
  /** Used for publish error caching */
121
135
  prostgles?: AnyObject;
122
136
  };
@@ -361,7 +375,7 @@ class ColSet {
361
375
  this.opts = { columns, tableName, colNames: columns.map(c => c.name) }
362
376
  }
363
377
 
364
- private async getRow(data: any, allowedCols: string[], validate: ValidateRow): Promise<{ escapedCol: string; escapedVal: string }[]> {
378
+ private async getRow(data: any, allowedCols: string[], validate?: ValidateRow): Promise<{ escapedCol: string; escapedVal: string }[]> {
365
379
  const badCol = allowedCols.find(c => !this.opts.colNames.includes(c))
366
380
  if(!allowedCols || badCol){
367
381
  throw "Missing or unexpected columns: " + badCol;
@@ -387,7 +401,7 @@ class ColSet {
387
401
  let escapedVal: string;
388
402
  if(["geometry", "geography"].includes(col.udt_name) && row[key] && isPlainObject(row[key])){
389
403
 
390
- const basicFunc = (args) => {
404
+ const basicFunc = (args: any[]) => {
391
405
  return args.map(arg => asValue(arg)).join(", ")
392
406
  }
393
407
  const basicFuncNames = ["ST_GeomFromText", "ST_Point", "ST_MakePoint", "ST_MakePointM", "ST_PointFromText", "ST_GeomFromEWKT", "ST_GeomFromGeoJSON"]
@@ -413,7 +427,7 @@ class ColSet {
413
427
 
414
428
  }
415
429
 
416
- async getInsertQuery(data: any[], allowedCols: string[], validate: ValidateRow): Promise<string> {
430
+ async getInsertQuery(data: any[], allowedCols: string[], validate: ValidateRow | undefined): Promise<string> {
417
431
  const res = (await Promise.all((Array.isArray(data)? data : [data]).map(async d => {
418
432
  const rowParts = await this.getRow(d, allowedCols, validate);
419
433
  const select = rowParts.map(r => r.escapedCol).join(", "),
@@ -423,7 +437,7 @@ class ColSet {
423
437
  }))).join(";\n") + " ";
424
438
  return res;
425
439
  }
426
- async getUpdateQuery(data: any[], allowedCols: string[], validate: ValidateRow): Promise<string> {
440
+ async getUpdateQuery(data: any[], allowedCols: string[], validate: ValidateRow | undefined): Promise<string> {
427
441
  const res = (await Promise.all((Array.isArray(data)? data : [data]).map(async d => {
428
442
  const rowParts = await this.getRow(d, allowedCols, validate);
429
443
  return `UPDATE ${asName(this.opts.tableName)} SET ` + rowParts.map(r => `${r.escapedCol} = ${r.escapedVal} `).join(",\n")
@@ -483,7 +497,7 @@ export class ViewHandler {
483
497
 
484
498
  // this.pubSubManager = pubSubManager;
485
499
  this.dboBuilder = dboBuilder;
486
- this.joins = this.dboBuilder.joins;
500
+ this.joins = this.dboBuilder.joins ?? [];
487
501
 
488
502
  // fix this
489
503
  // and also make hot schema reload over ws
@@ -533,7 +547,7 @@ export class ViewHandler {
533
547
  `)` + (alias? ` as ${asName(alias)}` : "");
534
548
  }
535
549
 
536
- async validateViewRules(fields: FieldFilter, filterFields: FieldFilter, returningFields: FieldFilter, forcedFilter: object, rule: "update" | "select" | "insert" | "delete"){
550
+ async validateViewRules(fields: FieldFilter | undefined, filterFields: FieldFilter | undefined, returningFields: FieldFilter | undefined, forcedFilter: AnyObject | undefined, rule: "update" | "select" | "insert" | "delete"){
537
551
 
538
552
  /* Safely test publish rules */
539
553
  if(fields) {
@@ -932,7 +946,7 @@ export class ViewHandler {
932
946
 
933
947
  }
934
948
 
935
- async find(filter?: Filter, selectParams?: SelectParams , param3_unused = null, tableRules?: TableRule, localParams?: LocalParams): Promise<any[]>{
949
+ async find(filter?: Filter, selectParams?: SelectParams , param3_unused?: never, tableRules?: TableRule, localParams?: LocalParams): Promise<any[]>{
936
950
  try {
937
951
  filter = filter || {};
938
952
  const allowedReturnTypes: Array<SelectParams["returnType"]> = ["row", "value", "values"]
@@ -954,9 +968,9 @@ export class ViewHandler {
954
968
  if(tableRules){
955
969
 
956
970
  let fields: FieldFilter,
957
- filterFields: FieldFilter,
958
- forcedFilter: object,
959
- maxLimit: number;
971
+ filterFields: FieldFilter | undefined,
972
+ forcedFilter: AnyObject | undefined,
973
+ maxLimit: number | undefined | null;
960
974
 
961
975
  if(!tableRules.select) throw "select rules missing for " + this.name;
962
976
  fields = tableRules.select.fields;
@@ -983,7 +997,7 @@ export class ViewHandler {
983
997
  }
984
998
 
985
999
  if(returnQuery) return (_query as unknown as any[]);
986
- if(["row", "value"].includes(returnType)) {
1000
+ if(["row", "value"].includes(returnType!)) {
987
1001
  return (this.t || this.db).oneOrNone(_query).then(data => {
988
1002
  return (data && returnType === "value")? Object.values(data)[0] : data;
989
1003
  }).catch(err => makeErr(err, localParams, this));
@@ -1003,26 +1017,26 @@ export class ViewHandler {
1003
1017
  }
1004
1018
  }
1005
1019
 
1006
- findOne(filter?: Filter, selectParams?: SelectParams, param3_unused?, table_rules?: TableRule, localParams?: LocalParams): Promise<any>{
1020
+ findOne(filter?: Filter, selectParams?: SelectParams, param3_unused?: never, table_rules?: TableRule, localParams?: LocalParams): Promise<any>{
1007
1021
 
1008
1022
  try {
1009
- const { select = "*", orderBy = null, offset = 0 } = selectParams || {};
1023
+ const { select = "*", orderBy, offset = 0 } = selectParams || {};
1010
1024
  if(selectParams){
1011
1025
  const good_params = ["select", "orderBy", "offset"];
1012
1026
  const bad_params = Object.keys(selectParams).filter(k => !good_params.includes(k));
1013
1027
  if(bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
1014
1028
  }
1015
- return this.find(filter, { select, orderBy, limit: 1, offset, returnType: "row" }, null, table_rules, localParams);
1029
+ return this.find(filter, { select, orderBy, limit: 1, offset, returnType: "row" }, undefined, table_rules, localParams);
1016
1030
  } catch(e){
1017
1031
  if(localParams && localParams.testRule) throw e;
1018
1032
  throw { err: parseError(e), msg: `Issue with dbo.${this.name}.findOne()` };
1019
1033
  }
1020
1034
  }
1021
1035
 
1022
- async count(filter?: Filter, param2_unused?, param3_unused?, table_rules?: TableRule, localParams: any = {}): Promise<number>{
1036
+ async count(filter?: Filter, param2_unused?: never, param3_unused?: never, table_rules?: TableRule, localParams: any = {}): Promise<number>{
1023
1037
  filter = filter || {};
1024
1038
  try {
1025
- return await this.find(filter, { select: "", limit: 0 }, null, table_rules, localParams)
1039
+ return await this.find(filter, { select: "", limit: 0 }, undefined, table_rules, localParams)
1026
1040
  .then(async allowed => {
1027
1041
  const { filterFields, forcedFilter } = get(table_rules, "select") || {};
1028
1042
  const where = (await this.prepareWhere({ filter, forcedFilter, filterFields, addKeywords: true, localParams, tableRule: table_rules }));
@@ -1035,10 +1049,10 @@ export class ViewHandler {
1035
1049
  }
1036
1050
  }
1037
1051
 
1038
- async size(filter?: Filter, selectParams?: SelectParams, param3_unused?, table_rules?: TableRule, localParams: any = {}): Promise<string>{
1052
+ async size(filter?: Filter, selectParams?: SelectParams, param3_unused?: never, table_rules?: TableRule, localParams: any = {}): Promise<string>{
1039
1053
  filter = filter || {};
1040
1054
  try {
1041
- return await this.find(filter, { ...selectParams, limit: 2 }, null, table_rules, localParams)
1055
+ return await this.find(filter, { ...selectParams, limit: 2 }, undefined, table_rules, localParams)
1042
1056
  .then(async _allowed => {
1043
1057
  // let rules: TableRule = table_rules || {};
1044
1058
  // rules.select.maxLimit = Number.MAX_SAFE_INTEGER;
@@ -1046,7 +1060,7 @@ export class ViewHandler {
1046
1060
 
1047
1061
  const q: string = await this.find(
1048
1062
  filter, { ...selectParams, limit: selectParams?.limit ?? Number.MAX_SAFE_INTEGER },
1049
- null,
1063
+ undefined,
1050
1064
  table_rules,
1051
1065
  { ...localParams, returnQuery: true }
1052
1066
  ) as any;
@@ -1068,7 +1082,7 @@ export class ViewHandler {
1068
1082
  getAllowedSelectFields(selectParams: FieldFilter = "*", allowed_cols: FieldFilter, allow_empty: boolean = true): string[] {
1069
1083
  let all_columns = this.column_names.slice(0),
1070
1084
  allowedFields = all_columns.slice(0),
1071
- resultFields = [];
1085
+ resultFields: string[] = [];
1072
1086
 
1073
1087
  if(selectParams){
1074
1088
  resultFields = this.parseFieldFilter(selectParams, allow_empty);
@@ -1121,27 +1135,27 @@ export class ViewHandler {
1121
1135
  * Parses group or simple filter
1122
1136
  */
1123
1137
  async prepareWhere(params: {
1124
- filter: Filter;
1138
+ filter?: Filter;
1125
1139
  select?: SelectItem[];
1126
- forcedFilter: object;
1127
- filterFields: FieldFilter;
1140
+ forcedFilter?: AnyObject;
1141
+ filterFields?: FieldFilter;
1128
1142
  addKeywords?: boolean;
1129
1143
  tableAlias?: string,
1130
- localParams: LocalParams,
1131
- tableRule: TableRule
1144
+ localParams: LocalParams | undefined,
1145
+ tableRule: TableRule | undefined
1132
1146
  }): Promise<string>
1133
1147
  {
1134
- const { filter, select, forcedFilter, filterFields: ff, addKeywords = true, tableAlias = null, localParams, tableRule } = params;
1148
+ const { filter, select, forcedFilter, filterFields: ff, addKeywords = true, tableAlias, localParams, tableRule } = params;
1135
1149
  const { $and: $and_key, $or: $or_key } = this.dboBuilder.prostgles.keywords;
1136
1150
 
1137
1151
  let filterFields = ff;
1138
1152
  /* Local update allow all. TODO -> FIX THIS */
1139
1153
  if(!ff && !tableRule) filterFields = "*";
1140
1154
 
1141
- const parseFullFilter = async (f: any, parentFilter: any = null) => {
1155
+ const parseFullFilter = async (f: any, parentFilter: any = null): Promise<string> => {
1142
1156
  if(!f) throw "Invalid/missing group filter provided";
1143
1157
  let result = "";
1144
- let keys = Object.keys(f);
1158
+ let keys = getKeys(f);
1145
1159
  if(!keys.length) return result;
1146
1160
  if((keys.includes($and_key) || keys.includes($or_key))){
1147
1161
  if(keys.length > 1) throw `\ngroup filter must contain only one array property. e.g.: { ${$and_key}: [...] } OR { ${$or_key}: [...] } `;
@@ -1149,7 +1163,7 @@ export class ViewHandler {
1149
1163
  }
1150
1164
 
1151
1165
  const { [$and_key]: $and, [$or_key]: $or } = f,
1152
- group = $and || $or;
1166
+ group: AnyObject[] = $and || $or;
1153
1167
 
1154
1168
  if(group && group.length){
1155
1169
  const operand = $and? " AND " : " OR ";
@@ -1175,7 +1189,7 @@ export class ViewHandler {
1175
1189
  if(!isPlainObject(filter)) throw "\nInvalid filter\nExpecting an object but got -> " + JSON.stringify(filter);
1176
1190
 
1177
1191
 
1178
- let _filter = { ... filter };
1192
+ let _filter = { ...filter };
1179
1193
  if(forcedFilter){
1180
1194
  _filter = {
1181
1195
  [$and_key]: [forcedFilter, _filter].filter(f => f)
@@ -1190,7 +1204,7 @@ export class ViewHandler {
1190
1204
  return cond || "";
1191
1205
  }
1192
1206
 
1193
- async prepareExistCondition(eConfig: ExistsFilterConfig, localParams: LocalParams, tableRules: TableRule): Promise<string> {
1207
+ async prepareExistCondition(eConfig: ExistsFilterConfig, localParams: LocalParams | undefined): Promise<string> {
1194
1208
  let res = "";
1195
1209
  const thisTable = this.name;
1196
1210
  const isNotExists = ["$notExists", "$notExistsJoined"].includes(eConfig.existType);
@@ -1214,11 +1228,11 @@ export class ViewHandler {
1214
1228
  let expectOne = true;
1215
1229
  tables.map((t2, depth) => {
1216
1230
  let t1 = depth? tables[depth - 1] : thisTable;
1217
- let exactPaths = [t1, t2];
1231
+ let exactPaths: string[] | undefined = [t1, t2];
1218
1232
 
1219
1233
  if(!depth && eConfig.shortestJoin) exactPaths = undefined;
1220
1234
  const jinf= this.getJoins(t1, t2, exactPaths, true);
1221
- expectOne = expectOne && jinf.expectOne
1235
+ expectOne = Boolean(expectOne && jinf.expectOne)
1222
1236
  joinPaths = joinPaths.concat(jinf.paths);
1223
1237
  });
1224
1238
 
@@ -1229,7 +1243,7 @@ export class ViewHandler {
1229
1243
  const { paths } = joinInfo;
1230
1244
  const jp = paths[ji];
1231
1245
 
1232
- let prevTable = ji? paths[ji - 1].table : jp.source;
1246
+ // let prevTable = ji? paths[ji - 1].table : jp.source;
1233
1247
  let table = paths[ji].table;
1234
1248
  let tableAlias = asName(ji < paths.length - 1? `jd${ji}` : table);
1235
1249
  let prevTableAlias = asName(ji? `jd${ji - 1}` : thisTable);
@@ -1248,7 +1262,7 @@ export class ViewHandler {
1248
1262
  j += `AND ${finalFilter} \n`;
1249
1263
  }
1250
1264
 
1251
- const indent = (a, b) => a;
1265
+ const indent = (a: any, b: any) => a;
1252
1266
 
1253
1267
  if(ji < paths.length - 1){
1254
1268
  j += `AND ${makeJoin(joinInfo, ji + 1)} \n`
@@ -1264,9 +1278,9 @@ export class ViewHandler {
1264
1278
 
1265
1279
  }
1266
1280
 
1267
- let t2Rules: TableRule = undefined,
1268
- forcedFilter,
1269
- filterFields,
1281
+ let t2Rules: TableRule | undefined = undefined,
1282
+ forcedFilter: AnyObject | undefined,
1283
+ filterFields: FieldFilter | undefined,
1270
1284
  tableAlias;
1271
1285
 
1272
1286
  /* Check if allowed to view data */
@@ -1314,7 +1328,7 @@ export class ViewHandler {
1314
1328
  let data = { ... (filter as any) } as any ;
1315
1329
 
1316
1330
  /* Exists join filter */
1317
- const ERR = "Invalid exists filter. \nExpecting somethibng like: { $exists: { tableName.tableName2: Filter } } | { $exists: { \"**.tableName3\": Filter } }"
1331
+ const ERR = "Invalid exists filter. \nExpecting somethibng like: { $exists: { tableName.tableName2: Filter } } | { $exists: { \"**.tableName3\": Filter } }\n"
1318
1332
  const SP_WILDCARD = "**";
1319
1333
  let existsKeys: ExistsFilterConfig[] = Object.keys(data)
1320
1334
  .filter(k => EXISTS_KEYS.includes(k as EXISTS_KEY) && Object.keys(data[k] || {}).length)
@@ -1366,7 +1380,7 @@ export class ViewHandler {
1366
1380
  funcFilterkeys.map(f => {
1367
1381
  const funcArgs = data[f.name];
1368
1382
  if(!Array.isArray(funcArgs)) throw `A function filter must contain an array. E.g: { $funcFilterName: ["col1"] } \n but got: ${JSON.stringify(filterObj(data, [f.name]))} `;
1369
- const fields = this.parseFieldFilter(f.getFields(funcArgs, allowed_colnames), true, allowed_colnames);
1383
+ const fields = this.parseFieldFilter(f.getFields(funcArgs), true, allowed_colnames);
1370
1384
 
1371
1385
  const dissallowedCols = fields.filter(fname => !allowed_colnames.includes(fname))
1372
1386
  if(dissallowedCols.length){
@@ -1378,11 +1392,10 @@ export class ViewHandler {
1378
1392
 
1379
1393
  let existsCond = "";
1380
1394
  if(existsKeys.length){
1381
- existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams, tableRules)))).join(" AND ");
1395
+ existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams)))).join(" AND ");
1382
1396
  }
1383
1397
 
1384
1398
  /* Computed field queries */
1385
-
1386
1399
  const p = this.getValidatedRules(tableRules, localParams);
1387
1400
  const computedFields = p.allColumns.filter(c => c.type === "computed" );
1388
1401
  let computedColConditions: string[] = [];
@@ -1438,7 +1451,43 @@ export class ViewHandler {
1438
1451
  }))
1439
1452
  );
1440
1453
 
1441
- let filterKeys = Object.keys(data).filter(k => !funcFilterkeys.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsKeys.find(ek => ek.key === k));
1454
+ /* Parse complex filters
1455
+ { $filter: [{ $func: [...] }, "=", value | { $func: [..] }] }
1456
+ */
1457
+ const complexFilters: string[] = [];
1458
+ const complexFilterKey = "$filter";
1459
+ const allowedComparators = [">", "<", "=", "<=", ">=", "<>", "!="]
1460
+ if(complexFilterKey in data){
1461
+ const getFuncQuery = (funcData: any): string => {
1462
+ const { funcName, args } = parseFunctionObject(funcData);
1463
+ const funcDef = parseFunction({ func: funcName, args, functions: FUNCTIONS, allowedFields: allowed_colnames });
1464
+ return funcDef.getQuery({ args, tableAlias, allColumns: this.columns, allowedFields: allowed_colnames });
1465
+ }
1466
+
1467
+ const complexFilter = data[complexFilterKey];
1468
+ if(!Array.isArray(complexFilter)) throw `Invalid $filter. Must contain an array of at least element but got: ${JSON.stringify(complexFilter)} `
1469
+ const leftFilter = complexFilter[0];
1470
+ const comparator = complexFilter[1];
1471
+ const rightFilterOrValue = complexFilter[2];
1472
+ const leftVal = getFuncQuery(leftFilter);
1473
+ let result = leftVal;
1474
+ if(comparator){
1475
+ if(!allowedComparators.includes(comparator)) throw `Invalid $filter. comparator ${JSON.stringify(comparator)} is not valid. Expecting one of: ${allowedComparators}`
1476
+ if(!rightFilterOrValue) throw "Invalid $filter. Expecting a value or function after the comparator";
1477
+ const rightVal = isObject(rightFilterOrValue)? getFuncQuery(rightFilterOrValue) : asValue(rightFilterOrValue);
1478
+ if(leftVal === rightVal) throw "Invalid $filter. Cannot compare two identical function signatures: " + JSON.stringify(leftFilter);
1479
+ result += ` ${comparator} ${rightVal}`;
1480
+ }
1481
+ complexFilters.push(result);
1482
+ }
1483
+
1484
+
1485
+ /* Parse join filters
1486
+ { $joinFilter: { $ST_DWithin: [table.col, foreignTable.col, distance] }
1487
+ will make an exists filter
1488
+ */
1489
+
1490
+ let filterKeys = Object.keys(data).filter(k => k !== complexFilterKey && !funcFilterkeys.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsKeys.find(ek => ek.key === k));
1442
1491
  // if(allowed_colnames){
1443
1492
  // const aliasedColumns = (select || []).filter(s =>
1444
1493
  // ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
@@ -1480,6 +1529,7 @@ export class ViewHandler {
1480
1529
  if(existsCond) templates.push(existsCond);
1481
1530
  templates = templates.concat(funcConds);
1482
1531
  templates = templates.concat(computedColConditions);
1532
+ templates = templates.concat(complexFilters);
1483
1533
 
1484
1534
  return templates.sort() /* sorted to ensure duplicate subscription channels are not created due to different condition order */
1485
1535
  .join(" AND \n");
@@ -1493,7 +1543,7 @@ export class ViewHandler {
1493
1543
  }
1494
1544
 
1495
1545
  /* This relates only to SELECT */
1496
- prepareSort(orderBy: OrderBy, allowed_cols, tableAlias: string, excludeOrder: boolean = false, select: SelectItem[]): string {
1546
+ prepareSort(orderBy: OrderBy | undefined, allowed_cols: FieldFilter, tableAlias: string | undefined, excludeOrder: boolean = false, select: SelectItem[]): string {
1497
1547
  let column_names = this.column_names.slice(0);
1498
1548
 
1499
1549
  const throwErr = () => {
@@ -1504,7 +1554,7 @@ export class ViewHandler {
1504
1554
  [{ key1: true }, { key2: false }] \
1505
1555
  [{ key: 'colName', asc: true, nulls: 'first', nullEmpty: true }]"
1506
1556
  },
1507
- parseOrderObj = (orderBy, expectOne = false): { key: string, asc: boolean, nulls?: "first" | "last", nullEmpty?: boolean }[] => {
1557
+ parseOrderObj = (orderBy: any, expectOne = false): { key: string, asc: boolean, nulls?: "first" | "last", nullEmpty?: boolean }[] => {
1508
1558
  if(!isPlainObject(orderBy)) return throwErr();
1509
1559
 
1510
1560
  const keys = Object.keys(orderBy);
@@ -1542,7 +1592,7 @@ export class ViewHandler {
1542
1592
 
1543
1593
  if(!orderBy) return "";
1544
1594
 
1545
- let allowedFields = [];
1595
+ let allowedFields: string[] = [];
1546
1596
  if(allowed_cols){
1547
1597
  allowedFields = this.parseFieldFilter(allowed_cols);
1548
1598
  }
@@ -1594,7 +1644,7 @@ export class ViewHandler {
1594
1644
  const orderType = asc? " ASC " : " DESC ";
1595
1645
  const index = selectedAliases.indexOf(key) + 1;
1596
1646
  const nullOrder = nulls? ` NULLS ${nulls === "first"? " FIRST " : " LAST "}` : "";
1597
- let colKey = (index > 0 && !nullEmpty)? index : [tableAlias, key].filter(v => v).map(asName).join(".");
1647
+ let colKey = (index > 0 && !nullEmpty)? index : [tableAlias, key].filter(isDefined).map(asName).join(".");
1598
1648
  if(nullEmpty){
1599
1649
  colKey = `nullif(trim(${colKey}::text), '')`
1600
1650
  }
@@ -1627,7 +1677,7 @@ export class ViewHandler {
1627
1677
  } else {
1628
1678
 
1629
1679
  /* If a limit higher than maxLimit specified throw error */
1630
- if(Number.isInteger(p.select.maxLimit) && _limit > p.select.maxLimit){
1680
+ if(Number.isInteger(p.select.maxLimit) && _limit > p.select.maxLimit!){
1631
1681
  throw `Unexpected LIMIT ${_limit}. Must be less than the published maxLimit: ` + p.select.maxLimit;
1632
1682
  }
1633
1683
  }
@@ -1637,9 +1687,9 @@ export class ViewHandler {
1637
1687
  }
1638
1688
 
1639
1689
  /* This relates only to SELECT */
1640
- prepareOffsetQuery(offset: number): number{
1690
+ prepareOffsetQuery(offset?: number): number{
1641
1691
  if(Number.isInteger(offset)){
1642
- return offset;
1692
+ return offset!;
1643
1693
  }
1644
1694
 
1645
1695
  return 0;
@@ -1647,7 +1697,7 @@ export class ViewHandler {
1647
1697
 
1648
1698
 
1649
1699
  intersectColumns(allowedFields: FieldFilter, dissallowedFields: FieldFilter, fixIssues: boolean = false): string[] {
1650
- let result = [];
1700
+ let result: string[] = [];
1651
1701
  if(allowedFields){
1652
1702
  result = this.parseFieldFilter(allowedFields);
1653
1703
  }
@@ -1673,7 +1723,7 @@ export class ViewHandler {
1673
1723
  * @param {Object} forcedData - set/override property
1674
1724
  * @param {string[]} allowed_cols - allowed columns (excluding forcedData) from table rules
1675
1725
  */
1676
- prepareFieldValues(obj: object = {}, forcedData: object = {}, allowed_cols: FieldFilter, fixIssues = false): object {
1726
+ prepareFieldValues(obj: Record<string, any> = {}, forcedData: object = {}, allowed_cols: FieldFilter | undefined, fixIssues = false): AnyObject {
1677
1727
  let column_names = this.column_names.slice(0);
1678
1728
  if(!column_names || !column_names.length) throw "table column_names mising";
1679
1729
  let _allowed_cols = column_names.slice(0);
@@ -1683,7 +1733,7 @@ export class ViewHandler {
1683
1733
  _allowed_cols = this.parseFieldFilter(allowed_cols, false);
1684
1734
  }
1685
1735
  let final_filter = { ..._obj },
1686
- filter_keys = Object.keys(final_filter);
1736
+ filter_keys: Array<keyof typeof final_filter> = Object.keys(final_filter);
1687
1737
 
1688
1738
  if(fixIssues && filter_keys.length){
1689
1739
  final_filter = {};
@@ -1720,7 +1770,7 @@ export class ViewHandler {
1720
1770
  static _parseFieldFilter(fieldParams: FieldFilter = "*", allow_empty: boolean = true, all_cols: string[]): string[] {
1721
1771
  if(!all_cols) throw "all_cols missing"
1722
1772
  const all_fields = all_cols;// || this.column_names.slice(0);
1723
- let colNames = null,
1773
+ let colNames: string[] = [],
1724
1774
  initialParams = JSON.stringify(fieldParams);
1725
1775
 
1726
1776
  if(fieldParams){
@@ -1763,7 +1813,9 @@ export class ViewHandler {
1763
1813
  } else if(isPlainObject(fieldParams)){
1764
1814
 
1765
1815
  if(Object.keys(fieldParams).length){
1766
- let keys = Object.keys(fieldParams);
1816
+ let keys = Object.keys(fieldParams as {
1817
+ [key: string]: boolean | 0 | 1;
1818
+ });
1767
1819
  if(keys[0] === ""){
1768
1820
  if(allow_empty){
1769
1821
  return [""];
@@ -1776,11 +1828,11 @@ export class ViewHandler {
1776
1828
 
1777
1829
  keys.forEach(key => {
1778
1830
  const allowedVals = [true, false, 0, 1];
1779
- if(!allowedVals.includes(fieldParams[key])) throw `Invalid field selection value for: { ${key}: ${fieldParams[key]} }. \n Allowed values: ${allowedVals.join(" OR ")}`
1831
+ if(!allowedVals.includes((fieldParams as any)[key])) throw `Invalid field selection value for: { ${key}: ${(fieldParams as any)[key]} }. \n Allowed values: ${allowedVals.join(" OR ")}`
1780
1832
  })
1781
1833
 
1782
- let allowed = keys.filter(key => fieldParams[key]),
1783
- disallowed = keys.filter(key => !fieldParams[key]);
1834
+ let allowed = keys.filter(key => (fieldParams as any)[key]),
1835
+ disallowed = keys.filter(key => !(fieldParams as any)[key]);
1784
1836
 
1785
1837
 
1786
1838
  if(disallowed && disallowed.length){
@@ -1808,7 +1860,7 @@ export class ViewHandler {
1808
1860
  }
1809
1861
  }
1810
1862
 
1811
- function isPojoObject(obj): boolean {
1863
+ function isPojoObject<T>(obj: T): obj is Record<string, any> {
1812
1864
  if(obj && (typeof obj !== "object" || Array.isArray(obj) || obj instanceof Date)){
1813
1865
  return false;
1814
1866
  }
@@ -1817,10 +1869,10 @@ function isPojoObject(obj): boolean {
1817
1869
 
1818
1870
 
1819
1871
  type ValidatedParams = {
1820
- row: object;
1821
- forcedData: object;
1822
- allowedFields: FieldFilter;
1823
- tableRules: TableRule;
1872
+ row: AnyObject;
1873
+ forcedData?: AnyObject;
1874
+ allowedFields?: FieldFilter;
1875
+ tableRules?: TableRule;
1824
1876
  fixIssues: boolean;
1825
1877
  }
1826
1878
 
@@ -1829,7 +1881,7 @@ export class TableHandler extends ViewHandler {
1829
1881
  throttle_queries_per_sec: number;
1830
1882
  since: number,
1831
1883
  queries: number,
1832
- batching: string[]
1884
+ batching: string[] | null
1833
1885
  }
1834
1886
 
1835
1887
  constructor(db: DB, tableOrViewInfo: TableSchema, dboBuilder: DboBuilder, t?: pgPromise.ITask<{}>, dbTX?: TxHandler, joinPaths?: JoinPaths){
@@ -1864,8 +1916,8 @@ export class TableHandler extends ViewHandler {
1864
1916
  }
1865
1917
 
1866
1918
  async subscribe(filter: Filter, params: SubscribeParams, localFunc: (items: AnyObject[]) => any): Promise<{ unsubscribe: () => any }>
1867
- async subscribe(filter: Filter, params: SubscribeParams, localFunc: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams): Promise<string>
1868
- async subscribe(filter: Filter, params: SubscribeParams = {}, localFunc: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams):
1919
+ async subscribe(filter: Filter, params: SubscribeParams, localFunc?: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams): Promise<string>
1920
+ async subscribe(filter: Filter, params: SubscribeParams = {}, localFunc?: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams):
1869
1921
  Promise<string | { unsubscribe: () => any }>
1870
1922
  {
1871
1923
  try {
@@ -1878,7 +1930,7 @@ export class TableHandler extends ViewHandler {
1878
1930
  }
1879
1931
 
1880
1932
  const { filterFields, forcedFilter } = get(table_rules, "select") || {},
1881
- condition = await this.prepareWhere({ filter, forcedFilter, addKeywords: false, filterFields, tableAlias: null, localParams, tableRule: table_rules }),
1933
+ condition = await this.prepareWhere({ filter, forcedFilter, addKeywords: false, filterFields, tableAlias: undefined, localParams, tableRule: table_rules }),
1882
1934
  throttle = get(params, "throttle") || 0,
1883
1935
  selectParams = filterObj(params || {}, [], ["throttle"]);
1884
1936
 
@@ -1890,21 +1942,20 @@ export class TableHandler extends ViewHandler {
1890
1942
 
1891
1943
  if(!localFunc) {
1892
1944
  if(!this.dboBuilder.prostgles.isSuperUser) throw "Subscribe not possible. Must be superuser to add triggers 1856";
1893
- return await this.find(filter, { ...selectParams, limit: 0 }, null, table_rules, localParams)
1945
+ return await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams)
1894
1946
  .then(async isValid => {
1895
1947
 
1896
- const { socket = null } = localParams;
1948
+ const { socket } = localParams ?? {};
1897
1949
  const pubSubManager = await this.dboBuilder.getPubSubManager();
1898
1950
  return pubSubManager.addSub({
1899
1951
  table_info: this.tableOrViewInfo,
1900
1952
  socket,
1901
1953
  table_rules,
1902
1954
  condition: condition,
1903
- func: localFunc,
1955
+ func: undefined,
1904
1956
  filter: { ...filter },
1905
1957
  params: { ...selectParams },
1906
- channel_name: null,
1907
- socket_id: socket.id,
1958
+ socket_id: socket?.id,
1908
1959
  table_name: this.name,
1909
1960
  throttle,
1910
1961
  last_throttled: 0,
@@ -1915,14 +1966,13 @@ export class TableHandler extends ViewHandler {
1915
1966
  const pubSubManager = await this.dboBuilder.getPubSubManager();
1916
1967
  pubSubManager.addSub({
1917
1968
  table_info: this.tableOrViewInfo,
1918
- socket: null,
1969
+ socket: undefined,
1919
1970
  table_rules,
1920
1971
  condition,
1921
1972
  func: localFunc,
1922
1973
  filter: { ...filter },
1923
1974
  params: { ...selectParams },
1924
- channel_name: null,
1925
- socket_id: null,
1975
+ socket_id: undefined,
1926
1976
  table_name: this.name,
1927
1977
  throttle,
1928
1978
  last_throttled: 0,
@@ -1947,12 +1997,12 @@ export class TableHandler extends ViewHandler {
1947
1997
  subscribeOne(filter: Filter, params: SubscribeParams = {}, localFunc: (item: AnyObject) => any, table_rules?: TableRule, localParams?: LocalParams):
1948
1998
  Promise<string | { unsubscribe: () => any }>
1949
1999
  {
1950
- let func = localParams? undefined : (rows) => localFunc(rows[0]);
2000
+ let func = localParams? undefined : (rows: AnyObject[]) => localFunc(rows[0]);
1951
2001
  return this.subscribe(filter, { ...params, limit: 2 }, func, table_rules, localParams);
1952
2002
  }
1953
2003
 
1954
2004
 
1955
- async updateBatch(data: [Filter, AnyObject][], params?: UpdateParams, tableRules?: TableRule, localParams: LocalParams = null): Promise<any>{
2005
+ async updateBatch(data: [Filter, AnyObject][], params?: UpdateParams, tableRules?: TableRule, localParams?: LocalParams): Promise<any>{
1956
2006
  try {
1957
2007
  const queries = await Promise.all(
1958
2008
  data.map(async ([filter, data]) =>
@@ -1976,20 +2026,20 @@ export class TableHandler extends ViewHandler {
1976
2026
  }
1977
2027
  }
1978
2028
 
1979
- async update(filter: Filter, newData: { [key: string]: any }, params?: UpdateParams, tableRules?: TableRule, localParams: LocalParams = null): Promise<AnyObject | void>{
2029
+ async update(filter: Filter, newData: AnyObject, params?: UpdateParams, tableRules?: TableRule, localParams?: LocalParams): Promise<AnyObject | void>{
1980
2030
  try {
1981
2031
 
1982
- const { testRule = false, returnQuery = false } = localParams || {};
2032
+ const { testRule = false, returnQuery = false } = localParams ?? {};
1983
2033
  if(!testRule){
1984
2034
  if(!newData || !Object.keys(newData).length) throw "no update data provided\nEXPECTING db.table.update(filter, updateData, options)";
1985
2035
  this.checkFilter(filter);
1986
2036
  }
1987
2037
 
1988
- let forcedFilter: object = {},
1989
- forcedData: object = {},
1990
- validate: ValidateRow,
1991
- returningFields: FieldFilter = "*",
1992
- filterFields: FieldFilter = "*",
2038
+ let forcedFilter: AnyObject | undefined = {},
2039
+ forcedData: AnyObject | undefined = {},
2040
+ validate: ValidateRow | undefined,
2041
+ returningFields: FieldFilter | undefined = "*",
2042
+ filterFields: FieldFilter | undefined = "*",
1993
2043
  fields: FieldFilter = "*";
1994
2044
 
1995
2045
  if(tableRules){
@@ -2004,7 +2054,7 @@ export class TableHandler extends ViewHandler {
2004
2054
  await this.validateViewRules(fields, filterFields, returningFields, forcedFilter, "update");
2005
2055
  if(forcedData) {
2006
2056
  try {
2007
- const { data, allowedCols } = this.validateNewData({ row: forcedData, forcedData: null, allowedFields: "*", tableRules, fixIssues: false });
2057
+ const { data, allowedCols } = this.validateNewData({ row: forcedData, forcedData: undefined, allowedFields: "*", tableRules, fixIssues: false });
2008
2058
  const updateQ = await this.colSet.getUpdateQuery(data, allowedCols, validate) //pgp.helpers.update(data, columnSet)
2009
2059
  let query = updateQ + " WHERE FALSE ";
2010
2060
  await this.db.any("EXPLAIN " + query);
@@ -2049,13 +2099,13 @@ export class TableHandler extends ViewHandler {
2049
2099
  if(c.data_type === "text" && d && isPlainObject(d) && !["from", "to"].find(key => typeof d[key] !== "number")){
2050
2100
  const unrecProps = Object.keys(d).filter(k => !["from", "to", "text", "md5"].includes(k));
2051
2101
  if(unrecProps.length) throw "Unrecognised params in textPatch field: " + unrecProps.join(", ");
2052
- patchedTextData.push({ ...d, fieldName: c.name });
2102
+ patchedTextData.push({ ...d, fieldName: c.name } as (typeof patchedTextData)[number]);
2053
2103
  }
2054
2104
  });
2055
2105
 
2056
2106
  if(patchedTextData && patchedTextData.length){
2057
2107
  if(tableRules && !tableRules.select) throw "Select needs to be permitted to patch data";
2058
- const rows = await this.find(filter, { select: patchedTextData.reduce((a, v) => ({ ...a, [v.fieldName]: 1 }), {}) }, null, tableRules);
2108
+ const rows = await this.find(filter, { select: patchedTextData.reduce((a, v) => ({ ...a, [v.fieldName]: 1 }), {}) }, undefined, tableRules);
2059
2109
 
2060
2110
  if(rows.length !== 1) {
2061
2111
  throw "Cannot patch data within a filter that affects more/less than 1 row";
@@ -2091,9 +2141,9 @@ export class TableHandler extends ViewHandler {
2091
2141
  if(returnQuery) return query as unknown as void;
2092
2142
 
2093
2143
  if(this.t){
2094
- return this.t[qType](query).catch(err => makeErr(err, localParams, this, _fields));
2144
+ return (this.t as any)[qType](query).catch((err: any) => makeErr(err, localParams, this, _fields));
2095
2145
  }
2096
- return this.db.tx(t => t[qType](query)).catch(err => makeErr(err, localParams, this, _fields));
2146
+ return this.db.tx(t => (t as any)[qType](query)).catch(err => makeErr(err, localParams, this, _fields));
2097
2147
  } catch(e){
2098
2148
  if(localParams && localParams.testRule) throw e;
2099
2149
  throw { err: parseError(e), msg: `Issue with dbo.${this.name}.update(${JSON.stringify(filter || {}, null, 2)}, ${JSON.stringify(newData || {}, null, 2)}, ${JSON.stringify(params || {}, null, 2)})` };
@@ -2101,7 +2151,7 @@ export class TableHandler extends ViewHandler {
2101
2151
  };
2102
2152
 
2103
2153
  validateNewData({ row, forcedData, allowedFields, tableRules, fixIssues = false }: ValidatedParams): { data: any; allowedCols: string[] } {
2104
- const synced_field = get(tableRules || {}, "sync.synced_field");
2154
+ const synced_field = get(tableRules ?? {}, "sync.synced_field");
2105
2155
 
2106
2156
  /* Update synced_field if sync is on and missing */
2107
2157
  if(synced_field && !row[synced_field]){
@@ -2109,7 +2159,7 @@ export class TableHandler extends ViewHandler {
2109
2159
  }
2110
2160
 
2111
2161
  let data = this.prepareFieldValues(row, forcedData, allowedFields, fixIssues);
2112
- const dataKeys = Object.keys(data);
2162
+ const dataKeys = getKeys(data);
2113
2163
 
2114
2164
  dataKeys.map(col => {
2115
2165
  this.dboBuilder.prostgles?.tableConfigurator?.checkColVal({ table: this.name, col, value: data[col] });
@@ -2128,7 +2178,7 @@ export class TableHandler extends ViewHandler {
2128
2178
  }
2129
2179
 
2130
2180
 
2131
- private async insertDataParse(data: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?, tableRules?: TableRule, _localParams: LocalParams = null): Promise<{
2181
+ private async insertDataParse(data: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?: never, tableRules?: TableRule, _localParams?: LocalParams): Promise<{
2132
2182
  data?: AnyObject | AnyObject[];
2133
2183
  insertResult?: AnyObject | AnyObject[];
2134
2184
  }>{
@@ -2136,7 +2186,7 @@ export class TableHandler extends ViewHandler {
2136
2186
  let dbTX = localParams?.dbTX || this.dbTX;
2137
2187
 
2138
2188
  const isMultiInsert = Array.isArray(data);
2139
- const getExtraKeys = d => Object.keys(d).filter(k => !this.columns.find(c => c.name === k));
2189
+ const getExtraKeys = (d: AnyObject)=> Object.keys(d).filter(k => !this.columns.find(c => c.name === k));
2140
2190
 
2141
2191
  /* Nested insert is not allowed for the file table */
2142
2192
  const isNestedInsert = this.is_media? false : (Array.isArray(data)? data : [data]).some(d => getExtraKeys(d).length);
@@ -2201,7 +2251,7 @@ export class TableHandler extends ViewHandler {
2201
2251
  const _media: Media = await this.dboBuilder.prostgles.fileManager.uploadAsMedia({
2202
2252
  item: {
2203
2253
  data,
2204
- name: media.name,
2254
+ name: media.name ?? "????",
2205
2255
  content_type: media.content_type as any
2206
2256
  },
2207
2257
  // imageCompression: {
@@ -2221,21 +2271,21 @@ export class TableHandler extends ViewHandler {
2221
2271
  } else if(extraKeys.length){
2222
2272
 
2223
2273
  /* Ensure we're using the same transaction */
2224
- const _this = this.t? this : dbTX[this.name] as TableHandler;
2274
+ const _this = this.t? this : dbTX![this.name] as TableHandler;
2225
2275
 
2226
- let rootData = filterObj(data, null, extraKeys);
2276
+ let rootData = filterObj(data, undefined, extraKeys);
2227
2277
 
2228
2278
  let insertedChildren: AnyObject[];
2229
2279
  let targetTableRules: TableRule;
2230
2280
 
2231
- const fullRootResult = await _this.insert(rootData, { returning: "*" }, null, tableRules, localParams);
2232
- let returnData: AnyObject;
2281
+ const fullRootResult = await _this.insert(rootData, { returning: "*" }, undefined, tableRules, localParams);
2282
+ let returnData: AnyObject | undefined;
2233
2283
  const returning = param2?.returning;
2234
2284
  if(returning){
2235
2285
  returnData = {}
2236
2286
  const returningItems = await this.prepareReturning(returning, this.parseFieldFilter(tableRules?.insert?.returningFields));
2237
2287
  returningItems.filter(s => s.selected).map(rs => {
2238
- returnData[rs.alias] = fullRootResult[rs.alias];
2288
+ returnData![rs.alias] = fullRootResult[rs.alias];
2239
2289
  })
2240
2290
  }
2241
2291
 
@@ -2244,7 +2294,7 @@ export class TableHandler extends ViewHandler {
2244
2294
 
2245
2295
  /* Must be allowed to insert into media table */
2246
2296
  const canInsert = async (tbl: string) => {
2247
- const childRules = await this.dboBuilder.publishParser.getValidatedRequestRuleWusr({ tableName: tbl, command: "insert", localParams });
2297
+ const childRules = await this.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: tbl, command: "insert", localParams });
2248
2298
  if(!childRules || !childRules.insert) throw "Dissallowed nested insert into table " + childRules;
2249
2299
  return childRules;
2250
2300
  }
@@ -2254,9 +2304,9 @@ export class TableHandler extends ViewHandler {
2254
2304
  if(!jp) throw `Could not find a valid table for the nested data { ${targetTable} } `;
2255
2305
 
2256
2306
  const thisInfo = await this.getInfo();
2257
- const childInsert = async (cdata, tableName) => {
2307
+ const childInsert = async (cdata: AnyObject | AnyObject[], tableName: string) => {
2258
2308
  // console.log("childInsert", {data, tableName})
2259
- if(!cdata || !dbTX[tableName] || !("insert" in dbTX[tableName])) throw "childInsertErr: Child table handler missing for: " + tableName;
2309
+ if(!cdata || !dbTX?.[tableName] || !("insert" in dbTX[tableName])) throw "childInsertErr: Child table handler missing for: " + tableName;
2260
2310
 
2261
2311
  const tableRules = await canInsert(tableName);
2262
2312
 
@@ -2265,8 +2315,8 @@ export class TableHandler extends ViewHandler {
2265
2315
  }
2266
2316
  return Promise.all(
2267
2317
  (Array.isArray(cdata)? cdata : [cdata])
2268
- .map(m => (dbTX[tableName] as TableHandler)
2269
- .insert(m, { returning: "*" }, null, tableRules, localParams)
2318
+ .map(m => (dbTX![tableName] as TableHandler)
2319
+ .insert(m, { returning: "*" }, undefined, tableRules, localParams)
2270
2320
  .catch(e => {
2271
2321
  console.trace({ childInsertErr: e })
2272
2322
  return Promise.reject({ childInsertErr: e });
@@ -2293,10 +2343,10 @@ export class TableHandler extends ViewHandler {
2293
2343
 
2294
2344
  // console.log(childDataItems, JSON.stringify(colsRefT1, null, 2))
2295
2345
  insertedChildren = await childInsert(
2296
- childDataItems.map(d => {
2346
+ childDataItems.map((d: AnyObject) => {
2297
2347
  let result = {...d};
2298
2348
  colsRefT1.map(col => {
2299
- result[col.references.cols[0]] = fullRootResult[col.references.fcols[0]]
2349
+ result[col.references!.cols[0]] = fullRootResult[col.references!.fcols[0]]
2300
2350
  })
2301
2351
  return result;
2302
2352
  }),
@@ -2309,7 +2359,7 @@ export class TableHandler extends ViewHandler {
2309
2359
  const colsRefT3 = cols2?.filter(c => c.references?.cols.length === 1 && c.references?.ftable === tbl3);
2310
2360
  if(!colsRefT1.length || !colsRefT3.length) throw "Incorrectly referenced or missing columns for nested insert";
2311
2361
 
2312
- if(targetTable !== this.dboBuilder.prostgles.fileManager.tableName){
2362
+ if(targetTable !== this.dboBuilder.prostgles.fileManager?.tableName){
2313
2363
  throw "Only media allowed to have nested inserts more than 2 tables apart"
2314
2364
  }
2315
2365
 
@@ -2322,13 +2372,13 @@ export class TableHandler extends ViewHandler {
2322
2372
 
2323
2373
  /* Insert in key_lookup table */
2324
2374
  await Promise.all(insertedChildren.map(async t3Child => {
2325
- let tbl2Row = {};
2375
+ let tbl2Row: AnyObject = {};
2326
2376
 
2327
2377
  colsRefT3.map(col => {
2328
- tbl2Row[col.name] = t3Child[col.references.fcols[0]];
2378
+ tbl2Row[col.name] = t3Child[col.references!.fcols[0]];
2329
2379
  })
2330
2380
  colsRefT1.map(col => {
2331
- tbl2Row[col.name] = fullRootResult[col.references.fcols[0]];
2381
+ tbl2Row[col.name] = fullRootResult[col.references!.fcols[0]];
2332
2382
  })
2333
2383
  // console.log({ rootResult, tbl2Row, t3Child, colsRefT3, colsRefT1, t: this.t?.ctx?.start });
2334
2384
 
@@ -2342,18 +2392,18 @@ export class TableHandler extends ViewHandler {
2342
2392
 
2343
2393
  /* Return also the nested inserted data */
2344
2394
  if(targetTableRules && insertedChildren?.length && returning){
2345
- const targetTableHandler = dbTX[targetTable] as TableHandler;
2395
+ const targetTableHandler = dbTX![targetTable] as TableHandler;
2346
2396
  const targetReturning = await targetTableHandler.prepareReturning("*", targetTableHandler.parseFieldFilter(targetTableRules?.insert?.returningFields));
2347
2397
  let clientTargetInserts = insertedChildren.map(d => {
2348
2398
  let _d = { ...d };
2349
- let res = {};
2399
+ let res: AnyObject = {};
2350
2400
  targetReturning.map(r => {
2351
2401
  res[r.alias] = _d[r.alias]
2352
2402
  });
2353
2403
  return res;
2354
2404
  });
2355
2405
 
2356
- returnData[targetTable] = clientTargetInserts.length === 1? clientTargetInserts[0] : clientTargetInserts;
2406
+ returnData![targetTable] = clientTargetInserts.length === 1? clientTargetInserts[0] : clientTargetInserts;
2357
2407
  }
2358
2408
  }));
2359
2409
 
@@ -2374,7 +2424,7 @@ export class TableHandler extends ViewHandler {
2374
2424
  return res;
2375
2425
  }
2376
2426
 
2377
- async insert(rowOrRows: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?, tableRules?: TableRule, _localParams: LocalParams = null): Promise<any | any[] | boolean>{
2427
+ async insert(rowOrRows: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?: never, tableRules?: TableRule, _localParams?: LocalParams): Promise<any | any[] | boolean>{
2378
2428
  const localParams = _localParams || {};
2379
2429
  const {dbTX} = localParams
2380
2430
  try {
@@ -2382,9 +2432,9 @@ export class TableHandler extends ViewHandler {
2382
2432
  const { returning, onConflictDoNothing, fixIssues = false } = param2 || {};
2383
2433
  const { testRule = false, returnQuery = false } = localParams || {};
2384
2434
 
2385
- let returningFields: FieldFilter,
2386
- forcedData: object,
2387
- fields: FieldFilter;
2435
+ let returningFields: FieldFilter | undefined,
2436
+ forcedData: AnyObject | undefined,
2437
+ fields: FieldFilter | undefined;
2388
2438
 
2389
2439
  if(tableRules){
2390
2440
  if(!tableRules.insert) throw "insert rules missing for " + this.name;
@@ -2400,7 +2450,7 @@ export class TableHandler extends ViewHandler {
2400
2450
  /* Safely test publish rules */
2401
2451
  if(testRule){
2402
2452
  // if(this.is_media && tableRules.insert.preValidate) throw "Media table cannot have a preValidate. It already is used internally by prostgles for file upload";
2403
- await this.validateViewRules(fields, null, returningFields, null, "insert");
2453
+ await this.validateViewRules(fields, undefined, returningFields, undefined, "insert");
2404
2454
  if(forcedData) {
2405
2455
  const keys = Object.keys(forcedData);
2406
2456
  if(keys.length){
@@ -2432,7 +2482,7 @@ export class TableHandler extends ViewHandler {
2432
2482
 
2433
2483
  if(!rowOrRows) rowOrRows = {}; //throw "Provide data in param1";
2434
2484
  let returningSelect = this.makeReturnQuery(await this.prepareReturning(returning, this.parseFieldFilter(returningFields)));
2435
- const makeQuery = async (_row, isOne = false) => {
2485
+ const makeQuery = async (_row: AnyObject | undefined, isOne = false) => {
2436
2486
  let row = { ..._row };
2437
2487
 
2438
2488
  if(!isPojoObject(row)) {
@@ -2450,7 +2500,7 @@ export class TableHandler extends ViewHandler {
2450
2500
  };
2451
2501
 
2452
2502
  let query = "";
2453
- let queryType = "none";
2503
+ let queryType: keyof pgPromise.ITask<{}> = "none";
2454
2504
 
2455
2505
  /**
2456
2506
  * If media it will: upload file and continue insert
@@ -2487,9 +2537,9 @@ export class TableHandler extends ViewHandler {
2487
2537
 
2488
2538
  const allowedFieldKeys = this.parseFieldFilter(fields);
2489
2539
  if(tx) {
2490
- result = tx[queryType](query).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
2540
+ result = (tx as any)[queryType](query).catch((err: any) => makeErr(err, localParams, this, allowedFieldKeys));
2491
2541
  } else {
2492
- result = this.db.tx(t => t[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
2542
+ result = this.db.tx(t => (t as any)[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
2493
2543
  }
2494
2544
 
2495
2545
  return result;
@@ -2502,7 +2552,7 @@ export class TableHandler extends ViewHandler {
2502
2552
  }
2503
2553
  };
2504
2554
 
2505
- prepareReturning = async (returning: FieldFilter, allowedFields: string[]): Promise<SelectItem[]> => {
2555
+ prepareReturning = async (returning: Select<AnyObject>, allowedFields: string[]): Promise<SelectItem[]> => {
2506
2556
  let result: SelectItem[] = [];
2507
2557
  if(returning){
2508
2558
  let sBuilder = new SelectItemBuilder({
@@ -2526,7 +2576,7 @@ export class TableHandler extends ViewHandler {
2526
2576
  return "";
2527
2577
  }
2528
2578
 
2529
- async delete(filter?: Filter, params?: DeleteParams, param3_unused?, table_rules?: TableRule, localParams: LocalParams = null): Promise<any> { //{ socket, func, has_rules = false, socketDb } = {}
2579
+ async delete(filter?: Filter, params?: DeleteParams, param3_unused?: never, table_rules?: TableRule, localParams?: LocalParams): Promise<any> { //{ socket, func, has_rules = false, socketDb } = {}
2530
2580
  try {
2531
2581
  const { returning } = params || {};
2532
2582
  filter = filter || {};
@@ -2534,9 +2584,9 @@ export class TableHandler extends ViewHandler {
2534
2584
 
2535
2585
  // table_rules = table_rules || {};
2536
2586
 
2537
- let forcedFilter: object = {},
2538
- filterFields: FieldFilter = "*",
2539
- returningFields: FieldFilter = "*";
2587
+ let forcedFilter: AnyObject | undefined = {},
2588
+ filterFields: FieldFilter | undefined = "*",
2589
+ returningFields: FieldFilter | undefined = "*";
2540
2590
 
2541
2591
  const { testRule = false, returnQuery = false } = localParams || {};
2542
2592
  if(table_rules){
@@ -2552,7 +2602,7 @@ export class TableHandler extends ViewHandler {
2552
2602
 
2553
2603
  /* Safely test publish rules */
2554
2604
  if(testRule){
2555
- await this.validateViewRules(null, filterFields, returningFields, forcedFilter, "delete");
2605
+ await this.validateViewRules(undefined, filterFields, returningFields, forcedFilter, "delete");
2556
2606
  return true;
2557
2607
  }
2558
2608
  }
@@ -2564,7 +2614,7 @@ export class TableHandler extends ViewHandler {
2564
2614
  if(bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
2565
2615
  }
2566
2616
 
2567
- let queryType = 'none';
2617
+ let queryType: keyof pgPromise.ITask<{}> = 'none';
2568
2618
  let _query = "DELETE FROM " + this.escapedName;
2569
2619
 
2570
2620
  _query += (await this.prepareWhere({
@@ -2584,7 +2634,7 @@ export class TableHandler extends ViewHandler {
2584
2634
  }
2585
2635
 
2586
2636
  if(returnQuery) return _query;
2587
- return (this.t || this.db)[queryType](_query).catch(err => makeErr(err, localParams));
2637
+ return (this.t || this.db as any)[queryType](_query).catch((err: any) => makeErr(err, localParams));
2588
2638
  } catch(e){
2589
2639
  // console.trace(e)
2590
2640
  if(localParams && localParams.testRule) throw e;
@@ -2592,11 +2642,11 @@ export class TableHandler extends ViewHandler {
2592
2642
  }
2593
2643
  };
2594
2644
 
2595
- remove(filter: Filter, params?: UpdateParams, param3_unused?: null, tableRules?: TableRule, localParams: LocalParams = null){
2645
+ remove(filter: Filter, params?: UpdateParams, param3_unused?: never, tableRules?: TableRule, localParams?: LocalParams){
2596
2646
  return this.delete(filter, params, param3_unused , tableRules, localParams);
2597
2647
  }
2598
2648
 
2599
- async upsert(filter: Filter, newData?: object, params?: UpdateParams, table_rules?: TableRule, localParams: LocalParams = null): Promise<any> {
2649
+ async upsert(filter: Filter, newData: AnyObject, params?: UpdateParams, table_rules?: TableRule, localParams?: LocalParams): Promise<any> {
2600
2650
  try {
2601
2651
  /* Do it within a transaction to ensure consisency */
2602
2652
  if(!this.t){
@@ -2606,12 +2656,12 @@ export class TableHandler extends ViewHandler {
2606
2656
  }
2607
2657
 
2608
2658
  async function _upsert(tblH: TableHandler){
2609
- return tblH.find(filter, { select: "", limit: 1 }, {}, table_rules, localParams)
2659
+ return tblH.find(filter, { select: "", limit: 1 }, undefined, table_rules, localParams)
2610
2660
  .then(exists => {
2611
2661
  if(exists && exists.length){
2612
2662
  return tblH.update(filter, newData, params, table_rules, localParams);
2613
2663
  } else {
2614
- return tblH.insert({ ...newData, ...filter }, params, null, table_rules, localParams);
2664
+ return tblH.insert({ ...newData, ...filter }, params, undefined, table_rules, localParams);
2615
2665
  }
2616
2666
  });
2617
2667
  }
@@ -2622,7 +2672,7 @@ export class TableHandler extends ViewHandler {
2622
2672
  };
2623
2673
 
2624
2674
  /* External request. Cannot sync from server */
2625
- async sync(filter: Filter, params: SelectParams, param3_unused, table_rules: TableRule, localParams: LocalParams){
2675
+ async sync(filter: Filter, params: SelectParams, param3_unused: never, table_rules: TableRule, localParams: LocalParams){
2626
2676
  if(!localParams) throw "Sync not allowed within the same server code";
2627
2677
  const { socket } = localParams;
2628
2678
  if(!socket) throw "INTERNAL ERROR: socket missing";
@@ -2667,7 +2717,7 @@ export class TableHandler extends ViewHandler {
2667
2717
  });
2668
2718
 
2669
2719
  /* Step 1: parse command and params */
2670
- return this.find(filter, { select, limit: 0 }, null, table_rules, localParams)
2720
+ return this.find(filter, { select, limit: 0 }, undefined, table_rules, localParams)
2671
2721
  .then(async isValid => {
2672
2722
 
2673
2723
  const { filterFields, forcedFilter } = get(table_rules, "select") || {};
@@ -2678,7 +2728,8 @@ export class TableHandler extends ViewHandler {
2678
2728
  return pubSubManager.addSync({
2679
2729
  table_info: this.tableOrViewInfo,
2680
2730
  condition,
2681
- id_fields, synced_field, allow_delete,
2731
+ id_fields, synced_field,
2732
+ allow_delete,
2682
2733
  socket,
2683
2734
  table_rules,
2684
2735
  filter: { ...filter },
@@ -2725,18 +2776,18 @@ import { JOIN_TYPES } from "./Prostgles";
2725
2776
  import { BasicSession } from "./AuthHandler";
2726
2777
 
2727
2778
  export class DboBuilder {
2728
- tablesOrViews: TableSchema[]; //TableSchema TableOrViewInfo
2779
+ tablesOrViews?: TableSchema[]; //TableSchema TableOrViewInfo
2729
2780
  /**
2730
2781
  * Used in obtaining column names for error messages
2731
2782
  */
2732
- constraints: PGConstraint[];
2783
+ constraints?: PGConstraint[];
2733
2784
 
2734
2785
  db: DB;
2735
2786
  schema: string = "public";
2736
2787
 
2737
2788
  // dbo: DbHandler | DbHandlerTX;
2738
2789
  dbo: DbHandler;
2739
- _pubSubManager: PubSubManager;
2790
+ _pubSubManager?: PubSubManager;
2740
2791
 
2741
2792
  getPubSubManager = async () : Promise<PubSubManager> => {
2742
2793
  if(!this._pubSubManager){
@@ -2763,26 +2814,27 @@ export class DboBuilder {
2763
2814
  console.warn(`subscribe and sync cannot be used because db user is not a superuser `)
2764
2815
  }
2765
2816
  }
2766
-
2817
+ if(!this._pubSubManager) throw "Could not create this._pubSubManager";
2767
2818
  return this._pubSubManager;
2768
2819
  }
2769
2820
 
2770
- pojoDefinitions: string[];
2771
- dboDefinition: string;
2821
+ pojoDefinitions?: string[];
2822
+ dboDefinition?: string;
2772
2823
 
2773
- tsTypesDefinition: string;
2824
+ tsTypesDefinition?: string;
2774
2825
 
2775
- joins: Join[];
2776
- joinGraph: Graph;
2777
- joinPaths: JoinPaths;
2826
+ joins?: Join[];
2827
+ joinGraph?: Graph;
2828
+ joinPaths: JoinPaths = [];
2778
2829
 
2779
2830
  prostgles: Prostgles;
2780
- publishParser: PublishParser;
2831
+ publishParser?: PublishParser;
2781
2832
 
2782
- onSchemaChange: (event: { command: string; query: string }) => void;
2833
+ onSchemaChange?: (event: { command: string; query: string }) => void;
2783
2834
 
2784
2835
  private constructor(prostgles: Prostgles){
2785
2836
  this.prostgles = prostgles;
2837
+ if(!this.prostgles.db) throw "db missing"
2786
2838
  this.db = this.prostgles.db;
2787
2839
  this.schema = this.prostgles.opts.schema || "public";
2788
2840
  this.dbo = { } as unknown as DbHandler;
@@ -2844,10 +2896,10 @@ export class DboBuilder {
2844
2896
  if(dup){
2845
2897
  throw "Duplicate join declaration for table: " + dup.tables[0];
2846
2898
  }
2847
- const tovNames = this.tablesOrViews.map(t => t.name);
2899
+ const tovNames = this.tablesOrViews!.map(t => t.name);
2848
2900
 
2849
2901
  // 2 find incorrect tables
2850
- const missing = flat(joins.map(j => j.tables)).find(t => !tovNames.includes(t));
2902
+ const missing = joins.flatMap(j => j.tables).find(t => !tovNames.includes(t));
2851
2903
  if(missing){
2852
2904
  throw "Table not found: " + missing;
2853
2905
  }
@@ -2862,9 +2914,9 @@ export class DboBuilder {
2862
2914
  var t = <string>v[0],
2863
2915
  f = <string[]>v[1];
2864
2916
 
2865
- let tov = this.tablesOrViews.find(_t => _t.name === t);
2917
+ let tov = this.tablesOrViews!.find(_t => _t.name === t);
2866
2918
  if(!tov) throw "Table not found: " + t;
2867
- const m1 = f.filter(k => !tov.columns.map(c => c.name).includes(k))
2919
+ const m1 = f.filter(k => !tov!.columns.map(c => c.name).includes(k))
2868
2920
  if(m1 && m1.length){
2869
2921
  throw `Table ${t}(${tov.columns.map(c => c.name).join()}) has no fields named: ${m1.join()}`;
2870
2922
  }
@@ -2889,17 +2941,17 @@ export class DboBuilder {
2889
2941
  let _t = tables.slice().sort(),
2890
2942
  t1 = _t[0],
2891
2943
  t2 = _t[1];
2892
- this.joinGraph[t1] = this.joinGraph[t1] || {};
2893
- this.joinGraph[t1][t2] = 1;
2944
+ this.joinGraph![t1] = this.joinGraph![t1] || {};
2945
+ this.joinGraph![t1][t2] = 1;
2894
2946
 
2895
- this.joinGraph[t2] = this.joinGraph[t2] || {};
2896
- this.joinGraph[t2][t1] = 1;
2947
+ this.joinGraph![t2] = this.joinGraph![t2] || {};
2948
+ this.joinGraph![t2][t1] = 1;
2897
2949
  });
2898
- const tables = flat(this.joins.map(t => t.tables));
2950
+ const tables = this.joins.flatMap(t => t.tables);
2899
2951
  this.joinPaths = [];
2900
2952
  tables.map(t1 => {
2901
2953
  tables.map(t2 => {
2902
- const spath = findShortestPath(this.joinGraph, t1, t2);
2954
+ const spath = findShortestPath(this.joinGraph!, t1, t2);
2903
2955
  if(spath && spath.distance < Infinity){
2904
2956
 
2905
2957
  const existing1 = this.joinPaths.find(j => j.t1 === t1 && j.t2 === t2)
@@ -2946,7 +2998,7 @@ export type TxCB = {
2946
2998
 
2947
2999
  await this.parseJoins();
2948
3000
 
2949
- let joinTableNames = [];
3001
+ let joinTableNames: string[] = [];
2950
3002
 
2951
3003
  let allDataDefs = "";
2952
3004
  let i18nDef = "type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]>; }; \n"
@@ -2974,10 +3026,10 @@ export type TxCB = {
2974
3026
  const TSTableDataName = snakify(tov.name, true);
2975
3027
  const TSTableHandlerName = JSON.stringify(tov.name)
2976
3028
  if(tov.is_view){
2977
- this.dbo[tov.name] = new ViewHandler(this.db, tov, this, null, undefined, this.joinPaths);
3029
+ this.dbo[tov.name] = new ViewHandler(this.db, tov, this, undefined, undefined, this.joinPaths);
2978
3030
  this.dboDefinition += ` ${TSTableHandlerName}: ViewHandler<${TSTableDataName}> \n`;
2979
3031
  } else {
2980
- this.dbo[tov.name] = new TableHandler(this.db, tov, this, null, undefined, this.joinPaths);
3032
+ this.dbo[tov.name] = new TableHandler(this.db, tov, this, undefined, undefined, this.joinPaths);
2981
3033
  this.dboDefinition += ` ${TSTableHandlerName}: TableHandler<${TSTableDataName}> \n`;
2982
3034
  }
2983
3035
  allDataDefs += `export type ${TSTableDataName} = { \n` +
@@ -2991,6 +3043,19 @@ export type TxCB = {
2991
3043
  let table = tov.name;
2992
3044
  joinTableNames.push(table);
2993
3045
 
3046
+ const makeJoin = (
3047
+ isLeft = true,
3048
+ filter: Parameters<JoinMaker<AnyObject>>[0],
3049
+ select: Parameters<JoinMaker<AnyObject>>[1],
3050
+ options: Parameters<JoinMaker<AnyObject>>[2]
3051
+ ): ReturnType<JoinMaker<AnyObject>> => {
3052
+ return {
3053
+ [isLeft? "$leftJoin" : "$innerJoin"]: table,
3054
+ filter,
3055
+ select,
3056
+ ...options
3057
+ }
3058
+ }
2994
3059
  this.dbo.innerJoin = this.dbo.innerJoin || {};
2995
3060
  this.dbo.leftJoin = this.dbo.leftJoin || {};
2996
3061
  this.dbo.innerJoinOne = this.dbo.innerJoinOne || {};
@@ -3007,14 +3072,6 @@ export type TxCB = {
3007
3072
  this.dbo.innerJoinOne[table] = (filter, select, options = {}) => {
3008
3073
  return makeJoin(false, filter, select, {...options, limit: 1});
3009
3074
  }
3010
- function makeJoin(isLeft = true, filter, select, options){
3011
- return {
3012
- [isLeft? "$leftJoin" : "$innerJoin"]: table,
3013
- filter,
3014
- select,
3015
- ...options
3016
- }
3017
- }
3018
3075
  }
3019
3076
  });
3020
3077
  i18nDef += " }> \n";
@@ -3044,17 +3101,17 @@ export type TxCB = {
3044
3101
  if(!this.dbo.sql){
3045
3102
 
3046
3103
  let needType = true;// this.publishRawSQL && typeof this.publishRawSQL === "function";
3047
- let DATA_TYPES = !needType? [] : await this.db.any("SELECT oid, typname FROM pg_type");
3048
- let USER_TABLES = !needType? [] : await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables");
3104
+ let DATA_TYPES: {oid: string, typname: PG_COLUMN_UDT_DATA_TYPE }[] = !needType? [] : await this.db.any("SELECT oid, typname FROM pg_type");
3105
+ let USER_TABLES: { relid: string; relname: string; }[] = !needType? [] : await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables");
3049
3106
 
3050
- this.dbo.sql = async (query: string, params: any, options: SQLOptions, localParams?: LocalParams) => {
3107
+ this.dbo.sql = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
3051
3108
 
3052
- const canRunSQL = async (localParams: LocalParams) => {
3109
+ const canRunSQL = async (localParams?: LocalParams) => {
3053
3110
  if(!localParams) return true;
3054
3111
 
3055
3112
  const { socket } = localParams;
3056
- const publishParams = await this.prostgles.publishParser.getPublishParams({ socket });
3057
- let res = await this.prostgles.opts.publishRawSQL(publishParams);
3113
+ const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
3114
+ let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
3058
3115
  return Boolean(res && typeof res === "boolean" || res === "*");
3059
3116
  }
3060
3117
 
@@ -3065,12 +3122,12 @@ export type TxCB = {
3065
3122
 
3066
3123
  if(returnType === "noticeSubscription"){
3067
3124
  if(!socket) throw "Only allowed with client socket"
3068
- return await this.prostgles.dbEventsManager.addNotice(socket);
3125
+ return await this.prostgles.dbEventsManager?.addNotice(socket);
3069
3126
  } else if(returnType === "statement"){
3070
3127
  try {
3071
3128
  return pgp.as.format(query, params);
3072
3129
  } catch (err){
3073
- throw err.toString();
3130
+ throw (err as any).toString();
3074
3131
  }
3075
3132
  } else if(this.db) {
3076
3133
 
@@ -3079,8 +3136,8 @@ export type TxCB = {
3079
3136
  finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
3080
3137
  }
3081
3138
 
3082
- let qres = await this.db.result(finalQuery, params)
3083
- const { duration, fields, rows, command } = qres;
3139
+ let _qres = await this.db.result(finalQuery, params)
3140
+ const { fields, rows, command } = _qres;
3084
3141
 
3085
3142
  /**
3086
3143
  * Fallback for watchSchema in case not superuser and cannot add db event listener
@@ -3102,7 +3159,7 @@ export type TxCB = {
3102
3159
 
3103
3160
  if(command === "LISTEN"){
3104
3161
  if(!socket) throw "Only allowed with client socket"
3105
- return await this.prostgles.dbEventsManager.addNotify(query, socket);
3162
+ return await this.prostgles.dbEventsManager?.addNotify(query, socket);
3106
3163
 
3107
3164
  } else if(returnType === "rows") {
3108
3165
  return rows;
@@ -3117,22 +3174,27 @@ export type TxCB = {
3117
3174
  return rows.map(r => Object.values(r[0]));
3118
3175
 
3119
3176
  } else {
3120
- if(fields && DATA_TYPES.length){
3121
- qres.fields = fields.map(f => {
3122
- const dataType = DATA_TYPES.find(dt => +dt.oid === +f.dataTypeID),
3177
+
3178
+ let qres: SQLResult = {
3179
+ duration: 0,
3180
+ ..._qres,
3181
+ fields: fields?.map(f => {
3182
+ const dataType = DATA_TYPES.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
3123
3183
  tableName = USER_TABLES.find(t => +t.relid === +f.tableID),
3124
- { name } = f;
3184
+ tsDataType = postgresToTsType(dataType);
3125
3185
 
3126
3186
  return {
3127
3187
  ...f,
3128
- ...(dataType? { dataType: dataType.typname } : {}),
3129
- ...(tableName? { tableName: tableName.relname } : {}),
3188
+ tsDataType,
3189
+ dataType,
3190
+ udt_name: dataType,
3191
+ tableName: tableName?.relname
3130
3192
  }
3131
- });
3132
- }
3193
+ }) ?? []
3194
+ };
3133
3195
  return qres;
3134
3196
  }
3135
-
3197
+
3136
3198
  } else console.error("db missing");
3137
3199
  }
3138
3200
  } else {
@@ -3159,7 +3221,7 @@ export type TxCB = {
3159
3221
  getTX = (cb: TxCB) => {
3160
3222
  return this.db.tx((t) => {
3161
3223
  let dbTX: TxHandler = {};
3162
- this.tablesOrViews.map(tov => {
3224
+ this.tablesOrViews?.map(tov => {
3163
3225
  if(tov.is_view){
3164
3226
 
3165
3227
  dbTX[tov.name] = new ViewHandler(this.db, tov, this, t, dbTX, this.joinPaths);
@@ -3416,17 +3478,20 @@ function validateObj(obj: object, allowedKeys: string[]): object{
3416
3478
  }
3417
3479
 
3418
3480
 
3419
- export function isPlainObject(o) {
3481
+ export function isPlainObject<T>(o: T): o is T | Record<string, any> {
3420
3482
  return Object(o) === o && Object.getPrototypeOf(o) === Object.prototype;
3421
3483
  }
3422
-
3484
+ export function getKeys<T>(o: T): Array<keyof T>{
3485
+ return Object.keys(o) as any
3486
+ }
3423
3487
  export function postgresToTsType(udt_data_type: PG_COLUMN_UDT_DATA_TYPE): keyof typeof TS_PG_Types {
3424
- return Object.keys(TS_PG_Types).find(k => {
3488
+ return getKeys(TS_PG_Types).find(k => {
3489
+ // @ts-ignore
3425
3490
  return TS_PG_Types[k].includes(udt_data_type) || !TS_PG_Types[k].length;
3426
3491
  }) as keyof typeof TS_PG_Types;
3427
3492
  }
3428
3493
 
3429
- function sqlErrCodeToMsg(code){
3494
+ function sqlErrCodeToMsg(code: string){
3430
3495
  const errs = {
3431
3496
  "00000": "successful_completion",
3432
3497
  "01000": "warning",
@@ -3671,6 +3736,7 @@ function sqlErrCodeToMsg(code){
3671
3736
  },
3672
3737
  c2 = {"20000":"case_not_found","21000":"cardinality_violation","22000":"data_exception","22001":"string_data_right_truncation","22002":"null_value_no_indicator_parameter","22003":"numeric_value_out_of_range","22004":"null_value_not_allowed","22005":"error_in_assignment","22007":"invalid_datetime_format","22008":"datetime_field_overflow","22009":"invalid_time_zone_displacement_value","22010":"invalid_indicator_parameter_value","22011":"substring_error","22012":"division_by_zero","22013":"invalid_preceding_or_following_size","22014":"invalid_argument_for_ntile_function","22015":"interval_field_overflow","22016":"invalid_argument_for_nth_value_function","22018":"invalid_character_value_for_cast","22019":"invalid_escape_character","22021":"character_not_in_repertoire","22022":"indicator_overflow","22023":"invalid_parameter_value","22024":"unterminated_c_string","22025":"invalid_escape_sequence","22026":"string_data_length_mismatch","22027":"trim_error","22030":"duplicate_json_object_key_value","22031":"invalid_argument_for_sql_json_datetime_function","22032":"invalid_json_text","22033":"invalid_sql_json_subscript","22034":"more_than_one_sql_json_item","22035":"no_sql_json_item","22036":"non_numeric_sql_json_item","22037":"non_unique_keys_in_a_json_object","22038":"singleton_sql_json_item_required","22039":"sql_json_array_not_found","23000":"integrity_constraint_violation","23001":"restrict_violation","23502":"not_null_violation","23503":"foreign_key_violation","23505":"unique_violation","23514":"check_violation","24000":"invalid_cursor_state","25000":"invalid_transaction_state","25001":"active_sql_transaction","25002":"branch_transaction_already_active","25003":"inappropriate_access_mode_for_branch_transaction","25004":"inappropriate_isolation_level_for_branch_transaction","25005":"no_active_sql_transaction_for_branch_transaction","25006":"read_only_sql_transaction","25007":"schema_and_data_statement_mixing_not_supported","25008":"held_cursor_requires_same_isolation_level","26000":"invalid_sql_statement_name","27000":"triggered_data_change_violation","28000":"invalid_authorization_specification","34000":"invalid_cursor_name","38000":"external_routine_exception","38001":"containing_sql_not_permitted","38002":"modifying_sql_data_not_permitted","38003":"prohibited_sql_statement_attempted","38004":"reading_sql_data_not_permitted","39000":"external_routine_invocation_exception","39001":"invalid_sqlstate_returned","39004":"null_value_not_allowed","40000":"transaction_rollback","40001":"serialization_failure","40002":"transaction_integrity_constraint_violation","40003":"statement_completion_unknown","42000":"syntax_error_or_access_rule_violation","42501":"insufficient_privilege","42601":"syntax_error","42602":"invalid_name","42611":"invalid_column_definition","42622":"name_too_long","42701":"duplicate_column","42702":"ambiguous_column","42703":"undefined_column","42704":"undefined_object","42710":"duplicate_object","42712":"duplicate_alias","42723":"duplicate_function","42725":"ambiguous_function","42803":"grouping_error","42804":"datatype_mismatch","42809":"wrong_object_type","42830":"invalid_foreign_key","42846":"cannot_coerce","42883":"undefined_function","42939":"reserved_name","44000":"with_check_option_violation","53000":"insufficient_resources","53100":"disk_full","53200":"out_of_memory","53300":"too_many_connections","53400":"configuration_limit_exceeded","54000":"program_limit_exceeded","54001":"statement_too_complex","54011":"too_many_columns","54023":"too_many_arguments","55000":"object_not_in_prerequisite_state","55006":"object_in_use","57000":"operator_intervention","57014":"query_canceled","58000":"system_error","58030":"io_error","72000":"snapshot_too_old","00000":"successful_completion","01000":"warning","0100C":"dynamic_result_sets_returned","01008":"implicit_zero_bit_padding","01003":"null_value_eliminated_in_set_function","01007":"privilege_not_granted","01006":"privilege_not_revoked","01004":"string_data_right_truncation","01P01":"deprecated_feature","02000":"no_data","02001":"no_additional_dynamic_result_sets_returned","03000":"sql_statement_not_yet_complete","08000":"connection_exception","08003":"connection_does_not_exist","08006":"connection_failure","08001":"sqlclient_unable_to_establish_sqlconnection","08004":"sqlserver_rejected_establishment_of_sqlconnection","08007":"transaction_resolution_unknown","08P01":"protocol_violation","09000":"triggered_action_exception","0A000":"feature_not_supported","0B000":"invalid_transaction_initiation","0F000":"locator_exception","0F001":"invalid_locator_specification","0L000":"invalid_grantor","0LP01":"invalid_grant_operation","0P000":"invalid_role_specification","0Z000":"diagnostics_exception","0Z002":"stacked_diagnostics_accessed_without_active_handler","2202E":"array_subscript_error","2200B":"escape_character_conflict","2201E":"invalid_argument_for_logarithm","2201F":"invalid_argument_for_power_function","2201G":"invalid_argument_for_width_bucket_function","2200D":"invalid_escape_octet","22P06":"nonstandard_use_of_escape_character","2201B":"invalid_regular_expression","2201W":"invalid_row_count_in_limit_clause","2201X":"invalid_row_count_in_result_offset_clause","2202H":"invalid_tablesample_argument","2202G":"invalid_tablesample_repeat","2200C":"invalid_use_of_escape_character","2200G":"most_specific_type_mismatch","2200H":"sequence_generator_limit_exceeded","2200F":"zero_length_character_string","22P01":"floating_point_exception","22P02":"invalid_text_representation","22P03":"invalid_binary_representation","22P04":"bad_copy_file_format","22P05":"untranslatable_character","2200L":"not_an_xml_document","2200M":"invalid_xml_document","2200N":"invalid_xml_content","2200S":"invalid_xml_comment","2200T":"invalid_xml_processing_instruction","2203A":"sql_json_member_not_found","2203B":"sql_json_number_not_found","2203C":"sql_json_object_not_found","2203D":"too_many_json_array_elements","2203E":"too_many_json_object_members","2203F":"sql_json_scalar_required","23P01":"exclusion_violation","25P01":"no_active_sql_transaction","25P02":"in_failed_sql_transaction","25P03":"idle_in_transaction_session_timeout","28P01":"invalid_password","2B000":"dependent_privilege_descriptors_still_exist","2BP01":"dependent_objects_still_exist","2D000":"invalid_transaction_termination","2F000":"sql_routine_exception","2F005":"function_executed_no_return_statement","2F002":"modifying_sql_data_not_permitted","2F003":"prohibited_sql_statement_attempted","2F004":"reading_sql_data_not_permitted","39P01":"trigger_protocol_violated","39P02":"srf_protocol_violated","39P03":"event_trigger_protocol_violated","3B000":"savepoint_exception","3B001":"invalid_savepoint_specification","3D000":"invalid_catalog_name","3F000":"invalid_schema_name","40P01":"deadlock_detected","42P20":"windowing_error","42P19":"invalid_recursion","42P18":"indeterminate_datatype","42P21":"collation_mismatch","42P22":"indeterminate_collation","428C9":"generated_always","42P01":"undefined_table","42P02":"undefined_parameter","42P03":"duplicate_cursor","42P04":"duplicate_database","42P05":"duplicate_prepared_statement","42P06":"duplicate_schema","42P07":"duplicate_table","42P08":"ambiguous_parameter","42P09":"ambiguous_alias","42P10":"invalid_column_reference","42P11":"invalid_cursor_definition","42P12":"invalid_database_definition","42P13":"invalid_function_definition","42P14":"invalid_prepared_statement_definition","42P15":"invalid_schema_definition","42P16":"invalid_table_definition","42P17":"invalid_object_definition","55P02":"cant_change_runtime_param","55P03":"lock_not_available","55P04":"unsafe_new_enum_value_usage","57P01":"admin_shutdown","57P02":"crash_shutdown","57P03":"cannot_connect_now","57P04":"database_dropped","58P01":"undefined_file","58P02":"duplicate_file","F0000":"config_file_error","F0001":"lock_file_exists","HV000":"fdw_error","HV005":"fdw_column_name_not_found","HV002":"fdw_dynamic_parameter_value_needed","HV010":"fdw_function_sequence_error","HV021":"fdw_inconsistent_descriptor_information","HV024":"fdw_invalid_attribute_value","HV007":"fdw_invalid_column_name","HV008":"fdw_invalid_column_number","HV004":"fdw_invalid_data_type","HV006":"fdw_invalid_data_type_descriptors","HV091":"fdw_invalid_descriptor_field_identifier","HV00B":"fdw_invalid_handle","HV00C":"fdw_invalid_option_index","HV00D":"fdw_invalid_option_name","HV090":"fdw_invalid_string_length_or_buffer_length","HV00A":"fdw_invalid_string_format","HV009":"fdw_invalid_use_of_null_pointer","HV014":"fdw_too_many_handles","HV001":"fdw_out_of_memory","HV00P":"fdw_no_schemas","HV00J":"fdw_option_name_not_found","HV00K":"fdw_reply_handle","HV00Q":"fdw_schema_not_found","HV00R":"fdw_table_not_found","HV00L":"fdw_unable_to_create_execution","HV00M":"fdw_unable_to_create_reply","HV00N":"fdw_unable_to_establish_connection","P0000":"plpgsql_error","P0001":"raise_exception","P0002":"no_data_found","P0003":"too_many_rows","P0004":"assert_failure","XX000":"internal_error","XX001":"data_corrupted","XX002":"index_corrupted"}
3673
3738
 
3739
+ //@ts-ignore
3674
3740
  return c2[code] || errs[code] || code;
3675
3741
 
3676
3742
  /*