@jskit-ai/http-runtime 0.1.59 → 0.1.61

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  "packageVersion": 1,
3
3
  "packageId": "@jskit-ai/http-runtime",
4
- "version": "0.1.59",
4
+ "version": "0.1.61",
5
5
  "kind": "runtime",
6
6
  "dependsOn": [],
7
7
  "capabilities": {
@@ -67,7 +67,7 @@ export default Object.freeze({
67
67
  "mutations": {
68
68
  "dependencies": {
69
69
  "runtime": {
70
- "@jskit-ai/kernel": "0.1.60"
70
+ "@jskit-ai/kernel": "0.1.62"
71
71
  },
72
72
  "dev": {}
73
73
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/http-runtime",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -18,7 +18,7 @@
18
18
  "./shared/validators/operationValidation": "./src/shared/validators/operationValidation.js"
19
19
  },
20
20
  "dependencies": {
21
- "@jskit-ai/kernel": "0.1.60",
21
+ "@jskit-ai/kernel": "0.1.62",
22
22
  "json-rest-schema": "1.x.x"
23
23
  }
24
24
  }
@@ -7,6 +7,10 @@ import {
7
7
  resolveJsonApiTransportTypes
8
8
  } from "../validators/jsonApiTransport.js";
9
9
  import { encodeJsonApiResourceQueryObject } from "../validators/jsonApiQueryTransport.js";
10
+ import {
11
+ resolveRelationshipFieldKey,
12
+ simplifyJsonApiResourceWithRelationshipIds
13
+ } from "../support/jsonApiSimplify.js";
10
14
 
11
15
  function isRecord(value) {
12
16
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -98,16 +102,6 @@ function encodeJsonApiResourceQuery(query, transport = null) {
98
102
  });
99
103
  }
100
104
 
101
- function resolveRelationshipFieldKey(relationshipName = "", lookupFieldMap = null) {
102
- const normalizedRelationshipName = normalizeText(relationshipName);
103
- const explicitFieldKey = normalizeText(lookupFieldMap?.[normalizedRelationshipName]);
104
- if (explicitFieldKey) {
105
- return explicitFieldKey;
106
- }
107
-
108
- return normalizedRelationshipName ? `${normalizedRelationshipName}Id` : "";
109
- }
110
-
111
105
  function createIncludedResourceIndex(document = {}) {
112
106
  const index = new Map();
113
107
 
@@ -135,9 +129,7 @@ function simplifyRelationshipResource(linkage = {}, options = {}) {
135
129
  const resourceKey = `${type}:${id}`;
136
130
  const includedResource = options.includedResourceIndex?.get(resourceKey);
137
131
  if (!includedResource) {
138
- return {
139
- id
140
- };
132
+ return null;
141
133
  }
142
134
 
143
135
  return simplifyResourceObject(includedResource, options);
@@ -145,10 +137,9 @@ function simplifyRelationshipResource(linkage = {}, options = {}) {
145
137
 
146
138
  function simplifyResourceObject(resource = {}, options = {}) {
147
139
  const normalizedResource = isRecord(resource) ? normalizeObject(resource) : {};
148
- const simplified = {
149
- id: normalizedResource.id == null ? "" : String(normalizedResource.id),
150
- ...(normalizeObject(normalizedResource.attributes))
151
- };
140
+ const simplified = simplifyJsonApiResourceWithRelationshipIds(normalizedResource, {
141
+ lookupFieldMap: options.lookupFieldMap || null
142
+ });
152
143
  const lookupContainerKey = normalizeText(options.lookupContainerKey, {
153
144
  fallback: "lookups"
154
145
  });
@@ -0,0 +1,39 @@
1
+ import { isRecord, normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
+
3
+ function resolveRelationshipFieldKey(relationshipName = "", lookupFieldMap = null) {
4
+ const normalizedRelationshipName = normalizeText(relationshipName);
5
+ const explicitFieldKey = normalizeText(lookupFieldMap?.[normalizedRelationshipName]);
6
+ if (explicitFieldKey) {
7
+ return explicitFieldKey;
8
+ }
9
+
10
+ return normalizedRelationshipName ? `${normalizedRelationshipName}Id` : "";
11
+ }
12
+
13
+ function simplifyJsonApiResourceWithRelationshipIds(resource = {}, { lookupFieldMap = null } = {}) {
14
+ const normalizedResource = isRecord(resource) ? normalizeObject(resource) : {};
15
+ const simplified = {
16
+ id: normalizedResource.id == null ? "" : String(normalizedResource.id),
17
+ ...(normalizeObject(normalizedResource.attributes))
18
+ };
19
+
20
+ for (const [relationshipName, relationshipValue] of Object.entries(normalizeObject(normalizedResource.relationships))) {
21
+ const relationshipData = relationshipValue?.data;
22
+ if (Array.isArray(relationshipData)) {
23
+ continue;
24
+ }
25
+
26
+ const fieldKey = resolveRelationshipFieldKey(relationshipName, lookupFieldMap);
27
+ if (!fieldKey || Object.hasOwn(simplified, fieldKey)) {
28
+ continue;
29
+ }
30
+ simplified[fieldKey] = relationshipData?.id == null ? null : String(relationshipData.id);
31
+ }
32
+
33
+ return simplified;
34
+ }
35
+
36
+ export {
37
+ resolveRelationshipFieldKey,
38
+ simplifyJsonApiResourceWithRelationshipIds
39
+ };
@@ -1,4 +1,5 @@
1
1
  import { normalizeArray, normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
+ import { simplifyJsonApiResourceWithRelationshipIds } from "../support/jsonApiSimplify.js";
2
3
 
3
4
  const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
4
5
 
@@ -441,11 +442,7 @@ function createJsonApiErrorDocumentFromFailure({
441
442
  }
442
443
 
443
444
  function simplifyJsonApiResourceObject(resource = {}) {
444
- const normalized = normalizeJsonApiResourceObject(resource);
445
- return {
446
- id: normalized.id == null ? "" : normalized.id,
447
- ...(normalized.attributes || {})
448
- };
445
+ return simplifyJsonApiResourceWithRelationshipIds(normalizeJsonApiResourceObject(resource));
449
446
  }
450
447
 
451
448
  function simplifyJsonApiDocument(payload = {}) {
@@ -236,6 +236,57 @@ test("request decodes json:api relationship includes into JSKIT lookups and fore
236
236
  });
237
237
  });
238
238
 
239
+ test("request keeps relationship foreign-key fields even when related resources are not included", async () => {
240
+ const fetchImpl = async () =>
241
+ mockResponse({
242
+ contentType: "application/vnd.api+json",
243
+ data: {
244
+ data: {
245
+ type: "pets",
246
+ id: "729900",
247
+ attributes: {
248
+ name: "Daisy"
249
+ },
250
+ relationships: {
251
+ contact: {
252
+ data: {
253
+ type: "contacts",
254
+ id: "552252"
255
+ }
256
+ },
257
+ breed: {
258
+ data: {
259
+ type: "breeds",
260
+ id: "2"
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ });
267
+
268
+ const client = createHttpClient({ fetchImpl });
269
+ const payload = await client.request("/api/pets/729900", {
270
+ method: "GET",
271
+ transport: {
272
+ kind: "jsonapi-resource",
273
+ responseType: "pets",
274
+ responseKind: "record",
275
+ lookupFieldMap: {
276
+ contact: "contactId",
277
+ breed: "breedId"
278
+ }
279
+ }
280
+ });
281
+
282
+ assert.deepEqual(payload, {
283
+ id: "729900",
284
+ name: "Daisy",
285
+ contactId: "552252",
286
+ breedId: "2"
287
+ });
288
+ });
289
+
239
290
  test("request recursively decodes nested included relationships for collection-style lookups", async () => {
240
291
  const fetchImpl = async () =>
241
292
  mockResponse({
@@ -173,6 +173,40 @@ test("simplifyJsonApiDocument keeps flat-record behavior for resource and collec
173
173
  );
174
174
  });
175
175
 
176
+ test("simplifyJsonApiDocument preserves single-relationship foreign key ids without lookup hydration", () => {
177
+ assert.deepEqual(
178
+ simplifyJsonApiDocument({
179
+ data: {
180
+ type: "workspace-memberships",
181
+ id: "11",
182
+ attributes: {
183
+ status: "active"
184
+ },
185
+ relationships: {
186
+ workspace: {
187
+ data: {
188
+ type: "workspaces",
189
+ id: "7"
190
+ }
191
+ },
192
+ user: {
193
+ data: {
194
+ type: "userProfiles",
195
+ id: "9"
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }),
201
+ {
202
+ id: "11",
203
+ status: "active",
204
+ workspaceId: "7",
205
+ userId: "9"
206
+ }
207
+ );
208
+ });
209
+
176
210
  test("createJsonApiErrorDocumentFromFailure maps field errors and validation issues to JSON:API errors", () => {
177
211
  assert.deepEqual(
178
212
  createJsonApiErrorDocumentFromFailure({