@jsondrop/core 0.0.0-stage → 0.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.
Files changed (48) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +126 -2
  3. package/dist/auth.d.ts +74 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +123 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/client.d.ts +175 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +280 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/deserialize.d.ts +15 -0
  12. package/dist/deserialize.d.ts.map +1 -0
  13. package/dist/deserialize.js +64 -0
  14. package/dist/deserialize.js.map +1 -0
  15. package/dist/errors.d.ts +18 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +26 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +9 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/oauth.d.ts +110 -0
  24. package/dist/oauth.d.ts.map +1 -0
  25. package/dist/oauth.js +184 -0
  26. package/dist/oauth.js.map +1 -0
  27. package/dist/query.d.ts +52 -0
  28. package/dist/query.d.ts.map +1 -0
  29. package/dist/query.js +62 -0
  30. package/dist/query.js.map +1 -0
  31. package/dist/serialize.d.ts +31 -0
  32. package/dist/serialize.d.ts.map +1 -0
  33. package/dist/serialize.js +22 -0
  34. package/dist/serialize.js.map +1 -0
  35. package/dist/types.d.ts +129 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +8 -0
  38. package/dist/types.js.map +1 -0
  39. package/package.json +50 -4
  40. package/src/auth.ts +167 -0
  41. package/src/client.ts +409 -0
  42. package/src/deserialize.ts +68 -0
  43. package/src/errors.ts +25 -0
  44. package/src/index.ts +8 -0
  45. package/src/oauth.ts +254 -0
  46. package/src/query.ts +125 -0
  47. package/src/serialize.ts +41 -0
  48. package/src/types.ts +145 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Valentino Međimorec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,127 @@
1
- # Temporary Holding Version
1
+ # @jsondrop/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A typed, framework-agnostic client for Drupal's JSON:API. No dependencies.
4
+
5
+ ```sh
6
+ npm install @jsondrop/core
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```ts
12
+ import { JsonApiClient, clientCredentials } from '@jsondrop/core';
13
+
14
+ const drupal = new JsonApiClient({
15
+ baseUrl: 'https://cms.example.com',
16
+ auth: clientCredentials({ clientId: '…', clientSecret: '…', scope: 'content:read' }),
17
+ });
18
+
19
+ const articles = await drupal.getCollection('node--article', {
20
+ query: {
21
+ include: ['field_image', 'uid'],
22
+ filter: { status: 1 },
23
+ sort: ['-created'],
24
+ page: { limit: 10 },
25
+ },
26
+ });
27
+
28
+ articles[0].field_image; // the included file--file, with relationshipMeta.alt
29
+ ```
30
+
31
+ Responses are deserialized: attributes and relationships sit on one
32
+ object, and relationships are replaced by their included resources
33
+ (recursively). A relationship whose target was not included is a stub
34
+ (`{ id, type, stub: true }`, see `isStub`).
35
+
36
+ ## Typing your resources
37
+
38
+ Register resource types once, by declaration merging:
39
+
40
+ ```ts
41
+ import type { ResourceBase, ToOne, ToMany } from '@jsondrop/core';
42
+
43
+ interface NodeArticle extends ResourceBase<'node--article'> {
44
+ title: string;
45
+ body: { value: string; processed: string } | null;
46
+ field_tags: ToMany<TaxonomyTerm>;
47
+ field_image: ToOne<File>;
48
+ }
49
+
50
+ declare module '@jsondrop/core' {
51
+ interface ResourceTypes {
52
+ 'node--article': NodeArticle;
53
+ }
54
+ }
55
+
56
+ const article = await drupal.getResource('node--article', id); // NodeArticle
57
+ ```
58
+
59
+ ## API
60
+
61
+ **Resources**: `getResource`, `getCollection`, `getPage` (with `next()`),
62
+ `iterate` (async, follows `next` links), `createResource`, `updateResource`,
63
+ `deleteResource`, `uploadFile`.
64
+
65
+ **Requests** relative to the API prefix, for custom resources:
66
+ `get`, `post`, `patch`, `delete`, `request`. They return the raw document
67
+ alongside `data`, `meta`, `links`, `status` and `headers`.
68
+
69
+ **Options** (client, some per request):
70
+
71
+ | Option | |
72
+ |---|---|
73
+ | `baseUrl` | Site origin |
74
+ | `apiPrefix` | Default `/jsonapi` |
75
+ | `locale`, `defaultLocale` | Language path prefix (`/de/jsonapi/…`) |
76
+ | `auth` | `bearer`, `basic`, `sessionCookie`, `clientCredentials`, or your own `AuthStrategy`; `false` per request |
77
+ | `headers` | Object or (async) function |
78
+ | `resourcePaths` | Type → path, for types renamed with JSON:API Extras |
79
+ | `fetchOptions` | Passed to `fetch`, e.g. `{ next: { revalidate: 60, tags: ['node:1'] } }` |
80
+ | `onResponse` | Called after each request, for logging |
81
+
82
+ ## Users logging in (OAuth)
83
+
84
+ `OAuthClient` runs simple_oauth 6.x's authorization code flow with PKCE
85
+ and refresh tokens (6.x dropped the password and implicit grants):
86
+
87
+ ```ts
88
+ import { OAuthClient } from '@jsondrop/core';
89
+
90
+ const oauth = new OAuthClient({ baseUrl, clientId, clientSecret, redirectUri, scope: 'openid profile' });
91
+
92
+ // 1. Redirect to Drupal; keep state + codeVerifier in an httpOnly cookie.
93
+ const { url, state, codeVerifier } = await oauth.startLogin();
94
+ // 2. On the redirect back: checks the state, exchanges the code.
95
+ const tokens = await oauth.finishLogin({ code, state: returnedState }, { state, codeVerifier });
96
+ // 3. Store the tokens server-side; requests as the user:
97
+ const asUser = drupal.extend({ auth: oauth.strategy(tokenStore) });
98
+ ```
99
+
100
+ The strategy refreshes the token before it expires and after a 401.
101
+ Refresh tokens are single-use, so concurrent refreshes share one request:
102
+ keep one `OAuthClient` per app. When Drupal rejects the refresh token, the
103
+ store is emptied and requests throw `LoginRequiredError`: send the user to
104
+ log in again. `oauth.userInfo(accessToken)` reads OpenID Connect claims.
105
+ See [example 7](../../examples/07-user-login.ts).
106
+
107
+ **Renamed resources** (JSON:API Extras): the API's entry point lists every
108
+ type's path, so let the client read them once at startup instead of
109
+ mapping them by hand:
110
+
111
+ ```ts
112
+ const drupal = await new JsonApiClient({ baseUrl }).withDiscoveredPaths();
113
+ drupal.resourcePath('products'); // '/products', as the site configures it
114
+ ```
115
+
116
+ `client.extend({ headers })` derives a client that adds headers (a cart
117
+ token, a market) and shares the parent's auth and token cache.
118
+
119
+ **Errors**: non-2xx responses throw `JsonApiError` with `status`, `errors`
120
+ and the document; its `message` is Drupal's `detail`. On a 401, strategies
121
+ with cached tokens are renewed and the request retried once.
122
+
123
+ **Links**: absolute URLs from documents (`links.next`) are sent to
124
+ `baseUrl`'s origin, so a Drupal behind a proxy that builds links with an
125
+ internal host still works, and credentials never go elsewhere.
126
+
127
+ **Queries** also accept a prebuilt string, e.g. from drupal-jsonapi-params.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Authentication strategies. A strategy returns the headers that
3
+ * authenticate one request; the client calls it before every request.
4
+ *
5
+ * A strategy may also implement `invalidate()`. When a request comes back
6
+ * 401, the client calls it and retries once, so cached tokens that Drupal
7
+ * has revoked are replaced transparently.
8
+ */
9
+ export interface AuthContext {
10
+ /** Site origin the client talks to, without a trailing slash. */
11
+ baseUrl: string;
12
+ fetch: typeof fetch;
13
+ }
14
+ export interface AuthStrategy {
15
+ headers(context: AuthContext): HeadersInit | undefined | Promise<HeadersInit | undefined>;
16
+ /**
17
+ * Called after a 401: drop cached credentials, so the retry gets fresh
18
+ * ones. Return `false` when there is nothing to renew, to skip the retry.
19
+ */
20
+ invalidate?(): void | boolean;
21
+ }
22
+ type Value = string | null | undefined;
23
+ type ValueSource = Value | (() => Value | Promise<Value>);
24
+ /** `Authorization: Bearer <token>`, from a token or a function that returns one. */
25
+ export declare function bearer(token: ValueSource): AuthStrategy;
26
+ /** HTTP Basic authentication (Drupal core's `basic_auth` module). */
27
+ export declare function basic(username: string, password: string): AuthStrategy;
28
+ /**
29
+ * Forwards a Drupal session cookie (`SSESS…=value`), e.g. one a server-side
30
+ * login obtained and the application keeps in its own httpOnly cookie.
31
+ */
32
+ export declare function sessionCookie(cookie: ValueSource): AuthStrategy;
33
+ export interface ClientCredentialsOptions {
34
+ clientId: string;
35
+ clientSecret: string;
36
+ /** Space-separated scopes. */
37
+ scope?: string;
38
+ /** Token endpoint path or absolute URL. Default `/oauth/token` (simple_oauth). */
39
+ tokenUrl?: string;
40
+ /** Renew this many milliseconds before expiry. Default 30 seconds. */
41
+ renewBeforeMs?: number;
42
+ }
43
+ export interface TokenResponse {
44
+ access_token: string;
45
+ token_type?: string;
46
+ expires_in?: number;
47
+ refresh_token?: string;
48
+ scope?: string;
49
+ /** With the `openid` scope (simple_oauth's OpenID Connect). */
50
+ id_token?: string;
51
+ }
52
+ export declare class OAuthError extends Error {
53
+ readonly status: number;
54
+ readonly body: string;
55
+ readonly name = "OAuthError";
56
+ /** The OAuth error code, e.g. `invalid_grant` (expired or reused refresh token), `invalid_client`. */
57
+ readonly error: string | null;
58
+ constructor(status: number, body: string);
59
+ }
60
+ /** POSTs a token request (form-encoded) and returns the token response. */
61
+ export declare function requestToken(context: AuthContext, tokenUrl: string, params: Record<string, string | undefined>): Promise<TokenResponse>;
62
+ /** An absolute URL for a site path, or the URL itself. */
63
+ export declare function absolute(baseUrl: string, pathOrUrl: string): string;
64
+ /**
65
+ * OAuth 2 client credentials (simple_oauth), for server-to-server requests.
66
+ * The token is cached in this strategy until shortly before it expires, and
67
+ * concurrent requests share one token request. Never use it in a browser:
68
+ * it needs the client secret.
69
+ */
70
+ export declare function clientCredentials(options: ClientCredentialsOptions): AuthStrategy & {
71
+ token(context: AuthContext): Promise<string>;
72
+ };
73
+ export {};
74
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAC1F;;;OAGG;IACH,UAAU,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC;CAC/B;AAED,KAAK,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AACvC,KAAK,WAAW,GAAG,KAAK,GAAG,CAAC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAM1D,oFAAoF;AACpF,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAOvD;AAED,qEAAqE;AACrE,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAItE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAO/D;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,UAAW,SAAQ,KAAK;IAKvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM;IAJ1D,SAAkB,IAAI,gBAAgB;IACtC,sGAAsG;IACtG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;gBAET,MAAM,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAY3D;AAED,2EAA2E;AAC3E,wBAAsB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAc7I;AAED,0DAA0D;AAC1D,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,YAAY,GAAG;IAAE,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAqCpI"}
package/dist/auth.js ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Authentication strategies. A strategy returns the headers that
3
+ * authenticate one request; the client calls it before every request.
4
+ *
5
+ * A strategy may also implement `invalidate()`. When a request comes back
6
+ * 401, the client calls it and retries once, so cached tokens that Drupal
7
+ * has revoked are replaced transparently.
8
+ */
9
+ async function value(source) {
10
+ return typeof source === 'function' ? source() : source;
11
+ }
12
+ /** `Authorization: Bearer <token>`, from a token or a function that returns one. */
13
+ export function bearer(token) {
14
+ return {
15
+ async headers() {
16
+ const resolved = await value(token);
17
+ return resolved ? { Authorization: `Bearer ${resolved}` } : undefined;
18
+ },
19
+ };
20
+ }
21
+ /** HTTP Basic authentication (Drupal core's `basic_auth` module). */
22
+ export function basic(username, password) {
23
+ const bytes = new TextEncoder().encode(`${username}:${password}`);
24
+ const encoded = btoa(String.fromCharCode(...bytes));
25
+ return { headers: () => ({ Authorization: `Basic ${encoded}` }) };
26
+ }
27
+ /**
28
+ * Forwards a Drupal session cookie (`SSESS…=value`), e.g. one a server-side
29
+ * login obtained and the application keeps in its own httpOnly cookie.
30
+ */
31
+ export function sessionCookie(cookie) {
32
+ return {
33
+ async headers() {
34
+ const resolved = await value(cookie);
35
+ return resolved ? { Cookie: resolved } : undefined;
36
+ },
37
+ };
38
+ }
39
+ export class OAuthError extends Error {
40
+ status;
41
+ body;
42
+ name = 'OAuthError';
43
+ /** The OAuth error code, e.g. `invalid_grant` (expired or reused refresh token), `invalid_client`. */
44
+ error;
45
+ constructor(status, body) {
46
+ super(`OAuth token request failed: HTTP ${status}${body ? ` ${body}` : ''}`);
47
+ this.status = status;
48
+ this.body = body;
49
+ let error = null;
50
+ try {
51
+ const parsed = JSON.parse(body);
52
+ error = typeof parsed.error === 'string' ? parsed.error : null;
53
+ }
54
+ catch {
55
+ // Not JSON.
56
+ }
57
+ this.error = error;
58
+ }
59
+ }
60
+ /** POSTs a token request (form-encoded) and returns the token response. */
61
+ export async function requestToken(context, tokenUrl, params) {
62
+ const body = new URLSearchParams();
63
+ for (const [name, value] of Object.entries(params)) {
64
+ if (value !== undefined && value !== '')
65
+ body.set(name, value);
66
+ }
67
+ const response = await context.fetch(absolute(context.baseUrl, tokenUrl), {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
70
+ body,
71
+ });
72
+ if (!response.ok) {
73
+ throw new OAuthError(response.status, await response.text());
74
+ }
75
+ return (await response.json());
76
+ }
77
+ /** An absolute URL for a site path, or the URL itself. */
78
+ export function absolute(baseUrl, pathOrUrl) {
79
+ return /^https?:\/\//.test(pathOrUrl) ? pathOrUrl : `${baseUrl}${pathOrUrl}`;
80
+ }
81
+ /**
82
+ * OAuth 2 client credentials (simple_oauth), for server-to-server requests.
83
+ * The token is cached in this strategy until shortly before it expires, and
84
+ * concurrent requests share one token request. Never use it in a browser:
85
+ * it needs the client secret.
86
+ */
87
+ export function clientCredentials(options) {
88
+ const renewBeforeMs = options.renewBeforeMs ?? 30_000;
89
+ let cached = null;
90
+ let pending = null;
91
+ const request = async (context) => {
92
+ const token = await requestToken(context, options.tokenUrl ?? '/oauth/token', {
93
+ grant_type: 'client_credentials',
94
+ client_id: options.clientId,
95
+ client_secret: options.clientSecret,
96
+ scope: options.scope,
97
+ });
98
+ cached = {
99
+ token: token.access_token,
100
+ expiresAt: token.expires_in === undefined ? Number.POSITIVE_INFINITY : Date.now() + token.expires_in * 1000,
101
+ };
102
+ return cached.token;
103
+ };
104
+ const strategy = {
105
+ async token(context) {
106
+ if (cached && cached.expiresAt - renewBeforeMs > Date.now()) {
107
+ return cached.token;
108
+ }
109
+ pending ??= request(context).finally(() => {
110
+ pending = null;
111
+ });
112
+ return pending;
113
+ },
114
+ async headers(context) {
115
+ return { Authorization: `Bearer ${await strategy.token(context)}` };
116
+ },
117
+ invalidate() {
118
+ cached = null;
119
+ },
120
+ };
121
+ return strategy;
122
+ }
123
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ import type { AuthStrategy } from './auth.js';
2
+ import { type QueryInput } from './query.js';
3
+ import { type ResourceInput } from './serialize.js';
4
+ import type { Document, Links, Meta, Resource, ResourceOf, ResourceType } from './types.js';
5
+ export declare const JSON_API_MEDIA_TYPE = "application/vnd.api+json";
6
+ /**
7
+ * Extra `fetch` options: `cache`, `credentials`, and framework extensions
8
+ * such as Next.js's `next: { revalidate, tags }`.
9
+ */
10
+ export type FetchOptions = Omit<RequestInit, 'method' | 'headers' | 'body' | 'signal'> & Record<string, unknown>;
11
+ type HeadersSource = HeadersInit | (() => HeadersInit | undefined | Promise<HeadersInit | undefined>);
12
+ /** One finished request, for logging or a request inspector. */
13
+ export interface RequestRecord {
14
+ method: string;
15
+ url: string;
16
+ status: number;
17
+ durationMs: number;
18
+ requestBody?: unknown;
19
+ responseBody?: unknown;
20
+ }
21
+ export interface JsonApiClientOptions {
22
+ /** Site origin, e.g. `https://example.com`. A trailing slash is ignored. */
23
+ baseUrl: string;
24
+ /** Path prefix of the JSON:API. Default `/jsonapi`. */
25
+ apiPrefix?: string;
26
+ /** Language of requests; sent as a path prefix unless it is `defaultLocale`. */
27
+ locale?: string;
28
+ /** The site's default language, which has no path prefix. */
29
+ defaultLocale?: string;
30
+ auth?: AuthStrategy;
31
+ /** Headers added to every request. */
32
+ headers?: HeadersSource;
33
+ /**
34
+ * JSON:API path per resource type, relative to `apiPrefix`, for types
35
+ * whose path is not the default `/{entity_type}/{bundle}` (renamed with
36
+ * JSON:API Extras, or custom resources).
37
+ */
38
+ resourcePaths?: Record<string, string>;
39
+ /**
40
+ * Computes paths for whole families of types (commerce_api's renamed
41
+ * Commerce types); `undefined` falls through to the default. Consulted
42
+ * after `resourcePaths`.
43
+ */
44
+ resolveResourcePath?: (type: string) => string | undefined;
45
+ /** Default `fetch` options for every request. */
46
+ fetchOptions?: FetchOptions;
47
+ fetch?: typeof fetch;
48
+ /** Called after every request, failed ones included. */
49
+ onResponse?: (record: RequestRecord) => void;
50
+ }
51
+ export interface RequestOptions {
52
+ query?: QueryInput;
53
+ /** A JSON:API document, sent as JSON. */
54
+ body?: unknown;
55
+ /** A raw body (file uploads); the caller sets its Content-Type. */
56
+ rawBody?: BodyInit;
57
+ headers?: HeadersInit;
58
+ /** Language of this request, overriding the client's. */
59
+ locale?: string;
60
+ /** `false` sends this request anonymously. */
61
+ auth?: AuthStrategy | false;
62
+ fetchOptions?: FetchOptions;
63
+ signal?: AbortSignal;
64
+ }
65
+ /** Options for the resource-level methods. */
66
+ export type ResourceRequestOptions = Omit<RequestOptions, 'body' | 'rawBody'>;
67
+ /** A response: the raw document plus its deserialized primary data. */
68
+ export interface JsonApiResponse<T> {
69
+ status: number;
70
+ headers: Headers;
71
+ document: Document;
72
+ data: T;
73
+ meta: Meta;
74
+ links: Links;
75
+ }
76
+ /** One page of a collection. */
77
+ export interface Page<T> {
78
+ data: T[];
79
+ meta: Meta;
80
+ links: Links;
81
+ document: Document;
82
+ hasNext: boolean;
83
+ /** The following page, or NULL on the last one. */
84
+ next(): Promise<Page<T> | null>;
85
+ }
86
+ export interface FileInput {
87
+ data: Blob | ArrayBuffer | Uint8Array;
88
+ filename: string;
89
+ }
90
+ /**
91
+ * A client for Drupal's JSON:API.
92
+ *
93
+ * Two levels:
94
+ * - resource methods (`getResource`, `getCollection`, `createResource`, …)
95
+ * take a resource type name and return deserialized resources, typed by
96
+ * the {@link ResourceTypes} registry;
97
+ * - request methods (`get`, `post`, `request`, …) take a path relative to
98
+ * the API prefix, for custom resources such as commerce_api's cart.
99
+ *
100
+ * Non-2xx responses throw {@link JsonApiError}.
101
+ */
102
+ export declare class JsonApiClient {
103
+ readonly options: JsonApiClientOptions;
104
+ readonly baseUrl: string;
105
+ readonly apiPrefix: string;
106
+ private readonly fetchImpl;
107
+ constructor(options: JsonApiClientOptions);
108
+ /**
109
+ * A client with some options replaced. Headers are combined: the new
110
+ * ones are added after (and win over) this client's. The auth strategy is
111
+ * shared unless replaced, so a cached token is reused.
112
+ */
113
+ extend(options: Partial<JsonApiClientOptions>): JsonApiClient;
114
+ /**
115
+ * Reads every resource type's path from the API's entry point (`GET
116
+ * /jsonapi`), which lists them as links keyed by type name. Covers types
117
+ * renamed or moved with JSON:API Extras without mapping them by hand.
118
+ *
119
+ * ```ts
120
+ * const drupal = await new JsonApiClient({ baseUrl }).withDiscoveredPaths();
121
+ * ```
122
+ */
123
+ discoverResourcePaths(options?: Pick<RequestOptions, 'auth' | 'headers' | 'fetchOptions' | 'signal'>): Promise<Record<string, string>>;
124
+ /**
125
+ * A client with the paths from {@link discoverResourcePaths}. Paths
126
+ * configured in `resourcePaths` keep precedence. Discover once at
127
+ * startup and reuse the client: it is one request.
128
+ */
129
+ withDiscoveredPaths(options?: Parameters<JsonApiClient['discoverResourcePaths']>[0]): Promise<JsonApiClient>;
130
+ /** JSON:API path of a resource type, relative to the API prefix. */
131
+ resourcePath(type: string): string;
132
+ /** Absolute URL of an API path. */
133
+ url(path: string, query?: QueryInput, locale?: string): string;
134
+ /**
135
+ * Absolute URL of a site path outside the API (`/router/translate-path`),
136
+ * with the language prefix.
137
+ */
138
+ siteUrl(path: string, query?: QueryInput, locale?: string): string;
139
+ getResource<T extends ResourceType>(type: T, id: string, options?: ResourceRequestOptions): Promise<ResourceOf<T>>;
140
+ /** One page of a collection (Drupal's default page size is 50). */
141
+ getCollection<T extends ResourceType>(type: T, options?: ResourceRequestOptions): Promise<ResourceOf<T>[]>;
142
+ /** A page of a collection, with its meta and a way to the next page. */
143
+ getPage<T extends ResourceType>(type: T, options?: ResourceRequestOptions): Promise<Page<ResourceOf<T>>>;
144
+ /** Every resource of a collection, following `next` links page by page. */
145
+ iterate<T extends ResourceType>(type: T, options?: ResourceRequestOptions): AsyncGenerator<ResourceOf<T>, void, undefined>;
146
+ createResource<T extends ResourceType>(type: T, input: ResourceInput, options?: ResourceRequestOptions): Promise<ResourceOf<T>>;
147
+ updateResource<T extends ResourceType>(type: T, id: string, input: Omit<ResourceInput, 'id'>, options?: ResourceRequestOptions): Promise<ResourceOf<T>>;
148
+ deleteResource(type: ResourceType, id: string, options?: ResourceRequestOptions): Promise<void>;
149
+ /**
150
+ * Uploads a file into a file field (core's JSON:API file upload). With
151
+ * `entityId`, the file is attached to that entity's field; without, a
152
+ * temporary file is created to reference in a later create or update.
153
+ */
154
+ uploadFile(type: ResourceType, field: string, file: FileInput, options?: ResourceRequestOptions & {
155
+ entityId?: string;
156
+ }): Promise<ResourceOf<'file--file'>>;
157
+ get<T = Resource | Resource[] | null>(path: string, options?: Omit<RequestOptions, 'body' | 'rawBody'>): Promise<JsonApiResponse<T>>;
158
+ post<T = Resource | Resource[] | null>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<JsonApiResponse<T>>;
159
+ patch<T = Resource | Resource[] | null>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<JsonApiResponse<T>>;
160
+ delete<T = Resource | Resource[] | null>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<JsonApiResponse<T>>;
161
+ /**
162
+ * Sends a request. `path` is relative to the API prefix, or an absolute
163
+ * URL (from a document's links, or {@link siteUrl}); an absolute URL keeps
164
+ * only its path and query, plus `query` if given, and is sent to
165
+ * `baseUrl`, so links Drupal builds with an internal host (behind a
166
+ * proxy) still work and credentials never leave the site.
167
+ */
168
+ request<T>(method: string, path: string, options?: RequestOptions): Promise<JsonApiResponse<T>>;
169
+ private send;
170
+ private parse;
171
+ private page;
172
+ private sameOrigin;
173
+ }
174
+ export {};
175
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}