# @ferret/assets: API (continued)

### PackageEncryption

*interface* · `packages/assets/src/security.ts:24`

```ts
interface PackageEncryption
```

- `alg: 'AES-256-GCM'`
- `kdf: 'PBKDF2-SHA256' | 'none'`
- `salt?: string` — base64 (kdf PBKDF2)
- `iterations?: number`
- `check: string` — a sealed constant: opening it tells a wrong password from a damaged file (kdf none: the key's id)

### PackageInfo

*interface* · `packages/assets/src/package.ts:369`

```ts
interface PackageInfo
```

- `id: string`
- `name: string`
- `version: string`
- `kind: PackageKind`
- `format: number`
- `files: number`
- `bytes: number`
- `levels: string[]`
- `assets: string[]`
- `behaviours: string[]`
- `dependencies: Record<string, string>`
- `hasCode: boolean`
- `signed: boolean`
- `encrypted: boolean`
- `locked: boolean`
- `entry?: PackageManifest['entry']`
- `codeEntry?: string` — the module the code loader runs, if any (`code.entry`, else a game's `entry.code`)

### PackageIssue

*interface* · `packages/assets/src/package.ts:80`

```ts
interface PackageIssue
```

- `path: string`
- `message: string`

### PackageManifest

*interface* · `packages/assets/src/package.ts:31`

```ts
interface PackageManifest
```

- `format: number`
- `id: string` — lower case letters, digits, `.`, `_`, `-`
- `name: string`
- `version: string` — semver `major.minor.patch`
- `kind: PackageKind`
- `engine?: { ferret?: string; babylon?: string; /** the parts of Babylon the game loads at start, and the ones a feature may ask for later (stage P6; without them the full build is loaded) */ parts?: string[]; lazyParts?: string[]; /** a standalone game: the exact release whose engine files are inside the package, under `engine/` */ bundled?: string }` — the versions it was built and tested with
- `dependencies?: Record<string, string>` — other packages by id and version range (`1.2.0`, `^1.2.0`, `~1.2.0`, `>=1.2.0`, `*`)
- `modules?: string[]` — a game only: the engine modules its code and levels use, written by the compiler (a project has none)
- `babylon?: string[]` — a game only: the Babylon parts it uses, written by the compiler
- `keep?: string[]` — a game or a project: files the compiler must keep although no literal path reaches them (globs, `*` within a folder, `**` across)
- `game?: GameBlock` — a game or a project: what `createGame` gets
- `for?: { game: string; version: string }` — a package of levels made for a game: which game and versions it fits
- `entry?: { code?: string; level: string; spawn?: string; ui?: string }` — a game or a project: the code module (a game: built, under `code/`; a project: the source), the level to start in, the spawn, the UI fragment
- `code?: { entry: string }` — a game or a library: the code module the code loader runs (one bundled ES module under `code/` exporting `install`)
- `provides?: PackageProvides` — what it offers to others
- `files?: Record<string, string>` — sha256 (hex) of every file but this one
- `encryption?: PackageEncryption` — present when the files are encrypted (see ./security); `ferret.json` itself never is
- `signature?: PackageSignature` — a signature over the whole manifest (so over `files`) made by the publisher; checked by whoever decides to trust the code

### PackageProvides

*interface* · `packages/assets/src/package.ts:72`

```ts
interface PackageProvides
```

What a package offers, so that a level or a program can be resolved to the packages it needs without running their code. The
lists after `behaviours` are names a library's code registers: physics capabilities, actuator kinds, flow sources, forms of the
motion language, commands and level manifest keys.

- `levels?: string[]`
- `assets?: string[]`
- `behaviours?: { name: string; params?: Record<string, unknown> }[]`
- `capabilities?: string[]`
- `kinds?: string[]`
- `flowSources?: string[]`
- `forms?: string[]`
- `commands?: string[]`
- `levelKeys?: string[]`

### PackageSignature

*interface* · `packages/assets/src/security.ts:14`

```ts
interface PackageSignature
```

- `alg: 'ES256'`
- `key: string` — the public key, SPKI in base64
- `keyId: string` — first 16 bytes of the sha256 of the public key, in hex
- `value: string` — base64 of the raw (r || s) signature

### PackageSource

*interface* · `packages/assets/src/package.ts:277`

```ts
interface PackageSource
```

Reads the files of a package, whether it is a zip or a folder.

- `readonly kind: 'zip' | 'folder' | 'memory'`
- `list(): string[]` — every file path (the manifest included)
- `has(path: string): boolean`
- `size(path: string): number` — bytes of the file in the package (uncompressed); 0 when unknown
- `read(path: string): Promise<Uint8Array>`

### PackedPackage

*interface* · `packages/assets/src/catalog.ts:244`

```ts
interface PackedPackage
```

- `bytes: Uint8Array`
- `manifest: PackageManifest`
- `tiers?: Record<string, Uint8Array>`

### PackOptions

*interface* · `packages/assets/src/package.ts:631`

```ts
interface PackOptions
```

Build a `.ferret` from a source (a folder, another package, a draft): the manifest gets the sha256 of every file, the levels
and assets it provides when they are not named, and the zip is written the same way every time (sorted, no dates). Files that
are compressed already are stored, so a reader slices them out without inflating.

- `manifest?: Partial<PackageManifest>`
- `engine?: PackageManifest['engine']`
- `encrypt?: KeyInput` — seal every file but the manifest (a password, or a 32-byte key in base64)
- `iterations?: number` — PBKDF2 rounds for a password (default 310000; tests use less)
- `sign?: { privateKey: string }` — sign the finished manifest with this private key (base64 PKCS8, from `generateSigningKey`)
- `decrypt?: boolean` — pack an encrypted package's files as plain ones (without it, repacking an encrypted package needs `encrypt`)

### PersistentCacheOptions

*interface* · `packages/assets/src/persist.ts:120`

```ts
interface PersistentCacheOptions
```

- `cap?: number` — Hard cap in bytes (defaults: 400 MB desktop, 200 MB phone, 100 MB console: the caller picks).
- `quota?: () => Promise<number | undefined>` — The browser's quota (`navigator.storage.estimate().quota`) when it has one; the cache stays under 80% of it.
- `now?: () => number`
- `partialMaxAgeMs?: number` — Partial downloads older than this are removed (24 h).

### RequireOptions

*interface* · `packages/assets/src/store.ts:35`

```ts
interface RequireOptions
```

- `priority?: StreamPriority`
- `signal?: AbortSignal`
- `sha256?: string` — sha256 (hex) from a signed catalog or package manifest. Without it the file is not verified (development only).
- `size?: number` — Size in bytes when the catalog knows it: the progress total is exact from the start.
- `mirrors?: string[]`
- `pkg?: string` — The package (or level) the file belongs to: the unit of eviction, pinning and `clear`.
- `version?: string`
- `group?: string` — Files asked with the same group add up into one progress (a travel).

### SigningKey

*interface* · `packages/assets/src/security.ts:56`

```ts
interface SigningKey
```

- `publicKey: string`
- `privateKey: string`
- `keyId: string`

### Socket

*interface* · `packages/assets/src/library.ts:74`

```ts
interface Socket
```

A place on a body where something is held: a bone and an offset from it (metres, radians as a quaternion), kept in the item so every game puts a weapon in a hand the same way.

- `bone: string`
- `position?: [number, number, number]`
- `rotation?: [number, number, number, number]`
- `grip?: { point: [number, number, number]; axis: [number, number, number]; radius: number; spacing?: number; edge?: [number, number, number] }` — how the thing in this socket is held, in the frame of `bone`: the middle of the grip, its axis (toward the blade), its radius, the distance to the second hand (toward the pommel), and for a blade the way its edge faces (across the axis; a two-edged blade cuts along it either way)

### StoreOptions

*interface* · `packages/assets/src/store.ts:16`

```ts
interface StoreOptions
```

- `fetch?: typeof fetch`
- `origins?: string[]` — Origins files may come from (`https://cdn.example.com`). Empty or missing: any origin that passes the https rule.
- `base?: string` — Relative refs are resolved against this.
- `concurrency?: number`
- `stallMs?: number` — No bytes for this long: the attempt is abandoned and retried (resumed when it can).
- `retries?: number`
- `backoffMs?: number`
- `maxFileBytes?: number` — A file bigger than this is refused (the catalog's size is the usual limit; this is the engine's cap).
- `cache?: PersistentCache` — The persistent cache; without one every file lives in memory for the session.
- `random?: () => number`
- `sleep?: (ms: number, signal?: AbortSignal) => Promise<void>`

### StreamProgress

*interface* · `packages/assets/src/store.ts:14`

```ts
interface StreamProgress
```

- `loaded: number`
- `total: number`
- `files: number`
- `filesDone: number`
- `rate: number`
- `eta: number`

### TextureItem

*interface* · `packages/assets/src/library.ts:29`

```ts
interface TextureItem extends ItemBase
```

- `type: 'texture'`
- `maps: Partial<Record<'basecolor' | 'normal' | 'orm' | 'emissive', string>>` — the maps present: path inside the package per channel (`basecolor`, `normal`, `orm`, `emissive`), per tier a folder or file set
- `tiling?: number` — metres of the world one repeat of the texture covers; absent when unknown
- `seamless?: boolean`
- `size?: number` — pixels of the largest side at full size
- `tiers?: Partial<Record<LibraryTier, { bytes: number; side: number }>>`
- `aliases?: string[]` — other names of the same image (the same pixels exported under another name)

### TierFile

*interface* · `packages/assets/src/library.ts:16`

```ts
interface TierFile
```

- `file: string`
- `bytes: number`
- `triangles?: number`
- `texCap?: number`
- `bones?: number`

### VerifyResult

*interface* · `packages/assets/src/package.ts:392`

```ts
interface VerifyResult
```

- `signed: boolean`
- `valid: boolean` — the signature matches the manifest (and, when files could be read, every file matches the manifest)
- `keyId?: string`
- `trusted: boolean` — valid, and the key is in (true for any valid signature when no list was given)
- `problems: string[]`

### ZipEntryInfo

*interface* · `packages/assets/src/zip.ts:72`

```ts
interface ZipEntryInfo
```

- `path: string`
- `size: number`
- `compressedSize: number`
- `method: 0 | 8`
- `crc: number`
- `offset: number`

### ZipFile

*interface* · `packages/assets/src/zip.ts:190`

```ts
interface ZipFile
```

- `path: string`
- `data: Uint8Array`
- `compress?: boolean`

## Types

### AlphaMode

*type* · `packages/assets/src/library.ts:43`

```ts
type AlphaMode = 'opaque' | 'mask' | 'blend'
```

### CatalogFailure

*type* · `packages/assets/src/catalog.ts:57`

```ts
type CatalogFailure = 'invalid' | 'signature' | 'untrusted' | 'unsigned' | 'engine' | 'network'
```

### CatalogPackageSource

*type* · `packages/assets/src/catalog.ts:240`

```ts
type CatalogPackageSource = PackageSource & { manifest: PackageManifest; cached(paths: string[]): Promise<boolean>; /** `id@version`: the unit the cache evicts and pins */ tag: string }
```

### CharacterKind

*type* · `packages/assets/src/library.ts:10`

```ts
type CharacterKind = 'human' | 'animal' | 'creature' | 'part'
```

### KeyInput

*type* · `packages/assets/src/security.ts:35`

```ts
type KeyInput = { password: string } | { key: string }
```

What opens an encrypted package: a password, or the 32-byte key in base64.

### Ktx2Target

*type* · `packages/assets/src/ktx2.ts:32`

```ts
type Ktx2Target = 'ASTC4x4' | 'BC7' | 'ETC2' | 'ETC1' | 'BC3' | 'BC1' | 'PVRTC' | 'RGBA8'
```

the block format a KTX2 map ends up in on a device

### LibraryItem

*type* · `packages/assets/src/library.ts:134`

```ts
type LibraryItem = TextureItem | MaterialItem | CharacterItem | ClipsItem | ModelItem
```

### LibraryItemType

*type* · `packages/assets/src/library.ts:6`

```ts
type LibraryItemType = 'texture' | 'material' | 'character' | 'clips' | 'model'
```

### LibraryTier

*type* · `packages/assets/src/library.ts:8`

```ts
type LibraryTier = 'desktop' | 'mobile' | 'low'
```

### MapSlot

*type* · `packages/assets/src/library.ts:44`

```ts
type MapSlot = 'baseColor' | 'normal' | 'orm' | 'emissive' | 'opacity'
```

### PackageInput

*type* · `packages/assets/src/package.ts:380`

```ts
type PackageInput = Blob | Uint8Array | ArrayBuffer | string | ByteSource | PackageSource
```

### PackageKind

*type* · `packages/assets/src/package.ts:11`

```ts
type PackageKind = 'game' | 'levels' | 'assets' | 'library' | 'project'
```

### StoreStatus

*type* · `packages/assets/src/store.ts:51`

```ts
type StoreStatus = 'missing' | 'queued' | 'loading' | 'cached' | 'failed'
```

### StreamFailure

*type* · `packages/assets/src/stream-error.ts:3`

```ts
type StreamFailure = 'network' | 'http' | 'timeout' | 'integrity' | 'origin' | 'aborted' | 'size' | 'build'
```

### StreamPriority

*type* · `packages/assets/src/store.ts:10`

```ts
type StreamPriority = 'high' | 'normal' | 'low' | 'idle'
```

## Constants

### CATALOG_FORMAT

*const* · `packages/assets/src/catalog.ts:11`

```ts
const CATALOG_FORMAT: 1
```

### CHARACTER_KINDS

*const* · `packages/assets/src/library.ts:11`

```ts
const CHARACTER_KINDS: readonly CharacterKind[]
```

### GAME_TIERS

*const* · `packages/assets/src/package.ts:29`

```ts
const GAME_TIERS: readonly ["desktop", "mobile", "low"]
```

### KTX2_BYTES_PER_TEXEL

*const* · `packages/assets/src/ktx2.ts:36`

```ts
const KTX2_BYTES_PER_TEXEL: Record<Ktx2Target, number>
```

video memory per texel (RGBA unless noted; BC1, ETC1 and PVRTC 4 bpp carry no alpha and take half a byte)

### KTX2_FILES

*const* · `packages/assets/src/ktx2.ts:19`

```ts
const KTX2_FILES: readonly ["babylon.ktx2Decoder.js", "msc_basis_transcoder.js", "msc_basis_transcoder.wasm", "zstddec.wasm", "uastc_astc.wasm", "uastc_bc7.wasm", "uastc_r8_unorm.wasm", "uastc_rg8_unorm.wasm", "uastc_rgba8_srgb_v2.wasm", "uastc_rgba8_unorm_v2.wasm"]
```

the files `ktx2/` holds, as the build copies them (tools/build.mjs, tools/modules/build.mjs, the game builds)

### LIBRARY_ITEM_TYPES

*const* · `packages/assets/src/library.ts:7`

```ts
const LIBRARY_ITEM_TYPES: readonly LibraryItemType[]
```

### LIBRARY_TIERS

*const* · `packages/assets/src/library.ts:9`

```ts
const LIBRARY_TIERS: readonly LibraryTier[]
```

### MANIFEST_FILE

*const* · `packages/assets/src/package.ts:10`

```ts
const MANIFEST_FILE: "ferret.json"
```

### MAP_SLOTS

*const* · `packages/assets/src/library.ts:45`

```ts
const MAP_SLOTS: readonly MapSlot[]
```

### PACKAGE_EXTENSIONS

*const* · `packages/assets/src/package.ts:17`

```ts
const PACKAGE_EXTENSIONS: Partial<Record<PackageKind, string>>
```

The file name extension a kind goes by. `.fex` is a finished game and `.fprj` a project in development; both are
`.ferret` packages. A name ending in `.fex` or `.fprj` must carry the matching kind; other names are not checked.

### PACKAGE_FORMAT

*const* · `packages/assets/src/package.ts:9`

```ts
const PACKAGE_FORMAT: 1
```

### PROVIDED_NAMES

*const* · `packages/assets/src/package.ts:78`

```ts
const PROVIDED_NAMES: readonly ["capabilities", "kinds", "flowSources", "forms", "commands", "levelKeys"]
```

The `provides` lists that are plain names.

### SEAL_OVERHEAD

*const* · `packages/assets/src/security.ts:173`

```ts
const SEAL_OVERHEAD: number
```

What a sealed file adds to its size.

### ZIP_LIMITS

*const* · `packages/assets/src/zip.ts:91`

```ts
const ZIP_LIMITS: { maxEntries: number; maxEntryBytes: number; maxTotalBytes: number; maxJsonBytes: number; }
```

Limits that keep a hostile package from exhausting memory: a zip bomb is a few kilobytes that inflate to
gigabytes, or a directory of millions of entries. An engine cap, checked on the declared sizes when the directory is read and on
the real output while a file inflates. Change them before reading (a game with huge cooked files may raise `maxEntryBytes`).

