UNPKG

zenstack

Version:

FullStack enhancement for Prisma ORM: seamless integration from database to UI

103 lines 4.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateAuthType = generateAuthType; const sdk_1 = require("@zenstackhq/sdk"); const ast_1 = require("@zenstackhq/sdk/ast"); const langium_1 = require("langium"); const ast_utils_1 = require("../../../utils/ast-utils"); /** * Generate types for typing the `user` context object passed to the `enhance` call, based * on the fields (potentially deeply) access through `auth()`. */ function generateAuthType(model, authDecl) { const types = new Map(); types.set(authDecl.name, { requiredRelations: [] }); const ensureType = (model) => { if (!types.has(model)) { types.set(model, { requiredRelations: [] }); } }; const addAddField = (model, name, type, array) => { let fields = types.get(model); if (!fields) { fields = { requiredRelations: [] }; types.set(model, fields); } if (!fields.requiredRelations.find((f) => f.name === name)) { fields.requiredRelations.push({ name, type: array ? `${type}[]` : type }); } }; // get all policy expressions involving `auth()` const authInvolvedExprs = (0, langium_1.streamAst)(model).filter(isAuthAccess); // traverse the expressions and collect types and fields involved authInvolvedExprs.forEach((expr) => { (0, langium_1.streamAst)(expr).forEach((node) => { var _a, _b, _c; if ((0, ast_1.isMemberAccessExpr)(node)) { const exprType = (_a = node.operand.$resolvedType) === null || _a === void 0 ? void 0 : _a.decl; if ((0, ast_1.isDataModel)(exprType)) { const memberDecl = node.member.ref; if ((0, ast_1.isDataModel)((_b = memberDecl === null || memberDecl === void 0 ? void 0 : memberDecl.type.reference) === null || _b === void 0 ? void 0 : _b.ref)) { // member is a relation const fieldType = memberDecl.type.reference.ref.name; ensureType(fieldType); addAddField(exprType.name, memberDecl.name, fieldType, memberDecl.type.array); } } } if ((0, sdk_1.isDataModelFieldReference)(node)) { // this can happen inside collection predicates const fieldDecl = node.target.ref; const fieldType = (_c = fieldDecl.type.reference) === null || _c === void 0 ? void 0 : _c.ref; if ((0, ast_1.isDataModel)(fieldType)) { // field is a relation ensureType(fieldType.name); addAddField(fieldDecl.$container.name, node.target.$refText, fieldType.name, fieldDecl.type.array); } } }); }); // generate: // ` // namespace auth { // export type User = WithRequired<Partial<_P.User>, 'id'> & { profile: Profile; } & Record<string, unknown>; // export type Profile = WithRequired<Partial<_P.Profile>, 'age'> & Record<string, unknown>; // } // ` return `export namespace auth { type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }; ${Array.from(types.entries()) .map(([model, fields]) => { let result = `Partial<_P.${model}>`; if (model === authDecl.name) { // auth model's id fields are always required const idFields = (0, sdk_1.getIdFields)(authDecl).map((f) => f.name); if (idFields.length > 0) { result = `WithRequired<${result}, ${idFields.map((f) => `'${f}'`).join('|')}>`; } } if (fields.requiredRelations.length > 0) { // merge required relation fields result = `${result} & { ${fields.requiredRelations.map((f) => `${f.name}: ${f.type}`).join('; ')} }`; } result = `${result} & Record<string, unknown>`; return ` export type ${model} = ${result};`; }) .join('\n')} }`; } function isAuthAccess(node) { if ((0, sdk_1.isAuthInvocation)(node)) { return true; } if ((0, ast_1.isMemberAccessExpr)(node) && isAuthAccess(node.operand)) { return true; } if ((0, ast_utils_1.isCollectionPredicate)(node)) { if (isAuthAccess(node.left)) { return true; } } return false; } //# sourceMappingURL=auth-type-generator.js.map