prostgles-server 4.2.625 → 4.2.627

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 (172) hide show
  1. package/debug/prostgles-types-4.0.285.tgz +0 -0
  2. package/dist/Audit/AuditTable.d.ts +2 -1
  3. package/dist/Audit/AuditTable.d.ts.map +1 -1
  4. package/dist/Audit/AuditTable.js.map +1 -1
  5. package/dist/Audit/getAuditTableRules.d.ts.map +1 -1
  6. package/dist/Audit/getAuditTableRules.js +4 -4
  7. package/dist/Audit/getAuditTableRules.js.map +1 -1
  8. package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.d.ts.map +1 -1
  9. package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js +7 -1
  10. package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js.map +1 -1
  11. package/dist/DboBuilder/QueryBuilder/Functions/Functions.d.ts +3 -1
  12. package/dist/DboBuilder/QueryBuilder/Functions/Functions.d.ts.map +1 -1
  13. package/dist/DboBuilder/QueryBuilder/Functions/Functions.js +18 -11
  14. package/dist/DboBuilder/QueryBuilder/Functions/Functions.js.map +1 -1
  15. package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.d.ts.map +1 -1
  16. package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.js +16 -21
  17. package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.js.map +1 -1
  18. package/dist/DboBuilder/QueryBuilder/Functions/utils.d.ts +1 -0
  19. package/dist/DboBuilder/QueryBuilder/Functions/utils.d.ts.map +1 -1
  20. package/dist/DboBuilder/QueryBuilder/Functions/utils.js +3 -1
  21. package/dist/DboBuilder/QueryBuilder/Functions/utils.js.map +1 -1
  22. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +18 -2
  23. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
  24. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +54 -16
  25. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
  26. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +1 -1
  27. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
  28. package/dist/DboBuilder/QueryBuilder/getNewQuery.js +40 -6
  29. package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
  30. package/dist/DboBuilder/QueryBuilder/parseJoinSelect.d.ts.map +1 -1
  31. package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js +2 -12
  32. package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js.map +1 -1
  33. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts +3 -1
  34. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts.map +1 -1
  35. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js +21 -6
  36. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js.map +1 -1
  37. package/dist/DboBuilder/TableHandler/updateFile.d.ts +2 -39
  38. package/dist/DboBuilder/TableHandler/updateFile.d.ts.map +1 -1
  39. package/dist/DboBuilder/TableHandler/updateFile.js +1 -0
  40. package/dist/DboBuilder/TableHandler/updateFile.js.map +1 -1
  41. package/dist/DboBuilder/TableHandler/uploadFile.d.ts +2 -1
  42. package/dist/DboBuilder/TableHandler/uploadFile.d.ts.map +1 -1
  43. package/dist/DboBuilder/TableHandler/uploadFile.js +2 -1
  44. package/dist/DboBuilder/TableHandler/uploadFile.js.map +1 -1
  45. package/dist/DboBuilder/ViewHandler/find.d.ts.map +1 -1
  46. package/dist/DboBuilder/ViewHandler/find.js +2 -29
  47. package/dist/DboBuilder/ViewHandler/find.js.map +1 -1
  48. package/dist/DboBuilder/ViewHandler/getRawInfo.d.ts.map +1 -1
  49. package/dist/DboBuilder/ViewHandler/getRawInfo.js +6 -2
  50. package/dist/DboBuilder/ViewHandler/getRawInfo.js.map +1 -1
  51. package/dist/DboBuilder/ViewHandler/parseComplexFilter.d.ts +4 -1
  52. package/dist/DboBuilder/ViewHandler/parseComplexFilter.d.ts.map +1 -1
  53. package/dist/DboBuilder/ViewHandler/parseComplexFilter.js +9 -2
  54. package/dist/DboBuilder/ViewHandler/parseComplexFilter.js.map +1 -1
  55. package/dist/DboBuilder/ViewHandler/subscribe.d.ts.map +1 -1
  56. package/dist/DboBuilder/ViewHandler/subscribe.js +8 -3
  57. package/dist/DboBuilder/ViewHandler/subscribe.js.map +1 -1
  58. package/dist/DboBuilder/ViewHandler/validateSelectParams.d.ts +3 -0
  59. package/dist/DboBuilder/ViewHandler/validateSelectParams.d.ts.map +1 -0
  60. package/dist/DboBuilder/ViewHandler/validateSelectParams.js +48 -0
  61. package/dist/DboBuilder/ViewHandler/validateSelectParams.js.map +1 -0
  62. package/dist/DboBuilder/getColumns.d.ts.map +1 -1
  63. package/dist/DboBuilder/getColumns.js +3 -0
  64. package/dist/DboBuilder/getColumns.js.map +1 -1
  65. package/dist/DboBuilder/getCondition.d.ts.map +1 -1
  66. package/dist/DboBuilder/getCondition.js +9 -3
  67. package/dist/DboBuilder/getCondition.js.map +1 -1
  68. package/dist/DboBuilder/getSubscribeRelatedTables.d.ts.map +1 -1
  69. package/dist/DboBuilder/getSubscribeRelatedTables.js +7 -2
  70. package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
  71. package/dist/ProstglesTypes.d.ts +10 -0
  72. package/dist/ProstglesTypes.d.ts.map +1 -1
  73. package/dist/ProstglesTypes.js.map +1 -1
  74. package/dist/PubSubManager/PubSubManager.d.ts +1 -1
  75. package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
  76. package/dist/PubSubManager/PubSubManager.js +1 -1
  77. package/dist/PubSubManager/PubSubManager.js.map +1 -1
  78. package/dist/PubSubManager/addSub.js +1 -1
  79. package/dist/PubSubManager/addSub.js.map +1 -1
  80. package/dist/PublishParser/PublishParser.d.ts.map +1 -1
  81. package/dist/PublishParser/PublishParser.js +7 -0
  82. package/dist/PublishParser/PublishParser.js.map +1 -1
  83. package/dist/PublishParser/getFileTableRules.d.ts.map +1 -1
  84. package/dist/PublishParser/getFileTableRules.js +4 -1
  85. package/dist/PublishParser/getFileTableRules.js.map +1 -1
  86. package/dist/PublishParser/getFileVersionTableRules.d.ts +7 -0
  87. package/dist/PublishParser/getFileVersionTableRules.d.ts.map +1 -0
  88. package/dist/PublishParser/getFileVersionTableRules.js +77 -0
  89. package/dist/PublishParser/getFileVersionTableRules.js.map +1 -0
  90. package/dist/PublishParser/getFileVersionTableRules.spec.d.ts +2 -0
  91. package/dist/PublishParser/getFileVersionTableRules.spec.d.ts.map +1 -0
  92. package/dist/PublishParser/getFileVersionTableRules.spec.js +50 -0
  93. package/dist/PublishParser/getFileVersionTableRules.spec.js.map +1 -0
  94. package/dist/PublishParser/getPublishedTableNames.d.ts.map +1 -1
  95. package/dist/PublishParser/getPublishedTableNames.js +11 -4
  96. package/dist/PublishParser/getPublishedTableNames.js.map +1 -1
  97. package/dist/StorageClient/deleteUnreferencedFile.d.ts.map +1 -1
  98. package/dist/StorageClient/deleteUnreferencedFile.js +17 -1
  99. package/dist/StorageClient/deleteUnreferencedFile.js.map +1 -1
  100. package/dist/StorageClient/fileTableDefinitions.d.ts +54 -0
  101. package/dist/StorageClient/fileTableDefinitions.d.ts.map +1 -0
  102. package/dist/StorageClient/fileTableDefinitions.js +38 -0
  103. package/dist/StorageClient/fileTableDefinitions.js.map +1 -0
  104. package/dist/StorageClient/fileVersionUtils.d.ts +18 -0
  105. package/dist/StorageClient/fileVersionUtils.d.ts.map +1 -0
  106. package/dist/StorageClient/fileVersionUtils.js +22 -0
  107. package/dist/StorageClient/fileVersionUtils.js.map +1 -0
  108. package/dist/StorageClient/getFileStorageKey.d.ts +3 -1
  109. package/dist/StorageClient/getFileStorageKey.d.ts.map +1 -1
  110. package/dist/StorageClient/getFileStorageKey.js +9 -1
  111. package/dist/StorageClient/getFileStorageKey.js.map +1 -1
  112. package/dist/StorageClient/getFileTableConfig.d.ts +1 -23
  113. package/dist/StorageClient/getFileTableConfig.d.ts.map +1 -1
  114. package/dist/StorageClient/getFileTableConfig.js +63 -25
  115. package/dist/StorageClient/getFileTableConfig.js.map +1 -1
  116. package/dist/StorageClient/getFileTableConfig.spec.js +6 -1
  117. package/dist/StorageClient/getFileTableConfig.spec.js.map +1 -1
  118. package/dist/StorageClient/saveFileVersion.d.ts +7 -0
  119. package/dist/StorageClient/saveFileVersion.d.ts.map +1 -0
  120. package/dist/StorageClient/saveFileVersion.js +71 -0
  121. package/dist/StorageClient/saveFileVersion.js.map +1 -0
  122. package/dist/StorageClient/setupFileServeHandler.d.ts.map +1 -1
  123. package/dist/StorageClient/setupFileServeHandler.js +38 -21
  124. package/dist/StorageClient/setupFileServeHandler.js.map +1 -1
  125. package/dist/TableConfig/TableRowFromColumnDefinitions.d.ts +11 -4
  126. package/dist/TableConfig/TableRowFromColumnDefinitions.d.ts.map +1 -1
  127. package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
  128. package/dist/TableConfig/initTableConfig.js +6 -6
  129. package/dist/TableConfig/initTableConfig.js.map +1 -1
  130. package/dist/index.d.ts +2 -2
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js.map +1 -1
  133. package/lib/Audit/AuditTable.ts +4 -1
  134. package/lib/Audit/getAuditTableRules.ts +6 -2
  135. package/lib/DBSchemaBuilder/getClientDBGeneratedSchemas.ts +9 -1
  136. package/lib/DboBuilder/QueryBuilder/Functions/Functions.ts +38 -13
  137. package/lib/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.ts +28 -24
  138. package/lib/DboBuilder/QueryBuilder/Functions/utils.ts +10 -0
  139. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +95 -18
  140. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +58 -10
  141. package/lib/DboBuilder/QueryBuilder/parseJoinSelect.ts +2 -13
  142. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +34 -7
  143. package/lib/DboBuilder/TableHandler/updateFile.ts +1 -0
  144. package/lib/DboBuilder/TableHandler/uploadFile.ts +3 -0
  145. package/lib/DboBuilder/ViewHandler/find.ts +2 -32
  146. package/lib/DboBuilder/ViewHandler/getRawInfo.ts +6 -2
  147. package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +10 -2
  148. package/lib/DboBuilder/ViewHandler/subscribe.ts +10 -3
  149. package/lib/DboBuilder/ViewHandler/validateSelectParams.ts +51 -0
  150. package/lib/DboBuilder/getColumns.ts +2 -0
  151. package/lib/DboBuilder/getCondition.ts +11 -3
  152. package/lib/DboBuilder/getSubscribeRelatedTables.ts +10 -3
  153. package/lib/ProstglesTypes.ts +11 -0
  154. package/lib/PubSubManager/PubSubManager.ts +1 -1
  155. package/lib/PubSubManager/addSub.ts +1 -1
  156. package/lib/PublishParser/PublishParser.ts +25 -7
  157. package/lib/PublishParser/getFileTableRules.ts +7 -1
  158. package/lib/PublishParser/getFileVersionTableRules.spec.ts +57 -0
  159. package/lib/PublishParser/getFileVersionTableRules.ts +106 -0
  160. package/lib/PublishParser/getPublishedTableNames.ts +14 -5
  161. package/lib/StorageClient/deleteUnreferencedFile.ts +20 -2
  162. package/lib/StorageClient/fileTableDefinitions.ts +52 -0
  163. package/lib/StorageClient/fileVersionUtils.ts +37 -0
  164. package/lib/StorageClient/getFileStorageKey.ts +11 -2
  165. package/lib/StorageClient/getFileTableConfig.spec.ts +6 -1
  166. package/lib/StorageClient/getFileTableConfig.ts +85 -30
  167. package/lib/StorageClient/saveFileVersion.ts +82 -0
  168. package/lib/StorageClient/setupFileServeHandler.ts +51 -28
  169. package/lib/TableConfig/TableRowFromColumnDefinitions.ts +37 -13
  170. package/lib/TableConfig/initTableConfig.ts +5 -6
  171. package/lib/index.ts +2 -1
  172. package/package.json +2 -2
@@ -1,7 +1,13 @@
1
1
  import type { SelectParams } from "prostgles-types";
2
- import { asName, isEmpty, omitKeys } from "prostgles-types";
2
+ import { asName, isEmpty, isObject, omitKeys } from "prostgles-types";
3
3
  import type { ParsedTableRule } from "../../PublishParser/PublishParser";
4
- import type { Filter, LocalParams, PGIdentifier, ValidatedTableRules } from "../DboBuilder";
4
+ import {
5
+ prepareOrderByQuery,
6
+ type Filter,
7
+ type LocalParams,
8
+ type PGIdentifier,
9
+ type ValidatedTableRules,
10
+ } from "../DboBuilder";
5
11
  import type { ViewHandler } from "../ViewHandler/ViewHandler";
6
12
  import { parseJoinPath } from "../ViewHandler/parseJoinPath";
7
13
  import { prepareSortItems } from "../ViewHandler/prepareSortItems";
@@ -21,22 +27,31 @@ export const getNewQuery = async (
21
27
  ): Promise<NewQuery> => {
22
28
  const { columns } = viewHandler;
23
29
 
24
- if (localParams?.isRemoteRequest && !tableRules?.select?.fields) {
30
+ const selectRule = tableRules?.select;
31
+ if (tableRules && !selectRule?.fields) {
25
32
  throw `INTERNAL ERROR: publish.${viewHandler.name}.select.fields rule missing`;
26
33
  }
27
34
 
35
+ /** Only missing tableRules means this is an unrestricted local query */
28
36
  const allowedOrderByFields =
29
- !tableRules ?
37
+ !selectRule ?
30
38
  viewHandler.column_names.slice(0)
31
- : viewHandler.parseFieldFilter(tableRules.select?.orderByFields ?? tableRules.select?.fields);
39
+ : viewHandler.parseFieldFilter(selectRule.orderByFields);
32
40
  const allowedSelectFields =
33
- !tableRules ?
41
+ !selectRule ?
34
42
  viewHandler.column_names.slice(0)
35
- : viewHandler.parseFieldFilter(tableRules.select?.fields);
43
+ : viewHandler.parseFieldFilter(selectRule.fields);
44
+ const allowedFilterFields =
45
+ !selectRule ?
46
+ viewHandler.column_names.slice(0)
47
+ : viewHandler.parseFieldFilter(selectRule.filterFields);
36
48
 
37
49
  const joinQueries: NewQueryJoin[] = [];
38
50
 
39
51
  const { select: userSelect = "*" } = selectParams;
52
+ const aggregateTableAlias =
53
+ selectParams.joinExpressionAlias ??
54
+ ({ raw: ROOT_TABLE_ALIAS, escaped: ROOT_TABLE_ALIAS } satisfies PGIdentifier);
40
55
 
41
56
  const selectItemBuilder = new SelectItemBuilder({
42
57
  allowedFields: allowedSelectFields,
@@ -46,6 +61,39 @@ export const getNewQuery = async (
46
61
  functions: FUNCTIONS,
47
62
  allFields: viewHandler.column_names.slice(0),
48
63
  columns,
64
+ parseAggregateOptions: async ({ $filter, $orderBy }) => {
65
+ if ($filter !== undefined && !isObject($filter)) {
66
+ throw `Invalid aggregate $filter. Expecting an object`;
67
+ }
68
+ const filterInfo =
69
+ $filter === undefined ? undefined : (
70
+ await prepareWhere(viewHandler, {
71
+ filter: $filter,
72
+ select: undefined,
73
+ filterFields: allowedFilterFields,
74
+ tableAlias: aggregateTableAlias,
75
+ localParams,
76
+ addWhere: false,
77
+ tableRule: tableRules,
78
+ })
79
+ );
80
+ const orderByItems = prepareSortItems(
81
+ $orderBy,
82
+ allowedOrderByFields,
83
+ aggregateTableAlias,
84
+ [],
85
+ [],
86
+ );
87
+ return {
88
+ filter: filterInfo?.condition,
89
+ orderBy: prepareOrderByQuery(orderByItems, aggregateTableAlias.raw)[0],
90
+ dependencyFields: [
91
+ ...(filterInfo?.columnsUsed ?? []),
92
+ ...orderByItems.map(({ key }) => key),
93
+ ],
94
+ dependencyExists: filterInfo?.exists ?? [],
95
+ };
96
+ },
49
97
  });
50
98
 
51
99
  await selectItemBuilder.parseUserSelectWithJoins(
@@ -151,9 +199,9 @@ export const getNewQuery = async (
151
199
  ...omitKeys(commonWhereParams, ["forcedFilter", "selectParams"]),
152
200
  filter: selectParams.having,
153
201
  tableAlias:
154
- selectParams.joinExpressionAlias ? source.alias : (
155
- { raw: ROOT_TABLE_ALIAS, escaped: ROOT_TABLE_ALIAS }
156
- ),
202
+ selectParams.joinExpressionAlias ?
203
+ source.alias
204
+ : { raw: ROOT_TABLE_ALIAS, escaped: ROOT_TABLE_ALIAS },
157
205
  isHaving: true,
158
206
  })
159
207
  );
@@ -5,18 +5,7 @@ import type {
5
5
  RawJoinPath,
6
6
  SimpleJoinSelect,
7
7
  } from "prostgles-types";
8
- import { getKeys, includes } from "prostgles-types";
9
-
10
- const JOIN_KEYS = ["$innerJoin", "$leftJoin"] as const;
11
- const JOIN_PARAM_KEYS = getKeys({
12
- $condition: 1,
13
- filter: 1,
14
- having: 1,
15
- limit: 1,
16
- offset: 1,
17
- orderBy: 1,
18
- select: 1,
19
- } satisfies Record<keyof Omit<DetailedJoinSelect, (typeof JOIN_KEYS)[number]>, 1>);
8
+ import { getKeys, includes, JOIN_KEYS, JOIN_PARAMS } from "prostgles-types";
20
9
 
21
10
  export type ParsedJoin =
22
11
  | {
@@ -47,7 +36,7 @@ export const parseJoinSelect = (joinParams: JoinSelect): ParsedJoin | string =>
47
36
  } else if (joinKey) {
48
37
  /* Full option join { field_name: db.innerJoin.table_name(filter, select) } */
49
38
  const invalidParams = Object.keys(joinParams).filter(
50
- (k) => !includes([...JOIN_PARAM_KEYS, ...JOIN_KEYS], k),
39
+ (k) => !includes([...JOIN_PARAMS, ...JOIN_KEYS], k),
51
40
  );
52
41
  if (invalidParams.length) {
53
42
  throw "Invalid join params: " + invalidParams.join(", ");
@@ -4,6 +4,11 @@ import { asName } from "prostgles-types";
4
4
  import type { FileTableConfig } from "../../ProstglesTypes";
5
5
  import type { OnCommit } from "../../PublishParser/publishTypesAndUtils";
6
6
  import { deleteUnreferencedFile } from "../../StorageClient/deleteUnreferencedFile";
7
+ import {
8
+ getFileRevisionsTableHandler,
9
+ getFileTableHandler,
10
+ } from "../../StorageClient/fileVersionUtils";
11
+ import type { TableHandlers } from "../DboBuilder";
7
12
 
8
13
  type OnDeleteFromFileTableArgs = {
9
14
  returningQuery: undefined | string;
@@ -12,12 +17,14 @@ type OnDeleteFromFileTableArgs = {
12
17
  filter: AnyObject;
13
18
  };
14
19
  t: pgPromise.ITask<{}>;
20
+ dbx: TableHandlers;
15
21
  onCommit: OnCommit;
16
22
  isOneOrNone: boolean;
17
23
  };
24
+
18
25
  export const onDeleteFromFileTable = async (
19
26
  config: FileTableConfig,
20
- { onCommit, t, returningQuery, filterOpts, isOneOrNone }: OnDeleteFromFileTableArgs,
27
+ { onCommit, t, dbx, returningQuery, filterOpts, isOneOrNone }: OnDeleteFromFileTableArgs,
21
28
  ) => {
22
29
  const { tableName } = config;
23
30
  if (config.delayedDelete) {
@@ -27,15 +34,35 @@ export const onDeleteFromFileTable = async (
27
34
  return (isOneOrNone ? result[0] : result) as undefined | AnyObject[];
28
35
  }
29
36
 
30
- const files = await t.any<Record<string, unknown> & { _prostgles_storage_key: string }>(
37
+ const fileIds =
38
+ config.versioning ?
39
+ await getFileTableHandler(dbx, config).find(filterOpts.filter, {
40
+ select: { id: 1 },
41
+ limit: null,
42
+ })
43
+ : [];
44
+ const versionFiles =
45
+ fileIds.length ?
46
+ await getFileRevisionsTableHandler(dbx, config).find(
47
+ { file_id: { $in: fileIds.map(({ id }) => id) } },
48
+ { select: { storage_key: 1 }, limit: null },
49
+ )
50
+ : [];
51
+
52
+ const KEY = "_prostgles_storage_key" as const;
53
+ const files = await t.any<Record<string, unknown> & { [KEY]: string }>(
31
54
  `DELETE FROM ${asName(tableName)} ${filterOpts.where}
32
55
  ${returningQuery ? `${returningQuery},` : "RETURNING"}
33
- COALESCE(storage_key, id) AS _prostgles_storage_key`,
56
+ COALESCE(storage_key, id) AS ${KEY}`,
34
57
  );
35
- const result = files.map(({ _prostgles_storage_key, ...row }) => {
36
- onCommit(({ db }) => deleteUnreferencedFile(db, config, _prostgles_storage_key));
37
- return row;
38
- });
58
+ const storageKeys = new Set([
59
+ ...versionFiles.map(({ storage_key }) => storage_key),
60
+ ...files.map((row) => row[KEY]),
61
+ ]);
62
+ for (const storageKey of storageKeys) {
63
+ onCommit(({ db }) => deleteUnreferencedFile(db, config, storageKey));
64
+ }
65
+ const result = files.map(({ [KEY]: _, ...row }) => row);
39
66
  if (!returningQuery) return undefined;
40
67
  return (isOneOrNone ? result[0] : result) as AnyObject[] | undefined;
41
68
  };
@@ -33,6 +33,7 @@ export const updateFile = async (
33
33
  data,
34
34
  localParams,
35
35
  mediaId: existingFile.id,
36
+ version: Number(existingFile.version ?? 1) + 1,
36
37
  original_last_modified,
37
38
  });
38
39
  return { newData: omitKeys(newFile, ["id"]) };
@@ -44,6 +44,7 @@ export type UploadFileArgs = TransactionCallbacks & {
44
44
  * Used to update an existing file
45
45
  */
46
46
  mediaId: string | undefined;
47
+ version?: number;
47
48
  };
48
49
 
49
50
  export const uploadFile = async (
@@ -54,6 +55,7 @@ export const uploadFile = async (
54
55
  original_last_modified,
55
56
  localParams,
56
57
  mediaId,
58
+ version,
57
59
  onCommit,
58
60
  onRollback,
59
61
  }: UploadFileArgs,
@@ -115,6 +117,7 @@ export const uploadFile = async (
115
117
  url: [fileServeRoute, coreInfo.id].join("/"),
116
118
  content_length: String(contentLength),
117
119
  data: Buffer.from([0x01]),
120
+ ...(config.versioning ? { version: version ?? 1 } : {}),
118
121
  };
119
122
 
120
123
  const isInsert = !mediaId;
@@ -11,6 +11,7 @@ import {
11
11
  import { getNewQuery } from "../QueryBuilder/getNewQuery";
12
12
  import { getSelectQuery } from "../QueryBuilder/getSelectQuery";
13
13
  import { getReturnTypeQuery } from "./getReturnTypeQuery";
14
+ import { validateSelectParams } from "./validateSelectParams";
14
15
  import type { ViewHandler } from "./ViewHandler";
15
16
 
16
17
  export type Param3 = {
@@ -30,44 +31,13 @@ export const find = async function (
30
31
 
31
32
  const command = limit === 1 && returnType === "row" ? "findOne" : "find";
32
33
  try {
33
- const allowedReturnTypes = Object.keys({
34
- row: 1,
35
- statement: 1,
36
- value: 1,
37
- values: 1,
38
- "statement-no-rls": 1,
39
- "statement-where": 1,
40
- } satisfies Record<Required<SelectParams>["returnType"], 1>);
34
+ validateSelectParams(selectParams);
41
35
 
42
36
  const { returnType } = selectParams || {};
43
- if (returnType && !allowedReturnTypes.includes(returnType)) {
44
- throw `returnType (${returnType}) can only be ${allowedReturnTypes.join(" OR ")}`;
45
- }
46
37
 
47
38
  const { testRule = false } = localParams || {};
48
39
 
49
40
  if (testRule) return [];
50
- if (selectParams) {
51
- const validParamNames = Object.keys({
52
- select: 1,
53
- orderBy: 1,
54
- offset: 1,
55
- limit: 1,
56
- returnType: 1,
57
- groupBy: 1,
58
- having: 1,
59
- abortSignal: 1,
60
- } satisfies Record<keyof SelectParams, 1>);
61
-
62
- const invalidParams = Object.keys(selectParams).filter((k) => !validParamNames.includes(k));
63
- if (invalidParams.length)
64
- throw (
65
- "Invalid params: " +
66
- invalidParams.join(", ") +
67
- " \n Expecting: " +
68
- validParamNames.join(", ")
69
- );
70
- }
71
41
 
72
42
  /* Validate publish */
73
43
  if (tableRules) {
@@ -2,6 +2,7 @@ import { type TableInfo as TInfo } from "prostgles-types/dist";
2
2
  import type { ParsedTableRule } from "../../PublishParser/PublishParser";
3
3
  import type { LocalParams } from "../DboBuilder";
4
4
  import type { ViewHandler } from "./ViewHandler";
5
+ import { isFileVersionTable } from "../../StorageClient/fileVersionUtils";
5
6
 
6
7
  /** Table metadata before client schema customisation. */
7
8
  export function getRawInfo(
@@ -11,7 +12,9 @@ export function getRawInfo(
11
12
  localParams?: LocalParams,
12
13
  ): TInfo {
13
14
  const validatedTableRules = this.getValidatedRules(tableRules, localParams);
14
- const fileTableName = this.dboBuilder.prostgles.opts.fileTable?.tableName;
15
+ const fileTableConfig = this.dboBuilder.prostgles.opts.fileTable;
16
+ const fileTableName = fileTableConfig?.tableName;
17
+ const isVersionTable = isFileVersionTable(fileTableConfig, this.name);
15
18
  const allowedFieldsToSelect = this.parseFieldFilter(tableRules?.select?.fields);
16
19
  const { requiredNestedInserts, allowedNestedInserts } = tableRules?.insert ?? {};
17
20
  const label = this.dboBuilder.prostgles.tableConfigurator?.getTableLabel({
@@ -32,10 +35,11 @@ export function getRawInfo(
32
35
  isView: this.isView,
33
36
  hasFiles: Boolean(
34
37
  !this.is_media &&
38
+ !isVersionTable &&
35
39
  fileTableName &&
36
40
  this.columns.some((c) => c.references?.some((r) => r.ftable === fileTableName)),
37
41
  ),
38
- fileTableName,
42
+ fileTableName: isVersionTable ? undefined : fileTableName,
39
43
  dynamicRules: {
40
44
  update: Boolean(tableRules?.update?.dynamicFields?.length),
41
45
  },
@@ -34,6 +34,8 @@ export const parseComplexFilter = ({
34
34
  allowed_colnames,
35
35
  columns,
36
36
  }: Args) => {
37
+ const columnsUsed: string[] = [];
38
+
37
39
  /**
38
40
  * { $funcName: [arg1, arg2] }
39
41
  * { $column: "column_name" }
@@ -45,8 +47,9 @@ export const parseComplexFilter = ({
45
47
  throw `expecting: \n { $column: "column_name" } received:\n ${JSON.stringify(funcData)}`;
46
48
  }
47
49
  if (!allowed_colnames.includes(column)) {
48
- throw `Dissallowed or Invalid column ${column}. Allowed columns: ${allowed_colnames}`;
50
+ throw `Disallowed or Invalid column ${column}. Allowed columns: ${allowed_colnames}`;
49
51
  }
52
+ columnsUsed.push(column);
50
53
  return asNameAlias(column, tableAliasRaw);
51
54
  }
52
55
  const { funcName, args } = parseFunctionObject(funcData);
@@ -56,6 +59,8 @@ export const parseComplexFilter = ({
56
59
  functions: FUNCTIONS,
57
60
  allowedFields: allowed_colnames,
58
61
  });
62
+ const fields = funcDef.getFields(args);
63
+ columnsUsed.push(...(fields === "*" ? allowed_colnames : fields));
59
64
  return funcDef.getQuery({
60
65
  args,
61
66
  tableAliasRaw,
@@ -108,5 +113,8 @@ export const parseComplexFilter = ({
108
113
  result += ` ${sqlOperand} ${rightVal}`;
109
114
  }
110
115
 
111
- return result;
116
+ return {
117
+ condition: result,
118
+ columnsUsed: Array.from(new Set(columnsUsed)),
119
+ };
112
120
  };
@@ -77,9 +77,16 @@ async function subscribe(
77
77
  newQuery,
78
78
  });
79
79
 
80
- const [firstField, ...otherFields] = newQuery.select
81
- .filter((s) => s.selected)
82
- .flatMap((c) => c.fields);
80
+ const [firstField, ...otherFields] = Array.from(
81
+ new Set([
82
+ ...newQuery.select
83
+ .filter((s) => s.selected)
84
+ .flatMap((item) => [...item.fields, ...(item.dependencyFields ?? [])]),
85
+ ...newQuery.orderByItems
86
+ .map(({ key }) => key)
87
+ .filter((key) => this.column_names.includes(key)),
88
+ ]),
89
+ );
83
90
 
84
91
  const commonSubOpts = {
85
92
  table_info: this.tableOrViewInfo,
@@ -0,0 +1,51 @@
1
+ import { getJSONBObjectSchemaValidationError, getKeys, type SelectParams } from "prostgles-types";
2
+
3
+ export const validateSelectParams = (
4
+ selectParams: any,
5
+ ): selectParams is SelectParams | undefined => {
6
+ if (selectParams === undefined) {
7
+ return true;
8
+ }
9
+ const allowedReturnTypes = getKeys({
10
+ row: 1,
11
+ statement: 1,
12
+ value: 1,
13
+ values: 1,
14
+ "statement-no-rls": 1,
15
+ "statement-where": 1,
16
+ } satisfies Record<Required<SelectParams>["returnType"], 1>);
17
+
18
+ const selectParamsValidation = getJSONBObjectSchemaValidationError(
19
+ {
20
+ select: {
21
+ oneOf: [{ enum: ["*", ""] as const }, { record: { values: "any" } }, "string[]"],
22
+ optional: true,
23
+ },
24
+ orderBy: {
25
+ oneOf: [
26
+ { record: { values: "any" } },
27
+ { arrayOf: { record: { values: "any" } } },
28
+ "string[]",
29
+ "string",
30
+ ],
31
+ optional: true,
32
+ },
33
+ offset: { type: "integer", optional: true },
34
+ limit: { type: "integer", nullable: true, optional: true },
35
+ returnType: {
36
+ enum: allowedReturnTypes,
37
+ optional: true,
38
+ },
39
+ groupBy: { type: "boolean", optional: true },
40
+ having: { record: { values: "any" }, optional: true },
41
+ abortSignal: { type: "any", optional: true },
42
+ },
43
+ selectParams,
44
+ "selectParams",
45
+ );
46
+ if (selectParamsValidation.error !== undefined) {
47
+ throw `selectParams validation error: ${JSON.stringify(selectParamsValidation.error)}`;
48
+ }
49
+ selectParamsValidation.data satisfies SelectParams;
50
+ return true;
51
+ };
@@ -6,6 +6,7 @@ import { getErrorAsObject, getSerializedClientErrorFromPGError } from "./DboBuil
6
6
  import type { TableHandler } from "./TableHandler/TableHandler";
7
7
  import type { ViewHandler } from "./ViewHandler/ViewHandler";
8
8
  import { getRawInfo } from "./ViewHandler/getRawInfo";
9
+ import { isFileVersionTable } from "../StorageClient/fileVersionUtils";
9
10
 
10
11
  export const isTableHandler = (v: any): v is TableHandler => "parseUpdateRules" in v;
11
12
 
@@ -173,6 +174,7 @@ const getFileColumnInfo = (args: {
173
174
  if (!fileTableConfig) return undefined;
174
175
  const { colName, tableHandler } = args;
175
176
  const tableName = tableHandler.name;
177
+ if (isFileVersionTable(fileTableConfig, tableName)) return undefined;
176
178
  const tableConfig = fileTableConfig.referencedTables?.[tableName];
177
179
  const isReferencingFileTable = tableHandler.columns.some(
178
180
  (c) =>
@@ -44,6 +44,11 @@ export async function getCondition(
44
44
  const filter = { ...rawFilter };
45
45
 
46
46
  const existsConfigs = getExistsFilters(filter, this);
47
+ const dependencyColumns = existsConfigs.flatMap((existsConfig) =>
48
+ existsConfig.isJoined ?
49
+ (existsConfig.parsedPath[0]?.on.flatMap((columnPair) => Object.keys(columnPair)) ?? [])
50
+ : [],
51
+ );
47
52
 
48
53
  const functionConditions: string[] = [];
49
54
  const funcFilter = FILTER_FUNCS.filter((f) => f.name in filter);
@@ -59,6 +64,7 @@ export async function getCondition(
59
64
  if (disallowedCols.length) {
60
65
  throw `Invalid/disallowed columns found in function filter: ${disallowedCols}`;
61
66
  }
67
+ dependencyColumns.push(...fields);
62
68
  functionConditions.push(
63
69
  f.getQuery({
64
70
  args: funcArgs,
@@ -89,6 +95,7 @@ export async function getCondition(
89
95
  const compCol = computedFields.find((cf) => cf.name === key);
90
96
  if (compCol) {
91
97
  if (!p.select) throw new Error("Computed column filter requires p.select.fields");
98
+ dependencyColumns.push(...p.select.fields);
92
99
  computedColConditions.push(
93
100
  compCol.getQuery({
94
101
  tableAliasRaw: tableAlias?.raw,
@@ -157,14 +164,15 @@ export async function getCondition(
157
164
  const complexFilters: string[] = [];
158
165
  const complexFilterKey = "$filter";
159
166
  if (complexFilterKey in filter) {
160
- const complexFilterCondition = parseComplexFilter({
167
+ const complexFilter = parseComplexFilter({
161
168
  filter,
162
169
  complexFilterKey,
163
170
  tableAliasRaw: tableAlias?.raw,
164
171
  allowed_colnames,
165
172
  columns: this.columns,
166
173
  });
167
- complexFilters.push(complexFilterCondition);
174
+ complexFilters.push(complexFilter.condition);
175
+ dependencyColumns.push(...complexFilter.columnsUsed);
168
176
  }
169
177
 
170
178
  /* Parse join filters
@@ -238,7 +246,7 @@ export async function getCondition(
238
246
 
239
247
  /* sorted to ensure duplicate subscription channels are not created due to different condition order */
240
248
  return {
241
- columnsUsed: q?.columnsUsed ?? [],
249
+ columnsUsed: Array.from(new Set([...(q?.columnsUsed ?? []), ...dependencyColumns])),
242
250
  exists: existsConfigs,
243
251
  condition: templates.sort().join(" AND \n"),
244
252
  };
@@ -1,5 +1,5 @@
1
1
  import type { AnyObject, ParsedJoinPath, SubscribeParams } from "prostgles-types";
2
- import { asName, isDefined, isEmpty, reverseParsedPath } from "prostgles-types";
2
+ import { asName, isEmpty, reverseParsedPath } from "prostgles-types";
3
3
  import type { ParsedTableRule } from "../PublishParser/PublishParser";
4
4
  import type { ViewSubscriptionOptions } from "../PubSubManager/PubSubManager";
5
5
  import type { Filter, LocalParams } from "./DboBuilder";
@@ -36,6 +36,9 @@ export async function getSubscribeRelatedTables(
36
36
  relatedTables: [],
37
37
  };
38
38
 
39
+ const dependencyExists = newQuery.select
40
+ .filter((item) => item.selected)
41
+ .flatMap((item) => item.dependencyExists ?? []);
39
42
  const nonExistsFilter = newQuery.whereOpts.exists.length ? {} : filter;
40
43
  const pushRelatedTable = async (
41
44
  relatedTableName: string,
@@ -112,11 +115,15 @@ export async function getSubscribeRelatedTables(
112
115
  await pushRelatedTable(
113
116
  j.table.raw,
114
117
  j.joinPath,
115
- j.select.map((s) => (s.selected ? s.columnName : undefined)).filter(isDefined),
118
+ j.select
119
+ .filter((item) => item.selected)
120
+ .flatMap((item) => [...item.fields, ...(item.dependencyFields ?? [])]),
116
121
  {},
117
122
  );
118
123
  }
119
- for (const existsFilter of newQuery.whereOpts.exists.filter((e) => e.isJoined)) {
124
+ for (const existsFilter of [...newQuery.whereOpts.exists, ...dependencyExists].filter(
125
+ (existsFilter) => existsFilter.isJoined,
126
+ )) {
120
127
  for (const [index, pathItem] of existsFilter.parsedPath.entries()) {
121
128
  const isLast = index === existsFilter.parsedPath.length - 1;
122
129
  await pushRelatedTable(
@@ -70,6 +70,17 @@ export type FileTableConfig = {
70
70
  checkIntervalHours?: number;
71
71
  };
72
72
 
73
+ /**
74
+ * Keeps immutable metadata records for every uploaded file revision.
75
+ * Existing file IDs and URLs continue to resolve to the latest version.
76
+ */
77
+ versioning?: {
78
+ /** Defaults to `${tableName}_versions`. */
79
+ tableName?: string;
80
+ /** Maximum versions kept per file. Undefined keeps every version. */
81
+ maxVersions?: number;
82
+ };
83
+
73
84
  /**
74
85
  * Specifying referencedTables with referenceColumns allows restricting the
75
86
  * allowed file types that can be inserted and referenced in the specified tables.
@@ -200,7 +200,7 @@ export class PubSubManager {
200
200
 
201
201
  subs: Subscription[] = [];
202
202
  syncs: SyncParams[] = [];
203
- readonly socketChannelPreffix = CHANNELS._preffix;
203
+ readonly socketChannelPrefix = CHANNELS._preffix;
204
204
  postgresNotifListenManager?: PostgresNotifListenManager;
205
205
 
206
206
  private constructor(dboBuilder: DboBuilder) {
@@ -45,7 +45,7 @@ export async function addSub(
45
45
  throw "addSub: cannot have socket AND func";
46
46
  }
47
47
 
48
- const channel_name = `${this.socketChannelPreffix}.${table_name}.${JSON.stringify(filter)}.${JSON.stringify(selectParams)}.m.sub`;
48
+ const channel_name = `${this.socketChannelPrefix}.${table_name}.${JSON.stringify(filter)}.${JSON.stringify(selectParams)}.m.sub`;
49
49
  const mainTrigger = {
50
50
  table_name: table_name,
51
51
  condition: parseCondition(condition),
@@ -17,6 +17,8 @@ import type {
17
17
  UnrestrictedFunctionContext,
18
18
  } from "./defineServerFunction";
19
19
  import { getFileTableRules } from "./getFileTableRules";
20
+ import { getFileVersionTableRules } from "./getFileVersionTableRules";
21
+ import { isFileVersionTable } from "../StorageClient/fileVersionUtils";
20
22
  import { getPublishedObjectFromResult } from "./getPublishedObjectFromResult";
21
23
  import { getSchemaFromPublish } from "./getSchemaFromPublish";
22
24
  import { getParsedPublishTable } from "./getParsedPublishTable";
@@ -313,13 +315,12 @@ export class PublishParser {
313
315
  (isAuditTable && clientReq ?
314
316
  await this.getPublishObjectForUser(clientReq, clientInfo)
315
317
  : undefined);
316
- const publishRulesExcludingInferredTables =
317
- await this.getParsedPublishTable({
318
- clientReq,
319
- tableName,
320
- clientInfo,
321
- resolvedPublishObject,
322
- });
318
+ const publishRulesExcludingInferredTables = await this.getParsedPublishTable({
319
+ clientReq,
320
+ tableName,
321
+ clientInfo,
322
+ resolvedPublishObject,
323
+ });
323
324
  if (audit && isAuditTable) {
324
325
  const auditTableRules = await getAuditTableRules.bind(this)(
325
326
  audit,
@@ -336,6 +337,23 @@ export class PublishParser {
336
337
  scope,
337
338
  );
338
339
  }
340
+ const fileConfig = this.prostgles.opts.fileTable;
341
+ if (isFileVersionTable(fileConfig, tableName)) {
342
+ const versionRules = await getFileVersionTableRules.bind(this)(
343
+ fileConfig,
344
+ publishRulesExcludingInferredTables,
345
+ clientReq,
346
+ clientInfo,
347
+ scope,
348
+ overriddenPublish,
349
+ );
350
+ return applyScopeToTableRules(
351
+ tableName,
352
+ tableHandler,
353
+ parsePublishTableRule(versionRules),
354
+ scope,
355
+ );
356
+ }
339
357
  if (this.dbo[tableName]?.is_media) {
340
358
  const { rules: fileTableRules } = await getFileTableRules.bind(this)(
341
359
  tableName,
@@ -5,6 +5,7 @@ import { parseFieldFilter } from "../DboBuilder/ViewHandler/parseFieldFilter";
5
5
  import type { PublishParser } from "./PublishParser";
6
6
  import type { ParsedPublishTable, PublishObject, UpdateRule } from "./publishTypesAndUtils";
7
7
  import type { LocalParams } from "../DboBuilder/DboBuilder";
8
+ import { isFileVersionTable } from "../StorageClient/fileVersionUtils";
8
9
 
9
10
  /**
10
11
  * Permissions for referencedTables columns are propagated to the file table (even if file table has no permissions)
@@ -28,7 +29,12 @@ export async function getFileTableRules(
28
29
  const forcedUpdateFilters: FullFilter<AnyObject, void>[] = [];
29
30
  const allowedNestedInserts: { table: string; column: string }[] = [];
30
31
  const referencedColumns = this.prostgles.dboBuilder.tablesOrViews
31
- ?.filter((t) => !t.is_view && t.name !== fileTableName)
32
+ ?.filter(
33
+ (t) =>
34
+ !t.is_view &&
35
+ t.name !== fileTableName &&
36
+ !isFileVersionTable(this.prostgles.opts.fileTable, t.name),
37
+ )
32
38
  .map((t) => {
33
39
  const refCols = t.columns.filter((c) =>
34
40
  c.references?.some((r) => r.ftable === fileTableName),