@warp-drive/json-api 5.9.0-alpha.17 → 5.9.0-alpha.18

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/dist/index.js CHANGED
@@ -60,7 +60,7 @@ function validateResourceFields(schema, resource, options) {
60
60
  case 'hasMany':
61
61
  {
62
62
  if (field.options.linksMode) {
63
- validateHasManyToLinksMode(resourceType, field);
63
+ validateHasManyToLinksMode(resourceType, field, relationshipDoc, options);
64
64
  }
65
65
  break;
66
66
  }
@@ -71,31 +71,105 @@ function validateBelongsToLinksMode(resourceType, field, relationshipDoc, option
71
71
  if (field.options.async) {
72
72
  throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async is not yet supported`);
73
73
  }
74
- if (!relationshipDoc.links?.related) {
75
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
74
+ if (!field.options.async) {
75
+ const relationshipData = relationshipDoc.data;
76
+ if (Array.isArray(relationshipData)) {
77
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
78
+ }
79
+
80
+ /**
81
+ * If we are sync, we must have a related link when we have no related data field
82
+ *
83
+ * We explicitly allow `null`! Missing key or `undefined` are always invalid.
84
+ */
85
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
86
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
87
+ }
88
+
89
+ /**
90
+ * Nothing more to verify since we are empty
91
+ */
92
+ if (!relationshipData) {
93
+ return;
94
+ }
95
+
96
+ /**
97
+ * We are explicitly asked to not verify full-linkage
98
+ */
99
+ if (!options.verifyIncluded) {
100
+ return;
101
+ }
102
+
103
+ /**
104
+ * If we have a link, full-linkage verification is not required.
105
+ */
106
+ if (relationshipDoc.links?.related) {
107
+ return;
108
+ }
109
+
110
+ /**
111
+ * If we are sync and have relationship data, we must have full linkage to an included resource
112
+ */
113
+ const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
114
+ if (!includedDoc) {
115
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
116
+ }
117
+ } else {
118
+ /**
119
+ * If we are async, we must have a related link.
120
+ */
121
+ if (!relationshipDoc.links?.related) {
122
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
123
+ }
124
+ }
125
+ }
126
+ function validateHasManyToLinksMode(resourceType, field, relationshipDoc, options) {
127
+ if (field.options.async) {
128
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
76
129
  }
77
130
  const relationshipData = relationshipDoc.data;
78
- if (Array.isArray(relationshipData)) {
79
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
131
+ if (relationshipData !== undefined && !Array.isArray(relationshipData)) {
132
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a hasMany relationship is unexpectedly not an array`);
80
133
  }
81
- // Explicitly allow `null`! Missing key or `undefined` are always invalid.
82
- if (relationshipData === undefined) {
83
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined`);
134
+
135
+ /**
136
+ * If we are sync, we must have a related link when we have no related data field
137
+ *
138
+ * We explicitly allow an empty array! Missing key or `undefined` are always invalid.
139
+ */
140
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
141
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
84
142
  }
85
- if (relationshipData === null) {
143
+
144
+ /**
145
+ * Nothing more to verify since we are empty
146
+ */
147
+ if (!relationshipData || relationshipData.length === 0) {
86
148
  return;
87
149
  }
150
+
151
+ /**
152
+ * We are explicitly asked to not verify full-linkage
153
+ */
88
154
  if (!options.verifyIncluded) {
89
155
  return;
90
156
  }
91
- const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
92
- if (!includedDoc) {
93
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
157
+
158
+ /**
159
+ * If we have a link, full-linkage verification is not required.
160
+ */
161
+ if (relationshipDoc.links?.related) {
162
+ return;
94
163
  }
95
- }
96
- function validateHasManyToLinksMode(resourceType, field, _relationshipDoc, _options) {
97
- if (field.options.async) {
98
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
164
+
165
+ /**
166
+ * If we are sync and have relationship data, we must have full linkage to included resources
167
+ */
168
+ for (const identifier of relationshipData) {
169
+ const includedDoc = options.included?.find(doc => doc.type === identifier.type && doc.id === identifier.id);
170
+ if (!includedDoc) {
171
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
172
+ }
99
173
  }
100
174
  }
101
175
 
@@ -59,7 +59,7 @@ function validateResourceFields(schema, resource, options) {
59
59
  case 'hasMany':
60
60
  {
61
61
  if (field.options.linksMode) {
62
- validateHasManyToLinksMode(resourceType, field);
62
+ validateHasManyToLinksMode(resourceType, field, relationshipDoc, options);
63
63
  }
64
64
  break;
65
65
  }
@@ -70,31 +70,105 @@ function validateBelongsToLinksMode(resourceType, field, relationshipDoc, option
70
70
  if (field.options.async) {
71
71
  throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async is not yet supported`);
72
72
  }
73
- if (!relationshipDoc.links?.related) {
74
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
73
+ if (!field.options.async) {
74
+ const relationshipData = relationshipDoc.data;
75
+ if (Array.isArray(relationshipData)) {
76
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
77
+ }
78
+
79
+ /**
80
+ * If we are sync, we must have a related link when we have no related data field
81
+ *
82
+ * We explicitly allow `null`! Missing key or `undefined` are always invalid.
83
+ */
84
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
85
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
86
+ }
87
+
88
+ /**
89
+ * Nothing more to verify since we are empty
90
+ */
91
+ if (!relationshipData) {
92
+ return;
93
+ }
94
+
95
+ /**
96
+ * We are explicitly asked to not verify full-linkage
97
+ */
98
+ if (!options.verifyIncluded) {
99
+ return;
100
+ }
101
+
102
+ /**
103
+ * If we have a link, full-linkage verification is not required.
104
+ */
105
+ if (relationshipDoc.links?.related) {
106
+ return;
107
+ }
108
+
109
+ /**
110
+ * If we are sync and have relationship data, we must have full linkage to an included resource
111
+ */
112
+ const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
113
+ if (!includedDoc) {
114
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
115
+ }
116
+ } else {
117
+ /**
118
+ * If we are async, we must have a related link.
119
+ */
120
+ if (!relationshipDoc.links?.related) {
121
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
122
+ }
123
+ }
124
+ }
125
+ function validateHasManyToLinksMode(resourceType, field, relationshipDoc, options) {
126
+ if (field.options.async) {
127
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
75
128
  }
76
129
  const relationshipData = relationshipDoc.data;
77
- if (Array.isArray(relationshipData)) {
78
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
130
+ if (relationshipData !== undefined && !Array.isArray(relationshipData)) {
131
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a hasMany relationship is unexpectedly not an array`);
79
132
  }
80
- // Explicitly allow `null`! Missing key or `undefined` are always invalid.
81
- if (relationshipData === undefined) {
82
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined`);
133
+
134
+ /**
135
+ * If we are sync, we must have a related link when we have no related data field
136
+ *
137
+ * We explicitly allow an empty array! Missing key or `undefined` are always invalid.
138
+ */
139
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
140
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
83
141
  }
84
- if (relationshipData === null) {
142
+
143
+ /**
144
+ * Nothing more to verify since we are empty
145
+ */
146
+ if (!relationshipData || relationshipData.length === 0) {
85
147
  return;
86
148
  }
149
+
150
+ /**
151
+ * We are explicitly asked to not verify full-linkage
152
+ */
87
153
  if (!options.verifyIncluded) {
88
154
  return;
89
155
  }
90
- const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
91
- if (!includedDoc) {
92
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
156
+
157
+ /**
158
+ * If we have a link, full-linkage verification is not required.
159
+ */
160
+ if (relationshipDoc.links?.related) {
161
+ return;
93
162
  }
94
- }
95
- function validateHasManyToLinksMode(resourceType, field, _relationshipDoc, _options) {
96
- if (field.options.async) {
97
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
163
+
164
+ /**
165
+ * If we are sync and have relationship data, we must have full linkage to included resources
166
+ */
167
+ for (const identifier of relationshipData) {
168
+ const includedDoc = options.included?.find(doc => doc.type === identifier.type && doc.id === identifier.id);
169
+ if (!includedDoc) {
170
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
171
+ }
98
172
  }
99
173
  }
100
174
 
@@ -59,7 +59,7 @@ function validateResourceFields(schema, resource, options) {
59
59
  case 'hasMany':
60
60
  {
61
61
  if (field.options.linksMode) {
62
- validateHasManyToLinksMode(resourceType, field);
62
+ validateHasManyToLinksMode(resourceType, field, relationshipDoc, options);
63
63
  }
64
64
  break;
65
65
  }
@@ -70,31 +70,105 @@ function validateBelongsToLinksMode(resourceType, field, relationshipDoc, option
70
70
  if (field.options.async) {
71
71
  throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async is not yet supported`);
72
72
  }
73
- if (!relationshipDoc.links?.related) {
74
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
73
+ if (!field.options.async) {
74
+ const relationshipData = relationshipDoc.data;
75
+ if (Array.isArray(relationshipData)) {
76
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
77
+ }
78
+
79
+ /**
80
+ * If we are sync, we must have a related link when we have no related data field
81
+ *
82
+ * We explicitly allow `null`! Missing key or `undefined` are always invalid.
83
+ */
84
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
85
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
86
+ }
87
+
88
+ /**
89
+ * Nothing more to verify since we are empty
90
+ */
91
+ if (!relationshipData) {
92
+ return;
93
+ }
94
+
95
+ /**
96
+ * We are explicitly asked to not verify full-linkage
97
+ */
98
+ if (!options.verifyIncluded) {
99
+ return;
100
+ }
101
+
102
+ /**
103
+ * If we have a link, full-linkage verification is not required.
104
+ */
105
+ if (relationshipDoc.links?.related) {
106
+ return;
107
+ }
108
+
109
+ /**
110
+ * If we are sync and have relationship data, we must have full linkage to an included resource
111
+ */
112
+ const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
113
+ if (!includedDoc) {
114
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
115
+ }
116
+ } else {
117
+ /**
118
+ * If we are async, we must have a related link.
119
+ */
120
+ if (!relationshipDoc.links?.related) {
121
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related link is missing`);
122
+ }
123
+ }
124
+ }
125
+ function validateHasManyToLinksMode(resourceType, field, relationshipDoc, options) {
126
+ if (field.options.async) {
127
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
75
128
  }
76
129
  const relationshipData = relationshipDoc.data;
77
- if (Array.isArray(relationshipData)) {
78
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a belongsTo relationship is unexpectedly an array`);
130
+ if (relationshipData !== undefined && !Array.isArray(relationshipData)) {
131
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the relationship data for a hasMany relationship is unexpectedly not an array`);
79
132
  }
80
- // Explicitly allow `null`! Missing key or `undefined` are always invalid.
81
- if (relationshipData === undefined) {
82
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined`);
133
+
134
+ /**
135
+ * If we are sync, we must have a related link when we have no related data field
136
+ *
137
+ * We explicitly allow an empty array! Missing key or `undefined` are always invalid.
138
+ */
139
+ if (relationshipData === undefined && !relationshipDoc.links?.related) {
140
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the relationship data is undefined and no link is present`);
83
141
  }
84
- if (relationshipData === null) {
142
+
143
+ /**
144
+ * Nothing more to verify since we are empty
145
+ */
146
+ if (!relationshipData || relationshipData.length === 0) {
85
147
  return;
86
148
  }
149
+
150
+ /**
151
+ * We are explicitly asked to not verify full-linkage
152
+ */
87
153
  if (!options.verifyIncluded) {
88
154
  return;
89
155
  }
90
- const includedDoc = options.included?.find(doc => doc.type === relationshipData.type && doc.id === relationshipData.id);
91
- if (!includedDoc) {
92
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
156
+
157
+ /**
158
+ * If we have a link, full-linkage verification is not required.
159
+ */
160
+ if (relationshipDoc.links?.related) {
161
+ return;
93
162
  }
94
- }
95
- function validateHasManyToLinksMode(resourceType, field, _relationshipDoc, _options) {
96
- if (field.options.async) {
97
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async hasMany is not yet supported`);
163
+
164
+ /**
165
+ * If we are sync and have relationship data, we must have full linkage to included resources
166
+ */
167
+ for (const identifier of relationshipData) {
168
+ const includedDoc = options.included?.find(doc => doc.type === identifier.type && doc.id === identifier.id);
169
+ if (!includedDoc) {
170
+ throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but the related data is not included`);
171
+ }
98
172
  }
99
173
  }
100
174
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warp-drive/json-api",
3
- "version": "5.9.0-alpha.17",
3
+ "version": "5.9.0-alpha.18",
4
4
  "description": "A {json:api} Cache Implementation for WarpDrive",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "peerDependencies": {
43
- "@warp-drive/core": "5.9.0-alpha.17"
43
+ "@warp-drive/core": "5.9.0-alpha.18"
44
44
  },
45
45
  "dependencies": {
46
46
  "@embroider/macros": "^1.19.6",
@@ -52,8 +52,8 @@
52
52
  "@babel/plugin-transform-typescript": "^7.28.0",
53
53
  "@babel/preset-typescript": "^7.27.1",
54
54
  "@types/json-to-ast": "^2.1.4",
55
- "@warp-drive/internal-config": "5.9.0-alpha.17",
56
- "@warp-drive/core": "5.9.0-alpha.17",
55
+ "@warp-drive/internal-config": "5.9.0-alpha.18",
56
+ "@warp-drive/core": "5.9.0-alpha.18",
57
57
  "decorator-transforms": "^2.3.0",
58
58
  "expect-type": "^1.2.2",
59
59
  "typescript": "^5.9.3",