@graphoria/server 0.2.1 → 0.2.3
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/dist/src/ai/agent/providers/ollama.d.ts.map +1 -1
- package/dist/src/ai/mcp/create-server.d.ts.map +1 -1
- package/dist/src/ai/mcp/index.d.ts +5 -2
- package/dist/src/ai/mcp/index.d.ts.map +1 -1
- package/dist/src/ai/tools/agent.d.ts.map +1 -1
- package/dist/src/ai/tools/core.d.ts.map +1 -1
- package/dist/src/ai/tools/query-data.d.ts +2 -2
- package/dist/src/ai/tools/query-data.d.ts.map +1 -1
- package/dist/src/analyzeQuery/analyzers/fragmentAnalyzer.d.ts.map +1 -1
- package/dist/src/analyzeQuery/analyzers/operationAnalyzer.d.ts.map +1 -1
- package/dist/src/analyzeQuery/analyzers/selectionAnalyzer.d.ts.map +1 -1
- package/dist/src/analyzeQuery/analyzers/variableAnalyzer.d.ts.map +1 -1
- package/dist/src/analyzeQuery/depthLimit.d.ts.map +1 -1
- package/dist/src/analyzeQuery/directiveUtils.d.ts.map +1 -1
- package/dist/src/analyzeQuery/resolveVariables.d.ts +17 -2
- package/dist/src/analyzeQuery/resolveVariables.d.ts.map +1 -1
- package/dist/src/analyzeQuery/typeUtils.d.ts.map +1 -1
- package/dist/src/analyzeQuery/valueExtractors.d.ts.map +1 -1
- package/dist/src/authentication/duration.d.ts.map +1 -1
- package/dist/src/authentication/index.d.ts.map +1 -1
- package/dist/src/authentication/jwt.d.ts.map +1 -1
- package/dist/src/authentication/paseto.d.ts.map +1 -1
- package/dist/src/authentication/tokenRepository.d.ts.map +1 -1
- package/dist/src/cli/seedAuth.d.ts.map +1 -1
- package/dist/src/cli/seedAuthArgs.d.ts.map +1 -1
- package/dist/src/config/helpers/cronHelper.d.ts.map +1 -1
- package/dist/src/config/helpers/operationHelper.d.ts +1 -1
- package/dist/src/config/helpers/operationHelper.d.ts.map +1 -1
- package/dist/src/config/helpers/queueHelper.d.ts.map +1 -1
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/types/ai.d.ts +0 -6
- package/dist/src/config/types/ai.d.ts.map +1 -1
- package/dist/src/config/types/auth.d.ts +11 -34
- package/dist/src/config/types/auth.d.ts.map +1 -1
- package/dist/src/config/types/auth.test.d.ts +2 -0
- package/dist/src/config/types/auth.test.d.ts.map +1 -0
- package/dist/src/config/types/cron.d.ts +0 -94
- package/dist/src/config/types/cron.d.ts.map +1 -1
- package/dist/src/config/types/db.d.ts +22 -93
- package/dist/src/config/types/db.d.ts.map +1 -1
- package/dist/src/config/types/operation.d.ts +6 -6
- package/dist/src/config/types/queue.d.ts +4 -51
- package/dist/src/config/types/queue.d.ts.map +1 -1
- package/dist/src/config/types/remote-rest.d.ts +0 -9
- package/dist/src/config/types/remote-rest.d.ts.map +1 -1
- package/dist/src/config/types/remote-schema.d.ts +0 -10
- package/dist/src/config/types/remote-schema.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/index.d.ts +103 -99
- package/dist/src/configuration/getSchemas/index.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts +50 -48
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/type-definition-generator/index.d.ts.map +1 -1
- package/dist/src/configuration/gql/buildExecute.d.ts.map +1 -1
- package/dist/src/configuration/gql/gqlAuthOperations.d.ts.map +1 -1
- package/dist/src/configuration/gql/handleGraphQLRequestFactory.d.ts.map +1 -1
- package/dist/src/configuration/gql/handleGraphQLSubscriptionFactory.d.ts.map +1 -1
- package/dist/src/configuration/index.d.ts +95 -91
- package/dist/src/configuration/index.d.ts.map +1 -1
- package/dist/src/configuration/rest/generateOpenAPI.d.ts.map +1 -1
- package/dist/src/configuration/rest/handleRESTRequestFactory.d.ts.map +1 -1
- package/dist/src/configuration/rest/index.d.ts.map +1 -1
- package/dist/src/configuration/rest/openApiErrors.d.ts.map +1 -1
- package/dist/src/configuration/rest/parseStringParams.d.ts.map +1 -1
- package/dist/src/console/api.d.ts.map +1 -1
- package/dist/src/cron/index.d.ts +1 -1
- package/dist/src/cron/index.d.ts.map +1 -1
- package/dist/src/cron/index.test.d.ts +2 -0
- package/dist/src/cron/index.test.d.ts.map +1 -0
- package/dist/src/databases/auth/auth-operations.d.ts.map +1 -1
- package/dist/src/databases/auth/password.d.ts.map +1 -1
- package/dist/src/databases/common.d.ts +1 -3
- package/dist/src/databases/common.d.ts.map +1 -1
- package/dist/src/databases/core/executor.d.ts.map +1 -1
- package/dist/src/databases/core/identifier.d.ts.map +1 -1
- package/dist/src/databases/core/query-builder.d.ts +3 -2
- package/dist/src/databases/core/query-builder.d.ts.map +1 -1
- package/dist/src/databases/directives.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/auth.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/getStructure.d.ts +12 -12
- package/dist/src/databases/engines/mssql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/getViews.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/auth.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/getStructure.d.ts +12 -12
- package/dist/src/databases/engines/mysql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/getViews.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/placeholders.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/auth.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/getStructure.d.ts +12 -12
- package/dist/src/databases/engines/postgresql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/getViews.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/shared/claims.d.ts.map +1 -1
- package/dist/src/databases/high-level-operations.d.ts +10 -59
- package/dist/src/databases/high-level-operations.d.ts.map +1 -1
- package/dist/src/databases/metadata/structure.d.ts +12 -12
- package/dist/src/databases/metadata/structure.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/generateCreateTable/index.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/generateCreateView/index.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/generateInsert/index.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/generateSchemaCreation/index.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/mssqlToPostgres/convertView.d.ts.map +1 -1
- package/dist/src/databases/schemaBuilder/utils.d.ts +2 -2
- package/dist/src/databases/schemaBuilder/utils.d.ts.map +1 -1
- package/dist/src/databases/sqlTypeUtils.d.ts.map +1 -1
- package/dist/src/databases/transformers/data-transformers.d.ts +34 -32
- package/dist/src/databases/transformers/data-transformers.d.ts.map +1 -1
- package/dist/src/databases/transformers/genResolverName.d.ts.map +1 -1
- package/dist/src/databases/transformers/graphqlName.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logging/index.d.ts.map +1 -1
- package/dist/src/queues/kafka.d.ts.map +1 -1
- package/dist/src/queues/rabbitmq.d.ts.map +1 -1
- package/dist/src/remoteREST/index.d.ts.map +1 -1
- package/dist/src/remoteREST/parse.d.ts.map +1 -1
- package/dist/src/remoteREST/proxy.d.ts.map +1 -1
- package/dist/src/remoteREST/transform.d.ts.map +1 -1
- package/dist/src/remoteSchemas/index.d.ts.map +1 -1
- package/dist/src/remoteSchemas/introspect.d.ts.map +1 -1
- package/dist/src/remoteSchemas/proxy.d.ts.map +1 -1
- package/dist/src/remoteSchemas/transform.d.ts.map +1 -1
- package/dist/src/singletons/ai.d.ts.map +1 -1
- package/dist/src/singletons/authentication.d.ts.map +1 -1
- package/dist/src/singletons/cache/lruCacheStore.d.ts.map +1 -1
- package/dist/src/singletons/cache/redisCacheStore.d.ts.map +1 -1
- package/dist/src/singletons/cache/registry.d.ts.map +1 -1
- package/dist/src/singletons/cron.d.ts.map +1 -1
- package/dist/src/singletons/databases.d.ts.map +1 -1
- package/dist/src/singletons/env.d.ts +190 -187
- package/dist/src/singletons/env.d.ts.map +1 -1
- package/dist/src/singletons/queues.d.ts.map +1 -1
- package/dist/src/subscriptions/strategies/index.d.ts.map +1 -1
- package/dist/src/subscriptions/utils/polling.d.ts.map +1 -1
- package/dist/src/types/db.d.ts.map +1 -1
- package/dist/src/types/env.d.ts +641 -630
- package/dist/src/types/env.d.ts.map +1 -1
- package/dist/src/types/resolver.d.ts.map +1 -1
- package/dist/src/types/zod/auth.d.ts +18 -18
- package/dist/src/types/zod/configuration.d.ts +298 -293
- package/dist/src/types/zod/configuration.d.ts.map +1 -1
- package/dist/src/types/zod/db.d.ts +80 -80
- package/dist/src/types/zod/db.d.ts.map +1 -1
- package/dist/src/types/zod/queue.d.ts +80 -80
- package/dist/src/types/zod/queue.d.ts.map +1 -1
- package/dist/src/utils/benchmark.d.ts.map +1 -1
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/safeJSONParse.d.ts.map +1 -1
- package/dist/src/utils/selection.d.ts.map +1 -1
- package/dist/src/utils/sessionVariables.d.ts +5 -1
- package/dist/src/utils/sessionVariables.d.ts.map +1 -1
- package/dist/src/utils/writeSchema.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/playgrounds/console/index.html +18 -15
- package/playgrounds/graphiql/index.html +855 -624
- package/playgrounds/scalar/index.html +5 -5
- package/src/analyzeQuery/analyzers/selectionAnalyzer.ts +7 -1
- package/src/analyzeQuery/resolveVariables.ts +130 -16
- package/src/config/types/auth.ts +7 -1
- package/src/config/types/db.ts +8 -0
- package/src/cron/index.ts +6 -2
- package/src/databases/engines/mysql/connection.ts +1 -0
- package/src/logging/index.ts +3 -1
- package/src/types/db.ts +0 -1
- package/src/types/zod/db.ts +1 -7
- package/src/types/zod/queue.ts +2 -10
- package/src/utils/sessionVariables.ts +7 -1
|
@@ -59,7 +59,13 @@ export const analyzeSelections = (
|
|
|
59
59
|
(dir) => dir.name === "include" || dir.name === "skip" || dir.name === "when",
|
|
60
60
|
).length === 0;
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// Gated on the field resolving to a table type, not on it being a list.
|
|
63
|
+
// A role filter has to reach every field backed by the table it
|
|
64
|
+
// restricts: the `_single` root field and any to-one relationship both
|
|
65
|
+
// return one object, and both accept a `where` the builders honour, so
|
|
66
|
+
// skipping them let a caller read a row the filter excludes by asking
|
|
67
|
+
// for it singly or by traversing into it from an unfiltered table.
|
|
68
|
+
if (fieldObjType && entities.queriesMap[fieldName]?.rolePermission?.filter) {
|
|
63
69
|
if (!args) {
|
|
64
70
|
args = {
|
|
65
71
|
where: entities.queriesMap[fieldName].rolePermission?.filter,
|
|
@@ -72,6 +72,7 @@ export const extractRuntimePrimitivesToVariables = (
|
|
|
72
72
|
runtimeVariables: Record<string, unknown>,
|
|
73
73
|
startIndex: number,
|
|
74
74
|
referencedVariables: Set<string> = new Set(),
|
|
75
|
+
declaredVariableNames?: Set<string>,
|
|
75
76
|
): unknown => {
|
|
76
77
|
// Handle null/undefined
|
|
77
78
|
if (obj === null || obj === undefined) {
|
|
@@ -87,13 +88,23 @@ export const extractRuntimePrimitivesToVariables = (
|
|
|
87
88
|
runtimeVariables,
|
|
88
89
|
startIndex,
|
|
89
90
|
referencedVariables,
|
|
91
|
+
declaredVariableNames,
|
|
90
92
|
),
|
|
91
93
|
);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// Handle primitives - convert to static variable references
|
|
95
97
|
if (typeof obj === "string") {
|
|
96
|
-
|
|
98
|
+
// A `$name` string is a reference only when the document declares that
|
|
99
|
+
// variable. These values arrive from the request body, so anything else
|
|
100
|
+
// beginning with `$` is data the client sent: `$1` resolved to index -1 and
|
|
101
|
+
// emitted the placeholder `$0`, and a value naming a real generated
|
|
102
|
+
// variable would have read that parameter's value instead of matching on
|
|
103
|
+
// its own text.
|
|
104
|
+
if (
|
|
105
|
+
obj.startsWith("$") &&
|
|
106
|
+
(!declaredVariableNames || declaredVariableNames.has(obj.substring(1)))
|
|
107
|
+
) {
|
|
97
108
|
referencedVariables.add(obj.substring(1));
|
|
98
109
|
return obj;
|
|
99
110
|
}
|
|
@@ -143,6 +154,7 @@ export const extractRuntimePrimitivesToVariables = (
|
|
|
143
154
|
runtimeVariables,
|
|
144
155
|
startIndex,
|
|
145
156
|
referencedVariables,
|
|
157
|
+
declaredVariableNames,
|
|
146
158
|
);
|
|
147
159
|
}
|
|
148
160
|
return result;
|
|
@@ -166,6 +178,7 @@ export const flattenObjectVariables = (
|
|
|
166
178
|
const resolvedObjectVarNames = new Set<string>();
|
|
167
179
|
const nestedReferencedVars = new Set<string>();
|
|
168
180
|
const resolvedMap = new Map<string, unknown>();
|
|
181
|
+
const declaredVariableNames = new Set(variables.map((variable) => variable.name));
|
|
169
182
|
|
|
170
183
|
for (const varDef of variables) {
|
|
171
184
|
if (varDef.name.startsWith("static_")) continue;
|
|
@@ -184,6 +197,7 @@ export const flattenObjectVariables = (
|
|
|
184
197
|
resolvedRuntimeValues,
|
|
185
198
|
existingStaticCount,
|
|
186
199
|
nestedReferencedVars,
|
|
200
|
+
declaredVariableNames,
|
|
187
201
|
);
|
|
188
202
|
|
|
189
203
|
resolvedMap.set(varDef.name, transformed);
|
|
@@ -200,15 +214,67 @@ export const flattenObjectVariables = (
|
|
|
200
214
|
|
|
201
215
|
// ─── Step 3: Immutable Field Resolution ─────────────────────────────────────
|
|
202
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Where late-resolved values are turned into bound parameters.
|
|
219
|
+
*
|
|
220
|
+
* Client-supplied argument values are hoisted into `static_N` variables while
|
|
221
|
+
* the document is analyzed, so by the time they reach the query builders they
|
|
222
|
+
* are `$name` references the builders emit as placeholders. Two classes of
|
|
223
|
+
* value never went through that hoisting, because they are not in the document:
|
|
224
|
+
* the constants written into a role's permission filter, and the claim values
|
|
225
|
+
* `$session.*` resolves to. Both were interpolated into the SQL text verbatim.
|
|
226
|
+
*/
|
|
227
|
+
export type VariableSink = {
|
|
228
|
+
generatedVariables: VariableDefinition[];
|
|
229
|
+
runtimeVariables: Record<string, unknown>;
|
|
230
|
+
startIndex: number;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const typeOfValue = (value: unknown): string => {
|
|
234
|
+
if (typeof value === "number") return Number.isInteger(value) ? "Int" : "Float";
|
|
235
|
+
if (typeof value === "boolean") return "Boolean";
|
|
236
|
+
return "String";
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/** Binds one value as a fresh `static_N` variable and returns its reference. */
|
|
240
|
+
const parameterize = (sink: VariableSink, value: unknown): unknown => {
|
|
241
|
+
if (value === null || value === undefined) return value;
|
|
242
|
+
|
|
243
|
+
// An array operand (`in`) is bound element-wise: the builders emit one
|
|
244
|
+
// placeholder per element.
|
|
245
|
+
if (Array.isArray(value)) return value.map((item) => parameterize(sink, item));
|
|
246
|
+
|
|
247
|
+
if (typeof value === "object") return value;
|
|
248
|
+
|
|
249
|
+
const name = `static_${sink.startIndex + sink.generatedVariables.length}`;
|
|
250
|
+
|
|
251
|
+
sink.generatedVariables.push({
|
|
252
|
+
name,
|
|
253
|
+
type: typeOfValue(value),
|
|
254
|
+
required: false,
|
|
255
|
+
defaultValue: value as never,
|
|
256
|
+
});
|
|
257
|
+
sink.runtimeVariables[name] = value;
|
|
258
|
+
|
|
259
|
+
return `$${name}`;
|
|
260
|
+
};
|
|
261
|
+
|
|
203
262
|
/**
|
|
204
263
|
* Replaces `$session.*` placeholders in any argument value.
|
|
205
264
|
* Walks all arguments, not just `where`.
|
|
265
|
+
*
|
|
266
|
+
* Substituted claim values are bound as parameters on the way out. Doing it
|
|
267
|
+
* here rather than in the `where` pass below matters: a claim whose value
|
|
268
|
+
* happens to begin with `$` would otherwise be indistinguishable from a
|
|
269
|
+
* variable reference, and would resolve to some other parameter's value.
|
|
206
270
|
*/
|
|
207
271
|
const resolveSessionInArguments = (
|
|
208
272
|
args: Record<string, unknown>,
|
|
209
273
|
session: SessionContext,
|
|
274
|
+
sink?: VariableSink,
|
|
210
275
|
): Record<string, unknown> => {
|
|
211
276
|
const result: Record<string, unknown> = {};
|
|
277
|
+
const onResolved = sink ? (value: unknown) => parameterize(sink, value) : undefined;
|
|
212
278
|
|
|
213
279
|
for (const [key, value] of Object.entries(args)) {
|
|
214
280
|
if (
|
|
@@ -217,7 +283,7 @@ const resolveSessionInArguments = (
|
|
|
217
283
|
!Array.isArray(value) &&
|
|
218
284
|
hasSessionVariables(value as Record<string, unknown>)
|
|
219
285
|
) {
|
|
220
|
-
result[key] = replaceSessionVariables(value as Record<string, unknown>, session);
|
|
286
|
+
result[key] = replaceSessionVariables(value as Record<string, unknown>, session, onResolved);
|
|
221
287
|
} else {
|
|
222
288
|
result[key] = value;
|
|
223
289
|
}
|
|
@@ -226,6 +292,33 @@ const resolveSessionInArguments = (
|
|
|
226
292
|
return result;
|
|
227
293
|
};
|
|
228
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Binds every literal still sitting in a `where` argument.
|
|
297
|
+
*
|
|
298
|
+
* Only `where` is walked. `orderBy` carries direction enums and `limit`/`offset`
|
|
299
|
+
* are consumed by the pagination builder, and neither is emitted as a bound
|
|
300
|
+
* value, so parameterizing them would change the SQL rather than protect it.
|
|
301
|
+
*/
|
|
302
|
+
const parameterizeWhereLiterals = (value: unknown, sink: VariableSink): unknown => {
|
|
303
|
+
if (value === null || value === undefined) return value;
|
|
304
|
+
|
|
305
|
+
if (Array.isArray(value)) return value.map((item) => parameterizeWhereLiterals(item, sink));
|
|
306
|
+
|
|
307
|
+
if (typeof value === "object") {
|
|
308
|
+
return Object.fromEntries(
|
|
309
|
+
Object.entries(value as Record<string, unknown>).map(([key, nested]) => [
|
|
310
|
+
key,
|
|
311
|
+
parameterizeWhereLiterals(nested, sink),
|
|
312
|
+
]),
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Already a reference — the analyzer hoisted it out of the document.
|
|
317
|
+
if (isString(value) && value.startsWith("$")) return value;
|
|
318
|
+
|
|
319
|
+
return parameterize(sink, value);
|
|
320
|
+
};
|
|
321
|
+
|
|
229
322
|
/**
|
|
230
323
|
* Recursively replaces `$varName` references with their resolved (already-flattened) values from
|
|
231
324
|
* `resolvedMap`, walking nested objects/arrays so a reference nested inside an inline argument
|
|
@@ -268,6 +361,7 @@ export const resolveFieldArguments = (
|
|
|
268
361
|
fields: SelectionAnalysis[],
|
|
269
362
|
resolvedMap: Map<string, unknown>,
|
|
270
363
|
session?: SessionContext,
|
|
364
|
+
sink?: VariableSink,
|
|
271
365
|
): SelectionAnalysis[] => {
|
|
272
366
|
return fields.map((field) => {
|
|
273
367
|
let newArguments = field.arguments;
|
|
@@ -287,13 +381,19 @@ export const resolveFieldArguments = (
|
|
|
287
381
|
|
|
288
382
|
// Replace session variables in ALL argument values (not just where)
|
|
289
383
|
if (session) {
|
|
290
|
-
newArguments = resolveSessionInArguments(newArguments, session);
|
|
384
|
+
newArguments = resolveSessionInArguments(newArguments, session, sink);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Bind whatever literals remain in `where`. After the two passes above the
|
|
388
|
+
// only ones left are the constants a role filter was configured with.
|
|
389
|
+
if (sink && newArguments["where"] !== undefined) {
|
|
390
|
+
newArguments["where"] = parameterizeWhereLiterals(newArguments["where"], sink);
|
|
291
391
|
}
|
|
292
392
|
}
|
|
293
393
|
|
|
294
394
|
// Recurse into nested selections
|
|
295
395
|
const newSelections = field.selections
|
|
296
|
-
? resolveFieldArguments(field.selections, resolvedMap, session)
|
|
396
|
+
? resolveFieldArguments(field.selections, resolvedMap, session, sink)
|
|
297
397
|
: field.selections;
|
|
298
398
|
|
|
299
399
|
// Only create a new field object if something changed
|
|
@@ -381,15 +481,20 @@ export const resolveVariables = (
|
|
|
381
481
|
const hasVars = operation.variables && operation.variables.length > 0;
|
|
382
482
|
|
|
383
483
|
if (!hasVars) {
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
:
|
|
484
|
+
// A document with no variables of its own can still carry a role filter,
|
|
485
|
+
// whose constants and session values both need binding.
|
|
486
|
+
const sink: VariableSink = {
|
|
487
|
+
generatedVariables: [],
|
|
488
|
+
runtimeVariables: {},
|
|
489
|
+
startIndex: 0,
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
const fields = resolveFieldArguments(operation.fields, new Map(), session, sink);
|
|
388
493
|
|
|
389
494
|
return {
|
|
390
495
|
fields,
|
|
391
|
-
variables: operation.variables ?? [],
|
|
392
|
-
allVariables: { ...variables },
|
|
496
|
+
variables: [...(operation.variables ?? []), ...sink.generatedVariables],
|
|
497
|
+
allVariables: { ...variables, ...sink.runtimeVariables },
|
|
393
498
|
};
|
|
394
499
|
}
|
|
395
500
|
|
|
@@ -404,10 +509,15 @@ export const resolveVariables = (
|
|
|
404
509
|
);
|
|
405
510
|
|
|
406
511
|
// 3. Resolve field arguments (immutable)
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
512
|
+
// Numbering continues past the statics flattening just produced, so the two
|
|
513
|
+
// passes cannot mint the same name.
|
|
514
|
+
const sink: VariableSink = {
|
|
515
|
+
generatedVariables: [],
|
|
516
|
+
runtimeVariables: {},
|
|
517
|
+
startIndex: existingStaticCount + flattenResult.newStaticVariables.length,
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const fields = resolveFieldArguments(operation.fields, flattenResult.resolvedMap, session, sink);
|
|
411
521
|
|
|
412
522
|
// 4. Build final variable list and merged values
|
|
413
523
|
const { variables: resolvedVarDefs, allVariables } = buildFinalVariables(
|
|
@@ -416,9 +526,13 @@ export const resolveVariables = (
|
|
|
416
526
|
variables,
|
|
417
527
|
);
|
|
418
528
|
|
|
529
|
+
const finalVarDefs = resolvedVarDefs.length > 0 ? resolvedVarDefs : operation.variables!;
|
|
530
|
+
|
|
419
531
|
return {
|
|
420
532
|
fields,
|
|
421
|
-
|
|
422
|
-
|
|
533
|
+
// Appended, never reordered: the builders emit `$n` from a definition's
|
|
534
|
+
// index in this list, and the driver binds it from the same list.
|
|
535
|
+
variables: [...finalVarDefs, ...sink.generatedVariables],
|
|
536
|
+
allVariables: { ...allVariables, ...sink.runtimeVariables },
|
|
423
537
|
};
|
|
424
538
|
};
|
package/src/config/types/auth.ts
CHANGED
|
@@ -64,11 +64,17 @@ export type FilterOperator = (typeof FILTER_OPERATORS)[number];
|
|
|
64
64
|
* holding more of the same. Operators are constrained to the implemented set so
|
|
65
65
|
* a typo fails at boot instead of evaluating to no condition, which would hand
|
|
66
66
|
* the role the whole table.
|
|
67
|
+
*
|
|
68
|
+
* `partialRecord`, not `record`: an enum-keyed `z.record` is exhaustive, and
|
|
69
|
+
* `z.unknown()` accepts `undefined`, so the plain spelling parses
|
|
70
|
+
* `{ eq: … }` into an object carrying all twelve operator keys. The extra keys
|
|
71
|
+
* are invisible to JSON.stringify but reach buildCondition through
|
|
72
|
+
* Object.entries, which then evaluates operators the filter never declared.
|
|
67
73
|
*/
|
|
68
74
|
export const FilterConditionZod: z.ZodType<Record<string, unknown>> = z.lazy(() =>
|
|
69
75
|
z.record(
|
|
70
76
|
z.string(),
|
|
71
|
-
z.union([z.
|
|
77
|
+
z.union([z.partialRecord(z.enum(FILTER_OPERATORS), z.unknown()), FilterConditionZod]),
|
|
72
78
|
),
|
|
73
79
|
);
|
|
74
80
|
|
package/src/config/types/db.ts
CHANGED
|
@@ -138,6 +138,14 @@ export const BunSQLConnectionOptionsZod = z.strictObject({
|
|
|
138
138
|
maxLifetime: z.number().nonnegative().default(3600),
|
|
139
139
|
/** Whether to use TLS/SSL for the connection */
|
|
140
140
|
tls: z.boolean().default(false),
|
|
141
|
+
/**
|
|
142
|
+
* MySQL only: allow the client to fetch the server's RSA public key over a
|
|
143
|
+
* plain connection when caching_sha2_password asks for full authentication.
|
|
144
|
+
* Off by default, as it is in Bun: without TLS a man-in-the-middle can answer
|
|
145
|
+
* with its own key and read the password. Needed for MySQL 8 servers that are
|
|
146
|
+
* reachable only over plain TCP.
|
|
147
|
+
*/
|
|
148
|
+
allowPublicKeyRetrieval: z.boolean().default(false),
|
|
141
149
|
/** Automatic creation of prepared statements (default: true) */
|
|
142
150
|
prepare: z.boolean().default(true),
|
|
143
151
|
/** Return values outside i32 range as BigInts instead of strings (default: false) */
|
package/src/cron/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { logger } from "../logging";
|
|
|
13
13
|
*/
|
|
14
14
|
export interface ScheduledCronJob {
|
|
15
15
|
name: string;
|
|
16
|
-
job: Cron
|
|
16
|
+
job: Cron<NonNullable<CronJob["context"]>>;
|
|
17
17
|
config: CronJob;
|
|
18
18
|
executionCount: number;
|
|
19
19
|
pause: () => boolean;
|
|
@@ -106,7 +106,11 @@ const createScheduledJob = (config: CronJob, gqlQuery: GqlQueryFn<true>): Schedu
|
|
|
106
106
|
name: config.name,
|
|
107
107
|
job,
|
|
108
108
|
config,
|
|
109
|
-
|
|
109
|
+
// Getter, not a copy: the counter lives in the tick closure and callers
|
|
110
|
+
// (console status, getSummary) read it after ticks have happened.
|
|
111
|
+
get executionCount() {
|
|
112
|
+
return executionCount;
|
|
113
|
+
},
|
|
110
114
|
pause: () => {
|
|
111
115
|
job.pause();
|
|
112
116
|
log.info("paused");
|
|
@@ -22,6 +22,7 @@ export const getPool = async (db: Database) => {
|
|
|
22
22
|
connectionTimeout: opts?.connectionTimeout,
|
|
23
23
|
maxLifetime: opts?.maxLifetime,
|
|
24
24
|
tls: opts?.tls,
|
|
25
|
+
allowPublicKeyRetrieval: opts?.allowPublicKeyRetrieval,
|
|
25
26
|
prepare: opts?.prepare,
|
|
26
27
|
bigint: opts?.bigint,
|
|
27
28
|
});
|
package/src/logging/index.ts
CHANGED
|
@@ -3,7 +3,9 @@ import pino from "pino";
|
|
|
3
3
|
const DEFAULT_LEVEL =
|
|
4
4
|
process.env.LOG_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "debug");
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// `bun test` sets NODE_ENV=test: pino-pretty runs on a worker thread, and
|
|
7
|
+
// Bun's test runner cannot construct one (thread-stream crashes on import).
|
|
8
|
+
const isDev = process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "test";
|
|
7
9
|
|
|
8
10
|
const DEFAULT_OPTIONS: pino.LoggerOptions = {
|
|
9
11
|
level: DEFAULT_LEVEL,
|
package/src/types/db.ts
CHANGED
package/src/types/zod/db.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
BunSQLConnectionOptionsZod,
|
|
5
|
-
DatabaseConnectionZod,
|
|
6
|
-
DatabaseSchemaConfigZod,
|
|
7
|
-
MSSQLConnectionOptionsZod,
|
|
8
|
-
TableRelationshipZod as TableRelationshipBaseZod,
|
|
9
|
-
} from "../../config";
|
|
3
|
+
import { TableRelationshipZod as TableRelationshipBaseZod } from "../../config";
|
|
10
4
|
import { genResolverName } from "../../databases/transformers/genResolverName";
|
|
11
5
|
import { sanitizeGraphQLName } from "../../databases/transformers/graphqlName";
|
|
12
6
|
|
package/src/types/zod/queue.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
KafkaConnectionZod,
|
|
6
|
-
PublisherConfigZod,
|
|
7
|
-
RabbitMQConnectionZod,
|
|
8
|
-
ReconnectConfigZod,
|
|
9
|
-
SubscriberConfigZod,
|
|
10
|
-
TopicConfigZod,
|
|
11
|
-
} from "../../config";
|
|
12
|
-
import type { CacheContext, SubscriberHandler } from "../../config";
|
|
3
|
+
import { BaseQueueConfigZod, KafkaConnectionZod, RabbitMQConnectionZod } from "../../config";
|
|
4
|
+
import type { SubscriberHandler } from "../../config";
|
|
13
5
|
|
|
14
6
|
// Re-export base types and schemas from the config module
|
|
15
7
|
export type {
|
|
@@ -37,6 +37,10 @@ const resolveDotPath = (obj: Record<string, unknown>, path: string): unknown =>
|
|
|
37
37
|
*
|
|
38
38
|
* @param filter - Filter object with potential session variables
|
|
39
39
|
* @param session - JWT payload containing user session data
|
|
40
|
+
* @param onResolved - Called with each substituted claim value; its return value
|
|
41
|
+
* is what lands in the filter. The caller uses this to parameterize the value
|
|
42
|
+
* rather than letting it be inlined into SQL — a claim is attacker-influenced
|
|
43
|
+
* whenever users can change their own profile data.
|
|
40
44
|
* @returns Filter object with session variables replaced
|
|
41
45
|
*
|
|
42
46
|
* @example
|
|
@@ -54,6 +58,7 @@ const resolveDotPath = (obj: Record<string, unknown>, path: string): unknown =>
|
|
|
54
58
|
export const replaceSessionVariables = (
|
|
55
59
|
filter: Record<string, unknown>,
|
|
56
60
|
session: SessionContext | null,
|
|
61
|
+
onResolved?: (value: unknown) => unknown,
|
|
57
62
|
): Record<string, unknown> => {
|
|
58
63
|
if (!filter || !session) {
|
|
59
64
|
return filter;
|
|
@@ -79,7 +84,7 @@ export const replaceSessionVariables = (
|
|
|
79
84
|
);
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
processedOperators[operator] = sessionValue;
|
|
87
|
+
processedOperators[operator] = onResolved ? onResolved(sessionValue) : sessionValue;
|
|
83
88
|
}
|
|
84
89
|
// Recursively handle nested objects
|
|
85
90
|
else if (
|
|
@@ -90,6 +95,7 @@ export const replaceSessionVariables = (
|
|
|
90
95
|
processedOperators[operator] = replaceSessionVariables(
|
|
91
96
|
operatorValue as Record<string, unknown>,
|
|
92
97
|
session,
|
|
98
|
+
onResolved,
|
|
93
99
|
);
|
|
94
100
|
}
|
|
95
101
|
// Keep other values as-is
|