@b4moss/median
v0.10.0
Published
Median media store (TypeScript) — Store/Delete/Get/PresignGet with local/S3, DB, image pipeline, SVG sanitize, PDF thumbnails
Readme
@b4moss/median
TypeScript port (packages/js). npm: @b4moss/median
Current: 0.10.0 (git tag v0.10.0). Requires Node.js 24+.
Language-port hub for JS/TS. Cross-language product overview and host workflow: root README. Specs: docs/specs/.
Install
npm install @b4moss/medianCLI (after install / via npx):
npx @b4moss/median migrate dump --dialect postgres --table mediapackage.json maps bin.median → ./dist/cli.js (built on publish).
Layout
| Path | Role |
| --- | --- |
| src/core | createMedian / store / delete / get / presignGet |
| src/storage | local / s3 |
| src/db | createMediaSQL / migrateUp / MediaRepo |
| src/cli.ts | CLI (migrate dump) |
| src/pipeline | process / sanitizeSvg / PDF thumbs |
| src/internal | MIME / filename / shardian path |
DB note: JS does not use crudian. Runtime MediaRepo is SQLite (better-sqlite3). DDL string generation still supports mysql / postgres / sqlite3 for host dumps.
Step-by-step (Node host)
Shared host workflow (all languages): root README.
1. Install
npm install @b4moss/median2. Create the media table (production)
npx @b4moss/median migrate dump \
--dialect postgres \
--id-strategy auto_increment \
--table media \
> migrations/XXXX_median_media.sql| Flag | Required | Default |
| --- | --- | --- |
| --dialect | yes | — (mysql | postgres | sqlite3) |
| --id-strategy | no | auto_increment |
| --table | no | media |
Commit the SQL into your migrate tool, then run it. Match flags with db config below.
For unit tests, migrateUp(db, "sqlite3", ID_AUTO_INCREMENT) on an in-memory better-sqlite3 is fine.
Existing tables + column maps: skip the CLI; see db-media.
3. Create a client
import Database from "better-sqlite3";
import {
createMedian,
migrateUp,
ID_AUTO_INCREMENT,
} from "@b4moss/median";
const sql = new Database("app.sqlite");
// Dev/demo only — production should apply CLI dump via host migrations:
migrateUp(sql, "sqlite3", ID_AUTO_INCREMENT);
const median = await createMedian({
storages: {
local: { driver: "local", path: "./data/median" },
},
defaultKey: "local",
defaultActor: "system",
db: {
db: sql,
idStrategy: ID_AUTO_INCREMENT,
tableName: "media",
},
});
const body = Buffer.from("hello");
const stored = await median.store(body, body.length, {
mime: "text/plain",
filename: "hello.txt",
});
console.log(stored.path, stored.hash);
const got = await median.get(stored.path, { withBody: true });
console.log(got.body?.toString());
await median.delete(stored.path);S3 storage entry sketch:
s3: {
driver: "s3",
bucket: "my-bucket",
region: "ap-northeast-1",
endpoint: "https://s3.example",
forcePathStyle: true,
prefix: "uploads/",
accessKey: "...",
secretKey: "...",
},4. Library DDL (optional)
import { createMediaSQL, ID_AUTO_INCREMENT } from "@b4moss/median";
const sqlText = createMediaSQL("postgres", ID_AUTO_INCREMENT, "media");Same payload as the CLI for the same arguments.
Develop / test
cd packages/js
npm ci
npm test
npm run test:coverage
npm run buildRelease
Root git tag vX.Y.Z must match package.json version. Pushing that tree to release publishes to npm when packed content differs (.github/CI.md). Trusted Publishing on GitHub Actions.
Docs
- Specs: packages-js, db-media, ddl-cli, …
- Tests: docs/tests/packages-js/, ddl-cli
- E2E: e2e/ / docs/specs/e2e/
- Migrations reference: migrations/README.md
