@tanstack/start-static-server-functions 1.132.0-alpha.1 → 1.132.0-alpha.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 +1 @@
|
|
|
1
|
-
export declare const staticFunctionMiddleware: import('@tanstack/start-client-core').FunctionMiddlewareAfterServer<unknown, undefined, undefined, undefined, undefined, undefined
|
|
1
|
+
export declare const staticFunctionMiddleware: import('@tanstack/start-client-core').FunctionMiddlewareAfterServer<import('@tanstack/router-core').Register, unknown, undefined, undefined, undefined, undefined, undefined>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createMiddleware, getDefaultSerovalPlugins } from "@tanstack/start-client-core";
|
|
4
|
+
import { fromJSON, toJSONAsync } from "seroval";
|
|
2
5
|
async function sha1Hash(message) {
|
|
3
6
|
const msgBuffer = new TextEncoder().encode(message);
|
|
4
7
|
const hashBuffer = await crypto.subtle.digest("SHA-1", msgBuffer);
|
|
@@ -19,41 +22,29 @@ const jsonToFilenameSafeString = (json) => {
|
|
|
19
22
|
return jsonString.replace(/[/\\?%*:|"<>]/g, "-").replace(/\s+/g, "_");
|
|
20
23
|
};
|
|
21
24
|
const staticClientCache = typeof document !== "undefined" ? /* @__PURE__ */ new Map() : null;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const { promises: fs } = await import("node:fs");
|
|
29
|
-
const path = await import("node:path");
|
|
30
|
-
const filePath = path.join(publicUrl, url);
|
|
31
|
-
const [cachedResult, readError] = await fs.readFile(filePath, "utf-8").then((c) => [startSerializer.parse(c), null]).catch((e) => [null, e]);
|
|
32
|
-
if (readError && readError.code !== "ENOENT") {
|
|
33
|
-
throw readError;
|
|
34
|
-
}
|
|
35
|
-
return cachedResult;
|
|
36
|
-
}
|
|
37
|
-
return void 0;
|
|
38
|
-
},
|
|
39
|
-
setItem: async ({ data, functionId, response }) => {
|
|
40
|
-
const { promises: fs } = await import("node:fs");
|
|
41
|
-
const path = await import("node:path");
|
|
25
|
+
async function addItemToCache({
|
|
26
|
+
functionId,
|
|
27
|
+
data,
|
|
28
|
+
response
|
|
29
|
+
}) {
|
|
30
|
+
{
|
|
42
31
|
const hash = jsonToFilenameSafeString(data);
|
|
43
32
|
const url = await getStaticCacheUrl({ functionId, hash });
|
|
44
|
-
const
|
|
45
|
-
const filePath = path.join(
|
|
33
|
+
const clientUrl = process.env.TSS_CLIENT_OUTPUT_DIR;
|
|
34
|
+
const filePath = path.join(clientUrl, url);
|
|
46
35
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
const stringifiedResult = JSON.stringify(
|
|
37
|
+
await toJSONAsync(
|
|
38
|
+
{
|
|
39
|
+
result: response.result,
|
|
40
|
+
context: response.context.sendContext
|
|
41
|
+
},
|
|
42
|
+
{ plugins: getDefaultSerovalPlugins() }
|
|
43
|
+
)
|
|
54
44
|
);
|
|
45
|
+
await fs.writeFile(filePath, stringifiedResult, "utf-8");
|
|
55
46
|
}
|
|
56
|
-
}
|
|
47
|
+
}
|
|
57
48
|
const fetchItem = async ({
|
|
58
49
|
data,
|
|
59
50
|
functionId
|
|
@@ -63,7 +54,7 @@ const fetchItem = async ({
|
|
|
63
54
|
let result = staticClientCache?.get(url);
|
|
64
55
|
result = await fetch(url, {
|
|
65
56
|
method: "GET"
|
|
66
|
-
}).then((r) => r.
|
|
57
|
+
}).then((r) => r.json()).then((d) => fromJSON(d, { plugins: getDefaultSerovalPlugins() }));
|
|
67
58
|
return result;
|
|
68
59
|
};
|
|
69
60
|
const staticFunctionMiddleware = createMiddleware({ type: "function" }).client(async (ctx) => {
|
|
@@ -84,7 +75,7 @@ const staticFunctionMiddleware = createMiddleware({ type: "function" }).client(a
|
|
|
84
75
|
}).server(async (ctx) => {
|
|
85
76
|
const response = await ctx.next();
|
|
86
77
|
if (process.env.NODE_ENV === "production") {
|
|
87
|
-
await
|
|
78
|
+
await addItemToCache({
|
|
88
79
|
functionId: ctx.functionId,
|
|
89
80
|
response: { result: response.result, context: ctx },
|
|
90
81
|
data: ctx.data
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staticFunctionMiddleware.js","sources":["../../src/staticFunctionMiddleware.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"staticFunctionMiddleware.js","sources":["../../src/staticFunctionMiddleware.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport {\n createMiddleware,\n getDefaultSerovalPlugins,\n} from '@tanstack/start-client-core'\nimport { fromJSON, toJSONAsync } from 'seroval'\n\ntype StaticCachedResult = {\n result: any\n context: any\n}\n\n/**\n * This is a simple hash function for generating a hash from a string to make the filenames shorter.\n *\n * It is not cryptographically secure (as its using SHA-1) and should not be used for any security purposes.\n *\n * It is only used to generate a hash for the static cache filenames.\n *\n * @param message - The input string to hash.\n * @returns A promise that resolves to the SHA-1 hash of the input string in hexadecimal format.\n *\n * @example\n * ```typescript\n * const hash = await sha1Hash(\"hello\");\n * console.log(hash); // Outputs the SHA-1 hash of \"hello\" -> \"aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d\"\n * ```\n */\nasync function sha1Hash(message: string): Promise<string> {\n // Encode the string as UTF-8\n const msgBuffer = new TextEncoder().encode(message)\n\n // Hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-1', msgBuffer)\n\n // Convert the ArrayBuffer to a string\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n return hashHex\n}\n\nconst getStaticCacheUrl = async (opts: {\n functionId: string\n hash: string\n}) => {\n const filename = await sha1Hash(`${opts.functionId}__${opts.hash}`)\n return `/__tsr/staticServerFnCache/${filename}.json`\n}\n\nconst jsonToFilenameSafeString = (json: any) => {\n // Custom replacer to sort keys\n const sortedKeysReplacer = (key: string, value: any) =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? Object.keys(value)\n .sort()\n .reduce((acc: any, curr: string) => {\n acc[curr] = value[curr]\n return acc\n }, {})\n : value\n\n // Convert JSON to string with sorted keys\n const jsonString = JSON.stringify(json ?? '', sortedKeysReplacer)\n\n // Replace characters invalid in filenames\n return jsonString\n .replace(/[/\\\\?%*:|\"<>]/g, '-') // Replace invalid characters with a dash\n .replace(/\\s+/g, '_') // Optionally replace whitespace with underscores\n}\n\nconst staticClientCache =\n typeof document !== 'undefined' ? new Map<string, any>() : null\n\nasync function addItemToCache({\n functionId,\n data,\n response,\n}: {\n functionId: string\n data: any\n response: StaticCachedResult\n}): Promise<void> {\n {\n const hash = jsonToFilenameSafeString(data)\n const url = await getStaticCacheUrl({ functionId, hash })\n const clientUrl = process.env.TSS_CLIENT_OUTPUT_DIR!\n const filePath = path.join(clientUrl, url)\n\n // Ensure the directory exists\n await fs.mkdir(path.dirname(filePath), { recursive: true })\n\n // Store the result with fs\n const stringifiedResult = JSON.stringify(\n await toJSONAsync(\n {\n result: response.result,\n context: response.context.sendContext,\n },\n { plugins: getDefaultSerovalPlugins() },\n ),\n )\n await fs.writeFile(filePath, stringifiedResult, 'utf-8')\n }\n}\n\nconst fetchItem = async ({\n data,\n functionId,\n}: {\n data: any\n functionId: string\n}) => {\n const hash = jsonToFilenameSafeString(data)\n const url = await getStaticCacheUrl({ functionId, hash })\n\n let result: any = staticClientCache?.get(url)\n\n result = await fetch(url, {\n method: 'GET',\n })\n .then((r) => r.json())\n .then((d) => fromJSON(d, { plugins: getDefaultSerovalPlugins() }))\n\n return result\n}\n\nexport const staticFunctionMiddleware = createMiddleware({ type: 'function' })\n .client(async (ctx) => {\n if (\n process.env.NODE_ENV === 'production' &&\n // do not run this during SSR on the server\n typeof document !== 'undefined'\n ) {\n const response = await fetchItem({\n functionId: ctx.functionId,\n data: ctx.data,\n })\n\n if (response) {\n return {\n result: response.result,\n context: { ...(ctx as any).context, ...response.context },\n } as any\n }\n }\n return ctx.next()\n })\n .server(async (ctx) => {\n const response = await ctx.next()\n if (process.env.NODE_ENV === 'production') {\n await addItemToCache({\n functionId: ctx.functionId,\n response: { result: (response as any).result, context: ctx },\n data: ctx.data,\n })\n }\n\n return response\n })\n"],"names":[],"mappings":";;;;AA6BA,eAAe,SAAS,SAAkC;AAExD,QAAM,YAAY,IAAI,cAAc,OAAO,OAAO;AAGlD,QAAM,aAAa,MAAM,OAAO,OAAO,OAAO,SAAS,SAAS;AAGhE,QAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,QAAM,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC7E,SAAO;AACT;AAEA,MAAM,oBAAoB,OAAO,SAG3B;AACJ,QAAM,WAAW,MAAM,SAAS,GAAG,KAAK,UAAU,KAAK,KAAK,IAAI,EAAE;AAClE,SAAO,8BAA8B,QAAQ;AAC/C;AAEA,MAAM,2BAA2B,CAAC,SAAc;AAE9C,QAAM,qBAAqB,CAAC,KAAa,UACvC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACtD,OAAO,KAAK,KAAK,EACd,OACA,OAAO,CAAC,KAAU,SAAiB;AAClC,QAAI,IAAI,IAAI,MAAM,IAAI;AACtB,WAAO;AAAA,EACT,GAAG,CAAA,CAAE,IACP;AAGN,QAAM,aAAa,KAAK,UAAU,QAAQ,IAAI,kBAAkB;AAGhE,SAAO,WACJ,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,QAAQ,GAAG;AACxB;AAEA,MAAM,oBACJ,OAAO,aAAa,cAAc,oBAAI,QAAqB;AAE7D,eAAe,eAAe;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIkB;AAChB;AACE,UAAM,OAAO,yBAAyB,IAAI;AAC1C,UAAM,MAAM,MAAM,kBAAkB,EAAE,YAAY,MAAM;AACxD,UAAM,YAAY,QAAQ,IAAI;AAC9B,UAAM,WAAW,KAAK,KAAK,WAAW,GAAG;AAGzC,UAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM;AAG1D,UAAM,oBAAoB,KAAK;AAAA,MAC7B,MAAM;AAAA,QACJ;AAAA,UACE,QAAQ,SAAS;AAAA,UACjB,SAAS,SAAS,QAAQ;AAAA,QAAA;AAAA,QAE5B,EAAE,SAAS,yBAAA,EAAyB;AAAA,MAAE;AAAA,IACxC;AAEF,UAAM,GAAG,UAAU,UAAU,mBAAmB,OAAO;AAAA,EACzD;AACF;AAEA,MAAM,YAAY,OAAO;AAAA,EACvB;AAAA,EACA;AACF,MAGM;AACJ,QAAM,OAAO,yBAAyB,IAAI;AAC1C,QAAM,MAAM,MAAM,kBAAkB,EAAE,YAAY,MAAM;AAExD,MAAI,SAAc,mBAAmB,IAAI,GAAG;AAE5C,WAAS,MAAM,MAAM,KAAK;AAAA,IACxB,QAAQ;AAAA,EAAA,CACT,EACE,KAAK,CAAC,MAAM,EAAE,MAAM,EACpB,KAAK,CAAC,MAAM,SAAS,GAAG,EAAE,SAAS,yBAAA,EAAyB,CAAG,CAAC;AAEnE,SAAO;AACT;AAEO,MAAM,2BAA2B,iBAAiB,EAAE,MAAM,YAAY,EAC1E,OAAO,OAAO,QAAQ;AACrB,MACE,QAAQ,IAAI,aAAa;AAAA,EAEzB,OAAO,aAAa,aACpB;AACA,UAAM,WAAW,MAAM,UAAU;AAAA,MAC/B,YAAY,IAAI;AAAA,MAChB,MAAM,IAAI;AAAA,IAAA,CACX;AAED,QAAI,UAAU;AACZ,aAAO;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB,SAAS,EAAE,GAAI,IAAY,SAAS,GAAG,SAAS,QAAA;AAAA,MAAQ;AAAA,IAE5D;AAAA,EACF;AACA,SAAO,IAAI,KAAA;AACb,CAAC,EACA,OAAO,OAAO,QAAQ;AACrB,QAAM,WAAW,MAAM,IAAI,KAAA;AAC3B,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,UAAM,eAAe;AAAA,MACnB,YAAY,IAAI;AAAA,MAChB,UAAU,EAAE,QAAS,SAAiB,QAAQ,SAAS,IAAA;AAAA,MACvD,MAAM,IAAI;AAAA,IAAA,CACX;AAAA,EACH;AAEA,SAAO;AACT,CAAC;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-static-server-functions",
|
|
3
|
-
"version": "1.132.0-alpha.
|
|
3
|
+
"version": "1.132.0-alpha.10",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,10 +40,11 @@
|
|
|
40
40
|
"src"
|
|
41
41
|
],
|
|
42
42
|
"engines": {
|
|
43
|
-
"node": ">=12"
|
|
43
|
+
"node": ">=22.12.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"
|
|
46
|
+
"seroval": "^1.3.2",
|
|
47
|
+
"@tanstack/start-client-core": "1.132.0-alpha.10"
|
|
47
48
|
},
|
|
48
49
|
"scripts": {}
|
|
49
50
|
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import {
|
|
4
|
+
createMiddleware,
|
|
5
|
+
getDefaultSerovalPlugins,
|
|
6
|
+
} from '@tanstack/start-client-core'
|
|
7
|
+
import { fromJSON, toJSONAsync } from 'seroval'
|
|
2
8
|
|
|
3
9
|
type StaticCachedResult = {
|
|
4
10
|
result: any
|
|
5
11
|
context: any
|
|
6
12
|
}
|
|
7
13
|
|
|
8
|
-
type ServerFnStaticCache = {
|
|
9
|
-
getItem: (opts: {
|
|
10
|
-
functionId: string
|
|
11
|
-
data: any
|
|
12
|
-
}) => StaticCachedResult | Promise<StaticCachedResult | undefined>
|
|
13
|
-
setItem: (opts: {
|
|
14
|
-
functionId: string
|
|
15
|
-
data: any
|
|
16
|
-
response: StaticCachedResult
|
|
17
|
-
}) => Promise<void>
|
|
18
|
-
}
|
|
19
|
-
|
|
20
14
|
/**
|
|
21
15
|
* This is a simple hash function for generating a hash from a string to make the filenames shorter.
|
|
22
16
|
*
|
|
@@ -78,54 +72,36 @@ const jsonToFilenameSafeString = (json: any) => {
|
|
|
78
72
|
const staticClientCache =
|
|
79
73
|
typeof document !== 'undefined' ? new Map<string, any>() : null
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const filePath = path.join(publicUrl, url)
|
|
92
|
-
|
|
93
|
-
const [cachedResult, readError] = await fs
|
|
94
|
-
.readFile(filePath, 'utf-8')
|
|
95
|
-
.then((c) => [startSerializer.parse(c), null])
|
|
96
|
-
.catch((e) => [null, e])
|
|
97
|
-
|
|
98
|
-
if (readError && readError.code !== 'ENOENT') {
|
|
99
|
-
throw readError
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return cachedResult as StaticCachedResult
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return undefined
|
|
106
|
-
},
|
|
107
|
-
setItem: async ({ data, functionId, response }) => {
|
|
108
|
-
const { promises: fs } = await import('node:fs')
|
|
109
|
-
const path = await import('node:path')
|
|
110
|
-
|
|
75
|
+
async function addItemToCache({
|
|
76
|
+
functionId,
|
|
77
|
+
data,
|
|
78
|
+
response,
|
|
79
|
+
}: {
|
|
80
|
+
functionId: string
|
|
81
|
+
data: any
|
|
82
|
+
response: StaticCachedResult
|
|
83
|
+
}): Promise<void> {
|
|
84
|
+
{
|
|
111
85
|
const hash = jsonToFilenameSafeString(data)
|
|
112
86
|
const url = await getStaticCacheUrl({ functionId, hash })
|
|
113
|
-
const
|
|
114
|
-
const filePath = path.join(
|
|
87
|
+
const clientUrl = process.env.TSS_CLIENT_OUTPUT_DIR!
|
|
88
|
+
const filePath = path.join(clientUrl, url)
|
|
115
89
|
|
|
116
90
|
// Ensure the directory exists
|
|
117
91
|
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
118
92
|
|
|
119
93
|
// Store the result with fs
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
const stringifiedResult = JSON.stringify(
|
|
95
|
+
await toJSONAsync(
|
|
96
|
+
{
|
|
97
|
+
result: response.result,
|
|
98
|
+
context: response.context.sendContext,
|
|
99
|
+
},
|
|
100
|
+
{ plugins: getDefaultSerovalPlugins() },
|
|
101
|
+
),
|
|
127
102
|
)
|
|
128
|
-
|
|
103
|
+
await fs.writeFile(filePath, stringifiedResult, 'utf-8')
|
|
104
|
+
}
|
|
129
105
|
}
|
|
130
106
|
|
|
131
107
|
const fetchItem = async ({
|
|
@@ -143,8 +119,8 @@ const fetchItem = async ({
|
|
|
143
119
|
result = await fetch(url, {
|
|
144
120
|
method: 'GET',
|
|
145
121
|
})
|
|
146
|
-
.then((r) => r.
|
|
147
|
-
.then((d) =>
|
|
122
|
+
.then((r) => r.json())
|
|
123
|
+
.then((d) => fromJSON(d, { plugins: getDefaultSerovalPlugins() }))
|
|
148
124
|
|
|
149
125
|
return result
|
|
150
126
|
}
|
|
@@ -172,9 +148,8 @@ export const staticFunctionMiddleware = createMiddleware({ type: 'function' })
|
|
|
172
148
|
})
|
|
173
149
|
.server(async (ctx) => {
|
|
174
150
|
const response = await ctx.next()
|
|
175
|
-
|
|
176
151
|
if (process.env.NODE_ENV === 'production') {
|
|
177
|
-
await
|
|
152
|
+
await addItemToCache({
|
|
178
153
|
functionId: ctx.functionId,
|
|
179
154
|
response: { result: (response as any).result, context: ctx },
|
|
180
155
|
data: ctx.data,
|