@originalvoices/job-echo-client
v0.5.2
Published
Typed worker runtime + Firestore event client for job-echo-collector
Maintainers
Readme
@originalvoices/job-echo-client
Typed worker runtime + Firestore event client for job-echo-collector.
Install
pnpm add @originalvoices/job-echo-clientConfiguration
Set env vars on the consuming service:
JOB_ECHO_GCP_PROJECT=job-echo-collector # GCP project hosting Firestore
# or
FIREBASE_PROJECT_ID=job-echo-collectorAuth comes from ADC — your runtime service account needs roles/datastore.user on the job-echo-collector project.
Usage
As a worker server
import { defineJob, startWorker } from '@originalvoices/job-echo-client'
const sendMessage = defineJob({
name: 'send-message',
async handler(input: { threadId: string; message: string }, ctx) {
// do work
return { messageId: 'abc' }
},
})
startWorker({
project: 'platform',
environment: 'prod',
jobs: [sendMessage],
// port: 8081
})Add to your app's package.json:
{
"scripts": {
"dev:worker": "tsx --watch src/worker/index.ts",
"start:worker": "node dist/worker/index.js"
}
}Every POST to /jobs/send-message logs in_progress → completed / failed events to Firestore automatically.
As a plain event client
import { createClient } from '@originalvoices/job-echo-client'
const echo = createClient({ project: 'platform', environment: 'prod' })
const ev = await echo.start('nightly-sync', { batch: 42 })
try {
// do work
await echo.complete(ev.id, { rows: 1234 })
} catch (e) {
await echo.fail(ev.id, (e as Error).message)
}Typed emit at call sites
import { sendMessage } from './jobs'
await sendMessage.emit({ threadId: 't1', message: 'hi' }).emit() auto-detects the dispatcher from NODE_ENV:
- dev: HTTP POST to
JOB_ECHO_WORKER_URL(defaulthttp://localhost:8080) - production: enqueues a Cloud Task on
JOB_ECHO_TASKS_QUEUEwith OIDC
Schedules
Declare a cron schedule inline with the job:
export const refillCredits = defineJob({
name: 'refill-subscription-credits',
schedule: '59 23 * * 0', // every Sunday at 23:59
timezone: 'Etc/UTC', // optional, defaults to Etc/UTC
async handler() { /* ... */ },
})Schedules are provisioned in Cloud Scheduler by running the worker image as a one-shot Cloud Run Job with JOB_ECHO_SYNC_MODE=1. startWorker detects the env var, calls syncSchedules instead of starting the server, then exits.
Required env on the sync run:
JOB_ECHO_SYNC_MODE=1
JOB_ECHO_TASKS_QUEUE=projects/<p>/locations/<l>/queues/<q>
JOB_ECHO_WORKER_URL=https://your-worker.run.app
JOB_ECHO_TASKS_SA_EMAIL=tasks-invoker@<p>.iam.gserviceaccount.com
JOB_ECHO_APP_LABEL=platform-prod # scopes which schedulers this run ownsThe Cloud Build SA needs roles/cloudscheduler.admin on the project. Sync only touches schedulers labelled managed-by=job-echo AND app=<JOB_ECHO_APP_LABEL>, so manually-created schedulers are untouched.
Cloud Build step:
- name: gcr.io/google.com/cloudsdktool/cloud-sdk
entrypoint: bash
args:
- -c
- |
gcloud run jobs deploy worker-sync-schedules \
--image=$_IMAGE_PREFIX/$_SERVICE_NAME:$SHORT_SHA \
--region=$_REGION --project=$PROJECT_ID \
--service-account=$_SERVICE_ACCOUNT_EMAIL \
--set-env-vars=JOB_ECHO_SYNC_MODE=1,JOB_ECHO_TASKS_QUEUE=...,JOB_ECHO_WORKER_URL=...,JOB_ECHO_TASKS_SA_EMAIL=...,JOB_ECHO_APP_LABEL=$_SERVICE_NAME \
--execute-now --waitRun reports what changed:
{ "created": [...], "updated": [...], "deleted": [...], "unchanged": [...] }