mirror of
https://github.com/marcogll/passkit-generator.git
synced 2026-03-15 21:25:26 +00:00
Added Bundle class implementation along with tests
This commit is contained in:
58
spec/Bundle.ts
Normal file
58
spec/Bundle.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Stream } from "stream";
|
||||||
|
import { default as Bundle, filesSymbol } from "../lib/Bundle";
|
||||||
|
|
||||||
|
describe("Bundle", () => {
|
||||||
|
let bundle: InstanceType<typeof Bundle>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
bundle = new Bundle("application/vnd.apple.pkpass");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error if no mime-type is specified in the constructor", () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => new Bundle()).toThrowError(
|
||||||
|
Error,
|
||||||
|
"Cannot build Bundle. MimeType is missing",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should expose the mime-type as public property", () => {
|
||||||
|
expect(bundle.mimeType).toBe("application/vnd.apple.pkpass");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow to add buffers", () => {
|
||||||
|
const buffer = Buffer.alloc(0);
|
||||||
|
bundle.addBuffer("pass.json", buffer);
|
||||||
|
|
||||||
|
expect(bundle[filesSymbol]).toEqual({ "pass.json": buffer });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error if freezed", async () => {
|
||||||
|
addEmptyFilesToBundle(bundle);
|
||||||
|
|
||||||
|
await bundle.getAsBuffer();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
bundle.addBuffer("icon.png", Buffer.alloc(0)),
|
||||||
|
).toThrowError(Error, "Cannot add file. Bundle is closed.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a stream with 'getAsStream'", () => {
|
||||||
|
addEmptyFilesToBundle(bundle);
|
||||||
|
|
||||||
|
expect(bundle.getAsStream()).toBeInstanceOf(Stream);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a buffer with 'getAsBuffer'", async () => {
|
||||||
|
addEmptyFilesToBundle(bundle);
|
||||||
|
|
||||||
|
expect(await bundle.getAsBuffer()).toBeInstanceOf(Buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function addEmptyFilesToBundle(bundle: Bundle) {
|
||||||
|
const buffer = Buffer.alloc(0);
|
||||||
|
bundle.addBuffer("pass.json", buffer);
|
||||||
|
bundle.addBuffer("icon@2x.png", buffer);
|
||||||
|
bundle.addBuffer("icon@3x.png", buffer);
|
||||||
|
}
|
||||||
99
src/Bundle.ts
Normal file
99
src/Bundle.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { Stream } from "stream";
|
||||||
|
import { ZipFile } from "yazl";
|
||||||
|
|
||||||
|
export const filesSymbol = Symbol("bundleFiles");
|
||||||
|
const bundleStateSymbol = Symbol("state");
|
||||||
|
const archiveSymbol = Symbol("zip");
|
||||||
|
|
||||||
|
enum BundleState {
|
||||||
|
CLOSED = 0,
|
||||||
|
OPEN = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Mime {
|
||||||
|
export type type = string;
|
||||||
|
export type subtype = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a container ready to be distributed.
|
||||||
|
* If no mimeType is passed to the constructor,
|
||||||
|
* it will throw an error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class Bundle {
|
||||||
|
private [bundleStateSymbol]: BundleState = BundleState.OPEN;
|
||||||
|
private [filesSymbol]: { [key: string]: Buffer } = {};
|
||||||
|
private [archiveSymbol] = new ZipFile();
|
||||||
|
|
||||||
|
constructor(public mimeType: `${Mime.type}/${Mime.subtype}`) {
|
||||||
|
if (!mimeType) {
|
||||||
|
throw new Error("Cannot build Bundle. MimeType is missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Freezes / Closes the bundle so no more files
|
||||||
|
* can be added any further.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private freeze() {
|
||||||
|
if (this[bundleStateSymbol] === BundleState.CLOSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this[bundleStateSymbol] = BundleState.CLOSED;
|
||||||
|
this[archiveSymbol].end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows files to be added to the bundle.
|
||||||
|
* If the bundle is closed, it will throw an error.
|
||||||
|
*
|
||||||
|
* @param fileName
|
||||||
|
* @param buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
public addBuffer(fileName: string, buffer: Buffer) {
|
||||||
|
if (this[bundleStateSymbol] === BundleState.CLOSED) {
|
||||||
|
throw new Error("Cannot add file. Bundle is closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this[filesSymbol][fileName] = buffer;
|
||||||
|
this[archiveSymbol].addBuffer(buffer, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the bundle and returns it as a Buffer.
|
||||||
|
* Once closed, the bundle does not allow files
|
||||||
|
* to be added any further.
|
||||||
|
*
|
||||||
|
* @returns Promise<Buffer>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public getAsBuffer(): Promise<Buffer> {
|
||||||
|
const stream = this.getAsStream();
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
stream.on("data", (data: Buffer) => {
|
||||||
|
chunks.push(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on("end", () => resolve(Buffer.from(chunks)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the bundle and returns it as a stream.
|
||||||
|
* Once closed, the bundle does not allow files
|
||||||
|
* to be added any further.
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
|
||||||
|
public getAsStream(): Stream {
|
||||||
|
this.freeze();
|
||||||
|
return this[archiveSymbol].outputStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,3 +3,5 @@ export type { AbstractModel } from "./abstract";
|
|||||||
|
|
||||||
export { createPass } from "./factory";
|
export { createPass } from "./factory";
|
||||||
export { createAbstractModel } from "./abstract";
|
export { createAbstractModel } from "./abstract";
|
||||||
|
|
||||||
|
export { default as Bundle } from "./Bundle";
|
||||||
|
|||||||
Reference in New Issue
Block a user