Skip to content

Commit ecdd9f3

Browse files
runspiredgitKrystan
authored andcommitted
feat: improve debuggability of SchemaRecord, RecordArray and Identifier (#9766)
* feat: improve debuggability of SchemaRecord, RecordArray and Identifier * fix configurability * fix keys
1 parent 07fddfc commit ecdd9f3

File tree

7 files changed

+74
-14
lines changed

7 files changed

+74
-14
lines changed

guides/relationships/features/links-mode.md

+18
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ interface LinksModeRelationship {
6060
}
6161
```
6262

63+
<br>
64+
6365
### Related Links May Be Provided by Handlers
6466

6567
This means that, in order to use links mode, a relationship payload given to the cache MUST contain this related link.
@@ -68,6 +70,8 @@ If your API does not provide this link, a [request handler](https://api.emberjs.
6870

6971
Note that this approach can even work if your API requires you to send a POST request to fetch the relationship. [This blog post](https://runspired.com/2025/02/26/exploring-advanced-handlers.html) contains an overview of advanced request handling to achieve a similar aim for pagination.
7072

73+
<br>
74+
7175
### When a Relationship Is Fetched, the Related Link Is Used
7276

7377
Fetching a relationship via any of `relationship.reload`, `reference.reload`, `reference.load` or `await record.relationship` will issue a request to your handler chain. That request will look like the following:
@@ -106,12 +110,18 @@ relationship as well.
106110

107111
Sideloads (included records) are valid to include in these responses.
108112

113+
<br>
114+
115+
---
116+
109117
## Activating LinksMode
110118

111119
LinksMode is activated by adding `linksMode: true` to the relationship's options.
112120

113121
Read on below for examples and nuances specific to Model vs SchemaRecord
114122

123+
<br>
124+
115125
### For a Relationship on a Model
116126

117127
```ts
@@ -129,6 +139,8 @@ export default class User extends Model {
129139

130140
This works for both `async` and `non-async` relationships and only changes the fetching behavior of the field it is defined on. For instance, in the example above, `homeAddress` is fetched in links mode while `<Address>.residents` might still be using the legacy adapter experience.
131141

142+
<br>
143+
132144
### For a SchemaRecord in LegacyMode
133145

134146
```ts
@@ -155,6 +167,8 @@ const UserSchema = {
155167
The behavior of a relationship for a SchemaRecord in LegacyMode is always identical to that of a the same
156168
relationship defined on a Model.
157169

170+
<br>
171+
158172
### For a SchemaRecord in PolarisMode
159173

160174
```ts
@@ -199,6 +213,10 @@ In the meantime, we've enabled synchronous linksMode relationships in order to a
199213
If this limitation is too great we would recommend continuing to use `LegacyMode` until the full story for
200214
relationships in PolarisMode is shipped.
201215

216+
<br>
217+
218+
---
219+
202220
#### What To Expect from PolarisMode Relationships in the Future
203221

204222
We intend to replace `belongsTo` and `hasMany` fields with the (as yet not implemented)

guides/relationships/features/polymorphism.md

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ interface Human {
6868
}
6969
```
7070

71+
<br>
72+
73+
---
74+
7175
## How To Implement
7276

7377
WarpDrive implements polymorphism structurally: as long as records on both sides of the relationship agree
@@ -79,6 +83,8 @@ There are two polymorphic modes in WarpDrive:
7983
- **open** - any type of record can be a value (this is like our last example above of `pets: unknown[]`)
8084
- **closed** - only types of records that conform to a specific contract can be a value
8185

86+
<br>
87+
8288
### Open Polymorphism
8389

8490
To make any relationship an open polymorphic relationship, its options should include both `inverse: null` and
@@ -117,6 +123,8 @@ store.schema.registerResource({
117123
})
118124
```
119125

126+
<br>
127+
120128
### Closed/Structural Polymorphism
121129

122130
To make any relationship a closed polymorphic relationship based on structural contract, its options should
@@ -219,6 +227,10 @@ schema:
219227
}
220228
```
221229

230+
<br>
231+
232+
---
233+
222234
## Fetching Polymorphic Data
223235

224236
When working with a polymorphic relationship, the resource data for each related resource

packages/core-types/src/identifier.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ export const DEBUG_CLIENT_ORIGINATED: unique symbol = Symbol('record-originated-
99
export const DEBUG_IDENTIFIER_BUCKET: unique symbol = Symbol('identifier-bucket');
1010
export const DEBUG_STALE_CACHE_OWNER: unique symbol = Symbol('warpDriveStaleCache');
1111

12-
function ProdSymbol<T extends string>(str: T): T {
13-
return DEBUG ? (Symbol(str) as unknown as T) : str;
12+
function ProdSymbol<T extends string>(str: T, debugStr: string): T {
13+
return DEBUG ? (Symbol(debugStr) as unknown as T) : str;
1414
}
1515

1616
// also present in production
17-
export const CACHE_OWNER: '__$co' = ProdSymbol('__$co');
17+
export const CACHE_OWNER: '__$co' = ProdSymbol('__$co', 'CACHE_OWNER');
1818

1919
export type IdentifierBucket = 'record' | 'document';
2020

packages/schema-record/src/-private/record.ts

+16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { NotificationType } from '@ember-data/store';
66
import type { RelatedCollection as ManyArray } from '@ember-data/store/-private';
77
import { recordIdentifierFor, setRecordIdentifier } from '@ember-data/store/-private';
88
import { addToTransaction, entangleSignal, getSignal, type Signal, Signals } from '@ember-data/tracking/-private';
9+
import { DEBUG } from '@warp-drive/build-config/env';
910
import { assert } from '@warp-drive/build-config/macros';
1011
import type { StableRecordIdentifier } from '@warp-drive/core-types';
1112
import type { ArrayValue, ObjectValue, Value } from '@warp-drive/core-types/json/raw';
@@ -688,6 +689,21 @@ export class SchemaRecord {
688689
}
689690
);
690691

692+
if (DEBUG) {
693+
Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
694+
enumerable: false,
695+
configurable: true,
696+
get() {
697+
const data: Record<string, unknown> = {};
698+
for (const key of fields.keys()) {
699+
data[key] = proxy[key as keyof SchemaRecord];
700+
}
701+
702+
return data;
703+
},
704+
});
705+
}
706+
691707
return proxy;
692708
}
693709

packages/store/src/-private/caches/identifier-cache.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -650,16 +650,14 @@ function makeStableRecordIdentifier(
650650
if (DEBUG) {
651651
// we enforce immutability in dev
652652
// but preserve our ability to do controlled updates to the reference
653-
let wrapper: StableRecordIdentifier = {
654-
get lid() {
655-
return recordIdentifier.lid;
656-
},
653+
let wrapper = {
654+
type: recordIdentifier.type,
655+
lid: recordIdentifier.lid,
657656
get id() {
658657
return recordIdentifier.id;
659658
},
660-
get type() {
661-
return recordIdentifier.type;
662-
},
659+
} as StableRecordIdentifier;
660+
const proto = {
663661
get [CACHE_OWNER](): number | undefined {
664662
return recordIdentifier[CACHE_OWNER];
665663
},
@@ -672,23 +670,28 @@ function makeStableRecordIdentifier(
672670
set [DEBUG_STALE_CACHE_OWNER](value: number | undefined) {
673671
(recordIdentifier as StableRecordIdentifier)[DEBUG_STALE_CACHE_OWNER] = value;
674672
},
673+
get [DEBUG_CLIENT_ORIGINATED]() {
674+
return clientOriginated;
675+
},
676+
get [DEBUG_IDENTIFIER_BUCKET]() {
677+
return bucket;
678+
},
675679
};
676-
Object.defineProperty(wrapper, 'toString', {
680+
Object.defineProperty(proto, 'toString', {
677681
enumerable: false,
678682
value: () => {
679683
const { type, id, lid } = recordIdentifier;
680684
return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
681685
},
682686
});
683-
Object.defineProperty(wrapper, 'toJSON', {
687+
Object.defineProperty(proto, 'toJSON', {
684688
enumerable: false,
685689
value: () => {
686690
const { type, id, lid } = recordIdentifier;
687691
return { type, id, lid };
688692
},
689693
});
690-
wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
691-
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
694+
Object.setPrototypeOf(wrapper, proto);
692695
DEBUG_MAP.set(wrapper, recordIdentifier);
693696
wrapper = freeze(wrapper);
694697
return wrapper;

packages/store/src/-private/record-arrays/identifier-array.ts

+10
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,16 @@ export class IdentifierArray<T = unknown> {
519519
};
520520
}
521521

522+
if (DEBUG) {
523+
Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
524+
enumerable: false,
525+
configurable: true,
526+
get() {
527+
return proxy.slice();
528+
},
529+
});
530+
}
531+
522532
createArrayTags(proxy, _SIGNAL);
523533

524534
this[NOTIFY] = this[NOTIFY].bind(proxy);

pnpm-lock.yaml

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)