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.
@@ -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.9",
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.5",
93
- "rari-darwin-x64": "0.5.5",
94
- "rari-linux-arm64": "0.5.5",
95
- "rari-linux-x64": "0.5.5",
96
- "rari-win32-x64": "0.5.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.1",
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.31.0",
104
+ "oxlint": "^1.32.0",
105
105
  "rolldown-vite": "^7.2.10",
106
106
  "tsdown": "^0.15.12"
107
107
  }
@@ -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 response = await fetch('/api/rsc/action', {
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/set-state-in-effect, react-hooks-extra/no-direct-set-state-in-use-effect
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
  }