@tanstack/start-static-server-functions 1.132.0-alpha.1 → 1.132.0-alpha.11

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, import('@tanstack/start-client-core').ServerFnResponseType>;
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 { createMiddleware, startSerializer } from "@tanstack/start-client-core";
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
- 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");
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 publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR;
45
- const filePath = path.join(publicUrl, url);
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
- await fs.writeFile(
48
- filePath,
49
- startSerializer.stringify({
50
- result: response.result,
51
- context: response.context.sendContext
52
- }),
53
- "utf-8"
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.text()).then((d) => startSerializer.parse(d));
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 serverFnStaticCache.setItem({
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 { 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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"}
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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-static-server-functions",
3
- "version": "1.132.0-alpha.1",
3
+ "version": "1.132.0-alpha.11",
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
- "@tanstack/start-client-core": "1.132.0-alpha.1"
46
+ "seroval": "^1.3.2",
47
+ "@tanstack/start-client-core": "1.132.0-alpha.11"
47
48
  },
48
49
  "scripts": {}
49
50
  }
@@ -1,22 +1,16 @@
1
- import { createMiddleware, startSerializer } from '@tanstack/start-client-core'
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
- 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
-
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 publicUrl = process.env.TSS_OUTPUT_PUBLIC_DIR!
114
- const filePath = path.join(publicUrl, url)
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
- await fs.writeFile(
121
- filePath,
122
- startSerializer.stringify({
123
- result: response.result,
124
- context: response.context.sendContext,
125
- }),
126
- 'utf-8',
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.text())
147
- .then((d) => startSerializer.parse(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 serverFnStaticCache.setItem({
152
+ await addItemToCache({
178
153
  functionId: ctx.functionId,
179
154
  response: { result: (response as any).result, context: ctx },
180
155
  data: ctx.data,