ba-memorial-lobby-wallpaper-toolkit 0.1.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 ADDED
@@ -0,0 +1,59 @@
1
+ # ba-memorial-lobby-wallpaper-toolkit
2
+
3
+ Development and packaging tools for Blue Archive memorial-lobby Wallpaper Engine
4
+ projects, published on npm as `ba-memorial-lobby-wallpaper-toolkit` (currently
5
+ `0.1.0`). The initial `/vite` export provides runtime asset delivery and a
6
+ validated local log bridge. Character projects supply the resolved runtime
7
+ bootstrap path and remain responsible for their content assets and Wallpaper
8
+ Engine metadata.
9
+
10
+ ## Install
11
+
12
+ ```powershell
13
+ npm install --save-dev ba-memorial-lobby-wallpaper-toolkit
14
+ ```
15
+
16
+ The toolkit is a build-time dependency of wallpaper instance projects and
17
+ declares `vite` (`>=8.0.0 <9`) as a peer dependency. It is consumed through
18
+ npm version ranges, so build, validation, and packaging improvements propagate
19
+ to existing projects via dependency updates.
20
+
21
+ ## CLI
22
+
23
+ The package exposes the `memorial-lobby-toolkit` binary:
24
+
25
+ ```text
26
+ memorial-lobby-toolkit checksums --input <dir> --output <path> [--base <dir>] [--verify]
27
+ memorial-lobby-toolkit package --input <dir> --output <path> --timestamp <ISO-8601> [--package-json <path>]
28
+ ```
29
+
30
+ - `checksums` writes a SHA-256 manifest covering every file under `--input`; with
31
+ `--verify` it checks the manifest at `--output` instead. `--base` sets the path
32
+ base recorded in the manifest (defaults to the input directory).
33
+ - `package` creates a deterministic offline ZIP archive with a fixed timestamp.
34
+ A `{version}` placeholder in `--output` is replaced from the version in
35
+ `--package-json`. The archive is re-read and verified after writing; the tool
36
+ prints its byte size and SHA-256.
37
+
38
+ ## Library API
39
+
40
+ - `createChecksumManifest` / `writeChecksumManifest` / `verifyChecksumManifest` —
41
+ programmatic manifest generation and verification.
42
+ - `validateDistributionFiles` — checks a distribution directory for required
43
+ files, allowed offline text extensions, and remote dependency patterns.
44
+ - `createOfflinePackage` — builds the deterministic offline ZIP with an
45
+ embedded SHA-256 manifest and returns the archive path, checksum, and file
46
+ list.
47
+
48
+ ## Vite plugin
49
+
50
+ `createMemorialLobbyVitePlugins(options)` from `ba-memorial-lobby-wallpaper-toolkit/vite`
51
+ provides:
52
+
53
+ - a dev-server route that serves the runtime logging bootstrap script, and
54
+ - a build-time step that persists log sessions under `dist/log/` and serves
55
+ them through the log route.
56
+
57
+ Options: `loggingBootstrapPath` (required), `outputDirectory` (default `dist`),
58
+ `logRoute` (default `/__memorial-lobby-log`), and
59
+ `maximumLogBodyCharacters` (default 1 MiB).
@@ -0,0 +1,10 @@
1
+ export interface ChecksumManifestOptions {
2
+ readonly inputDirectory: string;
3
+ readonly manifestPath: string;
4
+ readonly pathBase?: string;
5
+ readonly ignore?: (absolutePath: string) => boolean;
6
+ }
7
+ export declare function createChecksumManifest(options: ChecksumManifestOptions): Promise<string>;
8
+ export declare function writeChecksumManifest(options: ChecksumManifestOptions): Promise<number>;
9
+ export declare function verifyChecksumManifest(options: ChecksumManifestOptions): Promise<number>;
10
+ //# sourceMappingURL=checksums.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checksums.d.ts","sourceRoot":"","sources":["../src/checksums.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD;AAoBD,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9F;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7F;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9F"}
@@ -0,0 +1,52 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { readFile, readdir, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ async function walk(directory, ignore) {
6
+ const entries = await readdir(directory, { withFileTypes: true });
7
+ const files = [];
8
+ for (const entry of entries) {
9
+ const absolutePath = path.join(directory, entry.name);
10
+ if (ignore(absolutePath))
11
+ continue;
12
+ if (entry.isDirectory())
13
+ files.push(...await walk(absolutePath, ignore));
14
+ else if (entry.isFile())
15
+ files.push(absolutePath);
16
+ }
17
+ return files;
18
+ }
19
+ async function sha256(file) {
20
+ const hash = createHash("sha256");
21
+ for await (const chunk of createReadStream(file))
22
+ hash.update(chunk);
23
+ return hash.digest("hex");
24
+ }
25
+ export async function createChecksumManifest(options) {
26
+ const inputDirectory = path.resolve(options.inputDirectory);
27
+ const pathBase = path.resolve(options.pathBase ?? inputDirectory);
28
+ const ignore = options.ignore ?? ((absolutePath) => absolutePath.endsWith(".part"));
29
+ const files = (await walk(inputDirectory, ignore)).sort((left, right) => left.localeCompare(right, "en"));
30
+ const lines = await Promise.all(files.map(async (file) => {
31
+ const relative = path.relative(pathBase, file).replace(/\\/g, "/");
32
+ if (relative.startsWith("../") || path.isAbsolute(relative)) {
33
+ throw new Error(`Checksum file is outside path base: ${file}`);
34
+ }
35
+ return `${await sha256(file)} ${relative}`;
36
+ }));
37
+ return `${lines.join("\n")}\n`;
38
+ }
39
+ export async function writeChecksumManifest(options) {
40
+ const content = await createChecksumManifest(options);
41
+ await writeFile(options.manifestPath, content, "utf8");
42
+ return content === "\n" ? 0 : content.trimEnd().split("\n").length;
43
+ }
44
+ export async function verifyChecksumManifest(options) {
45
+ const [actual, expected] = await Promise.all([
46
+ createChecksumManifest(options), readFile(options.manifestPath, "utf8"),
47
+ ]);
48
+ if (actual !== expected)
49
+ throw new Error(`Checksum verification failed: ${options.manifestPath}`);
50
+ return actual === "\n" ? 0 : actual.trimEnd().split("\n").length;
51
+ }
52
+ //# sourceMappingURL=checksums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checksums.js","sourceRoot":"","sources":["../src/checksums.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { verifyChecksumManifest, writeChecksumManifest } from "./checksums.js";
5
+ import { createOfflinePackage } from "./offline-package.js";
6
+ function option(name) {
7
+ const index = process.argv.indexOf(name);
8
+ return index < 0 ? undefined : process.argv[index + 1];
9
+ }
10
+ const command = process.argv[2];
11
+ const input = option("--input");
12
+ const output = option("--output");
13
+ if (!input || !output)
14
+ throw new Error(`${command ?? "command"} requires --input and --output`);
15
+ if (command === "checksums") {
16
+ const options = {
17
+ inputDirectory: path.resolve(input),
18
+ manifestPath: path.resolve(output),
19
+ pathBase: path.resolve(option("--base") ?? input),
20
+ };
21
+ const verify = process.argv.includes("--verify");
22
+ const count = verify ? await verifyChecksumManifest(options) : await writeChecksumManifest(options);
23
+ console.log(`${verify ? "Verified" : "Wrote"} ${count} SHA-256 entries.`);
24
+ }
25
+ else if (command === "package") {
26
+ const timestamp = option("--timestamp");
27
+ if (!timestamp || Number.isNaN(new Date(timestamp).getTime())) {
28
+ throw new Error("package requires a valid --timestamp");
29
+ }
30
+ const packageJsonPath = option("--package-json");
31
+ const resolvedOutput = packageJsonPath
32
+ ? output.replace("{version}", String(JSON.parse(await readFile(path.resolve(packageJsonPath), "utf8")).version))
33
+ : output;
34
+ const result = await createOfflinePackage({
35
+ inputDirectory: input,
36
+ archivePath: resolvedOutput,
37
+ fixedTimestamp: new Date(timestamp),
38
+ });
39
+ console.log(`Created and verified ${path.basename(result.archivePath)} (${result.size} bytes).`);
40
+ console.log(`SHA-256: ${result.sha256}`);
41
+ }
42
+ else {
43
+ throw new Error("Usage: memorial-lobby-toolkit <checksums|package> --input <path> --output <path>");
44
+ }
45
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAChC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAChC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAClC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;IAAE,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,IAAI,SAAS,gCAAgC,CAAC,CAAC;AAChG,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG;QACd,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAClC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;KAClD,CAAC;IACF,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,IAAI,KAAK,mBAAmB,CAAC,CAAC;AAC5E,CAAC;KAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,eAAe,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,eAAe;QACpC,CAAC,CAAC,MAAM,CAAC,OAAO,CACZ,WAAW,EACX,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAClF;QACH,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC;QACxC,cAAc,EAAE,KAAK;QACrB,WAAW,EAAE,cAAc;QAC3B,cAAc,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC;KACpC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3C,CAAC;KAAM,CAAC;IACN,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;AACtG,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface DistributionValidationOptions {
2
+ readonly rootDirectory: string;
3
+ readonly requiredFiles: readonly string[];
4
+ readonly offlineTextExtensions?: ReadonlySet<string>;
5
+ readonly remoteDependencyPattern?: RegExp;
6
+ }
7
+ export declare function validateDistributionFiles(options: DistributionValidationOptions): Promise<void>;
8
+ //# sourceMappingURL=distribution-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distribution-validation.d.ts","sourceRoot":"","sources":["../src/distribution-validation.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAC3C;AAYD,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBrG"}
@@ -0,0 +1,35 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ async function walk(directory) {
4
+ const files = [];
5
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
6
+ const absolute = path.join(directory, entry.name);
7
+ if (entry.isDirectory())
8
+ files.push(...await walk(absolute));
9
+ else if (entry.isFile())
10
+ files.push(absolute);
11
+ }
12
+ return files;
13
+ }
14
+ export async function validateDistributionFiles(options) {
15
+ const root = path.resolve(options.rootDirectory);
16
+ for (const relative of options.requiredFiles) {
17
+ const absolute = path.resolve(root, relative);
18
+ const prefix = `${root}${path.sep}`;
19
+ if (!absolute.startsWith(prefix))
20
+ throw new Error(`Required file escapes distribution: ${relative}`);
21
+ const info = await stat(absolute).catch(() => undefined);
22
+ if (!info?.isFile() || info.size === 0)
23
+ throw new Error(`Invalid distribution file: ${relative}`);
24
+ }
25
+ const extensions = options.offlineTextExtensions ?? new Set([".html", ".js", ".css", ".json"]);
26
+ const remotePattern = options.remoteDependencyPattern ?? /https?:\/\//i;
27
+ for (const file of await walk(root)) {
28
+ if (!extensions.has(path.extname(file).toLowerCase()))
29
+ continue;
30
+ if (remotePattern.test(await readFile(file, "utf8"))) {
31
+ throw new Error(`Remote dependency found in ${path.relative(root, file).replace(/\\/g, "/")}`);
32
+ }
33
+ }
34
+ }
35
+ //# sourceMappingURL=distribution-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distribution-validation.js","sourceRoot":"","sources":["../src/distribution-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,KAAK,UAAU,IAAI,CAAC,SAAiB;IACnC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;aACxD,IAAI,KAAK,CAAC,MAAM,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAAsC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACjD,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,QAAQ,EAAE,CAAC,CAAC;QACrG,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,qBAAqB,IAAI,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/F,MAAM,aAAa,GAAG,OAAO,CAAC,uBAAuB,IAAI,cAAc,CAAC;IACxE,KAAK,MAAM,IAAI,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAAE,SAAS;QAChE,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from "./checksums.js";
2
+ export * from "./distribution-validation.js";
3
+ export * from "./offline-package.js";
4
+ export * from "./vite/index.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./checksums.js";
2
+ export * from "./distribution-validation.js";
3
+ export * from "./offline-package.js";
4
+ export * from "./vite/index.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface OfflinePackageOptions {
2
+ readonly inputDirectory: string;
3
+ readonly archivePath: string;
4
+ readonly fixedTimestamp: Date;
5
+ readonly requiredEntry?: string;
6
+ }
7
+ export interface OfflinePackageResult {
8
+ readonly archivePath: string;
9
+ readonly checksumPath: string;
10
+ readonly sha256: string;
11
+ readonly size: number;
12
+ readonly files: readonly string[];
13
+ }
14
+ export declare function createOfflinePackage(options: OfflinePackageOptions): Promise<OfflinePackageResult>;
15
+ //# sourceMappingURL=offline-package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-package.d.ts","sourceRoot":"","sources":["../src/offline-package.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAkED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyCxG"}
@@ -0,0 +1,116 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream, createWriteStream } from "node:fs";
3
+ import { mkdir, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { once } from "node:events";
6
+ import yauzl from "yauzl";
7
+ import yazl from "yazl";
8
+ import { createChecksumManifest } from "./checksums.js";
9
+ async function walk(directory, root = directory) {
10
+ const files = [];
11
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
12
+ const absolute = path.join(directory, entry.name);
13
+ if (entry.isDirectory())
14
+ files.push(...await walk(absolute, root));
15
+ else if (entry.isFile())
16
+ files.push({ absolute, relative: path.relative(root, absolute).replace(/\\/g, "/") });
17
+ }
18
+ return files;
19
+ }
20
+ async function fileSha256(file) {
21
+ const hash = createHash("sha256");
22
+ for await (const chunk of createReadStream(file))
23
+ hash.update(chunk);
24
+ return hash.digest("hex");
25
+ }
26
+ async function verifyArchive(archivePath, inputDirectory, expectedFiles) {
27
+ const zip = await new Promise((resolve, reject) => yauzl.open(archivePath, { lazyEntries: true }, (error, value) => error || !value ? reject(error ?? new Error("Failed to open ZIP archive")) : resolve(value)));
28
+ const actualFiles = [];
29
+ try {
30
+ zip.readEntry();
31
+ while (true) {
32
+ const event = await new Promise((resolve, reject) => {
33
+ const cleanup = () => {
34
+ zip.off("entry", onEntry);
35
+ zip.off("end", onEnd);
36
+ zip.off("error", onError);
37
+ };
38
+ const onEntry = (entry) => { cleanup(); resolve({ entry }); };
39
+ const onEnd = () => { cleanup(); resolve({}); };
40
+ const onError = (error) => { cleanup(); reject(error); };
41
+ zip.once("entry", onEntry);
42
+ zip.once("end", onEnd);
43
+ zip.once("error", onError);
44
+ });
45
+ if (!event.entry)
46
+ break;
47
+ const entry = event.entry;
48
+ if (/\/$/.test(entry.fileName)) {
49
+ zip.readEntry();
50
+ continue;
51
+ }
52
+ actualFiles.push(entry.fileName);
53
+ const sourcePath = path.resolve(inputDirectory, ...entry.fileName.split("/"));
54
+ const prefix = `${path.resolve(inputDirectory)}${path.sep}`;
55
+ if (!sourcePath.startsWith(prefix))
56
+ throw new Error(`Archive entry escapes input directory: ${entry.fileName}`);
57
+ const stream = await new Promise((resolve, reject) => zip.openReadStream(entry, (error, value) => error ? reject(error) : resolve(value)));
58
+ const hash = createHash("sha256");
59
+ for await (const chunk of stream)
60
+ hash.update(chunk);
61
+ if (hash.digest("hex") !== await fileSha256(sourcePath)) {
62
+ throw new Error(`Archive checksum mismatch: ${entry.fileName}`);
63
+ }
64
+ zip.readEntry();
65
+ }
66
+ }
67
+ finally {
68
+ zip.close();
69
+ }
70
+ if (actualFiles.join("\n") !== expectedFiles.join("\n")) {
71
+ throw new Error("Archive file list does not match input directory");
72
+ }
73
+ }
74
+ export async function createOfflinePackage(options) {
75
+ const inputDirectory = path.resolve(options.inputDirectory);
76
+ const archivePath = path.resolve(options.archivePath);
77
+ const requiredEntry = options.requiredEntry ?? "index.html";
78
+ const required = path.join(inputDirectory, requiredEntry);
79
+ if (!(await stat(required).catch(() => undefined))?.isFile()) {
80
+ throw new Error(`${requiredEntry} is missing from package input`);
81
+ }
82
+ const manifestPath = path.join(inputDirectory, "MANIFEST.sha256");
83
+ const manifest = await createChecksumManifest({
84
+ inputDirectory,
85
+ manifestPath,
86
+ pathBase: inputDirectory,
87
+ ignore: (file) => path.resolve(file) === manifestPath,
88
+ });
89
+ await writeFile(manifestPath, manifest, "utf8");
90
+ const files = (await walk(inputDirectory)).sort((left, right) => left.relative.localeCompare(right.relative, "en"));
91
+ await mkdir(path.dirname(archivePath), { recursive: true });
92
+ const temporaryArchive = `${archivePath}.tmp`;
93
+ await rm(temporaryArchive, { force: true });
94
+ const zip = new yazl.ZipFile();
95
+ for (const file of files) {
96
+ zip.addFile(file.absolute, file.relative, { mtime: options.fixedTimestamp, mode: 0o100644 });
97
+ }
98
+ zip.end();
99
+ const output = createWriteStream(temporaryArchive, { flags: "wx" });
100
+ zip.outputStream.pipe(output);
101
+ await once(output, "close");
102
+ await verifyArchive(temporaryArchive, inputDirectory, files.map((file) => file.relative));
103
+ await rm(archivePath, { force: true });
104
+ await rename(temporaryArchive, archivePath);
105
+ const sha256 = await fileSha256(archivePath);
106
+ const checksumPath = `${archivePath}.sha256`;
107
+ await writeFile(checksumPath, `${sha256} ${path.basename(archivePath)}\n`, "utf8");
108
+ return {
109
+ archivePath,
110
+ checksumPath,
111
+ sha256,
112
+ size: (await stat(archivePath)).size,
113
+ files: files.map((file) => file.relative),
114
+ };
115
+ }
116
+ //# sourceMappingURL=offline-package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-package.js","sourceRoot":"","sources":["../src/offline-package.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,OAAO,EAAY,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAiBxD,KAAK,UAAU,IAAI,CAAC,SAAiB,EAAE,IAAI,GAAG,SAAS;IACrD,MAAM,KAAK,GAAkD,EAAE,CAAC;IAChE,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;aAC9D,IAAI,KAAK,CAAC,MAAM,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,WAAmB,EAAE,cAAsB,EAAE,aAAgC;IACxG,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAC7D,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAC9D,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAC5F,CACF,CAAC;IACF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,GAAG,CAAC,SAAS,EAAE,CAAC;QAChB,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrE,MAAM,OAAO,GAAG,GAAG,EAAE;oBACnB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAC1B,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;oBACtB,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC5B,CAAC,CAAC;gBACF,MAAM,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrE,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,MAAM,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC3B,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,KAAK;gBAAE,MAAM;YACxB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAC,GAAG,CAAC,SAAS,EAAE,CAAC;gBAAC,SAAS;YAAC,CAAC;YAC9D,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9E,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChH,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAC1E,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACpF,CAAC;YACF,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;YAClC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACxD,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YAClE,CAAC;YACD,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,KAAK,EAAE,CAAC;IACd,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAA8B;IACvE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,YAAY,CAAC;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,gCAAgC,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC;QAC5C,cAAc;QACd,YAAY;QACZ,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,YAAY;KACtD,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IACpH,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,MAAM,gBAAgB,GAAG,GAAG,WAAW,MAAM,CAAC;IAC9C,MAAM,EAAE,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,GAAG,CAAC,GAAG,EAAE,CAAC;IACV,MAAM,MAAM,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,MAAM,aAAa,CAAC,gBAAgB,EAAE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1F,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,GAAG,WAAW,SAAS,CAAC;IAC7C,MAAM,SAAS,CAAC,YAAY,EAAE,GAAG,MAAM,KAAK,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpF,OAAO;QACL,WAAW;QACX,YAAY;QACZ,MAAM;QACN,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QACpC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;KAC1C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createMemorialLobbyVitePlugins } from "./plugins.js";
2
+ export type { MemorialLobbyVitePluginOptions } from "./plugins.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createMemorialLobbyVitePlugins } from "./plugins.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Plugin } from "vite";
2
+ export interface MemorialLobbyVitePluginOptions {
3
+ readonly loggingBootstrapPath: string;
4
+ readonly outputDirectory?: string;
5
+ readonly logRoute?: string;
6
+ readonly maximumLogBodyCharacters?: number;
7
+ }
8
+ export declare function createMemorialLobbyVitePlugins(options: MemorialLobbyVitePluginOptions): Plugin[];
9
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/vite/plugins.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CAC5C;AAID,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM,EAAE,CAwEhG"}
@@ -0,0 +1,78 @@
1
+ import { appendFile, copyFile, mkdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const SESSION_FILE_PATTERN = /^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3}_[a-z0-9]{6}\.log$/;
4
+ export function createMemorialLobbyVitePlugins(options) {
5
+ const outputDirectory = path.resolve(options.outputDirectory ?? "dist");
6
+ const logDirectory = path.join(outputDirectory, "log");
7
+ const logRoute = options.logRoute ?? "/__memorial-lobby-log";
8
+ const maximumBody = options.maximumLogBodyCharacters ?? 1024 * 1024;
9
+ const runtimeAssets = {
10
+ name: "memorial-lobby-runtime-assets",
11
+ configureServer(server) {
12
+ server.middlewares.use(async (request, response, next) => {
13
+ const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
14
+ if (pathname !== "/logging-bootstrap.js")
15
+ return next();
16
+ response.setHeader("Content-Type", "text/javascript; charset=utf-8");
17
+ response.end(await readFile(options.loggingBootstrapPath));
18
+ });
19
+ },
20
+ async writeBundle() {
21
+ await mkdir(outputDirectory, { recursive: true });
22
+ await copyFile(options.loggingBootstrapPath, path.join(outputDirectory, "logging-bootstrap.js"));
23
+ },
24
+ };
25
+ const installLogMiddleware = (server) => {
26
+ server.middlewares.use((request, response, next) => {
27
+ const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
28
+ if (request.method !== "POST" || pathname !== `${logRoute}/append`)
29
+ return next();
30
+ let body = "";
31
+ let rejected = false;
32
+ request.setEncoding("utf8");
33
+ request.on("data", (chunk) => {
34
+ if (rejected)
35
+ return;
36
+ if (body.length + chunk.length > maximumBody) {
37
+ rejected = true;
38
+ response.statusCode = 413;
39
+ response.end("Log payload too large");
40
+ return;
41
+ }
42
+ body += chunk;
43
+ });
44
+ request.on("end", async () => {
45
+ if (rejected || response.writableEnded)
46
+ return;
47
+ try {
48
+ const payload = JSON.parse(body);
49
+ if (typeof payload.sessionFile !== "string" ||
50
+ !SESSION_FILE_PATTERN.test(payload.sessionFile) ||
51
+ !Array.isArray(payload.lines) ||
52
+ payload.lines.some((line) => typeof line !== "string")) {
53
+ response.statusCode = 400;
54
+ response.end("Invalid log payload");
55
+ return;
56
+ }
57
+ await mkdir(logDirectory, { recursive: true });
58
+ await appendFile(path.join(logDirectory, payload.sessionFile), `${payload.lines.join("\n")}\n`, "utf8");
59
+ response.statusCode = 204;
60
+ response.end();
61
+ }
62
+ catch (error) {
63
+ if (response.writableEnded)
64
+ return;
65
+ response.statusCode = error instanceof SyntaxError ? 400 : 500;
66
+ response.end(error instanceof SyntaxError ? "Invalid JSON payload" : error instanceof Error ? error.message : String(error));
67
+ }
68
+ });
69
+ });
70
+ };
71
+ return [runtimeAssets, {
72
+ name: "memorial-lobby-local-log-bridge",
73
+ configureServer: installLogMiddleware,
74
+ configurePreviewServer: installLogMiddleware,
75
+ async writeBundle() { await mkdir(logDirectory, { recursive: true }); },
76
+ }];
77
+ }
78
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/vite/plugins.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "ba-memorial-lobby-wallpaper-toolkit",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "README.md"
8
+ ],
9
+ "bin": {
10
+ "memorial-lobby-toolkit": "./dist/cli.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./vite": {
18
+ "types": "./dist/vite/index.d.ts",
19
+ "import": "./dist/vite/index.js"
20
+ }
21
+ },
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "npm run build && node --test --experimental-strip-types test/*.test.ts",
26
+ "check": "npm run build && npm run typecheck && node --test --experimental-strip-types test/*.test.ts",
27
+ "prepack": "npm run check"
28
+ },
29
+ "peerDependencies": {
30
+ "vite": ">=8.0.0 <9"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "24.10.1",
34
+ "@types/yauzl": "^3.4.0",
35
+ "@types/yazl": "^3.3.1",
36
+ "typescript": "7.0.2",
37
+ "vite": "8.2.1"
38
+ },
39
+ "dependencies": {
40
+ "yauzl": "^3.4.0",
41
+ "yazl": "^3.3.1"
42
+ }
43
+ }