@memberjunction/server 6.1.4 → 6.1.5

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 (71) hide show
  1. package/README.md +30 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
  4. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  5. package/dist/auth/magicLink/MagicLinkService.js +49 -35
  6. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  7. package/dist/auth/magicLink/magicLinkCore.d.ts +14 -33
  8. package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
  9. package/dist/auth/magicLink/magicLinkCore.js +20 -52
  10. package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
  11. package/dist/context.d.ts.map +1 -1
  12. package/dist/context.js +11 -23
  13. package/dist/context.js.map +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +36 -44
  16. package/dist/index.js.map +1 -1
  17. package/dist/postgresqlCompat.d.ts +8 -0
  18. package/dist/postgresqlCompat.d.ts.map +1 -0
  19. package/dist/postgresqlCompat.js +19 -0
  20. package/dist/postgresqlCompat.js.map +1 -0
  21. package/dist/postgresqlPoolSettings.d.ts +108 -0
  22. package/dist/postgresqlPoolSettings.d.ts.map +1 -0
  23. package/dist/postgresqlPoolSettings.js +134 -0
  24. package/dist/postgresqlPoolSettings.js.map +1 -0
  25. package/dist/resolvers/AdhocQueryResolver.d.ts +10 -20
  26. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  27. package/dist/resolvers/AdhocQueryResolver.js +54 -174
  28. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  29. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
  30. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  31. package/dist/resolvers/RealtimeClientSessionResolver.js +90 -34
  32. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  33. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  34. package/dist/resolvers/RunAIAgentResolver.js +2 -1
  35. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  36. package/dist/resolvers/adhocTimeout.d.ts +7 -0
  37. package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
  38. package/dist/resolvers/adhocTimeout.js +12 -0
  39. package/dist/resolvers/adhocTimeout.js.map +1 -0
  40. package/dist/services/TaskGraphActionRunner.d.ts +13 -8
  41. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  42. package/dist/services/TaskGraphActionRunner.js +16 -15
  43. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  44. package/package.json +92 -92
  45. package/src/__tests__/AdhocQueryResolver.bugs.test.ts +12 -35
  46. package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
  47. package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
  48. package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
  49. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +129 -0
  50. package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
  51. package/src/__tests__/magicLink.test.ts +15 -76
  52. package/src/__tests__/magicLinkService.consume.test.ts +224 -0
  53. package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
  54. package/src/__tests__/translateBracketsToPG.test.ts +20 -0
  55. package/src/auth/magicLink/MagicLinkService.ts +55 -36
  56. package/src/auth/magicLink/magicLinkCore.ts +19 -56
  57. package/src/context.ts +11 -24
  58. package/src/index.ts +40 -45
  59. package/src/postgresqlCompat.ts +21 -0
  60. package/src/postgresqlPoolSettings.ts +213 -0
  61. package/src/resolvers/AdhocQueryResolver.ts +64 -194
  62. package/src/resolvers/RealtimeClientSessionResolver.ts +93 -35
  63. package/src/resolvers/RunAIAgentResolver.ts +4 -1
  64. package/src/resolvers/adhocTimeout.ts +12 -0
  65. package/src/services/TaskGraphActionRunner.ts +16 -15
  66. package/dist/resolvers/adhoc-query-helpers.d.ts +0 -27
  67. package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
  68. package/dist/resolvers/adhoc-query-helpers.js +0 -42
  69. package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
  70. package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
  71. package/src/resolvers/adhoc-query-helpers.ts +0 -50
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Connection settings for MJAPI's PostgreSQL pools, derived from the same `databaseSettings`
3
+ * values SQL Server reads, with the same defaults.
4
+ *
5
+ * | Setting | SQL Server | PostgreSQL |
6
+ * |--------------------------------------|----------------------------------|----------------------------------------------|
7
+ * | `requestTimeout` | driver request timeout | `statement_timeout` on every backend |
8
+ * | `connectionPool.max` / `min` | pool size | pool size |
9
+ * | `connectionPool.idleTimeoutMillis` | idle connection eviction | idle connection eviction |
10
+ * | `connectionPool.acquireTimeoutMillis`| wait for a pooled connection | `connectionTimeoutMillis` (see below) |
11
+ * | `connectionTimeout` | time to open a connection | not used |
12
+ *
13
+ * Where the two cannot match exactly:
14
+ * - The `pg` pool has one timeout covering both waiting for a free slot and opening a connection.
15
+ * It is fed from `acquireTimeoutMillis`; `connectionTimeout` is not used on PostgreSQL.
16
+ * - SQL Server's `requestTimeout` is a client-side timer over the whole request, including reading
17
+ * rows. PostgreSQL's `statement_timeout` is server-side execution time per statement, so a query
18
+ * that executes quickly and returns its rows slowly is not cut off on PostgreSQL.
19
+ * - `idle_in_transaction_session_timeout` has no SQL Server counterpart. It is derived as twice the
20
+ * statement timeout, so a transaction left open by an abandoned request is ended and its locks
21
+ * released.
22
+ * - MJAPI opens a raw compatibility pool and a provider pool, each up to `connectionPool.max`, plus
23
+ * a CodeGen pool of 10 and, when a read-only login is configured, a read-only pool of at most 10,
24
+ * where SQL Server opens one pool (and a read-only one). PostgreSQL's default `max_connections`
25
+ * is 100, so the read-only and CodeGen pools are kept small.
26
+ */
27
+ import type { PGConnectionConfig } from '@memberjunction/postgresql-dataprovider';
28
+ import type { Pool, PoolConfig } from 'pg';
29
+ import { BaseSingleton } from '@memberjunction/global';
30
+ import type { DatabaseSettingsInfo } from './config.js';
31
+ /** Statement timeout for the CodeGen/DDL pool, matching the SQL Server CodeGen pool. */
32
+ export declare const CODEGEN_STATEMENT_TIMEOUT_MS = 600000;
33
+ /**
34
+ * Which pool the settings are for: API traffic, the read-only pool for caller-supplied SQL, or
35
+ * CodeGen and DDL work.
36
+ */
37
+ export type PostgreSQLPoolPurpose = 'api' | 'read-only' | 'codegen';
38
+ /** Where a PostgreSQL pool connects and as whom. */
39
+ export interface PostgreSQLEndpoint {
40
+ Host: string;
41
+ Port: number;
42
+ Database: string;
43
+ User: string;
44
+ Password: string;
45
+ }
46
+ /** Reads the PostgreSQL endpoint from `PG_*` environment variables, then `DB_*` ones. */
47
+ export declare function ResolvePostgreSQLEndpoint(env?: NodeJS.ProcessEnv): PostgreSQLEndpoint;
48
+ /** The idle-in-transaction limit for a given statement timeout; 0 means none. */
49
+ export declare function IdleInTransactionTimeoutFor(statementTimeoutMs: number): number;
50
+ /** Builds the provider connection config for one of MJAPI's PostgreSQL pools. */
51
+ export declare function BuildPostgreSQLConnectionConfig(endpoint: PostgreSQLEndpoint, settings: DatabaseSettingsInfo, purpose: PostgreSQLPoolPurpose): PGConnectionConfig;
52
+ /**
53
+ * Maps a provider connection config onto `pg.Pool` options, for the pools MJAPI creates directly
54
+ * rather than through the provider. Timeouts that are unset or 0 are left out.
55
+ */
56
+ export declare function ToPGPoolConfig(config: PGConnectionConfig): PoolConfig;
57
+ /** Credentials for a read-only database login. */
58
+ export interface ReadOnlyCredentials {
59
+ User: string;
60
+ Password: string;
61
+ }
62
+ /**
63
+ * The read-only login for PostgreSQL: the `dbReadOnlyUsername` / `dbReadOnlyPassword` settings
64
+ * SQL Server uses (which default from `DB_READ_ONLY_USERNAME` / `DB_READ_ONLY_PASSWORD`), then
65
+ * `PG_READ_ONLY_USERNAME` / `PG_READ_ONLY_PASSWORD`. `null` when neither pair is complete.
66
+ */
67
+ export declare function ResolvePostgreSQLReadOnlyCredentials(configured: {
68
+ dbReadOnlyUsername?: string;
69
+ dbReadOnlyPassword?: string;
70
+ }, env?: NodeJS.ProcessEnv): ReadOnlyCredentials | null;
71
+ /**
72
+ * Holds MJAPI's PostgreSQL read-only pool, opened once at startup with the read-only login.
73
+ * Per-request read-only providers share it, so they connect as the read-only user rather than
74
+ * through the read-write pool.
75
+ */
76
+ export declare class PostgreSQLReadOnlyPool extends BaseSingleton<PostgreSQLReadOnlyPool> {
77
+ private _pool;
78
+ /** Use {@link PostgreSQLReadOnlyPool.Instance}. */
79
+ constructor();
80
+ static get Instance(): PostgreSQLReadOnlyPool;
81
+ /** The read-only pool, or `null` when no read-only login is configured. */
82
+ get Pool(): Pool | null;
83
+ set Pool(pool: Pool | null);
84
+ }
85
+ /** What {@link DescribeReadOnlyLoginOverreach} reads about the read-only login. */
86
+ export interface ReadOnlyLoginGrants {
87
+ ReadsServerFiles: boolean;
88
+ ReadableBaseTables: number;
89
+ }
90
+ /** The one method {@link DescribeReadOnlyLoginOverreach} needs from a connection; a `pg` client has it. */
91
+ export interface ReadOnlyLoginCheckClient {
92
+ query(sql: string, params: unknown[]): Promise<{
93
+ rows: ReadOnlyLoginGrants[];
94
+ }>;
95
+ }
96
+ /**
97
+ * What the PostgreSQL read-only login can do beyond reading entity base views, as warnings to log at
98
+ * startup. Ad-hoc SQL and `TestQuerySQL` run as this login, and SQL can reach data through functions
99
+ * that no check of its table references sees (`query_to_xml`, file readers), so the login's own
100
+ * grants are the only complete limit. It should hold `SELECT` on the entity base views and nothing
101
+ * else: not on the base tables, and not membership of `pg_read_server_files`. Empty when the login
102
+ * is that narrow.
103
+ *
104
+ * @param client - A connection opened as the read-only login.
105
+ * @param coreSchema - The MJ core schema, whose base tables the login should not read.
106
+ */
107
+ export declare function DescribeReadOnlyLoginOverreach(client: ReadOnlyLoginCheckClient, coreSchema: string): Promise<string[]>;
108
+ //# sourceMappingURL=postgresqlPoolSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresqlPoolSettings.d.ts","sourceRoot":"","sources":["../src/postgresqlPoolSettings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,wFAAwF;AACxF,eAAO,MAAM,4BAA4B,SAAS,CAAC;AAQnD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;AAEpE,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,yFAAyF;AACzF,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,kBAAkB,CAQlG;AAED,iFAAiF;AACjF,wBAAgB,2BAA2B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,iFAAiF;AACjF,wBAAgB,+BAA+B,CAC3C,QAAQ,EAAE,kBAAkB,EAC5B,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,qBAAqB,GAC/B,kBAAkB,CAuBpB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,UAAU,CAerE;AAED,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAChD,UAAU,EAAE;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,EACxE,GAAG,GAAE,MAAM,CAAC,UAAwB,GACrC,mBAAmB,GAAG,IAAI,CAQ5B;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,aAAa,CAAC,sBAAsB,CAAC;IAC7E,OAAO,CAAC,KAAK,CAAqB;IAElC,mDAAmD;;IAKnD,WAAkB,QAAQ,IAAI,sBAAsB,CAEnD;IAED,2EAA2E;IAC3E,IAAW,IAAI,IAAI,IAAI,GAAG,IAAI,CAE7B;IAED,IAAW,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAEhC;CACJ;AAED,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;CAC9B;AAED,2GAA2G;AAC3G,MAAM,WAAW,wBAAwB;IACrC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,mBAAmB,EAAE,CAAA;KAAE,CAAC,CAAC;CACnF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,8BAA8B,CAAC,MAAM,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB5H"}
@@ -0,0 +1,134 @@
1
+ import { BaseSingleton } from '@memberjunction/global';
2
+ /** Statement timeout for the CodeGen/DDL pool, matching the SQL Server CodeGen pool. */
3
+ export const CODEGEN_STATEMENT_TIMEOUT_MS = 600000;
4
+ /** Pool size for the CodeGen/DDL pool. */
5
+ const CODEGEN_MAX_CONNECTIONS = 10;
6
+ /** Largest pool size for the read-only pool, which serves only caller-supplied SQL. */
7
+ const READ_ONLY_MAX_CONNECTIONS = 10;
8
+ /** Reads the PostgreSQL endpoint from `PG_*` environment variables, then `DB_*` ones. */
9
+ export function ResolvePostgreSQLEndpoint(env = process.env) {
10
+ return {
11
+ Host: env.PG_HOST || env.DB_HOST || 'localhost',
12
+ Port: parseInt(env.PG_PORT || env.DB_PORT || '5432', 10),
13
+ Database: env.PG_DATABASE || env.DB_DATABASE || '',
14
+ User: env.PG_USERNAME || env.DB_USERNAME || 'postgres',
15
+ Password: env.PG_PASSWORD || env.DB_PASSWORD || ''
16
+ };
17
+ }
18
+ /** The idle-in-transaction limit for a given statement timeout; 0 means none. */
19
+ export function IdleInTransactionTimeoutFor(statementTimeoutMs) {
20
+ return statementTimeoutMs > 0 ? statementTimeoutMs * 2 : 0;
21
+ }
22
+ /** Builds the provider connection config for one of MJAPI's PostgreSQL pools. */
23
+ export function BuildPostgreSQLConnectionConfig(endpoint, settings, purpose) {
24
+ const pool = settings.connectionPool;
25
+ if (purpose === 'codegen') {
26
+ return {
27
+ ...endpoint,
28
+ MaxConnections: CODEGEN_MAX_CONNECTIONS,
29
+ StatementTimeoutMs: CODEGEN_STATEMENT_TIMEOUT_MS,
30
+ IdleInTransactionSessionTimeoutMs: 0
31
+ };
32
+ }
33
+ const statementTimeoutMs = Math.max(0, settings.requestTimeout);
34
+ const apiMax = pool?.max ?? 50;
35
+ const apiMin = pool?.min ?? 5;
36
+ const readOnly = purpose === 'read-only';
37
+ return {
38
+ ...endpoint,
39
+ MaxConnections: readOnly ? Math.min(READ_ONLY_MAX_CONNECTIONS, apiMax) : apiMax,
40
+ MinConnections: readOnly ? 0 : apiMin,
41
+ IdleTimeoutMillis: pool?.idleTimeoutMillis ?? 30000,
42
+ ConnectionTimeoutMillis: pool?.acquireTimeoutMillis ?? 30000,
43
+ StatementTimeoutMs: statementTimeoutMs,
44
+ IdleInTransactionSessionTimeoutMs: IdleInTransactionTimeoutFor(statementTimeoutMs)
45
+ };
46
+ }
47
+ /**
48
+ * Maps a provider connection config onto `pg.Pool` options, for the pools MJAPI creates directly
49
+ * rather than through the provider. Timeouts that are unset or 0 are left out.
50
+ */
51
+ export function ToPGPoolConfig(config) {
52
+ const pool = {
53
+ host: config.Host,
54
+ port: config.Port,
55
+ database: config.Database,
56
+ user: config.User,
57
+ password: config.Password,
58
+ max: config.MaxConnections
59
+ };
60
+ if (config.MinConnections !== undefined)
61
+ pool.min = config.MinConnections;
62
+ if (config.IdleTimeoutMillis !== undefined)
63
+ pool.idleTimeoutMillis = config.IdleTimeoutMillis;
64
+ if (config.ConnectionTimeoutMillis !== undefined)
65
+ pool.connectionTimeoutMillis = config.ConnectionTimeoutMillis;
66
+ if (config.StatementTimeoutMs)
67
+ pool.statement_timeout = config.StatementTimeoutMs;
68
+ if (config.IdleInTransactionSessionTimeoutMs)
69
+ pool.idle_in_transaction_session_timeout = config.IdleInTransactionSessionTimeoutMs;
70
+ return pool;
71
+ }
72
+ /**
73
+ * The read-only login for PostgreSQL: the `dbReadOnlyUsername` / `dbReadOnlyPassword` settings
74
+ * SQL Server uses (which default from `DB_READ_ONLY_USERNAME` / `DB_READ_ONLY_PASSWORD`), then
75
+ * `PG_READ_ONLY_USERNAME` / `PG_READ_ONLY_PASSWORD`. `null` when neither pair is complete.
76
+ */
77
+ export function ResolvePostgreSQLReadOnlyCredentials(configured, env = process.env) {
78
+ if (configured.dbReadOnlyUsername && configured.dbReadOnlyPassword) {
79
+ return { User: configured.dbReadOnlyUsername, Password: configured.dbReadOnlyPassword };
80
+ }
81
+ if (env.PG_READ_ONLY_USERNAME && env.PG_READ_ONLY_PASSWORD) {
82
+ return { User: env.PG_READ_ONLY_USERNAME, Password: env.PG_READ_ONLY_PASSWORD };
83
+ }
84
+ return null;
85
+ }
86
+ /**
87
+ * Holds MJAPI's PostgreSQL read-only pool, opened once at startup with the read-only login.
88
+ * Per-request read-only providers share it, so they connect as the read-only user rather than
89
+ * through the read-write pool.
90
+ */
91
+ export class PostgreSQLReadOnlyPool extends BaseSingleton {
92
+ /** Use {@link PostgreSQLReadOnlyPool.Instance}. */
93
+ constructor() {
94
+ super();
95
+ this._pool = null;
96
+ }
97
+ static get Instance() {
98
+ return PostgreSQLReadOnlyPool.getInstance();
99
+ }
100
+ /** The read-only pool, or `null` when no read-only login is configured. */
101
+ get Pool() {
102
+ return this._pool;
103
+ }
104
+ set Pool(pool) {
105
+ this._pool = pool;
106
+ }
107
+ }
108
+ /**
109
+ * What the PostgreSQL read-only login can do beyond reading entity base views, as warnings to log at
110
+ * startup. Ad-hoc SQL and `TestQuerySQL` run as this login, and SQL can reach data through functions
111
+ * that no check of its table references sees (`query_to_xml`, file readers), so the login's own
112
+ * grants are the only complete limit. It should hold `SELECT` on the entity base views and nothing
113
+ * else: not on the base tables, and not membership of `pg_read_server_files`. Empty when the login
114
+ * is that narrow.
115
+ *
116
+ * @param client - A connection opened as the read-only login.
117
+ * @param coreSchema - The MJ core schema, whose base tables the login should not read.
118
+ */
119
+ export async function DescribeReadOnlyLoginOverreach(client, coreSchema) {
120
+ const result = await client.query(`SELECT pg_has_role(current_user, 'pg_read_server_files', 'MEMBER') AS "ReadsServerFiles",
121
+ (SELECT count(*)::int FROM pg_catalog.pg_tables t
122
+ WHERE t.schemaname = $1
123
+ AND has_table_privilege(current_user, quote_ident(t.schemaname) || '.' || quote_ident(t.tablename), 'SELECT')) AS "ReadableBaseTables"`, [coreSchema]);
124
+ const row = result.rows[0];
125
+ const warnings = [];
126
+ if (row?.ReadableBaseTables > 0) {
127
+ warnings.push(`The PostgreSQL read-only login can read ${row.ReadableBaseTables} base table(s) in schema "${coreSchema}". Ad-hoc SQL and TestQuerySQL run as this login; grant it SELECT on the entity base views only.`);
128
+ }
129
+ if (row?.ReadsServerFiles) {
130
+ warnings.push('The PostgreSQL read-only login is a member of pg_read_server_files, so SQL run as it can read files on the database server. Revoke that membership.');
131
+ }
132
+ return warnings;
133
+ }
134
+ //# sourceMappingURL=postgresqlPoolSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresqlPoolSettings.js","sourceRoot":"","sources":["../src/postgresqlPoolSettings.ts"],"names":[],"mappings":"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"}
@@ -14,10 +14,15 @@ declare class AdhocQueryInput {
14
14
  /**
15
15
  * Resolver for executing ad-hoc (unsaved) SQL queries.
16
16
  *
17
+ * The query runs through the read-only provider's own ad-hoc path (`RunQuery` with `SQL`), the
18
+ * same one in-process callers use, so it behaves identically on every database platform: the SQL
19
+ * is validated and rendered there, paged in the database with a count, run under the caller's
20
+ * timeout, and on PostgreSQL inside a read-only transaction that is rolled back.
21
+ *
17
22
  * Security:
18
- * - SQL validated via SQLExpressionValidator (full_query context) — blocks mutations, dangerous operations
19
- * - Executes on read-only connection pool only (no fallback to read-write)
20
- * - Configurable timeout (default 30s)
23
+ * - SQL validated by the provider (SQLExpressionValidator, single read query) — blocks mutations
24
+ * - Read-only provider only (no fallback to read-write)
25
+ * - Configurable timeout (default 30s), enforced by the database
21
26
  * - Requires authenticated user (standard GraphQL auth, no @RequireSystemUser)
22
27
  * - Refuses scope-limited principals (see {@link IsScopeLimitedPrincipal}). Raw SQL bypasses
23
28
  * RunView, entity permissions and row-level security, so the RLS scope tokens that confine a
@@ -28,23 +33,8 @@ declare class AdhocQueryInput {
28
33
  */
29
34
  export declare class AdhocQueryResolver extends ResolverBase {
30
35
  ExecuteAdhocQuery(input: AdhocQueryInput, context: AppContext): Promise<RunQueryResultType>;
31
- /**
32
- * Runs the page's data SQL, then — only when the page is FULL (so more rows may
33
- * exist) — a COUNT(*) for the true total. A short (or unpaged) page needs no
34
- * count: the exact total is `startRow + rowsReturned` (see {@link exactTotalFromPage}).
35
- *
36
- * The count is NON-FATAL: some queries page fine but cannot be counted — e.g.
37
- * duplicate column names are legal in a result set but rejected inside the COUNT
38
- * CTE wrap. A count failure must never sink the whole result, so we log it and
39
- * report a lower-bound total (`startRow + rowsReturned`) and let the data render.
40
- */
41
- private executeDataAndCount;
42
- /**
43
- * Executes one SQL statement on the read-only pool, racing it against the shared
44
- * wall-clock `deadline`. The timer is always cleared on completion so a settled
45
- * query never leaves a dangling timeout armed.
46
- */
47
- private runSqlWithDeadline;
36
+ /** Maps the provider's result to the GraphQL shape, naming a timeout plainly. */
37
+ private toResultType;
48
38
  private buildErrorResult;
49
39
  }
50
40
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"AdhocQueryResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAIxD;;;GAGG;AACH,cACM,eAAe;IAEjB,GAAG,EAAE,MAAM,CAAC;IAGZ,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBACa,kBAAmB,SAAQ,YAAY;IAE1C,iBAAiB,CACkB,KAAK,EAAE,eAAe,EACpD,OAAO,EAAE,UAAU,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IAgI9B;;;;;;;;;OASG;YACW,mBAAmB;IA6BjC;;;;OAIG;YACW,kBAAkB;IAwBhC,OAAO,CAAC,gBAAgB;CAc3B"}
1
+ {"version":3,"file":"AdhocQueryResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD;;;GAGG;AACH,cACM,eAAe;IAEjB,GAAG,EAAE,MAAM,CAAC;IAGZ,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,kBAAmB,SAAQ,YAAY;IAE1C,iBAAiB,CACkB,KAAK,EAAE,eAAe,EACpD,OAAO,EAAE,UAAU,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IAgC9B,iFAAiF;IACjF,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,gBAAgB;CAc3B"}
@@ -12,14 +12,12 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  };
13
13
  import { Arg, Ctx, Query, Resolver, Field, Int, InputType } from 'type-graphql';
14
14
  import { LogError } from '@memberjunction/core';
15
- import { SQLExpressionValidator } from '@memberjunction/global';
16
- import { RenderPipeline } from '@memberjunction/generic-database-provider';
17
- import { GetReadOnlyDataSource, GetReadOnlyProvider } from '../util.js';
15
+ import { configInfo } from '../config.js';
16
+ import { GetReadOnlyProvider } from '../util.js';
18
17
  import { ResolverBase } from '../generic/ResolverBase.js';
19
18
  import { IsScopeLimitedPrincipal } from '../auth/scopeLimitedPrincipal.js';
20
19
  import { RunQueryResultType } from './QueryResolver.js';
21
- import { exactTotalFromPage, resolveAdhocTotalRowCount } from './adhoc-query-helpers.js';
22
- import sql from 'mssql';
20
+ import { ClampAdhocTimeoutSeconds } from './adhocTimeout.js';
23
21
  /**
24
22
  * Input type for executing ad-hoc SQL queries directly.
25
23
  * The SQL is validated server-side to ensure it's a safe SELECT/WITH statement.
@@ -48,10 +46,15 @@ AdhocQueryInput = __decorate([
48
46
  /**
49
47
  * Resolver for executing ad-hoc (unsaved) SQL queries.
50
48
  *
49
+ * The query runs through the read-only provider's own ad-hoc path (`RunQuery` with `SQL`), the
50
+ * same one in-process callers use, so it behaves identically on every database platform: the SQL
51
+ * is validated and rendered there, paged in the database with a count, run under the caller's
52
+ * timeout, and on PostgreSQL inside a read-only transaction that is rolled back.
53
+ *
51
54
  * Security:
52
- * - SQL validated via SQLExpressionValidator (full_query context) — blocks mutations, dangerous operations
53
- * - Executes on read-only connection pool only (no fallback to read-write)
54
- * - Configurable timeout (default 30s)
55
+ * - SQL validated by the provider (SQLExpressionValidator, single read query) — blocks mutations
56
+ * - Read-only provider only (no fallback to read-write)
57
+ * - Configurable timeout (default 30s), enforced by the database
55
58
  * - Requires authenticated user (standard GraphQL auth, no @RequireSystemUser)
56
59
  * - Refuses scope-limited principals (see {@link IsScopeLimitedPrincipal}). Raw SQL bypasses
57
60
  * RunView, entity permissions and row-level security, so the RLS scope tokens that confine a
@@ -63,180 +66,57 @@ AdhocQueryInput = __decorate([
63
66
  let AdhocQueryResolver = class AdhocQueryResolver extends ResolverBase {
64
67
  async ExecuteAdhocQuery(input, context) {
65
68
  const startTime = Date.now();
69
+ // SECURITY: refuse principals whose read authority is narrower than their role grant.
70
+ // Ad-hoc SQL does not go through RunView, entity permissions or row-level security, so the
71
+ // RLS scope tokens that confine a magic-link session are never substituted. There is no
72
+ // filter to narrow, so the only correct answer for such a principal is no.
73
+ if (IsScopeLimitedPrincipal(context.userPayload?.userRecord)) {
74
+ return this.buildErrorResult('Ad-hoc SQL execution is not permitted for scope-limited sessions.');
75
+ }
76
+ const provider = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: false });
77
+ if (!provider) {
78
+ return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
79
+ }
80
+ const contextUser = context.userPayload?.userRecord;
81
+ const timeoutSeconds = ClampAdhocTimeoutSeconds(input.TimeoutSeconds, configInfo.databaseSettings.requestTimeout);
82
+ // A negative offset must not reach paging, where it would turn paging off.
83
+ const startRow = Math.max(0, Number.isInteger(input.StartRow) ? input.StartRow : 0);
66
84
  try {
67
- // 1. SECURITY: refuse principals whose read authority is narrower than their role
68
- // grant. This resolver runs a raw SELECT on the read-only pool — it does NOT go
69
- // through RunView, entity permissions or row-level security, so the RLS scope tokens
70
- // that confine a magic-link session are never substituted here. There is no filter
71
- // to narrow, so the only correct answer for such a principal is no. Checked before
72
- // the SQL is even validated: authorization gates the work, it does not race it.
73
- if (IsScopeLimitedPrincipal(context.userPayload?.userRecord)) {
74
- return this.buildErrorResult('Ad-hoc SQL execution is not permitted for scope-limited sessions.');
75
- }
76
- // 2. Security: validate SQL using SQLExpressionValidator
77
- const validator = SQLExpressionValidator.Instance;
78
- const validation = validator.validateFullQuery(input.SQL);
79
- if (!validation.valid) {
80
- return this.buildErrorResult(validation.error || 'SQL validation failed');
81
- }
82
- // 3. Get READ-ONLY data source (no fallback to read-write)
83
- let readOnlyDS;
84
- try {
85
- readOnlyDS = GetReadOnlyDataSource(context.dataSources, { allowFallbackToReadWrite: false });
86
- }
87
- catch {
88
- return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
89
- }
90
- // 4. Resolve platform from the read-only provider for the render pipeline.
91
- let platform = 'sqlserver';
92
- try {
93
- const provider = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: false });
94
- if (provider?.PlatformKey)
95
- platform = provider.PlatformKey;
96
- }
97
- catch {
98
- // Provider not configured — keep the default platform.
99
- }
100
- const contextUser = context.userPayload?.userRecord;
101
- // 5. Route the SQL through RenderPipeline so composition tokens
102
- // resolve, comments and templates are processed, and the row cap
103
- // is applied at the database (via TOP / LIMIT / OFFSET-FETCH).
104
- //
105
- // Page whenever a positive MaxRows is requested — INCLUDING the first
106
- // page (StartRow 0). This yields OFFSET/FETCH data SQL *and* a
107
- // COUNT(*) query so the response reports the true total row count
108
- // rather than just the returned page size. It mirrors the saved-query
109
- // path (GenericDatabaseProvider.InternalRunQuery → WrapWithPaging).
110
- // (Previously paging was gated on StartRow > 0, so page 1 fell back to
111
- // a TOP-N cap with TotalRowCount = page size — hiding the pager.)
112
- // Clamp StartRow to a non-negative integer. A negative offset must not slip
113
- // past the paging gate (which would then run the query with no row cap at all).
114
- const startRow = Math.max(0, Number.isInteger(input.StartRow) ? input.StartRow : 0);
115
- const maxRows = input.MaxRows;
116
- const usePaging = maxRows != null && Number.isInteger(maxRows) && maxRows > 0;
117
- let dataSQL;
118
- let countSQL = null;
119
- try {
120
- const rendered = RenderPipeline.Run(input.SQL, {
121
- Platform: platform,
122
- ContextUser: contextUser,
123
- ...(usePaging ? { Paging: { StartRow: startRow, MaxRows: maxRows } } : {}),
124
- });
125
- dataSQL = rendered.FinalSQL;
126
- countSQL = rendered.PagingResult?.CountSQL ?? null;
127
- }
128
- catch (renderErr) {
129
- const renderMsg = renderErr instanceof Error ? renderErr.message : String(renderErr);
130
- return this.buildErrorResult(`Ad-hoc query rendering failed: ${renderMsg}`);
131
- }
132
- // 6. Execute the page (and, only when a full page needs it, the count) under
133
- // a shared wall-clock deadline derived from the request's timeout budget.
134
- const deadline = startTime + (input.TimeoutSeconds ?? 30) * 1000;
135
- const { recordset, totalRowCount } = await this.executeDataAndCount(readOnlyDS, dataSQL, countSQL, startRow, usePaging ? maxRows : null, deadline);
136
- const executionTimeMs = Date.now() - startTime;
137
- // 7. Return as RunQueryResultType
138
- return {
139
- QueryID: '',
140
- QueryName: 'Ad-Hoc Query',
141
- Success: true,
142
- Results: JSON.stringify(recordset),
143
- RowCount: recordset.length,
144
- TotalRowCount: totalRowCount,
145
- PageNumber: usePaging ? Math.floor(startRow / maxRows) + 1 : undefined,
146
- PageSize: usePaging ? maxRows : undefined,
147
- ExecutionTime: executionTimeMs,
148
- ErrorMessage: ''
149
- };
85
+ const result = await provider.RunQuery({
86
+ SQL: input.SQL,
87
+ MaxRows: input.MaxRows,
88
+ StartRow: startRow,
89
+ TimeoutSeconds: timeoutSeconds,
90
+ }, contextUser);
91
+ return this.toResultType(result, startRow, input.MaxRows, timeoutSeconds, Date.now() - startTime);
150
92
  }
151
93
  catch (err) {
152
- const executionTimeMs = Date.now() - startTime;
153
94
  const errorMessage = err instanceof Error ? err.message : String(err);
154
- // Handle timeout
155
- if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {
156
- return {
157
- QueryID: '',
158
- QueryName: 'Ad-Hoc Query',
159
- Success: false,
160
- Results: '[]',
161
- RowCount: 0,
162
- TotalRowCount: 0,
163
- PageNumber: undefined,
164
- PageSize: undefined,
165
- ExecutionTime: executionTimeMs,
166
- ErrorMessage: `Query execution exceeded ${input.TimeoutSeconds ?? 30} second timeout`
167
- };
168
- }
169
95
  LogError(`Ad-hoc query execution failed: ${errorMessage}`);
170
- return {
171
- QueryID: '',
172
- QueryName: 'Ad-Hoc Query',
173
- Success: false,
174
- Results: '[]',
175
- RowCount: 0,
176
- TotalRowCount: 0,
177
- PageNumber: undefined,
178
- PageSize: undefined,
179
- ExecutionTime: executionTimeMs,
180
- ErrorMessage: `Query execution failed: ${errorMessage}`
181
- };
182
- }
183
- }
184
- /**
185
- * Runs the page's data SQL, then — only when the page is FULL (so more rows may
186
- * exist) — a COUNT(*) for the true total. A short (or unpaged) page needs no
187
- * count: the exact total is `startRow + rowsReturned` (see {@link exactTotalFromPage}).
188
- *
189
- * The count is NON-FATAL: some queries page fine but cannot be counted — e.g.
190
- * duplicate column names are legal in a result set but rejected inside the COUNT
191
- * CTE wrap. A count failure must never sink the whole result, so we log it and
192
- * report a lower-bound total (`startRow + rowsReturned`) and let the data render.
193
- */
194
- async executeDataAndCount(ds, dataSQL, countSQL, startRow, maxRows, deadline) {
195
- const dataResult = await this.runSqlWithDeadline(ds, dataSQL, deadline);
196
- const recordset = (dataResult.recordset ?? []);
197
- // Total already known from the page alone (unpaged, or a short page)? Skip the count.
198
- const exact = exactTotalFromPage(startRow, recordset.length, maxRows);
199
- if (exact != null || !countSQL) {
200
- return { recordset, totalRowCount: exact ?? recordset.length };
201
- }
202
- // Full page — a COUNT(*) is required to know the true total.
203
- const lowerBound = startRow + recordset.length;
204
- try {
205
- const countResult = await this.runSqlWithDeadline(ds, countSQL, deadline);
206
- return { recordset, totalRowCount: resolveAdhocTotalRowCount(countResult.recordset, lowerBound) };
207
- }
208
- catch (countErr) {
209
- const msg = countErr instanceof Error ? countErr.message : String(countErr);
210
- LogError(`Ad-hoc query row-count failed; reporting a lower-bound total (${lowerBound}). ${msg}`);
211
- return { recordset, totalRowCount: lowerBound };
96
+ return this.buildErrorResult(`Query execution failed: ${errorMessage}`, Date.now() - startTime);
212
97
  }
213
98
  }
214
- /**
215
- * Executes one SQL statement on the read-only pool, racing it against the shared
216
- * wall-clock `deadline`. The timer is always cleared on completion so a settled
217
- * query never leaves a dangling timeout armed.
218
- */
219
- async runSqlWithDeadline(ds, sqlText, deadline) {
220
- const remaining = deadline - Date.now();
221
- if (remaining <= 0) {
222
- throw new Error('Query timeout exceeded');
223
- }
224
- let timer;
225
- try {
226
- return await Promise.race([
227
- new sql.Request(ds).query(sqlText),
228
- new Promise((_, reject) => {
229
- timer = setTimeout(() => reject(new Error('Query timeout exceeded')), remaining);
230
- }),
231
- ]);
232
- }
233
- finally {
234
- if (timer) {
235
- clearTimeout(timer);
236
- }
99
+ /** Maps the provider's result to the GraphQL shape, naming a timeout plainly. */
100
+ toResultType(result, startRow, maxRows, timeoutSeconds, executionTimeMs) {
101
+ if (!result.Success) {
102
+ const message = result.ErrorMessage ?? 'Ad-hoc query failed';
103
+ return this.buildErrorResult(/timeout/i.test(message) ? `Query execution exceeded ${timeoutSeconds} second timeout` : message, executionTimeMs);
237
104
  }
105
+ const paged = maxRows != null && Number.isInteger(maxRows) && maxRows > 0;
106
+ return {
107
+ QueryID: '',
108
+ QueryName: 'Ad-Hoc Query',
109
+ Success: true,
110
+ Results: JSON.stringify(result.Results),
111
+ RowCount: result.RowCount,
112
+ TotalRowCount: result.TotalRowCount,
113
+ PageNumber: paged ? Math.floor(startRow / maxRows) + 1 : undefined,
114
+ PageSize: paged ? maxRows : undefined,
115
+ ExecutionTime: executionTimeMs,
116
+ ErrorMessage: ''
117
+ };
238
118
  }
239
- buildErrorResult(errorMessage) {
119
+ buildErrorResult(errorMessage, executionTimeMs = 0) {
240
120
  return {
241
121
  QueryID: '',
242
122
  QueryName: 'Ad-Hoc Query',
@@ -246,7 +126,7 @@ let AdhocQueryResolver = class AdhocQueryResolver extends ResolverBase {
246
126
  TotalRowCount: 0,
247
127
  PageNumber: undefined,
248
128
  PageSize: undefined,
249
- ExecutionTime: 0,
129
+ ExecutionTime: executionTimeMs,
250
130
  ErrorMessage: errorMessage
251
131
  };
252
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AdhocQueryResolver.js","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AdhocQueryResolver.js","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"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"}