@codewheel/jsonapi-frontend-client 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ npm i @codewheel/jsonapi-frontend-client
12
12
 
13
13
  ## Usage
14
14
 
15
- Set `DRUPAL_BASE_URL`, then:
15
+ Set `DRUPAL_BASE_URL` (must be a full `http(s)://` URL), then:
16
16
 
17
17
  ```ts
18
18
  import { resolvePath, fetchJsonApi } from "@codewheel/jsonapi-frontend-client"
@@ -23,3 +23,72 @@ if (resolved.resolved && resolved.kind === "entity") {
23
23
  console.log(doc.data)
24
24
  }
25
25
  ```
26
+
27
+ ## Astro / Vite (`import.meta.env`)
28
+
29
+ ```ts
30
+ import { resolvePath, fetchJsonApi } from "@codewheel/jsonapi-frontend-client"
31
+
32
+ const baseUrl = import.meta.env.DRUPAL_BASE_URL
33
+
34
+ const resolved = await resolvePath("/about-us", { baseUrl })
35
+ if (resolved.resolved && resolved.kind === "entity") {
36
+ const doc = await fetchJsonApi(resolved.jsonapi_url, { baseUrl })
37
+ }
38
+ ```
39
+
40
+ ## Authentication (optional)
41
+
42
+ Keep credentials server-side. Pass headers via `options.headers`:
43
+
44
+ ```ts
45
+ import { resolvePath } from "@codewheel/jsonapi-frontend-client"
46
+
47
+ const baseUrl = process.env.DRUPAL_BASE_URL!
48
+ const auth = "Basic " + Buffer.from(`${process.env.DRUPAL_BASIC_USERNAME}:${process.env.DRUPAL_BASIC_PASSWORD}`).toString("base64")
49
+
50
+ await resolvePath("/about-us", {
51
+ baseUrl,
52
+ headers: { Authorization: auth },
53
+ })
54
+ ```
55
+
56
+ ```ts
57
+ import { resolvePath } from "@codewheel/jsonapi-frontend-client"
58
+
59
+ await resolvePath("/about-us", {
60
+ headers: { Authorization: `Bearer ${process.env.DRUPAL_JWT_TOKEN}` },
61
+ })
62
+ ```
63
+
64
+ ## Query building (optional)
65
+
66
+ This client doesn’t require a query builder, but `drupal-jsonapi-params` works well:
67
+
68
+ ```ts
69
+ import { DrupalJsonApiParams } from "drupal-jsonapi-params"
70
+ import { fetchJsonApi } from "@codewheel/jsonapi-frontend-client"
71
+
72
+ const baseUrl = process.env.DRUPAL_BASE_URL!
73
+
74
+ const params = new DrupalJsonApiParams()
75
+ .addFilter("status", "1")
76
+ .addFields("node--article", ["title", "path", "body"])
77
+
78
+ const url = `/jsonapi/node/article?${params.getQueryString()}`
79
+ await fetchJsonApi(url, { baseUrl })
80
+ ```
81
+
82
+ ## URL safety (recommended)
83
+
84
+ By default, `fetchJsonApi()` and `fetchView()` refuse to fetch absolute URLs on a different origin than your `DRUPAL_BASE_URL` (to avoid accidental SSRF in server environments).
85
+
86
+ If you intentionally need to fetch a cross-origin absolute URL, pass `allowExternalUrls: true`:
87
+
88
+ ```ts
89
+ import { fetchJsonApi } from "@codewheel/jsonapi-frontend-client"
90
+
91
+ await fetchJsonApi("https://cms.example.com/jsonapi/node/page/...", {
92
+ allowExternalUrls: true,
93
+ })
94
+ ```
package/dist/fetch.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { FetchInit, FetchLike } from "./transport";
2
- import { JsonApiDocument } from "./types";
1
+ import { FetchInit, FetchLike } from "./transport.js";
2
+ import { JsonApiDocument } from "./types.js";
3
3
  /**
4
4
  * Build cache tags for a JSON:API entity URL (e.g. /jsonapi/node/page/{uuid}).
5
5
  *
@@ -13,6 +13,8 @@ export declare function buildViewCacheTags(dataUrl: string): string[];
13
13
  export declare function fetchJsonApi<T = JsonApiDocument>(jsonapiPath: string, options?: {
14
14
  baseUrl?: string;
15
15
  envKey?: string;
16
+ /** Allow fetching absolute URLs on other origins (default: false). */
17
+ allowExternalUrls?: boolean;
16
18
  include?: string[];
17
19
  fields?: Record<string, string[]>;
18
20
  revalidate?: number;
@@ -25,6 +27,8 @@ export declare function fetchJsonApi<T = JsonApiDocument>(jsonapiPath: string, o
25
27
  export declare function fetchView<T = JsonApiDocument>(dataUrl: string, options?: {
26
28
  baseUrl?: string;
27
29
  envKey?: string;
30
+ /** Allow fetching absolute URLs on other origins (default: false). */
31
+ allowExternalUrls?: boolean;
28
32
  /**
29
33
  * JSON:API pagination parameters.
30
34
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuD,MAAM,aAAa,CAAA;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEzC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBlE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAa5D;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,eAAe,EACpD,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,eAAe,EACjD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,CAAC,CAAC,CAyCZ"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuD,MAAM,gBAAgB,CAAA;AAC1G,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBlE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAa5D;AAoBD,wBAAsB,YAAY,CAAC,CAAC,GAAG,eAAe,EACpD,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,eAAe,EACjD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,CAAC,CAAC,CAyCZ"}
package/dist/fetch.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getDrupalBaseUrlFromOptions, getFetch, mergeHeaders } from "./transport";
1
+ import { getDrupalBaseUrlFromOptions, getFetch, mergeHeaders } from "./transport.js";
2
2
  /**
3
3
  * Build cache tags for a JSON:API entity URL (e.g. /jsonapi/node/page/{uuid}).
4
4
  *
@@ -34,10 +34,22 @@ export function buildViewCacheTags(dataUrl) {
34
34
  tags.push(`view:${viewId}--${displayId}`);
35
35
  return tags;
36
36
  }
37
+ function buildSafeUrl(input, base, options) {
38
+ const baseUrl = new URL(base);
39
+ const url = new URL(input, baseUrl);
40
+ if (!options?.allowExternalUrls && url.origin !== baseUrl.origin) {
41
+ throw new Error(`Refusing to fetch a URL from a different origin (${url.origin}) than base (${baseUrl.origin}). ` +
42
+ "Pass allowExternalUrls: true to override.");
43
+ }
44
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
45
+ throw new Error(`Unsupported URL protocol "${url.protocol}" (expected http/https)`);
46
+ }
47
+ return url;
48
+ }
37
49
  export async function fetchJsonApi(jsonapiPath, options) {
38
50
  const base = getDrupalBaseUrlFromOptions({ baseUrl: options?.baseUrl, envKey: options?.envKey });
39
51
  const fetcher = getFetch(options?.fetch);
40
- const url = new URL(jsonapiPath, base);
52
+ const url = buildSafeUrl(jsonapiPath, base, { allowExternalUrls: options?.allowExternalUrls });
41
53
  if (options?.include?.length) {
42
54
  url.searchParams.set("include", options.include.join(","));
43
55
  }
@@ -68,7 +80,7 @@ export async function fetchJsonApi(jsonapiPath, options) {
68
80
  export async function fetchView(dataUrl, options) {
69
81
  const base = getDrupalBaseUrlFromOptions({ baseUrl: options?.baseUrl, envKey: options?.envKey });
70
82
  const fetcher = getFetch(options?.fetch);
71
- const url = new URL(dataUrl, base);
83
+ const url = buildSafeUrl(dataUrl, base, { allowExternalUrls: options?.allowExternalUrls });
72
84
  if (options?.page !== undefined) {
73
85
  if (typeof options.page === "number") {
74
86
  url.searchParams.set("page[offset]", String(options.page));
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { resolvePath } from "./resolve";
2
- export { fetchJsonApi, fetchView } from "./fetch";
3
- export { getDrupalBaseUrl, resolveFileUrl, getFileUrl, getImageStyleUrl } from "./url";
4
- export { findIncluded, findIncludedByRelationship, findIncludedByRelationshipMultiple, extractImageFromFile, extractMedia, extractMediaField, extractPrimaryImage, extractEmbeddedMediaUuids, parseDrupalMediaTag, } from "./media";
5
- export type { ResolveResponse, JsonApiDocument, JsonApiResource, JsonApiRelationship, JsonApiLinks, NodeAttributes, } from "./types";
6
- export type { DrupalImageData, DrupalMediaData } from "./media";
7
- export type { FetchInit, FetchLike } from "./transport";
1
+ export { resolvePath } from "./resolve.js";
2
+ export { fetchJsonApi, fetchView } from "./fetch.js";
3
+ export { getDrupalBaseUrl, resolveFileUrl, getFileUrl, getImageStyleUrl } from "./url.js";
4
+ export { findIncluded, findIncludedByRelationship, findIncludedByRelationshipMultiple, extractImageFromFile, extractMedia, extractMediaField, extractPrimaryImage, extractEmbeddedMediaUuids, parseDrupalMediaTag, } from "./media.js";
5
+ export type { ResolveResponse, JsonApiDocument, JsonApiResource, JsonApiRelationship, JsonApiLinks, NodeAttributes, } from "./types.js";
6
+ export type { DrupalImageData, DrupalMediaData } from "./media.js";
7
+ export type { FetchInit, FetchLike } from "./transport.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEjD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAEtF,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,kCAAkC,EAClC,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,cAAc,GACf,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE/D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEpD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEzF,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,kCAAkC,EAClC,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,cAAc,GACf,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAElE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { resolvePath } from "./resolve";
2
- export { fetchJsonApi, fetchView } from "./fetch";
3
- export { getDrupalBaseUrl, resolveFileUrl, getFileUrl, getImageStyleUrl } from "./url";
4
- export { findIncluded, findIncludedByRelationship, findIncludedByRelationshipMultiple, extractImageFromFile, extractMedia, extractMediaField, extractPrimaryImage, extractEmbeddedMediaUuids, parseDrupalMediaTag, } from "./media";
1
+ export { resolvePath } from "./resolve.js";
2
+ export { fetchJsonApi, fetchView } from "./fetch.js";
3
+ export { getDrupalBaseUrl, resolveFileUrl, getFileUrl, getImageStyleUrl } from "./url.js";
4
+ export { findIncluded, findIncludedByRelationship, findIncludedByRelationshipMultiple, extractImageFromFile, extractMedia, extractMediaField, extractPrimaryImage, extractEmbeddedMediaUuids, parseDrupalMediaTag, } from "./media.js";
package/dist/media.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { JsonApiResource, JsonApiRelationship } from "./types";
1
+ import { JsonApiResource, JsonApiRelationship } from "./types.js";
2
2
  export interface DrupalImageData {
3
3
  src: string;
4
4
  alt: string;
@@ -1 +1 @@
1
- {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAG9D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAA;IACvE,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,eAAe,GAAG,SAAS,CAE7B;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAC5C,eAAe,GAAG,SAAS,CAK7B;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAC5C,eAAe,EAAE,CAUnB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAkB9F;AAcD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,GAAG,SAAS,EAClC,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GACtC,eAAe,GAAG,IAAI,CAgGxB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GACtC,eAAe,EAAE,CAanB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAY5H;AAgBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUhE"}
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAGjE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAA;IACvE,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,eAAe,GAAG,SAAS,CAE7B;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAC5C,eAAe,GAAG,SAAS,CAK7B;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,EACvC,YAAY,EAAE,mBAAmB,GAAG,SAAS,GAC5C,eAAe,EAAE,CAUnB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAkB9F;AAcD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,GAAG,SAAS,EAClC,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GACtC,eAAe,GAAG,IAAI,CAgGxB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GACtC,eAAe,EAAE,CAanB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAY5H;AAgBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUhE"}
package/dist/media.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getFileUrl } from "./url";
1
+ import { getFileUrl } from "./url.js";
2
2
  export function findIncluded(included, type, id) {
3
3
  return included?.find((item) => item.type === type && item.id === id);
4
4
  }
package/dist/resolve.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ResolveResponse } from "./types";
2
- import { FetchInit, FetchLike } from "./transport";
1
+ import { ResolveResponse } from "./types.js";
2
+ import { FetchInit, FetchLike } from "./transport.js";
3
3
  /**
4
4
  * Resolve a frontend path to a Drupal resource.
5
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuD,MAAM,aAAa,CAAA;AAEvG;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,eAAe,CAAC,CA8B1B"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuD,MAAM,gBAAgB,CAAA;AAE1G;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,GACA,OAAO,CAAC,eAAe,CAAC,CA8B1B"}
package/dist/resolve.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getDrupalBaseUrlFromOptions, getFetch, mergeHeaders } from "./transport";
1
+ import { getDrupalBaseUrlFromOptions, getFetch, mergeHeaders } from "./transport.js";
2
2
  /**
3
3
  * Resolve a frontend path to a Drupal resource.
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAOzF,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAMnG;AAED,wBAAgB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAMzD;AAED,wBAAgB,YAAY,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,OAAO,CAOpF"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAOzF,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAkBnG;AAED,wBAAgB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAMzD;AAED,wBAAgB,YAAY,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,OAAO,CAOpF"}
package/dist/transport.js CHANGED
@@ -3,11 +3,21 @@ function getEnvString(key) {
3
3
  return typeof value === "string" && value.trim() !== "" ? value : undefined;
4
4
  }
5
5
  export function getDrupalBaseUrlFromOptions(options) {
6
- const baseUrl = options?.baseUrl ?? getEnvString(options?.envKey ?? "DRUPAL_BASE_URL");
7
- if (!baseUrl) {
6
+ const rawBaseUrl = options?.baseUrl ?? getEnvString(options?.envKey ?? "DRUPAL_BASE_URL");
7
+ if (!rawBaseUrl) {
8
8
  throw new Error(`Missing Drupal base URL (pass baseUrl or set ${options?.envKey ?? "DRUPAL_BASE_URL"})`);
9
9
  }
10
- return baseUrl.replace(/\/$/, "");
10
+ let parsed;
11
+ try {
12
+ parsed = new URL(rawBaseUrl);
13
+ }
14
+ catch {
15
+ throw new Error(`Invalid Drupal base URL "${rawBaseUrl}" (expected http(s) URL)`);
16
+ }
17
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
18
+ throw new Error(`Invalid Drupal base URL protocol "${parsed.protocol}" (expected http/https)`);
19
+ }
20
+ return parsed.toString().replace(/\/$/, "");
11
21
  }
12
22
  export function getFetch(fetchLike) {
13
23
  const f = fetchLike ?? globalThis.fetch;
package/dist/url.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getDrupalBaseUrlFromOptions } from "./transport";
1
+ import { getDrupalBaseUrlFromOptions } from "./transport.js";
2
2
  /**
3
3
  * Get the Drupal base URL.
4
4
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codewheel/jsonapi-frontend-client",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "TypeScript client helpers for Drupal drupal/jsonapi_frontend",
5
5
  "homepage": "https://github.com/code-wheel/jsonapi-frontend-client#readme",
6
6
  "repository": {