@tanstack/start-static-server-functions 1.121.0-alpha.28

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ export { staticFunctionMiddleware } from './staticFunctionMiddleware.js';
@@ -0,0 +1,5 @@
1
+ import { staticFunctionMiddleware } from "./staticFunctionMiddleware.js";
2
+ export {
3
+ staticFunctionMiddleware
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1 @@
1
+ export declare const staticFunctionMiddleware: import('@tanstack/start-client-core').FunctionMiddlewareAfterServer<unknown, undefined, undefined, undefined, undefined, undefined, import('@tanstack/start-client-core').ServerFnResponseType>;
@@ -0,0 +1,98 @@
1
+ import { createMiddleware, startSerializer } from "@tanstack/start-client-core";
2
+ async function sha1Hash(message) {
3
+ const msgBuffer = new TextEncoder().encode(message);
4
+ const hashBuffer = await crypto.subtle.digest("SHA-1", msgBuffer);
5
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
6
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
7
+ return hashHex;
8
+ }
9
+ const getStaticCacheUrl = async (opts) => {
10
+ const filename = await sha1Hash(`${opts.functionId}__${opts.hash}`);
11
+ return `/__tsr/staticServerFnCache/${filename}.json`;
12
+ };
13
+ const jsonToFilenameSafeString = (json) => {
14
+ const sortedKeysReplacer = (key, value) => value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).sort().reduce((acc, curr) => {
15
+ acc[curr] = value[curr];
16
+ return acc;
17
+ }, {}) : value;
18
+ const jsonString = JSON.stringify(json ?? "", sortedKeysReplacer);
19
+ return jsonString.replace(/[/\\?%*:|"<>]/g, "-").replace(/\s+/g, "_");
20
+ };
21
+ const staticClientCache = typeof document !== "undefined" ? /* @__PURE__ */ new Map() : null;
22
+ const serverFnStaticCache = {
23
+ getItem: async (ctx) => {
24
+ if (typeof document === "undefined") {
25
+ const hash = jsonToFilenameSafeString(ctx.data);
26
+ const url = await getStaticCacheUrl({ functionId: ctx.functionId, hash });
27
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR;
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");
42
+ const hash = jsonToFilenameSafeString(data);
43
+ const url = await getStaticCacheUrl({ functionId, hash });
44
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR;
45
+ const filePath = path.join(publicUrl, url);
46
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
47
+ await fs.writeFile(
48
+ filePath,
49
+ startSerializer.stringify({
50
+ result: response.result,
51
+ context: response.context.sendContext
52
+ }),
53
+ "utf-8"
54
+ );
55
+ }
56
+ };
57
+ const fetchItem = async ({
58
+ data,
59
+ functionId
60
+ }) => {
61
+ const hash = jsonToFilenameSafeString(data);
62
+ const url = await getStaticCacheUrl({ functionId, hash });
63
+ let result = staticClientCache?.get(url);
64
+ result = await fetch(url, {
65
+ method: "GET"
66
+ }).then((r) => r.text()).then((d) => startSerializer.parse(d));
67
+ return result;
68
+ };
69
+ const staticFunctionMiddleware = createMiddleware({ type: "function" }).client(async (ctx) => {
70
+ if (process.env.NODE_ENV === "production" && // do not run this during SSR on the server
71
+ typeof document !== "undefined") {
72
+ const response = await fetchItem({
73
+ functionId: ctx.functionId,
74
+ data: ctx.data
75
+ });
76
+ if (response) {
77
+ return {
78
+ result: response.result,
79
+ context: { ...ctx.context, ...response.context }
80
+ };
81
+ }
82
+ }
83
+ return ctx.next();
84
+ }).server(async (ctx) => {
85
+ const response = await ctx.next();
86
+ if (process.env.NODE_ENV === "production") {
87
+ await serverFnStaticCache.setItem({
88
+ functionId: ctx.functionId,
89
+ response: { result: response.result, context: ctx },
90
+ data: ctx.data
91
+ });
92
+ }
93
+ return response;
94
+ });
95
+ export {
96
+ staticFunctionMiddleware
97
+ };
98
+ //# sourceMappingURL=staticFunctionMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticFunctionMiddleware.js","sources":["../../src/staticFunctionMiddleware.ts"],"sourcesContent":["import { createMiddleware, startSerializer } from '@tanstack/start-client-core'\n\ntype StaticCachedResult = {\n result: any\n context: any\n}\n\ntype ServerFnStaticCache = {\n getItem: (opts: {\n functionId: string\n data: any\n }) => StaticCachedResult | Promise<StaticCachedResult | undefined>\n setItem: (opts: {\n functionId: string\n data: any\n response: StaticCachedResult\n }) => Promise<void>\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\nconst serverFnStaticCache: ServerFnStaticCache = {\n getItem: async (ctx) => {\n if (typeof document === 'undefined') {\n const hash = jsonToFilenameSafeString(ctx.data)\n const url = await getStaticCacheUrl({ functionId: ctx.functionId, hash })\n const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!\n\n // Use fs instead of fetch to read from filesystem\n const { promises: fs } = await import('node:fs')\n const path = await import('node:path')\n const filePath = path.join(publicUrl, url)\n\n const [cachedResult, readError] = await fs\n .readFile(filePath, 'utf-8')\n .then((c) => [startSerializer.parse(c), null])\n .catch((e) => [null, e])\n\n if (readError && readError.code !== 'ENOENT') {\n throw readError\n }\n\n return cachedResult as StaticCachedResult\n }\n\n return undefined\n },\n setItem: async ({ data, functionId, response }) => {\n const { promises: fs } = await import('node:fs')\n const path = await import('node:path')\n\n const hash = jsonToFilenameSafeString(data)\n const url = await getStaticCacheUrl({ functionId, hash })\n const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!\n const filePath = path.join(publicUrl, url)\n\n // Ensure the directory exists\n await fs.mkdir(path.dirname(filePath), { recursive: true })\n\n // Store the result with fs\n await fs.writeFile(\n filePath,\n startSerializer.stringify({\n result: response.result,\n context: response.context.sendContext,\n }),\n 'utf-8',\n )\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.text())\n .then((d) => startSerializer.parse(d))\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\n if (process.env.NODE_ENV === 'production') {\n await serverFnStaticCache.setItem({\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":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@tanstack/start-static-server-functions",
3
+ "version": "1.121.0-alpha.28",
4
+ "description": "Modern and scalable routing for React applications",
5
+ "author": "Tanner Linsley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/TanStack/router.git",
10
+ "directory": "packages/start-static-server-functions"
11
+ },
12
+ "homepage": "https://tanstack.com/start",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "location",
20
+ "router",
21
+ "routing",
22
+ "async",
23
+ "async router",
24
+ "typescript"
25
+ ],
26
+ "type": "module",
27
+ "types": "dist/esm/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/esm/index.d.ts",
32
+ "default": "./dist/esm/index.js"
33
+ }
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "sideEffects": false,
38
+ "files": [
39
+ "dist",
40
+ "src"
41
+ ],
42
+ "engines": {
43
+ "node": ">=12"
44
+ },
45
+ "dependencies": {
46
+ "@tanstack/start-client-core": "1.121.0-alpha.28"
47
+ },
48
+ "scripts": {}
49
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { staticFunctionMiddleware } from './staticFunctionMiddleware'
@@ -0,0 +1,185 @@
1
+ import { createMiddleware, startSerializer } from '@tanstack/start-client-core'
2
+
3
+ type StaticCachedResult = {
4
+ result: any
5
+ context: any
6
+ }
7
+
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
+ /**
21
+ * This is a simple hash function for generating a hash from a string to make the filenames shorter.
22
+ *
23
+ * It is not cryptographically secure (as its using SHA-1) and should not be used for any security purposes.
24
+ *
25
+ * It is only used to generate a hash for the static cache filenames.
26
+ *
27
+ * @param message - The input string to hash.
28
+ * @returns A promise that resolves to the SHA-1 hash of the input string in hexadecimal format.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const hash = await sha1Hash("hello");
33
+ * console.log(hash); // Outputs the SHA-1 hash of "hello" -> "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
34
+ * ```
35
+ */
36
+ async function sha1Hash(message: string): Promise<string> {
37
+ // Encode the string as UTF-8
38
+ const msgBuffer = new TextEncoder().encode(message)
39
+
40
+ // Hash the message
41
+ const hashBuffer = await crypto.subtle.digest('SHA-1', msgBuffer)
42
+
43
+ // Convert the ArrayBuffer to a string
44
+ const hashArray = Array.from(new Uint8Array(hashBuffer))
45
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
46
+ return hashHex
47
+ }
48
+
49
+ const getStaticCacheUrl = async (opts: {
50
+ functionId: string
51
+ hash: string
52
+ }) => {
53
+ const filename = await sha1Hash(`${opts.functionId}__${opts.hash}`)
54
+ return `/__tsr/staticServerFnCache/${filename}.json`
55
+ }
56
+
57
+ const jsonToFilenameSafeString = (json: any) => {
58
+ // Custom replacer to sort keys
59
+ const sortedKeysReplacer = (key: string, value: any) =>
60
+ value && typeof value === 'object' && !Array.isArray(value)
61
+ ? Object.keys(value)
62
+ .sort()
63
+ .reduce((acc: any, curr: string) => {
64
+ acc[curr] = value[curr]
65
+ return acc
66
+ }, {})
67
+ : value
68
+
69
+ // Convert JSON to string with sorted keys
70
+ const jsonString = JSON.stringify(json ?? '', sortedKeysReplacer)
71
+
72
+ // Replace characters invalid in filenames
73
+ return jsonString
74
+ .replace(/[/\\?%*:|"<>]/g, '-') // Replace invalid characters with a dash
75
+ .replace(/\s+/g, '_') // Optionally replace whitespace with underscores
76
+ }
77
+
78
+ const staticClientCache =
79
+ typeof document !== 'undefined' ? new Map<string, any>() : null
80
+
81
+ const serverFnStaticCache: ServerFnStaticCache = {
82
+ getItem: async (ctx) => {
83
+ if (typeof document === 'undefined') {
84
+ const hash = jsonToFilenameSafeString(ctx.data)
85
+ const url = await getStaticCacheUrl({ functionId: ctx.functionId, hash })
86
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
87
+
88
+ // Use fs instead of fetch to read from filesystem
89
+ const { promises: fs } = await import('node:fs')
90
+ const path = await import('node:path')
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
+
111
+ const hash = jsonToFilenameSafeString(data)
112
+ const url = await getStaticCacheUrl({ functionId, hash })
113
+ const publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
114
+ const filePath = path.join(publicUrl, url)
115
+
116
+ // Ensure the directory exists
117
+ await fs.mkdir(path.dirname(filePath), { recursive: true })
118
+
119
+ // Store the result with fs
120
+ await fs.writeFile(
121
+ filePath,
122
+ startSerializer.stringify({
123
+ result: response.result,
124
+ context: response.context.sendContext,
125
+ }),
126
+ 'utf-8',
127
+ )
128
+ },
129
+ }
130
+
131
+ const fetchItem = async ({
132
+ data,
133
+ functionId,
134
+ }: {
135
+ data: any
136
+ functionId: string
137
+ }) => {
138
+ const hash = jsonToFilenameSafeString(data)
139
+ const url = await getStaticCacheUrl({ functionId, hash })
140
+
141
+ let result: any = staticClientCache?.get(url)
142
+
143
+ result = await fetch(url, {
144
+ method: 'GET',
145
+ })
146
+ .then((r) => r.text())
147
+ .then((d) => startSerializer.parse(d))
148
+
149
+ return result
150
+ }
151
+
152
+ export const staticFunctionMiddleware = createMiddleware({ type: 'function' })
153
+ .client(async (ctx) => {
154
+ if (
155
+ process.env.NODE_ENV === 'production' &&
156
+ // do not run this during SSR on the server
157
+ typeof document !== 'undefined'
158
+ ) {
159
+ const response = await fetchItem({
160
+ functionId: ctx.functionId,
161
+ data: ctx.data,
162
+ })
163
+
164
+ if (response) {
165
+ return {
166
+ result: response.result,
167
+ context: { ...(ctx as any).context, ...response.context },
168
+ } as any
169
+ }
170
+ }
171
+ return ctx.next()
172
+ })
173
+ .server(async (ctx) => {
174
+ const response = await ctx.next()
175
+
176
+ if (process.env.NODE_ENV === 'production') {
177
+ await serverFnStaticCache.setItem({
178
+ functionId: ctx.functionId,
179
+ response: { result: (response as any).result, context: ctx },
180
+ data: ctx.data,
181
+ })
182
+ }
183
+
184
+ return response
185
+ })