@ttt-productions/media-processing-core 0.8.12 → 0.8.13
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/media-processing-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
@@ -45,9 +45,8 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^
|
|
49
|
-
"firebase-admin": "^14.0.0"
|
|
50
|
-
"typescript": "^5.8.3"
|
|
48
|
+
"@types/node": "^24.13.6",
|
|
49
|
+
"firebase-admin": "^14.0.0"
|
|
51
50
|
},
|
|
52
51
|
"author": "DJ (TTT Productions)",
|
|
53
52
|
"license": "MIT"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-input.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/local-input.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Readable } from "node:stream";
|
|
3
|
-
import { readFile, stat, writeFile, mkdtemp, rm } from "node:fs/promises";
|
|
4
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { streamToTempFile, hashFileSha256, readFileHeader, ByteLimitExceededError, FILE_HEADER_BYTES, } from "../io/local-input.js";
|
|
8
|
-
const chunked = (buf, chunkSize) => {
|
|
9
|
-
const chunks = [];
|
|
10
|
-
for (let i = 0; i < buf.length; i += chunkSize)
|
|
11
|
-
chunks.push(buf.subarray(i, i + chunkSize));
|
|
12
|
-
return Readable.from(chunks);
|
|
13
|
-
};
|
|
14
|
-
describe("streamToTempFile", () => {
|
|
15
|
-
it("writes the exact bytes and reports bytesWritten; cleanup removes the file", async () => {
|
|
16
|
-
const data = randomBytes(256 * 1024 + 17);
|
|
17
|
-
const input = await streamToTempFile(chunked(data, 64 * 1024), { maxBytes: 1024 * 1024 });
|
|
18
|
-
try {
|
|
19
|
-
expect(input.bytesWritten).toBe(data.length);
|
|
20
|
-
expect(Buffer.compare(await readFile(input.localPath), data)).toBe(0);
|
|
21
|
-
}
|
|
22
|
-
finally {
|
|
23
|
-
await input.cleanup();
|
|
24
|
-
}
|
|
25
|
-
await expect(stat(input.localPath)).rejects.toThrow();
|
|
26
|
-
});
|
|
27
|
-
it("HARD CAP: throws ByteLimitExceededError and leaves no temp file behind", async () => {
|
|
28
|
-
const data = randomBytes(100_000);
|
|
29
|
-
let caught;
|
|
30
|
-
try {
|
|
31
|
-
await streamToTempFile(chunked(data, 10_000), { maxBytes: 50_000 });
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
caught = err;
|
|
35
|
-
}
|
|
36
|
-
expect(caught).toBeInstanceOf(ByteLimitExceededError);
|
|
37
|
-
expect(caught.maxBytes).toBe(50_000);
|
|
38
|
-
});
|
|
39
|
-
it("a source stream error cleans up and propagates", async () => {
|
|
40
|
-
const bad = new Readable({
|
|
41
|
-
read() {
|
|
42
|
-
this.emit("error", new Error("source died"));
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
await expect(streamToTempFile(bad, { maxBytes: 1000 })).rejects.toThrow("source died");
|
|
46
|
-
});
|
|
47
|
-
it("rejects a non-positive maxBytes", async () => {
|
|
48
|
-
await expect(streamToTempFile(Readable.from([]), { maxBytes: 0 })).rejects.toThrow(/positive finite maxBytes/);
|
|
49
|
-
});
|
|
50
|
-
it("cleanup is idempotent", async () => {
|
|
51
|
-
const input = await streamToTempFile(Readable.from([Buffer.from("x")]), { maxBytes: 10 });
|
|
52
|
-
await input.cleanup();
|
|
53
|
-
await expect(input.cleanup()).resolves.toBeUndefined();
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe("hashFileSha256", () => {
|
|
57
|
-
it("matches crypto's whole-buffer digest", async () => {
|
|
58
|
-
const dir = await mkdtemp(path.join(os.tmpdir(), "local-input-test-"));
|
|
59
|
-
const p = path.join(dir, "f");
|
|
60
|
-
const data = randomBytes(300_000);
|
|
61
|
-
try {
|
|
62
|
-
await writeFile(p, data);
|
|
63
|
-
const expected = createHash("sha256").update(data).digest("hex");
|
|
64
|
-
await expect(hashFileSha256(p)).resolves.toBe(expected);
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
await rm(dir, { recursive: true, force: true });
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe("readFileHeader", () => {
|
|
72
|
-
it("reads at most the requested window and the whole file when smaller", async () => {
|
|
73
|
-
const dir = await mkdtemp(path.join(os.tmpdir(), "local-input-test-"));
|
|
74
|
-
const p = path.join(dir, "f");
|
|
75
|
-
const data = randomBytes(FILE_HEADER_BYTES + 500);
|
|
76
|
-
try {
|
|
77
|
-
await writeFile(p, data);
|
|
78
|
-
const header = await readFileHeader(p);
|
|
79
|
-
expect(header.byteLength).toBe(FILE_HEADER_BYTES);
|
|
80
|
-
expect(Buffer.compare(Buffer.from(header), data.subarray(0, FILE_HEADER_BYTES))).toBe(0);
|
|
81
|
-
const small = await readFileHeader(p, 16);
|
|
82
|
-
expect(small.byteLength).toBe(16);
|
|
83
|
-
await writeFile(p, Buffer.from("tiny"));
|
|
84
|
-
const whole = await readFileHeader(p);
|
|
85
|
-
expect(Buffer.from(whole).toString()).toBe("tiny");
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
await rm(dir, { recursive: true, force: true });
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
//# sourceMappingURL=local-input.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-input.test.js","sourceRoot":"","sources":["../../src/__tests__/local-input.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Readable } from \"node:stream\";\nimport { readFile, stat, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n streamToTempFile,\n hashFileSha256,\n readFileHeader,\n ByteLimitExceededError,\n FILE_HEADER_BYTES,\n} from \"../io/local-input.js\";\n\nconst chunked = (buf: Buffer, chunkSize: number): Readable => {\n const chunks: Buffer[] = [];\n for (let i = 0; i < buf.length; i += chunkSize) chunks.push(buf.subarray(i, i + chunkSize));\n return Readable.from(chunks);\n};\n\ndescribe(\"streamToTempFile\", () => {\n it(\"writes the exact bytes and reports bytesWritten; cleanup removes the file\", async () => {\n const data = randomBytes(256 * 1024 + 17);\n const input = await streamToTempFile(chunked(data, 64 * 1024), { maxBytes: 1024 * 1024 });\n try {\n expect(input.bytesWritten).toBe(data.length);\n expect(Buffer.compare(await readFile(input.localPath), data)).toBe(0);\n } finally {\n await input.cleanup();\n }\n await expect(stat(input.localPath)).rejects.toThrow();\n });\n\n it(\"HARD CAP: throws ByteLimitExceededError and leaves no temp file behind\", async () => {\n const data = randomBytes(100_000);\n let caught: unknown;\n try {\n await streamToTempFile(chunked(data, 10_000), { maxBytes: 50_000 });\n } catch (err) {\n caught = err;\n }\n expect(caught).toBeInstanceOf(ByteLimitExceededError);\n expect((caught as ByteLimitExceededError).maxBytes).toBe(50_000);\n });\n\n it(\"a source stream error cleans up and propagates\", async () => {\n const bad = new Readable({\n read() {\n this.emit(\"error\", new Error(\"source died\"));\n },\n });\n await expect(streamToTempFile(bad, { maxBytes: 1000 })).rejects.toThrow(\"source died\");\n });\n\n it(\"rejects a non-positive maxBytes\", async () => {\n await expect(streamToTempFile(Readable.from([]), { maxBytes: 0 })).rejects.toThrow(\n /positive finite maxBytes/,\n );\n });\n\n it(\"cleanup is idempotent\", async () => {\n const input = await streamToTempFile(Readable.from([Buffer.from(\"x\")]), { maxBytes: 10 });\n await input.cleanup();\n await expect(input.cleanup()).resolves.toBeUndefined();\n });\n});\n\ndescribe(\"hashFileSha256\", () => {\n it(\"matches crypto's whole-buffer digest\", async () => {\n const dir = await mkdtemp(path.join(os.tmpdir(), \"local-input-test-\"));\n const p = path.join(dir, \"f\");\n const data = randomBytes(300_000);\n try {\n await writeFile(p, data);\n const expected = createHash(\"sha256\").update(data).digest(\"hex\");\n await expect(hashFileSha256(p)).resolves.toBe(expected);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n });\n});\n\ndescribe(\"readFileHeader\", () => {\n it(\"reads at most the requested window and the whole file when smaller\", async () => {\n const dir = await mkdtemp(path.join(os.tmpdir(), \"local-input-test-\"));\n const p = path.join(dir, \"f\");\n const data = randomBytes(FILE_HEADER_BYTES + 500);\n try {\n await writeFile(p, data);\n const header = await readFileHeader(p);\n expect(header.byteLength).toBe(FILE_HEADER_BYTES);\n expect(Buffer.compare(Buffer.from(header), data.subarray(0, FILE_HEADER_BYTES))).toBe(0);\n\n const small = await readFileHeader(p, 16);\n expect(small.byteLength).toBe(16);\n\n await writeFile(p, Buffer.from(\"tiny\"));\n const whole = await readFileHeader(p);\n expect(Buffer.from(whole).toString()).toBe(\"tiny\");\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n });\n});\n"]}
|