@leodamours/jsonapi-client 1.0.3 → 1.0.5

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
@@ -147,6 +147,126 @@ await api.findAll(User, {
147
147
  });
148
148
  ```
149
149
 
150
+ ## Response Deserialization
151
+
152
+ Client methods return raw JSON:API documents with nested structure (`data.attributes`, `data.relationships`). Use `deserialize` / `deserializeMany` from the DSL to flatten them into plain objects (also re-exported from this package for convenience):
153
+
154
+ ```ts
155
+ import { deserialize, deserializeMany } from "@leodamours/jsonapi-dsl";
156
+
157
+ const doc = await api.findOne(Post, "1", { include: "author,comments" });
158
+ ```
159
+
160
+ **Without registry** — relationships stay as `{ type, id }` linkage:
161
+
162
+ ```ts
163
+ const { data: post } = deserialize(Post, doc);
164
+ post.id; // string
165
+ post.type; // "posts"
166
+ post.title; // string (was doc.data.attributes.title)
167
+ post.author; // { type: "users"; id: string } | null
168
+ post.comments; // { type: "comments"; id: string }[]
169
+ ```
170
+
171
+ **With registry** — relationships resolved one level from `included`:
172
+
173
+ ```ts
174
+ const { data: post } = deserialize(Post, doc, {
175
+ users: User,
176
+ comments: Comment,
177
+ });
178
+ post.author.name; // string — resolved from included
179
+ post.author.email; // string
180
+ post.comments[0].body; // string — resolved from included
181
+ ```
182
+
183
+ Resolution is **one level deep**: a resolved author's own relationships (e.g. `company`) remain as `{ type, id }` linkage. If a relationship's type isn't in the registry or the resource isn't in `included`, it falls back to linkage.
184
+
185
+ **Collections:**
186
+
187
+ ```ts
188
+ const allPosts = await api.findAll(Post, {
189
+ page: { size: 10 },
190
+ include: "author",
191
+ });
192
+
193
+ const { data: posts, meta, links } = deserializeMany(Post, allPosts, {
194
+ users: User,
195
+ });
196
+ // posts[0].title — string
197
+ // posts[0].author — resolved User or linkage
198
+ // meta, links — preserved from document
199
+ ```
200
+
201
+ **Deserialization types** for standalone use:
202
+
203
+ ```ts
204
+ import type {
205
+ Deserialized,
206
+ DeserializedWith,
207
+ DeserializeResult,
208
+ DeserializeManyResult,
209
+ } from "@leodamours/jsonapi-dsl";
210
+
211
+ type FlatPost = Deserialized<typeof Post>;
212
+ // { id: string; type: "posts"; title: string; content: string; author: { type: "users"; id: string } | null; ... }
213
+
214
+ type ResolvedPost = DeserializedWith<typeof Post, { users: typeof User }>;
215
+ // { id: string; type: "posts"; title: string; author: Deserialized<typeof User> | null; ... }
216
+ ```
217
+
218
+ ## Relationship Endpoints
219
+
220
+ Manipulate relationships directly via [JSON:API relationship endpoints](https://jsonapi.org/format/#crud-updating-relationships) without touching the resource itself:
221
+
222
+ ```ts
223
+ // GET — read relationship linkage
224
+ const rel = await api.getRelationship(Post, "1", "comments");
225
+ rel.data; // { type: "comments"; id: string }[]
226
+
227
+ // POST — add members to a to-many relationship
228
+ await api.addRelationship(Post, "1", "comments", [
229
+ { type: "comments", id: "10" },
230
+ ]);
231
+
232
+ // PATCH — replace a relationship entirely
233
+ await api.replaceRelationship(Post, "1", "author", {
234
+ type: "users",
235
+ id: "2",
236
+ });
237
+
238
+ // PATCH — clear a to-one relationship
239
+ await api.replaceRelationship(Post, "1", "author", null);
240
+
241
+ // DELETE — remove members from a to-many relationship
242
+ await api.removeRelationship(Post, "1", "comments", [
243
+ { type: "comments", id: "10" },
244
+ ]);
245
+ ```
246
+
247
+ All relationship methods operate on `/{resourceType}/{id}/relationships/{name}` and return a `RelationshipResponse` with the updated linkage.
248
+
249
+ ## Type-Safe Included Resources
250
+
251
+ When using `include`, the response's `included` array contains mixed resource types. Use `isResourceOf` from the DSL to narrow them:
252
+
253
+ ```ts
254
+ import { isResourceOf } from "@leodamours/jsonapi-dsl";
255
+
256
+ const result = await api.findOne(Post, "1", {
257
+ include: "author.company,comments",
258
+ });
259
+
260
+ const users = result.included?.filter(isResourceOf(User));
261
+ // ^? InferResource<typeof User>[]
262
+
263
+ const companies = result.included?.filter(isResourceOf(Company));
264
+ // ^? InferResource<typeof Company>[]
265
+
266
+ const comments = result.included?.filter(isResourceOf(Comment));
267
+ // ^? InferResource<typeof Comment>[]
268
+ ```
269
+
150
270
  ## Serialization Utilities
151
271
 
152
272
  Lower-level serialization functions are exported for custom use:
package/dist/index.d.mts CHANGED
@@ -1,24 +1,7 @@
1
1
  import { AxiosRequestConfig, AxiosInstance } from 'axios';
2
- import { ResourceDefinition, InferAttributesSimple, RelationshipsSchema, JsonApiDocument, InferResource, JsonApiCollectionDocument, JsonApiRelationships } from '@leodamours/jsonapi-dsl';
2
+ import { ResourceDefinition, InferAttributesSimple, SimplifiedRelationships, JsonApiLinks, JsonApiMeta, RelationshipsSchema, JsonApiDocument, InferResource, JsonApiCollectionDocument, JsonApiRelationships } from '@leodamours/jsonapi-dsl';
3
+ export { DeserializeManyResult, DeserializeResult, Deserialized, DeserializedWith, ResourceRegistry, SimplifiedHasMany, SimplifiedHasOne, SimplifiedRelationships, deserialize, deserializeMany } from '@leodamours/jsonapi-dsl';
3
4
 
4
- type SimplifiedHasOne<T extends string> = {
5
- type: T;
6
- id: string;
7
- } | null;
8
- type SimplifiedHasMany<T extends string> = Array<{
9
- type: T;
10
- id: string;
11
- }>;
12
- /**
13
- * Indexed lookup for simplified relationship shapes — avoids conditional types.
14
- */
15
- interface SimplifiedRelTypeMap<T extends string> {
16
- one: SimplifiedHasOne<T>;
17
- many: SimplifiedHasMany<T>;
18
- }
19
- type SimplifiedRelationships<Schema extends RelationshipsSchema> = {
20
- [K in keyof Schema]: SimplifiedRelTypeMap<Schema[K]["type"]>[Schema[K]["_rel"]];
21
- };
22
5
  type RelationshipClause<Rels extends RelationshipsSchema> = {
23
6
  relationships?: Partial<SimplifiedRelationships<Rels>>;
24
7
  };
@@ -62,6 +45,26 @@ interface QueryParams {
62
45
  fields?: Record<string, string | string[]>;
63
46
  [key: string]: unknown;
64
47
  }
48
+ /**
49
+ * Resource linkage — the `data` member of a relationship object.
50
+ * Used by relationship endpoint methods.
51
+ */
52
+ type ResourceLinkage = {
53
+ type: string;
54
+ id: string;
55
+ } | {
56
+ type: string;
57
+ id: string;
58
+ }[] | null;
59
+ /**
60
+ * Response from a relationship endpoint
61
+ * (GET/POST/PATCH/DELETE on /{type}/{id}/relationships/{rel}).
62
+ */
63
+ interface RelationshipResponse {
64
+ data: ResourceLinkage;
65
+ links?: JsonApiLinks;
66
+ meta?: JsonApiMeta;
67
+ }
65
68
 
66
69
  declare class JsonApiClient {
67
70
  private axios;
@@ -86,6 +89,31 @@ declare class JsonApiClient {
86
89
  * DELETE /{resourceType}/{id}
87
90
  */
88
91
  remove(definition: ResourceDefinition, id: string, options?: AxiosRequestConfig): Promise<void>;
92
+ /**
93
+ * GET /{resourceType}/{id}/relationships/{relationshipName}
94
+ */
95
+ getRelationship(definition: ResourceDefinition, id: string, relationshipName: string, options?: AxiosRequestConfig): Promise<RelationshipResponse>;
96
+ /**
97
+ * POST /{resourceType}/{id}/relationships/{relationshipName}
98
+ * Add members to a to-many relationship.
99
+ */
100
+ addRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: {
101
+ type: string;
102
+ id: string;
103
+ }[], options?: AxiosRequestConfig): Promise<RelationshipResponse>;
104
+ /**
105
+ * PATCH /{resourceType}/{id}/relationships/{relationshipName}
106
+ * Replace a relationship entirely.
107
+ */
108
+ replaceRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: ResourceLinkage, options?: AxiosRequestConfig): Promise<RelationshipResponse>;
109
+ /**
110
+ * DELETE /{resourceType}/{id}/relationships/{relationshipName}
111
+ * Remove members from a to-many relationship.
112
+ */
113
+ removeRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: {
114
+ type: string;
115
+ id: string;
116
+ }[], options?: AxiosRequestConfig): Promise<RelationshipResponse>;
89
117
  /**
90
118
  * Update client configuration at runtime.
91
119
  */
@@ -126,5 +154,23 @@ declare function serializeUpdatePayload(definition: ResourceDefinition, id: stri
126
154
  * `{ sort: ["-createdAt", "title"] }` → `"sort=-createdAt,title"`
127
155
  */
128
156
  declare function serializeQueryParams(params: Record<string, unknown>): Record<string, string>;
157
+ /**
158
+ * Wrap resource linkage in a JSON:API request body for relationship endpoints.
159
+ */
160
+ declare function serializeRelationshipPayload(data: {
161
+ type: string;
162
+ id: string;
163
+ } | {
164
+ type: string;
165
+ id: string;
166
+ }[] | null): {
167
+ data: {
168
+ type: string;
169
+ id: string;
170
+ } | {
171
+ type: string;
172
+ id: string;
173
+ }[] | null;
174
+ };
129
175
 
130
- export { type ClientConfig, type CreatePayload, JsonApiClient, type QueryParams, type RelationshipClause, type SimplifiedHasMany, type SimplifiedHasOne, type SimplifiedRelationships, type UpdatePayload, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationships, serializeUpdatePayload };
176
+ export { type ClientConfig, type CreatePayload, JsonApiClient, type QueryParams, type RelationshipClause, type RelationshipResponse, type ResourceLinkage, type UpdatePayload, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationshipPayload, serializeRelationships, serializeUpdatePayload };
package/dist/index.d.ts CHANGED
@@ -1,24 +1,7 @@
1
1
  import { AxiosRequestConfig, AxiosInstance } from 'axios';
2
- import { ResourceDefinition, InferAttributesSimple, RelationshipsSchema, JsonApiDocument, InferResource, JsonApiCollectionDocument, JsonApiRelationships } from '@leodamours/jsonapi-dsl';
2
+ import { ResourceDefinition, InferAttributesSimple, SimplifiedRelationships, JsonApiLinks, JsonApiMeta, RelationshipsSchema, JsonApiDocument, InferResource, JsonApiCollectionDocument, JsonApiRelationships } from '@leodamours/jsonapi-dsl';
3
+ export { DeserializeManyResult, DeserializeResult, Deserialized, DeserializedWith, ResourceRegistry, SimplifiedHasMany, SimplifiedHasOne, SimplifiedRelationships, deserialize, deserializeMany } from '@leodamours/jsonapi-dsl';
3
4
 
4
- type SimplifiedHasOne<T extends string> = {
5
- type: T;
6
- id: string;
7
- } | null;
8
- type SimplifiedHasMany<T extends string> = Array<{
9
- type: T;
10
- id: string;
11
- }>;
12
- /**
13
- * Indexed lookup for simplified relationship shapes — avoids conditional types.
14
- */
15
- interface SimplifiedRelTypeMap<T extends string> {
16
- one: SimplifiedHasOne<T>;
17
- many: SimplifiedHasMany<T>;
18
- }
19
- type SimplifiedRelationships<Schema extends RelationshipsSchema> = {
20
- [K in keyof Schema]: SimplifiedRelTypeMap<Schema[K]["type"]>[Schema[K]["_rel"]];
21
- };
22
5
  type RelationshipClause<Rels extends RelationshipsSchema> = {
23
6
  relationships?: Partial<SimplifiedRelationships<Rels>>;
24
7
  };
@@ -62,6 +45,26 @@ interface QueryParams {
62
45
  fields?: Record<string, string | string[]>;
63
46
  [key: string]: unknown;
64
47
  }
48
+ /**
49
+ * Resource linkage — the `data` member of a relationship object.
50
+ * Used by relationship endpoint methods.
51
+ */
52
+ type ResourceLinkage = {
53
+ type: string;
54
+ id: string;
55
+ } | {
56
+ type: string;
57
+ id: string;
58
+ }[] | null;
59
+ /**
60
+ * Response from a relationship endpoint
61
+ * (GET/POST/PATCH/DELETE on /{type}/{id}/relationships/{rel}).
62
+ */
63
+ interface RelationshipResponse {
64
+ data: ResourceLinkage;
65
+ links?: JsonApiLinks;
66
+ meta?: JsonApiMeta;
67
+ }
65
68
 
66
69
  declare class JsonApiClient {
67
70
  private axios;
@@ -86,6 +89,31 @@ declare class JsonApiClient {
86
89
  * DELETE /{resourceType}/{id}
87
90
  */
88
91
  remove(definition: ResourceDefinition, id: string, options?: AxiosRequestConfig): Promise<void>;
92
+ /**
93
+ * GET /{resourceType}/{id}/relationships/{relationshipName}
94
+ */
95
+ getRelationship(definition: ResourceDefinition, id: string, relationshipName: string, options?: AxiosRequestConfig): Promise<RelationshipResponse>;
96
+ /**
97
+ * POST /{resourceType}/{id}/relationships/{relationshipName}
98
+ * Add members to a to-many relationship.
99
+ */
100
+ addRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: {
101
+ type: string;
102
+ id: string;
103
+ }[], options?: AxiosRequestConfig): Promise<RelationshipResponse>;
104
+ /**
105
+ * PATCH /{resourceType}/{id}/relationships/{relationshipName}
106
+ * Replace a relationship entirely.
107
+ */
108
+ replaceRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: ResourceLinkage, options?: AxiosRequestConfig): Promise<RelationshipResponse>;
109
+ /**
110
+ * DELETE /{resourceType}/{id}/relationships/{relationshipName}
111
+ * Remove members from a to-many relationship.
112
+ */
113
+ removeRelationship(definition: ResourceDefinition, id: string, relationshipName: string, data: {
114
+ type: string;
115
+ id: string;
116
+ }[], options?: AxiosRequestConfig): Promise<RelationshipResponse>;
89
117
  /**
90
118
  * Update client configuration at runtime.
91
119
  */
@@ -126,5 +154,23 @@ declare function serializeUpdatePayload(definition: ResourceDefinition, id: stri
126
154
  * `{ sort: ["-createdAt", "title"] }` → `"sort=-createdAt,title"`
127
155
  */
128
156
  declare function serializeQueryParams(params: Record<string, unknown>): Record<string, string>;
157
+ /**
158
+ * Wrap resource linkage in a JSON:API request body for relationship endpoints.
159
+ */
160
+ declare function serializeRelationshipPayload(data: {
161
+ type: string;
162
+ id: string;
163
+ } | {
164
+ type: string;
165
+ id: string;
166
+ }[] | null): {
167
+ data: {
168
+ type: string;
169
+ id: string;
170
+ } | {
171
+ type: string;
172
+ id: string;
173
+ }[] | null;
174
+ };
129
175
 
130
- export { type ClientConfig, type CreatePayload, JsonApiClient, type QueryParams, type RelationshipClause, type SimplifiedHasMany, type SimplifiedHasOne, type SimplifiedRelationships, type UpdatePayload, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationships, serializeUpdatePayload };
176
+ export { type ClientConfig, type CreatePayload, JsonApiClient, type QueryParams, type RelationshipClause, type RelationshipResponse, type ResourceLinkage, type UpdatePayload, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationshipPayload, serializeRelationships, serializeUpdatePayload };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
+ var jsonapiDsl = require('@leodamours/jsonapi-dsl');
4
5
 
5
6
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
7
 
@@ -64,6 +65,9 @@ function serializeQueryParams(params) {
64
65
  }
65
66
  return out;
66
67
  }
68
+ function serializeRelationshipPayload(data) {
69
+ return { data };
70
+ }
67
71
 
68
72
  // src/client.ts
69
73
  var JsonApiClient = class {
@@ -124,6 +128,56 @@ var JsonApiClient = class {
124
128
  async remove(definition, id, options) {
125
129
  await this.axios.delete(`/${definition.resourceType}/${id}`, options);
126
130
  }
131
+ // -------------------------------------------------------------------------
132
+ // Relationship endpoints
133
+ // -------------------------------------------------------------------------
134
+ /**
135
+ * GET /{resourceType}/{id}/relationships/{relationshipName}
136
+ */
137
+ async getRelationship(definition, id, relationshipName, options) {
138
+ const res = await this.axios.get(
139
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
140
+ options
141
+ );
142
+ return res.data;
143
+ }
144
+ /**
145
+ * POST /{resourceType}/{id}/relationships/{relationshipName}
146
+ * Add members to a to-many relationship.
147
+ */
148
+ async addRelationship(definition, id, relationshipName, data, options) {
149
+ const body = serializeRelationshipPayload(data);
150
+ const res = await this.axios.post(
151
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
152
+ body,
153
+ options
154
+ );
155
+ return res.data;
156
+ }
157
+ /**
158
+ * PATCH /{resourceType}/{id}/relationships/{relationshipName}
159
+ * Replace a relationship entirely.
160
+ */
161
+ async replaceRelationship(definition, id, relationshipName, data, options) {
162
+ const body = serializeRelationshipPayload(data);
163
+ const res = await this.axios.patch(
164
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
165
+ body,
166
+ options
167
+ );
168
+ return res.data;
169
+ }
170
+ /**
171
+ * DELETE /{resourceType}/{id}/relationships/{relationshipName}
172
+ * Remove members from a to-many relationship.
173
+ */
174
+ async removeRelationship(definition, id, relationshipName, data, options) {
175
+ const res = await this.axios.delete(
176
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
177
+ { data: serializeRelationshipPayload(data), ...options }
178
+ );
179
+ return res.data;
180
+ }
127
181
  /**
128
182
  * Update client configuration at runtime.
129
183
  */
@@ -151,10 +205,19 @@ function createClient(config) {
151
205
  return new JsonApiClient(config);
152
206
  }
153
207
 
208
+ Object.defineProperty(exports, "deserialize", {
209
+ enumerable: true,
210
+ get: function () { return jsonapiDsl.deserialize; }
211
+ });
212
+ Object.defineProperty(exports, "deserializeMany", {
213
+ enumerable: true,
214
+ get: function () { return jsonapiDsl.deserializeMany; }
215
+ });
154
216
  exports.JsonApiClient = JsonApiClient;
155
217
  exports.createClient = createClient;
156
218
  exports.serializeCreatePayload = serializeCreatePayload;
157
219
  exports.serializeQueryParams = serializeQueryParams;
220
+ exports.serializeRelationshipPayload = serializeRelationshipPayload;
158
221
  exports.serializeRelationships = serializeRelationships;
159
222
  exports.serializeUpdatePayload = serializeUpdatePayload;
160
223
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/serialize.ts","../src/client.ts"],"names":["axios"],"mappings":"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","file":"index.js","sourcesContent":["import type { ResourceDefinition, JsonApiRelationships } from \"@leodamours/jsonapi-dsl\";\n\n/**\n * Wrap simplified relationship values in `{ data: ... }` per JSON:API spec.\n */\nexport function serializeRelationships(\n simplified: Record<string, unknown> | undefined,\n definition: ResourceDefinition,\n): JsonApiRelationships | undefined {\n if (!simplified) return undefined;\n\n const result: JsonApiRelationships = {};\n let hasKeys = false;\n\n for (const key of Object.keys(simplified)) {\n const value = simplified[key];\n if (value === undefined) continue;\n\n const relDef = definition.relationships[key];\n if (!relDef) continue;\n\n hasKeys = true;\n\n if (relDef._rel === \"one\") {\n result[key] = { data: value as { type: string; id: string } | null };\n } else {\n result[key] = { data: value as { type: string; id: string }[] };\n }\n }\n\n return hasKeys ? result : undefined;\n}\n\n/**\n * Build a JSON:API request body for creating a resource.\n */\nexport function serializeCreatePayload(\n definition: ResourceDefinition,\n payload: { attributes: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n attributes: payload.attributes,\n };\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Build a JSON:API request body for updating a resource.\n */\nexport function serializeUpdatePayload(\n definition: ResourceDefinition,\n id: string,\n payload: { attributes?: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n id,\n };\n\n if (payload.attributes && Object.keys(payload.attributes).length > 0) {\n data.attributes = payload.attributes;\n }\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Encode query params using JSON:API bracket conventions.\n *\n * `{ page: { size: 10 } }` → `\"page[size]=10\"`\n * `{ sort: [\"-createdAt\", \"title\"] }` → `\"sort=-createdAt,title\"`\n */\nexport function serializeQueryParams(params: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (value == null) continue;\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n for (const [nested, nv] of Object.entries(value as Record<string, unknown>)) {\n if (nv != null) out[`${key}[${nested}]`] = String(nv);\n }\n } else if (Array.isArray(value)) {\n out[key] = value.join(\",\");\n } else {\n out[key] = String(value);\n }\n }\n\n return out;\n}\n","import axios, { type AxiosInstance, type AxiosRequestConfig } from \"axios\";\nimport type {\n ResourceDefinition,\n InferResource,\n JsonApiDocument,\n JsonApiCollectionDocument,\n} from \"@leodamours/jsonapi-dsl\";\nimport type { ClientConfig, QueryParams, CreatePayload, UpdatePayload } from \"./types\";\nimport { serializeCreatePayload, serializeUpdatePayload, serializeQueryParams } from \"./serialize\";\n\nexport class JsonApiClient {\n private axios: AxiosInstance;\n\n constructor(config: ClientConfig) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/vnd.api+json\",\n Accept: \"application/vnd.api+json\",\n ...config.headers,\n };\n\n if (config.jwtToken) {\n headers.Authorization = `Bearer ${config.jwtToken}`;\n }\n\n this.axios = axios.create({\n baseURL: config.baseURL,\n headers,\n timeout: config.timeout,\n });\n }\n\n /**\n * POST /{resourceType}\n */\n async create<Def extends ResourceDefinition>(\n definition: Def,\n payload: CreatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeCreatePayload(definition, payload as any);\n const res = await this.axios.post(`/${definition.resourceType}`, body, options);\n return res.data;\n }\n\n /**\n * GET /{resourceType}/{id}\n */\n async findOne<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}/${id}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * GET /{resourceType}\n */\n async findAll<Def extends ResourceDefinition>(\n definition: Def,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiCollectionDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}\n */\n async update<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n payload: UpdatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeUpdatePayload(definition, id, payload as any);\n const res = await this.axios.patch(`/${definition.resourceType}/${id}`, body, options);\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}\n */\n async remove(\n definition: ResourceDefinition,\n id: string,\n options?: AxiosRequestConfig,\n ): Promise<void> {\n await this.axios.delete(`/${definition.resourceType}/${id}`, options);\n }\n\n /**\n * Update client configuration at runtime.\n */\n updateConfig(config: Partial<ClientConfig>): void {\n if (config.baseURL) {\n this.axios.defaults.baseURL = config.baseURL;\n }\n if (config.headers) {\n Object.assign(this.axios.defaults.headers.common, config.headers);\n }\n if (config.jwtToken) {\n this.axios.defaults.headers.common.Authorization = `Bearer ${config.jwtToken}`;\n } else if (config.jwtToken === null) {\n delete this.axios.defaults.headers.common.Authorization;\n }\n }\n\n /**\n * Access the underlying Axios instance for advanced use.\n */\n getAxiosInstance(): AxiosInstance {\n return this.axios;\n }\n}\n\nexport function createClient(config: ClientConfig): JsonApiClient {\n return new JsonApiClient(config);\n}\n"]}
1
+ {"version":3,"sources":["../src/serialize.ts","../src/client.ts"],"names":["axios"],"mappings":"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","file":"index.js","sourcesContent":["import type { ResourceDefinition, JsonApiRelationships } from \"@leodamours/jsonapi-dsl\";\n\n/**\n * Wrap simplified relationship values in `{ data: ... }` per JSON:API spec.\n */\nexport function serializeRelationships(\n simplified: Record<string, unknown> | undefined,\n definition: ResourceDefinition,\n): JsonApiRelationships | undefined {\n if (!simplified) return undefined;\n\n const result: JsonApiRelationships = {};\n let hasKeys = false;\n\n for (const key of Object.keys(simplified)) {\n const value = simplified[key];\n if (value === undefined) continue;\n\n const relDef = definition.relationships[key];\n if (!relDef) continue;\n\n hasKeys = true;\n\n if (relDef._rel === \"one\") {\n result[key] = { data: value as { type: string; id: string } | null };\n } else {\n result[key] = { data: value as { type: string; id: string }[] };\n }\n }\n\n return hasKeys ? result : undefined;\n}\n\n/**\n * Build a JSON:API request body for creating a resource.\n */\nexport function serializeCreatePayload(\n definition: ResourceDefinition,\n payload: { attributes: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n attributes: payload.attributes,\n };\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Build a JSON:API request body for updating a resource.\n */\nexport function serializeUpdatePayload(\n definition: ResourceDefinition,\n id: string,\n payload: { attributes?: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n id,\n };\n\n if (payload.attributes && Object.keys(payload.attributes).length > 0) {\n data.attributes = payload.attributes;\n }\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Encode query params using JSON:API bracket conventions.\n *\n * `{ page: { size: 10 } }` → `\"page[size]=10\"`\n * `{ sort: [\"-createdAt\", \"title\"] }` → `\"sort=-createdAt,title\"`\n */\nexport function serializeQueryParams(params: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (value == null) continue;\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n for (const [nested, nv] of Object.entries(value as Record<string, unknown>)) {\n if (nv != null) out[`${key}[${nested}]`] = String(nv);\n }\n } else if (Array.isArray(value)) {\n out[key] = value.join(\",\");\n } else {\n out[key] = String(value);\n }\n }\n\n return out;\n}\n\n/**\n * Wrap resource linkage in a JSON:API request body for relationship endpoints.\n */\nexport function serializeRelationshipPayload(\n data: { type: string; id: string } | { type: string; id: string }[] | null,\n): { data: { type: string; id: string } | { type: string; id: string }[] | null } {\n return { data };\n}\n","import axios, { type AxiosInstance, type AxiosRequestConfig } from \"axios\";\nimport type {\n ResourceDefinition,\n InferResource,\n JsonApiDocument,\n JsonApiCollectionDocument,\n} from \"@leodamours/jsonapi-dsl\";\nimport type { ClientConfig, QueryParams, CreatePayload, UpdatePayload, ResourceLinkage, RelationshipResponse } from \"./types\";\nimport { serializeCreatePayload, serializeUpdatePayload, serializeQueryParams, serializeRelationshipPayload } from \"./serialize\";\n\nexport class JsonApiClient {\n private axios: AxiosInstance;\n\n constructor(config: ClientConfig) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/vnd.api+json\",\n Accept: \"application/vnd.api+json\",\n ...config.headers,\n };\n\n if (config.jwtToken) {\n headers.Authorization = `Bearer ${config.jwtToken}`;\n }\n\n this.axios = axios.create({\n baseURL: config.baseURL,\n headers,\n timeout: config.timeout,\n });\n }\n\n /**\n * POST /{resourceType}\n */\n async create<Def extends ResourceDefinition>(\n definition: Def,\n payload: CreatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeCreatePayload(definition, payload as any);\n const res = await this.axios.post(`/${definition.resourceType}`, body, options);\n return res.data;\n }\n\n /**\n * GET /{resourceType}/{id}\n */\n async findOne<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}/${id}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * GET /{resourceType}\n */\n async findAll<Def extends ResourceDefinition>(\n definition: Def,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiCollectionDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}\n */\n async update<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n payload: UpdatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeUpdatePayload(definition, id, payload as any);\n const res = await this.axios.patch(`/${definition.resourceType}/${id}`, body, options);\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}\n */\n async remove(\n definition: ResourceDefinition,\n id: string,\n options?: AxiosRequestConfig,\n ): Promise<void> {\n await this.axios.delete(`/${definition.resourceType}/${id}`, options);\n }\n\n // -------------------------------------------------------------------------\n // Relationship endpoints\n // -------------------------------------------------------------------------\n\n /**\n * GET /{resourceType}/{id}/relationships/{relationshipName}\n */\n async getRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const res = await this.axios.get(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n options,\n );\n return res.data;\n }\n\n /**\n * POST /{resourceType}/{id}/relationships/{relationshipName}\n * Add members to a to-many relationship.\n */\n async addRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: { type: string; id: string }[],\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const body = serializeRelationshipPayload(data);\n const res = await this.axios.post(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n body,\n options,\n );\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}/relationships/{relationshipName}\n * Replace a relationship entirely.\n */\n async replaceRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: ResourceLinkage,\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const body = serializeRelationshipPayload(data);\n const res = await this.axios.patch(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n body,\n options,\n );\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}/relationships/{relationshipName}\n * Remove members from a to-many relationship.\n */\n async removeRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: { type: string; id: string }[],\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const res = await this.axios.delete(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n { data: serializeRelationshipPayload(data), ...options },\n );\n return res.data;\n }\n\n /**\n * Update client configuration at runtime.\n */\n updateConfig(config: Partial<ClientConfig>): void {\n if (config.baseURL) {\n this.axios.defaults.baseURL = config.baseURL;\n }\n if (config.headers) {\n Object.assign(this.axios.defaults.headers.common, config.headers);\n }\n if (config.jwtToken) {\n this.axios.defaults.headers.common.Authorization = `Bearer ${config.jwtToken}`;\n } else if (config.jwtToken === null) {\n delete this.axios.defaults.headers.common.Authorization;\n }\n }\n\n /**\n * Access the underlying Axios instance for advanced use.\n */\n getAxiosInstance(): AxiosInstance {\n return this.axios;\n }\n}\n\nexport function createClient(config: ClientConfig): JsonApiClient {\n return new JsonApiClient(config);\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios';
2
+ export { deserialize, deserializeMany } from '@leodamours/jsonapi-dsl';
2
3
 
3
4
  // src/client.ts
4
5
 
@@ -58,6 +59,9 @@ function serializeQueryParams(params) {
58
59
  }
59
60
  return out;
60
61
  }
62
+ function serializeRelationshipPayload(data) {
63
+ return { data };
64
+ }
61
65
 
62
66
  // src/client.ts
63
67
  var JsonApiClient = class {
@@ -118,6 +122,56 @@ var JsonApiClient = class {
118
122
  async remove(definition, id, options) {
119
123
  await this.axios.delete(`/${definition.resourceType}/${id}`, options);
120
124
  }
125
+ // -------------------------------------------------------------------------
126
+ // Relationship endpoints
127
+ // -------------------------------------------------------------------------
128
+ /**
129
+ * GET /{resourceType}/{id}/relationships/{relationshipName}
130
+ */
131
+ async getRelationship(definition, id, relationshipName, options) {
132
+ const res = await this.axios.get(
133
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
134
+ options
135
+ );
136
+ return res.data;
137
+ }
138
+ /**
139
+ * POST /{resourceType}/{id}/relationships/{relationshipName}
140
+ * Add members to a to-many relationship.
141
+ */
142
+ async addRelationship(definition, id, relationshipName, data, options) {
143
+ const body = serializeRelationshipPayload(data);
144
+ const res = await this.axios.post(
145
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
146
+ body,
147
+ options
148
+ );
149
+ return res.data;
150
+ }
151
+ /**
152
+ * PATCH /{resourceType}/{id}/relationships/{relationshipName}
153
+ * Replace a relationship entirely.
154
+ */
155
+ async replaceRelationship(definition, id, relationshipName, data, options) {
156
+ const body = serializeRelationshipPayload(data);
157
+ const res = await this.axios.patch(
158
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
159
+ body,
160
+ options
161
+ );
162
+ return res.data;
163
+ }
164
+ /**
165
+ * DELETE /{resourceType}/{id}/relationships/{relationshipName}
166
+ * Remove members from a to-many relationship.
167
+ */
168
+ async removeRelationship(definition, id, relationshipName, data, options) {
169
+ const res = await this.axios.delete(
170
+ `/${definition.resourceType}/${id}/relationships/${relationshipName}`,
171
+ { data: serializeRelationshipPayload(data), ...options }
172
+ );
173
+ return res.data;
174
+ }
121
175
  /**
122
176
  * Update client configuration at runtime.
123
177
  */
@@ -145,6 +199,6 @@ function createClient(config) {
145
199
  return new JsonApiClient(config);
146
200
  }
147
201
 
148
- export { JsonApiClient, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationships, serializeUpdatePayload };
202
+ export { JsonApiClient, createClient, serializeCreatePayload, serializeQueryParams, serializeRelationshipPayload, serializeRelationships, serializeUpdatePayload };
149
203
  //# sourceMappingURL=index.mjs.map
150
204
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/serialize.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ResourceDefinition, JsonApiRelationships } from \"@leodamours/jsonapi-dsl\";\n\n/**\n * Wrap simplified relationship values in `{ data: ... }` per JSON:API spec.\n */\nexport function serializeRelationships(\n simplified: Record<string, unknown> | undefined,\n definition: ResourceDefinition,\n): JsonApiRelationships | undefined {\n if (!simplified) return undefined;\n\n const result: JsonApiRelationships = {};\n let hasKeys = false;\n\n for (const key of Object.keys(simplified)) {\n const value = simplified[key];\n if (value === undefined) continue;\n\n const relDef = definition.relationships[key];\n if (!relDef) continue;\n\n hasKeys = true;\n\n if (relDef._rel === \"one\") {\n result[key] = { data: value as { type: string; id: string } | null };\n } else {\n result[key] = { data: value as { type: string; id: string }[] };\n }\n }\n\n return hasKeys ? result : undefined;\n}\n\n/**\n * Build a JSON:API request body for creating a resource.\n */\nexport function serializeCreatePayload(\n definition: ResourceDefinition,\n payload: { attributes: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n attributes: payload.attributes,\n };\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Build a JSON:API request body for updating a resource.\n */\nexport function serializeUpdatePayload(\n definition: ResourceDefinition,\n id: string,\n payload: { attributes?: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n id,\n };\n\n if (payload.attributes && Object.keys(payload.attributes).length > 0) {\n data.attributes = payload.attributes;\n }\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Encode query params using JSON:API bracket conventions.\n *\n * `{ page: { size: 10 } }` → `\"page[size]=10\"`\n * `{ sort: [\"-createdAt\", \"title\"] }` → `\"sort=-createdAt,title\"`\n */\nexport function serializeQueryParams(params: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (value == null) continue;\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n for (const [nested, nv] of Object.entries(value as Record<string, unknown>)) {\n if (nv != null) out[`${key}[${nested}]`] = String(nv);\n }\n } else if (Array.isArray(value)) {\n out[key] = value.join(\",\");\n } else {\n out[key] = String(value);\n }\n }\n\n return out;\n}\n","import axios, { type AxiosInstance, type AxiosRequestConfig } from \"axios\";\nimport type {\n ResourceDefinition,\n InferResource,\n JsonApiDocument,\n JsonApiCollectionDocument,\n} from \"@leodamours/jsonapi-dsl\";\nimport type { ClientConfig, QueryParams, CreatePayload, UpdatePayload } from \"./types\";\nimport { serializeCreatePayload, serializeUpdatePayload, serializeQueryParams } from \"./serialize\";\n\nexport class JsonApiClient {\n private axios: AxiosInstance;\n\n constructor(config: ClientConfig) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/vnd.api+json\",\n Accept: \"application/vnd.api+json\",\n ...config.headers,\n };\n\n if (config.jwtToken) {\n headers.Authorization = `Bearer ${config.jwtToken}`;\n }\n\n this.axios = axios.create({\n baseURL: config.baseURL,\n headers,\n timeout: config.timeout,\n });\n }\n\n /**\n * POST /{resourceType}\n */\n async create<Def extends ResourceDefinition>(\n definition: Def,\n payload: CreatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeCreatePayload(definition, payload as any);\n const res = await this.axios.post(`/${definition.resourceType}`, body, options);\n return res.data;\n }\n\n /**\n * GET /{resourceType}/{id}\n */\n async findOne<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}/${id}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * GET /{resourceType}\n */\n async findAll<Def extends ResourceDefinition>(\n definition: Def,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiCollectionDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}\n */\n async update<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n payload: UpdatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeUpdatePayload(definition, id, payload as any);\n const res = await this.axios.patch(`/${definition.resourceType}/${id}`, body, options);\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}\n */\n async remove(\n definition: ResourceDefinition,\n id: string,\n options?: AxiosRequestConfig,\n ): Promise<void> {\n await this.axios.delete(`/${definition.resourceType}/${id}`, options);\n }\n\n /**\n * Update client configuration at runtime.\n */\n updateConfig(config: Partial<ClientConfig>): void {\n if (config.baseURL) {\n this.axios.defaults.baseURL = config.baseURL;\n }\n if (config.headers) {\n Object.assign(this.axios.defaults.headers.common, config.headers);\n }\n if (config.jwtToken) {\n this.axios.defaults.headers.common.Authorization = `Bearer ${config.jwtToken}`;\n } else if (config.jwtToken === null) {\n delete this.axios.defaults.headers.common.Authorization;\n }\n }\n\n /**\n * Access the underlying Axios instance for advanced use.\n */\n getAxiosInstance(): AxiosInstance {\n return this.axios;\n }\n}\n\nexport function createClient(config: ClientConfig): JsonApiClient {\n return new JsonApiClient(config);\n}\n"]}
1
+ {"version":3,"sources":["../src/serialize.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ResourceDefinition, JsonApiRelationships } from \"@leodamours/jsonapi-dsl\";\n\n/**\n * Wrap simplified relationship values in `{ data: ... }` per JSON:API spec.\n */\nexport function serializeRelationships(\n simplified: Record<string, unknown> | undefined,\n definition: ResourceDefinition,\n): JsonApiRelationships | undefined {\n if (!simplified) return undefined;\n\n const result: JsonApiRelationships = {};\n let hasKeys = false;\n\n for (const key of Object.keys(simplified)) {\n const value = simplified[key];\n if (value === undefined) continue;\n\n const relDef = definition.relationships[key];\n if (!relDef) continue;\n\n hasKeys = true;\n\n if (relDef._rel === \"one\") {\n result[key] = { data: value as { type: string; id: string } | null };\n } else {\n result[key] = { data: value as { type: string; id: string }[] };\n }\n }\n\n return hasKeys ? result : undefined;\n}\n\n/**\n * Build a JSON:API request body for creating a resource.\n */\nexport function serializeCreatePayload(\n definition: ResourceDefinition,\n payload: { attributes: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n attributes: payload.attributes,\n };\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Build a JSON:API request body for updating a resource.\n */\nexport function serializeUpdatePayload(\n definition: ResourceDefinition,\n id: string,\n payload: { attributes?: Record<string, unknown>; relationships?: Record<string, unknown> },\n): { data: Record<string, unknown> } {\n const data: Record<string, unknown> = {\n type: definition.resourceType,\n id,\n };\n\n if (payload.attributes && Object.keys(payload.attributes).length > 0) {\n data.attributes = payload.attributes;\n }\n\n const rels = serializeRelationships(payload.relationships, definition);\n if (rels) data.relationships = rels;\n\n return { data };\n}\n\n/**\n * Encode query params using JSON:API bracket conventions.\n *\n * `{ page: { size: 10 } }` → `\"page[size]=10\"`\n * `{ sort: [\"-createdAt\", \"title\"] }` → `\"sort=-createdAt,title\"`\n */\nexport function serializeQueryParams(params: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (value == null) continue;\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n for (const [nested, nv] of Object.entries(value as Record<string, unknown>)) {\n if (nv != null) out[`${key}[${nested}]`] = String(nv);\n }\n } else if (Array.isArray(value)) {\n out[key] = value.join(\",\");\n } else {\n out[key] = String(value);\n }\n }\n\n return out;\n}\n\n/**\n * Wrap resource linkage in a JSON:API request body for relationship endpoints.\n */\nexport function serializeRelationshipPayload(\n data: { type: string; id: string } | { type: string; id: string }[] | null,\n): { data: { type: string; id: string } | { type: string; id: string }[] | null } {\n return { data };\n}\n","import axios, { type AxiosInstance, type AxiosRequestConfig } from \"axios\";\nimport type {\n ResourceDefinition,\n InferResource,\n JsonApiDocument,\n JsonApiCollectionDocument,\n} from \"@leodamours/jsonapi-dsl\";\nimport type { ClientConfig, QueryParams, CreatePayload, UpdatePayload, ResourceLinkage, RelationshipResponse } from \"./types\";\nimport { serializeCreatePayload, serializeUpdatePayload, serializeQueryParams, serializeRelationshipPayload } from \"./serialize\";\n\nexport class JsonApiClient {\n private axios: AxiosInstance;\n\n constructor(config: ClientConfig) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/vnd.api+json\",\n Accept: \"application/vnd.api+json\",\n ...config.headers,\n };\n\n if (config.jwtToken) {\n headers.Authorization = `Bearer ${config.jwtToken}`;\n }\n\n this.axios = axios.create({\n baseURL: config.baseURL,\n headers,\n timeout: config.timeout,\n });\n }\n\n /**\n * POST /{resourceType}\n */\n async create<Def extends ResourceDefinition>(\n definition: Def,\n payload: CreatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeCreatePayload(definition, payload as any);\n const res = await this.axios.post(`/${definition.resourceType}`, body, options);\n return res.data;\n }\n\n /**\n * GET /{resourceType}/{id}\n */\n async findOne<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}/${id}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * GET /{resourceType}\n */\n async findAll<Def extends ResourceDefinition>(\n definition: Def,\n params?: QueryParams,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiCollectionDocument<InferResource<Def>>> {\n const res = await this.axios.get(`/${definition.resourceType}`, {\n params: params ? serializeQueryParams(params) : undefined,\n ...options,\n });\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}\n */\n async update<Def extends ResourceDefinition>(\n definition: Def,\n id: string,\n payload: UpdatePayload<Def>,\n options?: AxiosRequestConfig,\n ): Promise<JsonApiDocument<InferResource<Def>>> {\n const body = serializeUpdatePayload(definition, id, payload as any);\n const res = await this.axios.patch(`/${definition.resourceType}/${id}`, body, options);\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}\n */\n async remove(\n definition: ResourceDefinition,\n id: string,\n options?: AxiosRequestConfig,\n ): Promise<void> {\n await this.axios.delete(`/${definition.resourceType}/${id}`, options);\n }\n\n // -------------------------------------------------------------------------\n // Relationship endpoints\n // -------------------------------------------------------------------------\n\n /**\n * GET /{resourceType}/{id}/relationships/{relationshipName}\n */\n async getRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const res = await this.axios.get(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n options,\n );\n return res.data;\n }\n\n /**\n * POST /{resourceType}/{id}/relationships/{relationshipName}\n * Add members to a to-many relationship.\n */\n async addRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: { type: string; id: string }[],\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const body = serializeRelationshipPayload(data);\n const res = await this.axios.post(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n body,\n options,\n );\n return res.data;\n }\n\n /**\n * PATCH /{resourceType}/{id}/relationships/{relationshipName}\n * Replace a relationship entirely.\n */\n async replaceRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: ResourceLinkage,\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const body = serializeRelationshipPayload(data);\n const res = await this.axios.patch(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n body,\n options,\n );\n return res.data;\n }\n\n /**\n * DELETE /{resourceType}/{id}/relationships/{relationshipName}\n * Remove members from a to-many relationship.\n */\n async removeRelationship(\n definition: ResourceDefinition,\n id: string,\n relationshipName: string,\n data: { type: string; id: string }[],\n options?: AxiosRequestConfig,\n ): Promise<RelationshipResponse> {\n const res = await this.axios.delete(\n `/${definition.resourceType}/${id}/relationships/${relationshipName}`,\n { data: serializeRelationshipPayload(data), ...options },\n );\n return res.data;\n }\n\n /**\n * Update client configuration at runtime.\n */\n updateConfig(config: Partial<ClientConfig>): void {\n if (config.baseURL) {\n this.axios.defaults.baseURL = config.baseURL;\n }\n if (config.headers) {\n Object.assign(this.axios.defaults.headers.common, config.headers);\n }\n if (config.jwtToken) {\n this.axios.defaults.headers.common.Authorization = `Bearer ${config.jwtToken}`;\n } else if (config.jwtToken === null) {\n delete this.axios.defaults.headers.common.Authorization;\n }\n }\n\n /**\n * Access the underlying Axios instance for advanced use.\n */\n getAxiosInstance(): AxiosInstance {\n return this.axios;\n }\n}\n\nexport function createClient(config: ClientConfig): JsonApiClient {\n return new JsonApiClient(config);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leodamours/jsonapi-client",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A typed HTTP client for JSON:API — powered by Axios with full type inference",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",