@jsondrop/drupal 0.0.0-stage → 0.1.1

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/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,79 @@
1
- # Temporary Holding Version
1
+ # @jsondrop/drupal
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
+ Drupal 11 core entity types and helpers for
4
+ [`@jsondrop/core`](../core).
5
+
6
+ ```sh
7
+ npm install @jsondrop/core @jsondrop/drupal
8
+ ```
9
+
10
+ ## Types
11
+
12
+ Importing the package registers Drupal core's resource types with the
13
+ client:
14
+
15
+ - fixed types: `user--user`, `file--file`, `menu_link_content--menu_link_content`,
16
+ `path_alias--path_alias`, and the config entities (`node_type`,
17
+ `taxonomy_vocabulary`, `media_type`, `user_role`, `block_content_type`);
18
+ - per-entity-type fallbacks: any `node--*` is a `Node`, `taxonomy_term--*` a
19
+ `Term`, `media--*` a `Media`, `block_content--*` a `BlockContent`,
20
+ `paragraph--*` a `Paragraph`, typed with the base fields.
21
+
22
+ Add your bundles' fields by extending them:
23
+
24
+ ```ts
25
+ import type { ToMany, ToOne } from '@jsondrop/core';
26
+ import type { File, ImageMeta, MediaImage, Node, Term, TextField } from '@jsondrop/drupal';
27
+
28
+ export interface Article extends Node<'article'> {
29
+ body: TextField | null;
30
+ field_image: ToOne<File<ImageMeta>>;
31
+ field_tags: ToMany<Term<'tags'>>;
32
+ field_hero: ToOne<MediaImage>;
33
+ }
34
+
35
+ declare module '@jsondrop/core' {
36
+ interface ResourceTypes {
37
+ 'node--article': Article;
38
+ }
39
+ }
40
+ ```
41
+
42
+ Field value types: `TextField`, `PathField`, `LinkField`, `FileUri`,
43
+ `ImageMeta`, `FileMeta`, `DateRangeField`, `AddressField`.
44
+ Standard-profile media types: `MediaImage`, `MediaDocument`, `MediaAudio`,
45
+ `MediaVideo`, `MediaRemoteVideo`.
46
+
47
+ ## Helpers
48
+
49
+ | Helper | Needs | |
50
+ |---|---|---|
51
+ | `translatePath(client, path)` | decoupled_router | Resolves a path; NULL when not found |
52
+ | `getResourceByPath(client, path, options)` | decoupled_router | Resolves a path and fetches its entity, with redirects |
53
+ | `getMenu(client, menu)` / `getMenuItems` | jsonapi_menu_items | A menu as a sorted tree / flat list |
54
+ | `buildMenuTree(items)` | | Nests links by `parent`; also takes core `menu_link_content` |
55
+ | `getView(client, view, display, { filters, arguments, sort, page })` | jsonapi_views | A view display's results and count |
56
+ | `image(client, field)` | | An image field as `{ url, alt, title, width, height }` |
57
+ | `fileUrl(client, file)` | | Absolute file URL |
58
+ | `imageStyleUrl(file, style)` | consumer_image_styles | An image style derivative's URL |
59
+
60
+ ### Users
61
+
62
+ | Helper | Needs | |
63
+ |---|---|---|
64
+ | `getCurrentUser(client)` / `getCurrentUserId` | | The logged-in user (any auth), from the entry point's `me` link; NULL when anonymous |
65
+ | `login(client, { name, pass })` | | Drupal session login (`/user/login`), server-side: returns the session cookie, CSRF and logout tokens |
66
+ | `logout(client, session)` | | Ends the session with its logout token |
67
+ | `isLoggedIn(client, cookie)` | | Whether a session is still valid |
68
+ | `requestPasswordReset(client, { mail })` | | Mails a reset link; same answer for unknown accounts |
69
+
70
+ For OAuth logins (simple_oauth) see `OAuthClient` in
71
+ `@jsondrop/core`. Both are shown in
72
+ [example 7](../../examples/07-user-login.ts).
73
+
74
+ ```ts
75
+ const page = await getResourceByPath<Article>(drupal, '/blog/hello', {
76
+ query: { include: ['field_image', 'field_tags'] },
77
+ });
78
+ if (!page?.resource) notFound();
79
+ ```
@@ -0,0 +1,65 @@
1
+ import { type JsonApiClient, type ResourceRequestOptions } from '@jsondrop/core';
2
+ import type { User } from './entities.js';
3
+ /**
4
+ * The current user, from Drupal core alone: an authenticated request to the
5
+ * JSON:API entry point carries `meta.links.me` with the user's UUID. Works
6
+ * with any auth (OAuth bearer token, session cookie). NULL when anonymous.
7
+ *
8
+ * Two requests: the entry point, then the user (with `options`).
9
+ */
10
+ export declare function getCurrentUser<T = User>(client: JsonApiClient, options?: ResourceRequestOptions): Promise<T | null>;
11
+ /** The current user's UUID, or NULL when anonymous (one request). */
12
+ export declare function getCurrentUserId(client: JsonApiClient, options?: Pick<ResourceRequestOptions, 'auth' | 'headers' | 'fetchOptions' | 'signal'>): Promise<string | null>;
13
+ /**
14
+ * Cookie session login through Drupal core's `/user/login?_format=json`,
15
+ * for sites without simple_oauth. Server-side only: it reads the session
16
+ * cookie from `Set-Cookie`, which browsers hide from scripts.
17
+ *
18
+ * Keep `cookie` in an httpOnly cookie of your app (never hand Drupal's own
19
+ * cookie to the browser) and send it with `sessionCookie(cookie)` as auth.
20
+ * Keep `logoutToken` too: logging out needs it.
21
+ */
22
+ export interface SessionLogin {
23
+ /** The session cookie as a `Cookie` header value (`SSESS…=…`). */
24
+ cookie: string;
25
+ /** The user as Drupal reports them; fields the user cannot view are absent. */
26
+ user: {
27
+ uid?: string;
28
+ name?: string;
29
+ roles?: string[];
30
+ };
31
+ /** For requests that need Drupal's CSRF token (`X-CSRF-Token`), e.g. core REST writes. */
32
+ csrfToken: string;
33
+ /** Pass to {@link logout}. */
34
+ logoutToken: string;
35
+ }
36
+ /**
37
+ * Logs in with a username and password. Wrong credentials and blocked
38
+ * accounts throw a 400; too many attempts (flood control) a 403 or 429.
39
+ * The error's `message` is Drupal's explanation.
40
+ */
41
+ export declare function login(client: JsonApiClient, credentials: {
42
+ name: string;
43
+ pass: string;
44
+ }): Promise<SessionLogin>;
45
+ /** Ends the Drupal session (`/user/logout`, with the logout token from {@link login}). */
46
+ export declare function logout(client: JsonApiClient, session: Pick<SessionLogin, 'cookie' | 'logoutToken'>): Promise<void>;
47
+ /** Whether the session is still logged in (`/user/login_status`). */
48
+ export declare function isLoggedIn(client: JsonApiClient, cookie: string): Promise<boolean>;
49
+ /**
50
+ * A CSRF token (`/session/token`), for the `X-CSRF-Token` header that
51
+ * Drupal requires on writes to core REST resources (webform_rest, for
52
+ * one) when a session cookie authenticates them. Requests with a bearer
53
+ * token, or anonymous ones, do not need it.
54
+ */
55
+ export declare function getCsrfToken(client: JsonApiClient, cookie?: string): Promise<string>;
56
+ /**
57
+ * Mails a password reset link (`/user/password`). Answers the same whether
58
+ * or not the account exists, so it does not reveal accounts.
59
+ */
60
+ export declare function requestPasswordReset(client: JsonApiClient, account: {
61
+ name: string;
62
+ } | {
63
+ mail: string;
64
+ }): Promise<void>;
65
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAG7H;AAED,qEAAqE;AACrE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,QAAQ,CAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKhL;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACxD,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAarH;AAED,0FAA0F;AAC1F,wBAAsB,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxH;AAED,qEAAqE;AACrE,wBAAsB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASxF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO1F;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7H"}
@@ -0,0 +1,96 @@
1
+ import { sessionCookie } from '@jsondrop/core';
2
+ /**
3
+ * The current user, from Drupal core alone: an authenticated request to the
4
+ * JSON:API entry point carries `meta.links.me` with the user's UUID. Works
5
+ * with any auth (OAuth bearer token, session cookie). NULL when anonymous.
6
+ *
7
+ * Two requests: the entry point, then the user (with `options`).
8
+ */
9
+ export async function getCurrentUser(client, options = {}) {
10
+ const id = await getCurrentUserId(client, options);
11
+ return id ? client.getResource('user--user', id, options) : null;
12
+ }
13
+ /** The current user's UUID, or NULL when anonymous (one request). */
14
+ export async function getCurrentUserId(client, options = {}) {
15
+ const { auth, headers, fetchOptions, signal } = options;
16
+ const response = await client.request('GET', '', { auth, headers, fetchOptions, signal });
17
+ const me = response.meta.links?.me;
18
+ return me?.meta?.id ?? null;
19
+ }
20
+ const JSON_HEADERS = { 'Content-Type': 'application/json', Accept: 'application/json' };
21
+ /**
22
+ * Logs in with a username and password. Wrong credentials and blocked
23
+ * accounts throw a 400; too many attempts (flood control) a 403 or 429.
24
+ * The error's `message` is Drupal's explanation.
25
+ */
26
+ export async function login(client, credentials) {
27
+ const response = await client.post(client.siteUrl('/user/login'), credentials, {
28
+ auth: false,
29
+ query: { params: { _format: 'json' } },
30
+ headers: JSON_HEADERS,
31
+ parse: 'json',
32
+ });
33
+ const body = response.data ?? {};
34
+ const cookie = sessionCookieOf(response.headers);
35
+ if (!cookie) {
36
+ throw new Error('Drupal answered the login without a session cookie. Log in from the server, not the browser.');
37
+ }
38
+ return { cookie, user: body.current_user ?? {}, csrfToken: body.csrf_token ?? '', logoutToken: body.logout_token ?? '' };
39
+ }
40
+ /** Ends the Drupal session (`/user/logout`, with the logout token from {@link login}). */
41
+ export async function logout(client, session) {
42
+ await client.post(client.siteUrl('/user/logout'), undefined, {
43
+ auth: sessionCookie(session.cookie),
44
+ query: { params: { _format: 'json', token: session.logoutToken } },
45
+ headers: JSON_HEADERS,
46
+ parse: 'json',
47
+ });
48
+ }
49
+ /** Whether the session is still logged in (`/user/login_status`). */
50
+ export async function isLoggedIn(client, cookie) {
51
+ const response = await client.get(client.siteUrl('/user/login_status'), {
52
+ auth: sessionCookie(cookie),
53
+ query: { params: { _format: 'json' } },
54
+ headers: JSON_HEADERS,
55
+ parse: 'json',
56
+ });
57
+ // Drupal answers a bare `1` or `0`.
58
+ return Number(response.data) === 1;
59
+ }
60
+ /**
61
+ * A CSRF token (`/session/token`), for the `X-CSRF-Token` header that
62
+ * Drupal requires on writes to core REST resources (webform_rest, for
63
+ * one) when a session cookie authenticates them. Requests with a bearer
64
+ * token, or anonymous ones, do not need it.
65
+ */
66
+ export async function getCsrfToken(client, cookie) {
67
+ const response = await client.get(client.siteUrl('/session/token'), {
68
+ auth: cookie ? sessionCookie(cookie) : false,
69
+ headers: { Accept: 'text/plain' },
70
+ parse: 'text',
71
+ });
72
+ return response.data.trim();
73
+ }
74
+ /**
75
+ * Mails a password reset link (`/user/password`). Answers the same whether
76
+ * or not the account exists, so it does not reveal accounts.
77
+ */
78
+ export async function requestPasswordReset(client, account) {
79
+ await client.post(client.siteUrl('/user/password'), account, {
80
+ auth: false,
81
+ query: { params: { _format: 'json' } },
82
+ headers: JSON_HEADERS,
83
+ parse: 'json',
84
+ });
85
+ }
86
+ /** Drupal's session cookie(s) from a response, as a `Cookie` header value. */
87
+ function sessionCookieOf(headers) {
88
+ const setCookies = typeof headers.getSetCookie === 'function'
89
+ ? headers.getSetCookie()
90
+ : (headers.get('set-cookie') ?? '').split(/,(?=\s*[^;,]+=)/);
91
+ const pairs = setCookies
92
+ .map((line) => line.split(';')[0]?.trim() ?? '')
93
+ .filter((pair) => /^S?SESS[^=]*=./.test(pair));
94
+ return pairs.length ? pairs.join('; ') : null;
95
+ }
96
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAmD,MAAM,gBAAgB,CAAC;AAGhG;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAW,MAAqB,EAAE,UAAkC,EAAE;IACxG,MAAM,EAAE,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,EAAE,OAAO,CAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AACjF,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAqB,EAAE,UAAwF,EAAE;IACtJ,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1F,MAAM,EAAE,GAAI,QAAQ,CAAC,IAAI,CAAC,KAAyD,EAAE,EAAE,CAAC;IACxF,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI,CAAC;AAC9B,CAAC;AAsBD,MAAM,YAAY,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;AAExF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,MAAqB,EAAE,WAA2C;IAC5F,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAA6F,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE;QACzK,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,8FAA8F,CAAC,CAAC;IAClH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;AAC3H,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAqB,EAAE,OAAqD;IACvG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE;QAC3D,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE;QAClE,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;AACL,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAqB,EAAE,MAAc;IACpE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAyB,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAE;QAC9F,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IACH,oCAAoC;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAqB,EAAE,MAAe;IACvE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAS,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE;QAC1E,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK;QAC5C,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;QACjC,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAqB,EAAE,OAA4C;IAC5G,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE;QAC3D,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,UAAU,GAAG,OAAO,OAAO,CAAC,YAAY,KAAK,UAAU;QAC3D,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE;QACxB,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,UAAU;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAC/C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC"}
@@ -0,0 +1,205 @@
1
+ import type { ResourceBase, ToMany, ToOne } from '@jsondrop/core';
2
+ import type { DateTimeString, FileMeta, FileUri, ImageMeta, LinkField, PathField, TextField } from './fields.js';
3
+ /**
4
+ * Drupal 11 core entity types, with the base fields JSON:API exposes.
5
+ *
6
+ * Bundle-specific fields are the project's: extend the generic types and
7
+ * register the bundle.
8
+ *
9
+ * ```ts
10
+ * interface Article extends Node<'article'> {
11
+ * body: TextField | null;
12
+ * field_image: ToOne<File<ImageMeta>>;
13
+ * field_tags: ToMany<Term<'tags'>>;
14
+ * }
15
+ * declare module '@jsondrop/core' {
16
+ * interface ResourceTypes { 'node--article': Article }
17
+ * }
18
+ * ```
19
+ *
20
+ * Fields hidden by access (an anonymous request sees no `mail`) or by
21
+ * JSON:API Extras are absent at runtime even where typed as required.
22
+ */
23
+ /** Fields shared by revisionable, translatable content entities. */
24
+ interface ContentEntity<TType extends string> extends ResourceBase<TType> {
25
+ langcode: string;
26
+ default_langcode?: boolean;
27
+ revision_translation_affected?: boolean | null;
28
+ }
29
+ export interface NodeType extends ResourceBase<'node_type--node_type'> {
30
+ drupal_internal__type: string;
31
+ name: string;
32
+ description: string | null;
33
+ }
34
+ export interface Vocabulary extends ResourceBase<'taxonomy_vocabulary--taxonomy_vocabulary'> {
35
+ drupal_internal__vid: string;
36
+ name: string;
37
+ description: string | null;
38
+ }
39
+ export interface MediaType extends ResourceBase<'media_type--media_type'> {
40
+ drupal_internal__id: string;
41
+ label: string;
42
+ description: string | null;
43
+ }
44
+ export interface Role extends ResourceBase<'user_role--user_role'> {
45
+ drupal_internal__id: string;
46
+ label: string;
47
+ }
48
+ export interface BlockContentType extends ResourceBase<'block_content_type--block_content_type'> {
49
+ drupal_internal__id: string;
50
+ label: string;
51
+ }
52
+ export interface User extends ContentEntity<'user--user'> {
53
+ drupal_internal__uid: number;
54
+ display_name: string;
55
+ name?: string;
56
+ mail?: string | null;
57
+ timezone?: string;
58
+ status?: boolean;
59
+ created?: DateTimeString;
60
+ changed?: DateTimeString;
61
+ access?: DateTimeString;
62
+ login?: DateTimeString | null;
63
+ preferred_langcode?: string;
64
+ roles?: ToMany<Role>;
65
+ user_picture?: ToOne<File<ImageMeta>>;
66
+ }
67
+ /**
68
+ * `file--file`. `TMeta` types the relationship meta it carries when reached
69
+ * through a field: {@link ImageMeta} for image fields.
70
+ */
71
+ export interface File<TMeta extends object = Record<string, unknown>> extends ContentEntity<'file--file'> {
72
+ drupal_internal__fid: number;
73
+ filename: string;
74
+ uri: FileUri;
75
+ filemime: string;
76
+ filesize: number;
77
+ status: boolean;
78
+ created: DateTimeString;
79
+ changed: DateTimeString;
80
+ uid?: ToOne<User>;
81
+ relationshipMeta?: TMeta & Record<string, unknown>;
82
+ }
83
+ export interface Node<TBundle extends string = string> extends ContentEntity<`node--${TBundle}`> {
84
+ drupal_internal__nid: number;
85
+ drupal_internal__vid: number;
86
+ title: string;
87
+ status: boolean;
88
+ promote: boolean;
89
+ sticky: boolean;
90
+ created: DateTimeString;
91
+ changed: DateTimeString;
92
+ revision_timestamp?: DateTimeString;
93
+ revision_log?: string | null;
94
+ path?: PathField;
95
+ node_type?: ToOne<NodeType>;
96
+ uid?: ToOne<User>;
97
+ revision_uid?: ToOne<User>;
98
+ }
99
+ export interface Term<TVocabulary extends string = string> extends ContentEntity<`taxonomy_term--${TVocabulary}`> {
100
+ drupal_internal__tid: number;
101
+ drupal_internal__revision_id?: number;
102
+ name: string;
103
+ description: TextField | null;
104
+ weight: number;
105
+ status: boolean;
106
+ changed: DateTimeString;
107
+ path?: PathField;
108
+ vid?: ToOne<Vocabulary>;
109
+ /** Parents; a root term's parent is a stub with the id `virtual`. */
110
+ parent?: ToMany<Term<TVocabulary>>;
111
+ revision_user?: ToOne<User>;
112
+ }
113
+ export interface Media<TBundle extends string = string> extends ContentEntity<`media--${TBundle}`> {
114
+ drupal_internal__mid: number;
115
+ drupal_internal__vid: number;
116
+ name: string;
117
+ status: boolean;
118
+ created: DateTimeString;
119
+ changed: DateTimeString;
120
+ path?: PathField;
121
+ bundle?: ToOne<MediaType>;
122
+ thumbnail?: ToOne<File<ImageMeta>>;
123
+ uid?: ToOne<User>;
124
+ revision_user?: ToOne<User>;
125
+ }
126
+ export interface MediaImage extends Media<'image'> {
127
+ field_media_image: ToOne<File<ImageMeta>>;
128
+ }
129
+ export interface MediaDocument extends Media<'document'> {
130
+ field_media_document: ToOne<File<FileMeta>>;
131
+ }
132
+ export interface MediaAudio extends Media<'audio'> {
133
+ field_media_audio_file: ToOne<File<FileMeta>>;
134
+ }
135
+ export interface MediaVideo extends Media<'video'> {
136
+ field_media_video_file: ToOne<File<FileMeta>>;
137
+ }
138
+ export interface MediaRemoteVideo extends Media<'remote_video'> {
139
+ /** The oEmbed URL (YouTube, Vimeo). */
140
+ field_media_oembed_video: string;
141
+ }
142
+ export interface MenuLinkContent extends ContentEntity<'menu_link_content--menu_link_content'> {
143
+ drupal_internal__id: number;
144
+ title: string;
145
+ description: string | null;
146
+ menu_name: string;
147
+ link: LinkField;
148
+ enabled: boolean;
149
+ expanded: boolean;
150
+ external: boolean;
151
+ weight: number;
152
+ /** Parent plugin ID, `menu_link_content:{uuid}`, or null at the root. */
153
+ parent: string | null;
154
+ changed: DateTimeString;
155
+ }
156
+ export interface BlockContent<TBundle extends string = string> extends ContentEntity<`block_content--${TBundle}`> {
157
+ drupal_internal__id: number;
158
+ info: string;
159
+ status: boolean;
160
+ reusable: boolean;
161
+ changed: DateTimeString;
162
+ body?: TextField | null;
163
+ block_content_type?: ToOne<BlockContentType>;
164
+ }
165
+ export interface PathAlias extends ResourceBase<'path_alias--path_alias'> {
166
+ drupal_internal__id: number;
167
+ langcode: string;
168
+ /** System path, e.g. `/node/1`. */
169
+ path: string;
170
+ alias: string;
171
+ status: boolean;
172
+ }
173
+ /** `paragraph--{bundle}` (contrib paragraphs). */
174
+ export interface Paragraph<TBundle extends string = string> extends ContentEntity<`paragraph--${TBundle}`> {
175
+ drupal_internal__id: number;
176
+ drupal_internal__revision_id: number;
177
+ status: boolean;
178
+ created: DateTimeString;
179
+ parent_id: string;
180
+ parent_type: string;
181
+ parent_field_name: string;
182
+ behavior_settings?: unknown;
183
+ }
184
+ declare module '@jsondrop/core' {
185
+ interface ResourceTypes {
186
+ 'user--user': User;
187
+ 'file--file': File;
188
+ 'menu_link_content--menu_link_content': MenuLinkContent;
189
+ 'path_alias--path_alias': PathAlias;
190
+ 'node_type--node_type': NodeType;
191
+ 'taxonomy_vocabulary--taxonomy_vocabulary': Vocabulary;
192
+ 'media_type--media_type': MediaType;
193
+ 'user_role--user_role': Role;
194
+ 'block_content_type--block_content_type': BlockContentType;
195
+ }
196
+ interface EntityTypes {
197
+ node: Node;
198
+ taxonomy_term: Term;
199
+ media: Media;
200
+ block_content: BlockContent;
201
+ paragraph: Paragraph;
202
+ }
203
+ }
204
+ export {};
205
+ //# sourceMappingURL=entities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Field value shapes, as Drupal 11's JSON:API normalizes core field types.
3
+ *
4
+ * Dates (`created`, `changed`, datetime fields) arrive as ISO 8601 strings.
5
+ */
6
+ /** ISO 8601 date-time string, e.g. `2026-10-31T12:00:00+00:00`. */
7
+ export type DateTimeString = string;
8
+ /** `text_long`, `text_with_summary` (`summary` only on the latter). */
9
+ export interface TextField {
10
+ value: string;
11
+ format: string | null;
12
+ /** The value rendered through its text format: safe HTML to output. */
13
+ processed: string;
14
+ summary?: string;
15
+ }
16
+ /** The `path` field (core path module). */
17
+ export interface PathField {
18
+ alias: string | null;
19
+ pid: number | null;
20
+ langcode: string;
21
+ }
22
+ /** The `link` field type. */
23
+ export interface LinkField {
24
+ /** `internal:/…`, `entity:node/1`, `route:<nolink>` or an external URL. */
25
+ uri: string;
26
+ title: string | null;
27
+ options: Record<string, unknown>;
28
+ }
29
+ /** The file field's `uri`: the stream URI and its (root-relative) URL. */
30
+ export interface FileUri {
31
+ value: string;
32
+ url: string;
33
+ }
34
+ /**
35
+ * Relationship meta of an `image` field item, found on the file resource
36
+ * as `relationshipMeta`.
37
+ */
38
+ export interface ImageMeta {
39
+ alt: string;
40
+ title: string;
41
+ width: number | null;
42
+ height: number | null;
43
+ drupal_internal__target_id: number;
44
+ }
45
+ /** Relationship meta of a `file` field item. */
46
+ export interface FileMeta {
47
+ display: boolean | null;
48
+ description: string | null;
49
+ drupal_internal__target_id: number;
50
+ }
51
+ /** `datetime_range` / `daterange`. */
52
+ export interface DateRangeField {
53
+ value: DateTimeString;
54
+ end_value: DateTimeString;
55
+ }
56
+ /** The `address` field (contrib address module, used by Commerce). */
57
+ export interface AddressField {
58
+ country_code: string;
59
+ langcode?: string | null;
60
+ administrative_area?: string | null;
61
+ locality?: string | null;
62
+ dependent_locality?: string | null;
63
+ postal_code?: string | null;
64
+ sorting_code?: string | null;
65
+ address_line1?: string | null;
66
+ address_line2?: string | null;
67
+ address_line3?: string | null;
68
+ organization?: string | null;
69
+ given_name?: string | null;
70
+ additional_name?: string | null;
71
+ family_name?: string | null;
72
+ }
73
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6BAA6B;AAC7B,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,0EAA0E;AAC1E,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,gDAAgD;AAChD,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B"}
package/dist/fields.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Field value shapes, as Drupal 11's JSON:API normalizes core field types.
3
+ *
4
+ * Dates (`created`, `changed`, datetime fields) arrive as ISO 8601 strings.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,27 @@
1
+ import { type JsonApiClient } from '@jsondrop/core';
2
+ import type { File } from './entities.js';
3
+ /**
4
+ * Absolute URL of a file. Drupal 11 serves `uri.url` root-relative
5
+ * (`/sites/default/files/…`); this prefixes the site's origin.
6
+ */
7
+ export declare function fileUrl(client: JsonApiClient, file: Pick<File, 'uri'>): string;
8
+ /**
9
+ * URL of an image style derivative, as the consumer_image_styles module
10
+ * adds them to the file's links. NULL when the style is not enabled for
11
+ * the requesting consumer.
12
+ */
13
+ export declare function imageStyleUrl(file: Pick<File, 'links'>, style: string): string | null;
14
+ export interface Image {
15
+ url: string;
16
+ alt: string;
17
+ title: string | null;
18
+ width: number | null;
19
+ height: number | null;
20
+ }
21
+ /**
22
+ * An image field's value, ready for an `<img>`: absolute URL plus the alt
23
+ * text and dimensions from the relationship. NULL for an empty field or a
24
+ * file that was not included.
25
+ */
26
+ export declare function image(client: JsonApiClient, value: unknown): Image | null;
27
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,CAG9E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErF;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAgBzE"}