@alchemy_cms/json_api 4.0.0 → 5.0.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 CHANGED
@@ -12,11 +12,22 @@ or with the package manager of your choice.
12
12
 
13
13
  ## Usage
14
14
 
15
- `deserialize` converts a JSON:API response into plain, acyclic JS objects:
16
- each resource's attributes are flattened with its `id`, and relationships
17
- present in `included` become nested objects (absent ones become `{ id }`
18
- stubs). Reference cycles are broken automatically, so the result is always
19
- safe to `JSON.stringify`.
15
+ `deserialize` converts a JSON:API response into plain JS objects: each
16
+ resource's attributes are flattened with its `id`, and relationships present in
17
+ `included` become nested objects (absent ones become `{ id }` stubs).
18
+
19
+ Each included resource is materialized **once** and shared by every reference
20
+ to it, so the result stays proportional to the size of the response even when
21
+ resources reference each other heavily. A consequence is that back-references
22
+ (e.g. a menu node's `parent`, which lists the node among its `children`) are
23
+ real references, and the result may contain **cycles**:
24
+
25
+ - `structuredClone`, [devalue](https://github.com/sveltejs/devalue) (Nuxt
26
+ payloads) and Vue/React state handle this fine.
27
+ - `JSON.stringify` does not. If you need JSON, serialize a projection of the
28
+ fields you need, or use a cycle-aware serializer.
29
+ - Mutating a resource is visible everywhere it is referenced. Copy before
30
+ mutating.
20
31
 
21
32
  ```js
22
33
  import { deserialize } from "@alchemy_cms/json_api"
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Deserialize a JSON:API document into plain, acyclic objects.
2
+ * Deserialize a JSON:API document into plain objects, sharing one object per
3
+ * resource. The result may contain reference cycles.
3
4
  *
4
5
  * The `document` is a raw API response with no compile-time shape, so the
5
6
  * parameter is `unknown`. The caller names the shape it expects out via `T`;
@@ -1,18 +1,21 @@
1
1
  //#region src/deserialize.ts
2
2
  var e = ({ type: e, id: t }) => `${e}:${t}`, t = ({ id: e }) => ({ id: e }), n = (t) => new Map(t.map((t) => [e(t), t])), r = (n, r, i) => {
3
- let o = e(i), s = r.has(o) ? void 0 : n.get(o);
4
- return s ? a(n, r, s) : t(i);
3
+ let o = n.get(e(i));
4
+ return o ? a(n, r, o) : t(i);
5
5
  }, i = (e, t, n) => Array.isArray(n) ? n.map((n) => r(e, t, n)) : n ? r(e, t, n) : null, a = (t, n, r) => {
6
- let a = new Set(n).add(e(r)), o = Object.entries(r.relationships ?? {}).map(([e, n]) => [e, i(t, a, n?.data ?? null)]);
7
- return {
6
+ let a = e(r), o = n.get(a);
7
+ if (o) return o;
8
+ let s = {
8
9
  ...r.attributes,
9
- id: r.id,
10
- ...Object.fromEntries(o)
10
+ id: r.id
11
11
  };
12
+ n.set(a, s);
13
+ for (let [e, a] of Object.entries(r.relationships ?? {})) s[e] = i(t, n, a?.data ?? null);
14
+ return s;
12
15
  };
13
16
  function o(e) {
14
- let { data: t = null, included: r = [] } = e == null ? {} : structuredClone(e), i = n(r), o = (e) => a(i, /* @__PURE__ */ new Set(), e);
15
- return Array.isArray(t) ? t.map(o) : t ? o(t) : null;
17
+ let { data: t = null, included: r = [] } = e == null ? {} : structuredClone(e), i = n(r), o = /* @__PURE__ */ new Map(), s = (e) => a(i, o, e);
18
+ return Array.isArray(t) ? t.map(s) : t ? s(t) : null;
16
19
  }
17
20
  //#endregion
18
21
  export { o as default, o as deserialize };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/json_api",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "JSON API deserializer for AlchemyCMS",
5
5
  "license": "BSD-3-Clause",
6
6
  "repository": {