solidstep 0.1.7 → 0.2.0
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/README.md +47 -1
- package/client.d.ts.map +1 -1
- package/client.js +26 -0
- package/package.json +1 -1
- package/server.d.ts.map +1 -1
- package/server.js +34 -6
- package/utils/cache.d.ts +1 -0
- package/utils/cache.d.ts.map +1 -1
- package/utils/cache.js +11 -1
- package/utils/diff-dom.d.ts +72 -0
- package/utils/diff-dom.d.ts.map +1 -0
- package/utils/diff-dom.js +1081 -0
- package/utils/server-action.client.d.ts.map +1 -1
- package/utils/server-action.client.js +35 -8
- package/utils/server-action.server.d.ts.map +1 -1
- package/utils/server-action.server.js +34 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-action.client.d.ts","sourceRoot":"","sources":["../../utils/server-action.client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server-action.client.d.ts","sourceRoot":"","sources":["../../utils/server-action.client.ts"],"names":[],"mappings":"AAqOA,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YA4C3E"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fetch from './fetch.client';
|
|
2
2
|
import { deserialize, toJSONAsync } from 'seroval';
|
|
3
3
|
import { CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLPlugin, URLSearchParamsPlugin } from 'seroval-plugins/web';
|
|
4
|
+
import { createDiffDOM } from './diff-dom';
|
|
4
5
|
class SerovalChunkReader {
|
|
5
6
|
reader;
|
|
6
7
|
buffer;
|
|
@@ -15,7 +16,7 @@ class SerovalChunkReader {
|
|
|
15
16
|
const chunk = await this.reader.read();
|
|
16
17
|
if (!chunk.done) {
|
|
17
18
|
// repopulate the buffer
|
|
18
|
-
|
|
19
|
+
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
|
|
19
20
|
newBuffer.set(this.buffer);
|
|
20
21
|
newBuffer.set(chunk.value, this.buffer.length);
|
|
21
22
|
this.buffer = newBuffer;
|
|
@@ -135,23 +136,25 @@ async function fetchServerFunction(base, id, options, args) {
|
|
|
135
136
|
body: JSON.stringify(await Promise.resolve(toJSONAsync(args, { plugins }))),
|
|
136
137
|
headers: { ...options.headers, 'Content-Type': 'application/json' }
|
|
137
138
|
}));
|
|
138
|
-
if (
|
|
139
|
+
/*if (
|
|
140
|
+
response.headers.has('Location') ||
|
|
139
141
|
response.headers.has('X-Revalidate') ||
|
|
140
|
-
response.headers.has('X-Single-Flight')
|
|
142
|
+
response.headers.has('X-Single-Flight')
|
|
143
|
+
) {
|
|
141
144
|
if (response.body) {
|
|
142
|
-
/* @ts-ignore-next-line
|
|
145
|
+
/* @ts-ignore-next-line
|
|
143
146
|
response.customBody = () => {
|
|
144
147
|
return deserializeStream(instance, response);
|
|
145
148
|
};
|
|
146
149
|
}
|
|
147
150
|
return response;
|
|
148
|
-
}
|
|
151
|
+
}*/
|
|
149
152
|
const contentType = response.headers.get('Content-Type');
|
|
150
153
|
let result;
|
|
151
|
-
if (contentType
|
|
154
|
+
if (contentType?.startsWith('text/plain')) {
|
|
152
155
|
result = await response.text();
|
|
153
156
|
}
|
|
154
|
-
else if (contentType
|
|
157
|
+
else if (contentType?.startsWith('application/json')) {
|
|
155
158
|
result = await response.json();
|
|
156
159
|
}
|
|
157
160
|
else {
|
|
@@ -163,8 +166,29 @@ async function fetchServerFunction(base, id, options, args) {
|
|
|
163
166
|
}
|
|
164
167
|
throw result;
|
|
165
168
|
}
|
|
169
|
+
if (response.headers.has('X-Revalidate')) {
|
|
170
|
+
const revalidatePath = response.headers.get('X-Revalidate');
|
|
171
|
+
const { result: actualResult, diff } = result;
|
|
172
|
+
// run algo to update UI using diff (maybe use a worker?) if the revalidate page is current page
|
|
173
|
+
if (diff && revalidatePath === window.location.pathname) {
|
|
174
|
+
const dd = createDiffDOM();
|
|
175
|
+
const didApply = dd.apply(document.body, diff);
|
|
176
|
+
if (didApply) {
|
|
177
|
+
const key = window.location.pathname;
|
|
178
|
+
sessionStorage.setItem(key, JSON.stringify(diff));
|
|
179
|
+
window.history.pushState({ revalidated: true }, '', window.location.href);
|
|
180
|
+
}
|
|
181
|
+
if (import.meta.env.DEV && !didApply) {
|
|
182
|
+
console.error('The mutation was not applied, this seems to be an edge case.');
|
|
183
|
+
console.error('Please raise an issue on GitHub describing your case.');
|
|
184
|
+
console.error('The diff calculated:', diff);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return actualResult;
|
|
188
|
+
}
|
|
166
189
|
return result;
|
|
167
190
|
}
|
|
191
|
+
// biome-ignore lint/complexity/noBannedTypes: <explanation>
|
|
168
192
|
export function createServerReference(fn, id, name) {
|
|
169
193
|
const baseURL = import.meta.env.SERVER_BASE_URL;
|
|
170
194
|
return new Proxy(fn, {
|
|
@@ -194,7 +218,10 @@ export function createServerReference(fn, id, name) {
|
|
|
194
218
|
return target[prop];
|
|
195
219
|
},
|
|
196
220
|
apply(target, thisArg, args) {
|
|
197
|
-
|
|
221
|
+
const maxClientFetchTime = +(import.meta.env.VITE_SERVER_ACTION_MAX_CLIENT_FETCH_TIME);
|
|
222
|
+
return fetchServerFunction(`${baseURL}/_server`, `${id}#${name}`, {
|
|
223
|
+
MAX_FETCH_TIME: maxClientFetchTime || undefined
|
|
224
|
+
}, args);
|
|
198
225
|
}
|
|
199
226
|
});
|
|
200
227
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-action.server.d.ts","sourceRoot":"","sources":["../../utils/server-action.server.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIN,KAAK,SAAS,EAWd,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"server-action.server.d.ts","sourceRoot":"","sources":["../../utils/server-action.server.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIN,KAAK,SAAS,EAWd,MAAM,YAAY,CAAC;AAuHpB,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,SAAS,oBA0M1D;;AAED,wBAAkD"}
|
|
@@ -7,6 +7,9 @@ import { eventHandler, setHeader, setResponseStatus, appendResponseHeader, toWeb
|
|
|
7
7
|
import invariant from 'vinxi/lib/invariant';
|
|
8
8
|
import { getManifest } from 'vinxi/manifest';
|
|
9
9
|
import { RedirectError } from './redirect';
|
|
10
|
+
import { createDiffDOM } from './diff-dom';
|
|
11
|
+
import { getCache, invalidateCache } from './cache';
|
|
12
|
+
import fetch from './fetch.server';
|
|
10
13
|
function createChunk(data) {
|
|
11
14
|
const encodeData = new TextEncoder().encode(data);
|
|
12
15
|
const bytes = encodeData.length;
|
|
@@ -228,6 +231,37 @@ export async function handleServerFunction(event) {
|
|
|
228
231
|
result = null;
|
|
229
232
|
}
|
|
230
233
|
}
|
|
234
|
+
const revalidatePath = getResponseHeader(event, 'X-Revalidate');
|
|
235
|
+
// Step 1: check if revalidation is needed
|
|
236
|
+
if (revalidatePath) {
|
|
237
|
+
// Step 2: get generated html page from cache
|
|
238
|
+
const cacheValue = getCache(revalidatePath);
|
|
239
|
+
const oldHtml = cacheValue?.rendered;
|
|
240
|
+
// Step 3: invalidate cache for path
|
|
241
|
+
invalidateCache(revalidatePath);
|
|
242
|
+
let diff;
|
|
243
|
+
if (oldHtml) {
|
|
244
|
+
// Step 4: diff the cache with new html from server
|
|
245
|
+
const reqUrl = new URL(request.url);
|
|
246
|
+
const serverUrl = reqUrl.origin;
|
|
247
|
+
await fetch(serverUrl + revalidatePath, {
|
|
248
|
+
method: 'GET'
|
|
249
|
+
}, false);
|
|
250
|
+
const newCacheValue = getCache(revalidatePath);
|
|
251
|
+
const newHtml = newCacheValue?.rendered;
|
|
252
|
+
const dd = createDiffDOM({
|
|
253
|
+
skipSelector: 'SCRIPT, STYLE, NOSCRIPT',
|
|
254
|
+
skipMode: 'full'
|
|
255
|
+
});
|
|
256
|
+
const ddDiff = dd.diff(oldHtml, newHtml);
|
|
257
|
+
diff = structuredClone(ddDiff);
|
|
258
|
+
}
|
|
259
|
+
// Step 5: add the changed html as json to the result
|
|
260
|
+
result = {
|
|
261
|
+
result,
|
|
262
|
+
diff,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
231
265
|
setHeader(event, 'content-type', 'text/javascript');
|
|
232
266
|
return serializeToStream(instance, result);
|
|
233
267
|
}
|