@quickql/server 1.0.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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/core/src/client.d.ts +57 -0
  4. package/dist/core/src/client.d.ts.map +1 -0
  5. package/dist/core/src/client.js +164 -0
  6. package/dist/core/src/client.js.map +1 -0
  7. package/dist/core/src/index.d.ts +13 -0
  8. package/dist/core/src/index.d.ts.map +1 -0
  9. package/dist/core/src/index.js +13 -0
  10. package/dist/core/src/index.js.map +1 -0
  11. package/dist/core/src/types.d.ts +82 -0
  12. package/dist/core/src/types.d.ts.map +1 -0
  13. package/dist/core/src/types.js +12 -0
  14. package/dist/core/src/types.js.map +1 -0
  15. package/dist/server/src/engine.d.ts +55 -0
  16. package/dist/server/src/engine.d.ts.map +1 -0
  17. package/dist/server/src/engine.js +422 -0
  18. package/dist/server/src/engine.js.map +1 -0
  19. package/dist/server/src/errors.d.ts +31 -0
  20. package/dist/server/src/errors.d.ts.map +1 -0
  21. package/dist/server/src/errors.js +73 -0
  22. package/dist/server/src/errors.js.map +1 -0
  23. package/dist/server/src/executor.d.ts +25 -0
  24. package/dist/server/src/executor.d.ts.map +1 -0
  25. package/dist/server/src/executor.js +121 -0
  26. package/dist/server/src/executor.js.map +1 -0
  27. package/dist/server/src/filters.d.ts +10 -0
  28. package/dist/server/src/filters.d.ts.map +1 -0
  29. package/dist/server/src/filters.js +47 -0
  30. package/dist/server/src/filters.js.map +1 -0
  31. package/dist/server/src/handler.d.ts +12 -0
  32. package/dist/server/src/handler.d.ts.map +1 -0
  33. package/dist/server/src/handler.js +138 -0
  34. package/dist/server/src/handler.js.map +1 -0
  35. package/dist/server/src/index.d.ts +25 -0
  36. package/dist/server/src/index.d.ts.map +1 -0
  37. package/dist/server/src/index.js +26 -0
  38. package/dist/server/src/index.js.map +1 -0
  39. package/dist/server/src/parser/parser.d.ts +21 -0
  40. package/dist/server/src/parser/parser.d.ts.map +1 -0
  41. package/dist/server/src/parser/parser.js +99 -0
  42. package/dist/server/src/parser/parser.js.map +1 -0
  43. package/dist/server/src/parser/tokenizer.d.ts +18 -0
  44. package/dist/server/src/parser/tokenizer.d.ts.map +1 -0
  45. package/dist/server/src/parser/tokenizer.js +94 -0
  46. package/dist/server/src/parser/tokenizer.js.map +1 -0
  47. package/dist/server/src/parser/transformer.d.ts +24 -0
  48. package/dist/server/src/parser/transformer.d.ts.map +1 -0
  49. package/dist/server/src/parser/transformer.js +61 -0
  50. package/dist/server/src/parser/transformer.js.map +1 -0
  51. package/dist/server/src/parser/types.d.ts +21 -0
  52. package/dist/server/src/parser/types.d.ts.map +1 -0
  53. package/dist/server/src/parser/types.js +11 -0
  54. package/dist/server/src/parser/types.js.map +1 -0
  55. package/dist/server/src/playground/assets.d.ts +16 -0
  56. package/dist/server/src/playground/assets.d.ts.map +1 -0
  57. package/dist/server/src/playground/assets.js +1113 -0
  58. package/dist/server/src/playground/assets.js.map +1 -0
  59. package/dist/server/src/playground/docs.d.ts +15 -0
  60. package/dist/server/src/playground/docs.d.ts.map +1 -0
  61. package/dist/server/src/playground/docs.js +223 -0
  62. package/dist/server/src/playground/docs.js.map +1 -0
  63. package/dist/server/src/playground/html.d.ts +20 -0
  64. package/dist/server/src/playground/html.d.ts.map +1 -0
  65. package/dist/server/src/playground/html.js +50 -0
  66. package/dist/server/src/playground/html.js.map +1 -0
  67. package/dist/server/src/plugins/index.d.ts +21 -0
  68. package/dist/server/src/plugins/index.d.ts.map +1 -0
  69. package/dist/server/src/plugins/index.js +38 -0
  70. package/dist/server/src/plugins/index.js.map +1 -0
  71. package/dist/server/src/relations.d.ts +22 -0
  72. package/dist/server/src/relations.d.ts.map +1 -0
  73. package/dist/server/src/relations.js +98 -0
  74. package/dist/server/src/relations.js.map +1 -0
  75. package/dist/server/src/resolver/generator.d.ts +25 -0
  76. package/dist/server/src/resolver/generator.d.ts.map +1 -0
  77. package/dist/server/src/resolver/generator.js +65 -0
  78. package/dist/server/src/resolver/generator.js.map +1 -0
  79. package/dist/server/src/resolver/mapper.d.ts +23 -0
  80. package/dist/server/src/resolver/mapper.d.ts.map +1 -0
  81. package/dist/server/src/resolver/mapper.js +96 -0
  82. package/dist/server/src/resolver/mapper.js.map +1 -0
  83. package/dist/server/src/schema/builder.d.ts +21 -0
  84. package/dist/server/src/schema/builder.d.ts.map +1 -0
  85. package/dist/server/src/schema/builder.js +23 -0
  86. package/dist/server/src/schema/builder.js.map +1 -0
  87. package/dist/server/src/schema/types.d.ts +81 -0
  88. package/dist/server/src/schema/types.d.ts.map +1 -0
  89. package/dist/server/src/schema/types.js +11 -0
  90. package/dist/server/src/schema/types.js.map +1 -0
  91. package/dist/server/src/schema/utils.d.ts +16 -0
  92. package/dist/server/src/schema/utils.d.ts.map +1 -0
  93. package/dist/server/src/schema/utils.js +42 -0
  94. package/dist/server/src/schema/utils.js.map +1 -0
  95. package/dist/server/src/security.d.ts +46 -0
  96. package/dist/server/src/security.d.ts.map +1 -0
  97. package/dist/server/src/security.js +189 -0
  98. package/dist/server/src/security.js.map +1 -0
  99. package/dist/server/src/types.d.ts +158 -0
  100. package/dist/server/src/types.d.ts.map +1 -0
  101. package/dist/server/src/types.js +11 -0
  102. package/dist/server/src/types.js.map +1 -0
  103. package/dist/server/src/validator.d.ts +30 -0
  104. package/dist/server/src/validator.d.ts.map +1 -0
  105. package/dist/server/src/validator.js +171 -0
  106. package/dist/server/src/validator.js.map +1 -0
  107. package/package.json +25 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * QuickQL Security Engine
3
+ *
4
+ * Enforces authentication, authorization (RBAC), complexity limits, and rate limiting.
5
+ *
6
+ * (c) 2024-2026 Udinmo Inc. All rights reserved.
7
+ * Author: Udinmo Inc. <engineering@udinmo.com>
8
+ * License: MIT
9
+ */
10
+ import { createSecurityError } from './errors';
11
+ export class SecurityEngine {
12
+ options;
13
+ schemaSnapshot;
14
+ rateLimitMap = new Map();
15
+ constructor(options, schemaSnapshot // Added to allow recursive lookup
16
+ ) {
17
+ this.options = options;
18
+ this.schemaSnapshot = schemaSnapshot;
19
+ }
20
+ /**
21
+ * Enforces strict query depth limiting for security.
22
+ */
23
+ validateDepth(query, currentDepth = 0) {
24
+ if (currentDepth > this.options.maxDepth) {
25
+ throw createSecurityError(`Security violation: Nested query depth exceeds limit of ${this.options.maxDepth}.`);
26
+ }
27
+ // Calculate Complexity Budget
28
+ const complexity = this.calculateComplexity(query);
29
+ if (this.options.maxComplexity && complexity > this.options.maxComplexity) {
30
+ throw createSecurityError(`Complexity violation: Query complexity ${complexity} exceeds budget of ${this.options.maxComplexity}.`);
31
+ }
32
+ if (query.include) {
33
+ for (const nested of Object.values(query.include)) {
34
+ this.validateDepth(nested, currentDepth + 1);
35
+ }
36
+ }
37
+ }
38
+ calculateComplexity(query) {
39
+ let score = (query.select?.length || 5); // Default field weight
40
+ if (query.include) {
41
+ Object.values(query.include).forEach(nested => {
42
+ score += 10 + this.calculateComplexity(nested); // Relations weighted heavily
43
+ });
44
+ }
45
+ return score;
46
+ }
47
+ /**
48
+ * Verifies if the current context is authorized to access the collection or mutation.
49
+ */
50
+ async validateAuth(name, definition, ctx, isMutation = false) {
51
+ const userRole = ctx.user?.role || ctx.role || 'anonymous';
52
+ const rbac = this.options.rbac;
53
+ // 0. Global RBAC Check
54
+ if (rbac && rbac.roles[userRole]) {
55
+ const config = rbac.roles[userRole];
56
+ const isAdmin = userRole === (rbac.adminRole || 'admin');
57
+ if (!isAdmin) {
58
+ // Explicit denylist check
59
+ if (config.deny?.includes(name)) {
60
+ throw createSecurityError(`Forbidden: Access to '${name}' is explicitly denied for role '${userRole}'`);
61
+ }
62
+ // Permission check
63
+ const permissions = isMutation ? config.write : config.can;
64
+ if (permissions && !permissions.includes(name) && !permissions.includes('*')) {
65
+ throw createSecurityError(`Forbidden: Role '${userRole}' does not have ${isMutation ? 'write' : 'read'} access to '${name}'`);
66
+ }
67
+ }
68
+ else {
69
+ return; // Admin bypass!
70
+ }
71
+ }
72
+ // 1. Policy check (Top level)
73
+ if (definition?.policy) {
74
+ switch (definition.policy) {
75
+ case 'public': break; // Allow all
76
+ case 'private': throw createSecurityError(`Private collection '${name}': ACCESS_DENIED.`);
77
+ case 'authenticated':
78
+ if (!ctx.user)
79
+ throw createSecurityError(`Authenticated session required for '${name}'`);
80
+ break;
81
+ case 'owner-only':
82
+ if (!ctx.user)
83
+ throw createSecurityError(`Access denied: Ownership required for '${name}'`);
84
+ break;
85
+ }
86
+ }
87
+ // 2. Roles check (Shorthand in collection)
88
+ if (definition?.roles && Array.isArray(definition.roles)) {
89
+ if (!definition.roles.includes(userRole)) {
90
+ throw createSecurityError(`Forbidden: Role '${userRole}' is not allowed to access '${name}'`);
91
+ }
92
+ }
93
+ // 3. Custom Auth Hook (Fine-grained)
94
+ if (definition?.auth) {
95
+ const isAuthorized = await definition.auth(ctx);
96
+ if (!isAuthorized) {
97
+ throw createSecurityError(`Unauthorized: Access denied for '${name}'.`, [name]);
98
+ }
99
+ }
100
+ }
101
+ /**
102
+ * Filters the results to remove fields marked as 'private' or failing field-level auth.
103
+ */
104
+ async filterResults(collection, data, ctx) {
105
+ if (!data || data.length === 0)
106
+ return data;
107
+ const filteredData = await Promise.all(data.map(async (row) => {
108
+ const filteredRow = { ...row };
109
+ for (const [fieldName, fieldDef] of Object.entries(collection.fields)) {
110
+ // Handle both shorthand 'string' and full FieldDefinition objects
111
+ const isPrivate = typeof fieldDef === 'object' && fieldDef.private === true;
112
+ const hasAuth = typeof fieldDef === 'object' && fieldDef.auth;
113
+ const hasRoles = typeof fieldDef === 'object' && fieldDef.roles;
114
+ if (isPrivate) {
115
+ delete filteredRow[fieldName];
116
+ continue;
117
+ }
118
+ if (hasRoles && !fieldDef.roles.includes(ctx.user?.role || ctx.role)) {
119
+ delete filteredRow[fieldName];
120
+ continue;
121
+ }
122
+ if (hasAuth && !(await fieldDef.auth(ctx))) {
123
+ delete filteredRow[fieldName];
124
+ }
125
+ }
126
+ // 2. Recursively filter relations if present in the row
127
+ if (collection.relations) {
128
+ for (const [relKey, relDef] of Object.entries(collection.relations)) {
129
+ if (filteredRow[relKey] !== undefined) {
130
+ const targetName = relDef.model || relDef.from;
131
+ const targetCol = this.schemaSnapshot?.[targetName];
132
+ if (targetCol) {
133
+ const relData = filteredRow[relKey];
134
+ if (Array.isArray(relData)) {
135
+ filteredRow[relKey] = await this.filterResults(targetCol, relData, ctx);
136
+ }
137
+ else if (relData !== null && typeof relData === 'object') {
138
+ filteredRow[relKey] = (await this.filterResults(targetCol, [relData], ctx))[0];
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ return filteredRow;
145
+ }));
146
+ return filteredData;
147
+ }
148
+ /**
149
+ * Simple Memory-based Rate Limiter.
150
+ */
151
+ checkRateLimit(key) {
152
+ if (!this.options.rateLimit)
153
+ return;
154
+ const now = Date.now();
155
+ const windowMs = 60000; // 1 minute window
156
+ const userLimit = this.rateLimitMap.get(key) || { count: 0, lastReset: now };
157
+ if (now - userLimit.lastReset > windowMs) {
158
+ userLimit.count = 1;
159
+ userLimit.lastReset = now;
160
+ }
161
+ else {
162
+ userLimit.count++;
163
+ }
164
+ this.rateLimitMap.set(key, userLimit);
165
+ if (userLimit.count > this.options.rateLimit) {
166
+ throw createSecurityError(`Rate limit exceeded: Please try again in a minute.`);
167
+ }
168
+ }
169
+ /**
170
+ * Basic input sanitization.
171
+ */
172
+ sanitize(input) {
173
+ if (typeof input === 'string') {
174
+ return input.trim();
175
+ }
176
+ if (Array.isArray(input)) {
177
+ return input.map(i => this.sanitize(i));
178
+ }
179
+ if (typeof input === 'object' && input !== null) {
180
+ const sanitized = {};
181
+ for (const [key, value] of Object.entries(input)) {
182
+ sanitized[key] = this.sanitize(value);
183
+ }
184
+ return sanitized;
185
+ }
186
+ return input;
187
+ }
188
+ }
189
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/security.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * QuickQL Server Types
3
+ *
4
+ * Core type definitions for the server engine, plugins, and configuration.
5
+ *
6
+ * (c) 2024-2026 Udinmo Inc. All rights reserved.
7
+ * Author: Udinmo Inc. <engineering@udinmo.com>
8
+ * License: MIT
9
+ */
10
+ import { Query, Mutation } from '@quickql/core';
11
+ /**
12
+ * Global Context Interface
13
+ */
14
+ export interface QuickQLServerContext {
15
+ orm: any;
16
+ user?: any;
17
+ [key: string]: any;
18
+ }
19
+ /**
20
+ * Internal Engine Types
21
+ */
22
+ export interface InternalRelationDefinition {
23
+ from: string;
24
+ localKey: string;
25
+ foreignKey: string;
26
+ isSingle?: boolean;
27
+ }
28
+ export type CollectionSchema = {
29
+ fields: Record<string, any> | string[];
30
+ relations?: Record<string, InternalRelationDefinition>;
31
+ resolver: (query: Partial<Query>, ctx?: QuickQLServerContext) => Promise<Record<string, unknown>[]>;
32
+ mutationResolver?: (mutation: Mutation, ctx?: QuickQLServerContext) => Promise<Record<string, unknown>>;
33
+ };
34
+ export type SchemaConfig = Record<string, CollectionSchema>;
35
+ /**
36
+ * Hook & Plugin System
37
+ */
38
+ export interface QuickQLPlugin {
39
+ name?: string;
40
+ /**
41
+ * Only run hooks if this matches the request context
42
+ */
43
+ when?: (ctx: QuickQLServerContext) => boolean | Promise<boolean>;
44
+ /**
45
+ * Run before query execution. Can transform the query or validation state.
46
+ */
47
+ beforeQuery?: (query: Query, ctx: QuickQLServerContext) => void | Query | Promise<void | Query>;
48
+ /**
49
+ * Run after successfull execution. Can transform the data results.
50
+ */
51
+ afterQuery?: (result: any[], ctx: QuickQLServerContext) => void | any[] | Promise<void | any[]>;
52
+ /**
53
+ * Run when an error occurs.
54
+ */
55
+ onError?: (error: unknown, ctx: QuickQLServerContext) => void | Promise<void>;
56
+ /**
57
+ * Run BEFORE a mutation. Can transform or block the mutation.
58
+ */
59
+ beforeMutation?: (mutation: Mutation, ctx: QuickQLServerContext) => void | Mutation | Promise<void | Mutation>;
60
+ /**
61
+ * Run AFTER a mutation succeeds.
62
+ */
63
+ onMutation?: (mutation: Mutation, ctx: QuickQLServerContext) => void | Promise<void>;
64
+ }
65
+ export interface RBACConfig {
66
+ roles: Record<string, {
67
+ can?: string[];
68
+ write?: string[];
69
+ deny?: string[];
70
+ }>;
71
+ adminRole?: string;
72
+ }
73
+ export interface CacheConfig {
74
+ enabled?: boolean;
75
+ ttl?: number;
76
+ maxItems?: number;
77
+ exclude?: string[];
78
+ }
79
+ export interface PlaygroundConfig {
80
+ endpoint?: string;
81
+ debug?: boolean;
82
+ /**
83
+ * If true, will hide some advanced features from the UI.
84
+ */
85
+ readonly?: boolean;
86
+ }
87
+ export interface AuthConfig {
88
+ secret?: string;
89
+ strategies?: ('jwt' | 'apiKey' | 'session' | 'custom')[];
90
+ onAuth?: (ctx: QuickQLServerContext) => Promise<boolean | QuickQLServerContext> | boolean | QuickQLServerContext;
91
+ }
92
+ export interface ServerFeatures {
93
+ /**
94
+ * Automatically match plural/singular or fuzzy collection names.
95
+ * Default: true
96
+ */
97
+ smartCollectionMatching?: boolean;
98
+ /**
99
+ * Automatically infer relations from field names (e.g. userId -> users).
100
+ * Default: true
101
+ */
102
+ smartRelationDiscovery?: boolean;
103
+ /**
104
+ * Allow select: ['*'] to return all fields from the resolver result.
105
+ * Default: true
106
+ */
107
+ superSelection?: boolean;
108
+ /**
109
+ * Include performance and security hints in response metadata.
110
+ * Default: true
111
+ */
112
+ smartHints?: boolean;
113
+ }
114
+ export interface LoggingConfig {
115
+ level?: 'debug' | 'info' | 'warn' | 'error' | 'none';
116
+ customLogger?: (level: string, message: string, data?: any) => void;
117
+ slowQueryThreshold?: number;
118
+ }
119
+ export interface DirectiveConfig {
120
+ name: string;
121
+ handler: (value: any, args: Record<string, any>, ctx: QuickQLServerContext) => any | Promise<any>;
122
+ }
123
+ export interface TransformerConfig {
124
+ onInput?: (data: any) => any;
125
+ onOutput?: (data: any) => any;
126
+ }
127
+ export interface ErrorConfig {
128
+ format?: (error: any, debug?: boolean) => any;
129
+ maskInternalErrors?: boolean;
130
+ }
131
+ export interface ServerOptions {
132
+ schema: SchemaConfig;
133
+ features?: ServerFeatures;
134
+ maxDepth?: number;
135
+ maxComplexity?: number;
136
+ rateLimit?: number;
137
+ cors?: boolean;
138
+ debug?: boolean;
139
+ playground?: boolean | PlaygroundConfig;
140
+ plugins?: QuickQLPlugin[];
141
+ auth?: AuthConfig;
142
+ rbac?: RBACConfig;
143
+ cache?: CacheConfig;
144
+ logging?: LoggingConfig;
145
+ directives?: DirectiveConfig[];
146
+ transformers?: TransformerConfig;
147
+ errors?: ErrorConfig;
148
+ /**
149
+ * Custom context builder function.
150
+ * Receives the request input and returns a context object.
151
+ */
152
+ context?: (reqInput: any) => Promise<Record<string, any>> | Record<string, any>;
153
+ /**
154
+ * Optional wrapper for atomic batch transactions (e.g. Prisma.$transaction)
155
+ */
156
+ transactionWrapper?: (ctx: QuickQLServerContext, executeBatch: (tx: any) => Promise<any>) => Promise<any>;
157
+ }
158
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * QuickQL Server Types
3
+ *
4
+ * Core type definitions for the server engine, plugins, and configuration.
5
+ *
6
+ * (c) 2024-2026 Udinmo Inc. All rights reserved.
7
+ * Author: Udinmo Inc. <engineering@udinmo.com>
8
+ * License: MIT
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * QuickQL Query Validator
3
+ *
4
+ * Ensures every incoming request matches the defined schema and
5
+ * respects the configured security rules.
6
+ *
7
+ * (c) 2024-2026 Udinmo Inc. All rights reserved.
8
+ * Author: Udinmo Inc. <engineering@udinmo.com>
9
+ * License: MIT
10
+ */
11
+ import { Query, Mutation } from '@quickql/core';
12
+ import { SchemaConfig } from './types';
13
+ import { SchemaOptions } from './schema/types';
14
+ import { ServerFeatures } from './types';
15
+ export declare class QueryValidator {
16
+ private schema;
17
+ private maxDepth;
18
+ private schemaOptions?;
19
+ private features;
20
+ constructor(schema: SchemaConfig, maxDepth: number, schemaOptions?: SchemaOptions | undefined, features?: ServerFeatures);
21
+ private suggestionCache;
22
+ private fieldSetCache;
23
+ private getFieldsSet;
24
+ validateQuery(query: Query, depth?: number): void;
25
+ validateMutation(mutation: Mutation): void;
26
+ private validateArguments;
27
+ private findCachedSuggestion;
28
+ private findSuggestion;
29
+ }
30
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,cAAc;IAEvB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa,CAAC;IACtB,OAAO,CAAC,QAAQ;gBAHR,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,aAAa,YAAA,EAC7B,QAAQ,GAAE,cAAgF;IAGpG,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,aAAa,CAAkC;IAEvD,OAAO,CAAC,YAAY;IAOpB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,SAAI,GAAG,IAAI;IAgF5C,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAqB1C,OAAO,CAAC,iBAAiB;IAsCzB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,cAAc;CAYvB"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * QuickQL Query Validator
3
+ *
4
+ * Ensures every incoming request matches the defined schema and
5
+ * respects the configured security rules.
6
+ *
7
+ * (c) 2024-2026 Udinmo Inc. All rights reserved.
8
+ * Author: Udinmo Inc. <engineering@udinmo.com>
9
+ * License: MIT
10
+ */
11
+ import { createValidationError, createSecurityError } from './errors';
12
+ export class QueryValidator {
13
+ schema;
14
+ maxDepth;
15
+ schemaOptions;
16
+ features;
17
+ constructor(schema, maxDepth, schemaOptions, features = { smartCollectionMatching: true, smartRelationDiscovery: true }) {
18
+ this.schema = schema;
19
+ this.maxDepth = maxDepth;
20
+ this.schemaOptions = schemaOptions;
21
+ this.features = features;
22
+ }
23
+ suggestionCache = new Map();
24
+ fieldSetCache = new Map();
25
+ getFieldsSet(collectionName, fields) {
26
+ if (this.fieldSetCache.has(collectionName))
27
+ return this.fieldSetCache.get(collectionName);
28
+ const set = new Set(Array.isArray(fields) ? fields : Object.keys(fields));
29
+ this.fieldSetCache.set(collectionName, set);
30
+ return set;
31
+ }
32
+ validateQuery(query, depth = 0) {
33
+ if (depth > this.maxDepth) {
34
+ throw createSecurityError(`Security violation: Query exceeds maximum depth of ${this.maxDepth}.`);
35
+ }
36
+ const collectionName = query.from;
37
+ let collection = this.schema[collectionName];
38
+ // Smart Discovery: Try singular / plural / fuzzy variations
39
+ if (!collection && this.features.smartCollectionMatching) {
40
+ const candidates = Object.keys(this.schema);
41
+ const exactMatch = candidates.find(c => c === collectionName ||
42
+ c === `${collectionName}s` ||
43
+ c === `${collectionName}es` ||
44
+ c === collectionName.replace(/s$/, ''));
45
+ if (exactMatch) {
46
+ collection = this.schema[exactMatch];
47
+ }
48
+ else {
49
+ const suggestion = this.findCachedSuggestion(collectionName, candidates);
50
+ if (suggestion) {
51
+ collection = this.schema[suggestion];
52
+ }
53
+ }
54
+ }
55
+ if (!collection) {
56
+ const suggestion = this.findCachedSuggestion(collectionName, Object.keys(this.schema));
57
+ throw createValidationError(`Unknown collection: ${collectionName}${suggestion ? `. Did you mean '${suggestion}'?` : ''}`);
58
+ }
59
+ // 1. Validate fields in selection
60
+ const fieldSet = this.getFieldsSet(collectionName, collection.fields);
61
+ if (query.select) {
62
+ for (const field of query.select) {
63
+ if (field === '*')
64
+ continue;
65
+ if (!fieldSet.has(field)) {
66
+ throw createValidationError(`Invalid field selection: '${field}' does not exist on collection '${collectionName}'`);
67
+ }
68
+ }
69
+ }
70
+ // 2. Validate relations in inclusion
71
+ if (query.include) {
72
+ for (const relationKey of Object.keys(query.include)) {
73
+ let relationDef = collection.relations?.[relationKey];
74
+ // Smart Discovery: If missing, allow if naming convention might match (e.g. author -> authorId)
75
+ if (!relationDef && this.features.smartRelationDiscovery) {
76
+ const possibleKeys = [`${relationKey}Id`, `${relationKey}_id`, `${relationKey}`];
77
+ const hasMatchingField = possibleKeys.some(k => collection.fields.includes(k));
78
+ const guestTarget = [relationKey, `${relationKey}s`, `${relationKey}es`].some(t => this.schema[t]);
79
+ if (hasMatchingField && guestTarget) {
80
+ // Implicitly allowed!
81
+ continue;
82
+ }
83
+ }
84
+ if (!relationDef) {
85
+ throw createValidationError(`Invalid relation inclusion: '${relationKey}' is not defined for collection '${collectionName}'`);
86
+ }
87
+ const nestedQuery = query.include?.[relationKey];
88
+ if (nestedQuery) {
89
+ // Normalize the nested query 'from' for validation
90
+ const schemaQuery = { ...nestedQuery, from: relationDef.from };
91
+ this.validateQuery(schemaQuery, depth + 1);
92
+ }
93
+ }
94
+ }
95
+ // 3. Validate arguments in where clause
96
+ if (query.where) {
97
+ this.validateArguments(query.where, collection.fields, collectionName);
98
+ }
99
+ }
100
+ validateMutation(mutation) {
101
+ // 1. Check if it's a custom root mutation (Schema-level)
102
+ if (this.schemaOptions?.mutations?.[mutation.collection]) {
103
+ return;
104
+ }
105
+ // Fallback logic for collection-level mutations
106
+ const collection = this.schema[mutation.collection];
107
+ if (collection) {
108
+ if (!collection.mutationResolver) {
109
+ throw createValidationError(`Mutations are NOT enabled for collection: ${mutation.collection}`);
110
+ }
111
+ if ((mutation.type === 'create' || mutation.type === 'update') && !mutation.data) {
112
+ throw createValidationError(`Data payload is required for mutation type: ${mutation.type}`);
113
+ }
114
+ return;
115
+ }
116
+ throw createValidationError(`Unknown mutation or collection: ${mutation.collection}`);
117
+ }
118
+ validateArguments(where, collectionFields, collectionName) {
119
+ const keys = Object.keys(where);
120
+ const fieldNames = Array.isArray(collectionFields) ? collectionFields : Object.keys(collectionFields);
121
+ for (const key of keys) {
122
+ if (key === 'AND' || key === 'OR') {
123
+ const subConditions = where[key];
124
+ if (Array.isArray(subConditions)) {
125
+ subConditions.forEach(sub => this.validateArguments(sub, collectionFields, collectionName));
126
+ }
127
+ continue;
128
+ }
129
+ if (!fieldNames.includes(key)) {
130
+ throw createValidationError(`Invalid filter: Field '${key}' does not exist on collection '${collectionName}'`);
131
+ }
132
+ // Add Type-Safety Check!
133
+ if (!Array.isArray(collectionFields)) {
134
+ const fieldDef = collectionFields[key];
135
+ const type = typeof fieldDef === 'string' ? fieldDef : fieldDef.type;
136
+ const value = where[key];
137
+ if (type && typeof value === 'object' && value !== null && !Array.isArray(value)) {
138
+ // Operator check (can't easily type check the value inside 'eq/gt' here without a mapping)
139
+ }
140
+ else if (type && value !== null) {
141
+ const actualType = typeof value;
142
+ if (type === 'number' && actualType !== 'number') {
143
+ throw createValidationError(`Type mismatch: Field '${key}' expects ${type}, but got ${actualType}`);
144
+ }
145
+ if (type === 'boolean' && actualType !== 'boolean') {
146
+ throw createValidationError(`Type mismatch: Field '${key}' expects ${type}, but got ${actualType}`);
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ findCachedSuggestion(input, options) {
153
+ if (this.suggestionCache.has(input))
154
+ return this.suggestionCache.get(input);
155
+ const suggestion = this.findSuggestion(input, options);
156
+ this.suggestionCache.set(input, suggestion);
157
+ return suggestion;
158
+ }
159
+ findSuggestion(input, options) {
160
+ if (options.length === 0)
161
+ return undefined;
162
+ const inputLower = input.toLowerCase();
163
+ // Exact case-insensitive match
164
+ const nearMatch = options.find(o => o.toLowerCase() === inputLower);
165
+ if (nearMatch)
166
+ return nearMatch;
167
+ // Basic substring check or length proximity
168
+ return options.find(o => o.toLowerCase().includes(inputLower) || inputLower.includes(o.toLowerCase()));
169
+ }
170
+ }
171
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/validator.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "name": "@quickql/server",
4
+ "description": "Enterprise-grade QuickQL server engine with advanced security and performance.",
5
+ "author": "Udinmo Inc. <engineering@udinmo.com>",
6
+ "license": "MIT",
7
+ "homepage": "https://quickql.io",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build",
16
+ "test": "tsx tests/runner.ts",
17
+ "playground": "tsx dev.ts"
18
+ },
19
+ "dependencies": {
20
+ "@quickql/core": "*"
21
+ },
22
+ "devDependencies": {
23
+ "prismjs": "^1.30.0"
24
+ }
25
+ }