yayson 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +229 -82
- package/build/legacy.cjs +84 -31
- package/build/legacy.d.cts +12 -4
- package/build/legacy.d.cts.map +1 -1
- package/build/legacy.d.mts +12 -4
- package/build/legacy.d.mts.map +1 -1
- package/build/legacy.mjs +84 -31
- package/build/legacy.mjs.map +1 -1
- package/build/{types-BsfPiPEw.d.mts → types-Do2flKZX.d.mts} +9 -2
- package/build/{types-BsfPiPEw.d.mts.map → types-Do2flKZX.d.mts.map} +1 -1
- package/build/{types-DbMIe8E2.d.cts → types-NiKV-lj-.d.cts} +9 -2
- package/build/{types-DbMIe8E2.d.cts.map → types-NiKV-lj-.d.cts.map} +1 -1
- package/build/utils.d.cts +1 -1
- package/build/utils.d.mts +1 -1
- package/build/{yayson-BKEyEcGk.d.cts → yayson-3UYKq2H5.d.cts} +14 -2
- package/build/yayson-3UYKq2H5.d.cts.map +1 -0
- package/build/{yayson-CRbukIqr.d.mts → yayson-Ce5uGpgU.d.mts} +14 -2
- package/build/yayson-Ce5uGpgU.d.mts.map +1 -0
- package/build/{yayson-BJv2Z0Z6.mjs → yayson-CwZg5FNt.mjs} +95 -34
- package/build/yayson-CwZg5FNt.mjs.map +1 -0
- package/build/{yayson-CUfrxK9d.cjs → yayson-l2JKseMH.cjs} +94 -33
- package/build/yayson.cjs +1 -1
- package/build/yayson.d.cts +2 -2
- package/build/yayson.d.mts +2 -2
- package/build/yayson.mjs +1 -1
- package/package.json +1 -1
- package/build/yayson-BJv2Z0Z6.mjs.map +0 -1
- package/build/yayson-BKEyEcGk.d.cts.map +0 -1
- package/build/yayson-CRbukIqr.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A library for serializing and reading [JSON API](http://jsonapi.org) data in JavaScript.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Zero dependencies
|
|
6
|
+
- Full TypeScript support with type inference from schema registries
|
|
7
|
+
- Optional runtime validation with [Zod](https://github.com/colinhacks/zod) or any compatible library
|
|
8
|
+
- Dual ESM/CJS package — works in browsers and Node.js 20+
|
|
6
9
|
|
|
7
10
|
[](https://nodei.co/npm/yayson/)
|
|
8
11
|
|
|
@@ -12,85 +15,6 @@ YAYSON supports both ESM and CommonJS, has zero dependencies, and works in the b
|
|
|
12
15
|
npm install yayson
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
## Upgrading from 3.x
|
|
16
|
-
|
|
17
|
-
Version 4.x is a full TypeScript rewrite with several new features and breaking changes.
|
|
18
|
-
|
|
19
|
-
### New features
|
|
20
|
-
|
|
21
|
-
- **TypeScript support** — Full type definitions included, with type inference from schema registries
|
|
22
|
-
- **Schema validation** — Optional runtime validation using [Zod](https://github.com/colinhacks/zod) or any compatible library with `parse()`/`safeParse()` methods
|
|
23
|
-
- **Dual ESM/CJS package** — Native ES module support alongside CommonJS
|
|
24
|
-
- **`yayson/utils` entry point** — Helper functions (`getType`, `getLinks`, `getMeta`, `getRelationshipLinks`, `getRelationshipMeta`) for reading model metadata
|
|
25
|
-
- **`retrieveAll(type, data)`** — Sync data and return only models of a specific type
|
|
26
|
-
- **`syncAll()`** — Always returns an array (useful when you always want array behavior)
|
|
27
|
-
- **`fields` property** — Limit which attributes a Presenter includes in its output
|
|
28
|
-
|
|
29
|
-
### Breaking changes
|
|
30
|
-
|
|
31
|
-
#### Node.js version
|
|
32
|
-
|
|
33
|
-
Node.js 20+ is now required (was 14+).
|
|
34
|
-
|
|
35
|
-
#### Metadata uses Symbols instead of plain properties
|
|
36
|
-
|
|
37
|
-
In 3.x, resource type, links, and meta were stored as plain properties on models (`model.type`, `model.links`, `model.meta`). Relationship metadata used `model._links` and `model._meta`.
|
|
38
|
-
|
|
39
|
-
In 4.x, these use Symbol keys to avoid collisions with your data. Use the helpers from `yayson/utils`:
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
// 3.x
|
|
43
|
-
model.type
|
|
44
|
-
model.links
|
|
45
|
-
model.meta
|
|
46
|
-
relatedModel._links
|
|
47
|
-
relatedModel._meta
|
|
48
|
-
|
|
49
|
-
// 4.x
|
|
50
|
-
import { getType, getLinks, getMeta, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'
|
|
51
|
-
getType(model)
|
|
52
|
-
getLinks(model)
|
|
53
|
-
getMeta(model)
|
|
54
|
-
getRelationshipLinks(relatedModel)
|
|
55
|
-
getRelationshipMeta(relatedModel)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
#### `sync()` restores 3.x behavior
|
|
59
|
-
|
|
60
|
-
`sync()` now matches 3.x behavior: single resources return a model directly, collections return an array. If you always want an array, use `syncAll()`.
|
|
61
|
-
|
|
62
|
-
```javascript
|
|
63
|
-
// Single resource — returns model directly
|
|
64
|
-
const event = store.sync({ data: { type: 'events', id: '1', attributes: { name: 'Demo' } } })
|
|
65
|
-
event.name // 'Demo'
|
|
66
|
-
|
|
67
|
-
// Collection — returns array
|
|
68
|
-
const events = store.sync({ data: [{ type: 'events', id: '1', attributes: { name: 'Demo' } }] })
|
|
69
|
-
events.length // 1
|
|
70
|
-
|
|
71
|
-
// Always returns array
|
|
72
|
-
const events = store.syncAll(data)
|
|
73
|
-
|
|
74
|
-
// retrieve/retrieveAll also available
|
|
75
|
-
const event = store.retrieve('events', data)
|
|
76
|
-
const events = store.retrieveAll('events', data)
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
#### Document-level meta uses Symbols
|
|
80
|
-
|
|
81
|
-
In 3.x, document-level metadata was stored as `result.meta`. In 4.x, it uses a Symbol key:
|
|
82
|
-
|
|
83
|
-
```javascript
|
|
84
|
-
// 3.x
|
|
85
|
-
const result = store.sync(data)
|
|
86
|
-
result.meta // { total: 100 }
|
|
87
|
-
|
|
88
|
-
// 4.x
|
|
89
|
-
import { META } from 'yayson/utils'
|
|
90
|
-
const result = store.sync(data)
|
|
91
|
-
result[META] // { total: 100 }
|
|
92
|
-
```
|
|
93
|
-
|
|
94
18
|
## Presenting data
|
|
95
19
|
|
|
96
20
|
A basic `Presenter` can look like this:
|
|
@@ -256,6 +180,49 @@ This would produce:
|
|
|
256
180
|
|
|
257
181
|
Both `meta` and `links` are optional and add top-level properties to the JSON API document.
|
|
258
182
|
|
|
183
|
+
### Creating payloads with payload()
|
|
184
|
+
|
|
185
|
+
Use `payload()` to serialize a single resource for create or update API requests. Unlike `render()`, it omits `included` resources — only the primary resource with relationship linkage is produced:
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
class WheelPresenter extends Presenter {
|
|
189
|
+
static type = 'wheels'
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class BikePresenter extends Presenter {
|
|
193
|
+
static type = 'bikes'
|
|
194
|
+
relationships() {
|
|
195
|
+
return { wheels: WheelPresenter }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Create (no id)
|
|
200
|
+
BikePresenter.payload({ name: 'Monark', wheels: [{ id: 1 }, { id: 2 }] })
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This would produce:
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
{
|
|
207
|
+
data: {
|
|
208
|
+
type: 'bikes',
|
|
209
|
+
attributes: {
|
|
210
|
+
name: 'Monark'
|
|
211
|
+
},
|
|
212
|
+
relationships: {
|
|
213
|
+
wheels: {
|
|
214
|
+
data: [
|
|
215
|
+
{ type: 'wheels', id: '1' },
|
|
216
|
+
{ type: 'wheels', id: '2' }
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
For updates, include an `id` and it will appear in the output. `payload()` also accepts `meta` and `links` options like `render()`. It throws on arrays and null — use `render()` for collections.
|
|
225
|
+
|
|
259
226
|
## Parsing data
|
|
260
227
|
|
|
261
228
|
You can use a `Store` like this:
|
|
@@ -266,7 +233,7 @@ const { Store } = yayson()
|
|
|
266
233
|
const store = new Store()
|
|
267
234
|
|
|
268
235
|
const data = await adapter.get({ path: '/events/' + id })
|
|
269
|
-
const event = store.sync(data) // single resource
|
|
236
|
+
const event = store.sync(data) // single resource -> model directly
|
|
270
237
|
```
|
|
271
238
|
|
|
272
239
|
The `sync()` method returns a single model for single resources and an array for collections. Use `syncAll()` if you always want an array.
|
|
@@ -303,6 +270,55 @@ const events = store.retrieveAll('events', response)
|
|
|
303
270
|
// events.length === 2
|
|
304
271
|
```
|
|
305
272
|
|
|
273
|
+
### Building models from create payloads
|
|
274
|
+
|
|
275
|
+
Use `build()` to create a model from a JSON:API document without storing it. This is useful for create payloads where the `id` may not yet exist:
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
const model = store.build({
|
|
279
|
+
data: {
|
|
280
|
+
type: 'events',
|
|
281
|
+
attributes: { name: 'New Event' },
|
|
282
|
+
},
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
model.name // 'New Event'
|
|
286
|
+
model.id // undefined
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
If the store already has synced data, `build()` resolves relationships against it. Unresolved references become stubs with just `id` and type:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
store.syncAll({
|
|
293
|
+
data: [{ type: 'images', id: '5', attributes: { url: 'http://example.com/img.jpg' } }],
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
const model = store.build({
|
|
297
|
+
data: {
|
|
298
|
+
type: 'events',
|
|
299
|
+
attributes: { name: 'New Event' },
|
|
300
|
+
relationships: {
|
|
301
|
+
image: { data: { type: 'images', id: '5' } },
|
|
302
|
+
sponsor: { data: { type: 'sponsors', id: '99' } },
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
model.image.url // 'http://example.com/img.jpg' (resolved from store)
|
|
308
|
+
model.sponsor.id // '99' (stub — not in store)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
A static version is also available when you don't need a store instance:
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
const model = Store.build({
|
|
315
|
+
data: {
|
|
316
|
+
type: 'events',
|
|
317
|
+
attributes: { name: 'New Event' },
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
```
|
|
321
|
+
|
|
306
322
|
### Finding synced data
|
|
307
323
|
|
|
308
324
|
You can also find in previously synced data:
|
|
@@ -451,17 +467,39 @@ const store = new Store({
|
|
|
451
467
|
const event = store.sync({
|
|
452
468
|
event: { id: '1', name: 'Demo Event' },
|
|
453
469
|
})
|
|
454
|
-
// single resource
|
|
470
|
+
// single resource -> returns model directly
|
|
455
471
|
|
|
456
472
|
const allSynced = store.syncAll({
|
|
457
473
|
event: { id: '1', name: 'Demo Event' },
|
|
458
474
|
images: [{ id: '2', url: 'http://example.com/image.jpg' }],
|
|
459
475
|
})
|
|
460
476
|
// syncAll always returns array
|
|
477
|
+
```
|
|
461
478
|
|
|
479
|
+
```javascript
|
|
462
480
|
const event = store.find('event', '1')
|
|
463
481
|
```
|
|
464
482
|
|
|
483
|
+
#### Creating payloads with payload()
|
|
484
|
+
|
|
485
|
+
The legacy presenter also supports `payload()` for create/update requests. It produces the legacy envelope format without `links` or sideloaded collections:
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
class TirePresenter extends Presenter {
|
|
489
|
+
static type = 'tire'
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
class CarPresenter extends Presenter {
|
|
493
|
+
static type = 'car'
|
|
494
|
+
relationships() {
|
|
495
|
+
return { tires: TirePresenter }
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
CarPresenter.payload({ name: 'Volvo', tires: [{ id: 1 }, { id: 2 }] })
|
|
500
|
+
// { car: { name: 'Volvo', tires: [1, 2] } }
|
|
501
|
+
```
|
|
502
|
+
|
|
465
503
|
#### Filtering by type with retrieveAll
|
|
466
504
|
|
|
467
505
|
Use `retrieveAll()` to sync data and return only models of a specific type:
|
|
@@ -485,6 +523,40 @@ const store = new Store({
|
|
|
485
523
|
})
|
|
486
524
|
```
|
|
487
525
|
|
|
526
|
+
#### Building models from create payloads
|
|
527
|
+
|
|
528
|
+
The legacy store also supports `build()` for creating models without storing them:
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
const model = store.build({
|
|
532
|
+
event: { name: 'New Event' },
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
model.name // 'New Event'
|
|
536
|
+
model.id // undefined
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Relationships are resolved against existing store data when available:
|
|
540
|
+
|
|
541
|
+
```javascript
|
|
542
|
+
store.syncAll({
|
|
543
|
+
links: { 'event.images': { type: 'images' } },
|
|
544
|
+
images: [{ id: '2', name: 'Header' }],
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
const model = store.build({
|
|
548
|
+
event: { name: 'New Event', images: [2] },
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
model.images[0].name // 'Header' (resolved from store)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
A static version is also available:
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
const model = Store.build({ event: { name: 'New Event' } })
|
|
558
|
+
```
|
|
559
|
+
|
|
488
560
|
### Schema Validation for Legacy Store
|
|
489
561
|
|
|
490
562
|
The legacy store also supports schema validation and type inference, maintaining full backward compatibility:
|
|
@@ -575,3 +647,78 @@ const event = store.find('event', '1')
|
|
|
575
647
|
```
|
|
576
648
|
|
|
577
649
|
**Note**: Validation happens eagerly during `sync()` when schemas are configured. This allows you to check `store.validationErrors` immediately after syncing.
|
|
650
|
+
|
|
651
|
+
## Upgrading from 4.0
|
|
652
|
+
|
|
653
|
+
### Unresolved relationships resolve to stubs
|
|
654
|
+
|
|
655
|
+
References missing from `included` now resolve to a stub `{ id, [TYPE]: type }` instead of `null`. References with `id: null` are dropped (arrays filter them out; to-one becomes `null`).
|
|
656
|
+
|
|
657
|
+
Tighten schemas that typed relationships as nullable, and detect "not sideloaded" by absence of attributes rather than `=== null`.
|
|
658
|
+
|
|
659
|
+
## Upgrading from 3.x
|
|
660
|
+
|
|
661
|
+
Version 4.x is a full TypeScript rewrite with several breaking changes.
|
|
662
|
+
|
|
663
|
+
### Node.js version
|
|
664
|
+
|
|
665
|
+
Node.js 20+ is now required (was 14+).
|
|
666
|
+
|
|
667
|
+
### Metadata uses Symbols instead of plain properties
|
|
668
|
+
|
|
669
|
+
In 3.x, resource type, links, and meta were stored as plain properties on models (`model.type`, `model.links`, `model.meta`). Relationship metadata used `model._links` and `model._meta`.
|
|
670
|
+
|
|
671
|
+
In 4.x, these use Symbol keys to avoid collisions with your data. Use the helpers from `yayson/utils`:
|
|
672
|
+
|
|
673
|
+
```javascript
|
|
674
|
+
// 3.x
|
|
675
|
+
model.type
|
|
676
|
+
model.links
|
|
677
|
+
model.meta
|
|
678
|
+
relatedModel._links
|
|
679
|
+
relatedModel._meta
|
|
680
|
+
|
|
681
|
+
// 4.x
|
|
682
|
+
import { getType, getLinks, getMeta, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'
|
|
683
|
+
getType(model)
|
|
684
|
+
getLinks(model)
|
|
685
|
+
getMeta(model)
|
|
686
|
+
getRelationshipLinks(relatedModel)
|
|
687
|
+
getRelationshipMeta(relatedModel)
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### `sync()` restores 3.x behavior
|
|
691
|
+
|
|
692
|
+
`sync()` now matches 3.x behavior: single resources return a model directly, collections return an array. If you always want an array, use `syncAll()`.
|
|
693
|
+
|
|
694
|
+
```javascript
|
|
695
|
+
// Single resource — returns model directly
|
|
696
|
+
const event = store.sync({ data: { type: 'events', id: '1', attributes: { name: 'Demo' } } })
|
|
697
|
+
event.name // 'Demo'
|
|
698
|
+
|
|
699
|
+
// Collection — returns array
|
|
700
|
+
const events = store.sync({ data: [{ type: 'events', id: '1', attributes: { name: 'Demo' } }] })
|
|
701
|
+
events.length // 1
|
|
702
|
+
|
|
703
|
+
// Always returns array
|
|
704
|
+
const events = store.syncAll(data)
|
|
705
|
+
|
|
706
|
+
// retrieve/retrieveAll also available
|
|
707
|
+
const event = store.retrieve('events', data)
|
|
708
|
+
const events = store.retrieveAll('events', data)
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Document-level meta uses Symbols
|
|
712
|
+
|
|
713
|
+
In 3.x, document-level metadata was stored as `result.meta`. In 4.x, it uses a Symbol key:
|
|
714
|
+
|
|
715
|
+
```javascript
|
|
716
|
+
// 3.x
|
|
717
|
+
const result = store.sync(data)
|
|
718
|
+
result.meta // { total: 100 }
|
|
719
|
+
|
|
720
|
+
// 4.x
|
|
721
|
+
import { META } from 'yayson/utils'
|
|
722
|
+
const result = store.sync(data)
|
|
723
|
+
result[META] // { total: 100 }
|
|
724
|
+
```
|
package/build/legacy.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_symbols = require('./symbols-nFs99aEX.cjs');
|
|
2
|
-
const require_yayson = require('./yayson-
|
|
2
|
+
const require_yayson = require('./yayson-l2JKseMH.cjs');
|
|
3
3
|
|
|
4
4
|
//#region src/yayson/legacy-presenter.ts
|
|
5
|
-
function hasId(value) {
|
|
5
|
+
function hasId$1(value) {
|
|
6
6
|
return typeof value === "object" && value !== null && "id" in value;
|
|
7
7
|
}
|
|
8
8
|
function createLegacyPresenter(Presenter) {
|
|
@@ -27,8 +27,8 @@ function createLegacyPresenter(Presenter) {
|
|
|
27
27
|
if (data == null) {
|
|
28
28
|
id = attributes[key + "Id"];
|
|
29
29
|
if (id != null) attributes[key] = id;
|
|
30
|
-
} else if (Array.isArray(data)) attributes[key] = data.map((obj) => hasId(obj) ? obj.id : obj);
|
|
31
|
-
else if (hasId(data)) attributes[key] = data.id;
|
|
30
|
+
} else if (Array.isArray(data)) attributes[key] = data.map((obj) => hasId$1(obj) ? obj.id : obj);
|
|
31
|
+
else if (hasId$1(data)) attributes[key] = data.id;
|
|
32
32
|
}
|
|
33
33
|
const relationshipKeys = relationships ? Object.keys(relationships) : [];
|
|
34
34
|
return require_yayson.filterByFields(attributes, this.constructor.fields ? [...this.constructor.fields, ...relationshipKeys] : void 0);
|
|
@@ -72,7 +72,7 @@ function createLegacyPresenter(Presenter) {
|
|
|
72
72
|
}
|
|
73
73
|
if (this.scope[this.constructor.type] && !this.scope[this.pluralType()]) {
|
|
74
74
|
const existingValue = this.scope[this.constructor.type];
|
|
75
|
-
if (attrs && hasId(existingValue) && existingValue.id !== attrs.id) {
|
|
75
|
+
if (attrs && hasId$1(existingValue) && existingValue.id !== attrs.id) {
|
|
76
76
|
this.scope[this.pluralType()] = [this.scope[this.constructor.type]];
|
|
77
77
|
delete this.scope[this.constructor.type];
|
|
78
78
|
const pluralArray = this.scope[this.pluralType()];
|
|
@@ -80,7 +80,7 @@ function createLegacyPresenter(Presenter) {
|
|
|
80
80
|
} else added = false;
|
|
81
81
|
} else if (this.scope[this.pluralType()]) {
|
|
82
82
|
const existing = this.scope[this.pluralType()];
|
|
83
|
-
if (Array.isArray(existing)) if (attrs && !existing.some((i) => hasId(i) && i.id === attrs.id)) existing.push(attrs);
|
|
83
|
+
if (Array.isArray(existing)) if (attrs && !existing.some((i) => hasId$1(i) && i.id === attrs.id)) existing.push(attrs);
|
|
84
84
|
else added = false;
|
|
85
85
|
} else if (opts.defaultPlural) this.scope[this.pluralType()] = attrs ? [attrs] : [];
|
|
86
86
|
else this.scope[this.constructor.type] = attrs;
|
|
@@ -88,6 +88,11 @@ function createLegacyPresenter(Presenter) {
|
|
|
88
88
|
}
|
|
89
89
|
return this.scope;
|
|
90
90
|
}
|
|
91
|
+
payload(instance) {
|
|
92
|
+
if (Array.isArray(instance)) throw new Error("payload() expects a single resource, not an array");
|
|
93
|
+
if (instance == null) throw new Error("payload() requires a resource, got null");
|
|
94
|
+
return { [this.constructor.type]: this.attributes(instance) };
|
|
95
|
+
}
|
|
91
96
|
render(instanceOrCollection) {
|
|
92
97
|
return this.toJSON(instanceOrCollection);
|
|
93
98
|
}
|
|
@@ -97,12 +102,18 @@ function createLegacyPresenter(Presenter) {
|
|
|
97
102
|
static render(instanceOrCollection, _options) {
|
|
98
103
|
return new this().render(instanceOrCollection);
|
|
99
104
|
}
|
|
105
|
+
static payload(instance) {
|
|
106
|
+
return new this().payload(instance);
|
|
107
|
+
}
|
|
100
108
|
};
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
//#endregion
|
|
104
112
|
//#region src/yayson/legacy-store.ts
|
|
105
|
-
|
|
113
|
+
function hasId(model) {
|
|
114
|
+
return model.id != null;
|
|
115
|
+
}
|
|
116
|
+
var LegacyStore = class LegacyStore {
|
|
106
117
|
types;
|
|
107
118
|
records;
|
|
108
119
|
relations;
|
|
@@ -125,33 +136,56 @@ var LegacyStore = class {
|
|
|
125
136
|
this.validationErrors = [];
|
|
126
137
|
this.models = {};
|
|
127
138
|
}
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
id: rec.data.id
|
|
135
|
-
};
|
|
136
|
-
model[require_symbols.TYPE] = type;
|
|
137
|
-
if (rec.data.meta != null) model[require_symbols.META] = rec.data.meta;
|
|
138
|
-
if (rec.data.links != null) model[require_symbols.LINKS] = rec.data.links;
|
|
139
|
-
models[type][idStr] = model;
|
|
139
|
+
#createStub(type, id) {
|
|
140
|
+
const stub = { id };
|
|
141
|
+
stub[require_symbols.TYPE] = type;
|
|
142
|
+
return stub;
|
|
143
|
+
}
|
|
144
|
+
#resolveRelationships(model, type, resolver) {
|
|
140
145
|
const relations = this.relations[type];
|
|
141
|
-
if (relations)
|
|
146
|
+
if (!relations) return;
|
|
147
|
+
for (const attribute in relations) {
|
|
142
148
|
const relationType = relations[attribute];
|
|
143
149
|
const value = model[attribute];
|
|
144
|
-
|
|
150
|
+
if (Array.isArray(value)) model[attribute] = value.filter((id) => id != null).map((id) => resolver(relationType, String(id)));
|
|
151
|
+
else if (value != null) model[attribute] = resolver(relationType, String(value));
|
|
152
|
+
else model[attribute] = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
#createModel(rec, type, options) {
|
|
156
|
+
const models = options?.models;
|
|
157
|
+
const model = { ...rec.data };
|
|
158
|
+
if (rec.data.id != null) model.id = rec.data.id;
|
|
159
|
+
model[require_symbols.TYPE] = type;
|
|
160
|
+
if (rec.data.meta != null) model[require_symbols.META] = rec.data.meta;
|
|
161
|
+
if (rec.data.links != null) model[require_symbols.LINKS] = rec.data.links;
|
|
162
|
+
if (models && hasId(model)) {
|
|
163
|
+
const idStr = String(model.id);
|
|
164
|
+
if (!models[type]) models[type] = {};
|
|
165
|
+
models[type][idStr] = model;
|
|
145
166
|
}
|
|
167
|
+
const resolver = (relationType, id) => {
|
|
168
|
+
return this.#findModel(relationType, id, models ?? {}) ?? this.#createStub(relationType, id);
|
|
169
|
+
};
|
|
170
|
+
this.#resolveRelationships(model, type, resolver);
|
|
171
|
+
return model;
|
|
172
|
+
}
|
|
173
|
+
toModel(rec, type, models) {
|
|
174
|
+
const idStr = String(rec.data.id);
|
|
175
|
+
if (!models[type]) models[type] = {};
|
|
176
|
+
if (models[type][idStr]) return models[type][idStr];
|
|
177
|
+
const result = this.#createModel(rec, type, { models });
|
|
178
|
+
if (!hasId(result)) throw new Error(`Expected model of type ${type} to have an id`);
|
|
179
|
+
const model = result;
|
|
146
180
|
if (this.schemas && this.schemas[type]) {
|
|
147
181
|
const schema = this.schemas[type];
|
|
148
|
-
const result = require_yayson.validate(schema, model, this.strict);
|
|
149
|
-
if (!result.valid) this.validationErrors.push({
|
|
182
|
+
const result$1 = require_yayson.validate(schema, model, this.strict);
|
|
183
|
+
if (!result$1.valid) this.validationErrors.push({
|
|
150
184
|
type,
|
|
151
185
|
id: idStr,
|
|
152
|
-
error: result.error
|
|
186
|
+
error: result$1.error
|
|
153
187
|
});
|
|
154
|
-
const validatedModel = result.data;
|
|
188
|
+
const validatedModel = result$1.data;
|
|
155
189
|
validatedModel[require_symbols.TYPE] = model[require_symbols.TYPE];
|
|
156
190
|
validatedModel[require_symbols.LINKS] = model[require_symbols.LINKS];
|
|
157
191
|
validatedModel[require_symbols.META] = model[require_symbols.META];
|
|
@@ -177,6 +211,30 @@ var LegacyStore = class {
|
|
|
177
211
|
findRecords(type) {
|
|
178
212
|
return this.records.filter((r) => r.type === type);
|
|
179
213
|
}
|
|
214
|
+
#findModel(type, id, models) {
|
|
215
|
+
const rec = this.findRecord(type, id);
|
|
216
|
+
if (rec == null) return null;
|
|
217
|
+
return models[type]?.[id] ?? this.toModel(rec, type, models);
|
|
218
|
+
}
|
|
219
|
+
static build(data) {
|
|
220
|
+
return new LegacyStore().build(data);
|
|
221
|
+
}
|
|
222
|
+
build(data) {
|
|
223
|
+
if (data.links) this.setupRelations(data.links);
|
|
224
|
+
let name;
|
|
225
|
+
for (const key in data) if (key !== "meta" && key !== "links") {
|
|
226
|
+
name = key;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
if (name == null) throw new Error("build() expects a single resource, not an array");
|
|
230
|
+
const value = data[name];
|
|
231
|
+
if (value == null || Array.isArray(value)) throw new Error("build() expects a single resource, not an array");
|
|
232
|
+
const type = this.types[name] || name;
|
|
233
|
+
return this.#createModel({
|
|
234
|
+
type,
|
|
235
|
+
data: value
|
|
236
|
+
}, type);
|
|
237
|
+
}
|
|
180
238
|
/** @deprecated Use retrieve() instead. */
|
|
181
239
|
retrive(type, data) {
|
|
182
240
|
return this.retrieve(type, data);
|
|
@@ -190,12 +248,7 @@ var LegacyStore = class {
|
|
|
190
248
|
return model;
|
|
191
249
|
}
|
|
192
250
|
find(type, id, models) {
|
|
193
|
-
|
|
194
|
-
const idStr = String(id);
|
|
195
|
-
const rec = this.findRecord(type, idStr);
|
|
196
|
-
if (rec == null) return null;
|
|
197
|
-
if (!modelsObj[type]) modelsObj[type] = {};
|
|
198
|
-
return modelsObj[type][idStr] || this.toModel(rec, type, modelsObj);
|
|
251
|
+
return this.#findModel(type, String(id), models ?? this.models);
|
|
199
252
|
}
|
|
200
253
|
findAll(type, models) {
|
|
201
254
|
const modelsObj = models ?? this.models;
|
package/build/legacy.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as Presenter, r as yayson$1, t as YaysonOptions } from "./yayson-
|
|
1
|
+
import { E as ModelLike, T as Adapter, _ as StoreResult, a as JsonApiRelationship, c as LegacyPresenterOptions, d as SchemaRegistry, f as StoreModel, i as JsonApiLinks, l as LegacyStoreOptions, m as StoreModels, n as JsonApiDocument, o as JsonApiRelationships, p as StoreModelWithOptionalId, r as JsonApiLink, s as JsonApiResource, t as InferModelType, u as PresenterOptions, v as ValidationError } from "./types-NiKV-lj-.cjs";
|
|
2
|
+
import { i as Presenter, r as yayson$1, t as YaysonOptions } from "./yayson-3UYKq2H5.cjs";
|
|
3
3
|
|
|
4
4
|
//#region src/yayson/legacy-presenter.d.ts
|
|
5
5
|
interface LegacyJsonApiDocument extends JsonApiDocument {
|
|
@@ -13,6 +13,7 @@ declare function createLegacyPresenter(Presenter: Presenter): {
|
|
|
13
13
|
attributes(instance: ModelLike | null): Record<string, unknown>;
|
|
14
14
|
includeRelationships(scope: LegacyJsonApiDocument, instance: ModelLike): unknown[];
|
|
15
15
|
toJSON(instanceOrCollection: ModelLike | ModelLike[] | null | undefined, options?: LegacyPresenterOptions): LegacyJsonApiDocument;
|
|
16
|
+
payload(instance: ModelLike): LegacyJsonApiDocument;
|
|
16
17
|
render(instanceOrCollection: ModelLike | ModelLike[] | null): LegacyJsonApiDocument;
|
|
17
18
|
id(instance: ModelLike): string | undefined;
|
|
18
19
|
selfLinks(_instance: ModelLike): JsonApiLink | string | undefined;
|
|
@@ -30,6 +31,7 @@ declare function createLegacyPresenter(Presenter: Presenter): {
|
|
|
30
31
|
buildRelationships(instance: ModelLike | null): JsonApiRelationships | null;
|
|
31
32
|
buildSelfLink(instance: ModelLike): JsonApiLink | undefined;
|
|
32
33
|
toJSON(instanceOrCollection: ModelLike | ModelLike[] | null | undefined, options?: PresenterOptions): JsonApiDocument;
|
|
34
|
+
payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
|
|
33
35
|
render(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
|
|
34
36
|
};
|
|
35
37
|
adapter: typeof Adapter;
|
|
@@ -37,6 +39,7 @@ declare function createLegacyPresenter(Presenter: Presenter): {
|
|
|
37
39
|
fields?: string[];
|
|
38
40
|
toJSON(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
|
|
39
41
|
render(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
|
|
42
|
+
payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
|
|
40
43
|
}>;
|
|
41
44
|
buildRelationships(instance: ModelLike | null): JsonApiRelationships | null;
|
|
42
45
|
buildSelfLink(instance: ModelLike): JsonApiLink | undefined;
|
|
@@ -44,8 +47,9 @@ declare function createLegacyPresenter(Presenter: Presenter): {
|
|
|
44
47
|
type: string;
|
|
45
48
|
plural?: string;
|
|
46
49
|
fields?: string[];
|
|
47
|
-
toJSON(instanceOrCollection: ModelLike | ModelLike[] | null, options?: LegacyPresenterOptions):
|
|
48
|
-
render(instanceOrCollection: ModelLike | ModelLike[] | null, _options?: LegacyPresenterOptions):
|
|
50
|
+
toJSON(instanceOrCollection: ModelLike | ModelLike[] | null, options?: LegacyPresenterOptions): LegacyJsonApiDocument;
|
|
51
|
+
render(instanceOrCollection: ModelLike | ModelLike[] | null, _options?: LegacyPresenterOptions): LegacyJsonApiDocument;
|
|
52
|
+
payload(instance: ModelLike): LegacyJsonApiDocument;
|
|
49
53
|
adapter: typeof Adapter;
|
|
50
54
|
};
|
|
51
55
|
//#endregion
|
|
@@ -53,6 +57,7 @@ declare function createLegacyPresenter(Presenter: Presenter): {
|
|
|
53
57
|
interface LegacyStoreRecordType {
|
|
54
58
|
type: string;
|
|
55
59
|
data: Record<string, unknown> & {
|
|
60
|
+
id?: string | number;
|
|
56
61
|
meta?: Record<string, unknown>;
|
|
57
62
|
links?: Record<string, unknown>;
|
|
58
63
|
};
|
|
@@ -69,6 +74,7 @@ interface LegacyData {
|
|
|
69
74
|
[key: string]: LegacyDataValue | LegacyLinks | Record<string, unknown> | undefined;
|
|
70
75
|
}
|
|
71
76
|
declare class LegacyStore<S extends SchemaRegistry = SchemaRegistry> {
|
|
77
|
+
#private;
|
|
72
78
|
types: Record<string, string>;
|
|
73
79
|
records: LegacyStoreRecordType[];
|
|
74
80
|
relations: Record<string, Record<string, string>>;
|
|
@@ -82,6 +88,8 @@ declare class LegacyStore<S extends SchemaRegistry = SchemaRegistry> {
|
|
|
82
88
|
setupRelations(links: LegacyLinks): void;
|
|
83
89
|
findRecord(type: string, id: string): LegacyStoreRecordType | undefined;
|
|
84
90
|
findRecords(type: string): LegacyStoreRecordType[];
|
|
91
|
+
static build(data: LegacyData): StoreModelWithOptionalId;
|
|
92
|
+
build(data: LegacyData): StoreModelWithOptionalId;
|
|
85
93
|
/** @deprecated Use retrieve() instead. */
|
|
86
94
|
retrive<T extends string>(type: T, data: LegacyData): InferModelType<S, T> | null;
|
|
87
95
|
retrieve<T extends string>(type: T, data: LegacyData): InferModelType<S, T> | null;
|
package/build/legacy.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.d.cts","names":[],"sources":["../../../../../../yayson/legacy-presenter.ts","../../../../../../yayson/legacy-store.ts","../../../../../../legacy.ts"],"mappings":";;;;UASU,qBAAA,SAA8B,eAAA;EAAA,CACrC,GAAA;AAAA;AAAA,iBAIqB,qBAAA,CAAsB,SAAA,EAAW,SAAA;EAAA,aASjC,eAAA;iBAbV;WAOK,qBAAA;;
|
|
1
|
+
{"version":3,"file":"legacy.d.cts","names":[],"sources":["../../../../../../yayson/legacy-presenter.ts","../../../../../../yayson/legacy-store.ts","../../../../../../legacy.ts"],"mappings":"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"}
|