zenstack 1.0.0 → 1.0.1

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 (300) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +194 -1
  3. package/bin/cli +3 -0
  4. package/bin/post-install.js +24 -0
  5. package/cli/actions/generate.d.ts +13 -0
  6. package/cli/actions/generate.js +71 -0
  7. package/cli/actions/generate.js.map +1 -0
  8. package/cli/actions/index.d.ts +3 -0
  9. package/cli/actions/index.js +20 -0
  10. package/cli/actions/index.js.map +1 -0
  11. package/cli/actions/info.d.ts +4 -0
  12. package/cli/actions/info.js +63 -0
  13. package/cli/actions/info.js.map +1 -0
  14. package/cli/actions/init.d.ts +12 -0
  15. package/cli/actions/init.js +83 -0
  16. package/cli/actions/init.js.map +1 -0
  17. package/cli/cli-error.d.ts +5 -0
  18. package/cli/cli-error.js +10 -0
  19. package/cli/cli-error.js.map +1 -0
  20. package/cli/cli-util.d.ts +21 -0
  21. package/cli/cli-util.js +211 -0
  22. package/cli/cli-util.js.map +1 -0
  23. package/cli/config.d.ts +10 -0
  24. package/cli/config.js +62 -0
  25. package/cli/config.js.map +1 -0
  26. package/cli/index.d.ts +7 -0
  27. package/cli/index.js +128 -0
  28. package/cli/index.js.map +1 -0
  29. package/cli/plugin-runner.d.ts +24 -0
  30. package/cli/plugin-runner.js +229 -0
  31. package/cli/plugin-runner.js.map +1 -0
  32. package/constants.d.ts +1 -0
  33. package/constants.js +6 -0
  34. package/constants.js.map +1 -0
  35. package/language-server/constants.d.ts +22 -0
  36. package/language-server/constants.js +27 -0
  37. package/language-server/constants.js.map +1 -0
  38. package/language-server/main.d.ts +1 -0
  39. package/language-server/main.js +13 -0
  40. package/language-server/main.js.map +1 -0
  41. package/language-server/types.d.ts +10 -0
  42. package/language-server/types.js +3 -0
  43. package/language-server/types.js.map +1 -0
  44. package/language-server/utils.d.ts +5 -0
  45. package/language-server/utils.js +22 -0
  46. package/language-server/utils.js.map +1 -0
  47. package/language-server/validator/attribute-application-validator.d.ts +15 -0
  48. package/language-server/validator/attribute-application-validator.js +246 -0
  49. package/language-server/validator/attribute-application-validator.js.map +1 -0
  50. package/language-server/validator/attribute-validator.d.ts +9 -0
  51. package/language-server/validator/attribute-validator.js +14 -0
  52. package/language-server/validator/attribute-validator.js.map +1 -0
  53. package/language-server/validator/datamodel-validator.d.ts +22 -0
  54. package/language-server/validator/datamodel-validator.js +329 -0
  55. package/language-server/validator/datamodel-validator.js.map +1 -0
  56. package/language-server/validator/datasource-validator.d.ts +12 -0
  57. package/language-server/validator/datasource-validator.js +66 -0
  58. package/language-server/validator/datasource-validator.js.map +1 -0
  59. package/language-server/validator/enum-validator.d.ts +11 -0
  60. package/language-server/validator/enum-validator.js +25 -0
  61. package/language-server/validator/enum-validator.js.map +1 -0
  62. package/language-server/validator/expression-validator.d.ts +10 -0
  63. package/language-server/validator/expression-validator.js +135 -0
  64. package/language-server/validator/expression-validator.js.map +1 -0
  65. package/language-server/validator/function-decl-validator.d.ts +9 -0
  66. package/language-server/validator/function-decl-validator.js +13 -0
  67. package/language-server/validator/function-decl-validator.js.map +1 -0
  68. package/language-server/validator/function-invocation-validator.d.ts +11 -0
  69. package/language-server/validator/function-invocation-validator.js +135 -0
  70. package/language-server/validator/function-invocation-validator.js.map +1 -0
  71. package/language-server/validator/schema-validator.d.ts +13 -0
  72. package/language-server/validator/schema-validator.js +49 -0
  73. package/language-server/validator/schema-validator.js.map +1 -0
  74. package/language-server/validator/utils.d.ts +24 -0
  75. package/language-server/validator/utils.js +154 -0
  76. package/language-server/validator/utils.js.map +1 -0
  77. package/language-server/validator/zmodel-validator.d.ts +25 -0
  78. package/language-server/validator/zmodel-validator.js +83 -0
  79. package/language-server/validator/zmodel-validator.js.map +1 -0
  80. package/language-server/zmodel-code-action.d.ts +15 -0
  81. package/language-server/zmodel-code-action.js +118 -0
  82. package/language-server/zmodel-code-action.js.map +1 -0
  83. package/language-server/zmodel-definition.d.ts +7 -0
  84. package/language-server/zmodel-definition.js +31 -0
  85. package/language-server/zmodel-definition.js.map +1 -0
  86. package/language-server/zmodel-formatter.d.ts +9 -0
  87. package/language-server/zmodel-formatter.js +76 -0
  88. package/language-server/zmodel-formatter.js.map +1 -0
  89. package/language-server/zmodel-linker.d.ts +32 -0
  90. package/language-server/zmodel-linker.js +447 -0
  91. package/language-server/zmodel-linker.js.map +1 -0
  92. package/language-server/zmodel-module.d.ts +41 -0
  93. package/language-server/zmodel-module.js +83 -0
  94. package/language-server/zmodel-module.js.map +1 -0
  95. package/language-server/zmodel-scope.d.ts +16 -0
  96. package/language-server/zmodel-scope.js +100 -0
  97. package/language-server/zmodel-scope.js.map +1 -0
  98. package/language-server/zmodel-workspace-manager.d.ts +12 -0
  99. package/language-server/zmodel-workspace-manager.js +138 -0
  100. package/language-server/zmodel-workspace-manager.js.map +1 -0
  101. package/package.json +140 -8
  102. package/plugins/access-policy/expression-writer.d.ts +46 -0
  103. package/plugins/access-policy/expression-writer.js +580 -0
  104. package/plugins/access-policy/expression-writer.js.map +1 -0
  105. package/plugins/access-policy/index.d.ts +4 -0
  106. package/plugins/access-policy/index.js +22 -0
  107. package/plugins/access-policy/index.js.map +1 -0
  108. package/plugins/access-policy/policy-guard-generator.d.ts +22 -0
  109. package/plugins/access-policy/policy-guard-generator.js +634 -0
  110. package/plugins/access-policy/policy-guard-generator.js.map +1 -0
  111. package/plugins/model-meta/index.d.ts +4 -0
  112. package/plugins/model-meta/index.js +232 -0
  113. package/plugins/model-meta/index.js.map +1 -0
  114. package/plugins/plugin-utils.d.ts +17 -0
  115. package/plugins/plugin-utils.js +80 -0
  116. package/plugins/plugin-utils.js.map +1 -0
  117. package/plugins/prisma/indent-string.d.ts +4 -0
  118. package/plugins/prisma/indent-string.js +12 -0
  119. package/plugins/prisma/indent-string.js.map +1 -0
  120. package/plugins/prisma/index.d.ts +4 -0
  121. package/plugins/prisma/index.js +22 -0
  122. package/plugins/prisma/index.js.map +1 -0
  123. package/plugins/prisma/prisma-builder.d.ts +145 -0
  124. package/plugins/prisma/prisma-builder.js +358 -0
  125. package/plugins/prisma/prisma-builder.js.map +1 -0
  126. package/plugins/prisma/schema-generator.d.ts +29 -0
  127. package/plugins/prisma/schema-generator.js +336 -0
  128. package/plugins/prisma/schema-generator.js.map +1 -0
  129. package/plugins/prisma/zmodel-code-generator.d.ts +30 -0
  130. package/plugins/prisma/zmodel-code-generator.js +124 -0
  131. package/plugins/prisma/zmodel-code-generator.js.map +1 -0
  132. package/plugins/zod/generator.d.ts +4 -0
  133. package/plugins/zod/generator.js +254 -0
  134. package/plugins/zod/generator.js.map +1 -0
  135. package/plugins/zod/index.d.ts +4 -0
  136. package/plugins/zod/index.js +24 -0
  137. package/plugins/zod/index.js.map +1 -0
  138. package/plugins/zod/transformer.d.ts +68 -0
  139. package/plugins/zod/transformer.js +554 -0
  140. package/plugins/zod/transformer.js.map +1 -0
  141. package/plugins/zod/types.d.ts +25 -0
  142. package/plugins/zod/types.js +3 -0
  143. package/plugins/zod/types.js.map +1 -0
  144. package/plugins/zod/utils/removeDir.d.ts +1 -0
  145. package/plugins/zod/utils/removeDir.js +30 -0
  146. package/plugins/zod/utils/removeDir.js.map +1 -0
  147. package/plugins/zod/utils/schema-gen.d.ts +3 -0
  148. package/plugins/zod/utils/schema-gen.js +188 -0
  149. package/plugins/zod/utils/schema-gen.js.map +1 -0
  150. package/res/prism-zmodel.js +20 -0
  151. package/res/starter.zmodel +51 -0
  152. package/res/stdlib.zmodel +506 -0
  153. package/telemetry.d.ts +21 -0
  154. package/telemetry.js +129 -0
  155. package/telemetry.js.map +1 -0
  156. package/utils/ast-utils.d.ts +13 -0
  157. package/utils/ast-utils.js +136 -0
  158. package/utils/ast-utils.js.map +1 -0
  159. package/utils/exec-utils.d.ts +6 -0
  160. package/utils/exec-utils.js +13 -0
  161. package/utils/exec-utils.js.map +1 -0
  162. package/utils/pkg-utils.d.ts +3 -0
  163. package/utils/pkg-utils.js +64 -0
  164. package/utils/pkg-utils.js.map +1 -0
  165. package/utils/typescript-expression-transformer.d.ts +54 -0
  166. package/utils/typescript-expression-transformer.js +326 -0
  167. package/utils/typescript-expression-transformer.js.map +1 -0
  168. package/utils/version-utils.d.ts +1 -0
  169. package/utils/version-utils.js +20 -0
  170. package/utils/version-utils.js.map +1 -0
  171. package/.vscode/extensions.json +0 -7
  172. package/.vscode/launch.json +0 -49
  173. package/.vscode/settings.json +0 -4
  174. package/packages/internal/jest.config.ts +0 -32
  175. package/packages/internal/package.json +0 -42
  176. package/packages/internal/src/constants.ts +0 -1
  177. package/packages/internal/src/handler/data/guard-utils.ts +0 -7
  178. package/packages/internal/src/handler/data/handler.ts +0 -415
  179. package/packages/internal/src/handler/data/query-processor.ts +0 -504
  180. package/packages/internal/src/handler/index.ts +0 -1
  181. package/packages/internal/src/handler/types.ts +0 -20
  182. package/packages/internal/src/index.ts +0 -3
  183. package/packages/internal/src/request-handler.ts +0 -27
  184. package/packages/internal/src/request.ts +0 -101
  185. package/packages/internal/src/types.ts +0 -40
  186. package/packages/internal/tests/query-processor.test.ts +0 -172
  187. package/packages/internal/tsconfig.json +0 -21
  188. package/packages/runtime/auth.d.ts +0 -1
  189. package/packages/runtime/auth.js +0 -3
  190. package/packages/runtime/hooks.d.ts +0 -10
  191. package/packages/runtime/hooks.js +0 -3
  192. package/packages/runtime/index.d.ts +0 -3
  193. package/packages/runtime/index.js +0 -1
  194. package/packages/runtime/package-lock.json +0 -512
  195. package/packages/runtime/package.json +0 -16
  196. package/packages/runtime/server.d.ts +0 -1
  197. package/packages/runtime/server.js +0 -3
  198. package/packages/runtime/types.d.ts +0 -1
  199. package/packages/runtime/types.js +0 -3
  200. package/packages/schema/.eslintrc.json +0 -13
  201. package/packages/schema/.vscodeignore +0 -4
  202. package/packages/schema/asset/logo-dark.png +0 -0
  203. package/packages/schema/asset/logo-light.png +0 -0
  204. package/packages/schema/bin/cli +0 -3
  205. package/packages/schema/jest.config.ts +0 -32
  206. package/packages/schema/langium-config.json +0 -14
  207. package/packages/schema/langium-quickstart.md +0 -41
  208. package/packages/schema/language-configuration.json +0 -30
  209. package/packages/schema/package.json +0 -96
  210. package/packages/schema/src/cli/cli-util.ts +0 -80
  211. package/packages/schema/src/cli/index.ts +0 -64
  212. package/packages/schema/src/extension.ts +0 -76
  213. package/packages/schema/src/generator/constants.ts +0 -5
  214. package/packages/schema/src/generator/index.ts +0 -92
  215. package/packages/schema/src/generator/next-auth/index.ts +0 -197
  216. package/packages/schema/src/generator/package.template.json +0 -9
  217. package/packages/schema/src/generator/prisma/expression-writer.ts +0 -352
  218. package/packages/schema/src/generator/prisma/index.ts +0 -32
  219. package/packages/schema/src/generator/prisma/plain-expression-builder.ts +0 -91
  220. package/packages/schema/src/generator/prisma/prisma-builder.ts +0 -366
  221. package/packages/schema/src/generator/prisma/query-gard-generator.ts +0 -208
  222. package/packages/schema/src/generator/prisma/schema-generator.ts +0 -300
  223. package/packages/schema/src/generator/react-hooks/index.ts +0 -181
  224. package/packages/schema/src/generator/service/index.ts +0 -107
  225. package/packages/schema/src/generator/tsconfig.template.json +0 -17
  226. package/packages/schema/src/generator/types.ts +0 -17
  227. package/packages/schema/src/generator/utils.ts +0 -9
  228. package/packages/schema/src/language-server/generated/ast.ts +0 -603
  229. package/packages/schema/src/language-server/generated/grammar.ts +0 -2190
  230. package/packages/schema/src/language-server/generated/module.ts +0 -24
  231. package/packages/schema/src/language-server/main.ts +0 -12
  232. package/packages/schema/src/language-server/stdlib.zmodel +0 -22
  233. package/packages/schema/src/language-server/types.ts +0 -9
  234. package/packages/schema/src/language-server/zmodel-index.ts +0 -33
  235. package/packages/schema/src/language-server/zmodel-linker.ts +0 -409
  236. package/packages/schema/src/language-server/zmodel-module.ts +0 -90
  237. package/packages/schema/src/language-server/zmodel-scope.ts +0 -21
  238. package/packages/schema/src/language-server/zmodel-validator.ts +0 -35
  239. package/packages/schema/src/language-server/zmodel.langium +0 -186
  240. package/packages/schema/src/utils/exec-utils.ts +0 -5
  241. package/packages/schema/src/utils/indent-string.ts +0 -6
  242. package/packages/schema/syntaxes/zmodel.json +0 -57
  243. package/packages/schema/syntaxes/zmodel.tmLanguage.json +0 -57
  244. package/packages/schema/tests/generator/expression-writer.test.ts +0 -676
  245. package/packages/schema/tests/generator/prisma-builder.test.ts +0 -138
  246. package/packages/schema/tests/schema/parser.test.ts +0 -423
  247. package/packages/schema/tests/schema/sample-todo.test.ts +0 -14
  248. package/packages/schema/tests/utils.ts +0 -38
  249. package/packages/schema/tsconfig.json +0 -23
  250. package/pnpm-workspace.yaml +0 -3
  251. package/samples/todo/.env +0 -2
  252. package/samples/todo/.eslintrc.json +0 -3
  253. package/samples/todo/.vscode/launch.json +0 -11
  254. package/samples/todo/README.md +0 -34
  255. package/samples/todo/components/AuthGuard.tsx +0 -17
  256. package/samples/todo/components/Avatar.tsx +0 -22
  257. package/samples/todo/components/BreadCrumb.tsx +0 -44
  258. package/samples/todo/components/ManageMembers.tsx +0 -134
  259. package/samples/todo/components/NavBar.tsx +0 -57
  260. package/samples/todo/components/SpaceMembers.tsx +0 -76
  261. package/samples/todo/components/Spaces.tsx +0 -28
  262. package/samples/todo/components/TimeInfo.tsx +0 -17
  263. package/samples/todo/components/Todo.tsx +0 -72
  264. package/samples/todo/components/TodoList.tsx +0 -77
  265. package/samples/todo/lib/context.ts +0 -31
  266. package/samples/todo/next.config.js +0 -10
  267. package/samples/todo/package-lock.json +0 -7527
  268. package/samples/todo/package.json +0 -45
  269. package/samples/todo/pages/_app.tsx +0 -50
  270. package/samples/todo/pages/api/auth/[...nextauth].ts +0 -83
  271. package/samples/todo/pages/api/zenstack/[...path].ts +0 -16
  272. package/samples/todo/pages/create-space.tsx +0 -114
  273. package/samples/todo/pages/index.tsx +0 -32
  274. package/samples/todo/pages/space/[slug]/[listId]/index.tsx +0 -88
  275. package/samples/todo/pages/space/[slug]/index.tsx +0 -169
  276. package/samples/todo/postcss.config.js +0 -6
  277. package/samples/todo/public/avatar.jpg +0 -0
  278. package/samples/todo/public/favicon.ico +0 -0
  279. package/samples/todo/public/logo.png +0 -0
  280. package/samples/todo/public/vercel.svg +0 -4
  281. package/samples/todo/styles/globals.css +0 -7
  282. package/samples/todo/tailwind.config.js +0 -11
  283. package/samples/todo/tsconfig.json +0 -28
  284. package/samples/todo/types/next-auth.d.ts +0 -14
  285. package/samples/todo/types/next.d.ts +0 -16
  286. package/samples/todo/zenstack/migrations/20221014084317_init/migration.sql +0 -153
  287. package/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql +0 -23
  288. package/samples/todo/zenstack/migrations/migration_lock.toml +0 -3
  289. package/samples/todo/zenstack/schema.prisma +0 -126
  290. package/samples/todo/zenstack/schema.zmodel +0 -161
  291. package/tests/integration/jest.config.ts +0 -16
  292. package/tests/integration/package-lock.json +0 -1081
  293. package/tests/integration/package.json +0 -27
  294. package/tests/integration/tests/operation-coverate.test.ts +0 -563
  295. package/tests/integration/tests/operations.zmodel +0 -69
  296. package/tests/integration/tests/todo-e2e.test.ts +0 -577
  297. package/tests/integration/tests/todo.zmodel +0 -123
  298. package/tests/integration/tests/tsconfig.template.json +0 -10
  299. package/tests/integration/tests/utils.ts +0 -133
  300. package/tests/integration/tsconfig.json +0 -10
@@ -0,0 +1,634 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const ast_1 = require("@zenstackhq/language/ast");
16
+ const runtime_1 = require("@zenstackhq/runtime");
17
+ const sdk_1 = require("@zenstackhq/sdk");
18
+ const langium_1 = require("langium");
19
+ const lower_case_first_1 = require("lower-case-first");
20
+ const path_1 = __importDefault(require("path"));
21
+ const ts_morph_1 = require("ts-morph");
22
+ const _1 = require(".");
23
+ const ast_utils_1 = require("../../utils/ast-utils");
24
+ const typescript_expression_transformer_1 = require("../../utils/typescript-expression-transformer");
25
+ const plugin_utils_1 = require("../plugin-utils");
26
+ const expression_writer_1 = require("./expression-writer");
27
+ /**
28
+ * Generates source file that contains Prisma query guard objects used for injecting database queries
29
+ */
30
+ class PolicyGenerator {
31
+ generate(model, options, globalOptions) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ let output = options.output ? options.output : (0, plugin_utils_1.getDefaultOutputFolder)(globalOptions);
34
+ if (!output) {
35
+ throw new sdk_1.PluginError(options.name, `Unable to determine output path, not running plugin`);
36
+ }
37
+ output = (0, sdk_1.resolvePath)(output, options);
38
+ const project = (0, sdk_1.createProject)();
39
+ const sf = project.createSourceFile(path_1.default.join(output, 'policy.ts'), undefined, { overwrite: true });
40
+ sf.addStatements('/* eslint-disable */');
41
+ sf.addImportDeclaration({
42
+ namedImports: [
43
+ { name: 'type QueryContext' },
44
+ { name: 'type DbOperations' },
45
+ { name: 'hasAllFields' },
46
+ { name: 'allFieldsEqual' },
47
+ { name: 'type PolicyDef' },
48
+ ],
49
+ moduleSpecifier: `${sdk_1.RUNTIME_PACKAGE}`,
50
+ });
51
+ // import enums
52
+ const prismaImport = (0, sdk_1.getPrismaClientImportSpec)(model, output);
53
+ for (const e of model.declarations.filter((d) => (0, ast_1.isEnum)(d) && this.isEnumReferenced(model, d))) {
54
+ sf.addImportDeclaration({
55
+ namedImports: [{ name: e.name }],
56
+ moduleSpecifier: prismaImport,
57
+ });
58
+ }
59
+ const models = (0, sdk_1.getDataModels)(model);
60
+ const policyMap = {};
61
+ for (const model of models) {
62
+ policyMap[model.name] = yield this.generateQueryGuardForModel(model, sf);
63
+ }
64
+ sf.addVariableStatement({
65
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
66
+ declarations: [
67
+ {
68
+ name: 'policy',
69
+ type: 'PolicyDef',
70
+ initializer: (writer) => {
71
+ writer.block(() => {
72
+ writer.write('guard:');
73
+ writer.inlineBlock(() => {
74
+ for (const [model, map] of Object.entries(policyMap)) {
75
+ writer.write(`${(0, lower_case_first_1.lowerCaseFirst)(model)}:`);
76
+ writer.inlineBlock(() => {
77
+ for (const [op, func] of Object.entries(map)) {
78
+ if (typeof func === 'object') {
79
+ writer.write(`${op}: ${JSON.stringify(func)},`);
80
+ }
81
+ else {
82
+ writer.write(`${op}: ${func},`);
83
+ }
84
+ }
85
+ });
86
+ writer.write(',');
87
+ }
88
+ });
89
+ writer.writeLine(',');
90
+ writer.write('validation:');
91
+ writer.inlineBlock(() => {
92
+ for (const model of models) {
93
+ writer.write(`${(0, lower_case_first_1.lowerCaseFirst)(model.name)}:`);
94
+ writer.inlineBlock(() => {
95
+ writer.write(`hasValidation: ${(0, sdk_1.hasValidationAttributes)(model)}`);
96
+ });
97
+ writer.writeLine(',');
98
+ }
99
+ });
100
+ });
101
+ },
102
+ },
103
+ ],
104
+ });
105
+ sf.addStatements('export default policy');
106
+ let shouldCompile = true;
107
+ if (typeof options.compile === 'boolean') {
108
+ // explicit override
109
+ shouldCompile = options.compile;
110
+ }
111
+ else if (globalOptions) {
112
+ shouldCompile = globalOptions.compile;
113
+ }
114
+ if (!shouldCompile || options.preserveTsFiles === true) {
115
+ // save ts files
116
+ yield (0, sdk_1.saveProject)(project);
117
+ }
118
+ if (shouldCompile) {
119
+ yield (0, sdk_1.emitProject)(project);
120
+ }
121
+ });
122
+ }
123
+ isEnumReferenced(model, decl) {
124
+ return (0, langium_1.streamAllContents)(model).some((node) => {
125
+ var _a, _b;
126
+ if ((0, ast_1.isDataModelField)(node) && ((_a = node.type.reference) === null || _a === void 0 ? void 0 : _a.ref) === decl) {
127
+ // referenced as field type
128
+ return true;
129
+ }
130
+ if ((0, sdk_1.isEnumFieldReference)(node) && ((_b = node.target.ref) === null || _b === void 0 ? void 0 : _b.$container) === decl) {
131
+ // enum field is referenced
132
+ return true;
133
+ }
134
+ return false;
135
+ });
136
+ }
137
+ getPolicyExpressions(target, kind, operation) {
138
+ const attributes = target.attributes;
139
+ const attrName = (0, ast_1.isDataModel)(target) ? `@@${kind}` : `@${kind}`;
140
+ const attrs = attributes.filter((attr) => { var _a; return ((_a = attr.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === attrName; });
141
+ const checkOperation = operation === 'postUpdate' ? 'update' : operation;
142
+ let result = attrs
143
+ .filter((attr) => {
144
+ const opsValue = (0, sdk_1.getLiteral)(attr.args[0].value);
145
+ if (!opsValue) {
146
+ return false;
147
+ }
148
+ const ops = opsValue.split(',').map((s) => s.trim());
149
+ return ops.includes(checkOperation) || ops.includes('all');
150
+ })
151
+ .map((attr) => attr.args[1].value);
152
+ if (operation === 'update') {
153
+ result = this.processUpdatePolicies(result, false);
154
+ }
155
+ else if (operation === 'postUpdate') {
156
+ result = this.processUpdatePolicies(result, true);
157
+ }
158
+ return result;
159
+ }
160
+ processUpdatePolicies(expressions, postUpdate) {
161
+ return expressions
162
+ .map((expr) => this.visitPolicyExpression(expr, postUpdate))
163
+ .filter((e) => !!e);
164
+ }
165
+ visitPolicyExpression(expr, postUpdate) {
166
+ if ((0, ast_1.isBinaryExpr)(expr) && (expr.operator === '&&' || expr.operator === '||')) {
167
+ const left = this.visitPolicyExpression(expr.left, postUpdate);
168
+ const right = this.visitPolicyExpression(expr.right, postUpdate);
169
+ if (!left)
170
+ return right;
171
+ if (!right)
172
+ return left;
173
+ return Object.assign(Object.assign({}, expr), { left, right });
174
+ }
175
+ if ((0, ast_1.isUnaryExpr)(expr) && expr.operator === '!') {
176
+ const operand = this.visitPolicyExpression(expr.operand, postUpdate);
177
+ if (!operand)
178
+ return undefined;
179
+ return Object.assign(Object.assign({}, expr), { operand });
180
+ }
181
+ if (postUpdate && !this.hasFutureReference(expr)) {
182
+ return undefined;
183
+ }
184
+ else if (!postUpdate && this.hasFutureReference(expr)) {
185
+ return undefined;
186
+ }
187
+ return expr;
188
+ }
189
+ hasFutureReference(expr) {
190
+ var _a;
191
+ for (const node of (0, langium_1.streamAst)(expr)) {
192
+ if ((0, ast_1.isInvocationExpr)(node) && ((_a = node.function.ref) === null || _a === void 0 ? void 0 : _a.name) === 'future' && (0, sdk_1.isFromStdlib)(node.function.ref)) {
193
+ return true;
194
+ }
195
+ }
196
+ return false;
197
+ }
198
+ generateQueryGuardForModel(model, sourceFile) {
199
+ return __awaiter(this, void 0, void 0, function* () {
200
+ const result = {};
201
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
+ const policies = (0, sdk_1.analyzePolicies)(model);
203
+ for (const kind of plugin_utils_1.ALL_OPERATION_KINDS) {
204
+ if (policies[kind] === true || policies[kind] === false) {
205
+ result[kind] = policies[kind];
206
+ if (kind === 'create') {
207
+ result[kind + '_input'] = policies[kind];
208
+ }
209
+ continue;
210
+ }
211
+ const denies = this.getPolicyExpressions(model, 'deny', kind);
212
+ const allows = this.getPolicyExpressions(model, 'allow', kind);
213
+ if (kind === 'update' && allows.length === 0) {
214
+ // no allow rule for 'update', policy is constant based on if there's
215
+ // post-update counterpart
216
+ if (this.getPolicyExpressions(model, 'allow', 'postUpdate').length === 0) {
217
+ result[kind] = false;
218
+ continue;
219
+ }
220
+ else {
221
+ result[kind] = true;
222
+ continue;
223
+ }
224
+ }
225
+ if (kind === 'postUpdate' && allows.length === 0 && denies.length === 0) {
226
+ // no rule 'postUpdate', always allow
227
+ result[kind] = true;
228
+ continue;
229
+ }
230
+ const guardFunc = this.generateQueryGuardFunction(sourceFile, model, kind, allows, denies);
231
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
232
+ result[kind] = guardFunc.getName();
233
+ if (kind === 'postUpdate') {
234
+ const preValueSelect = this.generateSelectForRules(allows, denies);
235
+ if (preValueSelect) {
236
+ result[runtime_1.PRE_UPDATE_VALUE_SELECTOR] = preValueSelect;
237
+ }
238
+ }
239
+ if (kind === 'create' && this.canCheckCreateBasedOnInput(model, allows, denies)) {
240
+ const inputCheckFunc = this.generateInputCheckFunction(sourceFile, model, kind, allows, denies);
241
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
242
+ result[kind + '_input'] = inputCheckFunc.getName();
243
+ }
244
+ }
245
+ // generate field read checkers
246
+ this.generateReadFieldsGuards(model, sourceFile, result);
247
+ // generate field update guards
248
+ this.generateUpdateFieldsGuards(model, sourceFile, result);
249
+ return result;
250
+ });
251
+ }
252
+ generateReadFieldsGuards(model, sourceFile, result) {
253
+ const allFieldsAllows = [];
254
+ const allFieldsDenies = [];
255
+ for (const field of model.fields) {
256
+ const allows = this.getPolicyExpressions(field, 'allow', 'read');
257
+ const denies = this.getPolicyExpressions(field, 'deny', 'read');
258
+ if (denies.length === 0 && allows.length === 0) {
259
+ continue;
260
+ }
261
+ allFieldsAllows.push(...allows);
262
+ allFieldsDenies.push(...denies);
263
+ const guardFunc = this.generateReadFieldGuardFunction(sourceFile, field, allows, denies);
264
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
265
+ result[`${runtime_1.FIELD_LEVEL_READ_CHECKER_PREFIX}${field.name}`] = guardFunc.getName();
266
+ }
267
+ if (allFieldsAllows.length > 0 || allFieldsDenies.length > 0) {
268
+ result[runtime_1.HAS_FIELD_LEVEL_POLICY_FLAG] = true;
269
+ const readFieldCheckSelect = this.generateSelectForRules(allFieldsAllows, allFieldsDenies);
270
+ if (readFieldCheckSelect) {
271
+ result[runtime_1.FIELD_LEVEL_READ_CHECKER_SELECTOR] = readFieldCheckSelect;
272
+ }
273
+ }
274
+ }
275
+ generateReadFieldGuardFunction(sourceFile, field, allows, denies) {
276
+ const statements = [];
277
+ this.generateNormalizedAuthRef(field.$container, allows, denies, statements);
278
+ // compile rules down to typescript expressions
279
+ statements.push((writer) => {
280
+ const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
281
+ context: sdk_1.ExpressionContext.AccessPolicy,
282
+ fieldReferenceContext: 'input',
283
+ });
284
+ const denyStmt = denies.length > 0
285
+ ? '!(' +
286
+ denies
287
+ .map((deny) => {
288
+ return transformer.transform(deny);
289
+ })
290
+ .join(' || ') +
291
+ ')'
292
+ : undefined;
293
+ const allowStmt = allows.length > 0
294
+ ? '(' +
295
+ allows
296
+ .map((allow) => {
297
+ return transformer.transform(allow);
298
+ })
299
+ .join(' || ') +
300
+ ')'
301
+ : undefined;
302
+ let expr;
303
+ if (denyStmt && allowStmt) {
304
+ expr = `${denyStmt} && ${allowStmt}`;
305
+ }
306
+ else if (denyStmt) {
307
+ expr = denyStmt;
308
+ }
309
+ else if (allowStmt) {
310
+ expr = allowStmt;
311
+ }
312
+ else {
313
+ throw new Error('should not happen');
314
+ }
315
+ writer.write('return ' + expr);
316
+ });
317
+ const func = sourceFile.addFunction({
318
+ name: `${field.$container.name}$${field.name}_read`,
319
+ returnType: 'boolean',
320
+ parameters: [
321
+ {
322
+ name: 'input',
323
+ type: 'any',
324
+ },
325
+ {
326
+ name: 'context',
327
+ type: 'QueryContext',
328
+ },
329
+ ],
330
+ statements,
331
+ });
332
+ return func;
333
+ }
334
+ generateUpdateFieldsGuards(model, sourceFile, result) {
335
+ for (const field of model.fields) {
336
+ const allows = this.getPolicyExpressions(field, 'allow', 'update');
337
+ const denies = this.getPolicyExpressions(field, 'deny', 'update');
338
+ if (denies.length === 0 && allows.length === 0) {
339
+ continue;
340
+ }
341
+ const guardFunc = this.generateQueryGuardFunction(sourceFile, model, 'update', allows, denies, field);
342
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
343
+ result[`${runtime_1.FIELD_LEVEL_UPDATE_GUARD_PREFIX}${field.name}`] = guardFunc.getName();
344
+ }
345
+ }
346
+ canCheckCreateBasedOnInput(model, allows, denies) {
347
+ return [...allows, ...denies].every((rule) => {
348
+ return (0, langium_1.streamAst)(rule).every((expr) => {
349
+ var _a;
350
+ if ((0, ast_1.isThisExpr)(expr)) {
351
+ return false;
352
+ }
353
+ if ((0, ast_1.isReferenceExpr)(expr)) {
354
+ if ((0, ast_1.isDataModel)((_a = expr.$resolvedType) === null || _a === void 0 ? void 0 : _a.decl)) {
355
+ // if policy rules uses relation fields,
356
+ // we can't check based on create input
357
+ return false;
358
+ }
359
+ if ((0, ast_1.isDataModelField)(expr.target.ref) &&
360
+ expr.target.ref.$container === model &&
361
+ (0, sdk_1.hasAttribute)(expr.target.ref, '@default')) {
362
+ // reference to field of current model
363
+ // if it has default value, we can't check
364
+ // based on create input
365
+ return false;
366
+ }
367
+ if ((0, ast_1.isDataModelField)(expr.target.ref) && (0, sdk_1.isForeignKeyField)(expr.target.ref)) {
368
+ // reference to foreign key field
369
+ // we can't check based on create input
370
+ return false;
371
+ }
372
+ }
373
+ return true;
374
+ });
375
+ });
376
+ }
377
+ // generates a "select" object that contains (recursively) fields referenced by the
378
+ // given policy rules
379
+ generateSelectForRules(allows, denies) {
380
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
381
+ const result = {};
382
+ const addPath = (path) => {
383
+ let curr = result;
384
+ path.forEach((seg, i) => {
385
+ if (i === path.length - 1) {
386
+ curr[seg] = true;
387
+ }
388
+ else {
389
+ if (!curr[seg]) {
390
+ curr[seg] = { select: {} };
391
+ }
392
+ curr = curr[seg].select;
393
+ }
394
+ });
395
+ };
396
+ // visit a reference or member access expression to build a
397
+ // selection path
398
+ const visit = (node) => {
399
+ if ((0, ast_1.isReferenceExpr)(node)) {
400
+ const target = (0, sdk_1.resolved)(node.target);
401
+ if ((0, ast_1.isDataModelField)(target)) {
402
+ // a field selection, it's a terminal
403
+ return [target.name];
404
+ }
405
+ }
406
+ else if ((0, ast_1.isMemberAccessExpr)(node)) {
407
+ if ((0, sdk_1.isFutureExpr)(node.operand)) {
408
+ // future().field is not subject to pre-update select
409
+ return undefined;
410
+ }
411
+ // build a selection path inside-out for chained member access
412
+ const inner = visit(node.operand);
413
+ if (inner) {
414
+ return [...inner, node.member.$refText];
415
+ }
416
+ }
417
+ return undefined;
418
+ };
419
+ // collect selection paths from the given expression
420
+ const collectReferencePaths = (expr) => {
421
+ var _a, _b, _c;
422
+ if ((0, ast_1.isThisExpr)(expr) && !(0, ast_1.isMemberAccessExpr)(expr.$container)) {
423
+ // a standalone `this` expression, include all id fields
424
+ const model = (_a = expr.$resolvedType) === null || _a === void 0 ? void 0 : _a.decl;
425
+ const idFields = (0, ast_utils_1.getIdFields)(model);
426
+ return idFields.map((field) => [field.name]);
427
+ }
428
+ if ((0, ast_1.isMemberAccessExpr)(expr) || (0, ast_1.isReferenceExpr)(expr)) {
429
+ const path = visit(expr);
430
+ if (path) {
431
+ if ((0, ast_1.isDataModel)((_b = expr.$resolvedType) === null || _b === void 0 ? void 0 : _b.decl)) {
432
+ // member selection ended at a data model field, include its id fields
433
+ const idFields = (0, ast_utils_1.getIdFields)((_c = expr.$resolvedType) === null || _c === void 0 ? void 0 : _c.decl);
434
+ return idFields.map((field) => [...path, field.name]);
435
+ }
436
+ else {
437
+ return [path];
438
+ }
439
+ }
440
+ else {
441
+ return [];
442
+ }
443
+ }
444
+ else if ((0, ast_utils_1.isCollectionPredicate)(expr)) {
445
+ const path = visit(expr.left);
446
+ if (path) {
447
+ // recurse into RHS
448
+ const rhs = collectReferencePaths(expr.right);
449
+ // combine path of LHS and RHS
450
+ return rhs.map((r) => [...path, ...r]);
451
+ }
452
+ else {
453
+ return [];
454
+ }
455
+ }
456
+ else {
457
+ // recurse
458
+ const children = (0, langium_1.streamContents)(expr)
459
+ .filter((child) => (0, ast_1.isExpression)(child))
460
+ .toArray();
461
+ return children.flatMap((child) => collectReferencePaths(child));
462
+ }
463
+ };
464
+ for (const rule of [...allows, ...denies]) {
465
+ const paths = collectReferencePaths(rule);
466
+ paths.forEach((p) => addPath(p));
467
+ }
468
+ return Object.keys(result).length === 0 ? undefined : result;
469
+ }
470
+ generateQueryGuardFunction(sourceFile, model, kind, allows, denies, forField) {
471
+ const statements = [];
472
+ this.generateNormalizedAuthRef(model, allows, denies, statements);
473
+ const hasFieldAccess = [...denies, ...allows].some((rule) => (0, langium_1.streamAst)(rule).some((child) =>
474
+ // this.???
475
+ (0, ast_1.isThisExpr)(child) ||
476
+ // future().???
477
+ (0, sdk_1.isFutureExpr)(child) ||
478
+ // field reference
479
+ ((0, ast_1.isReferenceExpr)(child) && (0, ast_1.isDataModelField)(child.target.ref))));
480
+ if (!hasFieldAccess) {
481
+ // none of the rules reference model fields, we can compile down to a plain boolean
482
+ // function in this case (so we can skip doing SQL queries when validating)
483
+ statements.push((writer) => {
484
+ const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
485
+ context: sdk_1.ExpressionContext.AccessPolicy,
486
+ isPostGuard: kind === 'postUpdate',
487
+ });
488
+ try {
489
+ denies.forEach((rule) => {
490
+ writer.write(`if (${transformer.transform(rule, false)}) { return ${expression_writer_1.FALSE}; }`);
491
+ });
492
+ allows.forEach((rule) => {
493
+ writer.write(`if (${transformer.transform(rule, false)}) { return ${expression_writer_1.TRUE}; }`);
494
+ });
495
+ }
496
+ catch (err) {
497
+ if (err instanceof typescript_expression_transformer_1.TypeScriptExpressionTransformerError) {
498
+ throw new sdk_1.PluginError(_1.name, err.message);
499
+ }
500
+ else {
501
+ throw err;
502
+ }
503
+ }
504
+ writer.write(`return ${expression_writer_1.FALSE};`);
505
+ });
506
+ }
507
+ else {
508
+ statements.push((writer) => {
509
+ writer.write('return ');
510
+ const exprWriter = new expression_writer_1.ExpressionWriter(writer, kind === 'postUpdate');
511
+ const writeDenies = () => {
512
+ writer.conditionalWrite(denies.length > 1, '{ AND: [');
513
+ denies.forEach((expr, i) => {
514
+ writer.inlineBlock(() => {
515
+ writer.write('NOT: ');
516
+ exprWriter.write(expr);
517
+ });
518
+ writer.conditionalWrite(i !== denies.length - 1, ',');
519
+ });
520
+ writer.conditionalWrite(denies.length > 1, ']}');
521
+ };
522
+ const writeAllows = () => {
523
+ writer.conditionalWrite(allows.length > 1, '{ OR: [');
524
+ allows.forEach((expr, i) => {
525
+ exprWriter.write(expr);
526
+ writer.conditionalWrite(i !== allows.length - 1, ',');
527
+ });
528
+ writer.conditionalWrite(allows.length > 1, ']}');
529
+ };
530
+ if (allows.length > 0 && denies.length > 0) {
531
+ writer.write('{ AND: [');
532
+ writeDenies();
533
+ writer.write(',');
534
+ writeAllows();
535
+ writer.write(']}');
536
+ }
537
+ else if (denies.length > 0) {
538
+ writeDenies();
539
+ }
540
+ else if (allows.length > 0) {
541
+ writeAllows();
542
+ }
543
+ else {
544
+ // disallow any operation
545
+ writer.write(`{ OR: [] }`);
546
+ }
547
+ writer.write(';');
548
+ });
549
+ }
550
+ const func = sourceFile.addFunction({
551
+ name: `${model.name}${forField ? '$' + forField.name : ''}_${kind}`,
552
+ returnType: 'any',
553
+ parameters: [
554
+ {
555
+ name: 'context',
556
+ type: 'QueryContext',
557
+ },
558
+ {
559
+ // for generating field references used by field comparison in the same model
560
+ name: 'db',
561
+ type: 'Record<string, DbOperations>',
562
+ },
563
+ ],
564
+ statements,
565
+ });
566
+ return func;
567
+ }
568
+ generateInputCheckFunction(sourceFile, model, kind, allows, denies) {
569
+ const statements = [];
570
+ this.generateNormalizedAuthRef(model, allows, denies, statements);
571
+ statements.push((writer) => {
572
+ if (allows.length === 0) {
573
+ writer.write('return false;');
574
+ return;
575
+ }
576
+ const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
577
+ context: sdk_1.ExpressionContext.AccessPolicy,
578
+ fieldReferenceContext: 'input',
579
+ });
580
+ let expr = denies.length > 0
581
+ ? '!(' +
582
+ denies
583
+ .map((deny) => {
584
+ return transformer.transform(deny);
585
+ })
586
+ .join(' || ') +
587
+ ')'
588
+ : undefined;
589
+ const allowStmt = allows
590
+ .map((allow) => {
591
+ return transformer.transform(allow);
592
+ })
593
+ .join(' || ');
594
+ expr = expr ? `${expr} && (${allowStmt})` : allowStmt;
595
+ writer.write('return ' + expr);
596
+ });
597
+ const func = sourceFile.addFunction({
598
+ name: model.name + '_' + kind + '_input',
599
+ returnType: 'boolean',
600
+ parameters: [
601
+ {
602
+ name: 'input',
603
+ type: 'any',
604
+ },
605
+ {
606
+ name: 'context',
607
+ type: 'QueryContext',
608
+ },
609
+ ],
610
+ statements,
611
+ });
612
+ return func;
613
+ }
614
+ generateNormalizedAuthRef(model, allows, denies, statements) {
615
+ // check if any allow or deny rule contains 'auth()' invocation
616
+ const hasAuthRef = [...allows, ...denies].some((rule) => (0, langium_1.streamAst)(rule).some((child) => (0, ast_utils_1.isAuthInvocation)(child)));
617
+ if (hasAuthRef) {
618
+ const userModel = model.$container.declarations.find((decl) => (0, ast_1.isDataModel)(decl) && decl.name === 'User');
619
+ if (!userModel) {
620
+ throw new sdk_1.PluginError(_1.name, 'User model not found');
621
+ }
622
+ const userIdFields = (0, ast_utils_1.getIdFields)(userModel);
623
+ if (!userIdFields || userIdFields.length === 0) {
624
+ throw new sdk_1.PluginError(_1.name, 'User model does not have an id field');
625
+ }
626
+ // normalize user to null to avoid accidentally use undefined in filter
627
+ statements.push(`const user = hasAllFields(context.user, [${userIdFields
628
+ .map((f) => "'" + f.name + "'")
629
+ .join(', ')}]) ? context.user as any : null;`);
630
+ }
631
+ }
632
+ }
633
+ exports.default = PolicyGenerator;
634
+ //# sourceMappingURL=policy-guard-generator.js.map