@warp-drive-mirror/json-api 5.8.0-alpha.37 → 5.8.0-alpha.40

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 (37) hide show
  1. package/dist/unpkg/dev/index.js +252 -139
  2. package/dist/unpkg/dev-deprecated/index.js +246 -172
  3. package/dist/unpkg/prod/index.js +4 -1357
  4. package/dist/unpkg/prod-deprecated/index.js +4 -1357
  5. package/package.json +12 -20
  6. package/dist/unpkg/dev/declarations/-private/cache.d.ts +0 -445
  7. package/dist/unpkg/dev/declarations/-private/validate-document-fields.d.ts +0 -3
  8. package/dist/unpkg/dev/declarations/-private/validator/1.1/7.1_top-level-document-members.d.ts +0 -1
  9. package/dist/unpkg/dev/declarations/-private/validator/1.1/7.2_resource-objects.d.ts +0 -1
  10. package/dist/unpkg/dev/declarations/-private/validator/1.1/links.d.ts +0 -1
  11. package/dist/unpkg/dev/declarations/-private/validator/index.d.ts +0 -4
  12. package/dist/unpkg/dev/declarations/-private/validator/utils.d.ts +0 -75
  13. package/dist/unpkg/dev/declarations/index.d.ts +0 -5
  14. package/dist/unpkg/dev-deprecated/declarations/-private/cache.d.ts +0 -445
  15. package/dist/unpkg/dev-deprecated/declarations/-private/validate-document-fields.d.ts +0 -3
  16. package/dist/unpkg/dev-deprecated/declarations/-private/validator/1.1/7.1_top-level-document-members.d.ts +0 -1
  17. package/dist/unpkg/dev-deprecated/declarations/-private/validator/1.1/7.2_resource-objects.d.ts +0 -1
  18. package/dist/unpkg/dev-deprecated/declarations/-private/validator/1.1/links.d.ts +0 -1
  19. package/dist/unpkg/dev-deprecated/declarations/-private/validator/index.d.ts +0 -4
  20. package/dist/unpkg/dev-deprecated/declarations/-private/validator/utils.d.ts +0 -75
  21. package/dist/unpkg/dev-deprecated/declarations/index.d.ts +0 -5
  22. package/dist/unpkg/prod/declarations/-private/cache.d.ts +0 -445
  23. package/dist/unpkg/prod/declarations/-private/validate-document-fields.d.ts +0 -3
  24. package/dist/unpkg/prod/declarations/-private/validator/1.1/7.1_top-level-document-members.d.ts +0 -1
  25. package/dist/unpkg/prod/declarations/-private/validator/1.1/7.2_resource-objects.d.ts +0 -1
  26. package/dist/unpkg/prod/declarations/-private/validator/1.1/links.d.ts +0 -1
  27. package/dist/unpkg/prod/declarations/-private/validator/index.d.ts +0 -4
  28. package/dist/unpkg/prod/declarations/-private/validator/utils.d.ts +0 -75
  29. package/dist/unpkg/prod/declarations/index.d.ts +0 -5
  30. package/dist/unpkg/prod-deprecated/declarations/-private/cache.d.ts +0 -445
  31. package/dist/unpkg/prod-deprecated/declarations/-private/validate-document-fields.d.ts +0 -3
  32. package/dist/unpkg/prod-deprecated/declarations/-private/validator/1.1/7.1_top-level-document-members.d.ts +0 -1
  33. package/dist/unpkg/prod-deprecated/declarations/-private/validator/1.1/7.2_resource-objects.d.ts +0 -1
  34. package/dist/unpkg/prod-deprecated/declarations/-private/validator/1.1/links.d.ts +0 -1
  35. package/dist/unpkg/prod-deprecated/declarations/-private/validator/index.d.ts +0 -4
  36. package/dist/unpkg/prod-deprecated/declarations/-private/validator/utils.d.ts +0 -75
  37. package/dist/unpkg/prod-deprecated/declarations/index.d.ts +0 -5
@@ -1,934 +1,14 @@
1
1
  import { graphFor, peekGraph, isBelongsTo } from '@warp-drive-mirror/core/graph/-private';
2
- import { logGroup, isResourceKey, assertPrivateCapabilities, isRequestKey } from '@warp-drive-mirror/core/store/-private';
3
- import Fuse from 'fuse.js';
4
- import jsonToAst from 'json-to-ast';
5
- import { macroCondition, getGlobalConfig } from '@embroider/macros';
2
+ import { assertPrivateCapabilities, isResourceKey, isRequestKey } from '@warp-drive-mirror/core/store/-private';
3
+ import 'fuse.js';
4
+ import 'json-to-ast';
6
5
 
7
- function validateDocumentFields(schema, jsonApiDoc) {
8
- const {
9
- data,
10
- included
11
- } = jsonApiDoc;
12
- if (data === null) {
13
- return;
14
- }
15
- if (typeof jsonApiDoc.data !== 'object') {
16
- throw new Error(`Expected a resource object in the 'data' property in the document provided to the cache, but was ${typeof jsonApiDoc.data}`);
17
- }
18
- if (Array.isArray(data)) {
19
- for (const resource of data) {
20
- validateResourceFields(schema, resource, {
21
- verifyIncluded: true,
22
- included
23
- });
24
- }
25
- } else {
26
- validateResourceFields(schema, data, {
27
- verifyIncluded: true,
28
- included
29
- });
30
- }
31
- if (included) {
32
- for (const resource of included) {
33
- validateResourceFields(schema, resource, {
34
- verifyIncluded: false
35
- });
36
- }
37
- }
38
- }
39
- function validateResourceFields(schema, resource, options) {
40
- if (!resource.relationships) {
41
- return;
42
- }
43
- const resourceType = resource.type;
44
- const fields = schema.fields({
45
- type: resource.type
46
- });
47
- for (const [type, relationshipDoc] of Object.entries(resource.relationships)) {
48
- const field = fields.get(type);
49
- if (!field) {
50
- return;
51
- }
52
- switch (field.kind) {
53
- case 'belongsTo':
54
- {
55
- if (field.options.linksMode) {
56
- validateBelongsToLinksMode(resourceType, field, relationshipDoc, options);
57
- }
58
- break;
59
- }
60
- case 'hasMany':
61
- {
62
- if (field.options.linksMode) {
63
- validateHasManyToLinksMode(resourceType, field);
64
- }
65
- break;
66
- }
67
- }
68
- }
69
- }
70
- function validateBelongsToLinksMode(resourceType, field, relationshipDoc, options) {
71
- if (field.options.async) {
72
- throw new Error(`Cannot fetch ${resourceType}.${field.name} because the field is in linksMode but async is not yet supported`);
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`);
76
- }
77
- 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`);
80
- }
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`);
84
- }
85
- if (relationshipData === null) {
86
- return;
87
- }
88
- if (!options.verifyIncluded) {
89
- return;
90
- }
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`);
94
- }
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`);
99
- }
100
- }
101
-
102
- function inspectType(obj) {
103
- if (obj === null) {
104
- return 'null';
105
- }
106
- if (Array.isArray(obj)) {
107
- return 'array';
108
- }
109
- if (typeof obj === 'object') {
110
- const proto = Object.getPrototypeOf(obj);
111
- if (proto === null) {
112
- return 'object';
113
- }
114
- if (proto === Object.prototype) {
115
- return 'object';
116
- }
117
- return `object (${proto.constructor?.name})`;
118
- }
119
- if (typeof obj === 'function') {
120
- return 'function';
121
- }
122
- if (typeof obj === 'string') {
123
- return 'string';
124
- }
125
- if (typeof obj === 'number') {
126
- return 'number';
127
- }
128
- if (typeof obj === 'boolean') {
129
- return 'boolean';
130
- }
131
- if (typeof obj === 'symbol') {
132
- return 'symbol';
133
- }
134
- if (typeof obj === 'bigint') {
135
- return 'bigint';
136
- }
137
- if (typeof obj === 'undefined') {
138
- return 'undefined';
139
- }
140
- return 'unknown';
141
- }
142
- function isSimpleObject(obj) {
143
- if (obj === null) {
144
- return false;
145
- }
146
- if (Array.isArray(obj)) {
147
- return false;
148
- }
149
- if (typeof obj !== 'object') {
150
- return false;
151
- }
152
- const proto = Object.getPrototypeOf(obj);
153
- if (proto === null) {
154
- return true;
155
- }
156
- if (proto === Object.prototype) {
157
- return true;
158
- }
159
- return false;
160
- }
161
- const RELATIONSHIP_FIELD_KINDS = ['belongsTo', 'hasMany', 'resource', 'collection'];
162
- class Reporter {
163
- capabilities;
164
- contextDocument;
165
- errors = [];
166
- ast;
167
- jsonStr;
168
-
169
- // TODO @runspired make this configurable to consuming apps before
170
- // activating by default
171
- strict = {
172
- linkage: true,
173
- unknownType: true,
174
- unknownAttribute: true,
175
- unknownRelationship: true
176
- };
177
- constructor(capabilities, doc) {
178
- this.capabilities = capabilities;
179
- this.contextDocument = doc;
180
- this.jsonStr = JSON.stringify(doc.content, null, 2);
181
- this.ast = jsonToAst(this.jsonStr, {
182
- loc: true
183
- });
184
- }
185
- searchTypes(type) {
186
- if (!this._typeFilter) {
187
- const allTypes = this.schema.resourceTypes();
188
- this._typeFilter = new Fuse(allTypes);
189
- }
190
- const result = this._typeFilter.search(type);
191
- return result;
192
- }
193
- _fieldFilters = new Map();
194
- searchFields(type, field) {
195
- if (!this._fieldFilters.has(type)) {
196
- const allFields = this.schema.fields({
197
- type
198
- });
199
- const allCacheFields = this.schema.cacheFields?.({
200
- type
201
- }) ?? allFields;
202
- const attrs = Array.from(allCacheFields.values()).filter(isRemoteField).map(v => v.name);
203
- this._fieldFilters.set(type, new Fuse(attrs));
204
- }
205
- const result = this._fieldFilters.get(type).search(field);
206
- return result;
207
- }
208
- get schema() {
209
- return this.capabilities.schema;
210
- }
211
- getLocation(path, kind) {
212
- if (path.length === 0) {
213
- return this.ast.loc;
214
- }
215
- let priorNode = this.ast;
216
- let node = this.ast;
217
- for (const segment of path) {
218
- //
219
- // handle array paths
220
- //
221
- if (typeof segment === 'number') {
222
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
223
- if (!test) {
224
- throw new Error(`Because the segment is a number, expected a node of type Array`);
225
- }
226
- })(node.type === 'Array') : {};
227
- if (node.children && node.children[segment]) {
228
- priorNode = node;
229
- const childNode = node.children[segment];
230
- if (childNode.type === 'Object' || childNode.type === 'Array') {
231
- node = childNode;
232
- } else {
233
- // set to the closest node we can find
234
- return node.loc;
235
- }
236
- } else {
237
- // set to the closest node we can find
238
- // as we had no children
239
- return priorNode.loc;
240
- }
241
-
242
- //
243
- // handle object paths
244
- //
245
- } else {
246
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
247
- if (!test) {
248
- throw new Error(`Because the segment is a string, expected a node of type Object`);
249
- }
250
- })(node.type === 'Object') : {};
251
- const child = node.children.find(childCandidate => {
252
- if (childCandidate.type === 'Property') {
253
- return childCandidate.key.type === 'Identifier' && childCandidate.key.value === segment;
254
- }
255
- return false;
256
- });
257
- if (child) {
258
- if (child.value.type === 'Object' || child.value.type === 'Array') {
259
- priorNode = node;
260
- node = child.value;
261
- } else {
262
- // set to the closest node we can find
263
- return kind === 'key' ? child.key.loc : child.value.loc;
264
- }
265
- } else {
266
- // set to the closest node we can find
267
- return priorNode.loc;
268
- }
269
- }
270
- }
271
- return node.loc;
272
- }
273
- error(path, message, kind = 'key') {
274
- const loc = this.getLocation(path, kind);
275
- this.errors.push({
276
- path,
277
- message,
278
- loc,
279
- type: 'error',
280
- kind
281
- });
282
- }
283
- warn(path, message, kind = 'key') {
284
- const loc = this.getLocation(path, kind);
285
- this.errors.push({
286
- path,
287
- message,
288
- loc,
289
- type: 'warning',
290
- kind
291
- });
292
- }
293
- info(path, message, kind = 'key') {
294
- const loc = this.getLocation(path, kind);
295
- this.errors.push({
296
- path,
297
- message,
298
- loc,
299
- type: 'info',
300
- kind
301
- });
302
- }
303
- hasExtension(extensionName) {
304
- return REGISTERED_EXTENSIONS.has(extensionName);
305
- }
306
- getExtension(extensionName) {
307
- return REGISTERED_EXTENSIONS.get(extensionName);
308
- }
309
- report(colorize = true) {
310
- const lines = this.jsonStr.split('\n');
311
-
312
- // sort the errors by line, then by column, then by type
313
- const {
314
- errors
315
- } = this;
316
- if (!errors.length) {
317
- return;
318
- }
319
- errors.sort((a, b) => {
320
- return a.loc.end.line < b.loc.end.line ? -1 : a.loc.end.column < b.loc.end.column ? -1 : compareType(a.type, b.type);
321
- });
322
-
323
- // store the errors in a map by line
324
- const errorMap = new Map();
325
- for (const error of errors) {
326
- const line = error.loc.end.line;
327
- if (!errorMap.has(line)) {
328
- errorMap.set(line, []);
329
- }
330
- errorMap.get(line).push(error);
331
- }
332
-
333
- // splice the errors into the lines
334
- const errorLines = [];
335
- const colors = [];
336
- const counts = {
337
- error: 0,
338
- warning: 0,
339
- info: 0
340
- };
341
- const LINE_SIZE = String(lines.length).length;
342
- for (let i = 0; i < lines.length; i++) {
343
- const line = lines[i];
344
- errorLines.push(colorize ? `${String(i + 1).padEnd(LINE_SIZE, ' ')} \t%c${line}%c` : `${String(i + 1).padEnd(LINE_SIZE, ' ')} \t${line}`);
345
- colors.push(`color: grey; background-color: transparent;`,
346
- // first color sets color
347
- `color: inherit; background-color: transparent;` // second color resets the color profile
348
- );
349
- if (errorMap.has(i + 1)) {
350
- const errorsForLine = errorMap.get(i + 1);
351
- for (const error of errorsForLine) {
352
- counts[error.type]++;
353
- const {
354
- loc,
355
- message
356
- } = error;
357
- const start = loc.end.line === loc.start.line ? loc.start.column - 1 : loc.end.column - 1;
358
- const end = loc.end.column - 1;
359
- const symbol = error.type === 'error' ? '❌' : error.type === 'warning' ? '⚠️' : 'ℹ️';
360
- const errorLine = colorize ? `${''.padStart(LINE_SIZE, ' ') + symbol}\t${' '.repeat(start)}%c^${'~'.repeat(end - start)} %c//%c ${message}%c` : `${''.padStart(LINE_SIZE, ' ') + symbol}\t${' '.repeat(start)}^${'~'.repeat(end - start)} // ${message}`;
361
- errorLines.push(errorLine);
362
- colors.push(error.type === 'error' ? 'color: red;' : error.type === 'warning' ? 'color: orange;' : 'color: blue;', 'color: grey;', error.type === 'error' ? 'color: red;' : error.type === 'warning' ? 'color: orange;' : 'color: blue;', 'color: inherit; background-color: transparent;' // reset color
363
- );
364
- }
365
- }
366
- }
367
- const contextStr = `${counts.error} errors and ${counts.warning} warnings found in the {json:api} document returned by ${this.contextDocument.request?.method} ${this.contextDocument.request?.url}`;
368
- const errorString = contextStr + `\n\n` + errorLines.join('\n');
369
-
370
- // eslint-disable-next-line no-console, @typescript-eslint/no-unused-expressions
371
- colorize ? console.log(errorString, ...colors) : console.log(errorString);
372
- if (macroCondition(getGlobalConfig().WarpDriveMirror.features.JSON_API_CACHE_VALIDATION_ERRORS)) {
373
- if (counts.error > 0) {
374
- throw new Error(contextStr);
375
- }
376
- }
377
- }
378
- }
379
-
380
- // we always want to sort errors first, then warnings, then info
381
- function compareType(a, b) {
382
- if (a === b) {
383
- return 0;
384
- }
385
- if (a === 'error') {
386
- return -1;
387
- }
388
- if (b === 'error') {
389
- return 1;
390
- }
391
- if (a === 'warning') {
392
- return -1;
393
- }
394
- if (b === 'warning') {
395
- return 1;
396
- }
397
- return 0;
398
- }
399
- const REGISTERED_EXTENSIONS = new Map();
400
6
  function isMetaDocument(doc) {
401
7
  return !(doc instanceof Error) && doc.content && !('data' in doc.content) && !('included' in doc.content) && 'meta' in doc.content;
402
8
  }
403
9
  function isErrorDocument(doc) {
404
10
  return doc instanceof Error;
405
11
  }
406
- function isPushedDocument(doc) {
407
- return !!doc && typeof doc === 'object' && 'content' in doc && !('request' in doc) && !('response' in doc);
408
- }
409
- function logPotentialMatches(matches, kind) {
410
- if (matches.length === 0) {
411
- return '';
412
- }
413
- if (matches.length === 1) {
414
- return ` Did you mean this available ${kind} "${matches[0].item}"?`;
415
- }
416
- const potentialMatches = matches.map(match => match.item).join('", "');
417
- return ` Did you mean one of these available ${kind}s: "${potentialMatches}"?`;
418
- }
419
- function isRemoteField(v) {
420
- return !(v.kind === '@local' || v.kind === 'alias' || v.kind === 'derived');
421
- }
422
- function getRemoteField(fields, key) {
423
- const field = fields.get(key);
424
- if (!field) {
425
- return undefined;
426
- }
427
- if (!isRemoteField(field)) {
428
- return undefined;
429
- }
430
- return field;
431
- }
432
-
433
- const VALID_TOP_LEVEL_MEMBERS = ['data', 'included', 'meta', 'jsonapi', 'links'];
434
-
435
- /**
436
- * Reports issues which violate the JSON:API spec for top-level members.
437
- *
438
- * Version: 1.1
439
- * Section: 7.1
440
- * Link: https://jsonapi.org/format/#document-top-level
441
- *
442
- * @internal
443
- */
444
- function validateTopLevelDocumentMembers(reporter, doc) {
445
- const keys = Object.keys(doc);
446
- for (const key of keys) {
447
- if (!VALID_TOP_LEVEL_MEMBERS.includes(key)) {
448
- if (key.includes(':')) {
449
- // TODO @runspired expose the API to enable folks to add validation for their own extensions
450
- const extensionName = key.split(':')[0];
451
- if (reporter.hasExtension(extensionName)) {
452
- const extension = reporter.getExtension(extensionName);
453
- extension(reporter, [key]);
454
- } else {
455
- reporter.warn([key], `Unrecognized extension ${extensionName}. The data provided by "${key}" will be ignored as it is not a valid {json:api} member`);
456
- }
457
- } else {
458
- reporter.error([key], `Unrecognized top-level member. The data it provides is ignored as it is not a valid {json:api} member`);
459
- }
460
- }
461
- }
462
-
463
- // additional rules for top-level members
464
- // ======================================
465
-
466
- // 1. MUST have either `data`, `errors`, or `meta`
467
- if (!('data' in doc) && !('errors' in doc) && !('meta' in doc)) {
468
- reporter.error([], 'A {json:api} Document must contain one-of `data` `errors` or `meta`');
469
- }
470
-
471
- // 2. MUST NOT have both `data` and `errors`
472
- if ('data' in doc && 'errors' in doc) {
473
- reporter.error(['data'], 'A {json:api} Document MUST NOT contain both `data` and `errors` members');
474
- }
475
-
476
- // 3. MUST NOT have both `included` and `errors`
477
- // while not explicitly stated in the spec, this is a logical extension of the above rule
478
- // since `included` is only valid when `data` is present.
479
- if ('included' in doc && 'errors' in doc) {
480
- reporter.error(['included'], 'A {json:api} Document MUST NOT contain both `included` and `errors` members');
481
- }
482
-
483
- // 4. MUST NOT have `included` if `data` is not present
484
- if ('included' in doc && !('data' in doc)) {
485
- reporter.error(['included'], 'A {json:api} Document MUST NOT contain `included` if `data` is not present');
486
- }
487
-
488
- // 5. MUST NOT have `included` if `data` is null
489
- // when strictly enforcing full linkage, we need to ensure that `included` is not present if `data` is null
490
- // however, most APIs will ignore this rule for DELETE requests, so unless strict linkage is enabled, we will only warn
491
- // about this issue.
492
- if ('included' in doc && doc.data === null) {
493
- const isMaybeDelete = reporter.contextDocument.request?.method?.toUpperCase() === 'DELETE' || reporter.contextDocument.request?.op === 'deleteRecord';
494
- const method = !reporter.strict.linkage && isMaybeDelete ? 'warn' : 'error';
495
- reporter[method](['included'], 'A {json:api} Document MUST NOT contain `included` if `data` is null');
496
- }
497
-
498
- // Simple Validation of Top-Level Members
499
- // ==========================================
500
- // 1. `data` MUST be a single resource object or an array of resource objects or `null`
501
- if ('data' in doc) {
502
- const dataMemberHasAppropriateForm = doc.data === null || Array.isArray(doc.data) || isSimpleObject(doc.data);
503
- if (!dataMemberHasAppropriateForm) {
504
- reporter.error(['data'], `The 'data' member MUST be a single resource object or an array of resource objects or null. Received data of type "${inspectType(doc.data)}"`);
505
- }
506
- }
507
-
508
- // 2. `included` MUST be an array of resource objects
509
- if ('included' in doc) {
510
- if (!Array.isArray(doc.included)) {
511
- reporter.error(['included'], `The 'included' member MUST be an array of resource objects. Received data of type "${inspectType(doc.included)}"`);
512
- }
513
- }
514
-
515
- // 3. `meta` MUST be a simple object
516
- if ('meta' in doc) {
517
- if (!isSimpleObject(doc.meta)) {
518
- reporter.error(['meta'], `The 'meta' member MUST be a simple object. Received data of type "${inspectType(doc.meta)}"`);
519
- }
520
- }
521
-
522
- // 4. `jsonapi` MUST be a simple object
523
- if ('jsonapi' in doc) {
524
- if (!isSimpleObject(doc.jsonapi)) {
525
- reporter.error(['jsonapi'], `The 'jsonapi' member MUST be a simple object. Received data of type "${inspectType(doc.jsonapi)}"`);
526
- }
527
- }
528
-
529
- // 5. `links` MUST be a simple object
530
- if ('links' in doc) {
531
- if (!isSimpleObject(doc.links)) {
532
- reporter.error(['links'], `The 'links' member MUST be a simple object. Received data of type "${inspectType(doc.links)}"`);
533
- }
534
- }
535
-
536
- // 6. `errors` MUST be an array of error objects
537
- if ('errors' in doc) {
538
- if (!Array.isArray(doc.errors)) {
539
- reporter.error(['errors'], `The 'errors' member MUST be an array of error objects. Received data of type "${inspectType(doc.errors)}"`);
540
- }
541
- }
542
- }
543
-
544
- const VALID_COLLECTION_LINKS = ['self', 'related', 'first', 'last', 'prev', 'next'];
545
- const VALID_RESOURCE_RELATIONSHIP_LINKS = ['self', 'related'];
546
- const VALID_RESOURCE_LINKS = ['self'];
547
-
548
- /**
549
- * Validates the links object in a top-level JSON API document or resource object
550
- *
551
- * Version: 1.1
552
- *
553
- * Section: 7.1 Top Level
554
- * Link: https://jsonapi.org/format/#document-top-level
555
- *
556
- * Section: 7.2.3 Resource Objects
557
- * Link: https://jsonapi.org/format/#document-resource-object-links
558
- *
559
- * Section: 7.2.2.2 Resource Relationships
560
- * Link: https://jsonapi.org/format/#document-resource-object-relationships
561
- *
562
- * Section: 7.6 Document Links
563
- * Link: https://jsonapi.org/format/#document-links
564
- *
565
- * @internal
566
- */
567
- function validateLinks(reporter, doc, type, path = ['links']) {
568
- if (!('links' in doc)) {
569
- return;
570
- }
571
- if (!isSimpleObject(doc.links)) {
572
- // this is a violation but we report it when validating section 7.1
573
- return;
574
- }
575
-
576
- // prettier-ignore
577
- const VALID_TOP_LEVEL_LINKS = type === 'collection-document' || type === 'collection-relationship' ? VALID_COLLECTION_LINKS : type === 'resource-document' || type === 'resource-relationship' ? VALID_RESOURCE_RELATIONSHIP_LINKS : type === 'resource' ? VALID_RESOURCE_LINKS : [];
578
- const links = doc.links;
579
- const keys = Object.keys(links);
580
- for (const key of keys) {
581
- if (!VALID_TOP_LEVEL_LINKS.includes(key)) {
582
- reporter.warn([...path, key], `Unrecognized top-level link. The data it provides may be ignored as it is not a valid {json:api} link for a ${type}`);
583
- }
584
- // links may be either a string or an object with an href property or null
585
- if (links[key] === null) ; else if (typeof links[key] === 'string') {
586
- if (links[key].length === 0) {
587
- reporter.warn([...path, key], `Expected a non-empty string, but received an empty string`);
588
- }
589
- // valid, though we should potentially validate the URL here
590
- } else if (isSimpleObject(links[key])) {
591
- if ('href' in links[key]) {
592
- const linksKeys = Object.keys(links[key]);
593
- if (linksKeys.length > 1) {
594
- reporter.warn([...path, key], `Expected the links object to only have an href property, but received unknown keys ${linksKeys.filter(k => k !== 'href').join(', ')}`);
595
- }
596
- if (typeof links[key].href !== 'string') {
597
- reporter.error([...path, key, 'href'], `Expected a string value, but received ${inspectType(links[key].href)}`);
598
- } else {
599
- if (links[key].href.length === 0) {
600
- reporter.warn([...path, key, 'href'], `Expected a non-empty string, but received an empty string`);
601
- }
602
- // valid, though we should potentially validate the URL here
603
- }
604
- } else {
605
- const linksKeys = Object.keys(links[key]);
606
- if (linksKeys.length > 0) {
607
- reporter.error([...path, key], `Expected the links object to have an href property, but received only the unknown keys ${linksKeys.join(', ')}`);
608
- } else {
609
- reporter.error([...path, key], `Expected the links object to have an href property`);
610
- }
611
- }
612
- } else {
613
- // invalid
614
- reporter.error([...path, key], `Expected a string, null, or an object with an href property for the link "${key}", but received ${inspectType(links[key])}`);
615
- }
616
- }
617
- }
618
-
619
- const SINGULAR_OPS = ['createRecord', 'updateRecord', 'deleteRecord', 'findRecord', 'queryRecord'];
620
-
621
- /**
622
- * Validates the resource objects in either the `data` or `included` members of
623
- * JSON:API document.
624
- *
625
- * Version: 1.1
626
- * Section: 7.2
627
- * Link: https://jsonapi.org/format/#document-resource-objects
628
- *
629
- * @internal
630
- */
631
- function validateDocumentResources(reporter, doc) {
632
- if ('data' in doc) {
633
- // scan for common mistakes of single vs multiple resource objects
634
- const op = reporter.contextDocument.request?.op;
635
- if (op && SINGULAR_OPS.includes(op)) {
636
- if (Array.isArray(doc.data)) {
637
- reporter.error(['data'], `"${op}" requests expect a single resource object in the returned data, but received an array`);
638
- }
639
- }
640
-
641
- // guard for a common mistake around deleteRecord
642
- if (op === 'deleteRecord') {
643
- if (doc.data !== null) {
644
- reporter.warn(['data'], `"deleteRecord" requests expect the data member to be null, but received ${inspectType(doc.data)}. This can sometimes cause unexpected resurrection of the deleted record.`);
645
- }
646
- }
647
- if (Array.isArray(doc.data)) {
648
- doc.data.forEach((resource, index) => {
649
- if (!isSimpleObject(resource)) {
650
- reporter.error(['data', index], `Expected a resource object, but received ${inspectType(resource)}`);
651
- } else {
652
- validateResourceObject(reporter, resource, ['data', index]);
653
- }
654
- });
655
- } else if (doc.data !== null) {
656
- if (!isSimpleObject(doc.data)) {
657
- reporter.error(['data'], `Expected a resource object, but received ${inspectType(doc.data)}`);
658
- } else {
659
- validateResourceObject(reporter, doc.data, ['data']);
660
- }
661
- }
662
- }
663
- if ('included' in doc && Array.isArray(doc.included)) {
664
- doc.included.forEach((resource, index) => {
665
- if (!isSimpleObject(resource)) {
666
- reporter.error(['included', index], `Expected a resource object, but received ${inspectType(resource)}`);
667
- } else {
668
- validateResourceObject(reporter, resource, ['included', index]);
669
- }
670
- });
671
- }
672
- }
673
- function validateResourceObject(reporter, resource, path) {
674
- validateTopLevelResourceShape(reporter, resource, path);
675
- }
676
- const VALID_TOP_LEVEL_RESOURCE_KEYS = ['lid', 'id', 'type', 'attributes', 'relationships', 'meta', 'links'];
677
- function validateTopLevelResourceShape(reporter, resource, path) {
678
- // a resource MUST have a string type
679
- if (!('type' in resource)) {
680
- reporter.error([...path, 'type'], `Expected a ResourceObject to have a type property`);
681
- } else if (typeof resource.type !== 'string') {
682
- reporter.error([...path, 'type'], `Expected a string value for the type property, but received ${inspectType(resource.type)}`, 'value');
683
- } else if (resource.type.length === 0) {
684
- reporter.error([...path, 'type'], `Expected a non-empty string value for the type property, but received an empty string`, 'value');
685
- } else if (!reporter.schema.hasResource({
686
- type: resource.type
687
- })) {
688
- const method = reporter.strict.unknownType ? 'error' : 'warn';
689
- const potentialTypes = reporter.searchTypes(resource.type);
690
- reporter[method]([...path, 'type'], `Expected a schema to be available for the ResourceType "${resource.type}" but none was found.${logPotentialMatches(potentialTypes, 'ResourceType')}`, 'value');
691
- }
692
-
693
- // a resource MUST have a string ID
694
- if (!('id' in resource)) {
695
- reporter.error([...path, 'id'], `Expected a ResourceObject to have an id property`);
696
- } else if (typeof resource.id !== 'string') {
697
- reporter.error([...path, 'id'], `Expected a string value for the id property, but received ${inspectType(resource.id)}`, 'value');
698
- } else if (resource.id.length === 0) {
699
- reporter.error([...path, 'id'], `Expected a non-empty string value for the id property, but received an empty string`, 'value');
700
- }
701
-
702
- // a resource MAY have a lid property
703
- if ('lid' in resource && typeof resource.lid !== 'string') {
704
- reporter.error([...path, 'lid'], `Expected a string value for the lid property, but received ${inspectType(resource.lid)}`, 'value');
705
- }
706
-
707
- // a resource MAY have a meta property
708
- if ('meta' in resource && !isSimpleObject(resource.meta)) {
709
- reporter.error([...path, 'meta'], `Expected a simple object for the meta property, but received ${inspectType(resource.meta)}`, 'value');
710
- }
711
-
712
- // a resource MAY have a links property
713
- if ('links' in resource && !isSimpleObject(resource.links)) {
714
- reporter.error([...path, 'links'], `Expected a simple object for the links property, but received ${inspectType(resource.links)}`, 'value');
715
- } else if ('links' in resource) {
716
- validateLinks(reporter, resource, 'resource', [...path, 'links']);
717
- }
718
- const hasAttributes = 'attributes' in resource && isSimpleObject(resource.attributes);
719
- const hasRelationships = 'relationships' in resource && isSimpleObject(resource.relationships);
720
-
721
- // We expect at least one of attributes or relationships to be present
722
- if (!hasAttributes && !hasRelationships) {
723
- reporter.warn(path, `Expected a ResourceObject to have either attributes or relationships`);
724
- }
725
-
726
- // we expect at least one of attributes or relationships to be non-empty
727
- const attributesLength = hasAttributes ? Object.keys(resource.attributes).length : 0;
728
- const relationshipsLength = hasRelationships ? Object.keys(resource.relationships).length : 0;
729
- if ((hasAttributes || hasRelationships) && attributesLength === 0 && relationshipsLength === 0) {
730
- reporter.warn([...path, hasAttributes ? 'attributes' : hasRelationships ? 'relationships' : 'attributes'], `Expected a ResourceObject to have either non-empty attributes or non-empty relationships`);
731
- }
732
-
733
- // check for unknown keys on the resource object
734
- const keys = Object.keys(resource);
735
- for (const key of keys) {
736
- if (!VALID_TOP_LEVEL_RESOURCE_KEYS.includes(key)) {
737
- // check for extension keys
738
- if (key.includes(':')) {
739
- const extensionName = key.split(':')[0];
740
- if (reporter.hasExtension(extensionName)) {
741
- const extension = reporter.getExtension(extensionName);
742
- extension(reporter, [...path, key]);
743
- } else {
744
- reporter.warn([...path, key], `Unrecognized extension ${extensionName}. The data provided by "${key}" will be ignored as it is not a valid {json:api} ResourceObject member`);
745
- }
746
- } else {
747
- // check if this is an attribute or relationship
748
- let didYouMean = ' Likely this field should have been inside of either "attributes" or "relationships"';
749
- const type = 'type' in resource ? resource.type : undefined;
750
- if (type && reporter.schema.hasResource({
751
- type
752
- })) {
753
- const fields = reporter.schema.fields({
754
- type
755
- });
756
- const field = getRemoteField(fields, key);
757
- if (field) {
758
- const isRelationship = RELATIONSHIP_FIELD_KINDS.includes(field.kind);
759
- didYouMean = ` Based on the ResourceSchema for "${type}" this field is likely a ${field.kind} and belongs inside of ${isRelationship ? 'relationships' : 'attributes'}, e.g. "${isRelationship ? 'relationships' : 'attributes'}": { "${key}": { ... } }`;
760
- } else {
761
- const fieldMatches = reporter.searchFields(type, key);
762
- if (fieldMatches.length === 1) {
763
- const matchedField = fields.get(fieldMatches[0].item);
764
- const isRelationship = RELATIONSHIP_FIELD_KINDS.includes(matchedField.kind);
765
- didYouMean = ` Based on the ResourceSchema for "${type}" this field is likely a ${matchedField.kind} and belongs inside of ${isRelationship ? 'relationships' : 'attributes'}, e.g. "${isRelationship ? 'relationships' : 'attributes'}": { "${matchedField.name}": { ... } }`;
766
- } else if (fieldMatches.length > 1) {
767
- const matchedField = fields.get(fieldMatches[0].item);
768
- const isRelationship = RELATIONSHIP_FIELD_KINDS.includes(matchedField.kind);
769
- didYouMean = ` Based on the ResourceSchema for "${type}" this field is likely one of "${fieldMatches.map(v => v.item).join('", "')}" and belongs inside of either "attributes" or "relationships", e.g. "${isRelationship ? 'relationships' : 'attributes'}": { "${matchedField.name}": { ... } }`;
770
- }
771
- }
772
- }
773
- reporter.error([...path, key], `Unrecognized ResourceObject member. The data it provides is ignored as it is not a valid {json:api} ResourceObject member.${didYouMean}`);
774
- }
775
- }
776
- }
777
-
778
- // if we have a schema, validate the individual attributes and relationships
779
- const type = 'type' in resource ? resource.type : undefined;
780
- if (type && reporter.schema.hasResource({
781
- type
782
- })) {
783
- if ('attributes' in resource) {
784
- validateResourceAttributes(reporter, type, resource.attributes, [...path, 'attributes']);
785
- }
786
- if ('relationships' in resource) {
787
- validateResourceRelationships(reporter, type, resource.relationships, [...path, 'relationships']);
788
- }
789
- }
790
- }
791
- function validateResourceAttributes(reporter, type, resource, path) {
792
- const fields = reporter.schema.fields({
793
- type
794
- });
795
- const cacheFields = reporter.schema.cacheFields?.({
796
- type
797
- }) ?? fields;
798
- for (const [key] of Object.entries(resource)) {
799
- const field = getRemoteField(cacheFields, key);
800
- const actualField = cacheFields.get(key);
801
- if (!field && actualField) {
802
- reporter.warn([...path, key], `Expected the ${actualField.kind} field "${key}" to not have its own data in the ResourceObject's attributes. Likely this field should either not be returned in this payload or the field definition should be updated in the schema.`);
803
- } else if (!field) {
804
- if (key.includes(':')) {
805
- const extensionName = key.split(':')[0];
806
- if (reporter.hasExtension(extensionName)) {
807
- const extension = reporter.getExtension(extensionName);
808
- extension(reporter, [...path, key]);
809
- } else {
810
- reporter.warn([...path, key], `Unrecognized extension ${extensionName}. The data provided by "${key}" will be ignored as it is not a valid {json:api} ResourceObject member`);
811
- }
812
- } else {
813
- const method = reporter.strict.unknownAttribute ? 'error' : 'warn';
814
-
815
- // TODO @runspired when we check for fuzzy matches we can adjust the message to say
816
- // whether the expected field is an attribute or a relationship
817
- const potentialFields = reporter.searchFields(type, key);
818
- reporter[method]([...path, key], `Unrecognized attribute. The data it provides is ignored as it is not part of the ResourceSchema for "${type}".${logPotentialMatches(potentialFields, 'field')}`);
819
- }
820
- } else if (field && RELATIONSHIP_FIELD_KINDS.includes(field.kind)) {
821
- reporter.error([...path, key], `Expected the "${key}" field to be in "relationships" as it has kind "${field.kind}", but received data for it in "attributes".`);
822
- }
823
- }
824
-
825
- // TODO @runspired we should also deep-validate the field value
826
- // TODO @runspired we should validate that field values are valid JSON and not instances
827
- }
828
- function validateResourceRelationships(reporter, type, resource, path) {
829
- const schema = reporter.schema.fields({
830
- type
831
- });
832
- for (const [key] of Object.entries(resource)) {
833
- const field = getRemoteField(schema, key);
834
- const actualField = schema.get(key);
835
- if (!field && actualField) {
836
- reporter.warn([...path, key], `Expected the ${actualField.kind} field "${key}" to not have its own data in the ResourceObject's relationships. Likely this field should either not be returned in this payload or the field definition should be updated in the schema.`);
837
- } else if (!field) {
838
- if (key.includes(':')) {
839
- const extensionName = key.split(':')[0];
840
- if (reporter.hasExtension(extensionName)) {
841
- const extension = reporter.getExtension(extensionName);
842
- extension(reporter, [...path, key]);
843
- } else {
844
- reporter.warn([...path, key], `Unrecognized extension ${extensionName}. The data provided by "${key}" will be ignored as it is not a valid {json:api} ResourceObject member`);
845
- }
846
- } else {
847
- const method = reporter.strict.unknownRelationship ? 'error' : 'warn';
848
-
849
- // TODO @runspired when we check for fuzzy matches we can adjust the message to say
850
- // whether the expected field is an attribute or a relationship
851
- const potentialFields = reporter.searchFields(type, key);
852
- reporter[method]([...path, key], `Unrecognized relationship. The data it provides is ignored as it is not part of the ResourceSchema for "${type}".${logPotentialMatches(potentialFields, 'field')}`);
853
- }
854
- } else if (field && !RELATIONSHIP_FIELD_KINDS.includes(field.kind)) {
855
- reporter.error([...path, key], `Expected the "${key}" field to be in "attributes" as it has kind "${field.kind}", but received data for it in "relationships".`);
856
- }
857
- }
858
-
859
- // TODO @runspired we should also deep-validate the relationship payload
860
- // TODO @runspired we should validate linksMode requirements for both Polaris and Legacy modes
861
- // TODO @runspired we should warn if the discovered resource-type in a relationship is the abstract
862
- // type instead of the concrete type.
863
- }
864
-
865
- function validateDocument(capabilities, doc) {
866
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
867
- if (!test) {
868
- throw new Error(`Expected a JSON:API Document as the content provided to the cache, received ${typeof doc.content}`);
869
- }
870
- })(doc instanceof Error || typeof doc.content === 'object' && doc.content !== null) : {};
871
-
872
- // if the feature is not active and the payloads are not being logged
873
- // we don't need to validate the payloads
874
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.features.JSON_API_CACHE_VALIDATION_ERRORS)) {
875
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
876
- if (!(getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE)) {
877
- return;
878
- }
879
- }
880
- }
881
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
882
- if (!(getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE)) {
883
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.features.JSON_API_CACHE_VALIDATION_ERRORS)) {
884
- return;
885
- }
886
- }
887
- }
888
- if (isErrorDocument(doc)) {
889
- return; // return validateErrorDocument(reporter, doc);
890
- } else if (isMetaDocument(doc)) {
891
- return; // return validateMetaDocument(reporter, doc);
892
- } else if (isPushedDocument(doc)) {
893
- return; // return validatePushedDocument(reporter, doc);
894
- }
895
- const reporter = new Reporter(capabilities, doc);
896
- return validateResourceDocument(reporter, doc);
897
- }
898
-
899
- // function validateErrorDocument(reporter: Reporter, doc: StructuredErrorDocument) {}
900
-
901
- // function validateMetaDocument(reporter: Reporter, doc: StructuredDataDocument<ResourceMetaDocument>) {}
902
-
903
- // function validatePushedDocument(reporter: Reporter, doc: StructuredDataDocument<ResourceDocument>) {}
904
-
905
- function validateResourceDocument(reporter, doc) {
906
- validateTopLevelDocumentMembers(reporter, doc.content);
907
- validateLinks(reporter, doc.content, 'data' in doc.content && Array.isArray(doc.content?.data) ? 'collection-document' : 'resource-document');
908
- validateDocumentResources(reporter, doc.content);
909
-
910
- // TODO @runspired - validateMeta on document
911
- // TODO @runspired - validateMeta on resource
912
- // TODO @runspired - validateMeta on resource relationships
913
- // TODO @runspired - validate no-meta on resource identifiers
914
- //
915
- // ---------------------------------
916
- // super-strict-mode
917
- //
918
- // TODO @runspired - validate that all referenced resource identifiers are present in the document (full linkage)
919
- // TODO @runspired - validate that all included resources have a path back to `data` (full linkage)
920
- //
921
- // ---------------------------------
922
- // nice-to-haves
923
- //
924
- // TODO @runspired - validate links objects more thoroughly for spec props we don't use
925
- // TODO @runspired - validate request includes are in fact included
926
- // TODO @runspired - validate request fields are in fact present
927
- // TODO @runspired - MAYBE validate request sort is in fact sorted? (useful for catching Mocking bugs)
928
- // TODO @runspired - MAYBE validate request pagination is in fact paginated? (useful for catching Mocking bugs)
929
-
930
- reporter.report();
931
- }
932
12
 
933
13
  function isImplicit(relationship) {
934
14
  return relationship.definition.isImplicit;
@@ -1051,9 +131,6 @@ class JSONAPICache {
1051
131
  */
1052
132
 
1053
133
  put(doc) {
1054
- if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
1055
- validateDocument(this._capabilities, doc);
1056
- }
1057
134
  if (isErrorDocument(doc)) {
1058
135
  return this._putDocument(doc, undefined, undefined);
1059
136
  } else if (isMetaDocument(doc)) {
@@ -1065,54 +142,6 @@ class JSONAPICache {
1065
142
  const {
1066
143
  cacheKeyManager
1067
144
  } = this._capabilities;
1068
- if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
1069
- validateDocumentFields(this._capabilities.schema, jsonApiDoc);
1070
- }
1071
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1072
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1073
- const Counts = new Map();
1074
- let totalCount = 0;
1075
- if (included) {
1076
- for (i = 0, length = included.length; i < length; i++) {
1077
- const type = included[i].type;
1078
- Counts.set(type, (Counts.get(type) || 0) + 1);
1079
- totalCount++;
1080
- }
1081
- }
1082
- if (Array.isArray(jsonApiDoc.data)) {
1083
- for (i = 0, length = jsonApiDoc.data.length; i < length; i++) {
1084
- const type = jsonApiDoc.data[i].type;
1085
- Counts.set(type, (Counts.get(type) || 0) + 1);
1086
- totalCount++;
1087
- }
1088
- } else if (jsonApiDoc.data) {
1089
- const type = jsonApiDoc.data.type;
1090
- Counts.set(type, (Counts.get(type) || 0) + 1);
1091
- totalCount++;
1092
- }
1093
- logGroup('cache', 'put', '<@document>', doc.content?.lid || doc.request?.url || 'unknown-request', `(${totalCount}) records`, '');
1094
- let str = `\tContent Counts:`;
1095
- Counts.forEach((count, type) => {
1096
- str += `\n\t\t${type}: ${count} record${count > 1 ? 's' : ''}`;
1097
- });
1098
- if (Counts.size === 0) {
1099
- str += `\t(empty)`;
1100
- }
1101
- // eslint-disable-next-line no-console
1102
- console.log(str);
1103
- // eslint-disable-next-line no-console
1104
- console.log({
1105
- lid: doc.content?.lid,
1106
- content: structuredClone(doc.content),
1107
- // we may need a specialized copy here
1108
- request: doc.request,
1109
- // structuredClone(doc.request),
1110
- response: doc.response // structuredClone(doc.response),
1111
- });
1112
- // eslint-disable-next-line no-console
1113
- console.groupEnd();
1114
- }
1115
- }
1116
145
  if (included) {
1117
146
  for (i = 0, length = included.length; i < length; i++) {
1118
147
  included[i] = putOne(this, cacheKeyManager, included[i]);
@@ -1149,16 +178,6 @@ class JSONAPICache {
1149
178
  resourceDocument.data = data;
1150
179
  }
1151
180
  if (included !== undefined) {
1152
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1153
- if (!test) {
1154
- throw new Error(`There should not be included data on an Error document`);
1155
- }
1156
- })(!isErrorDocument(doc)) : {};
1157
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1158
- if (!test) {
1159
- throw new Error(`There should not be included data on a Meta document`);
1160
- }
1161
- })(!isMetaDocument(doc)) : {};
1162
181
  resourceDocument.included = included;
1163
182
  }
1164
183
  const request = doc.request;
@@ -1175,16 +194,6 @@ class JSONAPICache {
1175
194
  if (doc.request?.op === 'findHasMany') {
1176
195
  const parentIdentifier = doc.request.options?.identifier;
1177
196
  const parentField = doc.request.options?.field;
1178
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1179
- if (!test) {
1180
- throw new Error(`Expected a hasMany field`);
1181
- }
1182
- })(parentField?.kind === 'hasMany') : {};
1183
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1184
- if (!test) {
1185
- throw new Error(`Expected a parent identifier for a findHasMany request`);
1186
- }
1187
- })(parentIdentifier && isResourceKey(parentIdentifier)) : {};
1188
197
  if (parentField && parentIdentifier) {
1189
198
  this.__graph.push({
1190
199
  op: 'updateRelationship',
@@ -1207,23 +216,12 @@ class JSONAPICache {
1207
216
  */
1208
217
  patch(op) {
1209
218
  if (Array.isArray(op)) {
1210
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1211
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1212
- logGroup('cache', 'patch', '<BATCH>', String(op.length) + ' operations', '', '');
1213
- }
1214
- }
1215
219
  assertPrivateCapabilities(this._capabilities);
1216
220
  this._capabilities._store._join(() => {
1217
221
  for (const operation of op) {
1218
222
  patchCache(this, operation);
1219
223
  }
1220
224
  });
1221
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1222
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1223
- // eslint-disable-next-line no-console
1224
- console.groupEnd();
1225
- }
1226
- }
1227
225
  } else {
1228
226
  patchCache(this, op);
1229
227
  }
@@ -1236,26 +234,7 @@ class JSONAPICache {
1236
234
  * @public
1237
235
  */
1238
236
  mutate(mutation) {
1239
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1240
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1241
- logGroup('cache', 'mutate', mutation.record.type, mutation.record.lid, mutation.field, mutation.op);
1242
- try {
1243
- const _data = JSON.parse(JSON.stringify(mutation));
1244
- // eslint-disable-next-line no-console
1245
- console.log(_data);
1246
- } catch {
1247
- // eslint-disable-next-line no-console
1248
- console.log(mutation);
1249
- }
1250
- }
1251
- }
1252
237
  this.__graph.update(mutation, false);
1253
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1254
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1255
- // eslint-disable-next-line no-console
1256
- console.groupEnd();
1257
- }
1258
- }
1259
238
  }
1260
239
 
1261
240
  /**
@@ -1551,18 +530,6 @@ class JSONAPICache {
1551
530
  * @public
1552
531
  */
1553
532
  clientDidCreate(identifier, options) {
1554
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1555
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1556
- try {
1557
- const _data = options ? JSON.parse(JSON.stringify(options)) : options;
1558
- // eslint-disable-next-line no-console
1559
- console.log(`WarpDrive | Mutation - clientDidCreate ${identifier.lid}`, _data);
1560
- } catch {
1561
- // eslint-disable-next-line no-console
1562
- console.log(`WarpDrive | Mutation - clientDidCreate ${identifier.lid}`, options);
1563
- }
1564
- }
1565
- }
1566
533
  const cached = this._createCache(identifier);
1567
534
  cached.isNew = true;
1568
535
  const createOptions = {};
@@ -1645,28 +612,9 @@ class JSONAPICache {
1645
612
  const payload = result ? result.content : null;
1646
613
  const operation = result?.request?.op ?? null;
1647
614
  const data = payload && payload.data;
1648
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
1649
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
1650
- try {
1651
- const payloadCopy = payload ? JSON.parse(JSON.stringify(payload)) : payload;
1652
- // eslint-disable-next-line no-console
1653
- console.log(`WarpDrive | Payload - ${operation}`, payloadCopy);
1654
- } catch {
1655
- // eslint-disable-next-line no-console
1656
- console.log(`WarpDrive | Payload - ${operation}`, payload);
1657
- }
1658
- }
1659
- }
1660
615
  const responseIsCollection = Array.isArray(data);
1661
616
  const hasMultipleIdentifiers = Array.isArray(committedIdentifier) && committedIdentifier.length > 1;
1662
617
  if (Array.isArray(committedIdentifier)) {
1663
- // if we get back an array of primary data, we treat each
1664
- // entry as a separate commit for each identifier
1665
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1666
- if (!test) {
1667
- throw new Error(`Expected the array of primary data to match the array of committed identifiers`);
1668
- }
1669
- })(!hasMultipleIdentifiers || !responseIsCollection || data.length === committedIdentifier.length) : {};
1670
618
  if (responseIsCollection) {
1671
619
  for (let i = 0; i < committedIdentifier.length; i++) {
1672
620
  const identifier = committedIdentifier[i];
@@ -1811,11 +759,6 @@ class JSONAPICache {
1811
759
  if (isSimplePath) {
1812
760
  const attribute = attr;
1813
761
  const cached = this.__peek(identifier, true);
1814
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1815
- if (!test) {
1816
- throw new Error(`Cannot retrieve attributes for identifier ${String(identifier)} as it is not present in the cache`);
1817
- }
1818
- })(cached) : {};
1819
762
 
1820
763
  // in Prod we try to recover when accessing something that
1821
764
  // doesn't exist
@@ -1880,11 +823,6 @@ class JSONAPICache {
1880
823
  if (isSimplePath) {
1881
824
  const attribute = attr;
1882
825
  const cached = this.__peek(identifier, true);
1883
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1884
- if (!test) {
1885
- throw new Error(`Cannot retrieve remote attributes for identifier ${String(identifier)} as it is not present in the cache`);
1886
- }
1887
- })(cached) : {};
1888
826
 
1889
827
  // in Prod we try to recover when accessing something that
1890
828
  // doesn't exist
@@ -1936,12 +874,6 @@ class JSONAPICache {
1936
874
  * @public
1937
875
  */
1938
876
  setAttr(identifier, attr, value) {
1939
- // this assert works to ensure we have a non-empty string and/or a non-empty array
1940
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1941
- if (!test) {
1942
- throw new Error('setAttr must receive at least one attribute path');
1943
- }
1944
- })(attr.length > 0) : {};
1945
877
  const isSimplePath = !Array.isArray(attr) || attr.length === 1;
1946
878
  if (Array.isArray(attr) && attr.length === 1) {
1947
879
  attr = attr[0];
@@ -2035,11 +967,6 @@ class JSONAPICache {
2035
967
  */
2036
968
  changedAttrs(identifier) {
2037
969
  const cached = this.__peek(identifier, false);
2038
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2039
- if (!test) {
2040
- throw new Error(`Cannot retrieve changed attributes for identifier ${String(identifier)} as it is not present in the cache`);
2041
- }
2042
- })(cached) : {};
2043
970
 
2044
971
  // in Prod we try to recover when accessing something that
2045
972
  // doesn't exist
@@ -2059,11 +986,6 @@ class JSONAPICache {
2059
986
  */
2060
987
  hasChangedAttrs(identifier) {
2061
988
  const cached = this.__peek(identifier, true);
2062
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2063
- if (!test) {
2064
- throw new Error(`Cannot retrieve changed attributes for identifier ${String(identifier)} as it is not present in the cache`);
2065
- }
2066
- })(cached) : {};
2067
989
 
2068
990
  // in Prod we try to recover when accessing something that
2069
991
  // doesn't exist
@@ -2274,11 +1196,6 @@ class JSONAPICache {
2274
1196
  * @internal
2275
1197
  */
2276
1198
  _createCache(identifier) {
2277
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2278
- if (!test) {
2279
- throw new Error(`Expected no resource data to yet exist in the cache`);
2280
- }
2281
- })(!this.__cache.has(identifier)) : {};
2282
1199
  const cache = makeCache();
2283
1200
  this.__cache.set(identifier, cache);
2284
1201
  return cache;
@@ -2306,62 +1223,22 @@ class JSONAPICache {
2306
1223
  */
2307
1224
  __peek(identifier, allowDestroyed) {
2308
1225
  const resource = this.__safePeek(identifier, allowDestroyed);
2309
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2310
- if (!test) {
2311
- throw new Error(`Expected Cache to have a resource entry for the identifier ${String(identifier)} but none was found`);
2312
- }
2313
- })(resource) : {};
2314
1226
  return resource;
2315
1227
  }
2316
1228
  }
2317
1229
  function addResourceToDocument(cache, op) {
2318
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2319
- if (!test) {
2320
- throw new Error(`Expected field to be either 'data' or 'included'`);
2321
- }
2322
- })(op.field === 'data' || op.field === 'included') : {};
2323
1230
  const doc = cache.__documents.get(op.record.lid);
2324
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2325
- if (!test) {
2326
- throw new Error(`Expected to have a cached document on which to perform the add operation`);
2327
- }
2328
- })(doc) : {};
2329
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2330
- if (!test) {
2331
- throw new Error(`Expected to have content on the document`);
2332
- }
2333
- })(doc.content) : {};
2334
1231
  const {
2335
1232
  content
2336
1233
  } = doc;
2337
1234
  if (op.field === 'data') {
2338
1235
  let shouldNotify = false;
2339
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2340
- if (!test) {
2341
- throw new Error(`Expected to have a data property on the document`);
2342
- }
2343
- })('data' in content) : {};
2344
1236
 
2345
1237
  // if data is not an array, we set the data property directly
2346
1238
  if (!Array.isArray(content.data)) {
2347
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2348
- if (!test) {
2349
- throw new Error(`Expected to have a single record as the operation value`);
2350
- }
2351
- })(op.value && !Array.isArray(op.value)) : {};
2352
1239
  shouldNotify = content.data !== op.value;
2353
1240
  if (shouldNotify) content.data = op.value;
2354
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2355
- if (!test) {
2356
- throw new Error(`The value '${op.value.lid}' cannot be added from the data of document '${op.record.lid}' as it is already the current value '${content.data ? content.data.lid : '<null>'}'`);
2357
- }
2358
- })(shouldNotify) : {};
2359
1241
  } else {
2360
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2361
- if (!test) {
2362
- throw new Error(`Expected to have a non-null operation value`);
2363
- }
2364
- })(op.value) : {};
2365
1242
  if (Array.isArray(op.value)) {
2366
1243
  if (op.index !== undefined) {
2367
1244
  // for collections, because we allow duplicates we are always changed.
@@ -2390,26 +1267,9 @@ function addResourceToDocument(cache, op) {
2390
1267
  return;
2391
1268
  }
2392
1269
  content.included = content.included || [];
2393
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2394
- if (!test) {
2395
- throw new Error(`Expected to have a non-null operation value`);
2396
- }
2397
- })(op.value) : {};
2398
1270
  if (Array.isArray(op.value)) {
2399
- // included is not allowed to have duplicates, so we do a dirty check here
2400
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2401
- if (!test) {
2402
- throw new Error(`included should not contain duplicate members`);
2403
- }
2404
- })(new Set([...content.included, ...op.value]).size === content.included.length + op.value.length) : {};
2405
1271
  content.included = content.included.concat(op.value);
2406
1272
  } else {
2407
- // included is not allowed to have duplicates, so we do a dirty check here
2408
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2409
- if (!test) {
2410
- throw new Error(`included should not contain duplicate members`);
2411
- }
2412
- })(content.included.includes(op.value) === false) : {};
2413
1273
  content.included.push(op.value);
2414
1274
  }
2415
1275
 
@@ -2417,54 +1277,19 @@ function addResourceToDocument(cache, op) {
2417
1277
  // exposed. We should possibly consider doing so though for subscribers
2418
1278
  }
2419
1279
  function removeResourceFromDocument(cache, op) {
2420
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2421
- if (!test) {
2422
- throw new Error(`Expected field to be either 'data' or 'included'`);
2423
- }
2424
- })(op.field === 'data' || op.field === 'included') : {};
2425
1280
  const doc = cache.__documents.get(op.record.lid);
2426
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2427
- if (!test) {
2428
- throw new Error(`Expected to have a cached document on which to perform the remove operation`);
2429
- }
2430
- })(doc) : {};
2431
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2432
- if (!test) {
2433
- throw new Error(`Expected to have content on the document`);
2434
- }
2435
- })(doc.content) : {};
2436
1281
  const {
2437
1282
  content
2438
1283
  } = doc;
2439
1284
  if (op.field === 'data') {
2440
1285
  let shouldNotify = false;
2441
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2442
- if (!test) {
2443
- throw new Error(`Expected to have a data property on the document`);
2444
- }
2445
- })('data' in content) : {};
2446
1286
 
2447
1287
  // if data is not an array, we set the data property directly
2448
1288
  if (!Array.isArray(content.data)) {
2449
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2450
- if (!test) {
2451
- throw new Error(`Expected to have a single record as the operation value`);
2452
- }
2453
- })(op.value && !Array.isArray(op.value)) : {};
2454
1289
  shouldNotify = content.data === op.value;
2455
1290
  // we only remove the value if it was our existing value
2456
1291
  if (shouldNotify) content.data = null;
2457
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2458
- if (!test) {
2459
- throw new Error(`The value '${op.value.lid}' cannot be removed from the data of document '${op.record.lid}' as it is not the current value '${content.data ? content.data.lid : '<null>'}'`);
2460
- }
2461
- })(shouldNotify) : {};
2462
1292
  } else {
2463
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2464
- if (!test) {
2465
- throw new Error(`Expected to have a non-null operation value`);
2466
- }
2467
- })(op.value) : {};
2468
1293
  const toRemove = Array.isArray(op.value) ? op.value : [op.value];
2469
1294
  for (let i = 0; i < toRemove.length; i++) {
2470
1295
  const value = toRemove[i];
@@ -2472,11 +1297,6 @@ function removeResourceFromDocument(cache, op) {
2472
1297
  // in production we want to recover gracefully
2473
1298
  // so we fallback to first-index-of
2474
1299
  const index = op.index < content.data.length && content.data[op.index] === value ? op.index : content.data.indexOf(value);
2475
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2476
- if (!test) {
2477
- throw new Error(`Mismatched Index: Expected index '${op.index}' to contain the value '${value.lid}' but that value is at index '${index}'`);
2478
- }
2479
- })(op.index < content.data.length && content.data[op.index] === value) : {};
2480
1300
  if (index !== -1) {
2481
1301
  // we remove the first occurrence of the value
2482
1302
  shouldNotify = true;
@@ -2497,24 +1317,9 @@ function removeResourceFromDocument(cache, op) {
2497
1317
  if (shouldNotify) cache._capabilities.notifyChange(op.record, 'updated', null);
2498
1318
  } else {
2499
1319
  content.included = content.included || [];
2500
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2501
- if (!test) {
2502
- throw new Error(`Expected to have a non-null operation value`);
2503
- }
2504
- })(op.value) : {};
2505
1320
  const toRemove = Array.isArray(op.value) ? op.value : [op.value];
2506
1321
  for (const identifier of toRemove) {
2507
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2508
- if (!test) {
2509
- throw new Error(`attempted to remove a value from included that was not present in the included array`);
2510
- }
2511
- })(content.included.includes(identifier)) : {};
2512
1322
  const index = content.included.indexOf(identifier);
2513
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2514
- if (!test) {
2515
- throw new Error(`The value '${identifier.lid}' cannot be removed from the included of document '${op.record.lid}' as it is not present`);
2516
- }
2517
- })(index !== -1) : {};
2518
1323
  if (index !== -1) {
2519
1324
  content.included.splice(index, 1);
2520
1325
  }
@@ -2570,11 +1375,6 @@ function getDefaultValue(schema, identifier, store) {
2570
1375
  // legacy support for defaultValues that are primitives
2571
1376
  } else if (options && 'defaultValue' in options) {
2572
1377
  const defaultValue = options.defaultValue;
2573
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2574
- if (!test) {
2575
- throw new Error(`Non primitive defaultValues are not supported because they are shared between all instances. If you would like to use a complex object as a default value please provide a function that returns the complex object.`);
2576
- }
2577
- })(typeof defaultValue !== 'object' || defaultValue === null) : {};
2578
1378
  return defaultValue;
2579
1379
 
2580
1380
  // new style transforms
@@ -2723,16 +1523,6 @@ function patchLocalAttributes(cached, changedRemoteKeys) {
2723
1523
  return hasAppliedPatch;
2724
1524
  }
2725
1525
  function putOne(cache, identifiers, resource) {
2726
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2727
- if (!test) {
2728
- throw new Error(`You must include an 'id' for the resource data ${resource.type}`);
2729
- }
2730
- })(resource.id !== null && resource.id !== undefined && resource.id !== '') : {};
2731
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2732
- if (!test) {
2733
- throw new Error(`Missing Resource Type: received resource data with a type '${resource.type}' but no schema could be found with that name.`);
2734
- }
2735
- })(cache._capabilities.schema.hasResource(resource)) : {};
2736
1526
  let identifier = identifiers.peekResourceKey(resource);
2737
1527
  if (identifier) {
2738
1528
  identifier = identifiers.updateRecordIdentifier(identifier, resource);
@@ -2866,20 +1656,6 @@ function cacheUpsert(cache, identifier, data, calculateChanges) {
2866
1656
  const cached = peeked || cache._createCache(identifier);
2867
1657
  const isLoading = /*#__NOINLINE__*/_isLoading(peeked, cache._capabilities, identifier) || !recordIsLoaded(peeked);
2868
1658
  const isUpdate = /*#__NOINLINE__*/!_isEmpty(peeked) && !isLoading;
2869
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
2870
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
2871
- logGroup('cache', 'upsert', identifier.type, identifier.lid, existed ? 'merged' : 'inserted', calculateChanges ? 'has-subscription' : '');
2872
- try {
2873
- const _data = JSON.parse(JSON.stringify(data));
2874
-
2875
- // eslint-disable-next-line no-console
2876
- console.log(_data);
2877
- } catch {
2878
- // eslint-disable-next-line no-console
2879
- console.log(data);
2880
- }
2881
- }
2882
- }
2883
1659
  if (cached.isNew) {
2884
1660
  cached.isNew = false;
2885
1661
  cache._capabilities.notifyChange(identifier, 'identity', null);
@@ -2910,35 +1686,11 @@ function cacheUpsert(cache, identifier, data, calculateChanges) {
2910
1686
  if (changedKeys?.size) {
2911
1687
  notifyAttributes(cache._capabilities, identifier, changedKeys);
2912
1688
  }
2913
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
2914
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
2915
- // eslint-disable-next-line no-console
2916
- console.groupEnd();
2917
- }
2918
- }
2919
1689
  return changedKeys?.size ? Array.from(changedKeys) : undefined;
2920
1690
  }
2921
1691
  function patchCache(Cache, op) {
2922
1692
  const isRecord = isResourceKey(op.record);
2923
- const isDocument = !isRecord && isRequestKey(op.record);
2924
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2925
- if (!test) {
2926
- throw new Error(`Expected Cache.patch op.record to be a record or document identifier`);
2927
- }
2928
- })(isRecord || isDocument) : {};
2929
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
2930
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
2931
- logGroup('cache', 'patch', isRecord ? op.record.type : '<@document>', op.record.lid, op.op, 'field' in op ? op.field : op.op === 'mergeIdentifiers' ? op.value.lid : '');
2932
- try {
2933
- const _data = JSON.parse(JSON.stringify(op));
2934
- // eslint-disable-next-line no-console
2935
- console.log(_data);
2936
- } catch {
2937
- // eslint-disable-next-line no-console
2938
- console.log(op);
2939
- }
2940
- }
2941
- }
1693
+ !isRecord && isRequestKey(op.record);
2942
1694
  switch (op.op) {
2943
1695
  case 'mergeIdentifiers':
2944
1696
  {
@@ -2955,11 +1707,6 @@ function patchCache(Cache, op) {
2955
1707
  if (isRecord) {
2956
1708
  if ('field' in op) {
2957
1709
  const field = getCacheFields(Cache, op.record).get(op.field);
2958
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2959
- if (!test) {
2960
- throw new Error(`Expected ${op.field} to be a field on ${op.record.type}`);
2961
- }
2962
- })(field) : {};
2963
1710
  if (isRelationship(field)) {
2964
1711
  Cache.__graph.push(op);
2965
1712
  } else {
@@ -2974,12 +1721,6 @@ function patchCache(Cache, op) {
2974
1721
  } else {
2975
1722
  Cache.upsert(op.record, op.value, Cache._capabilities.hasRecord(op.record));
2976
1723
  }
2977
- } else {
2978
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2979
- {
2980
- throw new Error(`Update operations on documents is not supported`);
2981
- }
2982
- })() : {};
2983
1724
  }
2984
1725
  break;
2985
1726
  }
@@ -2992,11 +1733,6 @@ function patchCache(Cache, op) {
2992
1733
  Cache.upsert(op.record, op.value, Cache._capabilities.hasRecord(op.record));
2993
1734
  }
2994
1735
  } else {
2995
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2996
- if (!test) {
2997
- throw new Error(`Expected a field in the add operation`);
2998
- }
2999
- })('field' in op) : {};
3000
1736
  addResourceToDocument(Cache, op);
3001
1737
  }
3002
1738
  break;
@@ -3022,39 +1758,11 @@ function patchCache(Cache, op) {
3022
1758
  }
3023
1759
  } else {
3024
1760
  if ('field' in op) {
3025
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3026
- if (!test) {
3027
- throw new Error(`Expected a field in the remove operation`);
3028
- }
3029
- })('field' in op) : {};
3030
1761
  removeResourceFromDocument(Cache, op);
3031
- } else {
3032
- // TODO @runspired teardown associated state ... notify subscribers etc.
3033
- // This likely means that the instance cache needs to handle
3034
- // holding onto reactive documents instead of the CacheHandler
3035
- // and use a subscription to remove them.
3036
- // Cache.__documents.delete(op.record.lid);
3037
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3038
- {
3039
- throw new Error(`Removing documents from the cache is not yet supported`);
3040
- }
3041
- })() : {};
3042
1762
  }
3043
1763
  }
3044
1764
  break;
3045
1765
  }
3046
- default:
3047
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3048
- {
3049
- throw new Error(`Unhandled cache.patch operation ${op.op}`);
3050
- }
3051
- })() : {};
3052
- }
3053
- if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_CACHE)) {
3054
- if (getGlobalConfig().WarpDriveMirror.debug.LOG_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_CACHE) {
3055
- // eslint-disable-next-line no-console
3056
- console.groupEnd();
3057
- }
3058
1766
  }
3059
1767
  }
3060
1768
  function getCacheFields(cache, identifier) {
@@ -3085,13 +1793,6 @@ function commitDidError(cache, identifier, errors) {
3085
1793
  cache._capabilities.notifyChange(identifier, 'errors', null);
3086
1794
  }
3087
1795
  function didCommit(cache, committedIdentifier, data, op) {
3088
- if (!data) {
3089
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3090
- if (!test) {
3091
- throw new Error(`Your ${committedIdentifier.type} record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response.`);
3092
- }
3093
- })(committedIdentifier.id) : {};
3094
- }
3095
1796
  const {
3096
1797
  cacheKeyManager
3097
1798
  } = cache._capabilities;
@@ -3109,16 +1810,6 @@ function didCommit(cache, committedIdentifier, data, op) {
3109
1810
  cache._capabilities.notifyChange(identifier, 'removed', null);
3110
1811
  // TODO @runspired should we early exit here?
3111
1812
  }
3112
- if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
3113
- if (cached.isNew && !identifier.id && (typeof data?.id !== 'string' || data.id.length > 0)) {
3114
- const error = new Error(`Expected an id ${String(identifier)} in response ${JSON.stringify(data)}`);
3115
- //@ts-expect-error
3116
- error.isAdapterError = true;
3117
- //@ts-expect-error
3118
- error.code = 'InvalidError';
3119
- throw error;
3120
- }
3121
- }
3122
1813
  const fields = getCacheFields(cache, identifier);
3123
1814
  cached.isNew = false;
3124
1815
  let newCanonicalAttributes;
@@ -3129,36 +1820,7 @@ function didCommit(cache, committedIdentifier, data, op) {
3129
1820
  if (identifier === committedIdentifier && identifier.id !== existingId) {
3130
1821
  cache._capabilities.notifyChange(identifier, 'identity', null);
3131
1822
  }
3132
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3133
- if (!test) {
3134
- throw new Error(`Expected the ID received for the primary '${identifier.type}' resource being saved to match the current id '${cached.id}' but received '${identifier.id}'.`);
3135
- }
3136
- })(identifier.id === cached.id) : {};
3137
1823
  if (data.relationships) {
3138
- if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
3139
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE)) {
3140
- // assert against bad API behavior where a belongsTo relationship
3141
- // is saved but the return payload indicates a different final state.
3142
- fields.forEach((field, name) => {
3143
- if (field.kind === 'belongsTo') {
3144
- const relationshipData = data.relationships[name]?.data;
3145
- if (relationshipData !== undefined) {
3146
- const inFlightData = cached.inflightRelationships?.[name];
3147
- if (!inFlightData || !('data' in inFlightData)) {
3148
- return;
3149
- }
3150
- const actualData = relationshipData ? cache._capabilities.cacheKeyManager.getOrCreateRecordIdentifier(relationshipData) : null;
3151
- macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3152
- if (!test) {
3153
- throw new Error(`Expected the resource relationship '<${identifier.type}>.${name}' on ${identifier.lid} to be saved as ${inFlightData.data ? inFlightData.data.lid : '<null>'} but it was saved as ${actualData ? actualData.lid : '<null>'}`);
3154
- }
3155
- })(inFlightData.data === actualData) : {};
3156
- }
3157
- }
3158
- });
3159
- cached.inflightRelationships = null;
3160
- }
3161
- }
3162
1824
  setupRelationships(cache.__graph, fields, identifier, data);
3163
1825
  }
3164
1826
  newCanonicalAttributes = data.attributes;
@@ -3205,21 +1867,6 @@ function willCommit(cache, identifier) {
3205
1867
  cached.inflightAttrs = cached.localAttrs;
3206
1868
  }
3207
1869
  cached.localAttrs = null;
3208
- if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
3209
- if (macroCondition(!getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE)) {
3210
- // save off info about saved relationships
3211
- const fields = getCacheFields(cache, identifier);
3212
- fields.forEach((schema, name) => {
3213
- if (schema.kind === 'belongsTo') {
3214
- if (cache.__graph._isDirty(identifier, name)) {
3215
- const relationshipData = cache.__graph.getData(identifier, name);
3216
- const inFlight = cached.inflightRelationships = cached.inflightRelationships || Object.create(null);
3217
- inFlight[name] = relationshipData;
3218
- }
3219
- }
3220
- });
3221
- }
3222
- }
3223
1870
  }
3224
1871
 
3225
1872
  export { JSONAPICache };