ember-data-model-fragments 7.0.3 → 8.0.2

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.
@@ -7,6 +7,7 @@ import {
7
7
  isFragment,
8
8
  setFragmentOwner,
9
9
  } from '../fragment';
10
+ import fragmentCacheFor from '../util/fragment-cache';
10
11
  import metaTypeFor from '../util/meta-type-for';
11
12
  import isInstanceOfType from '../util/instance-of-type';
12
13
 
@@ -60,57 +61,68 @@ export default function fragment(type, options) {
60
61
  options,
61
62
  };
62
63
 
63
- // eslint-disable-next-line ember/require-computed-property-dependencies -- isDestroying/isDestroyed are guards, not dependencies
64
- return computed('store.{_instanceCache,cache}', {
65
- get(key) {
66
- if (this.isDestroying || this.isDestroyed) {
67
- return null;
68
- }
69
- const identifier = recordIdentifierFor(this);
70
- const cache = this.store.cache;
71
- const fragmentIdentifier = cache.getFragment(identifier, key);
72
- if (fragmentIdentifier === null) {
73
- return null;
74
- }
75
- // Get the fragment record from the identifier
76
- return this.store._instanceCache.getRecord(fragmentIdentifier);
77
- },
78
- set(key, value) {
79
- assert(
80
- 'You must pass a fragment or null to set a fragment',
81
- value === null || isFragment(value) || typeOf(value) === 'object',
82
- );
83
- const identifier = recordIdentifierFor(this);
84
- const cache = this.store.cache;
85
- if (value === null) {
86
- cache.setDirtyFragment(identifier, key, null);
87
- return null;
88
- }
89
- if (isFragment(value)) {
64
+ // Use computed with a dependency on hasDirtyAttributes which changes on rollback
65
+ // This ensures the computed property is re-evaluated when dirty state changes
66
+ const cp = computed(
67
+ 'currentState',
68
+ 'hasDirtyAttributes',
69
+ 'isDestroyed',
70
+ 'isDestroying',
71
+ 'store.{_instanceCache,cache}',
72
+ {
73
+ get(key) {
74
+ if (this.isDestroying || this.isDestroyed) {
75
+ return null;
76
+ }
77
+ const identifier = recordIdentifierFor(this);
78
+ const cache = fragmentCacheFor(this.store);
79
+ const fragmentIdentifier = cache.getFragment(identifier, key);
80
+ if (fragmentIdentifier === null) {
81
+ return null;
82
+ }
83
+ // Get the fragment record from the identifier
84
+ return this.store._instanceCache.getRecord(fragmentIdentifier);
85
+ },
86
+ set(key, value) {
90
87
  assert(
91
- `You can only set '${type}' fragments to this property`,
92
- isInstanceOfType(this.store.modelFor(type), value),
88
+ 'You must pass a fragment or null to set a fragment',
89
+ value === null || isFragment(value) || typeOf(value) === 'object',
93
90
  );
94
- const fragmentIdentifier = recordIdentifierFor(value);
95
- setFragmentOwner(value, identifier, key);
96
- cache.setDirtyFragment(identifier, key, fragmentIdentifier);
97
- return value;
98
- }
99
- // Value is a plain object - update existing fragment or create new one
100
- const fragmentIdentifier = cache.getFragment(identifier, key);
101
- const actualType = getActualFragmentType(type, options, value, this);
102
- if (fragmentIdentifier?.type !== actualType) {
103
- // Create a new fragment
104
- const fragment = this.store.createFragment(actualType, value);
105
- const newFragmentIdentifier = recordIdentifierFor(fragment);
106
- setFragmentOwner(fragment, identifier, key);
107
- cache.setDirtyFragment(identifier, key, newFragmentIdentifier);
91
+ const identifier = recordIdentifierFor(this);
92
+ const cache = fragmentCacheFor(this.store);
93
+ if (value === null) {
94
+ cache.setDirtyFragment(identifier, key, null);
95
+ return null;
96
+ }
97
+ if (isFragment(value)) {
98
+ assert(
99
+ `You can only set '${type}' fragments to this property`,
100
+ isInstanceOfType(this.store.modelFor(type), value),
101
+ );
102
+ const fragmentIdentifier = recordIdentifierFor(value);
103
+ setFragmentOwner(value, identifier, key);
104
+ cache.setDirtyFragment(identifier, key, fragmentIdentifier);
105
+ return value;
106
+ }
107
+ // Value is a plain object - update existing fragment or create new one
108
+ const fragmentIdentifier = cache.getFragment(identifier, key);
109
+ const actualType = getActualFragmentType(type, options, value, this);
110
+ if (fragmentIdentifier?.type !== actualType) {
111
+ // Create a new fragment
112
+ const fragment = this.store.createFragment(actualType, value);
113
+ const newFragmentIdentifier = recordIdentifierFor(fragment);
114
+ setFragmentOwner(fragment, identifier, key);
115
+ cache.setDirtyFragment(identifier, key, newFragmentIdentifier);
116
+ return fragment;
117
+ }
118
+ // Update existing fragment
119
+ const fragment =
120
+ this.store._instanceCache.getRecord(fragmentIdentifier);
121
+ fragment.setProperties(value);
108
122
  return fragment;
109
- }
110
- // Update existing fragment
111
- const fragment = this.store._instanceCache.getRecord(fragmentIdentifier);
112
- fragment.setProperties(value);
113
- return fragment;
123
+ },
114
124
  },
115
- }).meta(meta);
125
+ ).meta(meta);
126
+
127
+ return cp;
116
128
  }
@@ -1,3 +1,4 @@
1
+ import { assert } from '@ember/debug';
1
2
  import JSONAPICache from '@ember-data/json-api';
2
3
  import FragmentStateManager from './fragment-state-manager';
3
4
  import FragmentRecordDataProxy from './fragment-record-data-proxy';
@@ -17,12 +18,40 @@ export default class FragmentCache {
17
18
  this.__innerCache = new JSONAPICache(storeWrapper);
18
19
  this.__fragmentState = new FragmentStateManager(storeWrapper);
19
20
  this.__recordDataProxies = new Map();
21
+ this.__storeValidated = false;
20
22
  }
21
23
 
22
24
  get store() {
23
25
  return this.__storeWrapper._store;
24
26
  }
25
27
 
28
+ /**
29
+ * Validates that the store service extends FragmentStore
30
+ * This validation is lazy - only checked when fragment functionality is first needed
31
+ */
32
+ _validateStore() {
33
+ if (this.__storeValidated) {
34
+ return;
35
+ }
36
+ this.__storeValidated = true;
37
+
38
+ const store = this.store;
39
+
40
+ // Check if the store has the required fragment methods
41
+ const hasFragmentMethods =
42
+ typeof store.createFragment === 'function' &&
43
+ typeof store.isFragment === 'function';
44
+
45
+ assert(
46
+ `ember-data-model-fragments requires your store service to extend FragmentStore.\n\n` +
47
+ `Create app/services/store.js with the following:\n\n` +
48
+ `import FragmentStore from 'ember-data-model-fragments/store';\n` +
49
+ `export default class extends FragmentStore {}\n\n` +
50
+ `See the ember-data-model-fragments documentation for more information.`,
51
+ hasFragmentMethods,
52
+ );
53
+ }
54
+
26
55
  /**
27
56
  * Get or create a FragmentRecordDataProxy for the given identifier.
28
57
  * This provides backwards compatibility with code expecting per-resource RecordData API.
@@ -41,6 +70,7 @@ export default class FragmentCache {
41
70
  // ==================
42
71
 
43
72
  getFragment(identifier, key) {
73
+ this._validateStore();
44
74
  return this.__fragmentState.getFragment(identifier, key);
45
75
  }
46
76
 
@@ -109,10 +139,136 @@ export default class FragmentCache {
109
139
  // ==================
110
140
 
111
141
  /**
112
- * Cache the response to a request
142
+ * Cache the response to a request.
143
+ *
144
+ * In ember-data 4.13+, this is the primary entry point for caching data.
145
+ * We intercept to extract fragment attributes before passing to the inner cache.
146
+ *
147
+ * The document structure is:
148
+ * {
149
+ * request: {...},
150
+ * response: {...},
151
+ * content: {
152
+ * data: { type, id, attributes, relationships } | [...],
153
+ * included: [...],
154
+ * meta: {...}
155
+ * }
156
+ * }
113
157
  */
114
158
  put(doc) {
115
- return this.__innerCache.put(doc);
159
+ // Normalize id to string to ensure consistent comparison
160
+ if (doc?.content?.data) {
161
+ if (Array.isArray(doc.content.data)) {
162
+ doc.content.data.forEach((resource) => {
163
+ if (resource.id != null && typeof resource.id !== 'string') {
164
+ resource.id = String(resource.id);
165
+ }
166
+ });
167
+ } else if (
168
+ doc.content.data.id != null &&
169
+ typeof doc.content.data.id !== 'string'
170
+ ) {
171
+ doc.content.data.id = String(doc.content.data.id);
172
+ }
173
+ }
174
+
175
+ // NEW APPROACH: Store fragment data separately, push to inner cache first, then process fragments
176
+ // This is needed because polymorphic fragment type resolution may require accessing owner attributes,
177
+ // which requires the owner record to exist in the cache first.
178
+
179
+ // Step 1: Extract fragment data from resources WITHOUT creating fragment identifiers
180
+ const fragmentDataByIdentifier = new Map();
181
+ if (doc && doc.content && doc.content.data) {
182
+ this._collectFragmentsFromDocument(doc.content, fragmentDataByIdentifier);
183
+ }
184
+
185
+ // Step 2: Push to inner cache (this creates the owner records)
186
+ const result = this.__innerCache.put(doc);
187
+
188
+ // Step 3: Now that owner records exist, push fragment data to create fragment identifiers
189
+ for (const [identifier, data] of fragmentDataByIdentifier) {
190
+ this.__fragmentState.pushFragmentData(identifier, data, false);
191
+ }
192
+
193
+ return result;
194
+ }
195
+
196
+ /**
197
+ * Collect fragment attributes from a JSON:API document WITHOUT creating fragment identifiers.
198
+ * This just stores the raw fragment data and removes fragment attributes from resources.
199
+ * Fragment identifiers will be created later after owner records are in the cache.
200
+ *
201
+ * @private
202
+ */
203
+ _collectFragmentsFromDocument(jsonApiDoc, fragmentDataByIdentifier) {
204
+ const { data, included } = jsonApiDoc;
205
+
206
+ // Handle single resource
207
+ if (data && !Array.isArray(data)) {
208
+ this._collectFragmentsFromResource(data, fragmentDataByIdentifier);
209
+ }
210
+
211
+ // Handle array of resources
212
+ if (Array.isArray(data)) {
213
+ for (const resource of data) {
214
+ this._collectFragmentsFromResource(resource, fragmentDataByIdentifier);
215
+ }
216
+ }
217
+
218
+ // Handle included resources
219
+ if (included) {
220
+ for (const resource of included) {
221
+ this._collectFragmentsFromResource(resource, fragmentDataByIdentifier);
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Collect fragment attributes from a single resource WITHOUT creating fragment identifiers.
228
+ *
229
+ * @private
230
+ */
231
+ _collectFragmentsFromResource(resource, fragmentDataByIdentifier) {
232
+ if (!resource || !resource.attributes || !resource.type) {
233
+ return;
234
+ }
235
+
236
+ // Get or create identifier for this resource
237
+ const identifier =
238
+ this.__storeWrapper.identifierCache.getOrCreateRecordIdentifier({
239
+ type: resource.type,
240
+ id: resource.id,
241
+ });
242
+
243
+ const definitions = this.__storeWrapper
244
+ .getSchemaDefinitionService()
245
+ .attributesDefinitionFor(identifier);
246
+
247
+ const fragmentData = {};
248
+ const fragmentKeys = [];
249
+
250
+ for (const [key, definition] of Object.entries(definitions)) {
251
+ const isFragment =
252
+ definition.isFragment || definition.options?.isFragment;
253
+
254
+ if (isFragment && resource.attributes[key] !== undefined) {
255
+ fragmentData[key] = resource.attributes[key];
256
+ fragmentKeys.push(key);
257
+ }
258
+ }
259
+
260
+ // Store fragment data for later processing (AFTER inner cache put)
261
+ if (fragmentKeys.length > 0) {
262
+ fragmentDataByIdentifier.set(identifier, { attributes: fragmentData });
263
+
264
+ // Clone attributes and remove fragment keys to avoid mutating original data
265
+ // This is necessary because resource.attributes may reference user-provided data
266
+ const cleanedAttributes = { ...resource.attributes };
267
+ for (const key of fragmentKeys) {
268
+ delete cleanedAttributes[key];
269
+ }
270
+ resource.attributes = cleanedAttributes;
271
+ }
116
272
  }
117
273
 
118
274
  /**
@@ -136,6 +292,10 @@ export default class FragmentCache {
136
292
  return this.__innerCache.peek(identifier);
137
293
  }
138
294
 
295
+ peekRemoteState(identifier) {
296
+ return this.__innerCache.peekRemoteState?.(identifier);
297
+ }
298
+
139
299
  /**
140
300
  * Peek the Cache for existing request data
141
301
  */
@@ -146,8 +306,13 @@ export default class FragmentCache {
146
306
  /**
147
307
  * Push resource data from a remote source into the cache.
148
308
  * Intercepts to handle fragment attributes.
309
+ *
310
+ * In ember-data 4.13+, the signature is:
311
+ * upsert(identifier, resource, hasRecord) where resource is the JSON:API resource object
312
+ * In ember-data 4.12, the signature was:
313
+ * upsert(identifier, data, calculateChanges) where data = { attributes: {...} }
149
314
  */
150
- upsert(identifier, data, calculateChanges) {
315
+ upsert(identifier, data, hasRecordOrCalculateChanges) {
151
316
  // Normalize the id to string to match identifier.id (which is always coerced to string)
152
317
  // This ensures cached.id matches identifier.id type when didCommit compares them
153
318
  if (data.id != null && typeof data.id !== 'string') {
@@ -164,7 +329,9 @@ export default class FragmentCache {
164
329
  .attributesDefinitionFor(identifier);
165
330
 
166
331
  for (const [key, definition] of Object.entries(definitions)) {
167
- if (definition.isFragment && data.attributes[key] !== undefined) {
332
+ const isFragment =
333
+ definition.isFragment || definition.options?.isFragment;
334
+ if (isFragment && data.attributes[key] !== undefined) {
168
335
  fragmentData[key] = data.attributes[key];
169
336
  fragmentAttributeKeys.push(key);
170
337
  }
@@ -187,7 +354,7 @@ export default class FragmentCache {
187
354
  const changedKeys = this.__innerCache.upsert(
188
355
  identifier,
189
356
  data,
190
- calculateChanges,
357
+ hasRecordOrCalculateChanges,
191
358
  );
192
359
 
193
360
  // Handle fragment attributes
@@ -195,9 +362,9 @@ export default class FragmentCache {
195
362
  const changedFragmentKeys = this.__fragmentState.pushFragmentData(
196
363
  identifier,
197
364
  { attributes: fragmentData },
198
- calculateChanges,
365
+ hasRecordOrCalculateChanges,
199
366
  );
200
- if (calculateChanges && changedFragmentKeys?.length) {
367
+ if (hasRecordOrCalculateChanges && changedFragmentKeys?.length) {
201
368
  return [...(changedKeys || []), ...changedFragmentKeys];
202
369
  }
203
370
  }
@@ -209,6 +376,91 @@ export default class FragmentCache {
209
376
  * Signal to the cache that a new record has been instantiated on the client
210
377
  */
211
378
  clientDidCreate(identifier, options) {
379
+ // Extract fragment attributes from options before passing to inner cache
380
+ if (options) {
381
+ const definitions = this.__storeWrapper
382
+ .getSchemaDefinitionService()
383
+ .attributesDefinitionFor(identifier);
384
+
385
+ // Raw fragment data (plain objects / arrays of plain objects) flows
386
+ // through pushFragmentData -> behavior.pushData which only accepts raw
387
+ // canonical data. Fragment-instance values must be adopted directly
388
+ // (mirroring behavior.getDefaultValue's handling of fragment defaults)
389
+ // so that consumers can pass `store.createFragment(...)` results into
390
+ // `store.createRecord(type, { fragmentKey: fragment })` without hitting
391
+ // "Fragment canonical value must be an object or null".
392
+ const fragmentData = {};
393
+ const fragmentInstanceData = {};
394
+ const regularOptions = {};
395
+ let hasFragmentData = false;
396
+ let hasFragmentInstanceData = false;
397
+
398
+ for (const [key, value] of Object.entries(options)) {
399
+ const definition = definitions[key];
400
+ const isFragmentAttr =
401
+ definition?.isFragment || definition?.options?.isFragment;
402
+
403
+ if (isFragmentAttr && value !== undefined) {
404
+ if (this.__fragmentState.valueContainsFragmentInstance(value)) {
405
+ fragmentInstanceData[key] = value;
406
+ hasFragmentInstanceData = true;
407
+ } else {
408
+ fragmentData[key] = value;
409
+ hasFragmentData = true;
410
+ }
411
+ } else {
412
+ regularOptions[key] = value;
413
+ }
414
+ }
415
+
416
+ // IMPORTANT: Create the inner cache entry first, so the owner's attributes
417
+ // are available when fragment typeKey functions try to access them
418
+ const result = this.__innerCache.clientDidCreate(
419
+ identifier,
420
+ regularOptions,
421
+ );
422
+
423
+ // Adopt fragment-instance values directly into the canonical fragment
424
+ // data map. This sets up ownership on each adopted fragment and avoids
425
+ // the raw-object-only pushData assertion path.
426
+ if (hasFragmentInstanceData) {
427
+ for (const [key, value] of Object.entries(fragmentInstanceData)) {
428
+ const adopted = this.__fragmentState.adoptFragmentForKey(
429
+ identifier,
430
+ key,
431
+ value,
432
+ );
433
+ if (adopted !== undefined) {
434
+ this.__fragmentState.setCanonicalFragmentValue(
435
+ identifier,
436
+ key,
437
+ adopted,
438
+ );
439
+ } else {
440
+ // Defensive fallback: shouldn't happen because
441
+ // _valueContainsFragmentInstance only flagged values that
442
+ // adoptFragmentForKey can handle, but if it ever does, route
443
+ // through the raw-object path as a last resort.
444
+ fragmentData[key] = value;
445
+ hasFragmentData = true;
446
+ }
447
+ }
448
+ }
449
+
450
+ // Push remaining raw fragment data (plain objects) through the normal
451
+ // canonical-data path.
452
+ if (hasFragmentData) {
453
+ this.__fragmentState.pushFragmentData(
454
+ identifier,
455
+ { attributes: fragmentData },
456
+ false,
457
+ false,
458
+ );
459
+ }
460
+
461
+ return result;
462
+ }
463
+
212
464
  return this.__innerCache.clientDidCreate(identifier, options);
213
465
  }
214
466
 
@@ -263,7 +515,9 @@ export default class FragmentCache {
263
515
  fragmentData = { attributes: {} };
264
516
 
265
517
  for (const [key, definition] of Object.entries(definitions)) {
266
- if (definition.isFragment && attributes[key] !== undefined) {
518
+ const isFragment =
519
+ definition.isFragment || definition.options?.isFragment;
520
+ if (isFragment && attributes[key] !== undefined) {
267
521
  fragmentData.attributes[key] = attributes[key];
268
522
  }
269
523
  }
@@ -315,7 +569,13 @@ export default class FragmentCache {
315
569
  .attributesDefinitionFor(identifier);
316
570
  const definition = definitions[attr];
317
571
 
318
- if (definition?.isFragment) {
572
+ // Check for fragment attribute - support both metadata formats:
573
+ // - Direct: definition.isFragment (ember-data 4.12 or original metadata)
574
+ // - Transformed: definition.options?.isFragment (from FragmentSchemaService in 4.13)
575
+ const isFragmentAttr =
576
+ definition?.isFragment || definition?.options?.isFragment;
577
+
578
+ if (isFragmentAttr) {
319
579
  // Fragment attributes are handled by fragment state manager
320
580
  // getFragment returns identifier(s), we need to convert to Fragment instance(s)
321
581
  const fragmentValue = this.__fragmentState.getFragment(identifier, attr);
@@ -324,29 +584,45 @@ export default class FragmentCache {
324
584
  return fragmentValue;
325
585
  }
326
586
 
587
+ // Get the fragment kind from the appropriate location:
588
+ // - Direct: definition.kind (original metadata)
589
+ // - Transformed: definition.options?.fragmentKind (from FragmentSchemaService)
590
+ const fragmentKind = definition.options?.fragmentKind || definition.kind;
591
+
327
592
  // For single fragments, convert identifier to Fragment instance
328
- if (definition.kind === 'fragment') {
593
+ if (fragmentKind === 'fragment') {
329
594
  if (fragmentValue.lid) {
330
595
  // It's an identifier, get the record instance
331
- return this.store._instanceCache.getRecord(fragmentValue);
596
+ const record = this.store._instanceCache.getRecord(fragmentValue);
597
+ return record;
332
598
  }
333
599
  return fragmentValue;
334
600
  }
335
601
 
336
- // For fragment arrays, convert array of identifiers to Fragment instances
337
- if (
338
- definition.kind === 'fragment-array' &&
339
- Array.isArray(fragmentValue)
340
- ) {
341
- return fragmentValue.map((item) => {
342
- if (item?.lid) {
343
- return this.store._instanceCache.getRecord(item);
344
- }
345
- return item;
346
- });
602
+ // For fragment arrays and primitive arrays, return the wrapper object
603
+ // This is needed for Snapshot._attributes which expects objects with _createSnapshot
604
+ if (fragmentKind === 'fragment-array' || fragmentKind === 'array') {
605
+ // Get the cached wrapper if it exists
606
+ let arrayWrapper = this.getFragmentArrayCache(identifier, attr);
607
+ if (arrayWrapper) {
608
+ return arrayWrapper;
609
+ }
610
+ // If no wrapper exists yet, convert identifiers to Fragment instances
611
+ // so ext.js patch can call _createSnapshot on each fragment
612
+ if (fragmentKind === 'fragment-array' && Array.isArray(fragmentValue)) {
613
+ const fragments = fragmentValue.map((item) => {
614
+ if (item?.lid) {
615
+ return this.store._instanceCache.getRecord(item);
616
+ }
617
+ return item;
618
+ });
619
+ return fragments;
620
+ }
621
+ // For primitive arrays, return as-is
622
+ return fragmentValue;
347
623
  }
348
624
 
349
- // For primitive arrays, return as-is
625
+ // For single fragment type that fell through, return as-is
350
626
  return fragmentValue;
351
627
  }
352
628
 
@@ -362,7 +638,10 @@ export default class FragmentCache {
362
638
  .attributesDefinitionFor(identifier);
363
639
  const definition = definitions[attr];
364
640
 
365
- if (definition?.isFragment) {
641
+ const isFragmentAttr =
642
+ definition?.isFragment || definition?.options?.isFragment;
643
+
644
+ if (isFragmentAttr) {
366
645
  return this.__fragmentState.setDirtyFragment(identifier, attr, value);
367
646
  }
368
647
 
@@ -431,6 +710,22 @@ export default class FragmentCache {
431
710
  return this.__innerCache.getRelationship(identifier, field);
432
711
  }
433
712
 
713
+ getRemoteRelationship(identifier, field) {
714
+ return this.__innerCache.getRemoteRelationship?.(identifier, field);
715
+ }
716
+
717
+ changedRelationships(identifier) {
718
+ return this.__innerCache.changedRelationships?.(identifier);
719
+ }
720
+
721
+ hasChangedRelationships(identifier) {
722
+ return this.__innerCache.hasChangedRelationships?.(identifier) || false;
723
+ }
724
+
725
+ rollbackRelationships(identifier) {
726
+ return this.__innerCache.rollbackRelationships?.(identifier);
727
+ }
728
+
434
729
  /**
435
730
  * Update the cache state for the given resource to be marked as locally deleted
436
731
  */
@@ -445,6 +740,10 @@ export default class FragmentCache {
445
740
  return this.__innerCache.getErrors(identifier);
446
741
  }
447
742
 
743
+ getRemoteAttr(identifier, attr) {
744
+ return this.__innerCache.getRemoteAttr?.(identifier, attr);
745
+ }
746
+
448
747
  /**
449
748
  * Query the cache for whether a given resource has any available data
450
749
  */
@@ -125,7 +125,9 @@ export default class FragmentRecordDataProxy {
125
125
  .getSchemaDefinitionService()
126
126
  .attributesDefinitionFor(this.identifier);
127
127
  for (const [key, definition] of Object.entries(definitions)) {
128
- if (!definition.isFragment) {
128
+ const isFragmentAttr =
129
+ definition.isFragment || definition.options?.isFragment;
130
+ if (!isFragmentAttr) {
129
131
  regularState[key] = this.__cache.getAttr(this.identifier, key);
130
132
  }
131
133
  }