zenstack 1.0.0-alpha.22 → 1.0.0-alpha.24

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 (85) hide show
  1. package/README.md +79 -9
  2. package/cli/cli-error.js +3 -5
  3. package/cli/cli-error.js.map +1 -1
  4. package/cli/cli-util.js +123 -105
  5. package/cli/cli-util.js.map +1 -1
  6. package/cli/index.js +105 -63
  7. package/cli/index.js.map +1 -1
  8. package/cli/plugin-runner.js +130 -127
  9. package/cli/plugin-runner.js.map +1 -1
  10. package/language-server/constants.js +5 -13
  11. package/language-server/constants.js.map +1 -1
  12. package/language-server/main.js +8 -15
  13. package/language-server/main.js.map +1 -1
  14. package/language-server/types.js +3 -1
  15. package/language-server/types.js.map +1 -1
  16. package/language-server/utils.js +13 -16
  17. package/language-server/utils.js.map +1 -1
  18. package/language-server/validator/attribute-validator.js +3 -7
  19. package/language-server/validator/attribute-validator.js.map +1 -1
  20. package/language-server/validator/datamodel-validator.js +293 -347
  21. package/language-server/validator/datamodel-validator.js.map +1 -1
  22. package/language-server/validator/datasource-validator.js +61 -71
  23. package/language-server/validator/datasource-validator.js.map +1 -1
  24. package/language-server/validator/enum-validator.js +6 -10
  25. package/language-server/validator/enum-validator.js.map +1 -1
  26. package/language-server/validator/expression-validator.js +25 -31
  27. package/language-server/validator/expression-validator.js.map +1 -1
  28. package/language-server/validator/schema-validator.js +18 -25
  29. package/language-server/validator/schema-validator.js.map +1 -1
  30. package/language-server/validator/utils.js +86 -85
  31. package/language-server/validator/utils.js.map +1 -1
  32. package/language-server/validator/zmodel-validator.js +55 -58
  33. package/language-server/validator/zmodel-validator.js.map +1 -1
  34. package/language-server/zmodel-formatter.js +40 -21
  35. package/language-server/zmodel-formatter.js.map +1 -1
  36. package/language-server/zmodel-linker.js +328 -331
  37. package/language-server/zmodel-linker.js.map +1 -1
  38. package/language-server/zmodel-module.js +50 -59
  39. package/language-server/zmodel-module.js.map +1 -1
  40. package/language-server/zmodel-scope.js +35 -25
  41. package/language-server/zmodel-scope.js.map +1 -1
  42. package/language-server/zmodel-workspace-manager.js +30 -18
  43. package/language-server/zmodel-workspace-manager.js.map +1 -1
  44. package/package.json +8 -12
  45. package/plugins/access-policy/expression-writer.js +301 -292
  46. package/plugins/access-policy/expression-writer.js.map +1 -1
  47. package/plugins/access-policy/index.js +20 -11
  48. package/plugins/access-policy/index.js.map +1 -1
  49. package/plugins/access-policy/policy-guard-generator.js +327 -321
  50. package/plugins/access-policy/policy-guard-generator.js.map +1 -1
  51. package/plugins/access-policy/typescript-expression-transformer.js +94 -95
  52. package/plugins/access-policy/typescript-expression-transformer.js.map +1 -1
  53. package/plugins/access-policy/utils.js +7 -9
  54. package/plugins/access-policy/utils.js.map +1 -1
  55. package/plugins/access-policy/zod-schema-generator.js +143 -159
  56. package/plugins/access-policy/zod-schema-generator.js.map +1 -1
  57. package/plugins/model-meta/index.js +97 -102
  58. package/plugins/model-meta/index.js.map +1 -1
  59. package/plugins/plugin-utils.js +34 -40
  60. package/plugins/plugin-utils.js.map +1 -1
  61. package/plugins/prisma/indent-string.js +4 -8
  62. package/plugins/prisma/indent-string.js.map +1 -1
  63. package/plugins/prisma/index.js +20 -11
  64. package/plugins/prisma/index.js.map +1 -1
  65. package/plugins/prisma/prisma-builder.js +235 -213
  66. package/plugins/prisma/prisma-builder.js.map +1 -1
  67. package/plugins/prisma/schema-generator.js +205 -186
  68. package/plugins/prisma/schema-generator.js.map +1 -1
  69. package/plugins/prisma/zmodel-code-generator.d.ts +2 -1
  70. package/plugins/prisma/zmodel-code-generator.js +109 -105
  71. package/plugins/prisma/zmodel-code-generator.js.map +1 -1
  72. package/telemetry.js +107 -90
  73. package/telemetry.js.map +1 -1
  74. package/types.js +3 -1
  75. package/types.js.map +1 -1
  76. package/utils/ast-utils.js +67 -67
  77. package/utils/ast-utils.js.map +1 -1
  78. package/utils/exec-utils.js +6 -15
  79. package/utils/exec-utils.js.map +1 -1
  80. package/utils/pkg-utils.js +38 -35
  81. package/utils/pkg-utils.js.map +1 -1
  82. package/utils/version-utils.js +9 -10
  83. package/utils/version-utils.js.map +1 -1
  84. package/global.d.js +0 -1
  85. package/global.d.js.map +0 -1
@@ -1,375 +1,321 @@
1
1
  "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _ast = require("@zenstackhq/language/ast");
8
- var _pluralize = _interopRequireDefault(require("pluralize"));
9
- var _astUtils = require("../../utils/ast-utils");
10
- var _constants = require("../constants");
11
- var _utils = require("./utils");
12
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ast_1 = require("@zenstackhq/language/ast");
7
+ const pluralize_1 = __importDefault(require("pluralize"));
8
+ const ast_utils_1 = require("../../utils/ast-utils");
9
+ const constants_1 = require("../constants");
10
+ const utils_1 = require("./utils");
13
11
  /**
14
12
  * Validates data model declarations.
15
13
  */
16
14
  class DataModelValidator {
17
- validate(dm, accept) {
18
- (0, _utils.validateDuplicatedDeclarations)(dm.fields, accept);
19
- this.validateFields(dm, accept);
20
- this.validateAttributes(dm, accept);
21
- }
22
- validateFields(dm, accept) {
23
- const idFields = dm.fields.filter(f => f.attributes.find(attr => {
24
- var _attr$decl$ref;
25
- return ((_attr$decl$ref = attr.decl.ref) === null || _attr$decl$ref === void 0 ? void 0 : _attr$decl$ref.name) === '@id';
26
- }));
27
- if (idFields.length === 0) {
28
- const {
29
- allows,
30
- denies,
31
- hasFieldValidation
32
- } = (0, _astUtils.analyzePolicies)(dm);
33
- if (allows.length > 0 || denies.length > 0 || hasFieldValidation) {
34
- // TODO: relax this requirement to require only @unique fields
35
- // when access policies or field valdaition is used, require an @id field
36
- accept('error', 'Model must include a field with @id attribute', {
37
- node: dm
38
- });
39
- }
40
- } else if (idFields.length > 1) {
41
- accept('error', 'Model can include at most one field with @id attribute', {
42
- node: dm
43
- });
44
- } else {
45
- if (idFields[0].type.optional) {
46
- accept('error', 'Field with @id attribute must not be optional', {
47
- node: idFields[0]
48
- });
49
- }
50
- if (idFields[0].type.array || !idFields[0].type.type || !_constants.SCALAR_TYPES.includes(idFields[0].type.type)) {
51
- accept('error', 'Field with @id attribute must be of scalar type', {
52
- node: idFields[0]
53
- });
54
- }
55
- }
56
- dm.fields.forEach(field => this.validateField(field, accept));
57
- }
58
- validateField(field, accept) {
59
- var _field$type$reference;
60
- if (field.type.array && field.type.optional) {
61
- accept('error', 'Optional lists are not supported. Use either `Type[]` or `Type?`', {
62
- node: field.type
63
- });
64
- }
65
- field.attributes.forEach(attr => this.validateAttributeApplication(attr, accept));
66
- if ((0, _ast.isDataModel)((_field$type$reference = field.type.reference) === null || _field$type$reference === void 0 ? void 0 : _field$type$reference.ref)) {
67
- this.validateRelationField(field, accept);
68
- }
69
- }
70
- validateAttributes(dm, accept) {
71
- dm.attributes.forEach(attr => {
72
- this.validateAttributeApplication(attr, accept);
73
- });
74
- }
75
- validateAttributeApplication(attr, accept) {
76
- const decl = attr.decl.ref;
77
- if (!decl) {
78
- return;
79
- }
80
- const targetDecl = attr.$container;
81
- if (decl.name === '@@@targetField' && !(0, _ast.isAttribute)(targetDecl)) {
82
- accept('error', `attribute "${decl.name}" can only be used on attribute declarations`, {
83
- node: attr
84
- });
85
- return;
86
- }
87
- if ((0, _ast.isDataModelField)(targetDecl) && !this.isValidAttributeTarget(decl, targetDecl)) {
88
- accept('error', `attribute "${decl.name}" cannot be used on this type of field`, {
89
- node: attr
90
- });
15
+ validate(dm, accept) {
16
+ (0, utils_1.validateDuplicatedDeclarations)(dm.fields, accept);
17
+ this.validateFields(dm, accept);
18
+ this.validateAttributes(dm, accept);
91
19
  }
92
- const filledParams = new Set();
93
- for (const arg of attr.args) {
94
- let paramDecl;
95
- if (!arg.name) {
96
- paramDecl = decl.params.find(p => p.default && !filledParams.has(p));
97
- if (!paramDecl) {
98
- accept('error', `Unexpected unnamed argument`, {
99
- node: arg
100
- });
101
- return false;
20
+ validateFields(dm, accept) {
21
+ const idFields = dm.fields.filter((f) => f.attributes.find((attr) => { var _a; return ((_a = attr.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === '@id'; }));
22
+ if (idFields.length === 0) {
23
+ const { allows, denies, hasFieldValidation } = (0, ast_utils_1.analyzePolicies)(dm);
24
+ if (allows.length > 0 || denies.length > 0 || hasFieldValidation) {
25
+ // TODO: relax this requirement to require only @unique fields
26
+ // when access policies or field valdaition is used, require an @id field
27
+ accept('error', 'Model must include a field with @id attribute', {
28
+ node: dm,
29
+ });
30
+ }
102
31
  }
103
- } else {
104
- paramDecl = decl.params.find(p => p.name === arg.name);
105
- if (!paramDecl) {
106
- accept('error', `Attribute "${decl.name}" doesn't have a parameter named "${arg.name}"`, {
107
- node: arg
108
- });
109
- return false;
32
+ else if (idFields.length > 1) {
33
+ accept('error', 'Model can include at most one field with @id attribute', {
34
+ node: dm,
35
+ });
110
36
  }
111
- }
112
- if (!(0, _utils.assignableToAttributeParam)(arg, paramDecl, attr)) {
113
- accept('error', `Value is not assignable to parameter`, {
114
- node: arg
115
- });
116
- return false;
117
- }
118
- if (filledParams.has(paramDecl)) {
119
- accept('error', `Parameter "${paramDecl.name}" is already provided`, {
120
- node: arg
121
- });
122
- return false;
123
- }
124
- filledParams.add(paramDecl);
125
- arg.$resolvedParam = paramDecl;
126
- }
127
- const missingParams = decl.params.filter(p => !p.type.optional && !filledParams.has(p));
128
- if (missingParams.length > 0) {
129
- accept('error', `Required ${(0, _pluralize.default)('parameter', missingParams.length)} not provided: ${missingParams.map(p => p.name).join(', ')}`, {
130
- node: attr
131
- });
132
- return false;
133
- }
134
- return true;
135
- }
136
- isValidAttributeTarget(attrDecl, targetDecl) {
137
- var _targetDecl$type$refe;
138
- const targetField = attrDecl.attributes.find(attr => {
139
- var _attr$decl$ref2;
140
- return ((_attr$decl$ref2 = attr.decl.ref) === null || _attr$decl$ref2 === void 0 ? void 0 : _attr$decl$ref2.name) === '@@@targetField';
141
- });
142
- if (!targetField) {
143
- // no field type constraint
144
- return true;
37
+ else {
38
+ if (idFields[0].type.optional) {
39
+ accept('error', 'Field with @id attribute must not be optional', { node: idFields[0] });
40
+ }
41
+ if (idFields[0].type.array || !idFields[0].type.type || !constants_1.SCALAR_TYPES.includes(idFields[0].type.type)) {
42
+ accept('error', 'Field with @id attribute must be of scalar type', { node: idFields[0] });
43
+ }
44
+ }
45
+ dm.fields.forEach((field) => this.validateField(field, accept));
145
46
  }
146
- const fieldTypes = targetField.args[0].value.items.map(item => {
147
- var _target$ref;
148
- return (_target$ref = item.target.ref) === null || _target$ref === void 0 ? void 0 : _target$ref.name;
149
- });
150
- let allowed = false;
151
- for (const allowedType of fieldTypes) {
152
- switch (allowedType) {
153
- case 'StringField':
154
- allowed = allowed || targetDecl.type.type === 'String';
155
- break;
156
- case 'IntField':
157
- allowed = allowed || targetDecl.type.type === 'Int';
158
- break;
159
- case 'FloatField':
160
- allowed = allowed || targetDecl.type.type === 'Float';
161
- break;
162
- case 'DecimalField':
163
- allowed = allowed || targetDecl.type.type === 'Decimal';
164
- break;
165
- case 'BooleanField':
166
- allowed = allowed || targetDecl.type.type === 'Boolean';
167
- break;
168
- case 'DateTimeField':
169
- allowed = allowed || targetDecl.type.type === 'DateTime';
170
- break;
171
- case 'JsonField':
172
- allowed = allowed || targetDecl.type.type === 'Json';
173
- break;
174
- case 'BytesField':
175
- allowed = allowed || targetDecl.type.type === 'Bytes';
176
- break;
177
- case 'ModelField':
178
- allowed = allowed || (0, _ast.isDataModel)((_targetDecl$type$refe = targetDecl.type.reference) === null || _targetDecl$type$refe === void 0 ? void 0 : _targetDecl$type$refe.ref);
179
- break;
180
- default:
181
- break;
182
- }
183
- if (allowed) {
184
- break;
185
- }
47
+ validateField(field, accept) {
48
+ var _a;
49
+ if (field.type.array && field.type.optional) {
50
+ accept('error', 'Optional lists are not supported. Use either `Type[]` or `Type?`', { node: field.type });
51
+ }
52
+ field.attributes.forEach((attr) => this.validateAttributeApplication(attr, accept));
53
+ if ((0, ast_1.isDataModel)((_a = field.type.reference) === null || _a === void 0 ? void 0 : _a.ref)) {
54
+ this.validateRelationField(field, accept);
55
+ }
186
56
  }
187
- return allowed;
188
- }
189
- parseRelation(field, accept) {
190
- const relAttr = field.attributes.find(attr => {
191
- var _attr$decl$ref3;
192
- return ((_attr$decl$ref3 = attr.decl.ref) === null || _attr$decl$ref3 === void 0 ? void 0 : _attr$decl$ref3.name) === '@relation';
193
- });
194
- let name;
195
- let fields;
196
- let references;
197
- let valid = true;
198
- if (!relAttr) {
199
- return {
200
- attr: relAttr,
201
- name,
202
- fields,
203
- references,
204
- valid: true
205
- };
57
+ validateAttributes(dm, accept) {
58
+ dm.attributes.forEach((attr) => {
59
+ this.validateAttributeApplication(attr, accept);
60
+ });
206
61
  }
207
- for (const arg of relAttr.args) {
208
- if (!arg.name || arg.name === 'name') {
209
- if ((0, _ast.isLiteralExpr)(arg.value)) {
210
- name = arg.value.value;
62
+ validateAttributeApplication(attr, accept) {
63
+ const decl = attr.decl.ref;
64
+ if (!decl) {
65
+ return;
211
66
  }
212
- } else if (arg.name === 'fields') {
213
- fields = arg.value.items;
214
- if (fields.length === 0) {
215
- if (accept) {
216
- accept('error', `"fields" value cannot be emtpy`, {
217
- node: arg
218
- });
219
- }
220
- valid = false;
67
+ const targetDecl = attr.$container;
68
+ if (decl.name === '@@@targetField' && !(0, ast_1.isAttribute)(targetDecl)) {
69
+ accept('error', `attribute "${decl.name}" can only be used on attribute declarations`, { node: attr });
70
+ return;
221
71
  }
222
- } else if (arg.name === 'references') {
223
- references = arg.value.items;
224
- if (references.length === 0) {
225
- if (accept) {
226
- accept('error', `"references" value cannot be emtpy`, {
227
- node: arg
228
- });
229
- }
230
- valid = false;
72
+ if ((0, ast_1.isDataModelField)(targetDecl) && !this.isValidAttributeTarget(decl, targetDecl)) {
73
+ accept('error', `attribute "${decl.name}" cannot be used on this type of field`, { node: attr });
231
74
  }
232
- }
75
+ const filledParams = new Set();
76
+ for (const arg of attr.args) {
77
+ let paramDecl;
78
+ if (!arg.name) {
79
+ paramDecl = decl.params.find((p) => p.default && !filledParams.has(p));
80
+ if (!paramDecl) {
81
+ accept('error', `Unexpected unnamed argument`, {
82
+ node: arg,
83
+ });
84
+ return false;
85
+ }
86
+ }
87
+ else {
88
+ paramDecl = decl.params.find((p) => p.name === arg.name);
89
+ if (!paramDecl) {
90
+ accept('error', `Attribute "${decl.name}" doesn't have a parameter named "${arg.name}"`, {
91
+ node: arg,
92
+ });
93
+ return false;
94
+ }
95
+ }
96
+ if (!(0, utils_1.assignableToAttributeParam)(arg, paramDecl, attr)) {
97
+ accept('error', `Value is not assignable to parameter`, {
98
+ node: arg,
99
+ });
100
+ return false;
101
+ }
102
+ if (filledParams.has(paramDecl)) {
103
+ accept('error', `Parameter "${paramDecl.name}" is already provided`, { node: arg });
104
+ return false;
105
+ }
106
+ filledParams.add(paramDecl);
107
+ arg.$resolvedParam = paramDecl;
108
+ }
109
+ const missingParams = decl.params.filter((p) => !p.type.optional && !filledParams.has(p));
110
+ if (missingParams.length > 0) {
111
+ accept('error', `Required ${(0, pluralize_1.default)('parameter', missingParams.length)} not provided: ${missingParams
112
+ .map((p) => p.name)
113
+ .join(', ')}`, { node: attr });
114
+ return false;
115
+ }
116
+ return true;
233
117
  }
234
- if (!fields || !references) {
235
- if (accept) {
236
- accept('error', `Both "fields" and "references" must be provided`, {
237
- node: relAttr
238
- });
239
- }
240
- } else {
241
- // validate "fields" and "references" typing consistency
242
- if (fields.length !== references.length) {
243
- if (accept) {
244
- accept('error', `"references" and "fields" must have the same length`, {
245
- node: relAttr
246
- });
118
+ isValidAttributeTarget(attrDecl, targetDecl) {
119
+ var _a;
120
+ const targetField = attrDecl.attributes.find((attr) => { var _a; return ((_a = attr.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === '@@@targetField'; });
121
+ if (!targetField) {
122
+ // no field type constraint
123
+ return true;
247
124
  }
248
- } else {
249
- for (let i = 0; i < fields.length; i++) {
250
- var _fields$i$$resolvedTy, _references$i$$resolv, _fields$i$$resolvedTy2, _references$i$$resolv2;
251
- if (!fields[i].$resolvedType) {
252
- if (accept) {
253
- accept('error', `field reference is unresolved`, {
254
- node: fields[i]
255
- });
125
+ const fieldTypes = targetField.args[0].value.items.map((item) => { var _a; return (_a = item.target.ref) === null || _a === void 0 ? void 0 : _a.name; });
126
+ let allowed = false;
127
+ for (const allowedType of fieldTypes) {
128
+ switch (allowedType) {
129
+ case 'StringField':
130
+ allowed = allowed || targetDecl.type.type === 'String';
131
+ break;
132
+ case 'IntField':
133
+ allowed = allowed || targetDecl.type.type === 'Int';
134
+ break;
135
+ case 'FloatField':
136
+ allowed = allowed || targetDecl.type.type === 'Float';
137
+ break;
138
+ case 'DecimalField':
139
+ allowed = allowed || targetDecl.type.type === 'Decimal';
140
+ break;
141
+ case 'BooleanField':
142
+ allowed = allowed || targetDecl.type.type === 'Boolean';
143
+ break;
144
+ case 'DateTimeField':
145
+ allowed = allowed || targetDecl.type.type === 'DateTime';
146
+ break;
147
+ case 'JsonField':
148
+ allowed = allowed || targetDecl.type.type === 'Json';
149
+ break;
150
+ case 'BytesField':
151
+ allowed = allowed || targetDecl.type.type === 'Bytes';
152
+ break;
153
+ case 'ModelField':
154
+ allowed = allowed || (0, ast_1.isDataModel)((_a = targetDecl.type.reference) === null || _a === void 0 ? void 0 : _a.ref);
155
+ break;
156
+ default:
157
+ break;
256
158
  }
257
- }
258
- if (!references[i].$resolvedType) {
259
- if (accept) {
260
- accept('error', `field reference is unresolved`, {
261
- node: references[i]
262
- });
159
+ if (allowed) {
160
+ break;
161
+ }
162
+ }
163
+ return allowed;
164
+ }
165
+ parseRelation(field, accept) {
166
+ var _a, _b, _c, _d;
167
+ const relAttr = field.attributes.find((attr) => { var _a; return ((_a = attr.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === '@relation'; });
168
+ let name;
169
+ let fields;
170
+ let references;
171
+ let valid = true;
172
+ if (!relAttr) {
173
+ return { attr: relAttr, name, fields, references, valid: true };
174
+ }
175
+ for (const arg of relAttr.args) {
176
+ if (!arg.name || arg.name === 'name') {
177
+ if ((0, ast_1.isLiteralExpr)(arg.value)) {
178
+ name = arg.value.value;
179
+ }
180
+ }
181
+ else if (arg.name === 'fields') {
182
+ fields = arg.value.items;
183
+ if (fields.length === 0) {
184
+ if (accept) {
185
+ accept('error', `"fields" value cannot be emtpy`, {
186
+ node: arg,
187
+ });
188
+ }
189
+ valid = false;
190
+ }
263
191
  }
264
- }
265
- if (((_fields$i$$resolvedTy = fields[i].$resolvedType) === null || _fields$i$$resolvedTy === void 0 ? void 0 : _fields$i$$resolvedTy.decl) !== ((_references$i$$resolv = references[i].$resolvedType) === null || _references$i$$resolv === void 0 ? void 0 : _references$i$$resolv.decl) || ((_fields$i$$resolvedTy2 = fields[i].$resolvedType) === null || _fields$i$$resolvedTy2 === void 0 ? void 0 : _fields$i$$resolvedTy2.array) !== ((_references$i$$resolv2 = references[i].$resolvedType) === null || _references$i$$resolv2 === void 0 ? void 0 : _references$i$$resolv2.array)) {
192
+ else if (arg.name === 'references') {
193
+ references = arg.value.items;
194
+ if (references.length === 0) {
195
+ if (accept) {
196
+ accept('error', `"references" value cannot be emtpy`, {
197
+ node: arg,
198
+ });
199
+ }
200
+ valid = false;
201
+ }
202
+ }
203
+ }
204
+ if (!fields || !references) {
266
205
  if (accept) {
267
- accept('error', `values of "references" and "fields" must have the same type`, {
268
- node: relAttr
269
- });
206
+ accept('error', `Both "fields" and "references" must be provided`, { node: relAttr });
270
207
  }
271
- }
272
208
  }
273
- }
274
- }
275
- return {
276
- attr: relAttr,
277
- name,
278
- fields,
279
- references,
280
- valid
281
- };
282
- }
283
- validateRelationField(field, accept) {
284
- var _thisRelation$referen, _thisRelation$fields, _oppositeRelation$ref, _oppositeRelation$fie;
285
- const thisRelation = this.parseRelation(field, accept);
286
- if (!thisRelation.valid) {
287
- return;
288
- }
289
-
290
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
291
- const oppositeModel = field.type.reference.ref;
292
- let oppositeFields = oppositeModel.fields.filter(f => {
293
- var _f$type$reference;
294
- return ((_f$type$reference = f.type.reference) === null || _f$type$reference === void 0 ? void 0 : _f$type$reference.ref) === field.$container;
295
- });
296
- oppositeFields = oppositeFields.filter(f => {
297
- const fieldRel = this.parseRelation(f);
298
- return fieldRel.valid && fieldRel.name === thisRelation.name;
299
- });
300
- if (oppositeFields.length === 0) {
301
- accept('error', `The relation field "${field.name}" on model "${field.$container.name}" is missing an opposite relation field on model "${oppositeModel.name}"`, {
302
- node: field
303
- });
304
- return;
305
- } else if (oppositeFields.length > 1) {
306
- oppositeFields.forEach(f => accept('error', `Fields ${oppositeFields.map(f => '"' + f.name + '"').join(', ')} on model "${oppositeModel.name}" refer to the same relation to model "${field.$container.name}"`, {
307
- node: f
308
- }));
309
- return;
209
+ else {
210
+ // validate "fields" and "references" typing consistency
211
+ if (fields.length !== references.length) {
212
+ if (accept) {
213
+ accept('error', `"references" and "fields" must have the same length`, { node: relAttr });
214
+ }
215
+ }
216
+ else {
217
+ for (let i = 0; i < fields.length; i++) {
218
+ if (!fields[i].$resolvedType) {
219
+ if (accept) {
220
+ accept('error', `field reference is unresolved`, { node: fields[i] });
221
+ }
222
+ }
223
+ if (!references[i].$resolvedType) {
224
+ if (accept) {
225
+ accept('error', `field reference is unresolved`, { node: references[i] });
226
+ }
227
+ }
228
+ if (((_a = fields[i].$resolvedType) === null || _a === void 0 ? void 0 : _a.decl) !== ((_b = references[i].$resolvedType) === null || _b === void 0 ? void 0 : _b.decl) ||
229
+ ((_c = fields[i].$resolvedType) === null || _c === void 0 ? void 0 : _c.array) !== ((_d = references[i].$resolvedType) === null || _d === void 0 ? void 0 : _d.array)) {
230
+ if (accept) {
231
+ accept('error', `values of "references" and "fields" must have the same type`, {
232
+ node: relAttr,
233
+ });
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ return { attr: relAttr, name, fields, references, valid };
310
240
  }
311
- const oppositeField = oppositeFields[0];
312
- const oppositeRelation = this.parseRelation(oppositeField);
313
- let relationOwner;
314
- if (thisRelation !== null && thisRelation !== void 0 && (_thisRelation$referen = thisRelation.references) !== null && _thisRelation$referen !== void 0 && _thisRelation$referen.length && (_thisRelation$fields = thisRelation.fields) !== null && _thisRelation$fields !== void 0 && _thisRelation$fields.length) {
315
- if (oppositeRelation !== null && oppositeRelation !== void 0 && oppositeRelation.references || oppositeRelation !== null && oppositeRelation !== void 0 && oppositeRelation.fields) {
316
- accept('error', '"fields" and "references" must be provided only on one side of relation field', {
317
- node: oppositeField
318
- });
319
- return;
320
- } else {
321
- relationOwner = oppositeField;
322
- }
323
- } else if (oppositeRelation !== null && oppositeRelation !== void 0 && (_oppositeRelation$ref = oppositeRelation.references) !== null && _oppositeRelation$ref !== void 0 && _oppositeRelation$ref.length && (_oppositeRelation$fie = oppositeRelation.fields) !== null && _oppositeRelation$fie !== void 0 && _oppositeRelation$fie.length) {
324
- if (thisRelation !== null && thisRelation !== void 0 && thisRelation.references || thisRelation !== null && thisRelation !== void 0 && thisRelation.fields) {
325
- accept('error', '"fields" and "references" must be provided only on one side of relation field', {
326
- node: field
241
+ validateRelationField(field, accept) {
242
+ var _a, _b, _c, _d, _e;
243
+ const thisRelation = this.parseRelation(field, accept);
244
+ if (!thisRelation.valid) {
245
+ return;
246
+ }
247
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
248
+ const oppositeModel = field.type.reference.ref;
249
+ let oppositeFields = oppositeModel.fields.filter((f) => { var _a; return ((_a = f.type.reference) === null || _a === void 0 ? void 0 : _a.ref) === field.$container; });
250
+ oppositeFields = oppositeFields.filter((f) => {
251
+ const fieldRel = this.parseRelation(f);
252
+ return fieldRel.valid && fieldRel.name === thisRelation.name;
327
253
  });
328
- return;
329
- } else {
330
- relationOwner = field;
331
- }
332
- } else {
333
- [field, oppositeField].forEach(f => accept('error', 'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields', {
334
- node: f
335
- }));
336
- return;
337
- }
338
- if (!relationOwner.type.array && !relationOwner.type.optional) {
339
- accept('error', 'Relation field needs to be list or optional', {
340
- node: relationOwner
341
- });
342
- return;
343
- }
344
- if (relationOwner !== field && !relationOwner.type.array) {
345
- var _thisRelation$fields2;
346
- // one-to-one relation requires defining side's reference field to be @unique
347
- // e.g.:
348
- // model User {
349
- // id String @id @default(cuid())
350
- // data UserData?
351
- // }
352
- // model UserData {
353
- // id String @id @default(cuid())
354
- // user User @relation(fields: [userId], references: [id])
355
- // userId String
356
- // }
357
- //
358
- // UserData.userId field needs to be @unique
359
-
360
- (_thisRelation$fields2 = thisRelation.fields) === null || _thisRelation$fields2 === void 0 ? void 0 : _thisRelation$fields2.forEach(ref => {
361
- const refField = ref.target.ref;
362
- if (refField && !refField.attributes.find(a => {
363
- var _a$decl$ref;
364
- return ((_a$decl$ref = a.decl.ref) === null || _a$decl$ref === void 0 ? void 0 : _a$decl$ref.name) === '@unique';
365
- })) {
366
- accept('error', `Field "${refField.name}" is part of a one-to-one relation and must be marked as @unique`, {
367
- node: refField
368
- });
254
+ if (oppositeFields.length === 0) {
255
+ accept('error', `The relation field "${field.name}" on model "${field.$container.name}" is missing an opposite relation field on model "${oppositeModel.name}"`, { node: field });
256
+ return;
257
+ }
258
+ else if (oppositeFields.length > 1) {
259
+ oppositeFields.forEach((f) => accept('error', `Fields ${oppositeFields.map((f) => '"' + f.name + '"').join(', ')} on model "${oppositeModel.name}" refer to the same relation to model "${field.$container.name}"`, { node: f }));
260
+ return;
261
+ }
262
+ const oppositeField = oppositeFields[0];
263
+ const oppositeRelation = this.parseRelation(oppositeField);
264
+ let relationOwner;
265
+ if (((_a = thisRelation === null || thisRelation === void 0 ? void 0 : thisRelation.references) === null || _a === void 0 ? void 0 : _a.length) && ((_b = thisRelation.fields) === null || _b === void 0 ? void 0 : _b.length)) {
266
+ if ((oppositeRelation === null || oppositeRelation === void 0 ? void 0 : oppositeRelation.references) || (oppositeRelation === null || oppositeRelation === void 0 ? void 0 : oppositeRelation.fields)) {
267
+ accept('error', '"fields" and "references" must be provided only on one side of relation field', {
268
+ node: oppositeField,
269
+ });
270
+ return;
271
+ }
272
+ else {
273
+ relationOwner = oppositeField;
274
+ }
275
+ }
276
+ else if (((_c = oppositeRelation === null || oppositeRelation === void 0 ? void 0 : oppositeRelation.references) === null || _c === void 0 ? void 0 : _c.length) && ((_d = oppositeRelation.fields) === null || _d === void 0 ? void 0 : _d.length)) {
277
+ if ((thisRelation === null || thisRelation === void 0 ? void 0 : thisRelation.references) || (thisRelation === null || thisRelation === void 0 ? void 0 : thisRelation.fields)) {
278
+ accept('error', '"fields" and "references" must be provided only on one side of relation field', {
279
+ node: field,
280
+ });
281
+ return;
282
+ }
283
+ else {
284
+ relationOwner = field;
285
+ }
286
+ }
287
+ else {
288
+ [field, oppositeField].forEach((f) => accept('error', 'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields', { node: f }));
289
+ return;
290
+ }
291
+ if (!relationOwner.type.array && !relationOwner.type.optional) {
292
+ accept('error', 'Relation field needs to be list or optional', {
293
+ node: relationOwner,
294
+ });
295
+ return;
296
+ }
297
+ if (relationOwner !== field && !relationOwner.type.array) {
298
+ // one-to-one relation requires defining side's reference field to be @unique
299
+ // e.g.:
300
+ // model User {
301
+ // id String @id @default(cuid())
302
+ // data UserData?
303
+ // }
304
+ // model UserData {
305
+ // id String @id @default(cuid())
306
+ // user User @relation(fields: [userId], references: [id])
307
+ // userId String
308
+ // }
309
+ //
310
+ // UserData.userId field needs to be @unique
311
+ (_e = thisRelation.fields) === null || _e === void 0 ? void 0 : _e.forEach((ref) => {
312
+ const refField = ref.target.ref;
313
+ if (refField && !refField.attributes.find((a) => { var _a; return ((_a = a.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === '@unique'; })) {
314
+ accept('error', `Field "${refField.name}" is part of a one-to-one relation and must be marked as @unique`, { node: refField });
315
+ }
316
+ });
369
317
  }
370
- });
371
318
  }
372
- }
373
319
  }
374
320
  exports.default = DataModelValidator;
375
321
  //# sourceMappingURL=datamodel-validator.js.map