vitecache 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,11 +29,7 @@ if (bytes === undefined) {
29
29
  useMeshBytes(bytes)
30
30
  ```
31
31
 
32
- Entries identify computations by version and named dependencies: JSON `{ value }`, file `{ path }` or `{ url }`, and installed library `{ package: "elkjs" }`. Node and the Vite development plugin validate file contents and package versions without changing entry keys.
33
-
34
- Paths are always relative to the project root: `root` in Node (the working directory by default), or the Vite project root in browsers. The Vite plugin resolves served URLs to their source paths and preserves mappings for public files and emitted assets in production builds; Node accepts local file URLs.
35
-
36
- Package installation paths are excluded from identity; transitive packages need their own declarations. Load before computing so saving can detect dependencies that changed during computation.
32
+ Entries identify computations by version and named dependencies: JSON `{ value }`, file `{ path }` or `{ url }`, and installed library `{ package: "layout-engine" }`. Node and the Vite development plugin validate file contents and package versions without changing entry keys.
37
33
 
38
34
  Node reads and writes `<project_root>/public/_vitecache/`. Add the plugin to save browser results into the same directory during development:
39
35
 
@@ -44,6 +40,6 @@ import { vitecache } from "vitecache/vite"
44
40
  export default defineConfig({ plugins: [vitecache()] })
45
41
  ```
46
42
 
47
- Node saves and the plugin add the cache directory to `.gitignore`. Generate caches before building and include them in your deployment. Browsers without the plugin read public cache files, assume dependencies unchanged, and warn and skip writes when `save()` is called.
43
+ Node saves and the plugin add the cache directory to `.gitignore`. Generate caches before building and include them in your deployment; `prune` from `vitecache/prune` removes entries no load or save has used since a given time. Browsers without the plugin read public cache files, assume dependencies unchanged, and warn and skip writes when `save()` is called.
48
44
 
49
45
  For usage patterns, see the [vitecache skill](skills/vitecache/SKILL.md), also shipped in this package.
package/dist/browser.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Entry } from './entry.js';
2
2
  import type { CacheEntryOptions } from './types.js';
3
- export { MissingDependencyError } from './dependencies.js';
3
+ export { ChangedDependenciesError, MissingDependencyError } from './dependencies.js';
4
4
  export type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';
5
5
  /** A persistent binary computation result. Pass a browser basePath matching the public cache URL, including any Vite base. */
6
6
  export declare class CacheEntry extends Entry {
package/dist/browser.js CHANGED
@@ -1,9 +1,21 @@
1
- import { hashes, MissingDependencyError, paths, record, resolvedDependencies } from './dependencies.js';
1
+ import { ChangedDependenciesError, hashes, MissingDependencyError, paths, resolvedDependencies } from './dependencies.js';
2
2
  import { Entry } from './entry.js';
3
- export { MissingDependencyError } from './dependencies.js';
3
+ export { ChangedDependenciesError, MissingDependencyError } from './dependencies.js';
4
+ /** Shares one request per URL across the page, and retries after a failure. */
5
+ function memoize(cache, url, request) {
6
+ const existing = cache.get(url.href);
7
+ if (existing)
8
+ return existing;
9
+ const pending = request(url).catch((error) => {
10
+ cache.delete(url.href);
11
+ throw error;
12
+ });
13
+ cache.set(url.href, pending);
14
+ return pending;
15
+ }
4
16
  const discoveries = new Map();
5
- async function discover(base) {
6
- const response = await fetch(new URL('__vitecache', base), { cache: 'no-store' });
17
+ async function discover(url) {
18
+ const response = await fetch(url, { cache: 'no-store' });
7
19
  if (response.status === 404)
8
20
  return false;
9
21
  if (!response.ok)
@@ -14,25 +26,34 @@ async function discover(base) {
14
26
  const data = await response.json();
15
27
  return typeof data === 'object' && data !== null && 'vitecache' in data && data.vitecache === 1;
16
28
  }
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);
29
+ const mappings = new Map();
30
+ /** The production build's served pathnames mapped to project-root-relative source paths. */
31
+ async function mapping(url) {
32
+ const response = await fetch(url, { cache: 'no-cache' });
33
+ if (!response.ok || !response.headers.get('content-type')?.includes('application/json')) {
34
+ throw new Error('URL dependencies require the vitecache Vite plugin during development and build. Missing production URL mapping.');
35
35
  }
36
+ const data = await response.json();
37
+ const manifest = responseObject(data);
38
+ const mapped = paths(manifest.paths);
39
+ if (typeof manifest.root !== 'string' || !/^(?:\.\.\/)+$/.test(manifest.root))
40
+ throw new Error('Invalid vitecache production mapping root.');
41
+ const projectRoot = new URL(manifest.root, url);
42
+ return Object.fromEntries(Object.entries(mapped).map(([served, path]) => [new URL(served, projectRoot).pathname, path]));
43
+ }
44
+ /** A JSON object that the plugin or a build answered with. */
45
+ function responseObject(data) {
46
+ if (typeof data !== 'object' || data === null || Array.isArray(data))
47
+ throw new TypeError('Invalid vitecache response.');
48
+ return Object.fromEntries(Object.entries(data));
49
+ }
50
+ /** The plugin answers a missing file or package dependency with `{ status: 'missing', message }` on every route. */
51
+ function missingMessage(data) {
52
+ if (data.status !== 'missing')
53
+ return undefined;
54
+ if (typeof data.message !== 'string')
55
+ throw new TypeError('Invalid vitecache missing dependency response.');
56
+ return data.message;
36
57
  }
37
58
  function createBackend(options) {
38
59
  const base = new URL(options.basePath ?? '/_vitecache/', globalThis.location.href);
@@ -40,12 +61,12 @@ function createBackend(options) {
40
61
  base.search = '';
41
62
  base.hash = '';
42
63
  function plugin() {
43
- return discovery(base);
64
+ return memoize(discoveries, new URL('__vitecache', base), discover);
44
65
  }
45
66
  return {
46
67
  canSave: plugin,
47
- async resolve(dependencies) {
48
- const locators = Object.entries(dependencies).map(([name, locator]) => {
68
+ async resolve(declared) {
69
+ const locators = Object.entries(declared).map(([name, locator]) => {
49
70
  if (locator.url === undefined)
50
71
  return [name, locator];
51
72
  const url = new URL(locator.url, globalThis.location.href);
@@ -63,28 +84,19 @@ function createBackend(options) {
63
84
  body: JSON.stringify({ dependencies: Object.fromEntries(locators) }),
64
85
  cache: 'no-store',
65
86
  });
66
- await rejectMissing(response);
67
87
  if (!response.ok)
68
- throw new Error(`vitecache URL resolution failed: HTTP ${response.status}`);
69
- const data = await response.json();
70
- const resolved = resolvedDependencies(record(data).dependencies);
88
+ throw new Error(`vitecache URL resolution failed: HTTP ${response.status}: ${await response.text()}`);
89
+ const data = responseObject(await response.json());
90
+ const missing = missingMessage(data);
91
+ if (missing !== undefined)
92
+ throw new MissingDependencyError(missing);
93
+ const resolved = resolvedDependencies(data.dependencies);
71
94
  if (Object.keys(resolved).length !== locators.length || !locators.every(([name]) => Object.hasOwn(resolved, name))) {
72
95
  throw new Error('Invalid vitecache resolved dependencies.');
73
96
  }
74
97
  return resolved;
75
98
  }
76
- const manifestUrl = new URL('__vitecache/paths.json', base);
77
- const response = await fetch(manifestUrl, { cache: 'no-store' });
78
- if (!response.ok || !response.headers.get('content-type')?.includes('application/json')) {
79
- throw new Error('URL dependencies require the vitecache Vite plugin during development and build. Missing production URL mapping.');
80
- }
81
- const data = await response.json();
82
- const manifest = record(data);
83
- const mapping = paths(manifest.paths);
84
- if (typeof manifest.root !== 'string' || !/^(?:\.\.\/)+$/.test(manifest.root))
85
- throw new Error('Invalid vitecache production mapping root.');
86
- const projectRoot = new URL(manifest.root, manifestUrl);
87
- const mapped = Object.fromEntries(Object.entries(mapping).map(([url, path]) => [new URL(url, projectRoot).pathname, path]));
99
+ const mapped = await memoize(mappings, new URL('__vitecache/paths.json', base), mapping);
88
100
  const resolved = locators.map(([name, locator]) => {
89
101
  if (locator.url === undefined)
90
102
  return [name, locator];
@@ -99,7 +111,7 @@ function createBackend(options) {
99
111
  },
100
112
  async snapshot(dependencies) {
101
113
  if (!await plugin())
102
- return { status: 'unavailable' };
114
+ return { status: 'unchecked' };
103
115
  const response = await fetch(new URL('__vitecache/snapshot', base), {
104
116
  method: 'POST',
105
117
  headers: { 'Content-Type': 'application/json' },
@@ -107,17 +119,18 @@ function createBackend(options) {
107
119
  cache: 'no-store',
108
120
  });
109
121
  if (!response.ok)
110
- throw new Error(`vitecache dependency validation failed: HTTP ${response.status}`);
111
- const data = await response.json();
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)) {
122
+ throw new Error(`vitecache dependency validation failed: HTTP ${response.status}: ${await response.text()}`);
123
+ const data = responseObject(await response.json());
124
+ const missing = missingMessage(data);
125
+ if (missing !== undefined)
126
+ return { status: 'missing', message: missing };
127
+ if (data.status !== 'current')
115
128
  throw new TypeError('Invalid vitecache dependency response.');
116
- }
117
- return { status: 'available', hashes: hashes(data.hashes) };
129
+ return { status: 'current', hashes: hashes(data.hashes) };
118
130
  },
119
- async load(key, validate) {
120
- const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-store' });
131
+ async load(key) {
132
+ // Entry names do not change with their contents, so cached bytes are revalidated before each use.
133
+ const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-cache' });
121
134
  // The plugin answers misses with 204 so they stay out of the console; static hosts answer 404.
122
135
  if (response.status === 204 || response.status === 404)
123
136
  return undefined;
@@ -129,17 +142,16 @@ function createBackend(options) {
129
142
  if (contentType !== 'application/octet-stream')
130
143
  throw new Error(`Unexpected vitecache content type: ${contentType ?? 'missing'}`);
131
144
  const bytes = new Uint8Array(await response.arrayBuffer());
132
- if (!validate)
133
- return { bytes };
145
+ // Only the plugin sends hashes.
134
146
  const header = response.headers.get('X-Vitecache-Dependencies');
135
147
  if (header === null)
136
- throw new Error('Missing vitecache dependency metadata.');
148
+ return { bytes };
137
149
  const value = JSON.parse(decodeURIComponent(header));
138
150
  return { bytes, hashes: hashes(value) };
139
151
  },
140
- async save(key, value, validation, expected, saved) {
152
+ async save(key, value, { checked, expected, saved }) {
141
153
  // 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 }));
154
+ const metadata = new TextEncoder().encode(JSON.stringify({ checked, expected, saved }));
143
155
  const body = new Uint8Array(4 + metadata.byteLength + value.byteLength);
144
156
  new DataView(body.buffer).setUint32(0, metadata.byteLength);
145
157
  body.set(metadata, 4);
@@ -149,9 +161,16 @@ function createBackend(options) {
149
161
  headers: { 'Content-Type': 'application/octet-stream' },
150
162
  body,
151
163
  });
152
- await rejectMissing(response);
164
+ if (response.status === 409)
165
+ throw new ChangedDependenciesError();
153
166
  if (!response.ok)
154
- throw new Error(`vitecache save failed: HTTP ${response.status}`);
167
+ throw new Error(`vitecache save failed: HTTP ${response.status}: ${await response.text()}`);
168
+ if (response.status === 204)
169
+ return;
170
+ const missing = missingMessage(responseObject(await response.json()));
171
+ if (missing === undefined)
172
+ throw new TypeError('Invalid vitecache save response.');
173
+ throw new MissingDependencyError(missing);
155
174
  },
156
175
  };
157
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACxG,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;AAExD,KAAK,UAAU,QAAQ,CAAC,IAAS;IAC/B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAClF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACzF,8DAA8D;IAC9D,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAC;IACtF,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC5C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,SAAS,CAAC,IAAS;IAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACtD,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACH,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,KAAK,UAAU,aAAa,CAAC,QAAkB;IAC7C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC;QAAE,OAAO;IAC3G,MAAM,KAAK,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,oBAAoB,IAAI,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvK,MAAM,IAAI,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,OAA0B;IAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,cAAc,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnF,IAAI,CAAC,QAAQ,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC;IACxD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACjB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;IAEf,SAAS,MAAM;QACb,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM;QACf,KAAK,CAAC,OAAO,CAAC,YAAY;YACxB,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAwB,EAAE;gBAC1F,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS;oBAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACtD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC3D,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;gBACjJ,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,CAAC;gBAC/D,OAAO,oBAAoB,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,MAAM,EAAE,EAAE,CAAC;gBACnB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,qBAAqB,EAAE,IAAI,CAAC,EAAE;oBACjE,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACpE,KAAK,EAAE,UAAU;iBAClB,CAAC,CAAC;gBACH,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAC9B,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9F,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC5C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;gBACjE,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;oBACnH,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBAC9D,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACxF,MAAM,IAAI,KAAK,CAAC,kHAAkH,CAAC,CAAC;YACtI,CAAC;YACD,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACtC,IAAI,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC7I,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5H,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAgC,EAAE;gBAC9E,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS;oBAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACtD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;gBAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC9B,uHAAuH;gBACvH,IAAI,IAAI,KAAK,SAAS;oBAAE,MAAM,IAAI,sBAAsB,CAAC,6CAA6C,QAAQ,EAAE,CAAC,CAAC;gBAClH,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YACH,OAAO,oBAAoB,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,YAAY;YACzB,IAAI,CAAC,MAAM,MAAM,EAAE;gBAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,EAAE;gBAClE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;gBACtC,KAAK,EAAE,UAAU;aAClB,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACrG,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC7H,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC;gBAC3H,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;YAChE,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ;YACtB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YACjF,+FAA+F;YAC/F,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,SAAS,CAAC;YACzE,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACpF,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YAChF,IAAI,WAAW,KAAK,WAAW;gBAAE,OAAO,SAAS,CAAC;YAClD,IAAI,WAAW,KAAK,0BAA0B;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,WAAW,IAAI,SAAS,EAAE,CAAC,CAAC;YAClI,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YAChE,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC/E,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK;YAChD,oGAAoG;YACpG,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YACjH,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,QAAQ,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;YACxE,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE;gBACxD,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;gBACvD,IAAI;aACL,CAAC,CAAC;YACH,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,CAAC;KACF,CAAC;AACJ,CAAC;AAED,8HAA8H;AAC9H,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAA0B;QACpC,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;CACF","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"]}
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangedDependenciesError, hashes, MissingDependencyError, paths, resolvedDependencies } from './dependencies.js';\nimport { Entry } from './entry.js';\nimport type { Backend, CacheEntryOptions, Dependency, Paths, Resolved } from './types.js';\nexport { ChangedDependenciesError, MissingDependencyError } from './dependencies.js';\nexport type { CacheEntryOptions, Dependency, Dependencies, JsonValue, SavedDependencies, SavedDependency, SaveOptions } from './types.js';\n\n/** Shares one request per URL across the page, and retries after a failure. */\nfunction memoize<T>(cache: Map<string, Promise<T>>, url: URL, request: (url: URL) => Promise<T>): Promise<T> {\n const existing = cache.get(url.href);\n if (existing) return existing;\n const pending = request(url).catch((error: unknown) => {\n cache.delete(url.href);\n throw error;\n });\n cache.set(url.href, pending);\n return pending;\n}\n\nconst discoveries = new Map<string, Promise<boolean>>();\n\nasync function discover(url: URL): Promise<boolean> {\n const response = await fetch(url, { 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\nconst mappings = new Map<string, Promise<Paths>>();\n\n/** The production build's served pathnames mapped to project-root-relative source paths. */\nasync function mapping(url: URL): Promise<Paths> {\n const response = await fetch(url, { cache: 'no-cache' });\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 = responseObject(data);\n const mapped = 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, url);\n return Object.fromEntries(Object.entries(mapped).map(([served, path]) => [new URL(served, projectRoot).pathname, path]));\n}\n\n/** A JSON object that the plugin or a build answered with. */\nfunction responseObject(data: unknown): Record<string, unknown> {\n if (typeof data !== 'object' || data === null || Array.isArray(data)) throw new TypeError('Invalid vitecache response.');\n return Object.fromEntries(Object.entries(data));\n}\n\n/** The plugin answers a missing file or package dependency with `{ status: 'missing', message }` on every route. */\nfunction missingMessage(data: Record<string, unknown>): string | undefined {\n if (data.status !== 'missing') return undefined;\n if (typeof data.message !== 'string') throw new TypeError('Invalid vitecache missing dependency response.');\n return data.message;\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 memoize(discoveries, new URL('__vitecache', base), discover);\n }\n\n return {\n canSave: plugin,\n async resolve(declared) {\n const locators = Object.entries(declared).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}: ${await response.text()}`);\n const data = responseObject(await response.json());\n const missing = missingMessage(data);\n if (missing !== undefined) throw new MissingDependencyError(missing);\n const resolved = resolvedDependencies(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 mapped = await memoize(mappings, new URL('__vitecache/paths.json', base), mapping);\n const resolved = locators.map(([name, locator]): [string, Resolved] => {\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: 'unchecked' };\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}: ${await response.text()}`);\n const data = responseObject(await response.json());\n const missing = missingMessage(data);\n if (missing !== undefined) return { status: 'missing', message: missing };\n if (data.status !== 'current') throw new TypeError('Invalid vitecache dependency response.');\n return { status: 'current', hashes: hashes(data.hashes) };\n },\n async load(key) {\n // Entry names do not change with their contents, so cached bytes are revalidated before each use.\n const response = await fetch(new URL(`${key}.bin`, base), { cache: 'no-cache' });\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 // Only the plugin sends hashes.\n const header = response.headers.get('X-Vitecache-Dependencies');\n if (header === null) return { bytes };\n const value: unknown = JSON.parse(decodeURIComponent(header));\n return { bytes, hashes: hashes(value) };\n },\n async save(key, value, { checked, 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({ checked, 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 if (response.status === 409) throw new ChangedDependenciesError();\n if (!response.ok) throw new Error(`vitecache save failed: HTTP ${response.status}: ${await response.text()}`);\n if (response.status === 204) return;\n const missing = missingMessage(responseObject(await response.json()));\n if (missing === undefined) throw new TypeError('Invalid vitecache save response.');\n throw new MissingDependencyError(missing);\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,15 +1,23 @@
1
- import type { Dependencies, Hashes, JsonValue, Paths, ResolvedDependencies, SavedDependencies } from './types.js';
1
+ import type { Checked, Dependencies, Dependency, Hashable, Hashes, JsonValue, Paths, Resolved, SavedDependencies } from './types.js';
2
2
  /** A file or package dependency has no backing file or installation, so its state cannot be checked. */
3
3
  export declare class MissingDependencyError extends Error {
4
4
  name: string;
5
5
  }
6
- export declare function record(value: unknown): Record<string, unknown>;
6
+ /** A file or package dependency changed between load() and save(), so the result may be built from stale inputs. */
7
+ export declare class ChangedDependenciesError extends Error {
8
+ name: string;
9
+ constructor();
10
+ }
7
11
  export declare function paths(value: unknown): Paths;
8
12
  /** Validate and snapshot JSON before asynchronous dependency resolution starts. */
9
13
  export declare function json(value: unknown, ancestors?: Set<object>): JsonValue;
10
14
  export declare function dependencies(value: unknown): Dependencies;
11
- export declare function resolvedDependencies(value: unknown): ResolvedDependencies;
15
+ export declare function isChecked<D extends Dependency>(dependency: D): dependency is Checked<D>;
16
+ /** Dependencies after resolution, which turns URLs into paths. */
17
+ export declare function resolvedDependencies(value: unknown): Dependencies<Resolved>;
12
18
  /** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */
13
19
  export declare function savedDependencies(value: unknown): SavedDependencies;
20
+ /** Resolved file and package dependencies: the ones whose current state can be hashed. */
21
+ export declare function hashableDependencies(value: unknown): Dependencies<Hashable>;
14
22
  export declare function hashes(value: unknown): Hashes;
15
23
  export declare function equalHashes(first: Hashes, second: Hashes): boolean;
@@ -2,19 +2,27 @@
2
2
  export class MissingDependencyError extends Error {
3
3
  name = 'MissingDependencyError';
4
4
  }
5
- export function record(value) {
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) {
6
14
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
7
- throw new TypeError('Expected a named dependency record.');
15
+ throw new TypeError('Expected an object of named entries.');
8
16
  }
9
17
  const prototype = Object.getPrototypeOf(value);
10
18
  if (prototype !== Object.prototype && prototype !== null)
11
- throw new TypeError('Expected a plain dependency record.');
19
+ throw new TypeError('Expected a plain object of named entries.');
12
20
  if (Object.getOwnPropertySymbols(value).length)
13
- throw new TypeError('Dependency names must be strings.');
21
+ throw new TypeError('Entry names must be strings.');
14
22
  return Object.fromEntries(Object.keys(value).sort().map((name) => {
15
23
  const property = Object.getOwnPropertyDescriptor(value, name);
16
24
  if (!property || !('value' in property))
17
- throw new TypeError('Dependency getters are not supported.');
25
+ throw new TypeError('Getters are not supported.');
18
26
  const item = property.value;
19
27
  return [name, item];
20
28
  }));
@@ -99,22 +107,30 @@ export function dependencies(value) {
99
107
  });
100
108
  return Object.freeze(Object.fromEntries(entries));
101
109
  }
102
- export function resolvedDependencies(value) {
103
- const entries = Object.entries(dependencies(value)).map(([name, dependency]) => {
104
- if (dependency.url !== undefined)
105
- throw new TypeError('Expected resolved dependencies, not URLs.');
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);
106
114
  return [name, dependency];
107
- });
108
- return Object.freeze(Object.fromEntries(entries));
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.');
109
126
  }
110
127
  /** Saved dependencies are rechecked later, so they cannot be values: values select the result and belong to the key. */
111
128
  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));
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.');
118
134
  }
119
135
  export function hashes(value) {
120
136
  return Object.freeze(Object.fromEntries(Object.entries(record(value)).map(([name, hash]) => {
@@ -1 +1 @@
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"]}
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/disk.d.ts CHANGED
@@ -1,10 +1,27 @@
1
1
  import type { CacheEntryOptions } from './types.js';
2
2
  export declare function missing(error: unknown): boolean;
3
- export declare function missingDependency(error: unknown): boolean;
3
+ /** A filesystem error for a path that does not exist, also below a path component that is a file. */
4
+ export declare function notFound(error: unknown): boolean;
5
+ /** Whether `path` is `root` or below it, compared lexically. */
6
+ export declare function inside(root: string, path: string): boolean;
7
+ /**
8
+ * `file` relative to `root`, with `/` separators. A file that lies outside `root` only lexically, as through a symlinked
9
+ * root, is related by real paths instead.
10
+ */
11
+ export declare function rootRelative(root: string, file: string): Promise<string>;
4
12
  export declare function location(options: Pick<CacheEntryOptions, 'root' | 'basePath'>): {
5
13
  root: string;
6
14
  directory: string;
7
15
  };
8
16
  export declare function ignore(root: string, directory: string): Promise<void>;
17
+ /**
18
+ * A file's bytes as a plain Uint8Array that owns its whole ArrayBuffer, as a browser's response gives them. A Buffer's
19
+ * slice() shares its memory, so a slice whose buffer is transferred to a worker would detach the bytes it came from.
20
+ */
9
21
  export declare function load(path: string): Promise<Uint8Array | undefined>;
10
22
  export declare function save(path: string, value: Uint8Array, beforeCommit?: () => Promise<void>): Promise<void>;
23
+ /**
24
+ * Sets a file's modification time to now; a file that no longer exists is left alone. Where this process may not change
25
+ * it, as on a read-only deployment, it warns once per directory instead of failing the read that called it.
26
+ */
27
+ export declare function touch(path: string): Promise<void>;
package/dist/disk.js CHANGED
@@ -1,11 +1,28 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { appendFile, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
2
+ import { appendFile, mkdir, readFile, realpath, rename, unlink, utimes, writeFile } from 'node:fs/promises';
3
3
  import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
+ function code(error) {
5
+ return error instanceof Error && 'code' in error ? error.code : undefined;
6
+ }
4
7
  export function missing(error) {
5
- return error instanceof Error && 'code' in error && error.code === 'ENOENT';
8
+ return code(error) === 'ENOENT';
9
+ }
10
+ /** A filesystem error for a path that does not exist, also below a path component that is a file. */
11
+ export function notFound(error) {
12
+ return missing(error) || code(error) === 'ENOTDIR';
13
+ }
14
+ /** Whether `path` is `root` or below it, compared lexically. */
15
+ export function inside(root, path) {
16
+ const name = relative(root, path);
17
+ return name !== '..' && !name.startsWith(`..${sep}`) && !isAbsolute(name);
6
18
  }
7
- export function missingDependency(error) {
8
- return missing(error) || (error instanceof Error && 'code' in error && error.code === 'ENOTDIR');
19
+ /**
20
+ * `file` relative to `root`, with `/` separators. A file that lies outside `root` only lexically, as through a symlinked
21
+ * root, is related by real paths instead.
22
+ */
23
+ export async function rootRelative(root, file) {
24
+ const path = inside(root, file) ? relative(root, file) : relative(await realpath(root), await realpath(file));
25
+ return path.split(sep).join('/');
9
26
  }
10
27
  export function location(options) {
11
28
  const root = resolve(options.root ?? process.cwd());
@@ -23,7 +40,7 @@ export function ignore(root, directory) {
23
40
  }
24
41
  async function updateIgnore(root, directory) {
25
42
  const path = relative(root, directory);
26
- if (!path || path === '..' || path.startsWith(`..${sep}`) || isAbsolute(path))
43
+ if (!path || !inside(root, directory))
27
44
  return;
28
45
  const entry = `${path.split(sep).join('/').replace(/[!*?[\]# ]/g, '\\$&')}/*`;
29
46
  const file = join(root, '.gitignore');
@@ -40,9 +57,15 @@ async function updateIgnore(root, directory) {
40
57
  return;
41
58
  await appendFile(file, `${contents && !contents.endsWith('\n') ? '\n' : ''}${entry}\n`);
42
59
  }
60
+ /**
61
+ * A file's bytes as a plain Uint8Array that owns its whole ArrayBuffer, as a browser's response gives them. A Buffer's
62
+ * slice() shares its memory, so a slice whose buffer is transferred to a worker would detach the bytes it came from.
63
+ */
43
64
  export async function load(path) {
44
65
  try {
45
- return await readFile(path);
66
+ const bytes = await readFile(path);
67
+ const owned = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
68
+ return owned ? new Uint8Array(bytes.buffer) : new Uint8Array(bytes);
46
69
  }
47
70
  catch (error) {
48
71
  if (!missing(error))
@@ -68,4 +91,26 @@ export async function save(path, value, beforeCommit) {
68
91
  }
69
92
  }
70
93
  }
94
+ const unrecorded = new Set();
95
+ /**
96
+ * Sets a file's modification time to now; a file that no longer exists is left alone. Where this process may not change
97
+ * it, as on a read-only deployment, it warns once per directory instead of failing the read that called it.
98
+ */
99
+ export async function touch(path) {
100
+ try {
101
+ const now = new Date();
102
+ await utimes(path, now, now);
103
+ }
104
+ catch (error) {
105
+ if (missing(error))
106
+ return;
107
+ const reason = code(error);
108
+ if (reason !== 'EROFS' && reason !== 'EACCES' && reason !== 'EPERM')
109
+ throw error;
110
+ const directory = dirname(path);
111
+ if (!unrecorded.has(directory))
112
+ console.warn(`vitecache: cannot record use of entries in ${directory} (${reason}), so prune() treats them as unused.`);
113
+ unrecorded.add(directory);
114
+ }
115
+ }
71
116
  //# sourceMappingURL=disk.js.map