zudoku 0.37.0 → 0.38.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 (137) hide show
  1. package/dist/app/main.js +2 -0
  2. package/dist/app/main.js.map +1 -1
  3. package/dist/config/validators/common.d.ts +287 -18
  4. package/dist/config/validators/common.js +2 -0
  5. package/dist/config/validators/common.js.map +1 -1
  6. package/dist/config/validators/validate.d.ts +107 -7
  7. package/dist/lib/authentication/authentication.d.ts +1 -0
  8. package/dist/lib/authentication/providers/clerk.js +19 -0
  9. package/dist/lib/authentication/providers/clerk.js.map +1 -1
  10. package/dist/lib/authentication/providers/openid.d.ts +1 -0
  11. package/dist/lib/authentication/providers/openid.js +5 -0
  12. package/dist/lib/authentication/providers/openid.js.map +1 -1
  13. package/dist/lib/authentication/providers/supabase.js +5 -0
  14. package/dist/lib/authentication/providers/supabase.js.map +1 -1
  15. package/dist/lib/authentication/state.d.ts +0 -26
  16. package/dist/lib/authentication/state.js +1 -16
  17. package/dist/lib/authentication/state.js.map +1 -1
  18. package/dist/lib/components/Layout.js +5 -3
  19. package/dist/lib/components/Layout.js.map +1 -1
  20. package/dist/lib/components/Zudoku.js +3 -2
  21. package/dist/lib/components/Zudoku.js.map +1 -1
  22. package/dist/lib/core/ZudokuContext.d.ts +7 -0
  23. package/dist/lib/core/ZudokuContext.js +8 -3
  24. package/dist/lib/core/ZudokuContext.js.map +1 -1
  25. package/dist/lib/core/plugins.d.ts +8 -6
  26. package/dist/lib/plugins/markdown/MdxPage.js +2 -8
  27. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  28. package/dist/lib/plugins/openapi/Endpoint.js +1 -1
  29. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  30. package/dist/lib/plugins/openapi/OperationList.js +1 -1
  31. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  32. package/dist/lib/plugins/openapi/Sidecar.js +29 -5
  33. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  34. package/dist/lib/plugins/openapi/interfaces.d.ts +26 -0
  35. package/dist/lib/plugins/openapi/playground/Playground.js +1 -1
  36. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  37. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +2 -2
  38. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -1
  39. package/dist/lib/plugins/openapi/state.d.ts +25 -0
  40. package/dist/lib/plugins/openapi/state.js +18 -0
  41. package/dist/lib/plugins/openapi/state.js.map +1 -0
  42. package/dist/lib/plugins/search-pagefind/PagefindSearch.js +13 -4
  43. package/dist/lib/plugins/search-pagefind/PagefindSearch.js.map +1 -1
  44. package/dist/lib/plugins/search-pagefind/ResultList.js +19 -12
  45. package/dist/lib/plugins/search-pagefind/ResultList.js.map +1 -1
  46. package/dist/lib/plugins/search-pagefind/get-results.d.ts +8 -1
  47. package/dist/lib/plugins/search-pagefind/get-results.js +9 -4
  48. package/dist/lib/plugins/search-pagefind/get-results.js.map +1 -1
  49. package/dist/lib/util/traverse.d.ts +2 -8
  50. package/dist/lib/util/traverse.js.map +1 -1
  51. package/dist/lib/util/types.d.ts +7 -0
  52. package/dist/lib/util/types.js +2 -0
  53. package/dist/lib/util/types.js.map +1 -0
  54. package/dist/lib/util/useScrollToAnchor.js +18 -12
  55. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  56. package/dist/vite/api/schema-codegen.d.ts +1 -1
  57. package/dist/vite/api/schema-codegen.js +8 -4
  58. package/dist/vite/api/schema-codegen.js.map +1 -1
  59. package/dist/vite/plugin-api.js +5 -2
  60. package/dist/vite/plugin-api.js.map +1 -1
  61. package/lib/{AuthenticationPlugin-Cij2tPWa.js → AuthenticationPlugin-Duy_R1TU.js} +3 -3
  62. package/lib/{AuthenticationPlugin-Cij2tPWa.js.map → AuthenticationPlugin-Duy_R1TU.js.map} +1 -1
  63. package/lib/{Markdown-DT5Rrq8_.js → Markdown-DIZ8nBVC.js} +742 -738
  64. package/lib/{Markdown-DT5Rrq8_.js.map → Markdown-DIZ8nBVC.js.map} +1 -1
  65. package/lib/{MdxPage-D2rD1vC4.js → MdxPage-JEdbfW-f.js} +42 -47
  66. package/lib/MdxPage-JEdbfW-f.js.map +1 -0
  67. package/lib/{OasProvider-DdEBf2qS.js → OasProvider-D1A10JeA.js} +4 -4
  68. package/lib/{OasProvider-DdEBf2qS.js.map → OasProvider-D1A10JeA.js.map} +1 -1
  69. package/lib/{OperationList-DT4-gm_S.js → OperationList-yOmYzMIp.js} +1128 -1112
  70. package/lib/OperationList-yOmYzMIp.js.map +1 -0
  71. package/lib/{Select-z1Lwl0-J.js → Select-fAYcJ0OU.js} +8 -8
  72. package/lib/{Select-z1Lwl0-J.js.map → Select-fAYcJ0OU.js.map} +1 -1
  73. package/lib/{SlotletProvider-D8OBnr77.js → SlotletProvider-BEwNY8q0.js} +4 -4
  74. package/lib/{SlotletProvider-D8OBnr77.js.map → SlotletProvider-BEwNY8q0.js.map} +1 -1
  75. package/lib/{chunk-HA7DTUK3-ZGg2W6yV.js → chunk-HA7DTUK3-C4gP41vD.js} +5 -5
  76. package/lib/{chunk-HA7DTUK3-ZGg2W6yV.js.map → chunk-HA7DTUK3-C4gP41vD.js.map} +1 -1
  77. package/lib/hook-Cge6LiTK.js +1483 -0
  78. package/lib/hook-Cge6LiTK.js.map +1 -0
  79. package/lib/{index-DdQSV2RF.js → index-B0y3fTg-.js} +261 -243
  80. package/lib/index-B0y3fTg-.js.map +1 -0
  81. package/lib/{mutation-_Z5C2wFZ.js → mutation-EChriCeF.js} +2 -2
  82. package/lib/{mutation-_Z5C2wFZ.js.map → mutation-EChriCeF.js.map} +1 -1
  83. package/lib/post-processors/traverse.js.map +1 -1
  84. package/lib/{useExposedProps-BslIn-FE.js → useExposedProps-B9qXJedG.js} +2 -2
  85. package/lib/{useExposedProps-BslIn-FE.js.map → useExposedProps-B9qXJedG.js.map} +1 -1
  86. package/lib/zudoku.auth-auth0.js +1 -1
  87. package/lib/zudoku.auth-clerk.js +59 -41
  88. package/lib/zudoku.auth-clerk.js.map +1 -1
  89. package/lib/zudoku.auth-openid.js +76 -73
  90. package/lib/zudoku.auth-openid.js.map +1 -1
  91. package/lib/zudoku.components.js +370 -354
  92. package/lib/zudoku.components.js.map +1 -1
  93. package/lib/zudoku.hooks.js +1 -1
  94. package/lib/zudoku.plugin-api-catalog.js +23 -24
  95. package/lib/zudoku.plugin-api-catalog.js.map +1 -1
  96. package/lib/zudoku.plugin-api-keys.js +15 -16
  97. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  98. package/lib/zudoku.plugin-custom-pages.js +2 -2
  99. package/lib/zudoku.plugin-markdown.js +1 -1
  100. package/lib/zudoku.plugin-openapi.js +5 -6
  101. package/lib/zudoku.plugin-openapi.js.map +1 -1
  102. package/lib/zudoku.plugin-redirect.js +1 -1
  103. package/lib/zudoku.plugin-search-pagefind.js +133 -98
  104. package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
  105. package/lib/zudoku.plugins.js.map +1 -1
  106. package/package.json +2 -2
  107. package/src/app/main.tsx +2 -0
  108. package/src/lib/authentication/authentication.ts +2 -0
  109. package/src/lib/authentication/providers/clerk.tsx +20 -0
  110. package/src/lib/authentication/providers/openid.tsx +6 -0
  111. package/src/lib/authentication/providers/supabase.tsx +6 -0
  112. package/src/lib/authentication/state.ts +1 -35
  113. package/src/lib/components/Layout.tsx +14 -2
  114. package/src/lib/components/Zudoku.tsx +5 -2
  115. package/src/lib/core/ZudokuContext.ts +13 -6
  116. package/src/lib/core/plugins.ts +9 -9
  117. package/src/lib/plugins/markdown/MdxPage.tsx +1 -8
  118. package/src/lib/plugins/openapi/Endpoint.tsx +1 -1
  119. package/src/lib/plugins/openapi/OperationList.tsx +1 -1
  120. package/src/lib/plugins/openapi/Sidecar.tsx +36 -7
  121. package/src/lib/plugins/openapi/interfaces.ts +29 -0
  122. package/src/lib/plugins/openapi/playground/Playground.tsx +1 -1
  123. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +2 -1
  124. package/src/lib/plugins/openapi/state.ts +36 -0
  125. package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +26 -4
  126. package/src/lib/plugins/search-pagefind/ResultList.tsx +59 -47
  127. package/src/lib/plugins/search-pagefind/get-results.tsx +31 -10
  128. package/src/lib/util/traverse.ts +2 -6
  129. package/src/lib/util/types.ts +7 -0
  130. package/src/lib/util/useScrollToAnchor.ts +20 -12
  131. package/lib/MdxPage-D2rD1vC4.js.map +0 -1
  132. package/lib/OperationList-DT4-gm_S.js.map +0 -1
  133. package/lib/hook-DzQC8PzJ.js +0 -355
  134. package/lib/hook-DzQC8PzJ.js.map +0 -1
  135. package/lib/index-DdQSV2RF.js.map +0 -1
  136. package/lib/joinUrl-BjDooT-T.js +0 -1154
  137. package/lib/joinUrl-BjDooT-T.js.map +0 -1
@@ -1,93 +1,114 @@
1
1
  import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
- import { C as x } from "./ClientOnly-E7hGysn1.js";
3
- import { VisuallyHidden as y } from "@radix-ui/react-visually-hidden";
4
- import { d as f, k as j, j as S } from "./joinUrl-BjDooT-T.js";
5
- import { useCallback as b, useState as k } from "react";
6
- import { C, a as u, b as h, c as N, d as v, e as w, f as _ } from "./Callout-B2vsR09t.js";
7
- import { b as L } from "./Dialog-sbgekbjb.js";
8
- import { FileTextIcon as F } from "lucide-react";
9
- import { a as T, L as g } from "./chunk-HA7DTUK3-ZGg2W6yV.js";
10
- const q = async (r, o) => {
11
- const s = o.maxResults ?? 10, n = o.transformResults ?? (() => !0), i = [], l = R(r, n);
12
- for await (const a of l)
13
- if (i.push(a), i.length >= s) break;
14
- return i;
2
+ import { C as S } from "./ClientOnly-E7hGysn1.js";
3
+ import { VisuallyHidden as k } from "@radix-ui/react-visually-hidden";
4
+ import { u as C, a as v, m as y, o as N, d as L } from "./hook-Cge6LiTK.js";
5
+ import { useRef as j, useCallback as _, useLayoutEffect as w, useState as R } from "react";
6
+ import { B as T } from "./Button-Fp19CMUr.js";
7
+ import { C as F, a as f, b as p, c as q, d as I, e as E, f as $ } from "./Callout-B2vsR09t.js";
8
+ import { b as D } from "./Dialog-sbgekbjb.js";
9
+ import { BracketsIcon as P, FileTextIcon as B } from "lucide-react";
10
+ import { a as U, L as x } from "./chunk-HA7DTUK3-C4gP41vD.js";
11
+ const A = async ({
12
+ search: t,
13
+ options: o,
14
+ auth: a,
15
+ context: i
16
+ }) => {
17
+ const d = o.maxResults ?? 10, n = o.transformResults ?? (() => !0), s = [], l = O({
18
+ search: t,
19
+ transformFn: n,
20
+ auth: a,
21
+ context: i
22
+ });
23
+ for await (const r of l)
24
+ if (s.push(r), s.length >= d) break;
25
+ return s;
15
26
  };
16
- async function* R(r, o) {
27
+ async function* O({
28
+ search: t,
29
+ transformFn: o,
30
+ auth: a,
31
+ context: i
32
+ }) {
17
33
  let n = 0;
18
- for (; n < r.results.length; ) {
19
- const i = r.results.slice(
34
+ for (; n < t.results.length; ) {
35
+ const s = t.results.slice(
20
36
  n,
21
37
  n + 5
22
38
  );
23
- n += i.length;
24
- const l = await Promise.all(i.map((a) => a.data()));
25
- for (const a of l) {
26
- const t = o(a);
27
- t !== !1 && (t === !0 || t == null ? yield a : yield t);
39
+ n += s.length;
40
+ const l = await Promise.all(s.map((r) => r.data()));
41
+ for (const r of l) {
42
+ const c = o({ result: r, auth: a, context: i });
43
+ c !== !1 && (c === !0 || c == null ? yield r : yield c);
28
44
  }
29
45
  }
30
46
  }
31
- const $ = (r, o) => {
32
- const s = r.weighted_locations.reduce(
33
- (i, l) => i + l.balanced_score,
47
+ const z = (t, o) => {
48
+ const a = t.weighted_locations.reduce(
49
+ (d, n) => d + n.balanced_score,
34
50
  0
35
51
  );
36
52
  return o.weighted_locations.reduce(
37
- (i, l) => i + l.balanced_score,
53
+ (d, n) => d + n.balanced_score,
38
54
  0
39
- ) - s;
40
- }, p = "cursor-pointer border border-transparent data-[selected=true]:border-border", D = ({
41
- basePath: r,
55
+ ) - a;
56
+ }, g = "cursor-pointer border border-transparent data-[selected=true]:border-border", G = ({
57
+ basePath: t,
42
58
  searchResults: o,
43
- searchTerm: s,
44
- onClose: n,
45
- maxSubResults: i = 4
59
+ searchTerm: a,
60
+ onClose: i,
61
+ maxSubResults: d = 4
46
62
  }) => {
47
- const l = T(), a = b(
48
- (t) => {
49
- const c = t.replace(".html", "");
50
- return r && c.startsWith(r) ? c.slice(r.length) : c;
63
+ const n = U(), s = j(null), l = _(
64
+ (r) => {
65
+ const c = r.replace(".html", "");
66
+ return t && c.startsWith(t) ? c.slice(t.length) : c;
51
67
  },
52
- [r]
68
+ [t]
53
69
  );
54
- return /* @__PURE__ */ e.jsxs(C, { className: "max-h-[450px]", children: [
55
- s && o.length > 0 && /* @__PURE__ */ e.jsx(
56
- u,
70
+ return w(() => {
71
+ requestIdleCallback(() => {
72
+ var r;
73
+ (r = s.current) == null || r.scrollTo({ top: 0 });
74
+ });
75
+ }, [a]), /* @__PURE__ */ e.jsxs(F, { className: "max-h-[450px]", ref: s, children: [
76
+ a && o.length > 0 && /* @__PURE__ */ e.jsx(
77
+ f,
57
78
  {
58
79
  className: "text-sm text-muted-foreground",
59
- heading: `${o.length} results for "${s}"`
80
+ heading: `${o.length} results for "${a}"`
60
81
  }
61
82
  ),
62
- o.map((t) => /* @__PURE__ */ e.jsxs(
63
- u,
83
+ a && o.map((r) => /* @__PURE__ */ e.jsxs(
84
+ f,
64
85
  {
65
86
  children: [
66
87
  /* @__PURE__ */ e.jsx(
67
- h,
88
+ p,
68
89
  {
69
90
  asChild: !0,
70
- value: `${t.meta.title}-${t.url}`,
71
- className: p,
91
+ value: `${r.meta.title}-${r.url}`,
92
+ className: g,
72
93
  onSelect: () => {
73
- l(a(t.url)), n();
94
+ n(l(r.url)), i();
74
95
  },
75
- children: /* @__PURE__ */ e.jsxs(g, { to: a(t.url), children: [
76
- /* @__PURE__ */ e.jsx(F, { size: 20, className: "text-muted-foreground" }),
77
- t.meta.title
96
+ children: /* @__PURE__ */ e.jsxs(x, { to: l(r.url), children: [
97
+ r.meta.section === "openapi" ? /* @__PURE__ */ e.jsx(P, {}) : /* @__PURE__ */ e.jsx(B, {}),
98
+ r.meta.title
78
99
  ] })
79
100
  }
80
101
  ),
81
- t.sub_results.sort($).slice(0, i).map((c) => /* @__PURE__ */ e.jsx(
82
- h,
102
+ r.sub_results.sort(z).slice(0, d).map((c) => /* @__PURE__ */ e.jsx(
103
+ p,
83
104
  {
84
105
  asChild: !0,
85
- value: `${t.meta.title}-${c.url}`,
86
- className: p,
106
+ value: `sub-${r.meta.title}-${c.url}`,
107
+ className: g,
87
108
  onSelect: () => {
88
- l(a(c.url)), n();
109
+ n(l(c.url)), i();
89
110
  },
90
- children: /* @__PURE__ */ e.jsx(g, { to: a(c.url), onClick: n, children: /* @__PURE__ */ e.jsxs("div", { className: "flex flex-col items-start gap-2 ms-2.5 ps-5 border-l border-muted-foreground/50", children: [
111
+ children: /* @__PURE__ */ e.jsx(x, { to: l(c.url), onClick: i, children: /* @__PURE__ */ e.jsxs("div", { className: "flex flex-col items-start gap-2 ms-2.5 ps-5 border-l border-muted-foreground/50", children: [
91
112
  /* @__PURE__ */ e.jsx("span", { className: "font-bold", children: c.title }),
92
113
  /* @__PURE__ */ e.jsx(
93
114
  "span",
@@ -98,14 +119,14 @@ const $ = (r, o) => {
98
119
  )
99
120
  ] }) })
100
121
  },
101
- `${t.meta.title}-${c.url}`
122
+ `sub-${r.meta.title}-${c.url}`
102
123
  ))
103
124
  ]
104
125
  },
105
- [t.meta.title ?? t.excerpt, t.url].join("-")
126
+ [r.meta.title ?? r.excerpt, r.url].join("-")
106
127
  ))
107
128
  ] });
108
- }, d = {
129
+ }, u = {
109
130
  // Slightly lower than default because API docs tend to have repetitive terms (parameter names, HTTP methods, etc.)
110
131
  termFrequency: 0.8,
111
132
  // Lower than default because API documentation pages tend to be longer due to comprehensive endpoint documentation
@@ -114,62 +135,76 @@ const $ = (r, o) => {
114
135
  termSimilarity: 1.2,
115
136
  // Slightly lower than default because API docs might have legitimate repetition of terms
116
137
  termSaturation: 1.2
117
- }, E = (r) => import(
138
+ }, K = (t) => import(
118
139
  /* @vite-ignore */
119
- S(r, "/pagefind/pagefind.js")
120
- ), I = (r) => {
121
- const { data: o, ...s } = f({
122
- queryKey: ["pagefind", r.ranking],
140
+ L(t, "/pagefind/pagefind.js")
141
+ ), H = (t) => {
142
+ const { data: o, ...a } = y({
143
+ queryKey: ["pagefind", t.ranking],
123
144
  retry: !1,
124
145
  queryFn: async () => {
125
- var i, l, a, t;
126
- const n = await E(r.basePath);
127
- return await n.init(), await n.options({
146
+ var d, n, s, l;
147
+ const i = await K(t.basePath);
148
+ return await i.init(), await i.options({
128
149
  ranking: {
129
- termFrequency: ((i = r.ranking) == null ? void 0 : i.termFrequency) ?? d.termFrequency,
130
- pageLength: ((l = r.ranking) == null ? void 0 : l.pageLength) ?? d.pageLength,
131
- termSimilarity: ((a = r.ranking) == null ? void 0 : a.termSimilarity) ?? d.termSimilarity,
132
- termSaturation: ((t = r.ranking) == null ? void 0 : t.termSaturation) ?? d.termSaturation
150
+ termFrequency: ((d = t.ranking) == null ? void 0 : d.termFrequency) ?? u.termFrequency,
151
+ pageLength: ((n = t.ranking) == null ? void 0 : n.pageLength) ?? u.pageLength,
152
+ termSimilarity: ((s = t.ranking) == null ? void 0 : s.termSimilarity) ?? u.termSimilarity,
153
+ termSaturation: ((l = t.ranking) == null ? void 0 : l.termSaturation) ?? u.termSaturation
133
154
  }
134
- }), n;
155
+ }), i;
135
156
  },
136
157
  enabled: typeof window < "u"
137
158
  });
138
- return s.isError && console.error(s.error), { ...s, pagefind: o };
139
- }, P = ({
140
- isOpen: r,
159
+ return a.isError && a.error.message !== "NOT_BUILT_YET" && console.error(a.error), { ...a, pagefind: o };
160
+ }, V = ({
161
+ isOpen: t,
141
162
  onClose: o,
142
- options: s
163
+ options: a
143
164
  }) => {
144
- const { pagefind: n, error: i, isError: l } = I(s), [a, t] = k(""), { data: c } = f({
145
- queryKey: ["pagefind-search", a],
165
+ const { pagefind: i, error: d, isError: n } = H(a), [s, l] = R(""), r = C(), c = v(), h = j(null), { data: b } = y({
166
+ queryKey: ["pagefind-search", s],
146
167
  queryFn: async () => {
147
- const m = await (n == null ? void 0 : n.search(a));
148
- return m ? q(m, s) : [];
168
+ const m = await (i == null ? void 0 : i.search(s));
169
+ return m ? A({ search: m, options: a, auth: r, context: c }) : [];
149
170
  },
150
- placeholderData: j,
151
- enabled: !!n && !!a
171
+ placeholderData: N,
172
+ enabled: !!i && !!s
152
173
  });
153
174
  return /* @__PURE__ */ e.jsxs(
154
- N,
175
+ q,
155
176
  {
156
177
  command: { shouldFilter: !1 },
157
178
  content: { className: "max-w-[750px]" },
158
- open: r,
179
+ open: t,
159
180
  onOpenChange: o,
160
181
  children: [
161
- /* @__PURE__ */ e.jsx(y, { children: /* @__PURE__ */ e.jsx(L, { children: "Search" }) }),
182
+ /* @__PURE__ */ e.jsx(k, { children: /* @__PURE__ */ e.jsx(D, { children: "Search" }) }),
162
183
  /* @__PURE__ */ e.jsx(
163
- v,
184
+ I,
164
185
  {
186
+ ref: h,
165
187
  placeholder: "Search...",
166
- value: a,
167
- onValueChange: t,
168
- disabled: l
188
+ value: s,
189
+ onValueChange: l,
190
+ disabled: n
169
191
  }
170
192
  ),
171
- /* @__PURE__ */ e.jsx(w, { children: a ? "No results found." : "Start typing to search" }),
172
- l ? /* @__PURE__ */ e.jsx("div", { className: "p-4 text-sm", children: i.message === "NOT_BUILT_YET" ? /* @__PURE__ */ e.jsxs(_, { type: "info", children: [
193
+ /* @__PURE__ */ e.jsx(E, { children: s ? /* @__PURE__ */ e.jsxs("div", { className: "flex flex-col items-center", children: [
194
+ "No results found.",
195
+ /* @__PURE__ */ e.jsx(
196
+ T,
197
+ {
198
+ variant: "link",
199
+ onClick: () => {
200
+ var m;
201
+ l(""), (m = h.current) == null || m.focus();
202
+ },
203
+ children: "Clear search"
204
+ }
205
+ )
206
+ ] }) : "Start typing to search" }),
207
+ n ? /* @__PURE__ */ e.jsx("div", { className: "p-4 text-sm", children: d.message === "NOT_BUILT_YET" ? /* @__PURE__ */ e.jsxs($, { type: "info", children: [
173
208
  "Search is currently not available in development mode by default.",
174
209
  /* @__PURE__ */ e.jsx("br", {}),
175
210
  "To still use search in development, run ",
@@ -182,22 +217,22 @@ const $ = (r, o) => {
182
217
  /* @__PURE__ */ e.jsx("code", { children: "public" }),
183
218
  " directory."
184
219
  ] }) : "An error occurred while loading search." }) : /* @__PURE__ */ e.jsx(
185
- D,
220
+ G,
186
221
  {
187
- basePath: s.basePath,
188
- searchResults: c ?? [],
189
- searchTerm: a,
222
+ basePath: a.basePath,
223
+ searchResults: b ?? [],
224
+ searchTerm: s,
190
225
  onClose: o,
191
- maxSubResults: s.maxSubResults
226
+ maxSubResults: a.maxSubResults
192
227
  }
193
228
  )
194
229
  ]
195
230
  }
196
231
  );
197
- }, M = (r) => ({
198
- renderSearch: ({ isOpen: o, onClose: s }) => /* @__PURE__ */ e.jsx(x, { children: /* @__PURE__ */ e.jsx(P, { isOpen: o, onClose: s, options: r }) })
232
+ }, ae = (t) => ({
233
+ renderSearch: ({ isOpen: o, onClose: a }) => /* @__PURE__ */ e.jsx(S, { children: /* @__PURE__ */ e.jsx(V, { isOpen: o, onClose: a, options: t }) })
199
234
  });
200
235
  export {
201
- M as pagefindSearchPlugin
236
+ ae as pagefindSearchPlugin
202
237
  };
203
238
  //# sourceMappingURL=zudoku.plugin-search-pagefind.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-search-pagefind.js","sources":["../src/lib/plugins/search-pagefind/get-results.tsx","../src/lib/plugins/search-pagefind/ResultList.tsx","../src/lib/plugins/search-pagefind/PagefindSearch.tsx","../src/lib/plugins/search-pagefind/index.tsx"],"sourcesContent":["import type { PagefindOptions } from \"./index.js\";\nimport type { PagefindSearchFragment, PagefindSearchResults } from \"./types.js\";\n\nexport const getResults = async (\n search: PagefindSearchResults,\n options: PagefindOptions,\n) => {\n const maxResults = options.maxResults ?? 10;\n const transformFn = options.transformResults ?? (() => true);\n\n const transformedResults: PagefindSearchFragment[] = [];\n\n const generator = searchResultGenerator(search, transformFn);\n\n for await (const result of generator) {\n transformedResults.push(result);\n if (transformedResults.length >= maxResults) break;\n }\n\n return transformedResults;\n};\n\nasync function* searchResultGenerator(\n search: PagefindSearchResults,\n transformFn: NonNullable<PagefindOptions[\"transformResults\"]>,\n) {\n const batchSize = 5;\n let processedCount = 0;\n\n while (processedCount < search.results.length) {\n const batch = search.results.slice(\n processedCount,\n processedCount + batchSize,\n );\n processedCount += batch.length;\n\n const batchData = await Promise.all(batch.map((result) => result.data()));\n\n for (const result of batchData) {\n const transformed = transformFn(result);\n\n if (transformed === false) {\n // Skip this result\n continue;\n } else if (transformed === true || transformed == null) {\n // Keep the original result\n yield result;\n } else {\n // Return the transformed result\n yield transformed;\n }\n }\n }\n}\n","import { FileTextIcon } from \"lucide-react\";\nimport { useCallback } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\nimport { CommandGroup, CommandItem, CommandList } from \"zudoku/ui/Command.js\";\nimport {\n type PagefindSearchFragment,\n type PagefindSubResult,\n} from \"./types.js\";\n\nconst sortSubResults = (a: PagefindSubResult, b: PagefindSubResult) => {\n const aScore = a.weighted_locations.reduce(\n (sum, loc) => sum + loc.balanced_score,\n 0,\n );\n const bScore = b.weighted_locations.reduce(\n (sum, loc) => sum + loc.balanced_score,\n 0,\n );\n return bScore - aScore;\n};\n\nconst hoverClassname = `cursor-pointer border border-transparent data-[selected=true]:border-border`;\n\nexport const ResultList = ({\n basePath,\n searchResults,\n searchTerm,\n onClose,\n maxSubResults = 4,\n}: {\n basePath?: string;\n searchResults: PagefindSearchFragment[];\n searchTerm: string;\n onClose: () => void;\n maxSubResults?: number;\n}) => {\n const navigate = useNavigate();\n\n const cleanResultUrl = useCallback(\n (url: string) => {\n const clean = url.replace(\".html\", \"\");\n return basePath && clean.startsWith(basePath)\n ? clean.slice(basePath.length)\n : clean;\n },\n [basePath],\n );\n\n return (\n <CommandList className=\"max-h-[450px]\">\n {searchTerm && searchResults.length > 0 && (\n <CommandGroup\n className=\"text-sm text-muted-foreground\"\n heading={`${searchResults.length} results for \"${searchTerm}\"`}\n />\n )}\n {searchResults.map((result) => (\n <CommandGroup\n key={[result.meta.title ?? result.excerpt, result.url].join(\"-\")}\n >\n <CommandItem\n asChild\n value={`${result.meta.title}-${result.url}`}\n className={hoverClassname}\n onSelect={() => {\n void navigate(cleanResultUrl(result.url));\n onClose();\n }}\n >\n <Link to={cleanResultUrl(result.url)}>\n <FileTextIcon size={20} className=\"text-muted-foreground\" />\n {result.meta.title}\n </Link>\n </CommandItem>\n {result.sub_results\n .sort(sortSubResults)\n .slice(0, maxSubResults)\n .map((subResult) => (\n <CommandItem\n asChild\n key={`${result.meta.title}-${subResult.url}`}\n value={`${result.meta.title}-${subResult.url}`}\n className={hoverClassname}\n onSelect={() => {\n void navigate(cleanResultUrl(subResult.url));\n onClose();\n }}\n >\n <Link to={cleanResultUrl(subResult.url)} onClick={onClose}>\n <div className=\"flex flex-col items-start gap-2 ms-2.5 ps-5 border-l border-muted-foreground/50\">\n <span className=\"font-bold\">{subResult.title}</span>\n <span\n className=\"text-[13px] [&_mark]:bg-primary [&_mark]:text-primary-foreground\"\n dangerouslySetInnerHTML={{ __html: subResult.excerpt }}\n />\n </div>\n </Link>\n </CommandItem>\n ))}\n </CommandGroup>\n ))}\n </CommandList>\n );\n};\n","import { VisuallyHidden } from \"@radix-ui/react-visually-hidden\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { Callout } from \"zudoku/ui/Callout.js\";\nimport {\n CommandDialog,\n CommandEmpty,\n CommandInput,\n} from \"zudoku/ui/Command.js\";\nimport { DialogTitle } from \"zudoku/ui/Dialog.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { getResults } from \"./get-results.js\";\nimport type { PagefindOptions } from \"./index.js\";\nimport { ResultList } from \"./ResultList.js\";\nimport type { Pagefind } from \"./types.js\";\n\nconst DEFAULT_RANKING = {\n // Slightly lower than default because API docs tend to have repetitive terms (parameter names, HTTP methods, etc.)\n termFrequency: 0.8,\n // Lower than default because API documentation pages tend to be longer due to comprehensive endpoint documentation\n pageLength: 0.6,\n // Slightly higher than default because in technical documentation, exact matches should be prioritized\n termSimilarity: 1.2,\n // Slightly lower than default because API docs might have legitimate repetition of terms\n termSaturation: 1.2,\n};\n\nconst importPagefind = (basePath?: string): Promise<Pagefind> =>\n import.meta.env.DEV\n ? // @ts-expect-error TypeScript can't resolve the import\n import(/* @vite-ignore */ \"/pagefind/pagefind.js\")\n : import(/* @vite-ignore */ joinUrl(basePath, \"/pagefind/pagefind.js\"));\n\nconst usePagefind = (options: PagefindOptions) => {\n const { data: pagefind, ...result } = useQuery<Pagefind>({\n queryKey: [\"pagefind\", options.ranking],\n retry: false,\n queryFn: async () => {\n const pagefind = await importPagefind(options.basePath);\n await pagefind.init();\n await pagefind.options({\n ranking: {\n termFrequency:\n options.ranking?.termFrequency ?? DEFAULT_RANKING.termFrequency,\n pageLength: options.ranking?.pageLength ?? DEFAULT_RANKING.pageLength,\n termSimilarity:\n options.ranking?.termSimilarity ?? DEFAULT_RANKING.termSimilarity,\n termSaturation:\n options.ranking?.termSaturation ?? DEFAULT_RANKING.termSaturation,\n },\n });\n\n return pagefind;\n },\n enabled: typeof window !== \"undefined\",\n });\n\n if (result.isError) {\n // eslint-disable-next-line no-console\n console.error(result.error);\n }\n\n return { ...result, pagefind };\n};\n\nexport const PagefindSearch = ({\n isOpen,\n onClose,\n options,\n}: {\n isOpen: boolean;\n onClose: () => void;\n options: PagefindOptions;\n}) => {\n const { pagefind, error, isError } = usePagefind(options);\n const [searchTerm, setSearchTerm] = useState(\"\");\n\n const { data: searchResults } = useQuery({\n queryKey: [\"pagefind-search\", searchTerm],\n queryFn: async () => {\n const search = await pagefind?.search(searchTerm);\n if (!search) return [];\n return getResults(search, options);\n },\n placeholderData: keepPreviousData,\n enabled: !!pagefind && !!searchTerm,\n });\n\n return (\n <CommandDialog\n command={{ shouldFilter: false }}\n content={{ className: \"max-w-[750px]\" }}\n open={isOpen}\n onOpenChange={onClose}\n >\n <VisuallyHidden>\n <DialogTitle>Search</DialogTitle>\n </VisuallyHidden>\n <CommandInput\n placeholder=\"Search...\"\n value={searchTerm}\n onValueChange={setSearchTerm}\n disabled={isError}\n />\n <CommandEmpty>\n {searchTerm ? \"No results found.\" : \"Start typing to search\"}\n </CommandEmpty>\n {isError ? (\n <div className=\"p-4 text-sm\">\n {error.message === \"NOT_BUILT_YET\" ? (\n <Callout type=\"info\">\n Search is currently not available in development mode by default.\n <br />\n To still use search in development, run <code>\n zudoku build\n </code>{\" \"}\n and copy the <code>dist/pagefind</code> directory to your{\" \"}\n <code>public</code> directory.\n </Callout>\n ) : (\n \"An error occurred while loading search.\"\n )}\n </div>\n ) : (\n <ResultList\n basePath={options.basePath}\n searchResults={searchResults ?? []}\n searchTerm={searchTerm}\n onClose={onClose}\n maxSubResults={options.maxSubResults}\n />\n )}\n </CommandDialog>\n );\n};\n","import type { ZudokuConfig } from \"../../../config/validators/validate.js\";\nimport { ClientOnly } from \"../../components/ClientOnly.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { PagefindSearch } from \"./PagefindSearch.js\";\n\nexport type PagefindOptions = Extract<\n ZudokuConfig[\"search\"],\n { type: \"pagefind\" }\n> & { basePath?: string };\n\nexport const pagefindSearchPlugin = (\n options: PagefindOptions,\n): ZudokuPlugin => {\n return {\n renderSearch: ({ isOpen, onClose }) => (\n <ClientOnly>\n <PagefindSearch isOpen={isOpen} onClose={onClose} options={options} />\n </ClientOnly>\n ),\n };\n};\n"],"names":["getResults","search","options","maxResults","transformFn","transformedResults","generator","searchResultGenerator","result","processedCount","batch","batchData","transformed","sortSubResults","a","b","aScore","sum","loc","hoverClassname","ResultList","basePath","searchResults","searchTerm","onClose","maxSubResults","navigate","useNavigate","cleanResultUrl","useCallback","url","clean","jsxs","CommandList","jsx","CommandGroup","CommandItem","Link","FileTextIcon","subResult","DEFAULT_RANKING","importPagefind","joinUrl","usePagefind","pagefind","useQuery","_a","_b","_c","_d","PagefindSearch","isOpen","error","isError","setSearchTerm","useState","keepPreviousData","CommandDialog","VisuallyHidden","DialogTitle","CommandInput","CommandEmpty","Callout","pagefindSearchPlugin","ClientOnly"],"mappings":";;;;;;;;;AAGa,MAAAA,IAAa,OACxBC,GACAC,MACG;AACG,QAAAC,IAAaD,EAAQ,cAAc,IACnCE,IAAcF,EAAQ,qBAAqB,MAAM,KAEjDG,IAA+C,CAAC,GAEhDC,IAAYC,EAAsBN,GAAQG,CAAW;AAE3D,mBAAiBI,KAAUF;AAErB,QADJD,EAAmB,KAAKG,CAAM,GAC1BH,EAAmB,UAAUF,EAAY;AAGxC,SAAAE;AACT;AAEA,gBAAgBE,EACdN,GACAG,GACA;AAEA,MAAIK,IAAiB;AAEd,SAAAA,IAAiBR,EAAO,QAAQ,UAAQ;AACvC,UAAAS,IAAQT,EAAO,QAAQ;AAAA,MAC3BQ;AAAA,MACAA,IAAiB;AAAA,IACnB;AACA,IAAAA,KAAkBC,EAAM;AAElB,UAAAC,IAAY,MAAM,QAAQ,IAAID,EAAM,IAAI,CAACF,MAAWA,EAAO,KAAK,CAAC,CAAC;AAExE,eAAWA,KAAUG,GAAW;AACxB,YAAAC,IAAcR,EAAYI,CAAM;AAEtC,MAAII,MAAgB,OAGTA,MAAgB,MAAQA,KAAe,OAE1C,MAAAJ,IAGA,MAAAI;AAAA,IACR;AAAA,EACF;AAEJ;AC5CA,MAAMC,IAAiB,CAACC,GAAsBC,MAAyB;AAC/D,QAAAC,IAASF,EAAE,mBAAmB;AAAA,IAClC,CAACG,GAAKC,MAAQD,IAAMC,EAAI;AAAA,IACxB;AAAA,EACF;AAKA,SAJeH,EAAE,mBAAmB;AAAA,IAClC,CAACE,GAAKC,MAAQD,IAAMC,EAAI;AAAA,IACxB;AAAA,EACF,IACgBF;AAClB,GAEMG,IAAiB,+EAEVC,IAAa,CAAC;AAAA,EACzB,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,YAAAC;AAAA,EACA,SAAAC;AAAA,EACA,eAAAC,IAAgB;AAClB,MAMM;AACJ,QAAMC,IAAWC,EAAY,GAEvBC,IAAiBC;AAAA,IACrB,CAACC,MAAgB;AACf,YAAMC,IAAQD,EAAI,QAAQ,SAAS,EAAE;AAC9B,aAAAT,KAAYU,EAAM,WAAWV,CAAQ,IACxCU,EAAM,MAAMV,EAAS,MAAM,IAC3BU;AAAA,IACN;AAAA,IACA,CAACV,CAAQ;AAAA,EACX;AAGE,SAAAW,gBAAAA,EAAA,KAACC,GAAY,EAAA,WAAU,iBACpB,UAAA;AAAA,IAAcV,KAAAD,EAAc,SAAS,KACpCY,gBAAAA,EAAA;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,GAAGb,EAAc,MAAM,iBAAiBC,CAAU;AAAA,MAAA;AAAA,IAC7D;AAAA,IAEDD,EAAc,IAAI,CAACd,MAClBwB,gBAAAA,EAAA;AAAA,MAACG;AAAA,MAAA;AAAA,QAGC,UAAA;AAAA,UAAAD,gBAAAA,EAAA;AAAA,YAACE;AAAA,YAAA;AAAA,cACC,SAAO;AAAA,cACP,OAAO,GAAG5B,EAAO,KAAK,KAAK,IAAIA,EAAO,GAAG;AAAA,cACzC,WAAWW;AAAA,cACX,UAAU,MAAM;AACd,gBAAKO,EAASE,EAAepB,EAAO,GAAG,CAAC,GAChCgB,EAAA;AAAA,cACV;AAAA,cAEA,iCAACa,GAAK,EAAA,IAAIT,EAAepB,EAAO,GAAG,GACjC,UAAA;AAAA,gBAAA0B,gBAAAA,EAAA,IAACI,GAAa,EAAA,MAAM,IAAI,WAAU,yBAAwB;AAAA,gBACzD9B,EAAO,KAAK;AAAA,cAAA,EACf,CAAA;AAAA,YAAA;AAAA,UACF;AAAA,UACCA,EAAO,YACL,KAAKK,CAAc,EACnB,MAAM,GAAGY,CAAa,EACtB,IAAI,CAACc,MACJL,gBAAAA,EAAA;AAAA,YAACE;AAAA,YAAA;AAAA,cACC,SAAO;AAAA,cAEP,OAAO,GAAG5B,EAAO,KAAK,KAAK,IAAI+B,EAAU,GAAG;AAAA,cAC5C,WAAWpB;AAAA,cACX,UAAU,MAAM;AACd,gBAAKO,EAASE,EAAeW,EAAU,GAAG,CAAC,GACnCf,EAAA;AAAA,cACV;AAAA,cAEA,UAACU,gBAAAA,EAAA,IAAAG,GAAA,EAAK,IAAIT,EAAeW,EAAU,GAAG,GAAG,SAASf,GAChD,UAAAQ,gBAAAA,OAAC,OAAI,EAAA,WAAU,mFACb,UAAA;AAAA,gBAAAE,gBAAAA,EAAA,IAAC,QAAK,EAAA,WAAU,aAAa,UAAAK,EAAU,OAAM;AAAA,gBAC7CL,gBAAAA,EAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAU;AAAA,oBACV,yBAAyB,EAAE,QAAQK,EAAU,QAAQ;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACvD,EAAA,CACF,EACF,CAAA;AAAA,YAAA;AAAA,YAhBK,GAAG/B,EAAO,KAAK,KAAK,IAAI+B,EAAU,GAAG;AAAA,UAkB7C,CAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAxCE,CAAC/B,EAAO,KAAK,SAASA,EAAO,SAASA,EAAO,GAAG,EAAE,KAAK,GAAG;AAAA,IA0ClE,CAAA;AAAA,EAAA,GACH;AAEJ,GCvFMgC,IAAkB;AAAA;AAAA,EAEtB,eAAe;AAAA;AAAA,EAEf,YAAY;AAAA;AAAA,EAEZ,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAClB,GAEMC,IAAiB,CAACpB,MAIlB;AAAA;AAAA,EAA0BqB,EAAQrB,GAAU,uBAAuB;AAAA,GAEnEsB,IAAc,CAACzC,MAA6B;AAChD,QAAM,EAAE,MAAM0C,GAAU,GAAGpC,EAAA,IAAWqC,EAAmB;AAAA,IACvD,UAAU,CAAC,YAAY3C,EAAQ,OAAO;AAAA,IACtC,OAAO;AAAA,IACP,SAAS,YAAY;;AACnB,YAAM0C,IAAW,MAAMH,EAAevC,EAAQ,QAAQ;AACtD,mBAAM0C,EAAS,KAAK,GACpB,MAAMA,EAAS,QAAQ;AAAA,QACrB,SAAS;AAAA,UACP,iBACEE,IAAA5C,EAAQ,YAAR,gBAAA4C,EAAiB,kBAAiBN,EAAgB;AAAA,UACpD,cAAYO,IAAA7C,EAAQ,YAAR,gBAAA6C,EAAiB,eAAcP,EAAgB;AAAA,UAC3D,kBACEQ,IAAA9C,EAAQ,YAAR,gBAAA8C,EAAiB,mBAAkBR,EAAgB;AAAA,UACrD,kBACES,IAAA/C,EAAQ,YAAR,gBAAA+C,EAAiB,mBAAkBT,EAAgB;AAAA,QAAA;AAAA,MACvD,CACD,GAEMI;AAAAA,IACT;AAAA,IACA,SAAS,OAAO,SAAW;AAAA,EAAA,CAC5B;AAED,SAAIpC,EAAO,WAED,QAAA,MAAMA,EAAO,KAAK,GAGrB,EAAE,GAAGA,GAAQ,UAAAoC,EAAS;AAC/B,GAEaM,IAAiB,CAAC;AAAA,EAC7B,QAAAC;AAAA,EACA,SAAA3B;AAAA,EACA,SAAAtB;AACF,MAIM;AACJ,QAAM,EAAE,UAAA0C,GAAU,OAAAQ,GAAO,SAAAC,EAAQ,IAAIV,EAAYzC,CAAO,GAClD,CAACqB,GAAY+B,CAAa,IAAIC,EAAS,EAAE,GAEzC,EAAE,MAAMjC,EAAc,IAAIuB,EAAS;AAAA,IACvC,UAAU,CAAC,mBAAmBtB,CAAU;AAAA,IACxC,SAAS,YAAY;AACnB,YAAMtB,IAAS,OAAM2C,KAAA,gBAAAA,EAAU,OAAOrB;AAClC,aAACtB,IACED,EAAWC,GAAQC,CAAO,IADb,CAAC;AAAA,IAEvB;AAAA,IACA,iBAAiBsD;AAAA,IACjB,SAAS,CAAC,CAACZ,KAAY,CAAC,CAACrB;AAAA,EAAA,CAC1B;AAGC,SAAAS,gBAAAA,EAAA;AAAA,IAACyB;AAAA,IAAA;AAAA,MACC,SAAS,EAAE,cAAc,GAAM;AAAA,MAC/B,SAAS,EAAE,WAAW,gBAAgB;AAAA,MACtC,MAAMN;AAAA,MACN,cAAc3B;AAAA,MAEd,UAAA;AAAA,QAAAU,gBAAAA,MAACwB,GACC,EAAA,UAAAxB,gBAAAA,EAAA,IAACyB,GAAY,EAAA,UAAA,SAAM,CAAA,GACrB;AAAA,QACAzB,gBAAAA,EAAA;AAAA,UAAC0B;AAAA,UAAA;AAAA,YACC,aAAY;AAAA,YACZ,OAAOrC;AAAA,YACP,eAAe+B;AAAA,YACf,UAAUD;AAAA,UAAA;AAAA,QACZ;AAAA,8BACCQ,GAAA,EACE,UAAatC,IAAA,sBAAsB,0BACtC;AAAA,QACC8B,IACEnB,gBAAAA,EAAA,IAAA,OAAA,EAAI,WAAU,eACZ,UAAMkB,EAAA,YAAY,kBAChBpB,gBAAAA,EAAAA,KAAA8B,GAAQ,EAAA,MAAK,QAAO,UAAA;AAAA,UAAA;AAAA,gCAElB,MAAG,EAAA;AAAA,UAAE;AAAA,UACmC5B,gBAAAA,EAAAA,IAAA,UAAK,UAE9C,gBAAA;AAAA,UAAQ;AAAA,UAAI;AAAA,UACEA,gBAAAA,EAAAA,IAAA,UAAK,UAAa,iBAAA;AAAA,UAAO;AAAA,UAAmB;AAAA,UACzDA,gBAAAA,EAAAA,IAAA,UAAK,UAAM,UAAA;AAAA,UAAO;AAAA,QAAA,GACrB,IAEA,0CAEJ,CAAA,IAEAA,gBAAAA,EAAA;AAAA,UAACd;AAAA,UAAA;AAAA,YACC,UAAUlB,EAAQ;AAAA,YAClB,eAAeoB,KAAiB,CAAC;AAAA,YACjC,YAAAC;AAAA,YACA,SAAAC;AAAA,YACA,eAAetB,EAAQ;AAAA,UAAA;AAAA,QAAA;AAAA,MACzB;AAAA,IAAA;AAAA,EAEJ;AAEJ,GC5Ha6D,IAAuB,CAClC7D,OAEO;AAAA,EACL,cAAc,CAAC,EAAE,QAAAiD,GAAQ,SAAA3B,EAAQ,MAC9BU,gBAAAA,EAAAA,IAAA8B,GAAA,EACC,UAAC9B,gBAAAA,EAAA,IAAAgB,GAAA,EAAe,QAAAC,GAAgB,SAAA3B,GAAkB,SAAAtB,EAAkB,CAAA,EACtE,CAAA;AAEJ;"}
1
+ {"version":3,"file":"zudoku.plugin-search-pagefind.js","sources":["../src/lib/plugins/search-pagefind/get-results.tsx","../src/lib/plugins/search-pagefind/ResultList.tsx","../src/lib/plugins/search-pagefind/PagefindSearch.tsx","../src/lib/plugins/search-pagefind/index.tsx"],"sourcesContent":["import type { AuthState } from \"../../authentication/state.js\";\nimport type { ZudokuContext } from \"../../core/ZudokuContext.js\";\nimport type { PagefindOptions } from \"./index.js\";\nimport type { PagefindSearchFragment, PagefindSearchResults } from \"./types.js\";\n\nexport const getResults = async ({\n search,\n options,\n auth,\n context,\n}: {\n search: PagefindSearchResults;\n options: PagefindOptions;\n auth: AuthState;\n context: ZudokuContext;\n}) => {\n const maxResults = options.maxResults ?? 10;\n const transformFn = options.transformResults ?? (() => true);\n\n const transformedResults: PagefindSearchFragment[] = [];\n\n const generator = searchResultGenerator({\n search,\n transformFn,\n auth,\n context,\n });\n\n for await (const result of generator) {\n transformedResults.push(result);\n if (transformedResults.length >= maxResults) break;\n }\n\n return transformedResults;\n};\n\nasync function* searchResultGenerator({\n search,\n transformFn,\n auth,\n context,\n}: {\n search: PagefindSearchResults;\n transformFn: NonNullable<PagefindOptions[\"transformResults\"]>;\n auth: AuthState<unknown>;\n context: ZudokuContext;\n}) {\n const batchSize = 5;\n let processedCount = 0;\n\n while (processedCount < search.results.length) {\n const batch = search.results.slice(\n processedCount,\n processedCount + batchSize,\n );\n processedCount += batch.length;\n\n const batchData = await Promise.all(batch.map((result) => result.data()));\n\n for (const result of batchData) {\n const transformed = transformFn({ result, auth, context });\n\n if (transformed === false) {\n // Skip this result\n continue;\n } else if (transformed === true || transformed == null) {\n // Keep the original result\n yield result;\n } else {\n // Return the transformed result\n yield transformed;\n }\n }\n }\n}\n","import { BracketsIcon, FileTextIcon } from \"lucide-react\";\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\nimport { CommandGroup, CommandItem, CommandList } from \"zudoku/ui/Command.js\";\nimport {\n type PagefindSearchFragment,\n type PagefindSubResult,\n} from \"./types.js\";\n\nconst sortSubResults = (a: PagefindSubResult, b: PagefindSubResult) => {\n const aScore = a.weighted_locations.reduce(\n (sum, loc) => sum + loc.balanced_score,\n 0,\n );\n const bScore = b.weighted_locations.reduce(\n (sum, loc) => sum + loc.balanced_score,\n 0,\n );\n return bScore - aScore;\n};\n\nconst hoverClassname = `cursor-pointer border border-transparent data-[selected=true]:border-border`;\n\nexport const ResultList = ({\n basePath,\n searchResults,\n searchTerm,\n onClose,\n maxSubResults = 4,\n}: {\n basePath?: string;\n searchResults: PagefindSearchFragment[];\n searchTerm: string;\n onClose: () => void;\n maxSubResults?: number;\n}) => {\n const navigate = useNavigate();\n const commandListRef = useRef<HTMLDivElement | null>(null);\n\n const cleanResultUrl = useCallback(\n (url: string) => {\n const clean = url.replace(\".html\", \"\");\n return basePath && clean.startsWith(basePath)\n ? clean.slice(basePath.length)\n : clean;\n },\n [basePath],\n );\n\n useLayoutEffect(() => {\n requestIdleCallback(() => {\n commandListRef.current?.scrollTo({ top: 0 });\n });\n }, [searchTerm]);\n\n return (\n <CommandList className=\"max-h-[450px]\" ref={commandListRef}>\n {searchTerm && searchResults.length > 0 && (\n <CommandGroup\n className=\"text-sm text-muted-foreground\"\n heading={`${searchResults.length} results for \"${searchTerm}\"`}\n />\n )}\n {searchTerm &&\n searchResults.map((result) => (\n <CommandGroup\n key={[result.meta.title ?? result.excerpt, result.url].join(\"-\")}\n >\n <CommandItem\n asChild\n value={`${result.meta.title}-${result.url}`}\n className={hoverClassname}\n onSelect={() => {\n void navigate(cleanResultUrl(result.url));\n onClose();\n }}\n >\n <Link to={cleanResultUrl(result.url)}>\n {result.meta.section === \"openapi\" ? (\n <BracketsIcon />\n ) : (\n <FileTextIcon />\n )}\n {result.meta.title}\n </Link>\n </CommandItem>\n {result.sub_results\n .sort(sortSubResults)\n .slice(0, maxSubResults)\n .map((subResult) => (\n <CommandItem\n asChild\n key={`sub-${result.meta.title}-${subResult.url}`}\n value={`sub-${result.meta.title}-${subResult.url}`}\n className={hoverClassname}\n onSelect={() => {\n void navigate(cleanResultUrl(subResult.url));\n onClose();\n }}\n >\n <Link to={cleanResultUrl(subResult.url)} onClick={onClose}>\n <div className=\"flex flex-col items-start gap-2 ms-2.5 ps-5 border-l border-muted-foreground/50\">\n <span className=\"font-bold\">{subResult.title}</span>\n <span\n className=\"text-[13px] [&_mark]:bg-primary [&_mark]:text-primary-foreground\"\n dangerouslySetInnerHTML={{ __html: subResult.excerpt }}\n />\n </div>\n </Link>\n </CommandItem>\n ))}\n </CommandGroup>\n ))}\n </CommandList>\n );\n};\n","import { VisuallyHidden } from \"@radix-ui/react-visually-hidden\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { useRef, useState } from \"react\";\nimport { Button } from \"zudoku/ui/Button.js\";\nimport { Callout } from \"zudoku/ui/Callout.js\";\nimport {\n CommandDialog,\n CommandEmpty,\n CommandInput,\n} from \"zudoku/ui/Command.js\";\nimport { DialogTitle } from \"zudoku/ui/Dialog.js\";\nimport { useAuthState } from \"../../authentication/state.js\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { getResults } from \"./get-results.js\";\nimport type { PagefindOptions } from \"./index.js\";\nimport { ResultList } from \"./ResultList.js\";\nimport type { Pagefind } from \"./types.js\";\n\nconst DEFAULT_RANKING = {\n // Slightly lower than default because API docs tend to have repetitive terms (parameter names, HTTP methods, etc.)\n termFrequency: 0.8,\n // Lower than default because API documentation pages tend to be longer due to comprehensive endpoint documentation\n pageLength: 0.6,\n // Slightly higher than default because in technical documentation, exact matches should be prioritized\n termSimilarity: 1.2,\n // Slightly lower than default because API docs might have legitimate repetition of terms\n termSaturation: 1.2,\n};\n\nconst importPagefind = (basePath?: string): Promise<Pagefind> =>\n import.meta.env.DEV\n ? // @ts-expect-error TypeScript can't resolve the import\n import(/* @vite-ignore */ \"/pagefind/pagefind.js\")\n : import(/* @vite-ignore */ joinUrl(basePath, \"/pagefind/pagefind.js\"));\n\nconst usePagefind = (options: PagefindOptions) => {\n const { data: pagefind, ...result } = useQuery<Pagefind>({\n queryKey: [\"pagefind\", options.ranking],\n retry: false,\n queryFn: async () => {\n const pagefind = await importPagefind(options.basePath);\n await pagefind.init();\n await pagefind.options({\n ranking: {\n termFrequency:\n options.ranking?.termFrequency ?? DEFAULT_RANKING.termFrequency,\n pageLength: options.ranking?.pageLength ?? DEFAULT_RANKING.pageLength,\n termSimilarity:\n options.ranking?.termSimilarity ?? DEFAULT_RANKING.termSimilarity,\n termSaturation:\n options.ranking?.termSaturation ?? DEFAULT_RANKING.termSaturation,\n },\n });\n\n return pagefind;\n },\n enabled: typeof window !== \"undefined\",\n });\n\n if (result.isError && result.error.message !== \"NOT_BUILT_YET\") {\n // eslint-disable-next-line no-console\n console.error(result.error);\n }\n\n return { ...result, pagefind };\n};\n\nexport const PagefindSearch = ({\n isOpen,\n onClose,\n options,\n}: {\n isOpen: boolean;\n onClose: () => void;\n options: PagefindOptions;\n}) => {\n const { pagefind, error, isError } = usePagefind(options);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const auth = useAuthState();\n const context = useZudoku();\n const inputRef = useRef<HTMLInputElement>(null);\n\n const { data: searchResults } = useQuery({\n queryKey: [\"pagefind-search\", searchTerm],\n queryFn: async () => {\n const search = await pagefind?.search(searchTerm);\n if (!search) return [];\n return getResults({ search, options, auth, context });\n },\n placeholderData: keepPreviousData,\n enabled: !!pagefind && !!searchTerm,\n });\n\n return (\n <CommandDialog\n command={{ shouldFilter: false }}\n content={{ className: \"max-w-[750px]\" }}\n open={isOpen}\n onOpenChange={onClose}\n >\n <VisuallyHidden>\n <DialogTitle>Search</DialogTitle>\n </VisuallyHidden>\n <CommandInput\n ref={inputRef}\n placeholder=\"Search...\"\n value={searchTerm}\n onValueChange={setSearchTerm}\n disabled={isError}\n />\n <CommandEmpty>\n {searchTerm ? (\n <div className=\"flex flex-col items-center\">\n No results found.\n <Button\n variant=\"link\"\n onClick={() => {\n setSearchTerm(\"\");\n inputRef.current?.focus();\n }}\n >\n Clear search\n </Button>\n </div>\n ) : (\n \"Start typing to search\"\n )}\n </CommandEmpty>\n {isError ? (\n <div className=\"p-4 text-sm\">\n {error.message === \"NOT_BUILT_YET\" ? (\n <Callout type=\"info\">\n Search is currently not available in development mode by default.\n <br />\n To still use search in development, run <code>\n zudoku build\n </code>{\" \"}\n and copy the <code>dist/pagefind</code> directory to your{\" \"}\n <code>public</code> directory.\n </Callout>\n ) : (\n \"An error occurred while loading search.\"\n )}\n </div>\n ) : (\n <ResultList\n basePath={options.basePath}\n searchResults={searchResults ?? []}\n searchTerm={searchTerm}\n onClose={onClose}\n maxSubResults={options.maxSubResults}\n />\n )}\n </CommandDialog>\n );\n};\n","import type { ZudokuConfig } from \"../../../config/validators/validate.js\";\nimport { ClientOnly } from \"../../components/ClientOnly.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { PagefindSearch } from \"./PagefindSearch.js\";\n\nexport type PagefindOptions = Extract<\n ZudokuConfig[\"search\"],\n { type: \"pagefind\" }\n> & { basePath?: string };\n\nexport const pagefindSearchPlugin = (\n options: PagefindOptions,\n): ZudokuPlugin => {\n return {\n renderSearch: ({ isOpen, onClose }) => (\n <ClientOnly>\n <PagefindSearch isOpen={isOpen} onClose={onClose} options={options} />\n </ClientOnly>\n ),\n };\n};\n"],"names":["getResults","search","options","auth","context","maxResults","transformFn","transformedResults","generator","searchResultGenerator","result","processedCount","batch","batchData","transformed","sortSubResults","a","b","aScore","sum","loc","hoverClassname","ResultList","basePath","searchResults","searchTerm","onClose","maxSubResults","navigate","useNavigate","commandListRef","useRef","cleanResultUrl","useCallback","url","clean","useLayoutEffect","_a","jsxs","CommandList","jsx","CommandGroup","CommandItem","Link","BracketsIcon","FileTextIcon","subResult","DEFAULT_RANKING","importPagefind","joinUrl","usePagefind","pagefind","useQuery","_b","_c","_d","PagefindSearch","isOpen","error","isError","setSearchTerm","useState","useAuthState","useZudoku","inputRef","keepPreviousData","CommandDialog","VisuallyHidden","DialogTitle","CommandInput","CommandEmpty","Button","Callout","pagefindSearchPlugin","ClientOnly"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugins.js","sources":["../src/lib/core/plugins.ts"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport { type ReactElement } from \"react\";\nimport { type RouteObject } from \"react-router\";\nimport type { Sidebar } from \"../../config/validators/SidebarSchema.js\";\nimport { type MdxComponentsType } from \"../util/MdxComponents.js\";\nimport {\n type ApiIdentity,\n type ZudokuContext,\n type ZudokuEvents,\n} from \"./ZudokuContext.js\";\n\nexport type ZudokuPlugin =\n | CommonPlugin\n | ProfileMenuPlugin\n | NavigationPlugin\n | ApiIdentityPlugin\n | SearchProviderPlugin\n | EventConsumerPlugin;\n\nexport type { RouteObject };\n\nexport interface NavigationPlugin {\n getRoutes: () => RouteObject[];\n getSidebar?: (path: string, context: ZudokuContext) => Promise<Sidebar>;\n}\n\nexport const createApiIdentityPlugin = (\n plugin: ApiIdentityPlugin,\n): ApiIdentityPlugin => plugin;\n\nexport const createProfileMenuPlugin = (\n plugin: ProfileMenuPlugin,\n): ProfileMenuPlugin => plugin;\n\nexport interface ApiIdentityPlugin {\n getIdentities: (context: ZudokuContext) => Promise<ApiIdentity[]>;\n}\n\nexport interface SearchProviderPlugin {\n renderSearch: (o: {\n isOpen: boolean;\n onClose: () => void;\n }) => React.JSX.Element | null;\n}\n\nexport interface ProfileMenuPlugin {\n getProfileMenuItems: (context: ZudokuContext) => ProfileNavigationItem[];\n}\n\nexport type ProfileNavigationItem = {\n label: string;\n path?: string;\n weight?: number;\n category?: \"top\" | \"middle\" | \"bottom\";\n children?: ProfileNavigationItem[];\n icon?: LucideIcon;\n};\n\nexport interface CommonPlugin {\n initialize?: (\n context: ZudokuContext,\n ) => Promise<void | boolean> | void | boolean;\n getHead?: () => ReactElement | undefined;\n getMdxComponents?: () => MdxComponentsType;\n}\n\nexport type EventConsumerPlugin<Event extends ZudokuEvents = ZudokuEvents> = {\n events: { [K in keyof Event]: Event[K] };\n};\n\nexport const isEventConsumerPlugin = (\n obj: ZudokuPlugin,\n): obj is EventConsumerPlugin =>\n \"events\" in obj && typeof obj.events === \"object\";\n\nexport const isProfileMenuPlugin = (\n obj: ZudokuPlugin,\n): obj is ProfileMenuPlugin =>\n \"getProfileMenuItems\" in obj && typeof obj.getProfileMenuItems === \"function\";\n\nexport const isNavigationPlugin = (\n obj: ZudokuPlugin,\n): obj is NavigationPlugin =>\n \"getRoutes\" in obj && typeof obj.getRoutes === \"function\";\n\nexport const isSearchPlugin = (\n obj: ZudokuPlugin,\n): obj is SearchProviderPlugin =>\n \"renderSearch\" in obj && typeof obj.renderSearch === \"function\";\n\nexport const needsInitialization = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"initialize\" in obj && typeof obj.initialize === \"function\";\n\nexport const hasHead = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getHead\" in obj && typeof obj.getHead === \"function\";\n\nexport const isMdxProviderPlugin = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getMdxComponents\" in obj && typeof obj.getMdxComponents === \"function\";\n\nexport const isApiIdentityPlugin = (\n obj: ZudokuPlugin,\n): obj is ApiIdentityPlugin =>\n \"getIdentities\" in obj && typeof obj.getIdentities === \"function\";\n"],"names":["createApiIdentityPlugin","plugin","createProfileMenuPlugin","isEventConsumerPlugin","obj","isProfileMenuPlugin","isNavigationPlugin","isSearchPlugin","needsInitialization","hasHead","isMdxProviderPlugin","isApiIdentityPlugin"],"mappings":"AA0Ba,MAAAA,IAA0B,CACrCC,MACsBA,GAEXC,IAA0B,CACrCD,MACsBA,GAsCXE,IAAwB,CACnCC,MAEA,YAAYA,KAAO,OAAOA,EAAI,UAAW,UAE9BC,IAAsB,CACjCD,MAEA,yBAAyBA,KAAO,OAAOA,EAAI,uBAAwB,YAExDE,IAAqB,CAChCF,MAEA,eAAeA,KAAO,OAAOA,EAAI,aAAc,YAEpCG,IAAiB,CAC5BH,MAEA,kBAAkBA,KAAO,OAAOA,EAAI,gBAAiB,YAE1CI,IAAsB,CAACJ,MAClC,gBAAgBA,KAAO,OAAOA,EAAI,cAAe,YAEtCK,IAAU,CAACL,MACtB,aAAaA,KAAO,OAAOA,EAAI,WAAY,YAEhCM,IAAsB,CAACN,MAClC,sBAAsBA,KAAO,OAAOA,EAAI,oBAAqB,YAElDO,IAAsB,CACjCP,MAEA,mBAAmBA,KAAO,OAAOA,EAAI,iBAAkB;"}
1
+ {"version":3,"file":"zudoku.plugins.js","sources":["../src/lib/core/plugins.ts"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport type { ReactElement } from \"react\";\nimport type { Location, RouteObject } from \"react-router\";\nimport type { Sidebar } from \"../../config/validators/SidebarSchema.js\";\nimport type { MdxComponentsType } from \"../util/MdxComponents.js\";\nimport type {\n ApiIdentity,\n ZudokuContext,\n ZudokuEvents,\n} from \"./ZudokuContext.js\";\n\nexport type ZudokuPlugin =\n | CommonPlugin\n | ProfileMenuPlugin\n | NavigationPlugin\n | ApiIdentityPlugin\n | SearchProviderPlugin\n | EventConsumerPlugin;\n\nexport type { RouteObject };\n\nexport interface NavigationPlugin {\n getRoutes: () => RouteObject[];\n getSidebar?: (path: string, context: ZudokuContext) => Promise<Sidebar>;\n}\n\nexport const createApiIdentityPlugin = (\n plugin: ApiIdentityPlugin,\n): ApiIdentityPlugin => plugin;\n\nexport const createProfileMenuPlugin = (\n plugin: ProfileMenuPlugin,\n): ProfileMenuPlugin => plugin;\n\nexport interface ApiIdentityPlugin {\n getIdentities: (context: ZudokuContext) => Promise<ApiIdentity[]>;\n}\n\nexport interface SearchProviderPlugin {\n renderSearch: (o: {\n isOpen: boolean;\n onClose: () => void;\n }) => React.JSX.Element | null;\n}\n\nexport interface ProfileMenuPlugin {\n getProfileMenuItems: (context: ZudokuContext) => ProfileNavigationItem[];\n}\n\nexport type ProfileNavigationItem = {\n label: string;\n path?: string;\n weight?: number;\n category?: \"top\" | \"middle\" | \"bottom\";\n children?: ProfileNavigationItem[];\n icon?: LucideIcon;\n};\n\nexport interface CommonPlugin {\n initialize?: (\n context: ZudokuContext,\n ) => Promise<void | boolean> | void | boolean;\n getHead?: ({ location }: { location: Location }) => ReactElement | undefined;\n getMdxComponents?: () => MdxComponentsType;\n}\n\nexport type EventConsumerPlugin<Event extends ZudokuEvents = ZudokuEvents> = {\n events: { [K in keyof Event]?: Event[K] };\n};\n\nexport const isEventConsumerPlugin = (\n obj: ZudokuPlugin,\n): obj is EventConsumerPlugin =>\n \"events\" in obj && typeof obj.events === \"object\";\n\nexport const isProfileMenuPlugin = (\n obj: ZudokuPlugin,\n): obj is ProfileMenuPlugin =>\n \"getProfileMenuItems\" in obj && typeof obj.getProfileMenuItems === \"function\";\n\nexport const isNavigationPlugin = (\n obj: ZudokuPlugin,\n): obj is NavigationPlugin =>\n \"getRoutes\" in obj && typeof obj.getRoutes === \"function\";\n\nexport const isSearchPlugin = (\n obj: ZudokuPlugin,\n): obj is SearchProviderPlugin =>\n \"renderSearch\" in obj && typeof obj.renderSearch === \"function\";\n\nexport const needsInitialization = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"initialize\" in obj && typeof obj.initialize === \"function\";\n\nexport const hasHead = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getHead\" in obj && typeof obj.getHead === \"function\";\n\nexport const isMdxProviderPlugin = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getMdxComponents\" in obj && typeof obj.getMdxComponents === \"function\";\n\nexport const isApiIdentityPlugin = (\n obj: ZudokuPlugin,\n): obj is ApiIdentityPlugin =>\n \"getIdentities\" in obj && typeof obj.getIdentities === \"function\";\n"],"names":["createApiIdentityPlugin","plugin","createProfileMenuPlugin","isEventConsumerPlugin","obj","isProfileMenuPlugin","isNavigationPlugin","isSearchPlugin","needsInitialization","hasHead","isMdxProviderPlugin","isApiIdentityPlugin"],"mappings":"AA0Ba,MAAAA,IAA0B,CACrCC,MACsBA,GAEXC,IAA0B,CACrCD,MACsBA,GAsCXE,IAAwB,CACnCC,MAEA,YAAYA,KAAO,OAAOA,EAAI,UAAW,UAE9BC,IAAsB,CACjCD,MAEA,yBAAyBA,KAAO,OAAOA,EAAI,uBAAwB,YAExDE,IAAqB,CAChCF,MAEA,eAAeA,KAAO,OAAOA,EAAI,aAAc,YAEpCG,IAAiB,CAC5BH,MAEA,kBAAkBA,KAAO,OAAOA,EAAI,gBAAiB,YAE1CI,IAAsB,CAACJ,MAClC,gBAAgBA,KAAO,OAAOA,EAAI,cAAe,YAEtCK,IAAU,CAACL,MACtB,aAAaA,KAAO,OAAOA,EAAI,WAAY,YAEhCM,IAAsB,CAACN,MAClC,sBAAsBA,KAAO,OAAOA,EAAI,oBAAqB,YAElDO,IAAsB,CACjCP,MAEA,mBAAmBA,KAAO,OAAOA,EAAI,iBAAkB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -226,7 +226,7 @@
226
226
  "strip-ansi": "7.1.0",
227
227
  "tailwind-merge": "2.6.0",
228
228
  "tailwindcss": "3.4.17",
229
- "tsx": "^4.19.2",
229
+ "tsx": "^4.19.3",
230
230
  "ulidx": "2.4.1",
231
231
  "unified": "^11.0.5",
232
232
  "unist-util-visit": "5.0.0",
package/src/app/main.tsx CHANGED
@@ -38,6 +38,8 @@ export const convertZudokuConfigToOptions = (
38
38
  !config.page?.logo?.src?.dark;
39
39
 
40
40
  return {
41
+ basePath: config.basePath,
42
+ canonicalUrlOrigin: config.canonicalUrlOrigin,
41
43
  protectedRoutes: config.protectedRoutes,
42
44
  page: {
43
45
  ...config.page,
@@ -4,9 +4,11 @@ export interface AuthenticationProvider {
4
4
  signUp(options?: { redirectTo?: string }): Promise<void>;
5
5
  signIn(options?: { redirectTo?: string }): Promise<void>;
6
6
  signOut(): Promise<void>;
7
+ // @deprecated use signRequest instead
7
8
  getAccessToken(): Promise<string>;
8
9
  getAuthenticationPlugin?(): ZudokuPlugin;
9
10
  onPageLoad?(): void;
11
+ signRequest(request: Request): Promise<Request>;
10
12
  }
11
13
 
12
14
  export interface AuthenticationProviderInitializer<TConfig> {
@@ -31,6 +31,9 @@ class ClerkAuthPlugin extends AuthenticationPlugin {
31
31
  emailVerified: verifiedEmail !== undefined,
32
32
  pictureUrl: clerk.session.user.imageUrl,
33
33
  },
34
+ providerData: {
35
+ user: clerk.session.user,
36
+ },
34
37
  });
35
38
  } else {
36
39
  useAuthState.setState({
@@ -74,6 +77,15 @@ const clerkAuth: AuthenticationProviderInitializer<
74
77
  emailVerified: verifiedEmail !== undefined,
75
78
  pictureUrl: clerkApi.user.imageUrl,
76
79
  },
80
+ providerData: {
81
+ user: {
82
+ publicMetadata: clerkApi.user.publicMetadata,
83
+ id: clerkApi.user.id,
84
+ emailAddresses: clerkApi.user.emailAddresses,
85
+ imageUrl: clerkApi.user.imageUrl,
86
+ fullName: clerkApi.user.fullName,
87
+ },
88
+ },
77
89
  });
78
90
  }
79
91
 
@@ -92,8 +104,16 @@ const clerkAuth: AuthenticationProviderInitializer<
92
104
  return response;
93
105
  }
94
106
 
107
+ async function signRequest(request: Request): Promise<Request> {
108
+ const response = await getAccessToken();
109
+ request.headers.set("Authorization", `Bearer ${response}`);
110
+ return request;
111
+ }
112
+
95
113
  return {
114
+ clerk: clerkApi,
96
115
  getAccessToken,
116
+ signRequest,
97
117
  signOut: async () => {
98
118
  await ensureLoaded;
99
119
  await clerkApi?.signOut({
@@ -255,6 +255,12 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
255
255
  }
256
256
  }
257
257
 
258
+ signRequest = async (request: Request): Promise<Request> => {
259
+ const accessToken = await this.getAccessToken();
260
+ request.headers.set("Authorization", `Bearer ${accessToken}`);
261
+ return request;
262
+ };
263
+
258
264
  signOut = async () => {
259
265
  useAuthState.setState({
260
266
  isAuthenticated: false,
@@ -88,6 +88,12 @@ class SupabaseAuthenticationProvider implements AuthenticationProvider {
88
88
  return data.session.access_token;
89
89
  }
90
90
 
91
+ async signRequest(request: Request): Promise<Request> {
92
+ const accessToken = await this.getAccessToken();
93
+ request.headers.set("Authorization", `Bearer ${accessToken}`);
94
+ return request;
95
+ }
96
+
91
97
  signUp = async ({ redirectTo }: { redirectTo?: string }) => {
92
98
  const finalRedirectTo = redirectTo ?? this.redirectToAfterSignUp;
93
99
 
@@ -1,4 +1,3 @@
1
- import { useMemo } from "react";
2
1
  import { create, type Mutate, type StoreApi } from "zustand";
3
2
  import { createJSONStorage, persist } from "zustand/middleware";
4
3
 
@@ -20,7 +19,7 @@ export type StoreWithPersist<T> = Mutate<
20
19
  [["zustand/persist", unknown]]
21
20
  >;
22
21
 
23
- export const withStorageDOMEvents = <T>(store: StoreWithPersist<T>) => {
22
+ const withStorageDOMEvents = <T>(store: StoreWithPersist<T>) => {
24
23
  const storageEventCallback = (e: StorageEvent) => {
25
24
  if (e.key === store.persist.getOptions().name && e.newValue) {
26
25
  void store.persist.rehydrate();
@@ -68,36 +67,3 @@ export interface UserProfile {
68
67
  pictureUrl: string | undefined;
69
68
  [key: string]: string | boolean | undefined;
70
69
  }
71
-
72
- interface SelectedServerState {
73
- selectedServer?: string;
74
- setSelectedServer: (newServer: string) => void;
75
- }
76
-
77
- export const useSelectedServerStore = create<SelectedServerState>()(
78
- persist(
79
- (set) => ({
80
- selectedServer: undefined,
81
- setSelectedServer: (newServer: string) =>
82
- set({ selectedServer: newServer }),
83
- }),
84
- { name: "zudoku-selected-server" },
85
- ),
86
- );
87
-
88
- /**
89
- * Simple wrapper for `useSelectedServerStore` to fall back to first of the provided servers
90
- */
91
- export const useSelectedServer = (servers: Array<{ url: string }>) => {
92
- const { selectedServer, setSelectedServer } = useSelectedServerStore();
93
-
94
- const finalSelectedServer = useMemo(
95
- () =>
96
- selectedServer && servers.some((s) => s.url === selectedServer)
97
- ? selectedServer
98
- : (servers.at(0)?.url ?? ""),
99
- [selectedServer, servers],
100
- );
101
-
102
- return { selectedServer: finalSelectedServer, setSelectedServer };
103
- };