gm-pantry 0.1.0 → 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/dist/verify.js CHANGED
@@ -1,15 +1,21 @@
1
- // SPEC.md §6: the sha256 check on a downloaded archive. Web Crypto only, so it runs in a browser.
2
- const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
1
+ // SPEC.md §6: the sha256 check on a downloaded archive. Runs in a browser.
2
+ import { hashStream, sha256Hex } from "./hash.js";
3
3
  /**
4
4
  * Checks bytes against a `file` entry: size first, then sha256. A consumer discards the archive on `ok: false`.
5
- * shortcut: a stream is buffered whole (Web Crypto has no incremental digest), upgrade when archives outgrow memory.
5
+ * A Blob or stream is hashed chunk by chunk, never buffered whole.
6
6
  */
7
- export async function verifyArchive(data, file) {
8
- const bytes = data instanceof ReadableStream
9
- ? new Uint8Array(await new Response(data).arrayBuffer())
10
- : new Uint8Array(data);
11
- if (bytes.byteLength !== file.size)
12
- return { ok: false, reason: "size", expected: file.size, actual: bytes.byteLength };
13
- const actual = hex(await crypto.subtle.digest("SHA-256", bytes));
14
- return actual === file.sha256 ? { ok: true } : { ok: false, reason: "sha256", expected: file.sha256, actual };
7
+ export async function verifyArchive(data, file, options) {
8
+ let actual;
9
+ if (data instanceof ReadableStream || data instanceof Blob) {
10
+ actual = await hashStream(data instanceof Blob ? data.stream() : data, options);
11
+ }
12
+ else {
13
+ const bytes = new Uint8Array(data);
14
+ if (bytes.byteLength !== file.size)
15
+ return { ok: false, reason: "size", expected: file.size, actual: bytes.byteLength };
16
+ actual = { sha256: await sha256Hex(bytes), size: bytes.byteLength };
17
+ }
18
+ if (actual.size !== file.size)
19
+ return { ok: false, reason: "size", expected: file.size, actual: actual.size };
20
+ return actual.sha256 === file.sha256 ? { ok: true } : { ok: false, reason: "sha256", expected: file.sha256, actual: actual.sha256 };
15
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-pantry",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Game repositories: static directories of game archives with a repo.json, plus a parser, validator and CLI.",
5
5
  "keywords": [
6
6
  "gamemaker",
@@ -26,6 +26,10 @@
26
26
  ".": {
27
27
  "types": "./dist/index.d.ts",
28
28
  "default": "./dist/index.js"
29
+ },
30
+ "./s3": {
31
+ "types": "./dist/s3.d.ts",
32
+ "default": "./dist/s3.js"
29
33
  }
30
34
  },
31
35
  "bin": {
@@ -40,6 +44,10 @@
40
44
  "test": "node --test test/*.test.ts scripts/*.test.mjs",
41
45
  "prepublishOnly": "npm run build"
42
46
  },
47
+ "dependencies": {
48
+ "aws4fetch": "^1.0.20",
49
+ "hash-wasm": "^4.13.0"
50
+ },
43
51
  "devDependencies": {
44
52
  "@types/node": "^24.0.0",
45
53
  "typescript": "^5.9.0"