rari 0.5.9 → 0.5.10
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/runtime/actions.mjs
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
|
+
//#region src/runtime/csrf.ts
|
|
2
|
+
function getCsrfToken() {
|
|
3
|
+
if (typeof window === "undefined") return null;
|
|
4
|
+
const meta = document.querySelector("meta[name=\"csrf-token\"]");
|
|
5
|
+
return meta ? meta.getAttribute("content") : null;
|
|
6
|
+
}
|
|
7
|
+
async function refreshCsrfToken() {
|
|
8
|
+
if (typeof window === "undefined") return false;
|
|
9
|
+
try {
|
|
10
|
+
const response = await fetch("/api/rsc/csrf-token");
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
console.warn("Failed to refresh CSRF token:", response.status);
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const data = await response.json();
|
|
16
|
+
if (data.token) {
|
|
17
|
+
let meta = document.querySelector("meta[name=\"csrf-token\"]");
|
|
18
|
+
if (!meta) {
|
|
19
|
+
meta = document.createElement("meta");
|
|
20
|
+
meta.setAttribute("name", "csrf-token");
|
|
21
|
+
document.head.appendChild(meta);
|
|
22
|
+
}
|
|
23
|
+
meta.setAttribute("content", data.token);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("Error refreshing CSRF token:", error);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function fetchWithCsrf(url, options = {}) {
|
|
33
|
+
let token = getCsrfToken();
|
|
34
|
+
if (!token) {
|
|
35
|
+
await refreshCsrfToken();
|
|
36
|
+
token = getCsrfToken();
|
|
37
|
+
}
|
|
38
|
+
const headers = new Headers(options.headers);
|
|
39
|
+
if (token) headers.set("X-CSRF-Token", token);
|
|
40
|
+
const response = await fetch(url, {
|
|
41
|
+
...options,
|
|
42
|
+
headers
|
|
43
|
+
});
|
|
44
|
+
if (response.status === 403 && url.includes("/api/rsc/")) {
|
|
45
|
+
if (await refreshCsrfToken()) {
|
|
46
|
+
const retryToken = getCsrfToken();
|
|
47
|
+
if (retryToken) {
|
|
48
|
+
headers.set("X-CSRF-Token", retryToken);
|
|
49
|
+
return fetch(url, {
|
|
50
|
+
...options,
|
|
51
|
+
headers
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
58
|
+
if (typeof window !== "undefined") {
|
|
59
|
+
window.getCsrfToken = getCsrfToken;
|
|
60
|
+
window.fetchWithCsrf = fetchWithCsrf;
|
|
61
|
+
window.refreshCsrfToken = refreshCsrfToken;
|
|
62
|
+
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", () => {
|
|
63
|
+
refreshCsrfToken();
|
|
64
|
+
});
|
|
65
|
+
else refreshCsrfToken();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
1
69
|
//#region src/runtime/actions.ts
|
|
2
70
|
function createServerReference(functionName, moduleId, exportName) {
|
|
3
71
|
return async (...args) => {
|
|
@@ -12,7 +80,7 @@ function createServerReference(functionName, moduleId, exportName) {
|
|
|
12
80
|
}
|
|
13
81
|
return arg;
|
|
14
82
|
});
|
|
15
|
-
const response = await fetch("/api/rsc/action", {
|
|
83
|
+
const response = await (typeof window !== "undefined" && window.fetchWithCsrf ? window.fetchWithCsrf : fetch)("/api/rsc/action", {
|
|
16
84
|
method: "POST",
|
|
17
85
|
headers: { "Content-Type": "application/json" },
|
|
18
86
|
body: JSON.stringify({
|
|
@@ -86,6 +154,19 @@ function createFormAction(moduleId, exportName, action) {
|
|
|
86
154
|
exportNameInput.name = "__export_name";
|
|
87
155
|
exportNameInput.value = exportName;
|
|
88
156
|
form.appendChild(exportNameInput);
|
|
157
|
+
if (typeof window !== "undefined" && window.getCsrfToken) {
|
|
158
|
+
const csrfToken = window.getCsrfToken();
|
|
159
|
+
if (csrfToken) {
|
|
160
|
+
let csrfInput = form.querySelector("input[name=\"__csrf_token\"]");
|
|
161
|
+
if (!csrfInput) {
|
|
162
|
+
csrfInput = document.createElement("input");
|
|
163
|
+
csrfInput.type = "hidden";
|
|
164
|
+
csrfInput.name = "__csrf_token";
|
|
165
|
+
form.appendChild(csrfInput);
|
|
166
|
+
}
|
|
167
|
+
csrfInput.value = csrfToken;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
89
170
|
form.action = "/api/rsc/form-action";
|
|
90
171
|
form.method = "POST";
|
|
91
172
|
return enhanceFormWithAction(form, action, options);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rari",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.10",
|
|
5
5
|
"description": "Runtime Accelerated Rendering Infrastructure (Rari)",
|
|
6
6
|
"author": "Ryan Skinner",
|
|
7
7
|
"license": "MIT",
|
|
@@ -89,19 +89,19 @@
|
|
|
89
89
|
"picocolors": "^1.1.1"
|
|
90
90
|
},
|
|
91
91
|
"optionalDependencies": {
|
|
92
|
-
"rari-darwin-arm64": "0.5.
|
|
93
|
-
"rari-darwin-x64": "0.5.
|
|
94
|
-
"rari-linux-arm64": "0.5.
|
|
95
|
-
"rari-linux-x64": "0.5.
|
|
96
|
-
"rari-win32-x64": "0.5.
|
|
92
|
+
"rari-darwin-arm64": "0.5.6",
|
|
93
|
+
"rari-darwin-x64": "0.5.6",
|
|
94
|
+
"rari-linux-arm64": "0.5.6",
|
|
95
|
+
"rari-linux-x64": "0.5.6",
|
|
96
|
+
"rari-win32-x64": "0.5.6"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
|
-
"@types/node": "^24.10.
|
|
99
|
+
"@types/node": "^24.10.2",
|
|
100
100
|
"@types/react": "^19.2.7",
|
|
101
101
|
"@typescript/native-preview": "7.0.0-dev.20251203.1",
|
|
102
102
|
"chokidar": "^4.0.3",
|
|
103
103
|
"eslint": "^9.39.1",
|
|
104
|
-
"oxlint": "^1.
|
|
104
|
+
"oxlint": "^1.32.0",
|
|
105
105
|
"rolldown-vite": "^7.2.10",
|
|
106
106
|
"tsdown": "^0.15.12"
|
|
107
107
|
}
|
package/src/runtime/actions.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import './csrf'
|
|
2
|
+
|
|
1
3
|
export interface ServerActionResponse {
|
|
2
4
|
success: boolean
|
|
3
5
|
result?: any
|
|
@@ -29,7 +31,11 @@ export function createServerReference(
|
|
|
29
31
|
return arg
|
|
30
32
|
})
|
|
31
33
|
|
|
32
|
-
const
|
|
34
|
+
const fetchFn = typeof window !== 'undefined' && (window as any).fetchWithCsrf
|
|
35
|
+
? (window as any).fetchWithCsrf
|
|
36
|
+
: fetch
|
|
37
|
+
|
|
38
|
+
const response = await fetchFn('/api/rsc/action', {
|
|
33
39
|
method: 'POST',
|
|
34
40
|
headers: {
|
|
35
41
|
'Content-Type': 'application/json',
|
|
@@ -150,6 +156,20 @@ export function createFormAction(
|
|
|
150
156
|
exportNameInput.value = exportName
|
|
151
157
|
form.appendChild(exportNameInput)
|
|
152
158
|
|
|
159
|
+
if (typeof window !== 'undefined' && (window as any).getCsrfToken) {
|
|
160
|
+
const csrfToken = (window as any).getCsrfToken()
|
|
161
|
+
if (csrfToken) {
|
|
162
|
+
let csrfInput = form.querySelector('input[name="__csrf_token"]') as HTMLInputElement
|
|
163
|
+
if (!csrfInput) {
|
|
164
|
+
csrfInput = document.createElement('input')
|
|
165
|
+
csrfInput.type = 'hidden'
|
|
166
|
+
csrfInput.name = '__csrf_token'
|
|
167
|
+
form.appendChild(csrfInput)
|
|
168
|
+
}
|
|
169
|
+
csrfInput.value = csrfToken
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
153
173
|
form.action = '/api/rsc/form-action'
|
|
154
174
|
form.method = 'POST'
|
|
155
175
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export function getCsrfToken(): string | null {
|
|
2
|
+
if (typeof window === 'undefined')
|
|
3
|
+
return null
|
|
4
|
+
const meta = document.querySelector('meta[name="csrf-token"]')
|
|
5
|
+
return meta ? meta.getAttribute('content') : null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function refreshCsrfToken(): Promise<boolean> {
|
|
9
|
+
if (typeof window === 'undefined')
|
|
10
|
+
return false
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch('/api/rsc/csrf-token')
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
console.warn('Failed to refresh CSRF token:', response.status)
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
const data = await response.json()
|
|
19
|
+
if (data.token) {
|
|
20
|
+
let meta = document.querySelector('meta[name="csrf-token"]')
|
|
21
|
+
if (!meta) {
|
|
22
|
+
meta = document.createElement('meta')
|
|
23
|
+
meta.setAttribute('name', 'csrf-token')
|
|
24
|
+
document.head.appendChild(meta)
|
|
25
|
+
}
|
|
26
|
+
meta.setAttribute('content', data.token)
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('Error refreshing CSRF token:', error)
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function fetchWithCsrf(
|
|
38
|
+
url: string,
|
|
39
|
+
options: RequestInit = {},
|
|
40
|
+
): Promise<Response> {
|
|
41
|
+
let token = getCsrfToken()
|
|
42
|
+
|
|
43
|
+
if (!token) {
|
|
44
|
+
await refreshCsrfToken()
|
|
45
|
+
token = getCsrfToken()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const headers = new Headers(options.headers)
|
|
49
|
+
if (token) {
|
|
50
|
+
headers.set('X-CSRF-Token', token)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
...options,
|
|
55
|
+
headers,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
if (response.status === 403 && url.includes('/api/rsc/')) {
|
|
59
|
+
const refreshed = await refreshCsrfToken()
|
|
60
|
+
if (refreshed) {
|
|
61
|
+
const retryToken = getCsrfToken()
|
|
62
|
+
if (retryToken) {
|
|
63
|
+
headers.set('X-CSRF-Token', retryToken)
|
|
64
|
+
return fetch(url, {
|
|
65
|
+
...options,
|
|
66
|
+
headers,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof window !== 'undefined') {
|
|
76
|
+
;(window as any).getCsrfToken = getCsrfToken
|
|
77
|
+
;(window as any).fetchWithCsrf = fetchWithCsrf
|
|
78
|
+
;(window as any).refreshCsrfToken = refreshCsrfToken
|
|
79
|
+
|
|
80
|
+
if (document.readyState === 'loading') {
|
|
81
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
82
|
+
refreshCsrfToken()
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
refreshCsrfToken()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -885,7 +885,7 @@ function ServerComponentWrapper({
|
|
|
885
885
|
let mounted = true
|
|
886
886
|
|
|
887
887
|
if (prevPropsKeyRef.current !== propsKey) {
|
|
888
|
-
// eslint-disable-next-line react-hooks
|
|
888
|
+
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect
|
|
889
889
|
setState({ data: null, loading: true, error: null })
|
|
890
890
|
prevPropsKeyRef.current = propsKey
|
|
891
891
|
}
|