@tdacorp/hyle-sdk 0.1.1 → 0.2.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/dist/next.js CHANGED
@@ -2,18 +2,23 @@ import {
2
2
  ArticlePage,
3
3
  DocsPage,
4
4
  ListPage
5
- } from "./chunk-XODMIT6N.js";
5
+ } from "./chunk-MHVWHSKC.js";
6
6
  import {
7
- fetchResolver
8
- } from "./chunk-HVAWEEHG.js";
7
+ fetchResolver,
8
+ workspaceTags
9
+ } from "./chunk-7VCMJUPC.js";
9
10
  import {
10
11
  Cp1ClientError,
11
- createRevalidationCallbackRoute
12
- } from "./chunk-RI3BP4MI.js";
12
+ createRevalidationCallbackRoute,
13
+ listPublicationPaths
14
+ } from "./chunk-HFUJRNFP.js";
13
15
 
14
16
  // src/next/page.tsx
15
17
  import { notFound, permanentRedirect, redirect } from "next/navigation";
16
18
 
19
+ // src/next/options.ts
20
+ import { unstable_rethrow } from "next/navigation";
21
+
17
22
  // src/next/paths.ts
18
23
  function normalizeBasePath(basePath) {
19
24
  const value = (basePath ?? "").replace(/\/+$/, "");
@@ -21,6 +26,7 @@ function normalizeBasePath(basePath) {
21
26
  if (!/^\/[^\s?#\\]+$/.test(value) || value.includes("//")) throw new TypeError(`basePath must look like "/learn", got "${basePath}"`);
22
27
  return value;
23
28
  }
29
+ var MAX_PATH_LENGTH = 1024;
24
30
  function sitePathFrom(basePath, segments) {
25
31
  const decoded = [];
26
32
  for (const raw of segments ?? []) {
@@ -30,10 +36,12 @@ function sitePathFrom(basePath, segments) {
30
36
  } catch {
31
37
  return void 0;
32
38
  }
39
+ if (segment !== segment.normalize("NFC")) return void 0;
33
40
  if (segment === "" || segment === "." || segment === ".." || /[/\\\u0000-\u001f\u007f]/.test(segment)) return void 0;
34
41
  decoded.push(segment);
35
42
  }
36
- return decoded.length === 0 ? basePath || "/" : `${basePath}/${decoded.join("/")}`;
43
+ const path = decoded.length === 0 ? basePath || "/" : `${basePath}/${decoded.join("/")}`;
44
+ return path.length > MAX_PATH_LENGTH ? void 0 : path;
37
45
  }
38
46
  function safeRedirectTarget(to) {
39
47
  if (typeof to !== "string" || !to.startsWith("/") || to.startsWith("//")) return void 0;
@@ -44,77 +52,135 @@ function safeRedirectTarget(to) {
44
52
  function resolverFor(options) {
45
53
  return options.resolver ?? fetchResolver(options);
46
54
  }
47
- var LOCALE_TAG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/;
55
+ var CURSOR = /^[A-Za-z0-9_-]{1,256}$/;
56
+ var validCursor = (value) => typeof value === "string" && CURSOR.test(value) ? value : void 0;
57
+ var LOCALE_TAG = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
58
+ var MAX_LOCALE_LENGTH = 35;
48
59
  function routeContext(options, params) {
49
60
  const basePath = normalizeBasePath(options.basePath);
50
61
  const segments = params[options.param ?? "hyle"];
51
62
  const path = sitePathFrom(basePath, typeof segments === "string" ? [segments] : segments);
52
63
  const given = typeof options.locale === "function" ? options.locale(params) : options.locale;
53
- const locale = typeof given === "string" && LOCALE_TAG.test(given) ? given : void 0;
64
+ const locale = typeof given === "string" && given.length <= MAX_LOCALE_LENGTH && LOCALE_TAG.test(given) ? given : void 0;
54
65
  const prefix = options.localePrefix ?? (() => "");
55
66
  const toHref = (sitePath, linkLocale = locale) => (linkLocale ? prefix(linkLocale) : "") + sitePath;
56
67
  return { basePath, path, locale, toHref };
57
68
  }
69
+ function isRejectedRequest(error) {
70
+ unstable_rethrow(error);
71
+ return error instanceof Cp1ClientError && error.status >= 400 && error.status < 500 && ![401, 403, 429].includes(error.status);
72
+ }
58
73
 
59
74
  // src/next/page.tsx
60
75
  import { jsx } from "react/jsx-runtime";
61
76
  function hylePage(options) {
62
77
  const resolve = resolverFor(options);
63
- return async function HylePage({ params }) {
78
+ return async function HylePage({ params, searchParams }) {
64
79
  const { path, locale, toHref } = routeContext(options, await params ?? {});
65
80
  if (path === void 0) notFound();
66
- const result = await resolve(path, locale, options.listLimit);
67
- switch (result.kind) {
68
- case "article": {
69
- const props = { article: result, components: options.components, slots: options.slots, locale, toHref };
70
- return options.layout === "docs" ? /* @__PURE__ */ jsx(DocsPage, { ...props }) : /* @__PURE__ */ jsx(ArticlePage, { ...props });
71
- }
72
- case "list":
73
- return /* @__PURE__ */ jsx(ListPage, { list: result, components: options.components, slots: options.slots, locale, toHref });
74
- case "redirect": {
75
- const to = safeRedirectTarget(result.to);
76
- if (to === void 0) throw new Cp1ClientError("INVALID_RESPONSE", "hyle returned an unsafe redirect target", 502, "");
77
- const target = safeRedirectTarget(toHref(to));
78
- if (target === void 0) throw new Cp1ClientError("INVALID_RESPONSE", "hyle returned an unsafe redirect target", 502, "");
79
- if (target === toHref(path)) notFound();
80
- return result.status === 301 ? permanentRedirect(target) : redirect(target);
81
+ try {
82
+ const cursor = options.paginate === "searchParams" ? validCursor((await searchParams)?.cursor) : void 0;
83
+ const result = await resolve(path, locale, options.listLimit, cursor);
84
+ switch (result.kind) {
85
+ case "article": {
86
+ const props = { article: result, components: options.components, slots: options.slots, locale, toHref };
87
+ return options.layout === "docs" ? /* @__PURE__ */ jsx(DocsPage, { ...props }) : /* @__PURE__ */ jsx(ArticlePage, { ...props });
88
+ }
89
+ case "list": {
90
+ const list = options.list?.title ? { ...result, section: { ...result.section, title: options.list.title } } : result;
91
+ const next = options.paginate === "searchParams" ? validCursor(result.next_cursor) : void 0;
92
+ const olderHref = next ? `${toHref(path)}?cursor=${next}` : void 0;
93
+ return /* @__PURE__ */ jsx(ListPage, { list, ...olderHref ? { olderHref } : {}, ...options.list?.description ? { description: options.list.description } : {}, components: options.components, slots: options.slots, locale, toHref });
94
+ }
95
+ case "redirect": {
96
+ const to = safeRedirectTarget(result.to);
97
+ if (to === void 0) throw new Cp1ClientError("INVALID_RESPONSE", "hyle returned an unsafe redirect target", 502, "");
98
+ const target = safeRedirectTarget(toHref(to));
99
+ if (target === void 0) throw new Cp1ClientError("INVALID_RESPONSE", "hyle returned an unsafe redirect target", 502, "");
100
+ if (target === toHref(path)) notFound();
101
+ return result.status === 301 ? permanentRedirect(target) : redirect(target);
102
+ }
103
+ case "notfound":
104
+ case "disabled":
105
+ notFound();
81
106
  }
82
- case "notfound":
83
- case "disabled":
84
- notFound();
107
+ } catch (error) {
108
+ if (isRejectedRequest(error)) notFound();
109
+ if (!options.onError) throw error;
110
+ console.error(`hyle: ${error instanceof Error ? error.name : "UnknownError"}`);
111
+ return options.onError(error);
85
112
  }
86
113
  };
87
114
  }
88
115
 
116
+ // src/next/sitemap.ts
117
+ var ISO_DATE = /^\d{4}-\d{2}-\d{2}T/;
118
+ function httpsOrigin(origin) {
119
+ let url;
120
+ try {
121
+ url = new URL(origin);
122
+ } catch {
123
+ throw new TypeError("hyleSitemap: `origin` must be an https origin such as https://example.com");
124
+ }
125
+ if (url.protocol !== "https:" || url.origin !== origin.replace(/\/$/, "")) {
126
+ throw new TypeError("hyleSitemap: `origin` must be an https origin with no path, query or credentials");
127
+ }
128
+ return url.origin;
129
+ }
130
+ async function hyleSitemap(options) {
131
+ const origin = httpsOrigin(options.origin);
132
+ const resolve = resolverFor(options);
133
+ const locale = typeof options.locale === "string" ? options.locale : void 0;
134
+ const { basePath, toHref } = routeContext({ ...options, locale: () => locale }, {});
135
+ const client = { resolve: ({ path, locale: l, limit, cursor }) => resolve(path, l, limit, cursor) };
136
+ const { paths, complete } = await listPublicationPaths({ client, basePath, ...options.sections ? { sections: options.sections } : {}, ...locale ? { locale } : {} });
137
+ if (!complete && (options.onIncomplete === "throw" || options.onIncomplete === void 0 && paths.length === 0)) {
138
+ throw new Error("hyleSitemap: hyle could not be listed completely (see onIncomplete)");
139
+ }
140
+ return paths.map(({ path, publishedAt }) => ({
141
+ url: origin + encodeURI(toHref(path)),
142
+ ...ISO_DATE.test(publishedAt) && !Number.isNaN(Date.parse(publishedAt)) ? { lastModified: publishedAt } : {}
143
+ }));
144
+ }
145
+
89
146
  // src/next/metadata.ts
90
147
  function hyleMetadata(options) {
91
148
  const resolve = resolverFor(options);
92
149
  return async function generateMetadata({ params }) {
93
150
  const { path, locale, toHref } = routeContext(options, await params ?? {});
94
151
  if (path === void 0) return {};
95
- const result = await resolve(path, locale, options.listLimit);
96
- if (result.kind === "list") return { title: result.section.title };
97
- if (result.kind !== "article") return {};
98
- const { publication: pub, alternates } = result;
99
- const canonical = toHref(pub.canonical_path, pub.locale);
100
- const description = pub.description ?? void 0;
101
- return {
102
- title: pub.title,
103
- ...description ? { description } : {},
104
- alternates: {
105
- canonical,
106
- ...alternates.length > 1 ? { languages: Object.fromEntries(alternates.map((a) => [a.locale, toHref(a.canonical_path, a.locale)])) } : {}
107
- },
108
- openGraph: {
109
- type: "article",
152
+ try {
153
+ const result = await resolve(path, locale, options.listLimit);
154
+ if (result.kind === "list") {
155
+ const title = options.list?.title || result.section.title;
156
+ const description2 = options.list?.description;
157
+ return { title, ...description2 ? { description: description2 } : {}, openGraph: { title, ...description2 ? { description: description2 } : {} } };
158
+ }
159
+ if (result.kind !== "article") return {};
160
+ const { publication: pub, alternates } = result;
161
+ const canonical = toHref(pub.canonical_path, pub.locale);
162
+ const description = pub.description ?? void 0;
163
+ return {
110
164
  title: pub.title,
111
165
  ...description ? { description } : {},
112
- url: canonical,
113
- locale: pub.locale,
114
- publishedTime: pub.published_at,
115
- modifiedTime: pub.updated_at
116
- }
117
- };
166
+ alternates: {
167
+ canonical,
168
+ ...alternates.length > 1 ? { languages: Object.fromEntries(alternates.map((a) => [a.locale, toHref(a.canonical_path, a.locale)])) } : {}
169
+ },
170
+ openGraph: {
171
+ type: "article",
172
+ title: pub.title,
173
+ ...description ? { description } : {},
174
+ url: canonical,
175
+ locale: pub.locale,
176
+ publishedTime: pub.published_at,
177
+ modifiedTime: pub.updated_at
178
+ }
179
+ };
180
+ } catch (error) {
181
+ if (isRejectedRequest(error)) return {};
182
+ throw error;
183
+ }
118
184
  };
119
185
  }
120
186
 
@@ -140,6 +206,9 @@ function createRevalidateRoute(options) {
140
206
  }
141
207
  export {
142
208
  createRevalidateRoute,
209
+ fetchResolver,
143
210
  hyleMetadata,
144
- hylePage
211
+ hylePage,
212
+ hyleSitemap,
213
+ workspaceTags
145
214
  };
package/dist/react.cjs CHANGED
@@ -28,6 +28,7 @@ __export(react_exports, {
28
28
  NotFound: () => NotFound,
29
29
  Redirecting: () => Redirecting,
30
30
  TrustedServerHtml: () => TrustedServerHtml,
31
+ Unavailable: () => Unavailable,
31
32
  isSafeUrl: () => isSafeUrl,
32
33
  sanitizeHast: () => sanitizeHast,
33
34
  scopeId: () => scopeId,
@@ -280,7 +281,10 @@ var en = {
280
281
  rdBody: "You are being taken to its new address.",
281
282
  from: "From",
282
283
  to: "To",
283
- rdFallback: "If nothing happens, go to the new address."
284
+ rdFallback: "If nothing happens, go to the new address.",
285
+ unKind: "Unavailable",
286
+ unTitle: "This page is temporarily unavailable",
287
+ unBody: "The content could not be loaded. Please try again in a moment."
284
288
  };
285
289
  var fr = {
286
290
  toc: "Sur cette page",
@@ -316,7 +320,10 @@ var fr = {
316
320
  rdBody: "Vous \xEAtes redirig\xE9 vers sa nouvelle adresse.",
317
321
  from: "De",
318
322
  to: "Vers",
319
- rdFallback: "Si rien ne se passe, allez \xE0 la nouvelle adresse."
323
+ rdFallback: "Si rien ne se passe, allez \xE0 la nouvelle adresse.",
324
+ unKind: "Indisponible",
325
+ unTitle: "Cette page est momentan\xE9ment indisponible",
326
+ unBody: "Le contenu n'a pas pu \xEAtre charg\xE9. R\xE9essayez dans un instant."
320
327
  };
321
328
  var es = {
322
329
  toc: "En esta p\xE1gina",
@@ -352,7 +359,10 @@ var es = {
352
359
  rdBody: "Te estamos llevando a su nueva direcci\xF3n.",
353
360
  from: "Desde",
354
361
  to: "A",
355
- rdFallback: "Si no pasa nada, ve a la nueva direcci\xF3n."
362
+ rdFallback: "Si no pasa nada, ve a la nueva direcci\xF3n.",
363
+ unKind: "No disponible",
364
+ unTitle: "Esta p\xE1gina no est\xE1 disponible por el momento",
365
+ unBody: "No se pudo cargar el contenido. Int\xE9ntalo de nuevo en un momento."
356
366
  };
357
367
  var TABLE = { en, fr, es };
358
368
  function stringsFor(locale) {
@@ -716,6 +726,14 @@ function NotFound({ path, homeHref = "/", ...base }) {
716
726
  ] })
717
727
  ] }) });
718
728
  }
729
+ function Unavailable(base) {
730
+ const c = ctx(base, "en");
731
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Shell, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "hyle-state", "data-hyle-page": "unavailable", lang: c.locale, role: "status", children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "kind", children: c.s.unKind }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h1", { children: c.s.unTitle }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: c.s.unBody })
735
+ ] }) });
736
+ }
719
737
  function Redirecting({ from, to, ...base }) {
720
738
  const c = ctx(base, "en");
721
739
  const { Link, s } = c;
@@ -748,6 +766,7 @@ function Redirecting({ from, to, ...base }) {
748
766
  NotFound,
749
767
  Redirecting,
750
768
  TrustedServerHtml,
769
+ Unavailable,
751
770
  isSafeUrl,
752
771
  sanitizeHast,
753
772
  scopeId,
package/dist/react.d.cts CHANGED
@@ -62,6 +62,8 @@ interface NotFoundProps extends PageBaseProps {
62
62
  homeHref?: string;
63
63
  }
64
64
  declare function NotFound({ path, homeHref, ...base }: NotFoundProps): ReactElement;
65
+ /** The fail-safe state `hylePage` renders when hyle cannot be reached. It takes no error: nothing internal can reach the markup. */
66
+ declare function Unavailable(base: PageBaseProps): ReactElement;
65
67
  interface RedirectingProps extends PageBaseProps {
66
68
  from?: string;
67
69
  to: string;
@@ -108,9 +110,12 @@ declare const en: {
108
110
  from: string;
109
111
  to: string;
110
112
  rdFallback: string;
113
+ unKind: string;
114
+ unTitle: string;
115
+ unBody: string;
111
116
  };
112
117
  type Strings = typeof en;
113
118
  /** Strings for a BCP 47 tag; the primary subtag decides, anything unknown falls back to English. */
114
119
  declare function stringsFor(locale: string | undefined): Strings;
115
120
 
116
- export { ArticlePage, type ArticlePageProps, DocsPage, HastContent, type HastContentProps, HastLimitError, HyleComponents, ListPage, type ListPageProps, NotFound, type NotFoundProps, PageBaseProps, Redirecting, type RedirectingProps, type Strings, TrustedServerHtml, isSafeUrl, sanitizeHast, scopeId, stringsFor };
121
+ export { ArticlePage, type ArticlePageProps, DocsPage, HastContent, type HastContentProps, HastLimitError, HyleComponents, ListPage, type ListPageProps, NotFound, type NotFoundProps, PageBaseProps, Redirecting, type RedirectingProps, type Strings, TrustedServerHtml, Unavailable, isSafeUrl, sanitizeHast, scopeId, stringsFor };
package/dist/react.d.ts CHANGED
@@ -62,6 +62,8 @@ interface NotFoundProps extends PageBaseProps {
62
62
  homeHref?: string;
63
63
  }
64
64
  declare function NotFound({ path, homeHref, ...base }: NotFoundProps): ReactElement;
65
+ /** The fail-safe state `hylePage` renders when hyle cannot be reached. It takes no error: nothing internal can reach the markup. */
66
+ declare function Unavailable(base: PageBaseProps): ReactElement;
65
67
  interface RedirectingProps extends PageBaseProps {
66
68
  from?: string;
67
69
  to: string;
@@ -108,9 +110,12 @@ declare const en: {
108
110
  from: string;
109
111
  to: string;
110
112
  rdFallback: string;
113
+ unKind: string;
114
+ unTitle: string;
115
+ unBody: string;
111
116
  };
112
117
  type Strings = typeof en;
113
118
  /** Strings for a BCP 47 tag; the primary subtag decides, anything unknown falls back to English. */
114
119
  declare function stringsFor(locale: string | undefined): Strings;
115
120
 
116
- export { ArticlePage, type ArticlePageProps, DocsPage, HastContent, type HastContentProps, HastLimitError, HyleComponents, ListPage, type ListPageProps, NotFound, type NotFoundProps, PageBaseProps, Redirecting, type RedirectingProps, type Strings, TrustedServerHtml, isSafeUrl, sanitizeHast, scopeId, stringsFor };
121
+ export { ArticlePage, type ArticlePageProps, DocsPage, HastContent, type HastContentProps, HastLimitError, HyleComponents, ListPage, type ListPageProps, NotFound, type NotFoundProps, PageBaseProps, Redirecting, type RedirectingProps, type Strings, TrustedServerHtml, Unavailable, isSafeUrl, sanitizeHast, scopeId, stringsFor };
package/dist/react.js CHANGED
@@ -7,11 +7,12 @@ import {
7
7
  NotFound,
8
8
  Redirecting,
9
9
  TrustedServerHtml,
10
+ Unavailable,
10
11
  isSafeUrl,
11
12
  sanitizeHast,
12
13
  scopeId,
13
14
  stringsFor
14
- } from "./chunk-XODMIT6N.js";
15
+ } from "./chunk-MHVWHSKC.js";
15
16
  export {
16
17
  ArticlePage,
17
18
  DocsPage,
@@ -21,6 +22,7 @@ export {
21
22
  NotFound,
22
23
  Redirecting,
23
24
  TrustedServerHtml,
25
+ Unavailable,
24
26
  isSafeUrl,
25
27
  sanitizeHast,
26
28
  scopeId,
@@ -0,0 +1,29 @@
1
+ import { R as Resolved } from './types-DjXf6iPo.cjs';
2
+
3
+ /** Resolves a site path to a result. The two built-in strategies are `fetchResolver` (default) and `cachedResolver` (`@tdacorp/hyle-sdk/next/use-cache`). */
4
+ type HyleResolver = (path: string, locale: string | undefined, limit?: number, cursor?: string) => Promise<Resolved>;
5
+ interface FetchResolverOptions {
6
+ readonly baseUrl: string;
7
+ readonly token: string;
8
+ /** The workspace id (HYLE_WORKSPACE_ID): the fetch is tagged before the response exists, so tags are built from it. */
9
+ readonly workspaceId: string;
10
+ /** Seconds before a cached page is revalidated in the background. Webhooks purge sooner. Default 300. */
11
+ readonly revalidate?: number;
12
+ /** Section slugs this mount can show besides the default "posts". Used only for tagging. */
13
+ readonly sections?: readonly string[];
14
+ }
15
+ /**
16
+ * The fetch cache tags for a workspace, in the contract's format (`hyle:1:ws:<id>`, `hyle:1:sec:<ws>:<section>`; docs/cp1-spec.md
17
+ * section 5.8). Tags known BEFORE the response, bounded however many URLs a visitor requests: the workspace tag, the default section
18
+ * and the configured `sections`. A publish, unpublish, rename or edit purges the section tag of the page's section, and the
19
+ * workspace tag covers section renames and the kill switch (docs/cp1-spec.md section 5.8), so a page refreshes without its
20
+ * own publication tag. A bad `workspaceId` or section slug throws a TypeError. A section not listed in `sections` falls back to `revalidate`, as does cross-section wikilink refresh.
21
+ */
22
+ declare function workspaceTags(workspaceId: string, sections?: readonly string[]): string[];
23
+ /**
24
+ * Next's fetch cache, tagged with `workspaceTags`: the default resolver of `hylePage`, exported to wrap (for example to adjust a
25
+ * result) or to reuse in a host's own routes. Works with no host configuration. Throws a TypeError without `workspaceId`.
26
+ */
27
+ declare function fetchResolver(options: FetchResolverOptions): HyleResolver;
28
+
29
+ export { type FetchResolverOptions as F, type HyleResolver as H, fetchResolver as f, workspaceTags as w };
@@ -0,0 +1,29 @@
1
+ import { R as Resolved } from './types-DjXf6iPo.js';
2
+
3
+ /** Resolves a site path to a result. The two built-in strategies are `fetchResolver` (default) and `cachedResolver` (`@tdacorp/hyle-sdk/next/use-cache`). */
4
+ type HyleResolver = (path: string, locale: string | undefined, limit?: number, cursor?: string) => Promise<Resolved>;
5
+ interface FetchResolverOptions {
6
+ readonly baseUrl: string;
7
+ readonly token: string;
8
+ /** The workspace id (HYLE_WORKSPACE_ID): the fetch is tagged before the response exists, so tags are built from it. */
9
+ readonly workspaceId: string;
10
+ /** Seconds before a cached page is revalidated in the background. Webhooks purge sooner. Default 300. */
11
+ readonly revalidate?: number;
12
+ /** Section slugs this mount can show besides the default "posts". Used only for tagging. */
13
+ readonly sections?: readonly string[];
14
+ }
15
+ /**
16
+ * The fetch cache tags for a workspace, in the contract's format (`hyle:1:ws:<id>`, `hyle:1:sec:<ws>:<section>`; docs/cp1-spec.md
17
+ * section 5.8). Tags known BEFORE the response, bounded however many URLs a visitor requests: the workspace tag, the default section
18
+ * and the configured `sections`. A publish, unpublish, rename or edit purges the section tag of the page's section, and the
19
+ * workspace tag covers section renames and the kill switch (docs/cp1-spec.md section 5.8), so a page refreshes without its
20
+ * own publication tag. A bad `workspaceId` or section slug throws a TypeError. A section not listed in `sections` falls back to `revalidate`, as does cross-section wikilink refresh.
21
+ */
22
+ declare function workspaceTags(workspaceId: string, sections?: readonly string[]): string[];
23
+ /**
24
+ * Next's fetch cache, tagged with `workspaceTags`: the default resolver of `hylePage`, exported to wrap (for example to adjust a
25
+ * result) or to reuse in a host's own routes. Works with no host configuration. Throws a TypeError without `workspaceId`.
26
+ */
27
+ declare function fetchResolver(options: FetchResolverOptions): HyleResolver;
28
+
29
+ export { type FetchResolverOptions as F, type HyleResolver as H, fetchResolver as f, workspaceTags as w };
@@ -125,11 +125,11 @@ var MAX_TAGS = 128;
125
125
  var DISABLED_REVALIDATE = 30;
126
126
  var tokens = /* @__PURE__ */ new Map();
127
127
  var keyOf = (baseUrl, workspaceId) => `${baseUrl}\0${workspaceId}`;
128
- async function resolveCached(baseUrl, workspaceId, path, locale, limit, revalidate) {
128
+ async function resolveCached(baseUrl, workspaceId, path, locale, limit, cursor, revalidate) {
129
129
  "use cache";
130
130
  const token = tokens.get(keyOf(baseUrl, workspaceId));
131
131
  if (!token) throw new Error("hyle: unknown cache configuration");
132
- const result = await createHyleClient({ baseUrl, token }).resolve({ path, ...locale ? { locale } : {}, ...limit > 0 ? { limit } : {} });
132
+ const result = await createHyleClient({ baseUrl, token }).resolve({ path, ...locale ? { locale } : {}, ...limit > 0 ? { limit } : {}, ...cursor ? { cursor } : {} });
133
133
  if (result.kind !== "disabled") (0, import_cache.cacheTag)(...result.cache.tags.filter(isValidCacheTag).slice(0, MAX_TAGS));
134
134
  (0, import_cache.cacheLife)({ stale: 30, revalidate: result.kind === "disabled" ? Math.min(revalidate, DISABLED_REVALIDATE) : revalidate });
135
135
  return result;
@@ -137,7 +137,7 @@ async function resolveCached(baseUrl, workspaceId, path, locale, limit, revalida
137
137
  function cachedResolver(options) {
138
138
  tokens.set(keyOf(options.baseUrl, options.workspaceId), options.token);
139
139
  const revalidate = options.revalidate ?? DEFAULT_REVALIDATE;
140
- return (path, locale, limit) => resolveCached(options.baseUrl, options.workspaceId, path, locale ?? "", limit ?? 0, revalidate);
140
+ return (path, locale, limit, cursor) => resolveCached(options.baseUrl, options.workspaceId, path, locale ?? "", limit ?? 0, cursor ?? "", revalidate);
141
141
  }
142
142
  // Annotate the CommonJS export names for ESM import in node:
143
143
  0 && (module.exports = {
@@ -1,4 +1,4 @@
1
- import { H as HyleResolver } from './resolve-DrmTGaCr.cjs';
1
+ import { H as HyleResolver } from './resolve-B_p0p4Zn.cjs';
2
2
  import './types-DjXf6iPo.cjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { H as HyleResolver } from './resolve-Cz-3Q7T-.js';
1
+ import { H as HyleResolver } from './resolve-fMx2W8RR.js';
2
2
  import './types-DjXf6iPo.js';
3
3
 
4
4
  /**
package/dist/use-cache.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  DEFAULT_REVALIDATE
3
- } from "./chunk-HVAWEEHG.js";
3
+ } from "./chunk-7VCMJUPC.js";
4
4
  import {
5
5
  createHyleClient,
6
6
  isValidCacheTag
7
- } from "./chunk-RI3BP4MI.js";
7
+ } from "./chunk-HFUJRNFP.js";
8
8
 
9
9
  // src/next/use-cache.ts
10
10
  import { cacheLife, cacheTag } from "next/cache";
@@ -12,11 +12,11 @@ var MAX_TAGS = 128;
12
12
  var DISABLED_REVALIDATE = 30;
13
13
  var tokens = /* @__PURE__ */ new Map();
14
14
  var keyOf = (baseUrl, workspaceId) => `${baseUrl}\0${workspaceId}`;
15
- async function resolveCached(baseUrl, workspaceId, path, locale, limit, revalidate) {
15
+ async function resolveCached(baseUrl, workspaceId, path, locale, limit, cursor, revalidate) {
16
16
  "use cache";
17
17
  const token = tokens.get(keyOf(baseUrl, workspaceId));
18
18
  if (!token) throw new Error("hyle: unknown cache configuration");
19
- const result = await createHyleClient({ baseUrl, token }).resolve({ path, ...locale ? { locale } : {}, ...limit > 0 ? { limit } : {} });
19
+ const result = await createHyleClient({ baseUrl, token }).resolve({ path, ...locale ? { locale } : {}, ...limit > 0 ? { limit } : {}, ...cursor ? { cursor } : {} });
20
20
  if (result.kind !== "disabled") cacheTag(...result.cache.tags.filter(isValidCacheTag).slice(0, MAX_TAGS));
21
21
  cacheLife({ stale: 30, revalidate: result.kind === "disabled" ? Math.min(revalidate, DISABLED_REVALIDATE) : revalidate });
22
22
  return result;
@@ -24,7 +24,7 @@ async function resolveCached(baseUrl, workspaceId, path, locale, limit, revalida
24
24
  function cachedResolver(options) {
25
25
  tokens.set(keyOf(options.baseUrl, options.workspaceId), options.token);
26
26
  const revalidate = options.revalidate ?? DEFAULT_REVALIDATE;
27
- return (path, locale, limit) => resolveCached(options.baseUrl, options.workspaceId, path, locale ?? "", limit ?? 0, revalidate);
27
+ return (path, locale, limit, cursor) => resolveCached(options.baseUrl, options.workspaceId, path, locale ?? "", limit ?? 0, cursor ?? "", revalidate);
28
28
  }
29
29
  export {
30
30
  cachedResolver
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tdacorp/hyle-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "hyle SDK: a typed client for hyle's resolve endpoint, a hast renderer for React, and a Next.js catch-all page with tag-based revalidation.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,29 +0,0 @@
1
- import {
2
- createHyleClient,
3
- isValidCacheTag
4
- } from "./chunk-RI3BP4MI.js";
5
-
6
- // src/next/resolve.ts
7
- var DEFAULT_REVALIDATE = 300;
8
- var MAX_TAGS = 64;
9
- function fetchTags(workspaceId, sections = []) {
10
- const slugs = /* @__PURE__ */ new Set(["posts", ...sections]);
11
- return [`hyle:1:ws:${workspaceId}`, ...[...slugs].map((s) => `hyle:1:sec:${workspaceId}:${s}`)].filter(isValidCacheTag).slice(0, MAX_TAGS);
12
- }
13
- function fetchResolver(options) {
14
- if (!options.workspaceId) throw new TypeError("hylePage: `workspaceId` is required for the fetch cache (or pass a cachedResolver)");
15
- const client = createHyleClient({ baseUrl: options.baseUrl, token: options.token });
16
- const revalidate = options.revalidate ?? DEFAULT_REVALIDATE;
17
- const tags = fetchTags(options.workspaceId, options.sections);
18
- return (path, locale, limit) => client.resolve({
19
- path,
20
- ...locale !== void 0 ? { locale } : {},
21
- ...limit !== void 0 ? { limit } : {},
22
- init: { next: { revalidate, tags } }
23
- });
24
- }
25
-
26
- export {
27
- DEFAULT_REVALIDATE,
28
- fetchResolver
29
- };
@@ -1,16 +0,0 @@
1
- import { R as Resolved } from './types-DjXf6iPo.js';
2
-
3
- /** Resolves a site path to a result. The two built-in strategies are `fetchResolver` (default) and `cachedResolver` (`@tdacorp/hyle-sdk/next/use-cache`). */
4
- type HyleResolver = (path: string, locale: string | undefined, limit?: number) => Promise<Resolved>;
5
- interface FetchResolverOptions {
6
- readonly baseUrl: string;
7
- readonly token: string;
8
- /** The workspace id (HYLE_WORKSPACE_ID): the fetch is tagged before the response exists, so tags are built from it. */
9
- readonly workspaceId: string;
10
- /** Seconds before a cached page is revalidated in the background. Webhooks purge sooner. Default 300. */
11
- readonly revalidate?: number;
12
- /** Section slugs this mount can show besides the default "posts". Used only for tagging. */
13
- readonly sections?: readonly string[];
14
- }
15
-
16
- export type { FetchResolverOptions as F, HyleResolver as H };
@@ -1,16 +0,0 @@
1
- import { R as Resolved } from './types-DjXf6iPo.cjs';
2
-
3
- /** Resolves a site path to a result. The two built-in strategies are `fetchResolver` (default) and `cachedResolver` (`@tdacorp/hyle-sdk/next/use-cache`). */
4
- type HyleResolver = (path: string, locale: string | undefined, limit?: number) => Promise<Resolved>;
5
- interface FetchResolverOptions {
6
- readonly baseUrl: string;
7
- readonly token: string;
8
- /** The workspace id (HYLE_WORKSPACE_ID): the fetch is tagged before the response exists, so tags are built from it. */
9
- readonly workspaceId: string;
10
- /** Seconds before a cached page is revalidated in the background. Webhooks purge sooner. Default 300. */
11
- readonly revalidate?: number;
12
- /** Section slugs this mount can show besides the default "posts". Used only for tagging. */
13
- readonly sections?: readonly string[];
14
- }
15
-
16
- export type { FetchResolverOptions as F, HyleResolver as H };