@memberjunction/generic-database-provider 5.36.0 → 5.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/GenericDatabaseProvider.d.ts +18 -26
- package/dist/GenericDatabaseProvider.d.ts.map +1 -1
- package/dist/GenericDatabaseProvider.js +85 -238
- package/dist/GenericDatabaseProvider.js.map +1 -1
- package/dist/queryCompositionEngine.d.ts +23 -4
- package/dist/queryCompositionEngine.d.ts.map +1 -1
- package/dist/queryCompositionEngine.js +27 -18
- package/dist/queryCompositionEngine.js.map +1 -1
- package/dist/queryPagingEngine.d.ts +69 -4
- package/dist/queryPagingEngine.d.ts.map +1 -1
- package/dist/queryPagingEngine.js +162 -55
- package/dist/queryPagingEngine.js.map +1 -1
- package/dist/renderPipeline.d.ts +34 -28
- package/dist/renderPipeline.d.ts.map +1 -1
- package/dist/renderPipeline.js +58 -130
- package/dist/renderPipeline.js.map +1 -1
- package/package.json +13 -13
package/dist/renderPipeline.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DatabasePlatform, UserInfo, QueryDependencySpec
|
|
1
|
+
import { DatabasePlatform, UserInfo, QueryDependencySpec } from '@memberjunction/core';
|
|
2
|
+
import { MJQueryParameterEntity } from '@memberjunction/core-entities';
|
|
2
3
|
import { CompositionResult, CompositionCTEInfo } from './queryCompositionEngine.js';
|
|
3
4
|
import { PagingWrappedSQL } from './queryPagingEngine.js';
|
|
4
5
|
import { type QueryTemplateInput } from '@memberjunction/query-processor';
|
|
@@ -44,7 +45,7 @@ export interface RenderContext {
|
|
|
44
45
|
/** Parameter values from the caller */
|
|
45
46
|
Parameters?: Record<string, string>;
|
|
46
47
|
/** Formal parameter definitions (for validation). Null = skip validation. */
|
|
47
|
-
ParameterDefinitions?:
|
|
48
|
+
ParameterDefinitions?: MJQueryParameterEntity[];
|
|
48
49
|
/** Whether the outer query uses Nunjucks templates */
|
|
49
50
|
UsesTemplate?: boolean;
|
|
50
51
|
/** Inline dependency specs for transient query testing */
|
|
@@ -55,12 +56,12 @@ export interface RenderContext {
|
|
|
55
56
|
* directly to processQueryTemplate for parameter validation. When omitted,
|
|
56
57
|
* a synthetic QueryTemplateInput is built from OriginalSQL + ParameterDefinitions. */
|
|
57
58
|
QueryInfo?: QueryTemplateInput;
|
|
58
|
-
/** Paging parameters (omit to skip paging) */
|
|
59
|
+
/** Paging parameters (omit to skip paging). Mutually exclusive with {@link MaxRows}. */
|
|
59
60
|
Paging?: {
|
|
60
61
|
StartRow: number;
|
|
61
62
|
MaxRows: number;
|
|
62
63
|
};
|
|
63
|
-
/** MaxRows safety limit
|
|
64
|
+
/** MaxRows safety limit. Mutually exclusive with {@link Paging}. */
|
|
64
65
|
MaxRows?: number;
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
@@ -107,6 +108,35 @@ export declare class RenderPipeline {
|
|
|
107
108
|
* @returns Final SQL, applied parameters, composition metadata, and diagnostic trace
|
|
108
109
|
*/
|
|
109
110
|
static Run(sql: string, ctx: RenderContext): RenderResult;
|
|
111
|
+
/**
|
|
112
|
+
* Defense-in-depth guard on the fully-resolved query: throws when the
|
|
113
|
+
* rendered SQL contains a write statement anywhere — a DML mutation
|
|
114
|
+
* (INSERT / UPDATE / DELETE / MERGE / REPLACE), DDL (DROP / CREATE / ALTER /
|
|
115
|
+
* TRUNCATE / RENAME), or EXEC / CALL / GRANT / REVOKE / USE.
|
|
116
|
+
*
|
|
117
|
+
* {@link SQLParser.HasWriteStatement} inspects EVERY top-level statement, so
|
|
118
|
+
* it catches both a single rendered mutation and a stacked-statement
|
|
119
|
+
* injection payload (e.g. `SELECT 1; DROP TABLE x`) that the first-statement
|
|
120
|
+
* `StatementKind` alone would miss. It is AST-precise: matching is on the
|
|
121
|
+
* statement type, so it cannot false-positive on legitimate read queries —
|
|
122
|
+
* the `REPLACE()` string function, parenthesized SELECTs, trailing
|
|
123
|
+
* semicolons, or benign `SET` / `DECLARE` prefixes all pass.
|
|
124
|
+
*
|
|
125
|
+
* A second, token-based check rejects stacked statements (an internal
|
|
126
|
+
* statement-separating `;`). Unlike the AST check it fires even when the
|
|
127
|
+
* trailing payload makes the SQL unparseable — e.g. `SELECT 1; EXEC
|
|
128
|
+
* xp_cmdshell '…'` or `SELECT 1; WAITFOR DELAY '…'` — which is the
|
|
129
|
+
* stacked-injection class the AST scan misses (the whole string fails to
|
|
130
|
+
* parse, so there is no AST to classify). A rendered read query must be a
|
|
131
|
+
* single statement.
|
|
132
|
+
*
|
|
133
|
+
* The broader dangerous-keyword scan
|
|
134
|
+
* ({@link SQLExpressionValidator.validateFullQuery}) deliberately stays on
|
|
135
|
+
* the ad-hoc execution path (untrusted free-text input); it is unsuitable as
|
|
136
|
+
* a blanket gate here because it rejects legitimate read constructs (the
|
|
137
|
+
* `REPLACE()` string function, parenthesized SELECTs, etc.).
|
|
138
|
+
*/
|
|
139
|
+
private static assertSafeToExecute;
|
|
110
140
|
/**
|
|
111
141
|
* Resolves only the composition step — for callers that need composition
|
|
112
142
|
* without the full pipeline (e.g., save-time token analysis).
|
|
@@ -122,29 +152,5 @@ export declare class RenderPipeline {
|
|
|
122
152
|
static HasCompositionTokens(sql: string): boolean;
|
|
123
153
|
private static runComposition;
|
|
124
154
|
private static runTemplates;
|
|
125
|
-
/**
|
|
126
|
-
* Injects a row-cap (`TOP N` for SQL Server, `LIMIT N` for Postgres) onto
|
|
127
|
-
* the outermost SELECT of `sql`.
|
|
128
|
-
*
|
|
129
|
-
* Uses an AST-based rewrite so CTE queries (`WITH … SELECT …`), nested
|
|
130
|
-
* subqueries, comments, and casing are all handled correctly. The cap is
|
|
131
|
-
* applied to the outermost projection only, leaving any inner `TOP N` /
|
|
132
|
-
* `LIMIT N` clauses inside CTE definitions or subqueries untouched.
|
|
133
|
-
*
|
|
134
|
-
* For shapes the parser can't represent at the top level
|
|
135
|
-
* (UNION/INTERSECT/EXCEPT, vendor-specific T-SQL the parser rejects, sqlify
|
|
136
|
-
* round-trip failures), falls back to wrapping the original SQL in
|
|
137
|
-
* `SELECT TOP N * FROM (<original>) AS _capped` — always safe because the
|
|
138
|
-
* inner SQL is treated as opaque.
|
|
139
|
-
*/
|
|
140
|
-
private static applyMaxRows;
|
|
141
|
-
private static applyMaxRowsViaAst;
|
|
142
|
-
private static applyMaxRowsViaSubqueryWrap;
|
|
143
|
-
private static getDialect;
|
|
144
|
-
/**
|
|
145
|
-
* Strips SQL comments (single-line -- and block comments) from a SQL string.
|
|
146
|
-
* Preserves single-quoted string literals to avoid stripping inside them.
|
|
147
|
-
*/
|
|
148
|
-
private static stripSQLComments;
|
|
149
155
|
}
|
|
150
156
|
//# sourceMappingURL=renderPipeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderPipeline.d.ts","sourceRoot":"","sources":["../src/renderPipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"renderPipeline.d.ts","sourceRoot":"","sources":["../src/renderPipeline.ts"],"names":[],"mappings":"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"}
|
package/dist/renderPipeline.js
CHANGED
|
@@ -33,6 +33,13 @@ export class RenderPipeline {
|
|
|
33
33
|
* @returns Final SQL, applied parameters, composition metadata, and diagnostic trace
|
|
34
34
|
*/
|
|
35
35
|
static Run(sql, ctx) {
|
|
36
|
+
const hasMaxRows = ctx.MaxRows != null && ctx.MaxRows > 0;
|
|
37
|
+
const hasPaging = ctx.Paging != null && QueryPagingEngine.ShouldPage(ctx.Paging.StartRow, ctx.Paging.MaxRows);
|
|
38
|
+
if (hasMaxRows && hasPaging) {
|
|
39
|
+
throw new Error('RenderPipeline.Run: ctx.MaxRows and ctx.Paging are mutually exclusive. ' +
|
|
40
|
+
'Use Paging for pagination (StartRow + MaxRows) or MaxRows alone for a safety cap. ' +
|
|
41
|
+
`Got MaxRows=${ctx.MaxRows} and Paging=${JSON.stringify(ctx.Paging)}.`);
|
|
42
|
+
}
|
|
36
43
|
let currentSQL = sql;
|
|
37
44
|
let appliedParameters = {};
|
|
38
45
|
// ── Step 1: Composition ──────────────────────────────────────
|
|
@@ -46,20 +53,27 @@ export class RenderPipeline {
|
|
|
46
53
|
// Nunjucks. Strip them after composition (which correctly ignores
|
|
47
54
|
// comment-embedded tokens) and before Nunjucks processes the SQL.
|
|
48
55
|
// Comments are still visible in Trace.AfterComposition for debugging.
|
|
49
|
-
currentSQL =
|
|
56
|
+
currentSQL = SQLParser.StripComments(currentSQL, GetDialect(ctx.Platform));
|
|
50
57
|
// ── Step 2: Nunjucks template evaluation ─────────────────────
|
|
51
58
|
const templateResult = RenderPipeline.runTemplates(currentSQL, sql, ctx, compositionResult);
|
|
52
59
|
currentSQL = templateResult.processedSQL;
|
|
53
60
|
appliedParameters = templateResult.appliedParameters;
|
|
54
61
|
const afterTemplates = currentSQL;
|
|
62
|
+
// ── Step 2.5: Safety validation (mutation + injection guard) ──
|
|
63
|
+
// The fully-resolved query is the point where composition and parameter
|
|
64
|
+
// substitution — the only injection vectors — are complete, and where
|
|
65
|
+
// StatementKind reflects the user's actual statement (paging wrapping
|
|
66
|
+
// below always produces a SELECT). Saved queries otherwise skip the
|
|
67
|
+
// dangerous-keyword validation that ad-hoc queries get at execution.
|
|
68
|
+
RenderPipeline.assertSafeToExecute(afterTemplates, ctx.Platform);
|
|
55
69
|
// ── Step 3: MaxRows safety limit (if specified) ──────────────
|
|
56
|
-
if (
|
|
57
|
-
currentSQL =
|
|
70
|
+
if (hasMaxRows) {
|
|
71
|
+
currentSQL = QueryPagingEngine.WrapWithMaxRows(currentSQL, ctx.MaxRows, ctx.Platform);
|
|
58
72
|
}
|
|
59
73
|
// ── Step 4: Paging (if requested) ────────────────────────────
|
|
60
74
|
let pagingResult = null;
|
|
61
75
|
let afterPaging = null;
|
|
62
|
-
if (
|
|
76
|
+
if (hasPaging) {
|
|
63
77
|
pagingResult = QueryPagingEngine.WrapWithPaging(currentSQL, ctx.Paging.StartRow, ctx.Paging.MaxRows, ctx.Platform);
|
|
64
78
|
currentSQL = pagingResult.DataSQL;
|
|
65
79
|
afterPaging = currentSQL;
|
|
@@ -78,6 +92,46 @@ export class RenderPipeline {
|
|
|
78
92
|
PagingResult: pagingResult,
|
|
79
93
|
};
|
|
80
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Defense-in-depth guard on the fully-resolved query: throws when the
|
|
97
|
+
* rendered SQL contains a write statement anywhere — a DML mutation
|
|
98
|
+
* (INSERT / UPDATE / DELETE / MERGE / REPLACE), DDL (DROP / CREATE / ALTER /
|
|
99
|
+
* TRUNCATE / RENAME), or EXEC / CALL / GRANT / REVOKE / USE.
|
|
100
|
+
*
|
|
101
|
+
* {@link SQLParser.HasWriteStatement} inspects EVERY top-level statement, so
|
|
102
|
+
* it catches both a single rendered mutation and a stacked-statement
|
|
103
|
+
* injection payload (e.g. `SELECT 1; DROP TABLE x`) that the first-statement
|
|
104
|
+
* `StatementKind` alone would miss. It is AST-precise: matching is on the
|
|
105
|
+
* statement type, so it cannot false-positive on legitimate read queries —
|
|
106
|
+
* the `REPLACE()` string function, parenthesized SELECTs, trailing
|
|
107
|
+
* semicolons, or benign `SET` / `DECLARE` prefixes all pass.
|
|
108
|
+
*
|
|
109
|
+
* A second, token-based check rejects stacked statements (an internal
|
|
110
|
+
* statement-separating `;`). Unlike the AST check it fires even when the
|
|
111
|
+
* trailing payload makes the SQL unparseable — e.g. `SELECT 1; EXEC
|
|
112
|
+
* xp_cmdshell '…'` or `SELECT 1; WAITFOR DELAY '…'` — which is the
|
|
113
|
+
* stacked-injection class the AST scan misses (the whole string fails to
|
|
114
|
+
* parse, so there is no AST to classify). A rendered read query must be a
|
|
115
|
+
* single statement.
|
|
116
|
+
*
|
|
117
|
+
* The broader dangerous-keyword scan
|
|
118
|
+
* ({@link SQLExpressionValidator.validateFullQuery}) deliberately stays on
|
|
119
|
+
* the ad-hoc execution path (untrusted free-text input); it is unsuitable as
|
|
120
|
+
* a blanket gate here because it rejects legitimate read constructs (the
|
|
121
|
+
* `REPLACE()` string function, parenthesized SELECTs, etc.).
|
|
122
|
+
*/
|
|
123
|
+
static assertSafeToExecute(sql, platform) {
|
|
124
|
+
const dialect = GetDialect(platform);
|
|
125
|
+
const parsed = new SQLParser(sql, dialect);
|
|
126
|
+
if (parsed.HasWriteStatement) {
|
|
127
|
+
throw new Error('RenderPipeline: rendered SQL contains a write statement ' +
|
|
128
|
+
'(mutation / DDL) — only a read query may be rendered for execution.');
|
|
129
|
+
}
|
|
130
|
+
if (SQLParser.HasStackedStatements(sql, dialect)) {
|
|
131
|
+
throw new Error('RenderPipeline: rendered SQL contains multiple statements ' +
|
|
132
|
+
'(a stacked-statement injection) — only a single read query may be rendered.');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
81
135
|
/**
|
|
82
136
|
* Resolves only the composition step — for callers that need composition
|
|
83
137
|
* without the full pipeline (e.g., save-time token analysis).
|
|
@@ -134,132 +188,6 @@ export class RenderPipeline {
|
|
|
134
188
|
appliedParameters: (result.appliedParameters || {}),
|
|
135
189
|
};
|
|
136
190
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Injects a row-cap (`TOP N` for SQL Server, `LIMIT N` for Postgres) onto
|
|
139
|
-
* the outermost SELECT of `sql`.
|
|
140
|
-
*
|
|
141
|
-
* Uses an AST-based rewrite so CTE queries (`WITH … SELECT …`), nested
|
|
142
|
-
* subqueries, comments, and casing are all handled correctly. The cap is
|
|
143
|
-
* applied to the outermost projection only, leaving any inner `TOP N` /
|
|
144
|
-
* `LIMIT N` clauses inside CTE definitions or subqueries untouched.
|
|
145
|
-
*
|
|
146
|
-
* For shapes the parser can't represent at the top level
|
|
147
|
-
* (UNION/INTERSECT/EXCEPT, vendor-specific T-SQL the parser rejects, sqlify
|
|
148
|
-
* round-trip failures), falls back to wrapping the original SQL in
|
|
149
|
-
* `SELECT TOP N * FROM (<original>) AS _capped` — always safe because the
|
|
150
|
-
* inner SQL is treated as opaque.
|
|
151
|
-
*/
|
|
152
|
-
static applyMaxRows(sql, maxRows, platform) {
|
|
153
|
-
const dialect = RenderPipeline.getDialect(platform);
|
|
154
|
-
const fromAst = RenderPipeline.applyMaxRowsViaAst(sql, maxRows, dialect);
|
|
155
|
-
if (fromAst !== null)
|
|
156
|
-
return fromAst;
|
|
157
|
-
return RenderPipeline.applyMaxRowsViaSubqueryWrap(sql, maxRows, dialect);
|
|
158
|
-
}
|
|
159
|
-
static applyMaxRowsViaAst(sql, maxRows, dialect) {
|
|
160
|
-
const ast = SQLParser.ParseSQL(sql, dialect);
|
|
161
|
-
if (!ast)
|
|
162
|
-
return null;
|
|
163
|
-
const root = Array.isArray(ast) ? ast[0] : ast;
|
|
164
|
-
if (!root)
|
|
165
|
-
return null;
|
|
166
|
-
// Non-SELECT (INSERT/UPDATE/DELETE/MERGE): row caps don't apply — leave alone.
|
|
167
|
-
// Discriminating on `type` narrows `root` to node-sql-parser's `Select` shape.
|
|
168
|
-
if (root.type !== 'select')
|
|
169
|
-
return sql;
|
|
170
|
-
// `top` is a SQL Server-specific field on the Select node that node-sql-parser's
|
|
171
|
-
// published .d.ts omits. Widen the narrowed type via intersection so we can
|
|
172
|
-
// read/write it without an `as unknown` escape hatch.
|
|
173
|
-
const selectNode = root;
|
|
174
|
-
// UNION/INTERSECT/EXCEPT: outer SELECT has set_op + _next branches. Injecting
|
|
175
|
-
// `top` here only caps the first branch, so defer to the subquery wrap.
|
|
176
|
-
if (selectNode.set_op)
|
|
177
|
-
return null;
|
|
178
|
-
// Idempotency: an explicit outer TOP/LIMIT wins over MaxRows.
|
|
179
|
-
// node-sql-parser distinguishes "no LIMIT" by an empty `limit.value` array
|
|
180
|
-
// on Postgres (the wrapper object is always present), while SQL Server uses
|
|
181
|
-
// `top === null`. Check both shapes so we don't double-inject.
|
|
182
|
-
if (selectNode.top != null)
|
|
183
|
-
return sql;
|
|
184
|
-
if (selectNode.limit != null && selectNode.limit.value.length > 0)
|
|
185
|
-
return sql;
|
|
186
|
-
const limitClause = dialect.LimitClause(maxRows);
|
|
187
|
-
if (limitClause.prefix) {
|
|
188
|
-
// SQL Server: { value, percent } matches node-sql-parser's parsed shape
|
|
189
|
-
selectNode.top = { value: maxRows, percent: null };
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
// PostgreSQL: { seperator, value[] } — note the parser's spelling of "seperator"
|
|
193
|
-
selectNode.limit = { seperator: '', value: [{ type: 'number', value: maxRows }] };
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
return SQLParser.SqlifyAST(ast, dialect);
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
static applyMaxRowsViaSubqueryWrap(sql, maxRows, dialect) {
|
|
203
|
-
const trimmed = sql.trim().replace(/;\s*$/, '');
|
|
204
|
-
const limitClause = dialect.LimitClause(maxRows);
|
|
205
|
-
if (limitClause.prefix) {
|
|
206
|
-
// SQL Server: outer projection cap. The optimizer pushes TOP N through trivially.
|
|
207
|
-
return `SELECT ${limitClause.prefix} * FROM (${trimmed}) AS _capped`;
|
|
208
|
-
}
|
|
209
|
-
// PostgreSQL: appending LIMIT N is always legal at the end of a SELECT/CTE.
|
|
210
|
-
return `${trimmed}\n${limitClause.suffix}`;
|
|
211
|
-
}
|
|
212
|
-
static getDialect(platform) {
|
|
213
|
-
return GetDialect(platform);
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Strips SQL comments (single-line -- and block comments) from a SQL string.
|
|
217
|
-
* Preserves single-quoted string literals to avoid stripping inside them.
|
|
218
|
-
*/
|
|
219
|
-
static stripSQLComments(sql) {
|
|
220
|
-
let result = '';
|
|
221
|
-
let i = 0;
|
|
222
|
-
while (i < sql.length) {
|
|
223
|
-
// Single-quoted string literal — preserve as-is
|
|
224
|
-
if (sql[i] === "'") {
|
|
225
|
-
result += sql[i++];
|
|
226
|
-
while (i < sql.length) {
|
|
227
|
-
if (sql[i] === "'" && i + 1 < sql.length && sql[i + 1] === "'") {
|
|
228
|
-
result += "''";
|
|
229
|
-
i += 2;
|
|
230
|
-
}
|
|
231
|
-
else if (sql[i] === "'") {
|
|
232
|
-
result += sql[i++];
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
result += sql[i++];
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
// Single-line comment: -- to end of line
|
|
241
|
-
else if (sql[i] === '-' && i + 1 < sql.length && sql[i + 1] === '-') {
|
|
242
|
-
while (i < sql.length && sql[i] !== '\n')
|
|
243
|
-
i++;
|
|
244
|
-
}
|
|
245
|
-
// Block comment: /* ... */
|
|
246
|
-
else if (sql[i] === '/' && i + 1 < sql.length && sql[i + 1] === '*') {
|
|
247
|
-
i += 2;
|
|
248
|
-
while (i < sql.length) {
|
|
249
|
-
if (sql[i] === '*' && i + 1 < sql.length && sql[i + 1] === '/') {
|
|
250
|
-
i += 2;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
i++;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
// Normal character
|
|
257
|
-
else {
|
|
258
|
-
result += sql[i++];
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return result;
|
|
262
|
-
}
|
|
263
191
|
}
|
|
264
192
|
// ════════════════════════════════════════════════════════════════════
|
|
265
193
|
// Helpers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderPipeline.js","sourceRoot":"","sources":["../src/renderPipeline.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"renderPipeline.js","sourceRoot":"","sources":["../src/renderPipeline.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/generic-database-provider",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.38.0",
|
|
5
5
|
"description": "Shared database provider logic for MemberJunction — intermediate base class between DatabaseProviderBase and platform-specific providers",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -22,18 +22,18 @@
|
|
|
22
22
|
"vitest": "^3.1.1"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@memberjunction/actions": "5.
|
|
26
|
-
"@memberjunction/actions-base": "5.
|
|
27
|
-
"@memberjunction/aiengine": "5.
|
|
28
|
-
"@memberjunction/core": "5.
|
|
29
|
-
"@memberjunction/core-entities": "5.
|
|
30
|
-
"@memberjunction/encryption": "5.
|
|
31
|
-
"@memberjunction/geo-core": "5.
|
|
32
|
-
"@memberjunction/global": "5.
|
|
33
|
-
"@memberjunction/query-processor": "5.
|
|
34
|
-
"@memberjunction/sql-dialect": "5.
|
|
35
|
-
"@memberjunction/sql-parser": "5.
|
|
36
|
-
"@memberjunction/queue": "5.
|
|
25
|
+
"@memberjunction/actions": "5.38.0",
|
|
26
|
+
"@memberjunction/actions-base": "5.38.0",
|
|
27
|
+
"@memberjunction/aiengine": "5.38.0",
|
|
28
|
+
"@memberjunction/core": "5.38.0",
|
|
29
|
+
"@memberjunction/core-entities": "5.38.0",
|
|
30
|
+
"@memberjunction/encryption": "5.38.0",
|
|
31
|
+
"@memberjunction/geo-core": "5.38.0",
|
|
32
|
+
"@memberjunction/global": "5.38.0",
|
|
33
|
+
"@memberjunction/query-processor": "5.38.0",
|
|
34
|
+
"@memberjunction/sql-dialect": "5.38.0",
|
|
35
|
+
"@memberjunction/sql-parser": "5.38.0",
|
|
36
|
+
"@memberjunction/queue": "5.38.0",
|
|
37
37
|
"sql-formatter": "^15.7.0",
|
|
38
38
|
"uuid": "^13.0.0"
|
|
39
39
|
},
|