@simtlix/simfinity-core 3.2.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/LICENSE +201 -0
- package/README.md +28 -0
- package/package.json +40 -0
- package/src/auth/errors.js +43 -0
- package/src/auth/expressions.js +159 -0
- package/src/auth/index.js +421 -0
- package/src/auth/object-id.js +25 -0
- package/src/auth/rules.js +300 -0
- package/src/const/QLOperator.js +19 -0
- package/src/const/QLSort.js +24 -0
- package/src/const/QLValue.js +32 -0
- package/src/errors/internal-server.error.js +11 -0
- package/src/errors/simfinity.error.js +15 -0
- package/src/index.js +17 -0
- package/src/introspection.js +37 -0
- package/src/metadata.js +140 -0
- package/src/plugins.js +53 -0
- package/src/query-limits.js +26 -0
- package/src/query-plan.js +110 -0
- package/src/runtime.js +1387 -0
- package/src/scalars/factory.js +52 -0
- package/src/scalars.js +187 -0
- package/src/validators.js +249 -0
- package/types/index.d.ts +306 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GraphQLEnumType,
|
|
3
|
+
GraphQLInputObjectType,
|
|
4
|
+
GraphQLObjectType,
|
|
5
|
+
GraphQLOutputType,
|
|
6
|
+
GraphQLScalarType,
|
|
7
|
+
GraphQLSchema,
|
|
8
|
+
} from 'graphql';
|
|
9
|
+
|
|
10
|
+
export const QLOperator: GraphQLEnumType;
|
|
11
|
+
export const QLSort: GraphQLInputObjectType;
|
|
12
|
+
export const QLValue: GraphQLScalarType<unknown, unknown>;
|
|
13
|
+
|
|
14
|
+
export interface ModelRegistration {
|
|
15
|
+
gqltype: GraphQLObjectType;
|
|
16
|
+
/** Defaults to true. False excludes embedded-only types until referenced. */
|
|
17
|
+
endpoint?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface FieldDescription {
|
|
20
|
+
name: string;
|
|
21
|
+
storageName: string;
|
|
22
|
+
kind: 'scalar' | 'reference' | 'collection' | 'embedded';
|
|
23
|
+
required: boolean;
|
|
24
|
+
list: boolean;
|
|
25
|
+
itemRequired: boolean;
|
|
26
|
+
unique: boolean;
|
|
27
|
+
readOnly: boolean;
|
|
28
|
+
scalar?: 'ID' | 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'Enum';
|
|
29
|
+
values?: string[];
|
|
30
|
+
enumValues?: { name: string; value: unknown }[];
|
|
31
|
+
target?: string;
|
|
32
|
+
connectionField?: string;
|
|
33
|
+
fields?: FieldDescription[];
|
|
34
|
+
private?: boolean;
|
|
35
|
+
inferred?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface ModelDescription {
|
|
38
|
+
entities: Array<{
|
|
39
|
+
name: string;
|
|
40
|
+
gqltype: GraphQLObjectType;
|
|
41
|
+
fields: FieldDescription[];
|
|
42
|
+
indexes: Array<{ fields: string[]; unique: boolean }>;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export function describeModels(registrations: ModelRegistration[]): ModelDescription;
|
|
46
|
+
export function createValidatedScalar<TInternal, TExternal>(
|
|
47
|
+
name: string,
|
|
48
|
+
description: string,
|
|
49
|
+
baseScalarType: GraphQLScalarType<TInternal, TExternal>,
|
|
50
|
+
validate: (value: unknown) => void,
|
|
51
|
+
): GraphQLScalarType<TInternal, TExternal> & { baseScalarType: GraphQLScalarType<TInternal, TExternal> };
|
|
52
|
+
export class SimfinityError extends Error {
|
|
53
|
+
constructor(message: string, code?: string, status?: number);
|
|
54
|
+
extensions: { code: string | undefined; status: number | undefined; timestamp: string };
|
|
55
|
+
getCode(): string | undefined;
|
|
56
|
+
getStatus(): number | undefined;
|
|
57
|
+
getTimestamp(): string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class InternalServerError extends SimfinityError {
|
|
61
|
+
constructor(message: string, cause?: unknown);
|
|
62
|
+
cause?: unknown;
|
|
63
|
+
getCause(): unknown;
|
|
64
|
+
}
|
|
65
|
+
export function buildErrorFormatter(callback?: (error: SimfinityError) => Error | void): (error: Error) => Error;
|
|
66
|
+
|
|
67
|
+
export interface EntityController<Session = any> {
|
|
68
|
+
onSaving?(record: any, args: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
69
|
+
onSaved?(record: any, args: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
70
|
+
onUpdating?(id: any, update: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
71
|
+
onUpdated?(record: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
72
|
+
onDelete?(record: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
export interface StateMachine<Session = any> {
|
|
75
|
+
initialState: { name: string; value: any };
|
|
76
|
+
actions: Record<string, {
|
|
77
|
+
description?: string;
|
|
78
|
+
from: { name: string; value: any };
|
|
79
|
+
to: { name: string; value: any };
|
|
80
|
+
action?: (args: any, session: Session) => void | Promise<void>;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
export interface RuntimeRegistration<Model = any, Session = any> extends ModelRegistration {
|
|
84
|
+
model?: Model | null;
|
|
85
|
+
simpleEntityEndpointName?: string;
|
|
86
|
+
listEntitiesEndpointName?: string;
|
|
87
|
+
controller?: EntityController<Session> | null;
|
|
88
|
+
onModelCreated?: ((model: Model) => void) | null;
|
|
89
|
+
stateMachine?: StateMachine<Session> | null;
|
|
90
|
+
}
|
|
91
|
+
export interface MiddlewareContext {
|
|
92
|
+
args: any;
|
|
93
|
+
operation: string;
|
|
94
|
+
entry?: string;
|
|
95
|
+
type?: RuntimeRegistration;
|
|
96
|
+
context?: any;
|
|
97
|
+
[key: string]: any;
|
|
98
|
+
}
|
|
99
|
+
export interface Runtime<Model = any, Session = any> {
|
|
100
|
+
configureQueryLimits(options?: { maxPageSize?: number }): void;
|
|
101
|
+
connect(model: Model | null, type: GraphQLObjectType, singular: string, plural: string, controller?: EntityController<Session> | null, onModelCreated?: ((model: Model) => void) | null, stateMachine?: StateMachine<Session> | null): void;
|
|
102
|
+
addNoEndpointType(type: GraphQLObjectType): void;
|
|
103
|
+
createSchema(includedQueryTypes?: GraphQLObjectType[] | null, includedMutationTypes?: GraphQLObjectType[] | null, includedCustomMutations?: string[] | null): GraphQLSchema;
|
|
104
|
+
getModel(type: GraphQLObjectType | { name: string }): Model | null | undefined;
|
|
105
|
+
getType(name: string | { name: string }): GraphQLObjectType | null | undefined;
|
|
106
|
+
/** Available after createSchema has built input types. */
|
|
107
|
+
getInputType(type: GraphQLObjectType | { name: string }): GraphQLInputObjectType | undefined;
|
|
108
|
+
getRegistrations(): RuntimeRegistration<Model, Session>[];
|
|
109
|
+
use(middleware: (params: MiddlewareContext, next: () => Promise<void>) => void | Promise<void>): void;
|
|
110
|
+
registerMutation(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (args: any, session: Session, context: any) => any): void;
|
|
111
|
+
/** Owns the full workflow transaction unless an active caller session is supplied. */
|
|
112
|
+
saveObject(typeName: string, args: Record<string, any>, session?: Session, context?: any): Promise<any>;
|
|
113
|
+
preventCreatingCollection(prevent: boolean): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Driver adapter contract. GraphQL generation and lifecycle live in createRuntime. */
|
|
117
|
+
export interface DatabaseAdapter<Model = any, Session = any> {
|
|
118
|
+
bind?(runtime: Pick<Runtime<Model, Session>, 'getModel' | 'getType' | 'getRegistrations'>): void;
|
|
119
|
+
validateRegistration?(registration: RuntimeRegistration<Model, Session>): void;
|
|
120
|
+
prepare?(registrations: RuntimeRegistration<Model, Session>[], options: { createCollection: boolean }): void;
|
|
121
|
+
createModel(type: GraphQLObjectType, callback: ((model: Model) => void) | null | undefined, options: { createCollection: boolean }): Model;
|
|
122
|
+
castId(value: any): any;
|
|
123
|
+
stateValue?(state: { name: string; value: any }): any;
|
|
124
|
+
withTransaction<T>(session: Session | null | undefined, callback: (session: Session) => Promise<T> | T, model?: Model): Promise<T>;
|
|
125
|
+
newRecord(model: Model, data: any, session?: Session): any;
|
|
126
|
+
saveRecord(model: Model, record: any, session?: Session): any;
|
|
127
|
+
toObject(record: any): any;
|
|
128
|
+
getById(model: Model, id: any, session?: Session | null, options?: { projection?: Record<string, number>; plain?: boolean; lock?: boolean; requiredId?: any; context?: any }): any;
|
|
129
|
+
prepareUpdate(set: Record<string, any>, unset: Record<string, string>): any;
|
|
130
|
+
update(model: Model, id: any, changes: any, session?: Session): any;
|
|
131
|
+
delete(model: Model, id: any, session?: Session): any;
|
|
132
|
+
find(model: Model, type: GraphQLObjectType, args: any, session?: Session | null, options?: { requiredId?: any; context?: any }): any;
|
|
133
|
+
count(model: Model, type: GraphQLObjectType, args: any, session?: Session | null): Promise<number> | number;
|
|
134
|
+
aggregate(model: Model, type: GraphQLObjectType, args: any, session?: Session | null): any;
|
|
135
|
+
findChildren(model: Model, type: GraphQLObjectType, connectionField: string, parentId: any, args: any, session?: Session | null): any;
|
|
136
|
+
}
|
|
137
|
+
export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapter<Model, Session>): Runtime<Model, Session>;
|
|
138
|
+
|
|
139
|
+
export type QueryPredicate = { kind: 'predicate'; path: string[]; operator: string; value: any } | { kind: 'and' | 'or'; terms: QueryPredicate[] };
|
|
140
|
+
export interface QueryPlan {
|
|
141
|
+
entity: string;
|
|
142
|
+
where: QueryPredicate | null;
|
|
143
|
+
mode: 'find' | 'count' | 'aggregate';
|
|
144
|
+
sort: Array<{ path: string[]; order: 'ASC' | 'DESC' }>;
|
|
145
|
+
pagination: { limit: number; offset: number } | null;
|
|
146
|
+
aggregation?: { groupId: string[]; facts: Array<{ path: string[]; factName: string; operation: 'SUM' | 'COUNT' | 'AVG' | 'MIN' | 'MAX' }> };
|
|
147
|
+
}
|
|
148
|
+
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode'] }): QueryPlan;
|
|
149
|
+
export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
|
|
150
|
+
|
|
151
|
+
export function configureQueryLimits(options?: { maxPageSize?: number }): void;
|
|
152
|
+
export function paginationStages(pagination: { page: number; size: number } | null | undefined, withDefault: boolean): Array<{ $skip: number } | { $limit: number }>;
|
|
153
|
+
|
|
154
|
+
/** Envelop-style schema plugin used by the shared authorization helpers. */
|
|
155
|
+
export interface EnvelopSchemaPlugin {
|
|
156
|
+
onSchemaChange?: (payload: {
|
|
157
|
+
schema: GraphQLSchema;
|
|
158
|
+
replaceSchema: (schema: GraphQLSchema) => void;
|
|
159
|
+
}) => void;
|
|
160
|
+
[key: string]: unknown;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** A single field validator (throws SimfinityError VALIDATION_ERROR on failure). */
|
|
164
|
+
export interface FieldValidator {
|
|
165
|
+
validate(typeName: string, fieldName: string, value: unknown, session?: unknown): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Validators grouped by operation, as expected by field `extensions.validations`. */
|
|
169
|
+
export interface FieldValidations {
|
|
170
|
+
CREATE: FieldValidator[];
|
|
171
|
+
UPDATE: FieldValidator[];
|
|
172
|
+
/** Backward-compatible alias of CREATE. */
|
|
173
|
+
save: FieldValidator[];
|
|
174
|
+
/** Backward-compatible alias of UPDATE. */
|
|
175
|
+
update: FieldValidator[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Built-in field validator factories. */
|
|
179
|
+
export const validators: {
|
|
180
|
+
stringLength(name: string, min?: number, max?: number): FieldValidations;
|
|
181
|
+
maxLength(name: string, max: number): FieldValidations;
|
|
182
|
+
pattern(name: string, regex: RegExp | string, message?: string): FieldValidations;
|
|
183
|
+
email(): FieldValidations;
|
|
184
|
+
url(): FieldValidations;
|
|
185
|
+
numberRange(name: string, min?: number, max?: number): FieldValidations;
|
|
186
|
+
positive(name: string): FieldValidations;
|
|
187
|
+
arrayLength(name: string, maxItems?: number, itemValidator?: FieldValidator[]): FieldValidations;
|
|
188
|
+
dateFormat(name: string, format?: string): FieldValidations;
|
|
189
|
+
futureDate(name: string): FieldValidations;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/* ========================================================================== *
|
|
193
|
+
* Shared scalars (packages/core/src/scalars.js default export)
|
|
194
|
+
* ========================================================================== */
|
|
195
|
+
|
|
196
|
+
/** Pre-built validated scalars and factory functions. */
|
|
197
|
+
export const scalars: {
|
|
198
|
+
EmailScalar: GraphQLScalarType;
|
|
199
|
+
URLScalar: GraphQLScalarType;
|
|
200
|
+
PositiveIntScalar: GraphQLScalarType;
|
|
201
|
+
PositiveFloatScalar: GraphQLScalarType;
|
|
202
|
+
createBoundedStringScalar(name: string, min?: number, max?: number): GraphQLScalarType;
|
|
203
|
+
createBoundedIntScalar(name: string, min?: number, max?: number): GraphQLScalarType;
|
|
204
|
+
createBoundedFloatScalar(name: string, min?: number, max?: number): GraphQLScalarType;
|
|
205
|
+
createPatternStringScalar(name: string, pattern: RegExp | string, message?: string): GraphQLScalarType;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/* ========================================================================== *
|
|
209
|
+
* Shared auth (packages/core/src/auth/index.js default export)
|
|
210
|
+
* ========================================================================== */
|
|
211
|
+
|
|
212
|
+
/** A rule function: only true/void allows; all other values deny (or throw). */
|
|
213
|
+
export type AuthRuleFunction = (
|
|
214
|
+
parent: any,
|
|
215
|
+
args: any,
|
|
216
|
+
ctx: any,
|
|
217
|
+
info: any,
|
|
218
|
+
) => boolean | void | Promise<boolean | void>;
|
|
219
|
+
|
|
220
|
+
/** Declarative policy expression (JSON AST or boolean). The complete AST is validated at runtime. */
|
|
221
|
+
export type PolicyExpression = boolean | Record<string, unknown>;
|
|
222
|
+
|
|
223
|
+
/** A rule: function, nonempty nested array of rules (AND), or a policy expression. */
|
|
224
|
+
export type AuthRule = AuthRuleFunction | AuthRule[] | PolicyExpression;
|
|
225
|
+
|
|
226
|
+
/** Field-name (or '*') to rule mapping for one GraphQL type. */
|
|
227
|
+
export type TypePermissions = Record<string, AuthRule>;
|
|
228
|
+
|
|
229
|
+
/** Type-name to {@link TypePermissions} mapping. */
|
|
230
|
+
export type PermissionSchema = Record<string, TypePermissions>;
|
|
231
|
+
|
|
232
|
+
/** Options for the auth plugin / middleware factories. */
|
|
233
|
+
export interface AuthPluginOptions {
|
|
234
|
+
/** Policy applied when no rule matches. Default 'DENY'. */
|
|
235
|
+
defaultPolicy?: 'ALLOW' | 'DENY';
|
|
236
|
+
debug?: boolean;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
declare class UnauthenticatedError extends SimfinityError {
|
|
240
|
+
constructor(message?: string);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
declare class ForbiddenError extends SimfinityError {
|
|
244
|
+
constructor(message?: string);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Authorization utilities (RBAC/ABAC rules, plugin factories and auth errors). */
|
|
248
|
+
export const auth: {
|
|
249
|
+
/** Wraps schema resolvers in-place. Throws TypeError for invalid rules, maps, or defaultPolicy. */
|
|
250
|
+
createAuthPlugin(permissions: PermissionSchema, options?: AuthPluginOptions): EnvelopSchemaPlugin;
|
|
251
|
+
/** @deprecated Use createAuthPlugin instead. graphql-middleware compatible middleware. */
|
|
252
|
+
createAuthMiddleware(
|
|
253
|
+
permissions: PermissionSchema,
|
|
254
|
+
options?: AuthPluginOptions,
|
|
255
|
+
): (resolve: any, parent: any, args: any, ctx: any, info: any) => Promise<any>;
|
|
256
|
+
/** @deprecated Use createAuthPlugin instead. Field middleware object for graphql-middleware. */
|
|
257
|
+
createFieldMiddleware(
|
|
258
|
+
permissions: PermissionSchema,
|
|
259
|
+
options?: AuthPluginOptions,
|
|
260
|
+
): Record<string, Record<string, any>>;
|
|
261
|
+
resolvePath(obj: any, pathOrFn: string | ((obj: any) => any)): any;
|
|
262
|
+
requireAuth(userPath?: string): AuthRuleFunction;
|
|
263
|
+
/** Required roles must be nonempty strings; invalid configuration throws TypeError. */
|
|
264
|
+
requireRole(role: string | string[], options?: { userPath?: string; rolePath?: string }): AuthRuleFunction;
|
|
265
|
+
/** Exact array membership; only a standalone '*' claim grants all permissions. */
|
|
266
|
+
requirePermission(
|
|
267
|
+
permission: string | string[],
|
|
268
|
+
options?: { userPath?: string; permissionsPath?: string },
|
|
269
|
+
): AuthRuleFunction;
|
|
270
|
+
composeRules(...rules: AuthRuleFunction[]): AuthRuleFunction;
|
|
271
|
+
anyRule(...rules: AuthRuleFunction[]): AuthRuleFunction;
|
|
272
|
+
/** Compares nonempty string, finite number, or MongoDB ObjectId identities; missing IDs deny. */
|
|
273
|
+
isOwner(ownerField?: string, userIdField?: string, options?: { userPath?: string }): AuthRuleFunction;
|
|
274
|
+
createRule(predicate: AuthRuleFunction, errorMessage?: string, errorCode?: string): AuthRuleFunction;
|
|
275
|
+
allow(): AuthRuleFunction;
|
|
276
|
+
deny(message?: string): AuthRuleFunction;
|
|
277
|
+
/** Invalid ASTs and unresolved comparisons deny, including under negation. */
|
|
278
|
+
evaluateExpression(expression: unknown, context: any): boolean;
|
|
279
|
+
isPolicyExpression(value: unknown): boolean;
|
|
280
|
+
/** Throws TypeError for a malformed expression. */
|
|
281
|
+
createRuleFromExpression(expression: PolicyExpression): AuthRuleFunction;
|
|
282
|
+
UnauthenticatedError: typeof UnauthenticatedError;
|
|
283
|
+
ForbiddenError: typeof ForbiddenError;
|
|
284
|
+
createAuthError(message: string, code?: string): SimfinityError;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/* ========================================================================== *
|
|
288
|
+
* Shared plugins (packages/core/src/plugins.js default export)
|
|
289
|
+
* ========================================================================== */
|
|
290
|
+
|
|
291
|
+
/** GraphQL server plugins: auth plugin factory plus count-extension plugins. */
|
|
292
|
+
export const plugins: {
|
|
293
|
+
createAuthPlugin(permissions: PermissionSchema, options?: AuthPluginOptions): EnvelopSchemaPlugin;
|
|
294
|
+
/** Apollo Server plugin that copies `contextValue.count` into `extensions.count`. */
|
|
295
|
+
apolloCountPlugin(): {
|
|
296
|
+
requestDidStart(): Promise<{
|
|
297
|
+
willSendResponse(payload: any): Promise<void>;
|
|
298
|
+
}>;
|
|
299
|
+
};
|
|
300
|
+
/** Envelop plugin that copies `contextValue.count` into `extensions.count`. */
|
|
301
|
+
envelopCountPlugin(): {
|
|
302
|
+
onExecute(): {
|
|
303
|
+
onExecuteDone(payload: any): void;
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
};
|