prostgles-server 4.2.648 → 4.2.650

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 (79) hide show
  1. package/debug/als-build.log +4 -0
  2. package/debug/als-e2e.log +1747 -0
  3. package/debug/als-lint.log +4 -0
  4. package/debug/als-npm-test.log +712 -0
  5. package/debug/als-row-parts-e2e.log +1759 -0
  6. package/debug/als-row-parts-npm-test.log +716 -0
  7. package/debug/als-typecheck.log +8 -0
  8. package/debug/als-unified-build.log +4 -0
  9. package/debug/als-unified-lint.log +4 -0
  10. package/debug/als-unified-test-build.log +11 -0
  11. package/debug/als-unified-test-server.log +3444 -0
  12. package/debug/table-config-build.log +4 -0
  13. package/debug/table-config-npm-test.log +4731 -0
  14. package/debug/table-config-regression-after.log +3439 -0
  15. package/debug/table-config-regression-before.log +3448 -0
  16. package/debug/table-config-test-build.log +0 -0
  17. package/debug/version-bump-review-test.log +1839 -0
  18. package/dist/Audit/AuditTable.d.ts +1 -1
  19. package/dist/DboBuilder/DboBuilder.d.ts.map +1 -1
  20. package/dist/DboBuilder/DboBuilder.js +4 -2
  21. package/dist/DboBuilder/DboBuilder.js.map +1 -1
  22. package/dist/DboBuilder/DboBuilder.spec.js +3 -1
  23. package/dist/DboBuilder/DboBuilder.spec.js.map +1 -1
  24. package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
  25. package/dist/DboBuilder/TableHandler/TableHandler.js +3 -2
  26. package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
  27. package/dist/DboBuilder/TableHandler/delete.d.ts.map +1 -1
  28. package/dist/DboBuilder/TableHandler/delete.js +4 -3
  29. package/dist/DboBuilder/TableHandler/delete.js.map +1 -1
  30. package/dist/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.d.ts.map +1 -1
  31. package/dist/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.js +7 -6
  32. package/dist/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.js.map +1 -1
  33. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +1 -1
  34. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
  35. package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts +2 -2
  36. package/dist/ExecutionContext.d.ts +32 -0
  37. package/dist/ExecutionContext.d.ts.map +1 -0
  38. package/dist/ExecutionContext.js +25 -0
  39. package/dist/ExecutionContext.js.map +1 -0
  40. package/dist/JSONBSchemaValidation/validateJSONBSchemaSQL.d.ts +1 -1
  41. package/dist/Prostgles.d.ts +5 -0
  42. package/dist/Prostgles.d.ts.map +1 -1
  43. package/dist/Prostgles.js +5 -0
  44. package/dist/Prostgles.js.map +1 -1
  45. package/dist/PubSubManager/PubSubManager.d.ts +1 -1
  46. package/dist/PubSubManager/init/getPubSubManagerInitQuery.d.ts.map +1 -1
  47. package/dist/PubSubManager/init/getPubSubManagerInitQuery.js +11 -3
  48. package/dist/PubSubManager/init/getPubSubManagerInitQuery.js.map +1 -1
  49. package/dist/PublishParser/PublishParser.d.ts.map +1 -1
  50. package/dist/PublishParser/PublishParser.js +14 -1
  51. package/dist/PublishParser/PublishParser.js.map +1 -1
  52. package/dist/PublishParser/getFunctionRelatedRecords.d.ts +5 -0
  53. package/dist/PublishParser/getFunctionRelatedRecords.d.ts.map +1 -0
  54. package/dist/PublishParser/getFunctionRelatedRecords.js +26 -0
  55. package/dist/PublishParser/getFunctionRelatedRecords.js.map +1 -0
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/initProstgles.d.ts +3 -0
  60. package/dist/initProstgles.d.ts.map +1 -1
  61. package/dist/initProstgles.js +1 -0
  62. package/dist/initProstgles.js.map +1 -1
  63. package/dist/runClientRequest.d.ts.map +1 -1
  64. package/dist/runClientRequest.js +6 -1
  65. package/dist/runClientRequest.js.map +1 -1
  66. package/lib/DboBuilder/DboBuilder.spec.ts +3 -7
  67. package/lib/DboBuilder/DboBuilder.ts +4 -2
  68. package/lib/DboBuilder/TableHandler/TableHandler.ts +14 -11
  69. package/lib/DboBuilder/TableHandler/delete.ts +14 -11
  70. package/lib/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.ts +28 -21
  71. package/lib/ExecutionContext.ts +60 -0
  72. package/lib/Prostgles.ts +8 -4
  73. package/lib/PubSubManager/init/getPubSubManagerInitQuery.ts +11 -3
  74. package/lib/PublishParser/PublishParser.ts +17 -1
  75. package/lib/PublishParser/getFunctionRelatedRecords.ts +25 -0
  76. package/lib/index.ts +2 -4
  77. package/lib/initProstgles.ts +5 -4
  78. package/lib/runClientRequest.ts +16 -8
  79. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { isArray } from "../../utils/utils";
4
4
  import type { LocalParams } from "../DboBuilder";
5
5
  import type { TableHandler } from "./TableHandler";
6
6
  import { isApplicableHook } from "./isApplicableHook";
7
+ import { runTableHook } from "../../ExecutionContext";
7
8
 
8
9
  /**
9
10
  * Prevent same-table same-command after-hook re-entry within the same tx.
@@ -82,10 +83,12 @@ export const executeAfterHooksCheckAndPostValidation = async ({
82
83
  );
83
84
  if (!isApplicable) continue;
84
85
  if (hook.type === "afterEach") {
85
- await hook.validate({
86
- ...commonParams,
87
- localParams,
88
- });
86
+ await runTableHook(tableHandler, [row], () =>
87
+ hook.validate({
88
+ ...commonParams,
89
+ localParams,
90
+ }),
91
+ );
89
92
  } else if (hook.type === "postValidate") {
90
93
  if (!localParams) throw new Error("Unexpected: no localParams for postValidate");
91
94
  await hook.validate({
@@ -117,27 +120,31 @@ export const executeAfterHooksCheckAndPostValidation = async ({
117
120
  });
118
121
  if (!applicableRows.length && !applicableData.length) continue;
119
122
  if (hook.type === "afterAll") {
120
- await hook.validate({
121
- ...txParams,
122
- command,
123
- data: applicableData,
124
- rows: applicableRows,
125
- localParams,
126
- context: tableHandler.dboBuilder.prostgles.context,
127
- });
128
- } else if (hook.type === "afterCommit" && applicableRows.length) {
129
- const context = tableHandler.dboBuilder.prostgles.context;
130
- txParams.onCommit(({ db, dbo }) =>
131
- hook.run({
132
- rows: applicableRows,
123
+ await runTableHook(tableHandler, applicableRows, () =>
124
+ hook.validate({
125
+ ...txParams,
133
126
  command,
134
- context,
135
- db,
136
- dbo,
137
- getClientDBHandlers: tableHandler.dboBuilder.prostgles.getClientDBHandlers,
127
+ data: applicableData,
128
+ rows: applicableRows,
138
129
  localParams,
130
+ context: tableHandler.dboBuilder.prostgles.context,
139
131
  }),
140
132
  );
133
+ } else if (hook.type === "afterCommit" && applicableRows.length) {
134
+ const context = tableHandler.dboBuilder.prostgles.context;
135
+ runTableHook(tableHandler, applicableRows, () =>
136
+ txParams.onCommit(({ db, dbo }) =>
137
+ hook.run({
138
+ rows: applicableRows,
139
+ command,
140
+ context,
141
+ db,
142
+ dbo,
143
+ getClientDBHandlers: tableHandler.dboBuilder.prostgles.getClientDBHandlers,
144
+ localParams,
145
+ }),
146
+ ),
147
+ );
141
148
  }
142
149
  }
143
150
  } finally {
@@ -0,0 +1,60 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { AuthClientRequest, SessionUser } from "./Auth/AuthTypes";
3
+ import type { ViewHandler } from "./DboBuilder/ViewHandler/ViewHandler";
4
+
5
+ export type FunctionInvocation = {
6
+ type: "function";
7
+ id: string;
8
+ functionName: string;
9
+ relatedRecords: {
10
+ argName: string;
11
+ tableName: string;
12
+ rowPart: Record<string, unknown>;
13
+ }[];
14
+ };
15
+
16
+ export type TriggerInvocation = {
17
+ type: "trigger";
18
+ id: string;
19
+ tableName: string;
20
+ rowParts: Record<string, unknown>[];
21
+ };
22
+
23
+ type Invocation = FunctionInvocation | TriggerInvocation;
24
+
25
+ /** Correlation metadata only; database authorization still uses the explicit caller. */
26
+ export type ExecutionContext = {
27
+ runId: string;
28
+ user: undefined | SessionUser["user"];
29
+ clientReq: undefined | AuthClientRequest;
30
+ /** Invocation ancestry, oldest first. Parallel branches have separate lists. */
31
+ invocations: Invocation[];
32
+ };
33
+
34
+ /** Record the row parts available to the hook, including rows without primary keys. */
35
+ export const runTableHook = <T>(
36
+ table: ViewHandler,
37
+ rows: Record<string, unknown>[],
38
+ callback: () => T,
39
+ ): T => {
40
+ const prostgles = table.dboBuilder.prostgles;
41
+ const parent = createExecutionContext(prostgles.getExecution());
42
+ const invocation: TriggerInvocation = {
43
+ type: "trigger",
44
+ id: randomUUID(),
45
+ tableName: table.name,
46
+ rowParts: rows,
47
+ };
48
+ return prostgles.runWithExecution(
49
+ { ...parent, invocations: [...parent.invocations, invocation] },
50
+ callback,
51
+ );
52
+ };
53
+
54
+ export const createExecutionContext = (parent: ExecutionContext | undefined): ExecutionContext =>
55
+ parent ?? {
56
+ runId: randomUUID(),
57
+ invocations: [],
58
+ clientReq: undefined,
59
+ user: undefined,
60
+ };
package/lib/Prostgles.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { DB_GENERATED_NAMES } from "./DBSchemaBuilder/constants";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import type { ExecutionContext } from "./ExecutionContext";
2
4
  import type pgPromise from "pg-promise";
3
5
  import { AuthHandler } from "./Auth/AuthHandler";
4
6
  import type { SessionUser } from "./Auth/AuthTypes";
@@ -6,10 +8,7 @@ import type { ContextCleanup, OnInitReason } from "./initProstgles";
6
8
  import { initProstgles } from "./initProstgles";
7
9
  import type { SchemaWatch } from "./SchemaWatch/SchemaWatch";
8
10
  import { getClientSchema } from "./WebsocketAPI/getClientSchema";
9
- import {
10
- getClientDBHandlers,
11
- type GetClientDBHandlers,
12
- } from "./WebsocketAPI/getClientHandlers";
11
+ import { getClientDBHandlers, type GetClientDBHandlers } from "./WebsocketAPI/getClientHandlers";
13
12
  import { onSocketConnected } from "./WebsocketAPI/onSocketConnected";
14
13
  import pg = require("pg-promise/typescript/pg-subset");
15
14
 
@@ -266,6 +265,10 @@ export class Prostgles {
266
265
  }
267
266
 
268
267
  context: unknown;
268
+ readonly executionStorage = new AsyncLocalStorage<ExecutionContext | undefined>();
269
+ getExecution = () => this.executionStorage.getStore();
270
+ runWithExecution = <T>(execution: ExecutionContext | undefined, callback: () => T): T =>
271
+ this.executionStorage.run(execution, callback);
269
272
  private contextCleanups: ContextCleanup[] = [];
270
273
 
271
274
  cleanupContext = async () => {
@@ -296,6 +299,7 @@ export class Prostgles {
296
299
  sql: this.dboBuilder.sql,
297
300
  tables: this.dboBuilder.tables,
298
301
  reason,
302
+ getExecution: this.getExecution,
299
303
  onCleanup: (cleanup) => cleanups.push(cleanup),
300
304
  });
301
305
  this.context = context;
@@ -57,6 +57,8 @@ BEGIN; -- TRANSACTION ISOLATION LEVEL SERIALIZABLE;
57
57
 
58
58
  DO
59
59
  $do$
60
+ DECLARE
61
+ reset_pubsub BOOLEAN := false;
60
62
  BEGIN
61
63
 
62
64
  /* Drop older version. */
@@ -69,21 +71,21 @@ BEGIN
69
71
  ${PROSTGLES_SCHEMA_EXISTS_QUERY}
70
72
  )
71
73
  THEN
72
- DROP SCHEMA IF EXISTS prostgles CASCADE;
74
+ reset_pubsub := true;
73
75
  ELSIF
74
76
  /* There is no newer version or same same version but different schema */
75
77
  NOT EXISTS (
76
78
  ${PROSTGLES_SCHEMA_VERSION_OK_QUERY}
77
79
  )
78
80
  THEN
79
- DROP SCHEMA IF EXISTS prostgles CASCADE;
81
+ reset_pubsub := true;
80
82
 
81
83
  END IF;
82
84
 
83
85
  END IF;
84
86
 
85
87
 
86
- IF NOT EXISTS (
88
+ IF reset_pubsub OR NOT EXISTS (
87
89
  SELECT 1
88
90
  FROM information_schema.schemata
89
91
  WHERE schema_name = 'prostgles'
@@ -93,6 +95,12 @@ BEGIN
93
95
  CREATE SCHEMA IF NOT EXISTS prostgles;
94
96
  COMMENT ON SCHEMA prostgles IS 'Used by prostgles-server to enable data/schema change tracking through subscribe/sync/watchSchema';
95
97
 
98
+ -- Keep application dependencies on the validator and helper functions intact.
99
+ DROP FUNCTION IF EXISTS ${DB_OBJ_NAMES.schema_watch_func}() CASCADE;
100
+ DROP FUNCTION IF EXISTS ${DB_OBJ_NAMES.data_watch_func}() CASCADE;
101
+ DROP FUNCTION IF EXISTS ${DB_OBJ_NAMES.trigger_add_remove_func}() CASCADE;
102
+ DROP TABLE IF EXISTS prostgles.app_triggers, prostgles.apps, prostgles.versions CASCADE;
103
+
96
104
  ${CREATE_VALIDATE_SCHEMA_FUNCTION_SQL}
97
105
 
98
106
  CREATE TABLE IF NOT EXISTS prostgles.versions(
@@ -36,6 +36,9 @@ import {
36
36
  type PublishObject,
37
37
  } from "./publishTypesAndUtils";
38
38
  import { validatePublishProfiles } from "./validatePublishProfiles";
39
+ import { randomUUID } from "node:crypto";
40
+ import { createExecutionContext } from "../ExecutionContext";
41
+ import { getFunctionRelatedRecords } from "./getFunctionRelatedRecords";
39
42
 
40
43
  export class PublishParser {
41
44
  parsedPublish: ReturnType<typeof getParsedPublish>;
@@ -144,7 +147,20 @@ export class PublishParser {
144
147
  })();
145
148
 
146
149
  this.prostgles.checkNotDestroyed();
147
- return method.run(args, ctx);
150
+ const parent = createExecutionContext(this.prostgles.getExecution());
151
+ const relatedRecords = getFunctionRelatedRecords(method.input, args);
152
+ return this.prostgles.runWithExecution(
153
+ {
154
+ ...parent,
155
+ user,
156
+ clientReq,
157
+ invocations: [
158
+ ...parent.invocations,
159
+ { type: "function", id: randomUUID(), functionName: name, relatedRecords },
160
+ ],
161
+ },
162
+ () => method.run(args, ctx),
163
+ );
148
164
  };
149
165
  allowedFunctionsMap.set(name, { ...method, run: runWithContext });
150
166
  }
@@ -0,0 +1,25 @@
1
+ import type { FunctionInvocation } from "../ExecutionContext";
2
+ import type { ServerFunctionDefinition } from "./defineServerFunction";
3
+
4
+ /** Capture top-level RowLookup/ValueLookup inputs without querying the database. */
5
+ export const getFunctionRelatedRecords = (
6
+ input: ServerFunctionDefinition["input"],
7
+ args: Record<string, unknown> | undefined,
8
+ ): FunctionInvocation["relatedRecords"] => {
9
+ const records: FunctionInvocation["relatedRecords"] = [];
10
+ for (const [argName, schema] of Object.entries(input ?? {})) {
11
+ if (typeof schema !== "object" || !("table" in schema)) continue;
12
+ const isRow = schema.type === "RowLookup" || schema.type === "RowLookup[]";
13
+ const isValue = schema.type === "ValueLookup" || schema.type === "ValueLookup[]";
14
+ if (!isRow && !isValue) continue;
15
+ const value = args?.[argName];
16
+ if (value === undefined || value === null) continue;
17
+ const values: unknown[] = schema.type.endsWith("[]") ? (value as unknown[]) : [value];
18
+ for (const item of values) {
19
+ const rowPart =
20
+ "column" in schema ? { [schema.column]: item } : (item as Record<string, unknown>);
21
+ records.push({ argName, tableName: schema.table, rowPart });
22
+ }
23
+ }
24
+ return records;
25
+ };
package/lib/index.ts CHANGED
@@ -28,6 +28,7 @@ export const createProstgles = <
28
28
  };
29
29
  export * from "./PublishParser/defineServerFunction";
30
30
  export * from "./Auth/AuthTypes";
31
+ export type { ExecutionContext, FunctionInvocation, TriggerInvocation } from "./ExecutionContext";
31
32
 
32
33
  export type {
33
34
  PublishContextValue,
@@ -42,10 +43,7 @@ export type {
42
43
  PublishProfile,
43
44
  PublishParams,
44
45
  } from "./PublishParser/publishTypesAndUtils";
45
- export type {
46
- ClientDBHandlerRequest,
47
- GetClientDBHandlers,
48
- } from "./WebsocketAPI/getClientHandlers";
46
+ export type { ClientDBHandlerRequest, GetClientDBHandlers } from "./WebsocketAPI/getClientHandlers";
49
47
  export type { DBOFullyTyped, DBOFullyTypedClient } from "./DBSchemaBuilder/DBSchemaBuilder";
50
48
  export type { DBHandlerServer } from "./Prostgles";
51
49
  export type { StorageClient as CloudClient } from "./StorageClient/StorageClientTypes";
@@ -1,4 +1,5 @@
1
1
  import { syncTableTriggers } from "./TableConfig/syncTableTriggers";
2
+ import type { ExecutionContext } from "./ExecutionContext";
2
3
  import { deferDbQueries } from "./deferDbQueries";
3
4
  import type pgPromise from "pg-promise";
4
5
  import type pg from "pg-promise/typescript/pg-subset";
@@ -18,10 +19,7 @@ import { runSQLFile } from "./TableConfig/runSQLFile";
18
19
  import { updateConfiguration, type clientOnlyUpdateKeys } from "./updateConfiguration";
19
20
  import { sleep } from "./utils/utils";
20
21
  import { getAdminClient } from "./DboBuilder/runSql/getAdminClient";
21
- import type {
22
- ClientDBHandlerRequest,
23
- GetClientDBHandlers,
24
- } from "./WebsocketAPI/getClientHandlers";
22
+ import type { ClientDBHandlerRequest, GetClientDBHandlers } from "./WebsocketAPI/getClientHandlers";
25
23
 
26
24
  /**
27
25
  * Database connection details
@@ -93,6 +91,8 @@ export type OnReadyParams<S, Context = undefined> = OnReadyParamsCommon & {
93
91
  export type ContextCleanup = () => MaybePromise<void>;
94
92
  export type CreateContextParams<S> = OnReadyParamsCommon & {
95
93
  dbo: DBOFullyTyped<S>;
94
+ /** Read at call time; undefined outside a function, client operation, or table hook. */
95
+ getExecution: () => ExecutionContext | undefined;
96
96
  onCleanup: (cleanup: ContextCleanup) => void;
97
97
  };
98
98
  export type CreateContext<S, Context> = (params: CreateContextParams<S>) => MaybePromise<Context>;
@@ -324,6 +324,7 @@ export const initProstgles = async function (
324
324
  }
325
325
  this.restApi?.destroy();
326
326
  await this.cleanupContext();
327
+ this.executionStorage.disable();
327
328
  await this.dboBuilder.destroy();
328
329
  this.authHandler.destroy();
329
330
  await this.tableConfigurator?.destroy();
@@ -12,10 +12,10 @@ import type { LocalParams } from "./DboBuilder/DboBuilder";
12
12
  import type { DbTxTableHandlers } from "./DboBuilder/DboBuilderTypes";
13
13
  import type { TableHandler as TableHandlerServer } from "./DboBuilder/TableHandler/TableHandler";
14
14
  import { canRunSQL } from "./DboBuilder/runSql/runSQL";
15
+ import { createExecutionContext } from "./ExecutionContext";
15
16
  import type { Prostgles } from "./Prostgles";
16
17
  import type { ParsedTableRule } from "./PublishParser/publishTypesAndUtils";
17
18
  import { type PermissionScope } from "./PublishParser/publishTypesAndUtils";
18
- import { getAbortSignalKeys } from "./DboBuilder/ViewHandler/getDbHandlerWithAbort";
19
19
 
20
20
  const TABLE_METHODS = {
21
21
  find: 1,
@@ -184,13 +184,21 @@ export const runClientRequest = async function (
184
184
  undefined | TableMethodFunctionWithRulesAndLocalParams;
185
185
 
186
186
  this.checkNotDestroyed();
187
- return (tableHandler[command] as TableMethodFunctionWithRulesAndLocalParams)(
188
- param1,
189
- param2,
190
- param3,
191
- parsedTableRule,
192
- localParams,
193
- ) as AnyObject | undefined;
187
+ return this.runWithExecution(
188
+ {
189
+ ...createExecutionContext(this.getExecution()),
190
+ user: clientInfo.user,
191
+ clientReq,
192
+ },
193
+ () =>
194
+ (tableHandler[command] as TableMethodFunctionWithRulesAndLocalParams)(
195
+ param1,
196
+ param2,
197
+ param3,
198
+ parsedTableRule,
199
+ localParams,
200
+ ) as AnyObject | undefined,
201
+ );
194
202
  };
195
203
 
196
204
  export const clientCanRunSqlRequest = async function (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.648",
3
+ "version": "4.2.650",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",