{"_id":"@backblaze-labs/b2-action-toolkit","name":"@backblaze-labs/b2-action-toolkit","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@backblaze-labs/b2-action-toolkit","version":"0.1.0","description":"Shared TypeScript toolkit for the Backblaze B2 GitHub Actions suite: inputs, tar+zstd archiving, integrity-correct upload/download, and lifecycle helpers built on @backblaze-labs/b2-sdk.","license":"MIT","author":{"name":"Backblaze, Inc."},"type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./test-helpers":{"types":"./dist/test-helpers.d.ts","default":"./dist/test-helpers.js"}},"types":"./dist/index.d.ts","engines":{"node":">=22"},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","test":"vitest run","test:watch":"vitest","clean":"rm -rf dist"},"dependencies":{"@actions/core":"^3.0.1","@backblaze-labs/b2-sdk":"^0.1.0","tar":"^7.5.1"},"devDependencies":{"@types/node":"^22.10.0","typescript":"^5.7.0","vitest":"^3.1.0"},"_id":"@backblaze-labs/b2-action-toolkit@0.1.0","gitHead":"4a4ce2e83f9af13a1c0fb8fdeeb9a8cea900cda2","_nodeVersion":"24.9.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-hqTNCD0kW4yZw+PmZyiXJ3gnwwru+jq5yXgvkdmIkwNvZMFJYM4nZRW9EFa0jwQCet9S8Te98o2NZyxyPhs8ng==","shasum":"1f600e56d9a7ec6a5a5841e70865a461e2c94f0f","tarball":"https://registry.npmjs.org/@backblaze-labs/b2-action-toolkit/-/b2-action-toolkit-0.1.0.tgz","fileCount":48,"unpackedSize":130551,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIC4TGCp1nh0KDKBTRagDFZy/vfauHgNwgx59H4/xyxRiAiEAvh2+3J83Mf0izUE5F90BO1qW6+TnIlPJ8H0753YNfe4="}]},"_npmUser":{"name":"jdnyc","email":"jdeleon@jdeleon.com"},"directories":{},"maintainers":[{"name":"goanpeca","email":"goanpeca@gmail.com"},{"name":"jdnyc","email":"jdeleon@jdeleon.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/b2-action-toolkit_0.1.0_1782244880590_0.9867223305144757"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-23T20:01:20.305Z","0.1.0":"2026-06-23T20:01:20.723Z","modified":"2026-06-23T20:01:21.156Z"},"maintainers":[{"name":"goanpeca","email":"goanpeca@gmail.com"},{"name":"jdnyc","email":"jdeleon@jdeleon.com"}],"description":"Shared TypeScript toolkit for the Backblaze B2 GitHub Actions suite: inputs, tar+zstd archiving, integrity-correct upload/download, and lifecycle helpers built on @backblaze-labs/b2-sdk.","author":{"name":"Backblaze, Inc."},"license":"MIT","readme":"# `@backblaze-labs/b2-action-toolkit` — Backblaze B2 GitHub Actions TypeScript Toolkit\n\n[![npm version](https://img.shields.io/npm/v/@backblaze-labs/b2-action-toolkit?label=%40backblaze-labs%2Fb2-action-toolkit)](https://www.npmjs.com/package/@backblaze-labs/b2-action-toolkit)\n[![CI](https://img.shields.io/github/actions/workflow/status/backblaze-labs/b2-action-toolkit/ci.yml?label=CI)](https://github.com/backblaze-labs/b2-action-toolkit/actions)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n\nShared TypeScript library for building Backblaze B2 GitHub Actions — providing integrity-correct upload/download, tar+zstd archiving, input parsing with secret masking, and a B2Simulator test harness. Built on [`@backblaze-labs/b2-sdk`](https://www.npmjs.com/package/@backblaze-labs/b2-sdk). Every action in the [Backblaze B2 GitHub Actions suite](#used-by) is a thin workflow-semantics layer on top of this toolkit.\n\n> First 10 GB of Backblaze B2 storage is always free.\n\n## What it provides\n\n- **Integrity-correct transfer** — closes B2's multipart SHA-1 gap: `uploadFile` stream-computes a whole-file SHA-1 and stamps it into `fileInfo.large_file_sha1`; `downloadFile` recomputes and rejects corrupt or truncated objects.\n- **tar + zstd archiving with the base-dir glob model** — `createArchive` / `extractArchive` backed by streaming tar + native Node `node:zlib` Zstd; `computeBaseDir` + `resolveFiles` give a filesystem-free base-dir contract so archives round-trip to the same absolute path on any runner.\n- **B2 input parsing + secret masking** — `getB2Inputs`, `connectBucket`, `connectB2`; shared helpers `multiline`, `boolInput`, `intInput`, `resolvePrefix` so every action parses credentials the same way.\n- **Lifecycle eviction** — `ensureLifecycleRule` (set-once) keeps buckets from billing forever; scoped to the action's prefix, never the whole bucket.\n- **JSON sidecars + file hashing** — `putJsonObject` / `getJsonObject` for manifests and `latest` pointers; `hashFile` for sha1/sha256 checksums.\n- **Actionable error helpers** — `describeB2Error` maps `TooManyRequestsError`, `CapExceededError`, `ChecksumMismatchError` to one clear line.\n- **B2Simulator test harness** — `makeTestB2` / `makeTempDir` under the `./test-helpers` subpath; in-memory B2, no network, no real bucket.\n\n## Install\n\n```bash\nnpm install @backblaze-labs/b2-action-toolkit\n```\n\n**Peer / runtime dependencies** (declared as `dependencies`, installed automatically):\n\n| Package | Purpose |\n|---|---|\n| `@backblaze-labs/b2-sdk` | B2 API client + simulator |\n| `@actions/core` | Input parsing, secret masking, logging |\n| `tar` | Streaming tar v7 |\n\nNode.js >= 22 required (uses native `node:zlib` Zstd compression and `fs.openAsBlob`).\n\n## Quick start\n\n```ts\nimport {\n  getB2Inputs,\n  connectBucket,\n  uploadFile,\n  downloadFile,\n} from '@backblaze-labs/b2-action-toolkit';\n\n// Parse + mask credentials from action inputs\nconst inputs = getB2Inputs();\nconst bucket = await connectBucket(inputs, 'my-action/1.0.0');\n\n// Integrity-correct upload — stamps large_file_sha1 for multipart objects\nawait uploadFile(bucket, 'cache/my-key.tar.zst', '/tmp/archive.tar.zst');\n\n// Integrity-correct download — verifies SHA-1 on the fly, rejects corruption\nawait downloadFile(bucket, 'cache/my-key.tar.zst', '/tmp/restored.tar.zst');\n```\n\nFor archiving a glob of paths:\n\n```ts\nimport {\n  computeBaseDir,\n  resolveFiles,\n  createArchive,\n  extractArchive,\n} from '@backblaze-labs/b2-action-toolkit';\n\nconst patterns = ['~/.cache/huggingface/**'];\nconst base = computeBaseDir(patterns);   // filesystem-free; identical on save + restore\n\n// Save\nconst files = await resolveFiles(patterns, base);\nawait createArchive(files, '/tmp/hf.tar.zst', { cwd: base });\n\n// Restore — tree lands back at the original absolute path on any runner\nawait extractArchive('/tmp/hf.tar.zst', base);\n```\n\n## API\n\nAll names below are exported from the package root (`@backblaze-labs/b2-action-toolkit`).\n\n### Inputs\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `getB2Inputs()` | function | Parse `key-id`, `application-key`, `bucket`, `realm` from action inputs; masks the key via `core.setSecret` |\n| `connectBucket(inputs, ua)` | function | Authorize and return a `Bucket` instance; `ua` sets the User-Agent |\n| `connectB2(inputs, ua)` | function | Same as `connectBucket` but also returns `downloadHost`; use when building download URLs |\n| `multiline(name)` | function | Newline-split, trimmed, empties dropped — canonical list-of-paths input parser |\n| `boolInput(name, fallback?)` | function | Strict `true`/`false` (case-insensitive); blank → fallback; throws otherwise |\n| `intInput(name, def, min, max)` | function | Bounded integer input; throws on non-integer or out-of-range |\n| `resolvePrefix(opts?)` | function | Explicit `prefix` input wins, else `GITHUB_REPOSITORY`, else `''`; `opts.repoFallback` controls the fallback |\n| `B2Inputs` | type | Parsed credential + bucket config |\n| `B2Connection` | type | `{ bucket, downloadHost }` returned by `connectB2` |\n| `ResolvePrefixOptions` | type | Options for `resolvePrefix` |\n\n### Paths\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `computeBaseDir(patterns)` | function | Derives the absolute common ancestor of all glob patterns — pure, filesystem-free, identical on save and restore |\n| `resolveFiles(patterns, baseDir)` | function | Expands `~`, globs, and plain paths into a sorted de-duped list of existing paths relative to `baseDir`; throws on empty result or missing literal path |\n\n### Archive\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `createArchive(paths, outFile, opts?)` | function | Streaming tar + zstd compress; `opts.level` sets compression level, `opts.cwd` sets the working directory |\n| `extractArchive(file, destDir)` | function | Streaming zstd decompress + tar extract into `destDir` |\n| `ArchiveResult` | type | Return value of `createArchive` |\n| `CreateArchiveOptions` | type | Options for `createArchive` (`level`, `cwd`) |\n\n### Storage\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `uploadFile(bucket, key, path, opts?)` | function | Integrity-correct upload — stamps `large_file_sha1` into `fileInfo` for multipart objects |\n| `downloadFile(bucket, key, destPath, opts?)` | function | Integrity-correct download — stream-verifies SHA-1, rejects corrupt/truncated objects |\n| `objectExists(bucket, key)` | function | Returns `true` if the object key exists |\n| `findLatestByPrefix(bucket, prefix)` | function | Returns the most recent `FileVersion` matching the prefix, or `null` |\n| `assertSha1Match(actual, stored)` | function | Throws `ChecksumMismatchError` when hashes differ; no-ops when stored value is `null`/`'none'` |\n| `putJsonObject(bucket, key, value)` | function | Upload a small JSON payload as `application/json` from a `BufferSource` |\n| `getJsonObject<T>(bucket, key)` | function | Download and parse a JSON sidecar; returns `null` on genuine miss, propagates parse errors |\n| `hashFile(path, algo)` | function | Stream-hash a file with `'sha1'` or `'sha256'`; returns hex digest |\n| `LARGE_FILE_SHA1_KEY` | const | `fileInfo` key used for whole-file SHA-1 (`\"large_file_sha1\"`) |\n| `SRC_LAST_MODIFIED_KEY` | const | `fileInfo` key used for source modification time |\n| `UploadFileOptions` | type | Options for `uploadFile` |\n| `DownloadFileOptions` | type | Options for `downloadFile` |\n\n### Lifecycle\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `ensureLifecycleRule(bucket, prefix, days?)` | function | Set-once eviction rule scoped to `prefix`; writes only when absent or changed (cheap read-first) |\n| `DEFAULT_RETENTION_DAYS` | const | Default eviction window (7 days) |\n\n### Errors\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `describeB2Error(err)` | function | One actionable message per common B2 failure: rate limit, cap exceeded, checksum mismatch, generic fallback |\n\n### SDK re-exports\n\nThese are re-exported so action repos don't need a direct `@backblaze-labs/b2-sdk` dependency.\n\n| Export | Kind | Purpose |\n|---|---|---|\n| `Bucket` | type | B2 bucket handle |\n| `FileVersion` | type | B2 file version metadata |\n| `LifecycleRule` | type | B2 lifecycle rule shape |\n| `B2Error` | class | Base B2 error class |\n| `TooManyRequestsError` | class | Rate-limit error (check `retryAfter`) |\n| `CapExceededError` | class | Account storage/download cap error |\n| `ChecksumMismatchError` | class | In-transit corruption error |\n| `classifyError(err)` | function | Classify an unknown error into a known B2 error type |\n\n## Testing with the B2Simulator\n\nImport the test harness from the published `./test-helpers` subpath — all action repos share this instead of duplicating the wiring:\n\n```ts\nimport { makeTestB2, makeTempDir } from '@backblaze-labs/b2-action-toolkit/test-helpers';\nimport { describe, it, expect } from 'vitest';\n\ndescribe('my action storage logic', () => {\n  it('round-trips a file with integrity', async () => {\n    const { bucket } = await makeTestB2('my-bucket');\n    const tmp = await makeTempDir();\n\n    await uploadFile(bucket, 'test/key', `${tmp}/input.bin`);\n    await downloadFile(bucket, 'test/key', `${tmp}/output.bin`);\n    // assert...\n  });\n});\n```\n\n`makeTestB2` wires a `B2Simulator` with lowered `recommendedPartSize`/`minimumPartSize` so multipart paths are exercised cheaply. Use `sim.injectFailure(...)` for retry/error-path coverage — see [`ARCHITECTURE.md`](./ARCHITECTURE.md).\n\n## Used by\n\nThese GitHub Actions are all thin layers on this toolkit:\n\n| Action | Description |\n|---|---|\n| [`b2-cache`](../b2-cache) | Backblaze B2 cache action for GitHub Actions — save and restore build caches |\n| [`b2-artifact`](../b2-artifact) | Backblaze B2 artifact upload/download for GitHub Actions |\n| [`b2-eval-report`](../b2-eval-report) | Backblaze B2 evaluation report storage for GitHub Actions ML pipelines |\n| [`b2-dvc-remote`](../b2-dvc-remote) | Backblaze B2 DVC remote setup action for GitHub Actions |\n| [`b2-hf-cache`](../b2-hf-cache) | Backblaze B2 Hugging Face model cache action for GitHub Actions |\n| [`b2-model-publish`](../b2-model-publish) | Backblaze B2 model publishing action for GitHub Actions |\n| [`b2-release-assets`](../b2-release-assets) | Backblaze B2 release asset upload action for GitHub Actions |\n\n## Conventions\n\nEvery action in the suite follows the layout, build, test, and naming rules documented in [`ARCHITECTURE.md`](./ARCHITECTURE.md) — the single source of truth for how the toolkit and each action are structured, how the base-dir contract works, and how the integrity rule is applied.\n\n## License\n\nMIT © Backblaze, Inc.\n","readmeFilename":"README.md","_rev":"1-2a3b63ff62530bc73241c16a293d4a10"}