Skip to content

Commit d4bf8a8

Browse files
committed
feat: improve debuggability of SchemaRecord, RecordArray and Identifier
1 parent ee2c917 commit d4bf8a8

File tree

9 files changed

+83
-23
lines changed

9 files changed

+83
-23
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

+14
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,19 @@ export class SchemaRecord {
688689
}
689690
);
690691

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

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

+15-18
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,22 @@ function makeStableRecordIdentifier(
672670
set [DEBUG_STALE_CACHE_OWNER](value: number | undefined) {
673671
(recordIdentifier as StableRecordIdentifier)[DEBUG_STALE_CACHE_OWNER] = value;
674672
},
675-
};
676-
Object.defineProperty(wrapper, 'toString', {
677-
enumerable: false,
678-
value: () => {
673+
get [DEBUG_CLIENT_ORIGINATED]() {
674+
return clientOriginated;
675+
},
676+
get [DEBUG_IDENTIFIER_BUCKET]() {
677+
return bucket;
678+
},
679+
toString() {
679680
const { type, id, lid } = recordIdentifier;
680681
return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
681682
},
682-
});
683-
Object.defineProperty(wrapper, 'toJSON', {
684-
enumerable: false,
685-
value: () => {
683+
toJSON() {
686684
const { type, id, lid } = recordIdentifier;
687685
return { type, id, lid };
688686
},
689-
});
690-
wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
691-
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
687+
};
688+
Object.setPrototypeOf(wrapper, proto);
692689
DEBUG_MAP.set(wrapper, recordIdentifier);
693690
wrapper = freeze(wrapper);
694691
return wrapper;

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

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
subscribe,
1212
} from '@ember-data/tracking/-private';
1313
import { DEPRECATE_COMPUTED_CHAINS } from '@warp-drive/build-config/deprecations';
14+
import { DEBUG } from '@warp-drive/build-config/env';
1415
import { assert } from '@warp-drive/build-config/macros';
1516
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
1617
import type { LocalRelationshipOperation } from '@warp-drive/core-types/graph';
@@ -448,6 +449,14 @@ export class IdentifierArray<T = unknown> {
448449
},
449450
}) as IdentifierArray<T>;
450451

452+
if (DEBUG) {
453+
Object.defineProperty(proxy, '__SHOW_ME_THE_DATA_(debug mode only)__', {
454+
get() {
455+
return proxy.slice();
456+
},
457+
});
458+
}
459+
451460
createArrayTags(proxy, _SIGNAL);
452461

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

pnpm-lock.yaml

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

tests/example-json-api/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@glimmer/tracking": "^1.1.2",
4646
"@html-next/vertical-collection": "^4.0.2",
4747
"@types/morgan": "^1.9.9",
48+
"@warp-drive/build-config": "workspace:*",
4849
"@warp-drive/core-types": "workspace:*",
4950
"@warp-drive/internal-config": "workspace:*",
5051
"ember-auto-import": "2.10.0",

tests/warp-drive__schema-record/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"qunit-console-grouper": "^0.3.0",
7070
"qunit-dom": "^3.1.1",
7171
"silent-error": "^1.1.1",
72+
"testem": "^3.12.0",
7273
"typescript": "^5.8.2",
7374
"webpack": "^5.98.0"
7475
},

0 commit comments

Comments
 (0)