vitecache 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +17 -22
  2. package/dist/browser.d.ts +9 -6
  3. package/dist/browser.js +28 -178
  4. package/dist/browser.js.map +1 -1
  5. package/dist/discover.d.ts +15 -0
  6. package/dist/discover.js +131 -0
  7. package/dist/discover.js.map +1 -0
  8. package/dist/disk.d.ts +2 -26
  9. package/dist/disk.js +8 -107
  10. package/dist/disk.js.map +1 -1
  11. package/dist/entry.d.ts +29 -17
  12. package/dist/entry.js +11 -114
  13. package/dist/entry.js.map +1 -1
  14. package/dist/node.d.ts +9 -6
  15. package/dist/node.js +101 -6
  16. package/dist/node.js.map +1 -1
  17. package/dist/shared.d.ts +17 -0
  18. package/dist/shared.js +5 -0
  19. package/dist/shared.js.map +1 -0
  20. package/dist/vite.d.ts +12 -0
  21. package/dist/vite.js +253 -0
  22. package/dist/vite.js.map +1 -0
  23. package/package.json +17 -13
  24. package/skills/vitecache/SKILL.md +41 -46
  25. package/dist/dependencies.d.ts +0 -23
  26. package/dist/dependencies.js +0 -147
  27. package/dist/dependencies.js.map +0 -1
  28. package/dist/hash.d.ts +0 -4
  29. package/dist/hash.js +0 -41
  30. package/dist/hash.js.map +0 -1
  31. package/dist/index.d.ts +0 -1
  32. package/dist/index.js +0 -2
  33. package/dist/index.js.map +0 -1
  34. package/dist/key.d.ts +0 -3
  35. package/dist/key.js +0 -10
  36. package/dist/key.js.map +0 -1
  37. package/dist/packages.d.ts +0 -6
  38. package/dist/packages.js +0 -52
  39. package/dist/packages.js.map +0 -1
  40. package/dist/prune.d.ts +0 -11
  41. package/dist/prune.js +0 -71
  42. package/dist/prune.js.map +0 -1
  43. package/dist/storage.d.ts +0 -12
  44. package/dist/storage.js +0 -95
  45. package/dist/storage.js.map +0 -1
  46. package/dist/types.d.ts +0 -89
  47. package/dist/types.js +0 -2
  48. package/dist/types.js.map +0 -1
  49. package/dist/vite/index.d.ts +0 -8
  50. package/dist/vite/index.js +0 -220
  51. package/dist/vite/index.js.map +0 -1
  52. package/dist/vite/manifest.d.ts +0 -3
  53. package/dist/vite/manifest.js +0 -34
  54. package/dist/vite/manifest.js.map +0 -1
  55. package/dist/vite/resolve.d.ts +0 -9
  56. package/dist/vite/resolve.js +0 -90
  57. package/dist/vite/resolve.js.map +0 -1
@@ -1,147 +0,0 @@
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
- }
5
- /** A file or package dependency changed between load() and save(), so the result may be built from stale inputs. */
6
- export class ChangedDependenciesError extends Error {
7
- name = 'ChangedDependenciesError';
8
- constructor() {
9
- super('vitecache dependencies changed during computation.');
10
- }
11
- }
12
- /** A plain object of named entries, sorted by name: dependencies, their values, paths or hashes. */
13
- function record(value) {
14
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
15
- throw new TypeError('Expected an object of named entries.');
16
- }
17
- const prototype = Object.getPrototypeOf(value);
18
- if (prototype !== Object.prototype && prototype !== null)
19
- throw new TypeError('Expected a plain object of named entries.');
20
- if (Object.getOwnPropertySymbols(value).length)
21
- throw new TypeError('Entry names must be strings.');
22
- return Object.fromEntries(Object.keys(value).sort().map((name) => {
23
- const property = Object.getOwnPropertyDescriptor(value, name);
24
- if (!property || !('value' in property))
25
- throw new TypeError('Getters are not supported.');
26
- const item = property.value;
27
- return [name, item];
28
- }));
29
- }
30
- function path(value) {
31
- if (typeof value !== 'string' || !value || value.includes('\0'))
32
- throw new TypeError('File dependencies require nonempty paths.');
33
- if (/^(?:[\\/]|[a-z]:|[a-z][a-z\d+.-]*:\/\/)/i.test(value)) {
34
- throw new TypeError('File dependency paths must be relative to the project root.');
35
- }
36
- const parts = [];
37
- for (const part of value.replace(/\\/g, '/').split('/')) {
38
- if (!part || part === '.')
39
- continue;
40
- if (part !== '..') {
41
- parts.push(part);
42
- continue;
43
- }
44
- if (!parts.length)
45
- throw new TypeError('File dependency paths must stay inside the project root.');
46
- parts.pop();
47
- }
48
- if (!parts.length)
49
- throw new TypeError('File dependencies must identify files, not the project root.');
50
- return parts.join('/');
51
- }
52
- export function paths(value) {
53
- return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, value]) => {
54
- if (!name)
55
- throw new TypeError('File dependencies require nonempty names.');
56
- return [name, path(value)];
57
- })));
58
- }
59
- /** Validate and snapshot JSON before asynchronous dependency resolution starts. */
60
- export function json(value, ancestors = new Set()) {
61
- if (value === null || typeof value === 'string' || typeof value === 'boolean')
62
- return value;
63
- if (typeof value === 'number' && Number.isFinite(value))
64
- return value;
65
- if (typeof value !== 'object')
66
- throw new TypeError('Dependency values must contain only JSON values.');
67
- if (ancestors.has(value))
68
- throw new TypeError('Dependency values must not contain cycles.');
69
- ancestors.add(value);
70
- try {
71
- if (!Array.isArray(value))
72
- return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, item]) => [name, json(item, ancestors)])));
73
- if (Object.getOwnPropertySymbols(value).length)
74
- throw new TypeError('Dependency values must not contain symbol properties.');
75
- const items = [];
76
- for (let index = 0; index < value.length; index++) {
77
- const property = Object.getOwnPropertyDescriptor(value, index);
78
- if (!property || !('value' in property))
79
- throw new TypeError('Dependency values must not contain sparse arrays or getters.');
80
- const item = property.value;
81
- items.push(json(item, ancestors));
82
- }
83
- return Object.freeze(items);
84
- }
85
- finally {
86
- ancestors.delete(value);
87
- }
88
- }
89
- export function dependencies(value) {
90
- const entries = Object.entries(record(value)).map(([name, value]) => {
91
- if (!name)
92
- throw new TypeError('Dependencies require nonempty names.');
93
- const source = record(value);
94
- if (Object.keys(source).length !== 1)
95
- throw new TypeError('Each dependency must have exactly one value, path, url, or package.');
96
- if (Object.hasOwn(source, 'value'))
97
- return [name, Object.freeze({ value: json(source.value) })];
98
- if (Object.hasOwn(source, 'path'))
99
- return [name, Object.freeze({ path: path(source.path) })];
100
- if (Object.hasOwn(source, 'url') && typeof source.url === 'string' && source.url && !source.url.includes('\0')) {
101
- return [name, Object.freeze({ url: source.url })];
102
- }
103
- 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)) {
104
- return [name, Object.freeze({ package: source.package })];
105
- }
106
- throw new TypeError('Each dependency must have a JSON value, root-relative path, nonempty URL, or package name without a subpath.');
107
- });
108
- return Object.freeze(Object.fromEntries(entries));
109
- }
110
- function only(value, keep, message) {
111
- return Object.freeze(Object.fromEntries(Object.entries(value).map(([name, dependency]) => {
112
- if (!keep(dependency))
113
- throw new TypeError(message);
114
- return [name, dependency];
115
- })));
116
- }
117
- function isResolved(dependency) {
118
- return dependency.url === undefined;
119
- }
120
- export function isChecked(dependency) {
121
- return dependency.value === undefined;
122
- }
123
- /** Dependencies after resolution, which turns URLs into paths. */
124
- export function resolvedDependencies(value) {
125
- return only(dependencies(value), isResolved, 'Expected resolved dependencies, not URLs.');
126
- }
127
- /** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */
128
- export function savedDependencies(value) {
129
- return only(dependencies(value), isChecked, 'Saved dependencies cannot be values: values select the result; pass them to the constructor.');
130
- }
131
- /** Resolved file and package dependencies: the ones whose current state can be hashed. */
132
- export function hashableDependencies(value) {
133
- return only(dependencies(value), (dependency) => isChecked(dependency) && isResolved(dependency), 'Expected file paths and packages, not values or URLs.');
134
- }
135
- export function hashes(value) {
136
- return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, hash]) => {
137
- if (!name || typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash)) {
138
- throw new TypeError('Invalid file dependency hash.');
139
- }
140
- return [name, hash];
141
- })));
142
- }
143
- export function equalHashes(first, second) {
144
- const names = Object.keys(first);
145
- return names.length === Object.keys(second).length && names.every((name) => first[name] === second[name]);
146
- }
147
- //# sourceMappingURL=dependencies.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../src/dependencies.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Checked, Dependencies, Dependency, Hashable, Hashes, JsonValue, Paths, Resolved, SavedDependencies } 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\n/** A file or package dependency changed between load() and save(), so the result may be built from stale inputs. */\nexport class ChangedDependenciesError extends Error {\n override name = 'ChangedDependenciesError';\n constructor() {\n super('vitecache dependencies changed during computation.');\n }\n}\n\n/** A plain object of named entries, sorted by name: dependencies, their values, paths or hashes. */\nfunction record(value: unknown): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new TypeError('Expected an object of named entries.');\n }\n const prototype: unknown = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) throw new TypeError('Expected a plain object of named entries.');\n if (Object.getOwnPropertySymbols(value).length) throw new TypeError('Entry 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('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\nfunction only<D extends Dependency, K extends D>(value: Dependencies<D>, keep: (dependency: D) => dependency is K, message: string): Dependencies<K> {\n return Object.freeze(Object.fromEntries(Object.entries(value).map(([name, dependency]) => {\n if (!keep(dependency)) throw new TypeError(message);\n return [name, dependency] as const;\n })));\n}\n\nfunction isResolved<D extends Dependency>(dependency: D): dependency is Resolved<D> {\n return dependency.url === undefined;\n}\n\nexport function isChecked<D extends Dependency>(dependency: D): dependency is Checked<D> {\n return dependency.value === undefined;\n}\n\n/** Dependencies after resolution, which turns URLs into paths. */\nexport function resolvedDependencies(value: unknown): Dependencies<Resolved> {\n return only(dependencies(value), isResolved, 'Expected resolved dependencies, not URLs.');\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 return only(dependencies(value), isChecked, 'Saved dependencies cannot be values: values select the result; pass them to the constructor.');\n}\n\n/** Resolved file and package dependencies: the ones whose current state can be hashed. */\nexport function hashableDependencies(value: unknown): Dependencies<Hashable> {\n return only(dependencies(value), (dependency): dependency is Hashable => isChecked(dependency) && isResolved(dependency), 'Expected file paths and packages, not values or URLs.');\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/hash.d.ts DELETED
@@ -1,4 +0,0 @@
1
- /** SHA-256 hex, computed natively since files can be large. */
2
- export declare function contentHash(bytes: Uint8Array): string;
3
- /** The content hash of the file at absolute `path`, or undefined when it does not exist. */
4
- export declare function fileHash(path: string): Promise<string | undefined>;
package/dist/hash.js DELETED
@@ -1,41 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { stat } from 'node:fs/promises';
3
- import * as disk from './disk.js';
4
- /** SHA-256 hex, computed natively since files can be large. */
5
- export function contentHash(bytes) {
6
- return createHash('sha256').update(bytes).digest('hex');
7
- }
8
- /**
9
- * Content hashes of dependency files by absolute path, shared by every storage in the process, so that each
10
- * `CacheEntry` does not read and hash a large file again. Reused while the file's identity, size, mtime and ctime are
11
- * unchanged. As git does for its index, a hash taken within `RACY_NS` of the file's mtime is not kept: a write in the
12
- * same timestamp tick could follow it unseen. That compares the process's clock with the filesystem's: one whose clock
13
- * runs more than `RACY_NS` behind, as a network filesystem's can, may have a hash reused across such a write.
14
- *
15
- * Nothing evicts a hash except a failed read, so a long-running server holds one per dependency file it ever hashed.
16
- */
17
- const fileHashes = new Map();
18
- const RACY_NS = 2000000000n;
19
- /** The content hash of the file at absolute `path`, or undefined when it does not exist. */
20
- export async function fileHash(path) {
21
- try {
22
- const { dev, ino, size, mtimeNs, ctimeNs } = await stat(path, { bigint: true });
23
- const stats = [dev, ino, size, mtimeNs, ctimeNs].join();
24
- const known = fileHashes.get(path);
25
- if (known?.stats === stats)
26
- return await known.hash;
27
- const hash = disk.load(path).then((bytes) => bytes === undefined ? undefined : contentHash(bytes));
28
- if (BigInt(Date.now()) * 1000000n - mtimeNs < RACY_NS)
29
- fileHashes.delete(path);
30
- else
31
- fileHashes.set(path, { stats, hash });
32
- return await hash;
33
- }
34
- catch (error) {
35
- fileHashes.delete(path);
36
- if (disk.notFound(error))
37
- return undefined;
38
- throw error;
39
- }
40
- }
41
- //# sourceMappingURL=hash.js.map
package/dist/hash.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,+DAA+D;AAC/D,MAAM,UAAU,WAAW,CAAC,KAAiB;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgE,CAAC;AAC3F,MAAM,OAAO,GAAG,WAAc,CAAC;AAE/B,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,EAAE,KAAK,KAAK,KAAK;YAAE,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QACnG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,QAAU,GAAG,OAAO,GAAG,OAAO;YAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;YAC5E,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,OAAO,MAAM,IAAI,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import { createHash } from 'node:crypto';\nimport { stat } from 'node:fs/promises';\nimport * as disk from './disk.js';\n\n/** SHA-256 hex, computed natively since files can be large. */\nexport function contentHash(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex');\n}\n\n/**\n * Content hashes of dependency files by absolute path, shared by every storage in the process, so that each\n * `CacheEntry` does not read and hash a large file again. Reused while the file's identity, size, mtime and ctime are\n * unchanged. As git does for its index, a hash taken within `RACY_NS` of the file's mtime is not kept: a write in the\n * same timestamp tick could follow it unseen. That compares the process's clock with the filesystem's: one whose clock\n * runs more than `RACY_NS` behind, as a network filesystem's can, may have a hash reused across such a write.\n *\n * Nothing evicts a hash except a failed read, so a long-running server holds one per dependency file it ever hashed.\n */\nconst fileHashes = new Map<string, { stats: string; hash: Promise<string | undefined> }>();\nconst RACY_NS = 2_000_000_000n;\n\n/** The content hash of the file at absolute `path`, or undefined when it does not exist. */\nexport async function fileHash(path: string): Promise<string | undefined> {\n try {\n const { dev, ino, size, mtimeNs, ctimeNs } = await stat(path, { bigint: true });\n const stats = [dev, ino, size, mtimeNs, ctimeNs].join();\n const known = fileHashes.get(path);\n if (known?.stats === stats) return await known.hash;\n const hash = disk.load(path).then((bytes) => bytes === undefined ? undefined : contentHash(bytes));\n if (BigInt(Date.now()) * 1_000_000n - mtimeNs < RACY_NS) fileHashes.delete(path);\n else fileHashes.set(path, { stats, hash });\n return await hash;\n } catch (error) {\n fileHashes.delete(path);\n if (disk.notFound(error)) return undefined;\n throw error;\n }\n}\n"]}
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './browser.js';
package/dist/index.js DELETED
@@ -1,2 +0,0 @@
1
- export * from './browser.js';
2
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC","sourcesContent":["export * from './browser.js';\n"]}
package/dist/key.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import type { Dependencies, Resolved } from './types.js';
2
- export declare function digest(bytes: Uint8Array): string;
3
- export declare function key(version: string, dependencies: Dependencies<Resolved>): string;
package/dist/key.js DELETED
@@ -1,10 +0,0 @@
1
- import { sha256 } from '@noble/hashes/sha2.js';
2
- import { bytesToHex } from '@noble/hashes/utils.js';
3
- import { json } from './dependencies.js';
4
- export function digest(bytes) {
5
- return bytesToHex(sha256(bytes));
6
- }
7
- export function key(version, dependencies) {
8
- return digest(new TextEncoder().encode(JSON.stringify(json({ version, dependencies }))));
9
- }
10
- //# sourceMappingURL=key.js.map
package/dist/key.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"key.js","sourceRoot":"","sources":["../src/key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAGzC,MAAM,UAAU,MAAM,CAAC,KAAiB;IACtC,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,YAAoC;IACvE,OAAO,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import { sha256 } from '@noble/hashes/sha2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\nimport { json } from './dependencies.js';\nimport type { Dependencies, Resolved } from './types.js';\n\nexport function digest(bytes: Uint8Array): string {\n return bytesToHex(sha256(bytes));\n}\n\nexport function key(version: string, dependencies: Dependencies<Resolved>): string {\n return digest(new TextEncoder().encode(JSON.stringify(json({ version, dependencies }))));\n}\n"]}
@@ -1,6 +0,0 @@
1
- /**
2
- * The installed version of package `name` as seen from `root`. The project at `root` resolves its own name to itself
3
- * before node_modules. Node does that only when its package.json declares `exports`; this does without, as a version
4
- * needs no entry point.
5
- */
6
- export declare function packageVersion(root: string, name: string): Promise<string | undefined>;
package/dist/packages.js DELETED
@@ -1,52 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { createRequire } from 'node:module';
3
- import { join } from 'node:path';
4
- import { notFound } from './disk.js';
5
- async function readManifest(directory) {
6
- const file = join(directory, 'package.json');
7
- let contents;
8
- try {
9
- contents = await readFile(file, 'utf8');
10
- }
11
- catch (error) {
12
- if (!notFound(error))
13
- throw error;
14
- return undefined;
15
- }
16
- let manifest;
17
- try {
18
- manifest = JSON.parse(contents);
19
- }
20
- catch (cause) {
21
- throw new Error(`Invalid package manifest: ${file}`, { cause });
22
- }
23
- if (typeof manifest !== 'object' || manifest === null)
24
- throw new TypeError(`Expected an object in ${file}.`);
25
- return manifest;
26
- }
27
- function version(name, manifest) {
28
- if (typeof manifest.version !== 'string' || !manifest.version.trim()) {
29
- throw new TypeError(`Package ${name} must declare a nonempty version in package.json.`);
30
- }
31
- return manifest.version;
32
- }
33
- /**
34
- * The installed version of package `name` as seen from `root`. The project at `root` resolves its own name to itself
35
- * before node_modules. Node does that only when its package.json declares `exports`; this does without, as a version
36
- * needs no entry point.
37
- */
38
- export async function packageVersion(root, name) {
39
- const own = await readManifest(root);
40
- if (own?.name === name)
41
- return version(name, own);
42
- const directories = createRequire(join(root, 'package.json')).resolve.paths(name);
43
- if (directories === null)
44
- throw new TypeError(`Expected an installed package: ${name}`);
45
- for (const directory of directories) {
46
- const manifest = await readManifest(join(directory, name));
47
- if (manifest !== undefined)
48
- return version(name, manifest);
49
- }
50
- return undefined;
51
- }
52
- //# sourceMappingURL=packages.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAIrC,KAAK,UAAU,YAAY,CAAC,SAAiB;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC7C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,IAAI,GAAG,CAAC,CAAC;IAC7G,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,QAAkB;IAC/C,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,WAAW,IAAI,mDAAmD,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,IAAY;IAC7D,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,GAAG,EAAE,IAAI,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClF,IAAI,WAAW,KAAK,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,IAAI,EAAE,CAAC,CAAC;IACxF,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3D,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { notFound } from './disk.js';\n\ntype Manifest = { name?: unknown; version?: unknown };\n\nasync function readManifest(directory: string): Promise<Manifest | undefined> {\n const file = join(directory, 'package.json');\n let contents: string;\n try {\n contents = await readFile(file, 'utf8');\n } catch (error) {\n if (!notFound(error)) throw error;\n return undefined;\n }\n let manifest: unknown;\n try {\n manifest = JSON.parse(contents);\n } catch (cause) {\n throw new Error(`Invalid package manifest: ${file}`, { cause });\n }\n if (typeof manifest !== 'object' || manifest === null) throw new TypeError(`Expected an object in ${file}.`);\n return manifest;\n}\n\nfunction version(name: string, manifest: Manifest): string {\n if (typeof manifest.version !== 'string' || !manifest.version.trim()) {\n throw new TypeError(`Package ${name} must declare a nonempty version in package.json.`);\n }\n return manifest.version;\n}\n\n/**\n * The installed version of package `name` as seen from `root`. The project at `root` resolves its own name to itself\n * before node_modules. Node does that only when its package.json declares `exports`; this does without, as a version\n * needs no entry point.\n */\nexport async function packageVersion(root: string, name: string): Promise<string | undefined> {\n const own = await readManifest(root);\n if (own?.name === name) return version(name, own);\n const directories = createRequire(join(root, 'package.json')).resolve.paths(name);\n if (directories === null) throw new TypeError(`Expected an installed package: ${name}`);\n for (const directory of directories) {\n const manifest = await readManifest(join(directory, name));\n if (manifest !== undefined) return version(name, manifest);\n }\n return undefined;\n}\n"]}
package/dist/prune.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import type { CacheEntryOptions } from './types.js';
2
- export interface PruneOptions extends Pick<CacheEntryOptions, 'root' | 'basePath'> {
3
- /** Entries not loaded or saved since this time are deleted. */
4
- before: Date;
5
- }
6
- /**
7
- * Deletes cache entries that no load or save used since `before` and returns their keys. Also deletes files that saves
8
- * staged before it, which no save will finish. Loads in Node and through the Vite development server count as use;
9
- * deployed browsers do not. Other files in the cache directory are kept.
10
- */
11
- export declare function prune(options: PruneOptions): Promise<string[]>;
package/dist/prune.js DELETED
@@ -1,71 +0,0 @@
1
- import { readdir, stat, unlink } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import * as disk from './disk.js';
4
- /** An entry's bytes or metadata. */
5
- const entryFile = /^([a-f0-9]{64})\.(?:bin|json)$/;
6
- /** Either one staged by a save, unfinished while recent and abandoned once old. */
7
- const stagedFile = /^[a-f0-9]{64}\.(?:bin|json)\.[^.]+\.tmp$/;
8
- /**
9
- * Deletes cache entries that no load or save used since `before` and returns their keys. Also deletes files that saves
10
- * staged before it, which no save will finish. Loads in Node and through the Vite development server count as use;
11
- * deployed browsers do not. Other files in the cache directory are kept.
12
- */
13
- export async function prune(options) {
14
- const time = options.before.getTime();
15
- if (Number.isNaN(time))
16
- throw new TypeError('prune requires a valid before date.');
17
- // Some filesystems store whole seconds, so a use just after `before` can be recorded just before it.
18
- const cutoff = Math.floor(time / 1000) * 1000;
19
- const { directory } = disk.location(options);
20
- let names;
21
- try {
22
- names = await readdir(directory);
23
- }
24
- catch (error) {
25
- if (!disk.missing(error))
26
- throw error;
27
- return [];
28
- }
29
- const files = await Promise.all(names.filter((name) => entryFile.test(name) || stagedFile.test(name)).map(async (name) => ({
30
- name,
31
- modified: await modified(join(directory, name)),
32
- })));
33
- const abandoned = [];
34
- const entries = new Map();
35
- for (const { name, modified } of files) {
36
- const key = entryFile.exec(name)?.[1];
37
- if (key === undefined) {
38
- if (modified < cutoff)
39
- abandoned.push(name);
40
- continue;
41
- }
42
- const entry = entries.get(key) ?? { files: [], used: -Infinity };
43
- entries.set(key, entry);
44
- entry.files.push(name);
45
- entry.used = Math.max(entry.used, modified);
46
- }
47
- const unused = [...entries].filter(([, { used }]) => used < cutoff);
48
- await Promise.all([...abandoned, ...unused.flatMap(([, entry]) => entry.files)].map((name) => remove(join(directory, name))));
49
- return unused.map(([key]) => key);
50
- }
51
- async function modified(path) {
52
- try {
53
- return (await stat(path)).mtimeMs;
54
- }
55
- catch (error) {
56
- if (!disk.missing(error))
57
- throw error;
58
- // A save renamed or removed it since the listing, so it is in use.
59
- return Infinity;
60
- }
61
- }
62
- async function remove(path) {
63
- try {
64
- await unlink(path);
65
- }
66
- catch (error) {
67
- if (!disk.missing(error))
68
- throw error;
69
- }
70
- }
71
- //# sourceMappingURL=prune.js.map
package/dist/prune.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"prune.js","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, stat, unlink } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as disk from './disk.js';\nimport type { CacheEntryOptions } from './types.js';\n\nexport interface PruneOptions extends Pick<CacheEntryOptions, 'root' | 'basePath'> {\n /** Entries not loaded or saved since this time are deleted. */\n before: Date;\n}\n\n/** An entry's bytes or metadata. */\nconst entryFile = /^([a-f0-9]{64})\\.(?:bin|json)$/;\n/** Either one staged by a save, unfinished while recent and abandoned once old. */\nconst stagedFile = /^[a-f0-9]{64}\\.(?:bin|json)\\.[^.]+\\.tmp$/;\n\n/**\n * Deletes cache entries that no load or save used since `before` and returns their keys. Also deletes files that saves\n * staged before it, which no save will finish. Loads in Node and through the Vite development server count as use;\n * deployed browsers do not. Other files in the cache directory are kept.\n */\nexport async function prune(options: PruneOptions): Promise<string[]> {\n const time = options.before.getTime();\n if (Number.isNaN(time)) throw new TypeError('prune requires a valid before date.');\n // Some filesystems store whole seconds, so a use just after `before` can be recorded just before it.\n const cutoff = Math.floor(time / 1000) * 1000;\n const { directory } = disk.location(options);\n let names: string[];\n try {\n names = await readdir(directory);\n } catch (error) {\n if (!disk.missing(error)) throw error;\n return [];\n }\n const files = await Promise.all(names.filter((name) => entryFile.test(name) || stagedFile.test(name)).map(async (name) => ({\n name,\n modified: await modified(join(directory, name)),\n })));\n const abandoned: string[] = [];\n const entries = new Map<string, { files: string[]; used: number }>();\n for (const { name, modified } of files) {\n const key = entryFile.exec(name)?.[1];\n if (key === undefined) {\n if (modified < cutoff) abandoned.push(name);\n continue;\n }\n const entry = entries.get(key) ?? { files: [], used: -Infinity };\n entries.set(key, entry);\n entry.files.push(name);\n entry.used = Math.max(entry.used, modified);\n }\n const unused = [...entries].filter(([, { used }]) => used < cutoff);\n await Promise.all([...abandoned, ...unused.flatMap(([, entry]) => entry.files)].map((name) => remove(join(directory, name))));\n return unused.map(([key]) => key);\n}\n\nasync function modified(path: string): Promise<number> {\n try {\n return (await stat(path)).mtimeMs;\n } catch (error) {\n if (!disk.missing(error)) throw error;\n // A save renamed or removed it since the listing, so it is in use.\n return Infinity;\n }\n}\n\nasync function remove(path: string): Promise<void> {\n try {\n await unlink(path);\n } catch (error) {\n if (!disk.missing(error)) throw error;\n }\n}\n"]}
package/dist/storage.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import type { Backend, CacheEntryOptions, Hashes, Stored } from './types.js';
2
- /** A stored entry as storage reads it, its hashes always known. */
3
- export interface Found extends Stored {
4
- /** The entity tag's opaque value: it changes whenever the entry's bytes or dependency hashes do. */
5
- tag: string;
6
- hashes: Hashes;
7
- }
8
- export interface Storage extends Backend {
9
- /** Reads a valid entry and records its use for prune(). */
10
- load(key: string): Promise<Found | undefined>;
11
- }
12
- export declare function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'>): Storage;
package/dist/storage.js DELETED
@@ -1,95 +0,0 @@
1
- import { join, resolve } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import * as disk from './disk.js';
4
- import { ChangedDependenciesError, equalHashes, hashableDependencies, hashes, MissingDependencyError, resolvedDependencies } from './dependencies.js';
5
- import { contentHash, fileHash } from './hash.js';
6
- import { packageVersion } from './packages.js';
7
- /** A metadata file vitecache wrote; any failure to read it names the file, since no request caused it. */
8
- function metadata(bytes, file) {
9
- try {
10
- const value = JSON.parse(new TextDecoder().decode(bytes));
11
- if (typeof value !== 'object' || value === null || !('contentHash' in value) || !('dependencies' in value)) {
12
- throw new Error('Expected contentHash and dependencies.');
13
- }
14
- const hash = value.contentHash;
15
- if (typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash))
16
- throw new Error('Expected a content hash.');
17
- const validated = hashes(value.dependencies);
18
- // Metadata without saved dependencies: all that vitecache 0.1 wrote, and what 0.2 wrote for entries that had none.
19
- if (!('saved' in value))
20
- return { contentHash: hash, dependencies: validated, saved: {}, savedHashes: {} };
21
- // vitecache 0.2 kept each saved dependency with its hash in one record. Its entry misses until a save replaces it.
22
- if (!('savedHashes' in value))
23
- return undefined;
24
- return { contentHash: hash, dependencies: validated, saved: hashableDependencies(value.saved), savedHashes: hashes(value.savedHashes) };
25
- }
26
- catch (cause) {
27
- throw new Error(`Invalid vitecache metadata: ${file}`, { cause });
28
- }
29
- }
30
- function matches(current, expected) {
31
- return current.status === 'current' && equalHashes(current.hashes, expected);
32
- }
33
- export function createStorage(options) {
34
- const { root, directory } = disk.location(options);
35
- async function snapshot(dependencies) {
36
- const entries = await Promise.all(Object.entries(dependencies).map(async ([name, dependency]) => {
37
- if (dependency.package === undefined)
38
- return [name, await fileHash(resolve(root, dependency.path))];
39
- const version = await packageVersion(root, dependency.package);
40
- return [name, version === undefined ? undefined : contentHash(new TextEncoder().encode(version))];
41
- }));
42
- const present = [];
43
- for (const [name, hash] of entries) {
44
- if (hash === undefined)
45
- return { status: 'missing', message: `Missing file or package dependency: ${name}` };
46
- present.push([name, hash]);
47
- }
48
- return { status: 'current', hashes: Object.fromEntries(present) };
49
- }
50
- async function load(key) {
51
- const file = join(directory, `${key}.json`);
52
- const raw = await disk.load(file);
53
- if (raw === undefined)
54
- return undefined;
55
- const parsed = metadata(raw, file);
56
- if (!parsed || !matches(await snapshot(parsed.saved), parsed.savedHashes))
57
- return undefined;
58
- const bytes = await disk.load(join(directory, `${key}.bin`));
59
- if (bytes === undefined || contentHash(bytes) !== parsed.contentHash)
60
- return undefined;
61
- // The metadata file's modification time is the entry's last use. The bytes keep theirs, so static hosts keep their
62
- // validators.
63
- await disk.touch(file);
64
- return { tag: contentHash(raw), hashes: parsed.dependencies, bytes };
65
- }
66
- return {
67
- async canSave() { return true; },
68
- async resolve(declared) {
69
- return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(declared).map(async ([name, dependency]) => {
70
- if (dependency.url === undefined)
71
- return [name, dependency];
72
- const url = new URL(dependency.url);
73
- if (url.protocol !== 'file:') {
74
- throw new Error('Node file dependency URLs must use file:. Use a project-root-relative path for Vite-served files.');
75
- }
76
- return [name, { path: await disk.rootRelative(root, fileURLToPath(url)) }];
77
- }))));
78
- },
79
- snapshot,
80
- load,
81
- async save(key, value, { checked, expected, saved }) {
82
- const current = await snapshot(saved);
83
- if (current.status !== 'current')
84
- throw new MissingDependencyError(`Cannot save a cache entry with a missing saved dependency. ${current.message}`);
85
- await disk.ignore(root, directory);
86
- await disk.save(join(directory, `${key}.bin`), value, async () => {
87
- if (!matches(await snapshot(checked), expected))
88
- throw new ChangedDependenciesError();
89
- });
90
- const record = { contentHash: contentHash(value), dependencies: expected, saved, savedHashes: current.hashes };
91
- await disk.save(join(directory, `${key}.json`), new TextEncoder().encode(JSON.stringify(record)));
92
- },
93
- };
94
- }
95
- //# sourceMappingURL=storage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as disk from './disk.js';\nimport { ChangedDependenciesError, equalHashes, hashableDependencies, hashes, MissingDependencyError, resolvedDependencies } from './dependencies.js';\nimport { contentHash, fileHash } from './hash.js';\nimport { packageVersion } from './packages.js';\nimport type { Backend, CacheEntryOptions, Dependencies, Hashable, Hashes, Snapshot, Stored } from './types.js';\n\ninterface Metadata {\n contentHash: string;\n dependencies: Hashes;\n saved: Dependencies<Hashable>;\n /** The saved dependencies' hashes when the entry was saved. */\n savedHashes: Hashes;\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 | undefined {\n try {\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 hash = value.contentHash;\n if (typeof hash !== 'string' || !/^[a-f0-9]{64}$/.test(hash)) throw new Error('Expected a content hash.');\n const validated = hashes(value.dependencies);\n // Metadata without saved dependencies: all that vitecache 0.1 wrote, and what 0.2 wrote for entries that had none.\n if (!('saved' in value)) return { contentHash: hash, dependencies: validated, saved: {}, savedHashes: {} };\n // vitecache 0.2 kept each saved dependency with its hash in one record. Its entry misses until a save replaces it.\n if (!('savedHashes' in value)) return undefined;\n return { contentHash: hash, dependencies: validated, saved: hashableDependencies(value.saved), savedHashes: hashes(value.savedHashes) };\n } catch (cause) {\n throw new Error(`Invalid vitecache metadata: ${file}`, { cause });\n }\n}\n\n/** A stored entry as storage reads it, its hashes always known. */\nexport interface Found extends Stored {\n /** The entity tag's opaque value: it changes whenever the entry's bytes or dependency hashes do. */\n tag: string;\n hashes: Hashes;\n}\n\nexport interface Storage extends Backend {\n /** Reads a valid entry and records its use for prune(). */\n load(key: string): Promise<Found | undefined>;\n}\n\nfunction matches(current: Snapshot, expected: Hashes): boolean {\n return current.status === 'current' && equalHashes(current.hashes, expected);\n}\n\nexport function createStorage(options: Pick<CacheEntryOptions, 'root' | 'basePath'>): Storage {\n const { root, directory } = disk.location(options);\n\n async function snapshot(dependencies: Dependencies<Hashable>): Promise<Exclude<Snapshot, { status: 'unchecked' }>> {\n const entries = await Promise.all(Object.entries(dependencies).map(async ([name, dependency]) => {\n if (dependency.package === undefined) return [name, await fileHash(resolve(root, dependency.path))] as const;\n const version = await packageVersion(root, dependency.package);\n return [name, version === undefined ? undefined : contentHash(new TextEncoder().encode(version))] as const;\n }));\n const present: [string, string][] = [];\n for (const [name, hash] of entries) {\n if (hash === undefined) return { status: 'missing', message: `Missing file or package dependency: ${name}` };\n present.push([name, hash]);\n }\n return { status: 'current', hashes: Object.fromEntries(present) };\n }\n\n async function load(key: string): Promise<Found | undefined> {\n const file = join(directory, `${key}.json`);\n const raw = await disk.load(file);\n if (raw === undefined) return undefined;\n const parsed = metadata(raw, file);\n if (!parsed || !matches(await snapshot(parsed.saved), parsed.savedHashes)) return undefined;\n const bytes = await disk.load(join(directory, `${key}.bin`));\n if (bytes === undefined || contentHash(bytes) !== parsed.contentHash) return undefined;\n // The metadata file's modification time is the entry's last use. The bytes keep theirs, so static hosts keep their\n // validators.\n await disk.touch(file);\n return { tag: contentHash(raw), hashes: parsed.dependencies, bytes };\n }\n\n return {\n async canSave() { return true; },\n async resolve(declared: Dependencies) {\n return resolvedDependencies(Object.fromEntries(await Promise.all(Object.entries(declared).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 return [name, { path: await disk.rootRelative(root, fileURLToPath(url)) }];\n }))));\n },\n snapshot,\n load,\n async save(key, value, { checked, expected, saved }) {\n const current = await snapshot(saved);\n if (current.status !== 'current') throw new MissingDependencyError(`Cannot save a cache entry with a missing saved dependency. ${current.message}`);\n await disk.ignore(root, directory);\n await disk.save(join(directory, `${key}.bin`), value, async () => {\n if (!matches(await snapshot(checked), expected)) throw new ChangedDependenciesError();\n });\n const record: Metadata = { contentHash: contentHash(value), dependencies: expected, saved, savedHashes: current.hashes };\n await disk.save(join(directory, `${key}.json`), new TextEncoder().encode(JSON.stringify(record)));\n },\n };\n}\n"]}