npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

serverless-offline-transcribe

v1.0.0

Published

Emulate AWS Transcribe batch jobs locally, backed by OpenAI Whisper, when developing your Serverless project

Downloads

24

Readme

serverless-offline-transcribe

This Serverless-offline plugin emulates Amazon Transcribe batch jobs on your local machine, so code that calls the Transcribe client runs fully offline — no AWS account, no network, no cloud cost. Media is read from local S3 (Minio), transcribed by a developer-provided local engine (OpenAI Whisper), and written back to local S3 in the AWS Transcribe JSON shape — exactly as serverless-offline-sqs translates SQS calls to ElasticMQ.

Scope (MVP): asynchronous batch jobs (StartTranscriptionJob / GetTranscriptionJob / ListTranscriptionJobs). Streaming transcription is out of scope for now.

Requirements: Node.js ≥ 20 (the bundled AWS SDK v3 requires it).

Local-dev tool — mind the exposure. Like the sibling emulators the server binds 0.0.0.0 by default, so it is reachable from your LAN; set host: 127.0.0.1 to keep it loopback-only. It is also unauthenticated and does not bound concurrency — every StartTranscriptionJob immediately launches its own Whisper process. This is fine for local development but is not hardened against untrusted callers or high job volume; do not expose it on an untrusted network.

How it works

The plugin stands up a local HTTP server speaking AWS JSON 1.1 and, in offline:start:init, injects AWS_ENDPOINT_URL_TRANSCRIBE before the offline Lambda runtime snapshots each function's environment. Your unmodified Transcribe client therefore resolves to localhost with no application code change.

StartTranscriptionJob registers the job IN_PROGRESS, returns the AWS job descriptor immediately, and processes asynchronously: download the media from local S3 → run Whisper with word-level timestamps → shape the result into the AWS Transcribe JSON (word items[], punctuation split into its own untimed items, audio_segments) → upload it to the resolved output location. GetTranscriptionJob reports COMPLETED (with Transcript.TranscriptFileUri) or FAILED (with FailureReason).

Prerequisites

  • OpenAI Whisper on your PATH:
    pip install -U openai-whisper      # also needs ffmpeg (e.g. `brew install ffmpeg`)
    which whisper                      # must resolve
    If whisper is missing, the plugin fails fast at startup naming the prerequisite.
  • Local S3 (Minio) — the same local S3 this repo uses for serverless-offline-s3:
    docker run -p 9000:9000 -e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \
      minio/minio server /data

Neither engine is bundled (developer prerequisites, like ElasticMQ for SQS).

Installation

npm install --save-dev serverless-offline-transcribe

Add it to your serverless.yml plugins before serverless-offline:

plugins:
  - serverless-offline-transcribe
  - serverless-offline

Configuration

custom:
  serverless-offline-transcribe:
    enabled: true                    # set false (or "false") to skip the emulator entirely
    host: 0.0.0.0
    port: 4569
    accountId: '000000000000'
    model: base                      # Whisper model: tiny | base | small | medium | large
    # Local S3 (Minio) — mirrors serverless-offline-s3 config keys
    endpoint: http://localhost:9000
    region: us-east-1
    accessKey: minioadmin
    secretKey: minioadmin
    # whisperBin: whisper            # optional: path to the whisper binary
    # whisperTimeout: 600000         # optional: per-job Whisper timeout (ms)

| Option | Default | Description | | --- | --- | --- | | enabled | true | false / "false" skips standing up the server. | | host / port | 0.0.0.0 / 4569 | Bind address of the local Transcribe endpoint. | | accountId | 000000000000 | Emitted on the transcript document. | | model | base | Whisper model size. | | endpoint | http://localhost:9000 | Local S3 (Minio) endpoint. Path-style addressing is forced (mandatory for Minio). | | region | us-east-1 | S3 region (Minio ignores the value but the v3 SDK requires one). | | accessKey / secretKey | minioadmin | Minio credentials (accessKeyId/secretAccessKey also accepted). | | whisperBin | whisper | Whisper executable (looked up on PATH). | | whisperTimeout | — | Optional per-job Whisper timeout in ms. |

Behavior notes

  • Lifecycle (AC-C1/C2): StartTranscriptionJob returns IN_PROGRESS without blocking; processing runs asynchronously; GetTranscriptionJob reports the transition to COMPLETED/FAILED.
  • Output location (AC-C3): resolved from OutputBucketName + OutputKey (ends .json → verbatim; ends / → {OutputKey}{jobName}.json; absent → {jobName}.json). Transcript.TranscriptFileUri is a path-style Minio http:// URL. When OutputBucketName is absent it falls back to the media bucket.
  • Failures (AC-C4/C5): an unsupported language/format, a run that recognizes no speech, or a bad media URI fails the job with a FailureReason (never a silently-empty transcript). A missing Whisper binary fails fast at startup.
  • Security: Whisper is invoked via execFile (no shell) with the audio path as an argument.
  • These are local-development / CI tools: the goal is a protocol-faithful substitute, not AWS Transcribe accuracy parity. Diarization / custom vocabularies / redaction are non-goals.

Copy-paste example

service: my-service

plugins:
  - serverless-offline-transcribe
  - serverless-offline

provider:
  name: aws
  runtime: nodejs18.x

custom:
  serverless-offline-transcribe:
    model: base
    endpoint: http://localhost:9000
    accessKey: minioadmin
    secretKey: minioadmin

functions:
  transcribe:
    handler: handler.transcribe
    events:
      - httpApi: 'POST /transcribe'
// handler.js — unchanged production code
const {
  TranscribeClient,
  StartTranscriptionJobCommand,
  GetTranscriptionJobCommand
} = require('@aws-sdk/client-transcribe');
const client = new TranscribeClient({}); // resolves to localhost:4569 offline

exports.transcribe = async event => {
  const {jobName, mediaUri} = JSON.parse(event.body);
  await client.send(
    new StartTranscriptionJobCommand({
      TranscriptionJobName: jobName,
      LanguageCode: 'en-US',
      Media: {MediaFileUri: mediaUri}, // s3://my-bucket/audio.wav in local Minio
      OutputBucketName: 'my-bucket',
      OutputKey: 'transcripts/'
    })
  );
  const {TranscriptionJob} = await client.send(
    new GetTranscriptionJobCommand({TranscriptionJobName: jobName})
  );
  return {statusCode: 200, body: JSON.stringify(TranscriptionJob)};
};
# with Minio + whisper running, and an audio object uploaded to s3://my-bucket/audio.wav
serverless offline

License

MIT