Fix StartOS 0.4 TypeScript packaging to match SDK API

This commit is contained in:
MacPro
2026-04-09 15:10:44 -05:00
parent d5046a0daf
commit 0b70cbb2bf
3436 changed files with 867051 additions and 92 deletions
+83
View File
@@ -0,0 +1,83 @@
import { DataUrl, Manifest, MerkleArchiveCommitment, PackageId } from '../osBindings';
import { MerkleArchive } from './merkleArchive';
/**
* Compares two `Uint8Array` instances byte-by-byte for equality.
*
* @returns `true` if both arrays have the same length and identical bytes
*/
export declare function compare(a: Uint8Array, b: Uint8Array): boolean;
/**
* Represents a parsed `.s9pk` package archive — the binary distribution format for StartOS services.
*
* An `S9pk` wraps a verified {@link Manifest}, a {@link MerkleArchive} containing the package's
* assets (icon, license, dependency metadata), and the total archive size in bytes.
*
* @example
* ```ts
* const s9pk = await S9pk.deserialize(file, null)
* console.log(s9pk.manifest.id) // e.g. "bitcoind"
* console.log(s9pk.size) // archive size in bytes
* const icon = await s9pk.icon() // base64 data URL
* const license = await s9pk.license()
* ```
*/
export declare class S9pk {
/** The parsed package manifest containing metadata, dependencies, and interface definitions. */
readonly manifest: Manifest;
/** The Merkle-verified archive containing the package's files. */
readonly archive: MerkleArchive;
/** The total size of the archive in bytes. */
readonly size: number;
private constructor();
/**
* Deserializes an `S9pk` from a `Blob` (e.g. a `File` from a browser file input).
*
* Validates the magic bytes and version header, then parses the Merkle archive structure.
* If a `commitment` is provided, the archive is cryptographically verified against it.
*
* @param source - The raw `.s9pk` file as a `Blob`
* @param commitment - An optional Merkle commitment to verify the archive against, or `null` to skip verification
* @returns A fully parsed `S9pk` instance
* @throws If the magic bytes are invalid or the archive fails verification
*/
static deserialize(source: Blob, commitment: MerkleArchiveCommitment | null): Promise<S9pk>;
/**
* Extracts the package icon from the archive and returns it as a base64-encoded data URL.
*
* Looks for a file named `icon.*` with an image MIME type (e.g. `icon.png`, `icon.svg`).
*
* @returns A data URL string like `"data:image/png;base64,..."` suitable for use in `<img src>`.
* @throws If no icon file is found in the archive
*/
icon(): Promise<DataUrl>;
/**
* Returns the metadata (e.g. `{ title }`) for a specific dependency by its package ID.
*
* @param id - The dependency's package identifier (e.g. `"bitcoind"`)
* @returns The dependency metadata object, or `null` if the dependency is not present in the archive
*/
dependencyMetadataFor(id: PackageId): Promise<{
title: string;
} | null>;
/**
* Returns the icon for a specific dependency as a base64 data URL.
*
* @param id - The dependency's package identifier
* @returns A data URL string, or `null` if the dependency or its icon is not present
*/
dependencyIconFor(id: PackageId): Promise<string | null>;
/**
* Returns a merged record of all dependency metadata (title, icon, description, optional flag)
* for every dependency declared in the manifest.
*
* @returns A record keyed by package ID, each containing `{ title, icon, description, optional }`
*/
dependencyMetadata(): Promise<any>;
/**
* Reads and returns the `LICENSE.md` file from the archive as a UTF-8 string.
*
* @returns The full license text
* @throws If `LICENSE.md` is not found in the archive
*/
license(): Promise<string>;
}
+160
View File
@@ -0,0 +1,160 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.S9pk = void 0;
exports.compare = compare;
const merkleArchive_1 = require("./merkleArchive");
const mime_1 = __importDefault(require("mime"));
const directoryContents_1 = require("./merkleArchive/directoryContents");
const fileContents_1 = require("./merkleArchive/fileContents");
const magicAndVersion = new Uint8Array([59, 59, 2]);
/**
* Compares two `Uint8Array` instances byte-by-byte for equality.
*
* @returns `true` if both arrays have the same length and identical bytes
*/
function compare(a, b) {
if (a.length !== b.length)
return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i])
return false;
}
return true;
}
/**
* Represents a parsed `.s9pk` package archive — the binary distribution format for StartOS services.
*
* An `S9pk` wraps a verified {@link Manifest}, a {@link MerkleArchive} containing the package's
* assets (icon, license, dependency metadata), and the total archive size in bytes.
*
* @example
* ```ts
* const s9pk = await S9pk.deserialize(file, null)
* console.log(s9pk.manifest.id) // e.g. "bitcoind"
* console.log(s9pk.size) // archive size in bytes
* const icon = await s9pk.icon() // base64 data URL
* const license = await s9pk.license()
* ```
*/
class S9pk {
constructor(
/** The parsed package manifest containing metadata, dependencies, and interface definitions. */
manifest,
/** The Merkle-verified archive containing the package's files. */
archive,
/** The total size of the archive in bytes. */
size) {
this.manifest = manifest;
this.archive = archive;
this.size = size;
}
/**
* Deserializes an `S9pk` from a `Blob` (e.g. a `File` from a browser file input).
*
* Validates the magic bytes and version header, then parses the Merkle archive structure.
* If a `commitment` is provided, the archive is cryptographically verified against it.
*
* @param source - The raw `.s9pk` file as a `Blob`
* @param commitment - An optional Merkle commitment to verify the archive against, or `null` to skip verification
* @returns A fully parsed `S9pk` instance
* @throws If the magic bytes are invalid or the archive fails verification
*/
static async deserialize(source, commitment) {
const header = new merkleArchive_1.ArrayBufferReader(await source
.slice(0, magicAndVersion.length + merkleArchive_1.MerkleArchive.headerSize)
.arrayBuffer());
const magicVersion = new Uint8Array(header.next(magicAndVersion.length));
if (!compare(magicVersion, magicAndVersion)) {
throw new Error('Invalid Magic or Unexpected Version');
}
const archive = await merkleArchive_1.MerkleArchive.deserialize(source, 's9pk', header, commitment);
const manifest = JSON.parse(new TextDecoder().decode(await archive.contents
.getPath(['manifest.json'])
?.verifiedFileContents()));
return new S9pk(manifest, archive, source.size);
}
/**
* Extracts the package icon from the archive and returns it as a base64-encoded data URL.
*
* Looks for a file named `icon.*` with an image MIME type (e.g. `icon.png`, `icon.svg`).
*
* @returns A data URL string like `"data:image/png;base64,..."` suitable for use in `<img src>`.
* @throws If no icon file is found in the archive
*/
async icon() {
const iconName = Object.keys(this.archive.contents.contents).find((name) => name.startsWith('icon.') && mime_1.default.getType(name)?.startsWith('image/'));
if (!iconName) {
throw new Error('no icon found in archive');
}
return (`data:${mime_1.default.getType(iconName)};base64,` +
Buffer.from(await this.archive.contents.getPath([iconName]).verifiedFileContents()).toString('base64'));
}
/**
* Returns the metadata (e.g. `{ title }`) for a specific dependency by its package ID.
*
* @param id - The dependency's package identifier (e.g. `"bitcoind"`)
* @returns The dependency metadata object, or `null` if the dependency is not present in the archive
*/
async dependencyMetadataFor(id) {
const entry = this.archive.contents.getPath([
'dependencies',
id,
'metadata.json',
]);
if (!entry)
return null;
return JSON.parse(new TextDecoder().decode(await entry.verifiedFileContents()));
}
/**
* Returns the icon for a specific dependency as a base64 data URL.
*
* @param id - The dependency's package identifier
* @returns A data URL string, or `null` if the dependency or its icon is not present
*/
async dependencyIconFor(id) {
const dir = this.archive.contents.getPath(['dependencies', id]);
if (!dir || !(dir.contents instanceof directoryContents_1.DirectoryContents))
return null;
const iconName = Object.keys(dir.contents.contents).find((name) => name.startsWith('icon.') && mime_1.default.getType(name)?.startsWith('image/'));
if (!iconName)
return null;
return (`data:${mime_1.default.getType(iconName)};base64,` +
Buffer.from(await dir.contents.getPath([iconName]).verifiedFileContents()).toString('base64'));
}
/**
* Returns a merged record of all dependency metadata (title, icon, description, optional flag)
* for every dependency declared in the manifest.
*
* @returns A record keyed by package ID, each containing `{ title, icon, description, optional }`
*/
async dependencyMetadata() {
return Object.fromEntries(await Promise.all(Object.entries(this.manifest.dependencies)
.filter(([_, info]) => !!info)
.map(async ([id, info]) => [
id,
{
...(await this.dependencyMetadataFor(id)),
icon: await this.dependencyIconFor(id),
description: info.description,
optional: info.optional,
},
])));
}
/**
* Reads and returns the `LICENSE.md` file from the archive as a UTF-8 string.
*
* @returns The full license text
* @throws If `LICENSE.md` is not found in the archive
*/
async license() {
const file = this.archive.contents.getPath(['LICENSE.md']);
if (!file || !(file.contents instanceof fileContents_1.FileContents))
throw new Error('license.md not found in archive');
return new TextDecoder().decode(await file.verifiedFileContents());
}
}
exports.S9pk = S9pk;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../base/lib/s9pk/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
import { ArrayBufferReader, Entry } from '.';
export declare class DirectoryContents {
readonly contents: {
[name: string]: Entry;
};
static readonly headerSize: number;
private constructor();
static deserialize(source: Blob, header: ArrayBufferReader, sighash: Uint8Array, maxSize: bigint): Promise<DirectoryContents>;
sighash(): Uint8Array;
getPath(path: string[]): Entry | null;
}
@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DirectoryContents = void 0;
const _1 = require(".");
const blake3_1 = require("@noble/hashes/blake3");
const varint_1 = require("./varint");
const __1 = require("..");
class DirectoryContents {
constructor(contents) {
this.contents = contents;
}
static async deserialize(source, header, sighash, maxSize) {
const position = header.nextU64();
const size = header.nextU64();
if (size > maxSize) {
throw new Error('size is greater than signed');
}
const tocReader = new _1.ArrayBufferReader(await source
.slice(Number(position), Number(position + size))
.arrayBuffer());
const len = tocReader.nextVarint();
const entries = {};
for (let i = 0; i < len; i++) {
const name = tocReader.nextVarstring();
const entry = await _1.Entry.deserialize(source, tocReader);
entries[name] = entry;
}
const res = new DirectoryContents(entries);
if (!(0, __1.compare)(res.sighash(), sighash)) {
throw new Error('hash sum does not match');
}
return res;
}
sighash() {
const hasher = blake3_1.blake3.create({});
const names = Object.keys(this.contents).sort();
hasher.update(new Uint8Array((0, varint_1.serializeVarint)(names.length)));
for (const name of names) {
const entry = this.contents[name];
const nameBuf = new TextEncoder().encode(name);
hasher.update(new Uint8Array((0, varint_1.serializeVarint)(nameBuf.length)));
hasher.update(nameBuf);
hasher.update(new Uint8Array(entry.hash));
const sizeBuf = new Uint8Array(8);
new DataView(sizeBuf.buffer).setBigUint64(0, entry.size);
hasher.update(sizeBuf);
hasher.update(new Uint8Array([0]));
}
return hasher.digest();
}
getPath(path) {
if (path.length === 0) {
return null;
}
const next = this.contents[path[0]];
const rest = path.slice(1);
if (next === undefined) {
return null;
}
if (rest.length === 0) {
return next;
}
if (next.contents instanceof DirectoryContents) {
return next.contents.getPath(rest);
}
return null;
}
}
exports.DirectoryContents = DirectoryContents;
DirectoryContents.headerSize = 8 + // position: u64 BE
8; // size: u64 BE
//# sourceMappingURL=directoryContents.js.map
@@ -0,0 +1 @@
{"version":3,"file":"directoryContents.js","sourceRoot":"","sources":["../../../../../base/lib/s9pk/merkleArchive/directoryContents.ts"],"names":[],"mappings":";;;AAAA,wBAA4C;AAC5C,iDAA6C;AAC7C,qCAA0C;AAE1C,0BAA4B;AAE5B,MAAa,iBAAiB;IAI5B,YAA6B,QAAmC;QAAnC,aAAQ,GAAR,QAAQ,CAA2B;IAAG,CAAC;IACpE,MAAM,CAAC,KAAK,CAAC,WAAW,CACtB,MAAY,EACZ,MAAyB,EACzB,OAAmB,EACnB,OAAe;QAEf,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;QAC7B,IAAI,IAAI,GAAG,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QAChD,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,oBAAiB,CACrC,MAAM,MAAM;aACT,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;aAChD,WAAW,EAAE,CACjB,CAAA;QACD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,CAAA;QAClC,MAAM,OAAO,GAA8B,EAAE,CAAA;QAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,EAAE,CAAA;YACtC,MAAM,KAAK,GAAG,MAAM,QAAK,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;YACxD,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;QACvB,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAE1C,IAAI,CAAC,IAAA,WAAO,EAAC,GAAG,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;QAC5C,CAAC;QAED,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,OAAO;QACL,MAAM,MAAM,GAAG,eAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAA;QAC/C,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,IAAA,wBAAe,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAC5D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;YACjC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,IAAA,wBAAe,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YAC9D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACtB,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;YACzC,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACjC,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YACxD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACtB,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACpC,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,EAAE,CAAA;IACxB,CAAC;IACD,OAAO,CAAC,IAAc;QACpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC1B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,YAAY,iBAAiB,EAAE,CAAC;YAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACpC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;;AAxEH,8CAyEC;AAxEiB,4BAAU,GACxB,CAAC,GAAG,mBAAmB;IACvB,CAAC,CAAA,CAAC,eAAe"}
@@ -0,0 +1,7 @@
import { ArrayBufferReader } from '.';
export declare class FileContents {
readonly contents: Blob;
private constructor();
static deserialize(source: Blob, header: ArrayBufferReader, size: bigint): FileContents;
verified(hash: Uint8Array): Promise<ArrayBuffer>;
}
@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileContents = void 0;
const blake3_1 = require("@noble/hashes/blake3");
const __1 = require("..");
class FileContents {
constructor(contents) {
this.contents = contents;
}
static deserialize(source, header, size) {
const position = header.nextU64();
return new FileContents(source.slice(Number(position), Number(position + size)));
}
async verified(hash) {
const res = await this.contents.arrayBuffer();
if (!(0, __1.compare)(hash, (0, blake3_1.blake3)(new Uint8Array(res)))) {
throw new Error('hash sum mismatch');
}
return res;
}
}
exports.FileContents = FileContents;
//# sourceMappingURL=fileContents.js.map
@@ -0,0 +1 @@
{"version":3,"file":"fileContents.js","sourceRoot":"","sources":["../../../../../base/lib/s9pk/merkleArchive/fileContents.ts"],"names":[],"mappings":";;;AAAA,iDAA6C;AAE7C,0BAA4B;AAE5B,MAAa,YAAY;IACvB,YAA6B,QAAc;QAAd,aAAQ,GAAR,QAAQ,CAAM;IAAG,CAAC;IAC/C,MAAM,CAAC,WAAW,CAChB,MAAY,EACZ,MAAyB,EACzB,IAAY;QAEZ,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;QACjC,OAAO,IAAI,YAAY,CACrB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CACxD,CAAA;IACH,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,IAAgB;QAC7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;QAC7C,IAAI,CAAC,IAAA,WAAO,EAAC,IAAI,EAAE,IAAA,eAAM,EAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;QACtC,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAnBD,oCAmBC"}
@@ -0,0 +1,33 @@
import { MerkleArchiveCommitment } from '../../osBindings';
import { DirectoryContents } from './directoryContents';
import { FileContents } from './fileContents';
export type Signer = {
pubkey: Uint8Array;
signature: Uint8Array;
maxSize: bigint;
context: string;
};
export declare class ArrayBufferReader {
private buffer;
constructor(buffer: ArrayBuffer);
next(length: number): ArrayBuffer;
nextU64(): bigint;
nextVarint(): number;
nextVarstring(): string;
}
export declare class MerkleArchive {
readonly signer: Signer;
readonly contents: DirectoryContents;
static readonly headerSize: number;
private constructor();
static deserialize(source: Blob, context: string, header: ArrayBufferReader, commitment: MerkleArchiveCommitment | null): Promise<MerkleArchive>;
}
export declare class Entry {
readonly hash: Uint8Array;
readonly size: bigint;
readonly contents: EntryContents;
private constructor();
static deserialize(source: Blob, header: ArrayBufferReader): Promise<Entry>;
verifiedFileContents(): Promise<ArrayBuffer>;
}
export type EntryContents = null | FileContents | DirectoryContents;
+119
View File
@@ -0,0 +1,119 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Entry = exports.MerkleArchive = exports.ArrayBufferReader = void 0;
const directoryContents_1 = require("./directoryContents");
const fileContents_1 = require("./fileContents");
const ed25519_1 = require("@noble/curves/ed25519");
const varint_1 = require("./varint");
const __1 = require("..");
const maxVarstringLen = 1024 * 1024;
class ArrayBufferReader {
constructor(buffer) {
this.buffer = buffer;
}
next(length) {
const res = this.buffer.slice(0, length);
this.buffer = this.buffer.slice(length);
return res;
}
nextU64() {
return new DataView(this.next(8)).getBigUint64(0);
}
nextVarint() {
const p = new varint_1.VarIntProcessor();
while (!p.finished()) {
p.push(new Uint8Array(this.buffer.slice(0, 1))[0]);
this.buffer = this.buffer.slice(1);
}
const res = p.decode();
if (res === null) {
throw new Error('Reached EOF');
}
return res;
}
nextVarstring() {
const len = Math.min(this.nextVarint(), maxVarstringLen);
return new TextDecoder().decode(this.next(len));
}
}
exports.ArrayBufferReader = ArrayBufferReader;
class MerkleArchive {
constructor(signer, contents) {
this.signer = signer;
this.contents = contents;
}
static async deserialize(source, context, header, commitment) {
const pubkey = new Uint8Array(header.next(32));
const signature = new Uint8Array(header.next(64));
const sighash = new Uint8Array(header.next(32));
const rootMaxSizeBytes = header.next(8);
const maxSize = new DataView(rootMaxSizeBytes).getBigUint64(0);
if (!ed25519_1.ed25519ph.verify(signature, new Uint8Array(await new Blob([sighash, rootMaxSizeBytes]).arrayBuffer()), pubkey, {
context: new TextEncoder().encode(context),
zip215: true,
})) {
throw new Error('signature verification failed');
}
if (commitment) {
if (!(0, __1.compare)(sighash, new Uint8Array(Buffer.from(commitment.rootSighash, 'base64').buffer))) {
throw new Error('merkle root mismatch');
}
if (maxSize > commitment.rootMaxsize) {
throw new Error('root directory max size too large');
}
}
else if (maxSize > 1024 * 1024) {
throw new Error('root directory max size over 1MiB, cancelling download in case of DOS attack');
}
const contents = await directoryContents_1.DirectoryContents.deserialize(source, header, sighash, maxSize);
return new MerkleArchive({
pubkey,
signature,
maxSize,
context,
}, contents);
}
}
exports.MerkleArchive = MerkleArchive;
MerkleArchive.headerSize = 32 + // pubkey
64 + // signature
32 + // sighash
8 + // size
directoryContents_1.DirectoryContents.headerSize;
class Entry {
constructor(hash, size, contents) {
this.hash = hash;
this.size = size;
this.contents = contents;
}
static async deserialize(source, header) {
const hash = new Uint8Array(header.next(32));
const size = header.nextU64();
const contents = await deserializeEntryContents(source, header, hash, size);
return new Entry(new Uint8Array(hash), size, contents);
}
async verifiedFileContents() {
if (!this.contents) {
throw new Error('file is missing from archive');
}
if (!(this.contents instanceof fileContents_1.FileContents)) {
throw new Error('is not a regular file');
}
return this.contents.verified(this.hash);
}
}
exports.Entry = Entry;
async function deserializeEntryContents(source, header, hash, size) {
const typeId = new Uint8Array(header.next(1))[0];
switch (typeId) {
case 0:
return null;
case 1:
return fileContents_1.FileContents.deserialize(source, header, size);
case 2:
return directoryContents_1.DirectoryContents.deserialize(source, header, hash, size);
default:
throw new Error(`Unknown type id ${typeId} found in MerkleArchive`);
}
}
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../base/lib/s9pk/merkleArchive/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
export declare class VarIntProcessor {
private buf;
private i;
constructor();
push(b: number): void;
finished(): boolean;
decode(): number | null;
}
export declare function serializeVarint(int: number): ArrayBuffer;
@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VarIntProcessor = void 0;
exports.serializeVarint = serializeVarint;
const util_1 = require("../../util");
const msb = 0x80;
const dropMsb = 0x7f;
const maxSize = Math.floor((8 * 8 + 7) / 7);
class VarIntProcessor {
constructor() {
this.buf = new Uint8Array(maxSize);
this.i = 0;
}
push(b) {
if (this.i >= maxSize) {
throw new Error('Unterminated varint');
}
this.buf[this.i] = b;
this.i += 1;
}
finished() {
return this.i > 0 && (this.buf[this.i - 1] & msb) === 0;
}
decode() {
let result = 0;
let shift = 0;
let success = false;
for (let i = 0; i < this.i; i++) {
const b = this.buf[i];
const msbDropped = b & dropMsb;
result |= msbDropped << shift;
shift += 7;
if ((b & msb) == 0 || shift > 9 * 7) {
success = (b & msb) === 0;
break;
}
}
if (success) {
return result;
}
else {
console.error((0, util_1.asError)(this.buf));
return null;
}
}
}
exports.VarIntProcessor = VarIntProcessor;
function serializeVarint(int) {
const buf = new Uint8Array(maxSize);
let n = int;
let i = 0;
while (n >= msb) {
buf[i] = msb | n;
i += 1;
n >>= 7;
}
buf[i] = n;
i += 1;
return buf.slice(0, i).buffer;
}
//# sourceMappingURL=varint.js.map
@@ -0,0 +1 @@
{"version":3,"file":"varint.js","sourceRoot":"","sources":["../../../../../base/lib/s9pk/merkleArchive/varint.ts"],"names":[],"mappings":";;;AAgDA,0CAeC;AA/DD,qCAAoC;AAEpC,MAAM,GAAG,GAAG,IAAI,CAAA;AAChB,MAAM,OAAO,GAAG,IAAI,CAAA;AACpB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;AAE3C,MAAa,eAAe;IAG1B;QACE,IAAI,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;QAClC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IACZ,CAAC;IACD,IAAI,CAAC,CAAS;QACZ,IAAI,IAAI,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QACpB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;IACb,CAAC;IACD,QAAQ;QACN,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;IACzD,CAAC;IACD,MAAM;QACJ,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YACrB,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAA;YAC9B,MAAM,IAAI,UAAU,IAAI,KAAK,CAAA;YAC7B,KAAK,IAAI,CAAC,CAAA;YAEV,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,OAAO,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;gBACzB,MAAK;YACP,CAAC;QACH,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,MAAM,CAAA;QACf,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,IAAA,cAAO,EAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;YAChC,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;CACF;AAxCD,0CAwCC;AAED,SAAgB,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;IACnC,IAAI,CAAC,GAAG,GAAG,CAAA;IACX,IAAI,CAAC,GAAG,CAAC,CAAA;IAET,OAAO,CAAC,IAAI,GAAG,EAAE,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;QAChB,CAAC,IAAI,CAAC,CAAA;QACN,CAAC,KAAK,CAAC,CAAA;IACT,CAAC;IAED,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACV,CAAC,IAAI,CAAC,CAAA;IAEN,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;AAC/B,CAAC"}