vitecache 0.2.0 → 0.2.1
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/dist/browser.d.ts +2 -1
- package/dist/browser.js +60 -32
- package/dist/browser.js.map +1 -1
- package/dist/dependencies.d.ts +7 -1
- package/dist/dependencies.js +13 -0
- package/dist/dependencies.js.map +1 -1
- package/dist/entry.d.ts +3 -3
- package/dist/entry.js +28 -13
- package/dist/entry.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.js +1 -0
- package/dist/node.js.map +1 -1
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +82 -20
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +28 -3
- package/dist/types.js.map +1 -1
- package/dist/vite/index.js +30 -20
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/resolve.js +4 -2
- package/dist/vite/resolve.js.map +1 -1
- package/package.json +1 -1
- package/skills/vitecache/SKILL.md +14 -2
package/dist/browser.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entry } from './entry.js';
|
|
2
2
|
import type { CacheEntryOptions } from './types.js';
|
|
3
|
-
export
|
|
3
|
+
export { MissingDependencyError } from './dependencies.js';
|
|
4
|
+
export type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';
|
|
4
5
|
/** A persistent binary computation result. Pass a browser basePath matching the public cache URL, including any Vite base. */
|
|
5
6
|
export declare class CacheEntry extends Entry {
|
|
6
7
|
constructor(options: CacheEntryOptions);
|
package/dist/browser.js
CHANGED
|
@@ -1,26 +1,46 @@
|
|
|
1
|
-
import { hashes, paths, record, resolvedDependencies } from './dependencies.js';
|
|
1
|
+
import { hashes, MissingDependencyError, paths, record, resolvedDependencies } from './dependencies.js';
|
|
2
2
|
import { Entry } from './entry.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
export { MissingDependencyError } from './dependencies.js';
|
|
4
|
+
const discoveries = new Map();
|
|
5
|
+
async function discover(base) {
|
|
6
|
+
const response = await fetch(new URL('__vitecache', base), { cache: 'no-store' });
|
|
7
|
+
if (response.status === 404)
|
|
8
|
+
return false;
|
|
9
|
+
if (!response.ok)
|
|
10
|
+
throw new Error(`vitecache discovery failed: HTTP ${response.status}`);
|
|
11
|
+
// Static SPA servers may return index.html for unknown paths.
|
|
12
|
+
if (!response.headers.get('content-type')?.includes('application/json'))
|
|
13
|
+
return false;
|
|
14
|
+
const data = await response.json();
|
|
15
|
+
return typeof data === 'object' && data !== null && 'vitecache' in data && data.vitecache === 1;
|
|
16
|
+
}
|
|
17
|
+
function discovery(base) {
|
|
18
|
+
const existing = discoveries.get(base.href);
|
|
19
|
+
if (existing)
|
|
20
|
+
return existing;
|
|
21
|
+
const pending = discover(base).catch((error) => {
|
|
22
|
+
discoveries.delete(base.href);
|
|
23
|
+
throw error;
|
|
24
|
+
});
|
|
25
|
+
discoveries.set(base.href, pending);
|
|
26
|
+
return pending;
|
|
27
|
+
}
|
|
28
|
+
/** The plugin answers a missing file or package dependency with 404 and a JSON reason. */
|
|
29
|
+
async function rejectMissing(response) {
|
|
30
|
+
if (response.status !== 404 || !response.headers.get('content-type')?.includes('application/json'))
|
|
31
|
+
return;
|
|
32
|
+
const error = await response.json();
|
|
33
|
+
if (typeof error === 'object' && error !== null && 'error' in error && error.error === 'missing-dependency' && 'message' in error && typeof error.message === 'string') {
|
|
34
|
+
throw new MissingDependencyError(error.message);
|
|
17
35
|
}
|
|
36
|
+
}
|
|
37
|
+
function createBackend(options) {
|
|
38
|
+
const base = new URL(options.basePath ?? '/_vitecache/', globalThis.location.href);
|
|
39
|
+
base.pathname = `${base.pathname.replace(/\/+$/, '')}/`;
|
|
40
|
+
base.search = '';
|
|
41
|
+
base.hash = '';
|
|
18
42
|
function plugin() {
|
|
19
|
-
discovery
|
|
20
|
-
discovery = undefined;
|
|
21
|
-
throw error;
|
|
22
|
-
});
|
|
23
|
-
return discovery;
|
|
43
|
+
return discovery(base);
|
|
24
44
|
}
|
|
25
45
|
return {
|
|
26
46
|
canSave: plugin,
|
|
@@ -43,6 +63,7 @@ function createBackend(options) {
|
|
|
43
63
|
body: JSON.stringify({ dependencies: Object.fromEntries(locators) }),
|
|
44
64
|
cache: 'no-store',
|
|
45
65
|
});
|
|
66
|
+
await rejectMissing(response);
|
|
46
67
|
if (!response.ok)
|
|
47
68
|
throw new Error(`vitecache URL resolution failed: HTTP ${response.status}`);
|
|
48
69
|
const data = await response.json();
|
|
@@ -69,15 +90,16 @@ function createBackend(options) {
|
|
|
69
90
|
return [name, locator];
|
|
70
91
|
const pathname = new URL(locator.url).pathname;
|
|
71
92
|
const path = mapped[pathname];
|
|
93
|
+
// The build maps every file it deployed, so an unmapped URL has no backing file, as the plugin reports in development.
|
|
72
94
|
if (path === undefined)
|
|
73
|
-
throw new
|
|
95
|
+
throw new MissingDependencyError(`No vitecache source file mapping for URL: ${pathname}`);
|
|
74
96
|
return [name, { path }];
|
|
75
97
|
});
|
|
76
98
|
return resolvedDependencies(Object.fromEntries(resolved));
|
|
77
99
|
},
|
|
78
100
|
async snapshot(dependencies) {
|
|
79
101
|
if (!await plugin())
|
|
80
|
-
return
|
|
102
|
+
return { status: 'unavailable' };
|
|
81
103
|
const response = await fetch(new URL('__vitecache/snapshot', base), {
|
|
82
104
|
method: 'POST',
|
|
83
105
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -87,14 +109,17 @@ function createBackend(options) {
|
|
|
87
109
|
if (!response.ok)
|
|
88
110
|
throw new Error(`vitecache dependency validation failed: HTTP ${response.status}`);
|
|
89
111
|
const data = await response.json();
|
|
90
|
-
if (typeof data
|
|
112
|
+
if (typeof data === 'object' && data !== null && 'status' in data && data.status === 'missing')
|
|
113
|
+
return { status: 'missing' };
|
|
114
|
+
if (typeof data !== 'object' || data === null || !('status' in data) || data.status !== 'available' || !('hashes' in data)) {
|
|
91
115
|
throw new TypeError('Invalid vitecache dependency response.');
|
|
92
116
|
}
|
|
93
|
-
return
|
|
117
|
+
return { status: 'available', hashes: hashes(data.hashes) };
|
|
94
118
|
},
|
|
95
119
|
async load(key, validate) {
|
|
96
120
|
const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-store' });
|
|
97
|
-
|
|
121
|
+
// The plugin answers misses with 204 so they stay out of the console; static hosts answer 404.
|
|
122
|
+
if (response.status === 204 || response.status === 404)
|
|
98
123
|
return undefined;
|
|
99
124
|
if (!response.ok)
|
|
100
125
|
throw new Error(`vitecache load failed: HTTP ${response.status}`);
|
|
@@ -112,16 +137,19 @@ function createBackend(options) {
|
|
|
112
137
|
const value = JSON.parse(decodeURIComponent(header));
|
|
113
138
|
return { bytes, hashes: hashes(value) };
|
|
114
139
|
},
|
|
115
|
-
async save(key, value,
|
|
140
|
+
async save(key, value, validation, expected, saved) {
|
|
141
|
+
// Metadata travels in the body: headers are capped near 16 KiB, and saved dependencies can be many.
|
|
142
|
+
const metadata = new TextEncoder().encode(JSON.stringify({ dependencies: validation, hashes: expected, saved }));
|
|
143
|
+
const body = new Uint8Array(4 + metadata.byteLength + value.byteLength);
|
|
144
|
+
new DataView(body.buffer).setUint32(0, metadata.byteLength);
|
|
145
|
+
body.set(metadata, 4);
|
|
146
|
+
body.set(value, 4 + metadata.byteLength);
|
|
116
147
|
const response = await fetch(new URL(`${key}.bin`, base), {
|
|
117
148
|
method: 'PUT',
|
|
118
|
-
headers: {
|
|
119
|
-
|
|
120
|
-
'X-Vitecache-Dependencies': encodeURIComponent(JSON.stringify(dependencies)),
|
|
121
|
-
'X-Vitecache-Hashes': encodeURIComponent(JSON.stringify(expected)),
|
|
122
|
-
},
|
|
123
|
-
body: new Uint8Array(value),
|
|
149
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
150
|
+
body,
|
|
124
151
|
});
|
|
152
|
+
await rejectMissing(response);
|
|
125
153
|
if (!response.ok)
|
|
126
154
|
throw new Error(`vitecache save failed: HTTP ${response.status}`);
|
|
127
155
|
},
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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","sourcesContent":["import { hashes, paths, record, resolvedDependencies } from './dependencies.js';\nimport { Entry } from './entry.js';\nimport type { Backend, CacheEntryOptions, Dependency, ResolvedDependency } from './types.js';\nexport type { CacheEntryOptions, Dependency, Dependencies, JsonValue } from './types.js';\n\nfunction createBackend(options: CacheEntryOptions): Backend {\n const base = new URL(`${(options.basePath ?? '/_vitecache/').replace(/\\/+$/, '')}/`, globalThis.location.href);\n let discovery: Promise<boolean> | undefined;\n\n async function discover(): Promise<boolean> {\n const response = await fetch(new URL('__vitecache', base), { cache: 'no-store' });\n if (response.status === 404) return false;\n if (!response.ok) throw new Error(`vitecache discovery failed: HTTP ${response.status}`);\n // Static SPA servers may return index.html for unknown paths.\n if (!response.headers.get('content-type')?.includes('application/json')) return false;\n const data: unknown = await response.json();\n return typeof data === 'object' && data !== null && 'vitecache' in data && data.vitecache === 1;\n }\n\n function plugin(): Promise<boolean> {\n discovery ??= discover().catch((error: unknown) => {\n discovery = undefined;\n throw error;\n });\n return discovery;\n }\n\n return {\n canSave: plugin,\n async resolve(dependencies) {\n const locators = Object.entries(dependencies).map(([name, locator]): [string, Dependency] => {\n if (locator.url === undefined) return [name, locator];\n const url = new URL(locator.url, globalThis.location.href);\n if (url.origin !== new URL(globalThis.location.href).origin) throw new Error('URL dependencies must be served by the same Vite project origin.');\n return [name, { url: url.href }];\n });\n if (locators.every(([, locator]) => locator.url === undefined)) {\n return resolvedDependencies(Object.fromEntries(locators));\n }\n if (await plugin()) {\n const response = await fetch(new URL('__vitecache/resolve', base), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ dependencies: Object.fromEntries(locators) }),\n cache: 'no-store',\n });\n if (!response.ok) throw new Error(`vitecache URL resolution failed: HTTP ${response.status}`);\n const data: unknown = await response.json();\n const resolved = resolvedDependencies(record(data).dependencies);\n if (Object.keys(resolved).length !== locators.length || !locators.every(([name]) => Object.hasOwn(resolved, name))) {\n throw new Error('Invalid vitecache resolved dependencies.');\n }\n return resolved;\n }\n const manifestUrl = new URL('__vitecache/paths.json', base);\n const response = await fetch(manifestUrl, { cache: 'no-store' });\n if (!response.ok || !response.headers.get('content-type')?.includes('application/json')) {\n throw new Error('URL dependencies require the vitecache Vite plugin during development and build. Missing production URL mapping.');\n }\n const data: unknown = await response.json();\n const manifest = record(data);\n const mapping = paths(manifest.paths);\n if (typeof manifest.root !== 'string' || !/^(?:\\.\\.\\/)+$/.test(manifest.root)) throw new Error('Invalid vitecache production mapping root.');\n const projectRoot = new URL(manifest.root, manifestUrl);\n const mapped = Object.fromEntries(Object.entries(mapping).map(([url, path]) => [new URL(url, projectRoot).pathname, path]));\n const resolved = locators.map(([name, locator]): [string, ResolvedDependency] => {\n if (locator.url === undefined) return [name, locator];\n const pathname = new URL(locator.url).pathname;\n const path = mapped[pathname];\n if (path === undefined) throw new Error(`No vitecache source file mapping for URL: ${pathname}`);\n return [name, { path }];\n });\n return resolvedDependencies(Object.fromEntries(resolved));\n },\n async snapshot(dependencies) {\n if (!await plugin()) return undefined;\n const response = await fetch(new URL('__vitecache/snapshot', base), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ dependencies }),\n cache: 'no-store',\n });\n if (!response.ok) throw new Error(`vitecache dependency validation failed: HTTP ${response.status}`);\n const data: unknown = await response.json();\n if (typeof data !== 'object' || data === null || !('hashes' in data)) {\n throw new TypeError('Invalid vitecache dependency response.');\n }\n return data.hashes === null ? null : hashes(data.hashes);\n },\n async load(key, validate) {\n const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-store' });\n if (response.status === 404) return undefined;\n if (!response.ok) throw new Error(`vitecache load failed: HTTP ${response.status}`);\n const contentType = response.headers.get('content-type')?.split(';')[0]?.trim();\n if (contentType === 'text/html') return undefined;\n if (contentType !== 'application/octet-stream') throw new Error(`Unexpected vitecache content type: ${contentType ?? 'missing'}`);\n const bytes = new Uint8Array(await response.arrayBuffer());\n if (!validate) return { bytes };\n const header = response.headers.get('X-Vitecache-Dependencies');\n if (header === null) throw new Error('Missing vitecache dependency metadata.');\n const value: unknown = JSON.parse(decodeURIComponent(header));\n return { bytes, hashes: hashes(value) };\n },\n async save(key, value, dependencies, expected) {\n const response = await fetch(new URL(`${key}.bin`, base), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/octet-stream',\n 'X-Vitecache-Dependencies': encodeURIComponent(JSON.stringify(dependencies)),\n 'X-Vitecache-Hashes': encodeURIComponent(JSON.stringify(expected)),\n },\n body: new Uint8Array(value),\n });\n if (!response.ok) throw new Error(`vitecache save failed: HTTP ${response.status}`);\n },\n };\n}\n\n/** A persistent binary computation result. Pass a browser basePath matching the public cache URL, including any Vite base. */\nexport class CacheEntry extends Entry {\n constructor(options: CacheEntryOptions) {\n super(options, createBackend(options));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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","sourcesContent":["import { hashes, MissingDependencyError, paths, record, resolvedDependencies } from './dependencies.js';\nimport { Entry } from './entry.js';\nimport type { Backend, CacheEntryOptions, Dependency, ResolvedDependency } from './types.js';\nexport { MissingDependencyError } from './dependencies.js';\nexport type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';\n\nconst discoveries = new Map<string, Promise<boolean>>();\n\nasync function discover(base: URL): Promise<boolean> {\n const response = await fetch(new URL('__vitecache', base), { cache: 'no-store' });\n if (response.status === 404) return false;\n if (!response.ok) throw new Error(`vitecache discovery failed: HTTP ${response.status}`);\n // Static SPA servers may return index.html for unknown paths.\n if (!response.headers.get('content-type')?.includes('application/json')) return false;\n const data: unknown = await response.json();\n return typeof data === 'object' && data !== null && 'vitecache' in data && data.vitecache === 1;\n}\n\nfunction discovery(base: URL): Promise<boolean> {\n const existing = discoveries.get(base.href);\n if (existing) return existing;\n const pending = discover(base).catch((error: unknown) => {\n discoveries.delete(base.href);\n throw error;\n });\n discoveries.set(base.href, pending);\n return pending;\n}\n\n/** The plugin answers a missing file or package dependency with 404 and a JSON reason. */\nasync function rejectMissing(response: Response): Promise<void> {\n if (response.status !== 404 || !response.headers.get('content-type')?.includes('application/json')) return;\n const error: unknown = await response.json();\n if (typeof error === 'object' && error !== null && 'error' in error && error.error === 'missing-dependency' && 'message' in error && typeof error.message === 'string') {\n throw new MissingDependencyError(error.message);\n }\n}\n\nfunction createBackend(options: CacheEntryOptions): Backend {\n const base = new URL(options.basePath ?? '/_vitecache/', globalThis.location.href);\n base.pathname = `${base.pathname.replace(/\\/+$/, '')}/`;\n base.search = '';\n base.hash = '';\n\n function plugin(): Promise<boolean> {\n return discovery(base);\n }\n\n return {\n canSave: plugin,\n async resolve(dependencies) {\n const locators = Object.entries(dependencies).map(([name, locator]): [string, Dependency] => {\n if (locator.url === undefined) return [name, locator];\n const url = new URL(locator.url, globalThis.location.href);\n if (url.origin !== new URL(globalThis.location.href).origin) throw new Error('URL dependencies must be served by the same Vite project origin.');\n return [name, { url: url.href }];\n });\n if (locators.every(([, locator]) => locator.url === undefined)) {\n return resolvedDependencies(Object.fromEntries(locators));\n }\n if (await plugin()) {\n const response = await fetch(new URL('__vitecache/resolve', base), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ dependencies: Object.fromEntries(locators) }),\n cache: 'no-store',\n });\n await rejectMissing(response);\n if (!response.ok) throw new Error(`vitecache URL resolution failed: HTTP ${response.status}`);\n const data: unknown = await response.json();\n const resolved = resolvedDependencies(record(data).dependencies);\n if (Object.keys(resolved).length !== locators.length || !locators.every(([name]) => Object.hasOwn(resolved, name))) {\n throw new Error('Invalid vitecache resolved dependencies.');\n }\n return resolved;\n }\n const manifestUrl = new URL('__vitecache/paths.json', base);\n const response = await fetch(manifestUrl, { cache: 'no-store' });\n if (!response.ok || !response.headers.get('content-type')?.includes('application/json')) {\n throw new Error('URL dependencies require the vitecache Vite plugin during development and build. Missing production URL mapping.');\n }\n const data: unknown = await response.json();\n const manifest = record(data);\n const mapping = paths(manifest.paths);\n if (typeof manifest.root !== 'string' || !/^(?:\\.\\.\\/)+$/.test(manifest.root)) throw new Error('Invalid vitecache production mapping root.');\n const projectRoot = new URL(manifest.root, manifestUrl);\n const mapped = Object.fromEntries(Object.entries(mapping).map(([url, path]) => [new URL(url, projectRoot).pathname, path]));\n const resolved = locators.map(([name, locator]): [string, ResolvedDependency] => {\n if (locator.url === undefined) return [name, locator];\n const pathname = new URL(locator.url).pathname;\n const path = mapped[pathname];\n // The build maps every file it deployed, so an unmapped URL has no backing file, as the plugin reports in development.\n if (path === undefined) throw new MissingDependencyError(`No vitecache source file mapping for URL: ${pathname}`);\n return [name, { path }];\n });\n return resolvedDependencies(Object.fromEntries(resolved));\n },\n async snapshot(dependencies) {\n if (!await plugin()) return { status: 'unavailable' };\n const response = await fetch(new URL('__vitecache/snapshot', base), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ dependencies }),\n cache: 'no-store',\n });\n if (!response.ok) throw new Error(`vitecache dependency validation failed: HTTP ${response.status}`);\n const data: unknown = await response.json();\n if (typeof data === 'object' && data !== null && 'status' in data && data.status === 'missing') return { status: 'missing' };\n if (typeof data !== 'object' || data === null || !('status' in data) || data.status !== 'available' || !('hashes' in data)) {\n throw new TypeError('Invalid vitecache dependency response.');\n }\n return { status: 'available', hashes: hashes(data.hashes) };\n },\n async load(key, validate) {\n const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-store' });\n // The plugin answers misses with 204 so they stay out of the console; static hosts answer 404.\n if (response.status === 204 || response.status === 404) return undefined;\n if (!response.ok) throw new Error(`vitecache load failed: HTTP ${response.status}`);\n const contentType = response.headers.get('content-type')?.split(';')[0]?.trim();\n if (contentType === 'text/html') return undefined;\n if (contentType !== 'application/octet-stream') throw new Error(`Unexpected vitecache content type: ${contentType ?? 'missing'}`);\n const bytes = new Uint8Array(await response.arrayBuffer());\n if (!validate) return { bytes };\n const header = response.headers.get('X-Vitecache-Dependencies');\n if (header === null) throw new Error('Missing vitecache dependency metadata.');\n const value: unknown = JSON.parse(decodeURIComponent(header));\n return { bytes, hashes: hashes(value) };\n },\n async save(key, value, validation, expected, saved) {\n // Metadata travels in the body: headers are capped near 16 KiB, and saved dependencies can be many.\n const metadata = new TextEncoder().encode(JSON.stringify({ dependencies: validation, hashes: expected, saved }));\n const body = new Uint8Array(4 + metadata.byteLength + value.byteLength);\n new DataView(body.buffer).setUint32(0, metadata.byteLength);\n body.set(metadata, 4);\n body.set(value, 4 + metadata.byteLength);\n const response = await fetch(new URL(`${key}.bin`, base), {\n method: 'PUT',\n headers: { 'Content-Type': 'application/octet-stream' },\n body,\n });\n await rejectMissing(response);\n if (!response.ok) throw new Error(`vitecache save failed: HTTP ${response.status}`);\n },\n };\n}\n\n/** A persistent binary computation result. Pass a browser basePath matching the public cache URL, including any Vite base. */\nexport class CacheEntry extends Entry {\n constructor(options: CacheEntryOptions) {\n super(options, createBackend(options));\n }\n}\n"]}
|
package/dist/dependencies.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type { Dependencies, Hashes, JsonValue, Paths, ResolvedDependencies } from './types.js';
|
|
1
|
+
import type { Dependencies, Hashes, JsonValue, Paths, ResolvedDependencies, SavedDependencies } from './types.js';
|
|
2
|
+
/** A file or package dependency has no backing file or installation, so its state cannot be checked. */
|
|
3
|
+
export declare class MissingDependencyError extends Error {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
2
6
|
export declare function record(value: unknown): Record<string, unknown>;
|
|
3
7
|
export declare function paths(value: unknown): Paths;
|
|
4
8
|
/** Validate and snapshot JSON before asynchronous dependency resolution starts. */
|
|
5
9
|
export declare function json(value: unknown, ancestors?: Set<object>): JsonValue;
|
|
6
10
|
export declare function dependencies(value: unknown): Dependencies;
|
|
7
11
|
export declare function resolvedDependencies(value: unknown): ResolvedDependencies;
|
|
12
|
+
/** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */
|
|
13
|
+
export declare function savedDependencies(value: unknown): SavedDependencies;
|
|
8
14
|
export declare function hashes(value: unknown): Hashes;
|
|
9
15
|
export declare function equalHashes(first: Hashes, second: Hashes): boolean;
|
package/dist/dependencies.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** A file or package dependency has no backing file or installation, so its state cannot be checked. */
|
|
2
|
+
export class MissingDependencyError extends Error {
|
|
3
|
+
name = 'MissingDependencyError';
|
|
4
|
+
}
|
|
1
5
|
export function record(value) {
|
|
2
6
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
3
7
|
throw new TypeError('Expected a named dependency record.');
|
|
@@ -103,6 +107,15 @@ export function resolvedDependencies(value) {
|
|
|
103
107
|
});
|
|
104
108
|
return Object.freeze(Object.fromEntries(entries));
|
|
105
109
|
}
|
|
110
|
+
/** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */
|
|
111
|
+
export function savedDependencies(value) {
|
|
112
|
+
const entries = Object.entries(dependencies(value)).map(([name, dependency]) => {
|
|
113
|
+
if (dependency.value !== undefined)
|
|
114
|
+
throw new TypeError('Saved dependencies cannot be values: values select the result; pass them to the constructor.');
|
|
115
|
+
return [name, dependency];
|
|
116
|
+
});
|
|
117
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
118
|
+
}
|
|
106
119
|
export function hashes(value) {
|
|
107
120
|
return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, hash]) => {
|
|
108
121
|
if (!name || typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash)) {
|
package/dist/dependencies.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../src/dependencies.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,SAAS,GAAY,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;IACrH,IAAI,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IACzG,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC9D,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACtG,MAAM,IAAI,GAAY,QAAQ,CAAC,KAAK,CAAC;QACrC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,IAAI,CAAC,KAAc;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IAClI,IAAI,0CAA0C,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG;YAAE,SAAS;QACpC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,0DAA0D,CAAC,CAAC;QACnG,KAAK,CAAC,GAAG,EAAE,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,8DAA8D,CAAC,CAAC;IACvG,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,KAAc;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;QAC1F,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;QAC5E,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,IAAI,CAAC,KAAc,EAAE,YAAY,IAAI,GAAG,EAAU;IAChE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5F,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IACvG,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;IAC5F,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxJ,IAAI,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;QAC7H,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC/D,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,8DAA8D,CAAC,CAAC;YAC7H,MAAM,IAAI,GAAY,QAAQ,CAAC,KAAK,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAwB,EAAE;QACxF,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,qEAAqE,CAAC,CAAC;QACjI,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;YAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAChG,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7F,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/G,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,qDAAqD,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACzJ,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,8GAA8G,CAAC,CAAC;IACtI,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAgC,EAAE;QAC3G,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS;YAAE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;QACnG,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACzF,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,MAAc;IACvD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5G,CAAC","sourcesContent":["import type { Dependencies, Dependency, Hashes, JsonValue, Paths, ResolvedDependencies, ResolvedDependency } from './types.js';\n\nexport function record(value: unknown): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new TypeError('Expected a named dependency record.');\n }\n const prototype: unknown = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) throw new TypeError('Expected a plain dependency record.');\n if (Object.getOwnPropertySymbols(value).length) throw new TypeError('Dependency names must be strings.');\n return Object.fromEntries(Object.keys(value).sort().map((name) => {\n const property = Object.getOwnPropertyDescriptor(value, name);\n if (!property || !('value' in property)) throw new TypeError('Dependency getters are not supported.');\n const item: unknown = property.value;\n return [name, item];\n }));\n}\n\nfunction path(value: unknown): string {\n if (typeof value !== 'string' || !value || value.includes('\\0')) throw new TypeError('File dependencies require nonempty paths.');\n if (/^(?:[\\\\/]|[a-z]:|[a-z][a-z\\d+.-]*:\\/\\/)/i.test(value)) {\n throw new TypeError('File dependency paths must be relative to the project root.');\n }\n const parts: string[] = [];\n for (const part of value.replace(/\\\\/g, '/').split('/')) {\n if (!part || part === '.') continue;\n if (part !== '..') {\n parts.push(part);\n continue;\n }\n if (!parts.length) throw new TypeError('File dependency paths must stay inside the project root.');\n parts.pop();\n }\n if (!parts.length) throw new TypeError('File dependencies must identify files, not the project root.');\n return parts.join('/');\n}\n\nexport function paths(value: unknown): Paths {\n return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, value]) => {\n if (!name) throw new TypeError('File dependencies require nonempty names.');\n return [name, path(value)];\n })));\n}\n\n/** Validate and snapshot JSON before asynchronous dependency resolution starts. */\nexport function json(value: unknown, ancestors = new Set<object>()): JsonValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value !== 'object') throw new TypeError('Dependency values must contain only JSON values.');\n if (ancestors.has(value)) throw new TypeError('Dependency values must not contain cycles.');\n ancestors.add(value);\n try {\n if (!Array.isArray(value)) return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, item]) => [name, json(item, ancestors)])));\n if (Object.getOwnPropertySymbols(value).length) throw new TypeError('Dependency values must not contain symbol properties.');\n const items: JsonValue[] = [];\n for (let index = 0; index < value.length; index++) {\n const property = Object.getOwnPropertyDescriptor(value, index);\n if (!property || !('value' in property)) throw new TypeError('Dependency values must not contain sparse arrays or getters.');\n const item: unknown = property.value;\n items.push(json(item, ancestors));\n }\n return Object.freeze(items);\n } finally {\n ancestors.delete(value);\n }\n}\n\nexport function dependencies(value: unknown): Dependencies {\n const entries = Object.entries(record(value)).map(([name, value]): [string, Dependency] => {\n if (!name) throw new TypeError('Dependencies require nonempty names.');\n const source = record(value);\n if (Object.keys(source).length !== 1) throw new TypeError('Each dependency must have exactly one value, path, url, or package.');\n if (Object.hasOwn(source, 'value')) return [name, Object.freeze({ value: json(source.value) })];\n if (Object.hasOwn(source, 'path')) return [name, Object.freeze({ path: path(source.path) })];\n if (Object.hasOwn(source, 'url') && typeof source.url === 'string' && source.url && !source.url.includes('\\0')) {\n return [name, Object.freeze({ url: source.url })];\n }\n if (Object.hasOwn(source, 'package') && typeof source.package === 'string' && /^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/i.test(source.package)) {\n return [name, Object.freeze({ package: source.package })];\n }\n throw new TypeError('Each dependency must have a JSON value, root-relative path, nonempty URL, or package name without a subpath.');\n });\n return Object.freeze(Object.fromEntries(entries));\n}\n\nexport function resolvedDependencies(value: unknown): ResolvedDependencies {\n const entries = Object.entries(dependencies(value)).map(([name, dependency]): [string, ResolvedDependency] => {\n if (dependency.url !== undefined) throw new TypeError('Expected resolved dependencies, not URLs.');\n return [name, dependency];\n });\n return Object.freeze(Object.fromEntries(entries));\n}\n\nexport function hashes(value: unknown): Hashes {\n return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, hash]) => {\n if (!name || typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash)) {\n throw new TypeError('Invalid file dependency hash.');\n }\n return [name, hash];\n })));\n}\n\nexport function equalHashes(first: Hashes, second: Hashes): boolean {\n const names = Object.keys(first);\n return names.length === Object.keys(second).length && names.every((name) => first[name] === second[name]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../src/dependencies.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Dependencies, Dependency, Hashes, JsonValue, Paths, ResolvedDependencies, ResolvedDependency, SavedDependencies, SavedDependency } from './types.js';\n\n/** A file or package dependency has no backing file or installation, so its state cannot be checked. */\nexport class MissingDependencyError extends Error {\n override name = 'MissingDependencyError';\n}\n\nexport function record(value: unknown): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new TypeError('Expected a named dependency record.');\n }\n const prototype: unknown = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) throw new TypeError('Expected a plain dependency record.');\n if (Object.getOwnPropertySymbols(value).length) throw new TypeError('Dependency names must be strings.');\n return Object.fromEntries(Object.keys(value).sort().map((name) => {\n const property = Object.getOwnPropertyDescriptor(value, name);\n if (!property || !('value' in property)) throw new TypeError('Dependency getters are not supported.');\n const item: unknown = property.value;\n return [name, item];\n }));\n}\n\nfunction path(value: unknown): string {\n if (typeof value !== 'string' || !value || value.includes('\\0')) throw new TypeError('File dependencies require nonempty paths.');\n if (/^(?:[\\\\/]|[a-z]:|[a-z][a-z\\d+.-]*:\\/\\/)/i.test(value)) {\n throw new TypeError('File dependency paths must be relative to the project root.');\n }\n const parts: string[] = [];\n for (const part of value.replace(/\\\\/g, '/').split('/')) {\n if (!part || part === '.') continue;\n if (part !== '..') {\n parts.push(part);\n continue;\n }\n if (!parts.length) throw new TypeError('File dependency paths must stay inside the project root.');\n parts.pop();\n }\n if (!parts.length) throw new TypeError('File dependencies must identify files, not the project root.');\n return parts.join('/');\n}\n\nexport function paths(value: unknown): Paths {\n return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, value]) => {\n if (!name) throw new TypeError('File dependencies require nonempty names.');\n return [name, path(value)];\n })));\n}\n\n/** Validate and snapshot JSON before asynchronous dependency resolution starts. */\nexport function json(value: unknown, ancestors = new Set<object>()): JsonValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value !== 'object') throw new TypeError('Dependency values must contain only JSON values.');\n if (ancestors.has(value)) throw new TypeError('Dependency values must not contain cycles.');\n ancestors.add(value);\n try {\n if (!Array.isArray(value)) return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, item]) => [name, json(item, ancestors)])));\n if (Object.getOwnPropertySymbols(value).length) throw new TypeError('Dependency values must not contain symbol properties.');\n const items: JsonValue[] = [];\n for (let index = 0; index < value.length; index++) {\n const property = Object.getOwnPropertyDescriptor(value, index);\n if (!property || !('value' in property)) throw new TypeError('Dependency values must not contain sparse arrays or getters.');\n const item: unknown = property.value;\n items.push(json(item, ancestors));\n }\n return Object.freeze(items);\n } finally {\n ancestors.delete(value);\n }\n}\n\nexport function dependencies(value: unknown): Dependencies {\n const entries = Object.entries(record(value)).map(([name, value]): [string, Dependency] => {\n if (!name) throw new TypeError('Dependencies require nonempty names.');\n const source = record(value);\n if (Object.keys(source).length !== 1) throw new TypeError('Each dependency must have exactly one value, path, url, or package.');\n if (Object.hasOwn(source, 'value')) return [name, Object.freeze({ value: json(source.value) })];\n if (Object.hasOwn(source, 'path')) return [name, Object.freeze({ path: path(source.path) })];\n if (Object.hasOwn(source, 'url') && typeof source.url === 'string' && source.url && !source.url.includes('\\0')) {\n return [name, Object.freeze({ url: source.url })];\n }\n if (Object.hasOwn(source, 'package') && typeof source.package === 'string' && /^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/i.test(source.package)) {\n return [name, Object.freeze({ package: source.package })];\n }\n throw new TypeError('Each dependency must have a JSON value, root-relative path, nonempty URL, or package name without a subpath.');\n });\n return Object.freeze(Object.fromEntries(entries));\n}\n\nexport function resolvedDependencies(value: unknown): ResolvedDependencies {\n const entries = Object.entries(dependencies(value)).map(([name, dependency]): [string, ResolvedDependency] => {\n if (dependency.url !== undefined) throw new TypeError('Expected resolved dependencies, not URLs.');\n return [name, dependency];\n });\n return Object.freeze(Object.fromEntries(entries));\n}\n\n/** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */\nexport function savedDependencies(value: unknown): SavedDependencies {\n const entries = Object.entries(dependencies(value)).map(([name, dependency]): [string, SavedDependency] => {\n if (dependency.value !== undefined) throw new TypeError('Saved dependencies cannot be values: values select the result; pass them to the constructor.');\n return [name, dependency];\n });\n return Object.freeze(Object.fromEntries(entries));\n}\n\nexport function hashes(value: unknown): Hashes {\n return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, hash]) => {\n if (!name || typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash)) {\n throw new TypeError('Invalid file dependency hash.');\n }\n return [name, hash];\n })));\n}\n\nexport function equalHashes(first: Hashes, second: Hashes): boolean {\n const names = Object.keys(first);\n return names.length === Object.keys(second).length && names.every((name) => first[name] === second[name]);\n}\n"]}
|
package/dist/entry.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Backend, CacheEntryOptions } from './types.js';
|
|
1
|
+
import type { Backend, CacheEntryOptions, SaveOptions } from './types.js';
|
|
2
2
|
export declare class Entry {
|
|
3
3
|
#private;
|
|
4
4
|
constructor(options: CacheEntryOptions, backend: Backend);
|
|
5
5
|
/** Checks validity like load(). Warns on browser misses without the Vite plugin. */
|
|
6
6
|
has(): Promise<boolean>;
|
|
7
|
-
/** Returns undefined for missing or stale results. A miss captures dependency hashes until the next save attempt finishes. */
|
|
7
|
+
/** Returns undefined for missing dependencies or missing/stale results, saved dependencies included. A miss captures dependency hashes until the next save attempt finishes. */
|
|
8
8
|
load(): Promise<Uint8Array | undefined>;
|
|
9
9
|
/** Saves bytes on disk. With file or package dependencies, call load()/has() before computing; changed dependencies reject the save. Without the Vite plugin, browser saves warn and skip the write. */
|
|
10
|
-
save(value: Uint8Array): Promise<void>;
|
|
10
|
+
save(value: Uint8Array, options?: SaveOptions): Promise<void>;
|
|
11
11
|
}
|
package/dist/entry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dependencies, equalHashes } from './dependencies.js';
|
|
1
|
+
import { dependencies, equalHashes, MissingDependencyError, savedDependencies } from './dependencies.js';
|
|
2
2
|
import { key } from './key.js';
|
|
3
3
|
export class Entry {
|
|
4
4
|
#version;
|
|
@@ -27,36 +27,50 @@ export class Entry {
|
|
|
27
27
|
});
|
|
28
28
|
return this.#resolution;
|
|
29
29
|
}
|
|
30
|
+
async #snapshot() {
|
|
31
|
+
try {
|
|
32
|
+
return await this.#backend.snapshot((await this.#resolve()).validation);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (!(error instanceof MissingDependencyError))
|
|
36
|
+
throw error;
|
|
37
|
+
return { status: 'missing' };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
/** Checks validity like load(). Warns on browser misses without the Vite plugin. */
|
|
31
41
|
async has() {
|
|
32
42
|
const found = await this.load() !== undefined;
|
|
33
|
-
if (!found && this.#
|
|
34
|
-
console.warn(`vitecache: cache miss for ${
|
|
43
|
+
if (!found && !await this.#backend.canSave()) {
|
|
44
|
+
console.warn(`vitecache: cache miss for ${this.#version}. Install the vitecache Vite plugin to generate cache files.`);
|
|
35
45
|
}
|
|
36
46
|
return found;
|
|
37
47
|
}
|
|
38
|
-
/** Returns undefined for missing or stale results. A miss captures dependency hashes until the next save attempt finishes. */
|
|
48
|
+
/** Returns undefined for missing dependencies or missing/stale results, saved dependencies included. A miss captures dependency hashes until the next save attempt finishes. */
|
|
39
49
|
async load() {
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
// Value dependencies are fully captured by the key, so there is nothing to validate or baseline.
|
|
51
|
+
if (!this.#validates)
|
|
52
|
+
return (await this.#backend.load((await this.#resolve()).key, false))?.bytes;
|
|
53
|
+
const snapshot = await this.#snapshot();
|
|
42
54
|
if (!this.#pending) {
|
|
43
55
|
this.#baseline = snapshot;
|
|
44
56
|
this.#prepared = true;
|
|
45
57
|
}
|
|
46
|
-
if (snapshot ===
|
|
58
|
+
if (snapshot.status === 'missing') {
|
|
47
59
|
this.#pending = true;
|
|
48
60
|
return undefined;
|
|
49
61
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
62
|
+
const { key } = await this.#resolve();
|
|
63
|
+
const stored = await this.#backend.load(key, snapshot.status === 'available');
|
|
64
|
+
if (!stored || (snapshot.status === 'available' && (!stored.hashes || !equalHashes(snapshot.hashes, stored.hashes)))) {
|
|
52
65
|
this.#pending = true;
|
|
53
66
|
return undefined;
|
|
54
67
|
}
|
|
55
68
|
return stored.bytes;
|
|
56
69
|
}
|
|
57
70
|
/** Saves bytes on disk. With file or package dependencies, call load()/has() before computing; changed dependencies reject the save. Without the Vite plugin, browser saves warn and skip the write. */
|
|
58
|
-
async save(value) {
|
|
71
|
+
async save(value, options = {}) {
|
|
59
72
|
const bytes = new Uint8Array(value);
|
|
73
|
+
const saved = savedDependencies(options.dependencies ?? {});
|
|
60
74
|
const expected = this.#baseline;
|
|
61
75
|
try {
|
|
62
76
|
if (!await this.#backend.canSave()) {
|
|
@@ -66,10 +80,11 @@ export class Entry {
|
|
|
66
80
|
if (this.#validates && !this.#prepared) {
|
|
67
81
|
throw new Error('Call CacheEntry.load() or has() before computing a result with file or package dependencies.');
|
|
68
82
|
}
|
|
69
|
-
if (expected ===
|
|
70
|
-
throw new
|
|
83
|
+
if (expected?.status === 'missing')
|
|
84
|
+
throw new MissingDependencyError('Cannot save a cache entry with missing file or package dependencies.');
|
|
71
85
|
const resolved = await this.#resolve();
|
|
72
|
-
|
|
86
|
+
const hashes = expected?.status === 'available' ? expected.hashes : {};
|
|
87
|
+
await this.#backend.save(resolved.key, bytes, resolved.validation, hashes, await this.#backend.resolve(saved));
|
|
73
88
|
}
|
|
74
89
|
finally {
|
|
75
90
|
this.#prepared = false;
|
package/dist/entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAG/B,MAAM,OAAO,KAAK;IACP,QAAQ,CAAS;IAC1B,WAAW,CAAyE;IAC3E,aAAa,CAAe;IAC5B,UAAU,CAAU;IACpB,QAAQ,CAAU;IAC3B,SAAS,CAAuB;IAChC,SAAS,GAAG,KAAK,CAAC;IAClB,QAAQ,GAAG,KAAK,CAAC;IAEjB,YAAY,OAA0B,EAAE,OAAgB;QACtD,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;QAC5H,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9G,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YACrF,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;YACrC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;SAC7H,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YAC3B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC7B,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;QAC1E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,sBAAsB,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC5D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,oFAAoF;IACpF,KAAK,CAAC,GAAG;QACP,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,KAAK,SAAS,CAAC;QAC9C,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,6BAA6B,IAAI,CAAC,QAAQ,8DAA8D,CAAC,CAAC;QACzH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gLAAgL;IAChL,KAAK,CAAC,IAAI;QACR,iGAAiG;QACjG,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QACnG,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;YAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACrH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,wMAAwM;IACxM,KAAK,CAAC,IAAI,CAAC,KAAiB,EAAE,UAAuB,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,mGAAmG,CAAC,CAAC;gBAClH,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,8FAA8F,CAAC,CAAC;YAClH,CAAC;YACD,IAAI,QAAQ,EAAE,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,sBAAsB,CAAC,sEAAsE,CAAC,CAAC;YAC7I,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACjH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC7B,CAAC;IACH,CAAC;CACF","sourcesContent":["import { dependencies, equalHashes, MissingDependencyError, savedDependencies } from './dependencies.js';\nimport { key } from './key.js';\nimport type { Backend, CacheEntryOptions, Dependencies, ResolvedDependencies, SaveOptions, Snapshot } from './types.js';\n\nexport class Entry {\n readonly #version: string;\n #resolution: Promise<{ key: string; validation: ResolvedDependencies }> | undefined;\n readonly #dependencies: Dependencies;\n readonly #validates: boolean;\n readonly #backend: Backend;\n #baseline: Snapshot | undefined;\n #prepared = false;\n #pending = false;\n\n constructor(options: CacheEntryOptions, backend: Backend) {\n if (typeof options.version !== 'string' || !options.version) throw new TypeError('CacheEntry requires a nonempty version.');\n this.#dependencies = dependencies(options.dependencies ?? {});\n this.#validates = Object.values(this.#dependencies).some((dependency) => !Object.hasOwn(dependency, 'value'));\n this.#version = options.version;\n this.#backend = backend;\n }\n\n #resolve(): Promise<{ key: string; validation: ResolvedDependencies }> {\n this.#resolution ??= this.#backend.resolve(this.#dependencies).then((dependencies) => ({\n key: key(this.#version, dependencies),\n validation: Object.fromEntries(Object.entries(dependencies).filter(([, dependency]) => !Object.hasOwn(dependency, 'value'))),\n })).catch((error: unknown) => {\n this.#resolution = undefined;\n throw error;\n });\n return this.#resolution;\n }\n\n async #snapshot(): Promise<Snapshot> {\n try {\n return await this.#backend.snapshot((await this.#resolve()).validation);\n } catch (error) {\n if (!(error instanceof MissingDependencyError)) throw error;\n return { status: 'missing' };\n }\n }\n\n /** Checks validity like load(). Warns on browser misses without the Vite plugin. */\n async has(): Promise<boolean> {\n const found = await this.load() !== undefined;\n if (!found && !await this.#backend.canSave()) {\n console.warn(`vitecache: cache miss for ${this.#version}. Install the vitecache Vite plugin to generate cache files.`);\n }\n return found;\n }\n\n /** Returns undefined for missing dependencies or missing/stale results, saved dependencies included. A miss captures dependency hashes until the next save attempt finishes. */\n async load(): Promise<Uint8Array | undefined> {\n // Value dependencies are fully captured by the key, so there is nothing to validate or baseline.\n if (!this.#validates) return (await this.#backend.load((await this.#resolve()).key, false))?.bytes;\n const snapshot = await this.#snapshot();\n if (!this.#pending) {\n this.#baseline = snapshot;\n this.#prepared = true;\n }\n if (snapshot.status === 'missing') {\n this.#pending = true;\n return undefined;\n }\n const { key } = await this.#resolve();\n const stored = await this.#backend.load(key, snapshot.status === 'available');\n if (!stored || (snapshot.status === 'available' && (!stored.hashes || !equalHashes(snapshot.hashes, stored.hashes)))) {\n this.#pending = true;\n return undefined;\n }\n return stored.bytes;\n }\n\n /** Saves bytes on disk. With file or package dependencies, call load()/has() before computing; changed dependencies reject the save. Without the Vite plugin, browser saves warn and skip the write. */\n async save(value: Uint8Array, options: SaveOptions = {}): Promise<void> {\n const bytes = new Uint8Array(value);\n const saved = savedDependencies(options.dependencies ?? {});\n const expected = this.#baseline;\n try {\n if (!await this.#backend.canSave()) {\n console.warn('vitecache: save skipped. Install the vitecache Vite plugin to write cache files from the browser.');\n return;\n }\n if (this.#validates && !this.#prepared) {\n throw new Error('Call CacheEntry.load() or has() before computing a result with file or package dependencies.');\n }\n if (expected?.status === 'missing') throw new MissingDependencyError('Cannot save a cache entry with missing file or package dependencies.');\n const resolved = await this.#resolve();\n const hashes = expected?.status === 'available' ? expected.hashes : {};\n await this.#backend.save(resolved.key, bytes, resolved.validation, hashes, await this.#backend.resolve(saved));\n } finally {\n this.#prepared = false;\n this.#pending = false;\n this.#baseline = undefined;\n }\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { CacheEntry } from './browser.js';
|
|
2
|
-
export type { CacheEntryOptions, Dependency, Dependencies, JsonValue } from './types.js';
|
|
1
|
+
export { CacheEntry, MissingDependencyError } from './browser.js';
|
|
2
|
+
export type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { CacheEntry } from './browser.js';
|
|
1
|
+
export { CacheEntry, MissingDependencyError } from './browser.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export { CacheEntry } from './browser.js';\nexport type { CacheEntryOptions, Dependency, Dependencies, JsonValue } from './types.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export { CacheEntry, MissingDependencyError } from './browser.js';\nexport type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';\n"]}
|
package/dist/node.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entry } from './entry.js';
|
|
2
2
|
import type { CacheEntryOptions } from './types.js';
|
|
3
|
-
export
|
|
3
|
+
export { MissingDependencyError } from './dependencies.js';
|
|
4
|
+
export type { CacheEntryOptions, Dependencies, Dependency, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';
|
|
4
5
|
/** A persistent binary computation result with content-based dependency validation. */
|
|
5
6
|
export declare class CacheEntry extends Entry {
|
|
6
7
|
constructor(options: CacheEntryOptions);
|
package/dist/node.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Entry } from './entry.js';
|
|
2
2
|
import { createStorage } from './storage.js';
|
|
3
|
+
export { MissingDependencyError } from './dependencies.js';
|
|
3
4
|
/** A persistent binary computation result with content-based dependency validation. */
|
|
4
5
|
export class CacheEntry extends Entry {
|
|
5
6
|
constructor(options) {
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,uFAAuF;AACvF,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAA0B;QACpC,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;CACF","sourcesContent":["import { Entry } from './entry.js';\nimport { createStorage } from './storage.js';\nimport type { CacheEntryOptions } from './types.js';\nexport { MissingDependencyError } from './dependencies.js';\nexport type { CacheEntryOptions, Dependencies, Dependency, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';\n\n/** A persistent binary computation result with content-based dependency validation. */\nexport class CacheEntry extends Entry {\n constructor(options: CacheEntryOptions) {\n super(options, createStorage(options));\n }\n}\n"]}
|
package/dist/storage.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ import type { Backend, CacheEntryOptions } from './types.js';
|
|
|
2
2
|
export declare class ChangedDependenciesError extends Error {
|
|
3
3
|
constructor();
|
|
4
4
|
}
|
|
5
|
-
export declare function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'>
|
|
5
|
+
export declare function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'> & {
|
|
6
|
+
memoizeHashes?: boolean;
|
|
7
|
+
}): Backend;
|
package/dist/storage.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { realpath } from 'node:fs/promises';
|
|
2
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
3
3
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import * as disk from './disk.js';
|
|
6
|
-
import { equalHashes, hashes, resolvedDependencies } from './dependencies.js';
|
|
6
|
+
import { equalHashes, hashes, MissingDependencyError, resolvedDependencies, savedDependencies } from './dependencies.js';
|
|
7
7
|
import { packageVersion } from './packages.js';
|
|
8
8
|
function digest(bytes) {
|
|
9
9
|
return createHash('sha256').update(bytes).digest('hex');
|
|
@@ -13,22 +13,66 @@ export class ChangedDependenciesError extends Error {
|
|
|
13
13
|
super('vitecache dependencies changed during computation.');
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
/** A metadata file vitecache wrote; any failure to read it names the file, since no request caused it. */
|
|
17
|
+
function metadata(bytes, file) {
|
|
18
|
+
try {
|
|
19
|
+
return parse(bytes);
|
|
20
|
+
}
|
|
21
|
+
catch (cause) {
|
|
22
|
+
throw new Error(`Invalid vitecache metadata: ${file}`, { cause });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function parse(bytes) {
|
|
17
26
|
const value = JSON.parse(new TextDecoder().decode(bytes));
|
|
18
27
|
if (typeof value !== 'object' || value === null || !('contentHash' in value) || !('dependencies' in value)) {
|
|
19
|
-
throw new Error('
|
|
28
|
+
throw new Error('Expected contentHash and dependencies.');
|
|
20
29
|
}
|
|
21
30
|
const { contentHash, dependencies } = value;
|
|
22
|
-
if (typeof contentHash !== 'string' || !/^[a-f0-9]{64}$/.test(contentHash))
|
|
23
|
-
throw new Error('
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
if (typeof contentHash !== 'string' || !/^[a-f0-9]{64}$/.test(contentHash))
|
|
32
|
+
throw new Error('Expected a content hash.');
|
|
33
|
+
if (!('saved' in value))
|
|
34
|
+
return { contentHash, dependencies: hashes(dependencies) };
|
|
35
|
+
if (typeof value.saved !== 'object' || value.saved === null)
|
|
36
|
+
throw new Error('Expected saved dependencies.');
|
|
37
|
+
const saved = Object.entries(value.saved).map(([name, item]) => {
|
|
38
|
+
if (typeof item !== 'object' || item === null || !('dependency' in item) || !('hash' in item))
|
|
39
|
+
throw new Error('Expected a saved dependency and its hash.');
|
|
40
|
+
const dependency = resolvedDependencies(savedDependencies({ [name]: item.dependency }))[name];
|
|
41
|
+
const hash = hashes({ [name]: item.hash })[name];
|
|
42
|
+
if (dependency === undefined || hash === undefined)
|
|
43
|
+
throw new Error('Expected a saved dependency and its hash.');
|
|
44
|
+
return [name, { dependency, hash }];
|
|
45
|
+
});
|
|
46
|
+
return { contentHash, dependencies: hashes(dependencies), saved: Object.fromEntries(saved) };
|
|
26
47
|
}
|
|
27
48
|
function matches(current, expected) {
|
|
28
|
-
return current
|
|
49
|
+
return current.status === 'available' && equalHashes(current.hashes, expected);
|
|
29
50
|
}
|
|
30
51
|
export function createStorage(options) {
|
|
31
52
|
const { root, directory } = disk.location(options);
|
|
53
|
+
const fileHashes = new Map();
|
|
54
|
+
async function fileHash(path) {
|
|
55
|
+
if (!options.memoizeHashes) {
|
|
56
|
+
const bytes = await disk.load(path);
|
|
57
|
+
return bytes === undefined ? undefined : digest(bytes);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const { size, mtimeNs: mtime, ctimeNs: ctime } = await stat(path, { bigint: true });
|
|
61
|
+
const cached = fileHashes.get(path);
|
|
62
|
+
if (cached?.size === size && cached.mtime === mtime && cached.ctime === ctime)
|
|
63
|
+
return await cached.hash;
|
|
64
|
+
const hash = disk.load(path).then((bytes) => bytes === undefined ? undefined : digest(bytes));
|
|
65
|
+
fileHashes.set(path, { size, mtime, ctime, hash });
|
|
66
|
+
const value = await hash;
|
|
67
|
+
if (value === undefined && fileHashes.get(path)?.hash === hash)
|
|
68
|
+
fileHashes.delete(path);
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
fileHashes.delete(path);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
32
76
|
async function snapshot(dependencies) {
|
|
33
77
|
const entries = await Promise.all(Object.entries(dependencies).filter(([, dependency]) => !('value' in dependency)).map(async ([name, dependency]) => {
|
|
34
78
|
try {
|
|
@@ -38,8 +82,8 @@ export function createStorage(options) {
|
|
|
38
82
|
}
|
|
39
83
|
if (dependency.path === undefined)
|
|
40
84
|
throw new TypeError('Expected a resolved dependency.');
|
|
41
|
-
const
|
|
42
|
-
return
|
|
85
|
+
const hash = await fileHash(resolve(root, dependency.path));
|
|
86
|
+
return hash === undefined ? null : [name, hash];
|
|
43
87
|
}
|
|
44
88
|
catch (error) {
|
|
45
89
|
if (!disk.missingDependency(error))
|
|
@@ -50,15 +94,15 @@ export function createStorage(options) {
|
|
|
50
94
|
const present = [];
|
|
51
95
|
for (const entry of entries) {
|
|
52
96
|
if (entry === null)
|
|
53
|
-
return
|
|
97
|
+
return { status: 'missing' };
|
|
54
98
|
present.push(entry);
|
|
55
99
|
}
|
|
56
|
-
return Object.fromEntries(present);
|
|
100
|
+
return { status: 'available', hashes: Object.fromEntries(present) };
|
|
57
101
|
}
|
|
58
102
|
return {
|
|
59
103
|
async canSave() { return true; },
|
|
60
|
-
async resolve(
|
|
61
|
-
return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(
|
|
104
|
+
async resolve(dependencies) {
|
|
105
|
+
return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(dependencies).map(async ([name, dependency]) => {
|
|
62
106
|
if (dependency.url === undefined)
|
|
63
107
|
return [name, dependency];
|
|
64
108
|
const url = new URL(dependency.url);
|
|
@@ -75,24 +119,42 @@ export function createStorage(options) {
|
|
|
75
119
|
},
|
|
76
120
|
snapshot,
|
|
77
121
|
async load(key) {
|
|
78
|
-
const
|
|
122
|
+
const file = join(directory, `${key}.json`);
|
|
123
|
+
const record = await disk.load(file);
|
|
79
124
|
if (record === undefined)
|
|
80
125
|
return undefined;
|
|
81
|
-
const { contentHash, dependencies } = metadata(record);
|
|
126
|
+
const { contentHash, dependencies, saved } = metadata(record, file);
|
|
127
|
+
if (saved) {
|
|
128
|
+
const entries = Object.entries(saved);
|
|
129
|
+
const current = await snapshot(Object.fromEntries(entries.map(([name, { dependency }]) => [name, dependency])));
|
|
130
|
+
if (!matches(current, Object.fromEntries(entries.map(([name, { hash }]) => [name, hash]))))
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
82
133
|
const bytes = await disk.load(join(directory, `${key}.bin`));
|
|
83
134
|
if (bytes === undefined || digest(bytes) !== contentHash)
|
|
84
135
|
return undefined;
|
|
85
136
|
return { bytes, hashes: dependencies };
|
|
86
137
|
},
|
|
87
|
-
async save(key, value,
|
|
88
|
-
if (!matches(await snapshot(
|
|
138
|
+
async save(key, value, validation, expected, saved) {
|
|
139
|
+
if (!matches(await snapshot(validation), expected))
|
|
89
140
|
throw new ChangedDependenciesError();
|
|
141
|
+
const current = await snapshot(saved);
|
|
142
|
+
if (current.status !== 'available')
|
|
143
|
+
throw new MissingDependencyError('Cannot save a cache entry with missing saved dependencies.');
|
|
90
144
|
await disk.ignore(root, directory);
|
|
91
145
|
await disk.save(join(directory, `${key}.bin`), value, async () => {
|
|
92
|
-
if (!matches(await snapshot(
|
|
146
|
+
if (!matches(await snapshot(validation), expected))
|
|
93
147
|
throw new ChangedDependenciesError();
|
|
94
148
|
});
|
|
95
149
|
const record = { contentHash: digest(value), dependencies: expected };
|
|
150
|
+
const entries = Object.entries(saved).map(([name, dependency]) => {
|
|
151
|
+
const hash = current.hashes[name];
|
|
152
|
+
if (hash === undefined)
|
|
153
|
+
throw new Error(`vitecache did not hash saved dependency ${name}.`);
|
|
154
|
+
return [name, { dependency, hash }];
|
|
155
|
+
});
|
|
156
|
+
if (entries.length)
|
|
157
|
+
record.saved = Object.fromEntries(entries);
|
|
96
158
|
await disk.save(join(directory, `${key}.json`), new TextEncoder().encode(JSON.stringify(record)));
|
|
97
159
|
},
|
|
98
160
|
};
|
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { realpath } from 'node:fs/promises';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as disk from './disk.js';\nimport { equalHashes, hashes, resolvedDependencies } from './dependencies.js';\nimport { packageVersion } from './packages.js';\nimport type { Backend, CacheEntryOptions, Dependencies, Hashes, ResolvedDependencies } from './types.js';\n\nfunction digest(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex');\n}\n\ninterface Metadata {\n contentHash: string;\n dependencies: Hashes;\n}\n\nexport class ChangedDependenciesError extends Error {\n constructor() {\n super('vitecache dependencies changed during computation.');\n }\n}\n\nfunction metadata(bytes: Uint8Array): Metadata {\n const value: unknown = JSON.parse(new TextDecoder().decode(bytes));\n if (typeof value !== 'object' || value === null || !('contentHash' in value) || !('dependencies' in value)) {\n throw new Error('Invalid vitecache metadata.');\n }\n const { contentHash, dependencies } = value;\n if (typeof contentHash !== 'string' || !/^[a-f0-9]{64}$/.test(contentHash)) {\n throw new Error('Invalid vitecache metadata.');\n }\n return { contentHash, dependencies: hashes(dependencies) };\n}\n\nfunction matches(current: Hashes | null, expected: Hashes): boolean {\n return current !== null && equalHashes(current, expected);\n}\n\nexport function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'>): Backend {\n const { root, directory } = disk.location(options);\n\n async function snapshot(dependencies: ResolvedDependencies): Promise<Hashes | null> {\n const entries = await Promise.all(Object.entries(dependencies).filter(([, dependency]) => !('value' in dependency)).map(async ([name, dependency]) => {\n try {\n if (dependency.package !== undefined) {\n const version = await packageVersion(root, dependency.package);\n return version === undefined ? null : [name, digest(new TextEncoder().encode(version))] satisfies [string, string];\n }\n if (dependency.path === undefined) throw new TypeError('Expected a resolved dependency.');\n const bytes = await disk.load(resolve(root, dependency.path));\n return bytes === undefined ? null : [name, digest(bytes)] satisfies [string, string];\n } catch (error) {\n if (!disk.missingDependency(error)) throw error;\n return null;\n }\n }));\n const present: [string, string][] = [];\n for (const entry of entries) {\n if (entry === null) return null;\n present.push(entry);\n }\n return Object.fromEntries(present);\n }\n\n return {\n async canSave() { return true; },\n async resolve(files: Dependencies) {\n return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(files).map(async ([name, dependency]) => {\n if (dependency.url === undefined) return [name, dependency];\n const url = new URL(dependency.url);\n if (url.protocol !== 'file:') {\n throw new Error('Node file dependency URLs must use file:. Use a project-root-relative path for Vite-served files.');\n }\n const file = fileURLToPath(url);\n let path = relative(root, file);\n if (path === '..' || path.startsWith(`..${sep}`) || isAbsolute(path)) {\n path = relative(await realpath(root), await realpath(file));\n }\n return [name, { path: path.split(sep).join('/') }];\n }))));\n },\n snapshot,\n async load(key) {\n const record = await disk.load(join(directory, `${key}.json`));\n if (record === undefined) return undefined;\n const { contentHash, dependencies } = metadata(record);\n const bytes = await disk.load(join(directory, `${key}.bin`));\n if (bytes === undefined || digest(bytes) !== contentHash) return undefined;\n return { bytes, hashes: dependencies };\n },\n async save(key, value, files, expected) {\n if (!matches(await snapshot(files), expected)) throw new ChangedDependenciesError();\n await disk.ignore(root, directory);\n await disk.save(join(directory, `${key}.bin`), value, async () => {\n if (!matches(await snapshot(files), expected)) throw new ChangedDependenciesError();\n });\n const record: Metadata = { contentHash: digest(value), dependencies: expected };\n await disk.save(join(directory, `${key}.json`), new TextEncoder().encode(JSON.stringify(record)));\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { realpath, stat } from 'node:fs/promises';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as disk from './disk.js';\nimport { equalHashes, hashes, MissingDependencyError, resolvedDependencies, savedDependencies } from './dependencies.js';\nimport { packageVersion } from './packages.js';\nimport type { Backend, CacheEntryOptions, Dependencies, Hashes, ResolvedDependencies, ResolvedDependency, Snapshot } from './types.js';\n\nfunction digest(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex');\n}\n\ninterface Metadata {\n contentHash: string;\n dependencies: Hashes;\n /** Saved dependencies with their hashes at save time; absent when there were none. */\n saved?: Readonly<Record<string, { dependency: ResolvedDependency; hash: string }>>;\n}\n\nexport class ChangedDependenciesError extends Error {\n constructor() {\n super('vitecache dependencies changed during computation.');\n }\n}\n\n/** A metadata file vitecache wrote; any failure to read it names the file, since no request caused it. */\nfunction metadata(bytes: Uint8Array, file: string): Metadata {\n try {\n return parse(bytes);\n } catch (cause) {\n throw new Error(`Invalid vitecache metadata: ${file}`, { cause });\n }\n}\n\nfunction parse(bytes: Uint8Array): Metadata {\n const value: unknown = JSON.parse(new TextDecoder().decode(bytes));\n if (typeof value !== 'object' || value === null || !('contentHash' in value) || !('dependencies' in value)) {\n throw new Error('Expected contentHash and dependencies.');\n }\n const { contentHash, dependencies } = value;\n if (typeof contentHash !== 'string' || !/^[a-f0-9]{64}$/.test(contentHash)) throw new Error('Expected a content hash.');\n if (!('saved' in value)) return { contentHash, dependencies: hashes(dependencies) };\n if (typeof value.saved !== 'object' || value.saved === null) throw new Error('Expected saved dependencies.');\n const saved = Object.entries(value.saved).map(([name, item]: [string, unknown]) => {\n if (typeof item !== 'object' || item === null || !('dependency' in item) || !('hash' in item)) throw new Error('Expected a saved dependency and its hash.');\n const dependency = resolvedDependencies(savedDependencies({ [name]: item.dependency }))[name];\n const hash = hashes({ [name]: item.hash })[name];\n if (dependency === undefined || hash === undefined) throw new Error('Expected a saved dependency and its hash.');\n return [name, { dependency, hash }] as const;\n });\n return { contentHash, dependencies: hashes(dependencies), saved: Object.fromEntries(saved) };\n}\n\nfunction matches(current: Snapshot, expected: Hashes): boolean {\n return current.status === 'available' && equalHashes(current.hashes, expected);\n}\n\nexport function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'> & { memoizeHashes?: boolean }): Backend {\n const { root, directory } = disk.location(options);\n const fileHashes = new Map<string, { size: bigint; mtime: bigint; ctime: bigint; hash: Promise<string | undefined> }>();\n\n async function fileHash(path: string): Promise<string | undefined> {\n if (!options.memoizeHashes) {\n const bytes = await disk.load(path);\n return bytes === undefined ? undefined : digest(bytes);\n }\n try {\n const { size, mtimeNs: mtime, ctimeNs: ctime } = await stat(path, { bigint: true });\n const cached = fileHashes.get(path);\n if (cached?.size === size && cached.mtime === mtime && cached.ctime === ctime) return await cached.hash;\n const hash = disk.load(path).then((bytes) => bytes === undefined ? undefined : digest(bytes));\n fileHashes.set(path, { size, mtime, ctime, hash });\n const value = await hash;\n if (value === undefined && fileHashes.get(path)?.hash === hash) fileHashes.delete(path);\n return value;\n } catch (error) {\n fileHashes.delete(path);\n throw error;\n }\n }\n\n async function snapshot(dependencies: ResolvedDependencies): Promise<Snapshot> {\n const entries = await Promise.all(Object.entries(dependencies).filter(([, dependency]) => !('value' in dependency)).map(async ([name, dependency]) => {\n try {\n if (dependency.package !== undefined) {\n const version = await packageVersion(root, dependency.package);\n return version === undefined ? null : [name, digest(new TextEncoder().encode(version))] satisfies [string, string];\n }\n if (dependency.path === undefined) throw new TypeError('Expected a resolved dependency.');\n const hash = await fileHash(resolve(root, dependency.path));\n return hash === undefined ? null : [name, hash] satisfies [string, string];\n } catch (error) {\n if (!disk.missingDependency(error)) throw error;\n return null;\n }\n }));\n const present: [string, string][] = [];\n for (const entry of entries) {\n if (entry === null) return { status: 'missing' };\n present.push(entry);\n }\n return { status: 'available', hashes: Object.fromEntries(present) };\n }\n\n return {\n async canSave() { return true; },\n async resolve(dependencies: Dependencies) {\n return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(dependencies).map(async ([name, dependency]) => {\n if (dependency.url === undefined) return [name, dependency];\n const url = new URL(dependency.url);\n if (url.protocol !== 'file:') {\n throw new Error('Node file dependency URLs must use file:. Use a project-root-relative path for Vite-served files.');\n }\n const file = fileURLToPath(url);\n let path = relative(root, file);\n if (path === '..' || path.startsWith(`..${sep}`) || isAbsolute(path)) {\n path = relative(await realpath(root), await realpath(file));\n }\n return [name, { path: path.split(sep).join('/') }];\n }))));\n },\n snapshot,\n async load(key) {\n const file = join(directory, `${key}.json`);\n const record = await disk.load(file);\n if (record === undefined) return undefined;\n const { contentHash, dependencies, saved } = metadata(record, file);\n if (saved) {\n const entries = Object.entries(saved);\n const current = await snapshot(Object.fromEntries(entries.map(([name, { dependency }]) => [name, dependency])));\n if (!matches(current, Object.fromEntries(entries.map(([name, { hash }]) => [name, hash])))) return undefined;\n }\n const bytes = await disk.load(join(directory, `${key}.bin`));\n if (bytes === undefined || digest(bytes) !== contentHash) return undefined;\n return { bytes, hashes: dependencies };\n },\n async save(key, value, validation, expected, saved) {\n if (!matches(await snapshot(validation), expected)) throw new ChangedDependenciesError();\n const current = await snapshot(saved);\n if (current.status !== 'available') throw new MissingDependencyError('Cannot save a cache entry with missing saved dependencies.');\n await disk.ignore(root, directory);\n await disk.save(join(directory, `${key}.bin`), value, async () => {\n if (!matches(await snapshot(validation), expected)) throw new ChangedDependenciesError();\n });\n const record: Metadata = { contentHash: digest(value), dependencies: expected };\n const entries = Object.entries(saved).map(([name, dependency]) => {\n const hash = current.hashes[name];\n if (hash === undefined) throw new Error(`vitecache did not hash saved dependency ${name}.`);\n return [name, { dependency, hash }] as const;\n });\n if (entries.length) record.saved = Object.fromEntries(entries);\n await disk.save(join(directory, `${key}.json`), new TextEncoder().encode(JSON.stringify(record)));\n },\n };\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -27,9 +27,24 @@ export type Dependencies = Readonly<Record<string, Dependency>>;
|
|
|
27
27
|
export type ResolvedDependency = Exclude<Dependency, {
|
|
28
28
|
readonly url: string;
|
|
29
29
|
}>;
|
|
30
|
+
/** A file, URL, or package dependency: one whose current state can be checked again later. */
|
|
31
|
+
export type SavedDependency = Exclude<Dependency, {
|
|
32
|
+
readonly value: JsonValue;
|
|
33
|
+
}>;
|
|
34
|
+
export type SavedDependencies = Readonly<Record<string, SavedDependency>>;
|
|
30
35
|
export type ResolvedDependencies = Readonly<Record<string, ResolvedDependency>>;
|
|
31
36
|
export type Paths = Readonly<Record<string, string>>;
|
|
37
|
+
/** Dependency names mapped to file-content or installed-package-version hashes. */
|
|
32
38
|
export type Hashes = Readonly<Record<string, string>>;
|
|
39
|
+
/** Current dependency hashes, a missing dependency, or unavailable browser validation. */
|
|
40
|
+
export type Snapshot = {
|
|
41
|
+
readonly status: 'available';
|
|
42
|
+
readonly hashes: Hashes;
|
|
43
|
+
} | {
|
|
44
|
+
readonly status: 'missing';
|
|
45
|
+
} | {
|
|
46
|
+
readonly status: 'unavailable';
|
|
47
|
+
};
|
|
33
48
|
export interface CacheEntryOptions {
|
|
34
49
|
/** Identifies the computation and its implementation version, for example "simplify@2". */
|
|
35
50
|
version: string;
|
|
@@ -40,6 +55,16 @@ export interface CacheEntryOptions {
|
|
|
40
55
|
/** Node project root, default process.cwd(). Browser dependencies resolve against the Vite root. Excluded from the key. */
|
|
41
56
|
root?: string;
|
|
42
57
|
}
|
|
58
|
+
export interface SaveOptions {
|
|
59
|
+
/**
|
|
60
|
+
* What the result was built from, when known only after computing it. They do not affect the key, so a save with
|
|
61
|
+
* different saved dependencies replaces the same entry. Rechecked by every load() where file contents are validated;
|
|
62
|
+
* a changed or missing one is a miss. Hashed when save() runs, so one that changed after the computation read it goes
|
|
63
|
+
* unnoticed until it changes again. One without a backing file makes save() throw MissingDependencyError. Values select
|
|
64
|
+
* the result, so they belong in the constructor.
|
|
65
|
+
*/
|
|
66
|
+
dependencies?: SavedDependencies;
|
|
67
|
+
}
|
|
43
68
|
export interface Stored {
|
|
44
69
|
bytes: Uint8Array;
|
|
45
70
|
hashes?: Hashes;
|
|
@@ -47,8 +72,8 @@ export interface Stored {
|
|
|
47
72
|
export interface Backend {
|
|
48
73
|
canSave(): Promise<boolean>;
|
|
49
74
|
resolve(dependencies: Dependencies): Promise<ResolvedDependencies>;
|
|
50
|
-
|
|
51
|
-
snapshot(dependencies: ResolvedDependencies): Promise<Hashes | null | undefined>;
|
|
75
|
+
snapshot(dependencies: ResolvedDependencies): Promise<Snapshot>;
|
|
52
76
|
load(key: string, validate: boolean): Promise<Stored | undefined>;
|
|
53
|
-
|
|
77
|
+
/** `validation` must still match `expected`; `saved` is hashed now and rechecked on load. Neither holds values. */
|
|
78
|
+
save(key: string, value: Uint8Array, validation: ResolvedDependencies, expected: Hashes, saved: ResolvedDependencies): Promise<void>;
|
|
54
79
|
}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type JsonValue = null | boolean | number | string | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\n/** Exactly one source per dependency. Names and descriptors participate in cache identity. */\nexport type Dependency =\n | { readonly value: JsonValue; readonly path?: never; readonly url?: never; readonly package?: never }\n | { readonly path: string; readonly value?: never; readonly url?: never; readonly package?: never }\n | { readonly url: string; readonly value?: never; readonly path?: never; readonly package?: never }\n | { readonly package: string; readonly value?: never; readonly path?: never; readonly url?: never };\nexport type Dependencies = Readonly<Record<string, Dependency>>;\nexport type ResolvedDependency = Exclude<Dependency, { readonly url: string }>;\nexport type ResolvedDependencies = Readonly<Record<string, ResolvedDependency>>;\nexport type Paths = Readonly<Record<string, string>>;\nexport type Hashes = Readonly<Record<string, string>>;\n\nexport interface CacheEntryOptions {\n /** Identifies the computation and its implementation version, for example \"simplify@2\". */\n version: string;\n /** Named dependencies sorted by name. JSON values affect the key; file contents and installed package versions are validated in Node or through the Vite plugin. Paths are always project-root-relative. URLs resolve to those paths through Vite, or from local file: URLs in Node. Packages track only the declared package's installed version, without importing it. */\n dependencies?: Dependencies;\n /** Disk directory in Node (default public/_vitecache); URL prefix in browsers (default /_vitecache/). Excluded from the key. */\n basePath?: string;\n /** Node project root, default process.cwd(). Browser dependencies resolve against the Vite root. Excluded from the key. */\n root?: string;\n}\n\nexport interface Stored {\n bytes: Uint8Array;\n hashes?: Hashes;\n}\n\nexport interface Backend {\n canSave(): Promise<boolean>;\n resolve(dependencies: Dependencies): Promise<ResolvedDependencies>;\n
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type JsonValue = null | boolean | number | string | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\n/** Exactly one source per dependency. Names and descriptors participate in cache identity. */\nexport type Dependency =\n | { readonly value: JsonValue; readonly path?: never; readonly url?: never; readonly package?: never }\n | { readonly path: string; readonly value?: never; readonly url?: never; readonly package?: never }\n | { readonly url: string; readonly value?: never; readonly path?: never; readonly package?: never }\n | { readonly package: string; readonly value?: never; readonly path?: never; readonly url?: never };\nexport type Dependencies = Readonly<Record<string, Dependency>>;\nexport type ResolvedDependency = Exclude<Dependency, { readonly url: string }>;\n/** A file, URL, or package dependency: one whose current state can be checked again later. */\nexport type SavedDependency = Exclude<Dependency, { readonly value: JsonValue }>;\nexport type SavedDependencies = Readonly<Record<string, SavedDependency>>;\nexport type ResolvedDependencies = Readonly<Record<string, ResolvedDependency>>;\nexport type Paths = Readonly<Record<string, string>>;\n/** Dependency names mapped to file-content or installed-package-version hashes. */\nexport type Hashes = Readonly<Record<string, string>>;\n/** Current dependency hashes, a missing dependency, or unavailable browser validation. */\nexport type Snapshot =\n | { readonly status: 'available'; readonly hashes: Hashes }\n | { readonly status: 'missing' }\n | { readonly status: 'unavailable' };\n\nexport interface CacheEntryOptions {\n /** Identifies the computation and its implementation version, for example \"simplify@2\". */\n version: string;\n /** Named dependencies sorted by name. JSON values affect the key; file contents and installed package versions are validated in Node or through the Vite plugin. Paths are always project-root-relative. URLs resolve to those paths through Vite, or from local file: URLs in Node. Packages track only the declared package's installed version, without importing it. */\n dependencies?: Dependencies;\n /** Disk directory in Node (default public/_vitecache); URL prefix in browsers (default /_vitecache/). Excluded from the key. */\n basePath?: string;\n /** Node project root, default process.cwd(). Browser dependencies resolve against the Vite root. Excluded from the key. */\n root?: string;\n}\n\nexport interface SaveOptions {\n /**\n * What the result was built from, when known only after computing it. They do not affect the key, so a save with\n * different saved dependencies replaces the same entry. Rechecked by every load() where file contents are validated;\n * a changed or missing one is a miss. Hashed when save() runs, so one that changed after the computation read it goes\n * unnoticed until it changes again. One without a backing file makes save() throw MissingDependencyError. Values select\n * the result, so they belong in the constructor.\n */\n dependencies?: SavedDependencies;\n}\n\nexport interface Stored {\n bytes: Uint8Array;\n hashes?: Hashes;\n}\n\nexport interface Backend {\n canSave(): Promise<boolean>;\n resolve(dependencies: Dependencies): Promise<ResolvedDependencies>;\n snapshot(dependencies: ResolvedDependencies): Promise<Snapshot>;\n load(key: string, validate: boolean): Promise<Stored | undefined>;\n /** `validation` must still match `expected`; `saved` is hashed now and rechecked on load. Neither holds values. */\n save(key: string, value: Uint8Array, validation: ResolvedDependencies, expected: Hashes, saved: ResolvedDependencies): Promise<void>;\n}\n"]}
|
package/dist/vite/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { relative, resolve, sep } from 'node:path';
|
|
2
2
|
import * as disk from '../disk.js';
|
|
3
3
|
import { ChangedDependenciesError, createStorage } from '../storage.js';
|
|
4
|
-
import { dependencies, hashes, resolvedDependencies } from '../dependencies.js';
|
|
4
|
+
import { dependencies, hashes, MissingDependencyError, resolvedDependencies, savedDependencies } from '../dependencies.js';
|
|
5
5
|
import { confine, inside, OutsideRootError, resolveDependencies } from './resolve.js';
|
|
6
6
|
import { assetUrl, publicPaths } from './manifest.js';
|
|
7
7
|
class RequestError extends Error {
|
|
@@ -16,6 +16,8 @@ function status(error) {
|
|
|
16
16
|
return error.status;
|
|
17
17
|
if (error instanceof ChangedDependenciesError)
|
|
18
18
|
return 409;
|
|
19
|
+
if (error instanceof MissingDependencyError)
|
|
20
|
+
return 404;
|
|
19
21
|
if (error instanceof OutsideRootError)
|
|
20
22
|
return 403;
|
|
21
23
|
if (error instanceof TypeError || error instanceof URIError)
|
|
@@ -64,18 +66,18 @@ function validate(value, parser) {
|
|
|
64
66
|
throw new RequestError(400, error.message);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
throw new RequestError(400,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
69
|
+
/** A save body: a 4-byte big-endian metadata length, that much JSON metadata, then the cached bytes. */
|
|
70
|
+
function upload(bytes) {
|
|
71
|
+
if (bytes.byteLength < 4)
|
|
72
|
+
throw new RequestError(400, 'Expected a metadata length');
|
|
73
|
+
const end = 4 + new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0);
|
|
74
|
+
if (end > bytes.byteLength)
|
|
75
|
+
throw new RequestError(400, 'Truncated save metadata');
|
|
76
|
+
const metadata = parse(new TextDecoder().decode(bytes.subarray(4, end)));
|
|
77
|
+
if (typeof metadata !== 'object' || metadata === null || !('dependencies' in metadata) || !('hashes' in metadata) || !('saved' in metadata)) {
|
|
78
|
+
throw new RequestError(400, 'Expected dependencies, hashes, and saved dependencies');
|
|
79
|
+
}
|
|
80
|
+
return { metadata, value: bytes.subarray(end) };
|
|
79
81
|
}
|
|
80
82
|
function json(response, value) {
|
|
81
83
|
response.setHeader('Content-Type', 'application/json');
|
|
@@ -103,7 +105,7 @@ async function respond(request, response, name, storage, server) {
|
|
|
103
105
|
}
|
|
104
106
|
const deps = validate(value.dependencies, resolvedDependencies);
|
|
105
107
|
await confine(root, deps);
|
|
106
|
-
json(response,
|
|
108
|
+
json(response, await storage.snapshot(deps));
|
|
107
109
|
return;
|
|
108
110
|
}
|
|
109
111
|
if (!/^[a-f0-9]{64}\.bin$/.test(name)) {
|
|
@@ -115,10 +117,13 @@ async function respond(request, response, name, storage, server) {
|
|
|
115
117
|
if (request.method === 'PUT') {
|
|
116
118
|
if (!allowed(request))
|
|
117
119
|
throw new RequestError(403, 'Cross-origin requests are forbidden');
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
const { metadata, value } = upload(await body(request));
|
|
121
|
+
const validation = validate(metadata.dependencies, resolvedDependencies);
|
|
122
|
+
const expected = validate(metadata.hashes, hashes);
|
|
123
|
+
const saved = validate(metadata.saved, (input) => resolvedDependencies(savedDependencies(input)));
|
|
124
|
+
await confine(root, validation);
|
|
125
|
+
await confine(root, saved);
|
|
126
|
+
await storage.save(key, value, validation, expected, saved);
|
|
122
127
|
response.statusCode = 204;
|
|
123
128
|
response.end();
|
|
124
129
|
return;
|
|
@@ -131,7 +136,8 @@ async function respond(request, response, name, storage, server) {
|
|
|
131
136
|
}
|
|
132
137
|
const stored = await storage.load(key, true);
|
|
133
138
|
if (stored === undefined) {
|
|
134
|
-
|
|
139
|
+
// Browsers log every 404 fetch as an error, and a miss is an expected answer.
|
|
140
|
+
response.statusCode = 204;
|
|
135
141
|
response.end();
|
|
136
142
|
return;
|
|
137
143
|
}
|
|
@@ -156,7 +162,7 @@ export function vitecache(options = {}) {
|
|
|
156
162
|
}
|
|
157
163
|
const base = new URL(config.base, 'http://vitecache').pathname.replace(/\/$/, '');
|
|
158
164
|
resolved = {
|
|
159
|
-
backend: createStorage({ root, ...options }),
|
|
165
|
+
backend: createStorage({ root, ...options, memoizeHashes: config.command === 'serve' }),
|
|
160
166
|
root,
|
|
161
167
|
directory,
|
|
162
168
|
config,
|
|
@@ -199,6 +205,10 @@ export function vitecache(options = {}) {
|
|
|
199
205
|
}
|
|
200
206
|
void respond(request, response, path.slice(storage.prefix.length), storage.backend, server).catch((error) => {
|
|
201
207
|
response.statusCode = status(error);
|
|
208
|
+
if (error instanceof MissingDependencyError) {
|
|
209
|
+
json(response, { error: 'missing-dependency', message: error.message });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
202
212
|
response.setHeader('Content-Type', 'text/plain');
|
|
203
213
|
response.end(error instanceof Error ? error.message : 'vitecache request failed');
|
|
204
214
|
});
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGnD,OAAO,KAAK,IAAI,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAOtD,MAAM,YAAa,SAAQ,KAAK;IACT;IAArB,YAAqB,MAAc,EAAE,OAAe;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QADI,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,YAAY,YAAY;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IACvD,IAAI,KAAK,YAAY,wBAAwB;QAAE,OAAO,GAAG,CAAC;IAC1D,IAAI,KAAK,YAAY,gBAAgB;QAAE,OAAO,GAAG,CAAC;IAClD,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,OAAwB;IACvC,IAAI,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IACrE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IACtG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,OAAwB;IAC1C,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAClC,MAAM,KAAK,GAAY,KAAK,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;QAC3F,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,KAAK,CAAC,KAAa;IAC1B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAI,KAAc,EAAE,MAA6B;IAChE,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,SAAS,CAAC;YAAE,MAAM,KAAK,CAAC;QAC/C,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,OAAwB,EAAE,IAAY;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,WAAW,IAAI,SAAS,CAAC,CAAC;IACrF,IAAI,CAAC;QACH,OAAO,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC;YAAE,MAAM,KAAK,CAAC;QAC9C,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,QAAwB,EAAE,KAAc;IACpD,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,OAAwB,EACxB,QAAwB,EACxB,IAAY,EACZ,OAAgB,EAChB,MAAqB;IAErB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAChD,IAAI,IAAI,KAAK,aAAa,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IACD,IAAI,CAAC,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,qBAAqB,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QACrG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;QAC1F,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,IAAI,KAAK,qBAAqB,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC5F,IAAI,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAClF,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,oBAAoB,CAAC,CAAC;QAChE,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;QAC1F,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,0BAA0B,CAAC,EAAE,oBAAoB,CAAC,CAAC;QACzF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC,CAAC;QACzE,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1B,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC7D,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1D,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAC9C,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;IAC/D,QAAQ,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9D,QAAQ,CAAC,SAAS,CAAC,0BAA0B,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,SAAS,CAAC,UAA4B,EAAE;IACtD,IAAI,QAAsI,CAAC;IAC3I,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,MAAM;QACf,KAAK,CAAC,cAAc,CAAC,MAAM;YACzB,IAAI,CAAC,MAAM,CAAC,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAC5E,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;YAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACnD,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;gBAClD,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;YAClF,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClF,QAAQ,GAAG;gBACT,OAAO,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;gBAC5C,IAAI;gBACJ,SAAS;gBACT,MAAM;gBACN,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACpC,MAAM,EAAE,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;aACxE,CAAC;YACF,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM;YACnC,MAAM,OAAO,GAAG,QAAQ,CAAC;YACzB,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC1E,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC5F,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;oBAAE,SAAS;gBACrC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;gBACxD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS;gBACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,CAAC,QAAQ;oBAAE,SAAS;gBACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;gBACrE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;oBAAE,SAAS;gBACrE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YACnH,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,SAAS,yBAAyB,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9H,CAAC;QACD,eAAe,CAAC,MAAM;YACpB,MAAM,OAAO,GAAG,QAAQ,CAAC;YACzB,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC1E,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;gBACjD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;gBACtE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrC,IAAI,EAAE,CAAC;oBACP,OAAO;gBACT,CAAC;gBACD,KAAK,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;oBACnH,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;oBACpC,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;oBACjD,QAAQ,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC;gBACpF,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,eAAe,SAAS,CAAC","sourcesContent":["import { relative, resolve, sep } from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite';\nimport * as disk from '../disk.js';\nimport { ChangedDependenciesError, createStorage } from '../storage.js';\nimport type { Backend } from '../types.js';\nimport { dependencies, hashes, resolvedDependencies } from '../dependencies.js';\nimport { confine, inside, OutsideRootError, resolveDependencies } from './resolve.js';\nimport { assetUrl, publicPaths } from './manifest.js';\n\nexport interface VitecacheOptions {\n /** Cache directory relative to the Vite project root; must be inside publicDir. */\n basePath?: string;\n}\n\nclass RequestError extends Error {\n constructor(readonly status: number, message: string) {\n super(message);\n }\n}\n\nfunction status(error: unknown): number {\n if (error instanceof RequestError) return error.status;\n if (error instanceof ChangedDependenciesError) return 409;\n if (error instanceof OutsideRootError) return 403;\n if (error instanceof TypeError || error instanceof URIError) return 400;\n return 500;\n}\n\nfunction allowed(request: IncomingMessage): boolean {\n if (request.headers['sec-fetch-site'] === 'cross-site') return false;\n const origin = request.headers.origin;\n if (!origin) return true;\n try {\n const url = new URL(origin);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.host === request.headers.host;\n } catch {\n return false;\n }\n}\n\nasync function body(request: IncomingMessage): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n for await (const value of request) {\n const chunk: unknown = value;\n if (!Buffer.isBuffer(chunk)) throw new RequestError(400, 'Expected a binary request body');\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n}\n\nfunction parse(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n throw new RequestError(400, 'Expected valid JSON');\n }\n}\n\nfunction validate<T>(value: unknown, parser: (value: unknown) => T): T {\n try {\n return parser(value);\n } catch (error) {\n if (!(error instanceof TypeError)) throw error;\n throw new RequestError(400, error.message);\n }\n}\n\nfunction header(request: IncomingMessage, name: string): unknown {\n const value = request.headers[name];\n if (typeof value !== 'string') throw new RequestError(400, `Missing ${name} header`);\n try {\n return parse(decodeURIComponent(value));\n } catch (error) {\n if (!(error instanceof URIError)) throw error;\n throw new RequestError(400, 'Invalid encoded request header');\n }\n}\n\nfunction json(response: ServerResponse, value: unknown): void {\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(value));\n}\n\nasync function respond(\n request: IncomingMessage,\n response: ServerResponse,\n name: string,\n storage: Backend,\n server: ViteDevServer,\n): Promise<void> {\n const root = server.config.root;\n response.setHeader('Cache-Control', 'no-store');\n if (name === '__vitecache' && request.method === 'GET') {\n json(response, { vitecache: 1 });\n return;\n }\n if ((name === '__vitecache/snapshot' || name === '__vitecache/resolve') && request.method === 'POST') {\n if (!allowed(request)) throw new RequestError(403, 'Cross-origin requests are forbidden');\n const value = parse(new TextDecoder().decode(await body(request)));\n if (typeof value !== 'object' || value === null || !('dependencies' in value)) {\n throw new RequestError(400, 'Expected dependencies');\n }\n if (name === '__vitecache/resolve') {\n const deps = validate(value.dependencies, dependencies);\n const origin = `${server.config.server.https ? 'https' : 'http'}://${request.headers.host}`;\n json(response, { dependencies: await resolveDependencies(server, deps, origin) });\n return;\n }\n const deps = validate(value.dependencies, resolvedDependencies);\n await confine(root, deps);\n json(response, { hashes: await storage.snapshot(deps) });\n return;\n }\n if (!/^[a-f0-9]{64}\\.bin$/.test(name)) {\n response.statusCode = 404;\n response.end();\n return;\n }\n const key = name.slice(0, -4);\n if (request.method === 'PUT') {\n if (!allowed(request)) throw new RequestError(403, 'Cross-origin requests are forbidden');\n const deps = validate(header(request, 'x-vitecache-dependencies'), resolvedDependencies);\n const expected = validate(header(request, 'x-vitecache-hashes'), hashes);\n await confine(root, deps);\n await storage.save(key, await body(request), deps, expected);\n response.statusCode = 204;\n response.end();\n return;\n }\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n response.statusCode = 405;\n response.setHeader('Allow', 'GET, HEAD, PUT');\n response.end();\n return;\n }\n const stored = await storage.load(key, true);\n if (stored === undefined) {\n response.statusCode = 404;\n response.end();\n return;\n }\n response.setHeader('Content-Type', 'application/octet-stream');\n response.setHeader('Content-Length', stored.bytes.byteLength);\n response.setHeader('X-Vitecache-Dependencies', encodeURIComponent(JSON.stringify(stored.hashes)));\n response.end(request.method === 'HEAD' ? undefined : stored.bytes);\n}\n\n/** Persist and validate entries in development; preserve asset URL mappings in production builds. */\nexport function vitecache(options: VitecacheOptions = {}): Plugin {\n let resolved: { backend: Backend; root: string; prefix: string; directory: string; config: ResolvedConfig; cachePath: string } | undefined;\n return {\n name: 'vitecache',\n enforce: 'post',\n async configResolved(config) {\n if (!config.publicDir) throw new Error('vitecache requires Vite publicDir');\n const { root, directory } = disk.location({ root: config.root, ...options });\n const path = relative(config.publicDir, directory);\n if (!path || !inside(config.publicDir, directory)) {\n throw new Error('vitecache basePath must be a directory inside Vite publicDir');\n }\n const base = new URL(config.base, 'http://vitecache').pathname.replace(/\\/$/, '');\n resolved = {\n backend: createStorage({ root, ...options }),\n root,\n directory,\n config,\n cachePath: path.split(sep).join('/'),\n prefix: `${base}/${path.split(sep).map(encodeURIComponent).join('/')}/`,\n };\n await disk.ignore(root, directory);\n },\n async generateBundle(_options, bundle) {\n const storage = resolved;\n if (!storage) throw new Error('vitecache configuration was not resolved');\n const paths = new Map(Object.entries(await publicPaths(storage.config, storage.directory)));\n for (const asset of Object.values(bundle)) {\n if (asset.type !== 'asset') continue;\n const originals = [...new Set(asset.originalFileNames)];\n if (originals.length !== 1) continue;\n const original = originals[0];\n if (!original) continue;\n const file = relative(storage.root, resolve(storage.root, original));\n if (!inside(storage.root, resolve(storage.root, original))) continue;\n paths.set(assetUrl(asset.fileName), file.split(sep).join('/'));\n }\n const manifest = { paths: Object.fromEntries(paths), root: '../'.repeat(storage.cachePath.split('/').length + 1) };\n this.emitFile({ type: 'asset', fileName: `${storage.cachePath}/__vitecache/paths.json`, source: JSON.stringify(manifest) });\n },\n configureServer(server) {\n const storage = resolved;\n if (!storage) throw new Error('vitecache configuration was not resolved');\n server.middlewares.use((request, response, next) => {\n const path = new URL(request.url ?? '/', 'http://vitecache').pathname;\n if (!path.startsWith(storage.prefix)) {\n next();\n return;\n }\n void respond(request, response, path.slice(storage.prefix.length), storage.backend, server).catch((error: unknown) => {\n response.statusCode = status(error);\n response.setHeader('Content-Type', 'text/plain');\n response.end(error instanceof Error ? error.message : 'vitecache request failed');\n });\n });\n },\n };\n}\n\nexport default vitecache;\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { relative, resolve, sep } from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite';\nimport * as disk from '../disk.js';\nimport { ChangedDependenciesError, createStorage } from '../storage.js';\nimport type { Backend } from '../types.js';\nimport { dependencies, hashes, MissingDependencyError, resolvedDependencies, savedDependencies } from '../dependencies.js';\nimport { confine, inside, OutsideRootError, resolveDependencies } from './resolve.js';\nimport { assetUrl, publicPaths } from './manifest.js';\n\nexport interface VitecacheOptions {\n /** Cache directory relative to the Vite project root; must be inside publicDir. */\n basePath?: string;\n}\n\nclass RequestError extends Error {\n constructor(readonly status: number, message: string) {\n super(message);\n }\n}\n\nfunction status(error: unknown): number {\n if (error instanceof RequestError) return error.status;\n if (error instanceof ChangedDependenciesError) return 409;\n if (error instanceof MissingDependencyError) return 404;\n if (error instanceof OutsideRootError) return 403;\n if (error instanceof TypeError || error instanceof URIError) return 400;\n return 500;\n}\n\nfunction allowed(request: IncomingMessage): boolean {\n if (request.headers['sec-fetch-site'] === 'cross-site') return false;\n const origin = request.headers.origin;\n if (!origin) return true;\n try {\n const url = new URL(origin);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.host === request.headers.host;\n } catch {\n return false;\n }\n}\n\nasync function body(request: IncomingMessage): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n for await (const value of request) {\n const chunk: unknown = value;\n if (!Buffer.isBuffer(chunk)) throw new RequestError(400, 'Expected a binary request body');\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n}\n\nfunction parse(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n throw new RequestError(400, 'Expected valid JSON');\n }\n}\n\nfunction validate<T>(value: unknown, parser: (value: unknown) => T): T {\n try {\n return parser(value);\n } catch (error) {\n if (!(error instanceof TypeError)) throw error;\n throw new RequestError(400, error.message);\n }\n}\n\n/** A save body: a 4-byte big-endian metadata length, that much JSON metadata, then the cached bytes. */\nfunction upload(bytes: Uint8Array): { metadata: Record<string, unknown>; value: Uint8Array } {\n if (bytes.byteLength < 4) throw new RequestError(400, 'Expected a metadata length');\n const end = 4 + new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0);\n if (end > bytes.byteLength) throw new RequestError(400, 'Truncated save metadata');\n const metadata = parse(new TextDecoder().decode(bytes.subarray(4, end)));\n if (typeof metadata !== 'object' || metadata === null || !('dependencies' in metadata) || !('hashes' in metadata) || !('saved' in metadata)) {\n throw new RequestError(400, 'Expected dependencies, hashes, and saved dependencies');\n }\n return { metadata, value: bytes.subarray(end) };\n}\n\nfunction json(response: ServerResponse, value: unknown): void {\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(value));\n}\n\nasync function respond(\n request: IncomingMessage,\n response: ServerResponse,\n name: string,\n storage: Backend,\n server: ViteDevServer,\n): Promise<void> {\n const root = server.config.root;\n response.setHeader('Cache-Control', 'no-store');\n if (name === '__vitecache' && request.method === 'GET') {\n json(response, { vitecache: 1 });\n return;\n }\n if ((name === '__vitecache/snapshot' || name === '__vitecache/resolve') && request.method === 'POST') {\n if (!allowed(request)) throw new RequestError(403, 'Cross-origin requests are forbidden');\n const value = parse(new TextDecoder().decode(await body(request)));\n if (typeof value !== 'object' || value === null || !('dependencies' in value)) {\n throw new RequestError(400, 'Expected dependencies');\n }\n if (name === '__vitecache/resolve') {\n const deps = validate(value.dependencies, dependencies);\n const origin = `${server.config.server.https ? 'https' : 'http'}://${request.headers.host}`;\n json(response, { dependencies: await resolveDependencies(server, deps, origin) });\n return;\n }\n const deps = validate(value.dependencies, resolvedDependencies);\n await confine(root, deps);\n json(response, await storage.snapshot(deps));\n return;\n }\n if (!/^[a-f0-9]{64}\\.bin$/.test(name)) {\n response.statusCode = 404;\n response.end();\n return;\n }\n const key = name.slice(0, -4);\n if (request.method === 'PUT') {\n if (!allowed(request)) throw new RequestError(403, 'Cross-origin requests are forbidden');\n const { metadata, value } = upload(await body(request));\n const validation = validate(metadata.dependencies, resolvedDependencies);\n const expected = validate(metadata.hashes, hashes);\n const saved = validate(metadata.saved, (input) => resolvedDependencies(savedDependencies(input)));\n await confine(root, validation);\n await confine(root, saved);\n await storage.save(key, value, validation, expected, saved);\n response.statusCode = 204;\n response.end();\n return;\n }\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n response.statusCode = 405;\n response.setHeader('Allow', 'GET, HEAD, PUT');\n response.end();\n return;\n }\n const stored = await storage.load(key, true);\n if (stored === undefined) {\n // Browsers log every 404 fetch as an error, and a miss is an expected answer.\n response.statusCode = 204;\n response.end();\n return;\n }\n response.setHeader('Content-Type', 'application/octet-stream');\n response.setHeader('Content-Length', stored.bytes.byteLength);\n response.setHeader('X-Vitecache-Dependencies', encodeURIComponent(JSON.stringify(stored.hashes)));\n response.end(request.method === 'HEAD' ? undefined : stored.bytes);\n}\n\n/** Persist and validate entries in development; preserve asset URL mappings in production builds. */\nexport function vitecache(options: VitecacheOptions = {}): Plugin {\n let resolved: { backend: Backend; root: string; prefix: string; directory: string; config: ResolvedConfig; cachePath: string } | undefined;\n return {\n name: 'vitecache',\n enforce: 'post',\n async configResolved(config) {\n if (!config.publicDir) throw new Error('vitecache requires Vite publicDir');\n const { root, directory } = disk.location({ root: config.root, ...options });\n const path = relative(config.publicDir, directory);\n if (!path || !inside(config.publicDir, directory)) {\n throw new Error('vitecache basePath must be a directory inside Vite publicDir');\n }\n const base = new URL(config.base, 'http://vitecache').pathname.replace(/\\/$/, '');\n resolved = {\n backend: createStorage({ root, ...options, memoizeHashes: config.command === 'serve' }),\n root,\n directory,\n config,\n cachePath: path.split(sep).join('/'),\n prefix: `${base}/${path.split(sep).map(encodeURIComponent).join('/')}/`,\n };\n await disk.ignore(root, directory);\n },\n async generateBundle(_options, bundle) {\n const storage = resolved;\n if (!storage) throw new Error('vitecache configuration was not resolved');\n const paths = new Map(Object.entries(await publicPaths(storage.config, storage.directory)));\n for (const asset of Object.values(bundle)) {\n if (asset.type !== 'asset') continue;\n const originals = [...new Set(asset.originalFileNames)];\n if (originals.length !== 1) continue;\n const original = originals[0];\n if (!original) continue;\n const file = relative(storage.root, resolve(storage.root, original));\n if (!inside(storage.root, resolve(storage.root, original))) continue;\n paths.set(assetUrl(asset.fileName), file.split(sep).join('/'));\n }\n const manifest = { paths: Object.fromEntries(paths), root: '../'.repeat(storage.cachePath.split('/').length + 1) };\n this.emitFile({ type: 'asset', fileName: `${storage.cachePath}/__vitecache/paths.json`, source: JSON.stringify(manifest) });\n },\n configureServer(server) {\n const storage = resolved;\n if (!storage) throw new Error('vitecache configuration was not resolved');\n server.middlewares.use((request, response, next) => {\n const path = new URL(request.url ?? '/', 'http://vitecache').pathname;\n if (!path.startsWith(storage.prefix)) {\n next();\n return;\n }\n void respond(request, response, path.slice(storage.prefix.length), storage.backend, server).catch((error: unknown) => {\n response.statusCode = status(error);\n if (error instanceof MissingDependencyError) {\n json(response, { error: 'missing-dependency', message: error.message });\n return;\n }\n response.setHeader('Content-Type', 'text/plain');\n response.end(error instanceof Error ? error.message : 'vitecache request failed');\n });\n });\n },\n };\n}\n\nexport default vitecache;\n"]}
|
package/dist/vite/resolve.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { realpath, stat } from 'node:fs/promises';
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import * as disk from '../disk.js';
|
|
4
|
+
import { MissingDependencyError } from '../dependencies.js';
|
|
4
5
|
export class OutsideRootError extends Error {
|
|
5
6
|
constructor() {
|
|
6
7
|
super('File dependencies must be inside the project root');
|
|
@@ -63,12 +64,13 @@ export async function resolveDependencies(server, dependencies, origin) {
|
|
|
63
64
|
file = rootFile;
|
|
64
65
|
else {
|
|
65
66
|
const module = await server.pluginContainer.resolveId(pathname);
|
|
67
|
+
// A virtual module is served without a file whose contents could be hashed.
|
|
66
68
|
if (!module || !isAbsolute(module.id))
|
|
67
|
-
throw new
|
|
69
|
+
throw new MissingDependencyError(`File dependency URL has no backing file: ${dependency.url}`);
|
|
68
70
|
file = module.id.split('?')[0] ?? module.id;
|
|
69
71
|
}
|
|
70
72
|
if (!await exists(file))
|
|
71
|
-
throw new
|
|
73
|
+
throw new MissingDependencyError(`File dependency URL has no backing file: ${dependency.url}`);
|
|
72
74
|
const path = inside(server.config.root, file)
|
|
73
75
|
? relative(server.config.root, file)
|
|
74
76
|
: relative(await realpath(server.config.root), await realpath(file));
|
package/dist/vite/resolve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/vite/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,KAAK,IAAI,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/vite/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath, stat } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve, sep } from 'node:path';\nimport type { ViteDevServer } from 'vite';\nimport type { Dependencies, ResolvedDependencies, ResolvedDependency } from '../types.js';\nimport * as disk from '../disk.js';\nimport { MissingDependencyError } from '../dependencies.js';\n\nexport class OutsideRootError extends Error {\n constructor() {\n super('File dependencies must be inside the project root');\n }\n}\n\nexport function inside(root: string, path: string): boolean {\n const name = relative(root, path);\n return name !== '..' && !name.startsWith(`..${sep}`) && !isAbsolute(name);\n}\n\nexport async function confine(root: string, dependencies: ResolvedDependencies): Promise<void> {\n const canonicalRoot = await realpath(root);\n for (const dependency of Object.values(dependencies)) {\n if (!('path' in dependency)) continue;\n const path = resolve(root, dependency.path);\n if (!inside(root, path)) throw new OutsideRootError();\n try {\n if (!inside(canonicalRoot, await realpath(path))) throw new OutsideRootError();\n } catch (error) {\n if (!disk.missingDependency(error)) throw error;\n }\n }\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch (error) {\n if (!disk.missingDependency(error)) throw error;\n return false;\n }\n}\n\nexport async function resolveDependencies(server: ViteDevServer, dependencies: Dependencies, origin: string): Promise<ResolvedDependencies> {\n const entries: [string, ResolvedDependency][] = [];\n const base = new URL(server.config.base, origin).pathname;\n for (const [name, dependency] of Object.entries(dependencies)) {\n if (!('url' in dependency)) {\n entries.push([name, dependency]);\n continue;\n }\n const url = new URL(dependency.url, origin);\n if (url.origin !== origin) throw new TypeError('File dependency URLs must have the Vite server origin');\n if (!url.pathname.startsWith(base)) throw new TypeError('File dependency URL is outside the Vite base');\n const pathname = decodeURIComponent(`/${url.pathname.slice(base.length)}`);\n const publicFile = resolve(server.config.publicDir, `.${pathname}`);\n const rootFile = resolve(server.config.root, `.${pathname}`);\n let file: string;\n if (server.config.publicDir && await exists(publicFile)) file = publicFile;\n else if (pathname.startsWith('/@fs/')) file = pathname.slice('/@fs'.length).replace(/^\\/([A-Za-z]:\\/)/, '$1');\n else if (await exists(rootFile)) file = rootFile;\n else {\n const module = await server.pluginContainer.resolveId(pathname);\n // A virtual module is served without a file whose contents could be hashed.\n if (!module || !isAbsolute(module.id)) throw new MissingDependencyError(`File dependency URL has no backing file: ${dependency.url}`);\n file = module.id.split('?')[0] ?? module.id;\n }\n if (!await exists(file)) throw new MissingDependencyError(`File dependency URL has no backing file: ${dependency.url}`);\n const path = inside(server.config.root, file)\n ? relative(server.config.root, file)\n : relative(await realpath(server.config.root), await realpath(file));\n entries.push([name, { path: path.split(sep).join('/') }]);\n }\n const resolved = Object.fromEntries(entries);\n await confine(server.config.root, resolved);\n return resolved;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ Give dependencies stable names. Use `{ path: "src/simplify.ts" }` for a project-
|
|
|
11
11
|
|
|
12
12
|
Reordering JSON object properties or dependency declarations does not change identity; array order matters. Changing a dependency name, JSON value, resolved path, or package name does. File contents and installed package versions invalidate the existing result when filesystem validation is available; installation paths do not affect identity.
|
|
13
13
|
|
|
14
|
-
Load once and compute on a miss. Reuse that entry when saving: loading captures file contents and package versions before computation, allowing saving to reject a result if those dependencies changed in the meantime. A new entry or a save without a preceding load cannot establish that relationship. Entries containing only value dependencies can save directly. If saving fails because dependencies changed, load again and recompute before retrying. Use a separate entry for each concurrent computation.
|
|
14
|
+
Load once and compute on a miss. Reuse that entry when saving: loading captures file contents and package versions before computation, allowing saving to reject a result if those dependencies changed in the meantime. A new entry or a save without a preceding load cannot establish that relationship. Entries containing only value dependencies can save directly. If saving fails because dependencies changed, load again and recompute before retrying. A dependency without a backing file, such as a server route, a virtual module or a package the app does not install, makes `save()` throw `MissingDependencyError`; catch it to carry on with the uncached result. Use a separate entry for each concurrent computation.
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
17
|
import { CacheEntry } from "vitecache"
|
|
@@ -36,6 +36,18 @@ if (bytes === undefined) {
|
|
|
36
36
|
const mesh = decodeMesh(bytes)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
Constructor dependencies select which result an entry holds. When some inputs are only discovered while computing, such as the files a script imported or the textures a model referenced, pass them to `save(bytes, { dependencies })` instead, like a compiler's depfile: paths, URLs and packages only, since anything that selects the result belongs in the constructor. Every later `load()` rechecks them and misses when one changed or disappeared, and the next save replaces the same entry. They are hashed when `save()` runs rather than when the computation read them, so an edit made during a long computation goes unnoticed until the file changes again.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const entry = new CacheEntry({ version: "bake@1", dependencies: { scene: { url: sceneUrl } } })
|
|
43
|
+
let bytes = await entry.load()
|
|
44
|
+
if (bytes === undefined) {
|
|
45
|
+
const { result, files } = await bake(sceneUrl) // files: URLs the bake read
|
|
46
|
+
bytes = encode(result)
|
|
47
|
+
await entry.save(bytes, { dependencies: Object.fromEntries(files.map((url, i) => [`file:${i}`, { url }])) })
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
39
51
|
Use the Vite plugin when browser computation should produce reusable project files:
|
|
40
52
|
|
|
41
53
|
```ts
|
|
@@ -45,6 +57,6 @@ import { vitecache } from "vitecache/vite"
|
|
|
45
57
|
export default defineConfig({ plugins: [vitecache()] })
|
|
46
58
|
```
|
|
47
59
|
|
|
48
|
-
Generate caches during development or from Node before building when deployed users should download precomputed results. Include the generated public files in the build or deployment even when Git ignores them. The plugin writes files during development; it does not supply a production write server. Browsers without the plugin assume files and package versions unchanged, so regenerate affected caches before deployment. Calling `save()` there warns and skips persistence.
|
|
60
|
+
Generate caches during development or from Node before building when deployed users should download precomputed results. Include the generated public files in the build or deployment even when Git ignores them. The plugin writes files during development; it does not supply a production write server. Browsers without the plugin assume files and package versions unchanged, saved dependencies included, so regenerate affected caches before deployment. Calling `save()` there warns and skips persistence. The plugin answers a missing entry without an HTTP error, but a static host answers it with 404, which the browser console shows; each one marks a result the deployment lacks, so precompute what deployed users will request.
|
|
49
61
|
|
|
50
62
|
For custom locations, coordinate the filesystem destination with the URL used by browser callers. A filesystem path and a browser URL are different values. When deploying below a URL prefix, include that prefix in the browser cache path; the client does not infer Vite's base URL. Node jobs should select the same project root as the Vite app when launched from a different working directory.
|