camox 0.31.0 → 0.31.2
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.
|
@@ -21,41 +21,7 @@ declare function createPageLoader(apiUrl: string, projectSlug: string, environme
|
|
|
21
21
|
queryClient: QueryClient;
|
|
22
22
|
};
|
|
23
23
|
}) => Promise<{
|
|
24
|
-
page:
|
|
25
|
-
page: {
|
|
26
|
-
status: undefined;
|
|
27
|
-
modifiedReason: undefined | null;
|
|
28
|
-
blockIds: number[];
|
|
29
|
-
id: number;
|
|
30
|
-
createdAt: number;
|
|
31
|
-
updatedAt: number;
|
|
32
|
-
projectId: number;
|
|
33
|
-
environmentId: number;
|
|
34
|
-
pathSegment: string;
|
|
35
|
-
fullPath: string;
|
|
36
|
-
parentPageId: number | null;
|
|
37
|
-
layoutId: number;
|
|
38
|
-
livePublishedCheckpointId: number | null;
|
|
39
|
-
contentUpdatedAt: number;
|
|
40
|
-
nickname: string;
|
|
41
|
-
metaTitle: string | null;
|
|
42
|
-
metaDescription: string | null;
|
|
43
|
-
aiSeoEnabled: boolean | null;
|
|
44
|
-
customOgImageBlobId: string | null;
|
|
45
|
-
customOgImageUrl: string | null;
|
|
46
|
-
};
|
|
47
|
-
layout: {
|
|
48
|
-
id: number;
|
|
49
|
-
layoutId: string;
|
|
50
|
-
beforeBlockIds: number[];
|
|
51
|
-
afterBlockIds: number[];
|
|
52
|
-
} | null;
|
|
53
|
-
projectName: string;
|
|
54
|
-
project: {
|
|
55
|
-
id: number;
|
|
56
|
-
updatedAt: number;
|
|
57
|
-
};
|
|
58
|
-
};
|
|
24
|
+
page: PageStructure;
|
|
59
25
|
origin: string;
|
|
60
26
|
faviconUrl: string;
|
|
61
27
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAuthCookieHeader, getServerAuthCookieHeader } from "../../lib/auth.js";
|
|
1
|
+
import { buildClearServerAuthCookieHeader, getAuthCookieHeader, getServerAuthCookieHeader } from "../../lib/auth.js";
|
|
2
2
|
import { seedBlockCaches } from "../../lib/normalized-data.js";
|
|
3
3
|
import { CamoxPreview, PageContent } from "../preview/CamoxPreview.js";
|
|
4
4
|
import { trackEvent } from "../../lib/telemetry.js";
|
|
@@ -9,7 +9,7 @@ import { queryKeys } from "@camox/api-contract/query-keys";
|
|
|
9
9
|
import { createORPCClient } from "@orpc/client";
|
|
10
10
|
import { RPCLink } from "@orpc/client/fetch";
|
|
11
11
|
import { createMiddleware, createServerFn } from "@tanstack/react-start";
|
|
12
|
-
import { getRequest } from "@tanstack/react-start/server";
|
|
12
|
+
import { getRequest, setResponseHeader } from "@tanstack/react-start/server";
|
|
13
13
|
|
|
14
14
|
//#region src/features/routes/pageRoute.tsx
|
|
15
15
|
function parseQuality(part) {
|
|
@@ -38,6 +38,88 @@ function createServerApiClient(apiUrl, environmentName, options) {
|
|
|
38
38
|
}
|
|
39
39
|
}));
|
|
40
40
|
}
|
|
41
|
+
function getErrorDetails(error) {
|
|
42
|
+
if (!error || typeof error !== "object") return { message: String(error) };
|
|
43
|
+
const errorRecord = error;
|
|
44
|
+
return {
|
|
45
|
+
code: typeof errorRecord.code === "string" ? errorRecord.code : void 0,
|
|
46
|
+
status: typeof errorRecord.status === "number" ? errorRecord.status : void 0,
|
|
47
|
+
message: error instanceof Error ? error.message : typeof errorRecord.message === "string" ? errorRecord.message : ""
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function isAuthSessionError(error) {
|
|
51
|
+
const { code, status, message } = getErrorDetails(error);
|
|
52
|
+
if (code === "UNAUTHORIZED" || code === "FORBIDDEN") return true;
|
|
53
|
+
if (status === 401 || status === 403) return true;
|
|
54
|
+
const lowerMessage = message.toLowerCase();
|
|
55
|
+
return lowerMessage.includes("unauthorized") || lowerMessage.includes("forbidden") || lowerMessage.includes("invalid session") || lowerMessage.includes("session expired") || lowerMessage.includes("expired session");
|
|
56
|
+
}
|
|
57
|
+
function isNotFoundError(error) {
|
|
58
|
+
const { code, status } = getErrorDetails(error);
|
|
59
|
+
return code === "NOT_FOUND" || status === 404;
|
|
60
|
+
}
|
|
61
|
+
function clearServerAuthCookie() {
|
|
62
|
+
if (typeof window !== "undefined") return;
|
|
63
|
+
setResponseHeader("Set-Cookie", buildClearServerAuthCookieHeader());
|
|
64
|
+
}
|
|
65
|
+
async function loadPage({ apiUrl, authCookieHeader, context, environmentName, pathname, projectSlug, source }) {
|
|
66
|
+
const serverApi = createServerApiClient(apiUrl, environmentName, { authCookieHeader });
|
|
67
|
+
return context.queryClient.ensureQueryData({
|
|
68
|
+
queryKey: queryKeys.pages.getByPath(pathname, source),
|
|
69
|
+
queryFn: async () => {
|
|
70
|
+
const [data, pagesList] = await Promise.all([serverApi.pages.getByPath({
|
|
71
|
+
path: pathname,
|
|
72
|
+
projectSlug,
|
|
73
|
+
source
|
|
74
|
+
}), serverApi.pages.listBySlug({ projectSlug }).catch(() => null)]);
|
|
75
|
+
seedBlockCaches(context.queryClient, data, source);
|
|
76
|
+
context.queryClient.setQueryData(queryKeys.pages.getByPath(pathname, source), {
|
|
77
|
+
page: data.page,
|
|
78
|
+
layout: data.layout,
|
|
79
|
+
projectName: data.projectName,
|
|
80
|
+
project: data.project
|
|
81
|
+
});
|
|
82
|
+
if (source === "live") {
|
|
83
|
+
seedBlockCaches(context.queryClient, data, "draft");
|
|
84
|
+
context.queryClient.setQueryData(queryKeys.pages.getByPath(pathname, "draft"), {
|
|
85
|
+
page: data.page,
|
|
86
|
+
layout: data.layout,
|
|
87
|
+
projectName: data.projectName,
|
|
88
|
+
project: data.project
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (pagesList) context.queryClient.setQueryData(queryKeys.pages.list, pagesList);
|
|
92
|
+
return {
|
|
93
|
+
page: data.page,
|
|
94
|
+
layout: data.layout,
|
|
95
|
+
projectName: data.projectName,
|
|
96
|
+
project: data.project
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
staleTime: Infinity
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function throwNotFoundOrRethrow(error) {
|
|
103
|
+
if (isNotFoundError(error)) throw notFound();
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
async function loadLivePage(options) {
|
|
107
|
+
try {
|
|
108
|
+
return await loadPage({
|
|
109
|
+
...options,
|
|
110
|
+
source: "live"
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throwNotFoundOrRethrow(error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function buildLoaderData(apiUrl, page) {
|
|
117
|
+
return {
|
|
118
|
+
page,
|
|
119
|
+
origin: await getOrigin(),
|
|
120
|
+
faviconUrl: `${apiUrl}/favicons/${page.project.id}?v=${page.project.updatedAt}`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
41
123
|
const getOrigin = createServerFn({ method: "GET" }).handler(async () => {
|
|
42
124
|
const request = getRequest();
|
|
43
125
|
return new URL(request.url).origin;
|
|
@@ -74,51 +156,26 @@ function createMarkdownMiddleware(apiUrl, projectSlug, environmentName) {
|
|
|
74
156
|
}
|
|
75
157
|
function createPageLoader(apiUrl, projectSlug, environmentName) {
|
|
76
158
|
return async ({ location, context }) => {
|
|
159
|
+
const authCookieHeader = typeof window !== "undefined" ? getAuthCookieHeader() : await getServerLoaderAuthCookieHeader();
|
|
160
|
+
const loadOptions = {
|
|
161
|
+
apiUrl,
|
|
162
|
+
context,
|
|
163
|
+
environmentName,
|
|
164
|
+
pathname: location.pathname,
|
|
165
|
+
projectSlug
|
|
166
|
+
};
|
|
167
|
+
if (!authCookieHeader) return buildLoaderData(apiUrl, await loadLivePage(loadOptions));
|
|
77
168
|
try {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}), serverApi.pages.listBySlug({ projectSlug }).catch(() => null)]);
|
|
89
|
-
seedBlockCaches(context.queryClient, data, source);
|
|
90
|
-
context.queryClient.setQueryData(queryKeys.pages.getByPath(location.pathname, source), {
|
|
91
|
-
page: data.page,
|
|
92
|
-
layout: data.layout,
|
|
93
|
-
projectName: data.projectName,
|
|
94
|
-
project: data.project
|
|
95
|
-
});
|
|
96
|
-
if (source === "live") {
|
|
97
|
-
seedBlockCaches(context.queryClient, data, "draft");
|
|
98
|
-
context.queryClient.setQueryData(queryKeys.pages.getByPath(location.pathname, "draft"), {
|
|
99
|
-
page: data.page,
|
|
100
|
-
layout: data.layout,
|
|
101
|
-
projectName: data.projectName,
|
|
102
|
-
project: data.project
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
if (pagesList) context.queryClient.setQueryData(queryKeys.pages.list, pagesList);
|
|
106
|
-
return {
|
|
107
|
-
page: data.page,
|
|
108
|
-
layout: data.layout,
|
|
109
|
-
projectName: data.projectName,
|
|
110
|
-
project: data.project
|
|
111
|
-
};
|
|
112
|
-
},
|
|
113
|
-
staleTime: Infinity
|
|
114
|
-
}), getOrigin()]);
|
|
115
|
-
return {
|
|
116
|
-
page,
|
|
117
|
-
origin,
|
|
118
|
-
faviconUrl: `${apiUrl}/favicons/${page.project.id}?v=${page.project.updatedAt}`
|
|
119
|
-
};
|
|
120
|
-
} catch {
|
|
121
|
-
throw notFound();
|
|
169
|
+
return buildLoaderData(apiUrl, await loadPage({
|
|
170
|
+
...loadOptions,
|
|
171
|
+
authCookieHeader,
|
|
172
|
+
source: "draft"
|
|
173
|
+
}));
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (!isAuthSessionError(error)) throwNotFoundOrRethrow(error);
|
|
176
|
+
clearServerAuthCookie();
|
|
177
|
+
console.warn("[camox] Ignoring stale camox_auth_cookie and retrying published page load.");
|
|
178
|
+
return buildLoaderData(apiUrl, await loadLivePage(loadOptions));
|
|
122
179
|
}
|
|
123
180
|
};
|
|
124
181
|
}
|
|
@@ -179,9 +236,9 @@ function createPageHead(camoxApp) {
|
|
|
179
236
|
}
|
|
180
237
|
const PageRouteComponent = () => {
|
|
181
238
|
const $ = c(2);
|
|
182
|
-
if ($[0] !== "
|
|
239
|
+
if ($[0] !== "267af96f07026fe91743b4ae5c2344f8ee96641a8dad8dc84ff7bf6a551f191e") {
|
|
183
240
|
for (let $i = 0; $i < 2; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
184
|
-
$[0] = "
|
|
241
|
+
$[0] = "267af96f07026fe91743b4ae5c2344f8ee96641a8dad8dc84ff7bf6a551f191e";
|
|
185
242
|
}
|
|
186
243
|
let t0;
|
|
187
244
|
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
|
@@ -168,6 +168,9 @@ function camox(options) {
|
|
|
168
168
|
"camox > lexical",
|
|
169
169
|
"camox > lucide-react",
|
|
170
170
|
"camox > posthog-js",
|
|
171
|
+
"camox > streamdown",
|
|
172
|
+
"camox > streamdown > hast-util-to-jsx-runtime",
|
|
173
|
+
"camox > streamdown > hast-util-to-jsx-runtime > style-to-js",
|
|
171
174
|
"camox > @tanstack/react-query-devtools/production",
|
|
172
175
|
"camox > @tanstack/react-router > @tanstack/router-core",
|
|
173
176
|
"camox > @tanstack/react-router > @tanstack/router-core/isServer",
|
package/dist/lib/auth.js
CHANGED
|
@@ -6,11 +6,14 @@ import { createAuthClient } from "better-auth/react";
|
|
|
6
6
|
|
|
7
7
|
//#region src/lib/auth.ts
|
|
8
8
|
const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
|
|
9
|
+
function buildClearServerAuthCookieHeader() {
|
|
10
|
+
return `${SERVER_AUTH_COOKIE_NAME}=; Path=/; SameSite=Lax; Max-Age=0`;
|
|
11
|
+
}
|
|
9
12
|
function writeServerAuthCookie(cookie) {
|
|
10
13
|
if (typeof document === "undefined") return;
|
|
11
14
|
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
12
15
|
if (!cookie) {
|
|
13
|
-
document.cookie = `${
|
|
16
|
+
document.cookie = `${buildClearServerAuthCookieHeader()}${secure}`;
|
|
14
17
|
return;
|
|
15
18
|
}
|
|
16
19
|
document.cookie = `${SERVER_AUTH_COOKIE_NAME}=${encodeURIComponent(cookie)}; Path=/; SameSite=Lax; Max-Age=2592000${secure}`;
|
|
@@ -192,9 +195,9 @@ function createCamoxAuthClient(apiUrl) {
|
|
|
192
195
|
*/
|
|
193
196
|
function useProcessOtt(authClient) {
|
|
194
197
|
const $ = c(5);
|
|
195
|
-
if ($[0] !== "
|
|
198
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
196
199
|
for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
197
|
-
$[0] = "
|
|
200
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
198
201
|
}
|
|
199
202
|
const [ready, setReady] = React.useState(_temp);
|
|
200
203
|
let t0;
|
|
@@ -237,9 +240,9 @@ function _temp() {
|
|
|
237
240
|
const AuthContext = React.createContext(null);
|
|
238
241
|
function useAuthContext() {
|
|
239
242
|
const $ = c(1);
|
|
240
|
-
if ($[0] !== "
|
|
243
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
241
244
|
for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
242
|
-
$[0] = "
|
|
245
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
243
246
|
}
|
|
244
247
|
const ctx = React.useContext(AuthContext);
|
|
245
248
|
if (!ctx) throw new Error("Missing CamoxProvider");
|
|
@@ -247,9 +250,9 @@ function useAuthContext() {
|
|
|
247
250
|
}
|
|
248
251
|
function useProjectSlug() {
|
|
249
252
|
const $ = c(1);
|
|
250
|
-
if ($[0] !== "
|
|
253
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
251
254
|
for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
252
|
-
$[0] = "
|
|
255
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
253
256
|
}
|
|
254
257
|
return useAuthContext().projectSlug;
|
|
255
258
|
}
|
|
@@ -263,18 +266,18 @@ function useAuthState() {
|
|
|
263
266
|
}
|
|
264
267
|
function useIsAuthenticated() {
|
|
265
268
|
const $ = c(1);
|
|
266
|
-
if ($[0] !== "
|
|
269
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
267
270
|
for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
268
|
-
$[0] = "
|
|
271
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
269
272
|
}
|
|
270
273
|
const { isAuthenticated } = useAuthState();
|
|
271
274
|
return isAuthenticated;
|
|
272
275
|
}
|
|
273
276
|
function useSignInRedirect() {
|
|
274
277
|
const $ = c(3);
|
|
275
|
-
if ($[0] !== "
|
|
278
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
276
279
|
for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
277
|
-
$[0] = "
|
|
280
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
278
281
|
}
|
|
279
282
|
const { authenticationUrl } = useAuthContext();
|
|
280
283
|
let t0;
|
|
@@ -295,9 +298,9 @@ function useSignInRedirect() {
|
|
|
295
298
|
*/
|
|
296
299
|
function useAuthActions() {
|
|
297
300
|
const $ = c(5);
|
|
298
|
-
if ($[0] !== "
|
|
301
|
+
if ($[0] !== "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032") {
|
|
299
302
|
for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
300
|
-
$[0] = "
|
|
303
|
+
$[0] = "61ec5b8d3f12e56931574a7cb47e1e3b938bb68c5135cf058423162e804c4032";
|
|
301
304
|
}
|
|
302
305
|
const { authClient, authenticationUrl } = useAuthContext();
|
|
303
306
|
let t0;
|
|
@@ -355,4 +358,4 @@ function _temp2() {
|
|
|
355
358
|
}
|
|
356
359
|
|
|
357
360
|
//#endregion
|
|
358
|
-
export { AuthContext, createCamoxAuthClient, getAuthCookieHeader, getServerAuthCookieHeader, useAuthActions, useAuthContext, useAuthState, useIsAuthenticated, useProcessOtt, useProjectSlug, useSignInRedirect };
|
|
361
|
+
export { AuthContext, buildClearServerAuthCookieHeader, createCamoxAuthClient, getAuthCookieHeader, getServerAuthCookieHeader, useAuthActions, useAuthContext, useAuthState, useIsAuthenticated, useProcessOtt, useProjectSlug, useSignInRedirect };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "camox",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.2",
|
|
4
4
|
"bin": {
|
|
5
5
|
"camox": "./bin/camox.mjs"
|
|
6
6
|
},
|
|
@@ -128,9 +128,9 @@
|
|
|
128
128
|
"shiki": "^4.1.0",
|
|
129
129
|
"streamdown": "^2.5.0",
|
|
130
130
|
"zod": "^4.4.3",
|
|
131
|
-
"@camox/api-contract": "0.31.
|
|
132
|
-
"@camox/ui": "0.31.
|
|
133
|
-
"@camox/cli": "0.31.
|
|
131
|
+
"@camox/api-contract": "0.31.2",
|
|
132
|
+
"@camox/ui": "0.31.2",
|
|
133
|
+
"@camox/cli": "0.31.2"
|
|
134
134
|
},
|
|
135
135
|
"devDependencies": {
|
|
136
136
|
"@babel/core": "^7.29.0",
|