diff --git a/.github/actions/rust-prelude/action.yml b/.github/actions/rust-prelude/action.yml new file mode 100644 index 00000000..05108138 --- /dev/null +++ b/.github/actions/rust-prelude/action.yml @@ -0,0 +1,18 @@ +name: Rust test prelude +description: >- + Python (interpreter only, for libpython linking) + Rust toolchain and cache. + Shared setup for every Rust test job. The caller must check out the repo first + — a local action can't be resolved until the workspace exists. + +runs: + using: composite + steps: + # Rust tests link the PyO3 extension against libpython; the interpreter is + # needed at runtime but the dev deps are not, so skip the uv sync. + - name: Set up Python (libpython only) + uses: ./.github/actions/setup-python + with: + sync: "false" + + - name: Set up Rust + uses: ./.github/actions/setup-rust diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0a492ab..ad892e24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,11 @@ jobs: name: Detect changed paths runs-on: ubuntu-latest outputs: - rust: ${{ steps.filter.outputs.rust }} - python: ${{ steps.filter.outputs.python }} - node: ${{ steps.filter.outputs.node }} + # Run policy folded in: true on push/dispatch (run everything) or when a + # suite's paths changed on a PR. Jobs gate on these directly. + rust: ${{ steps.decide.outputs.rust }} + python: ${{ steps.decide.outputs.python }} + node: ${{ steps.decide.outputs.node }} steps: - name: Check out repository uses: actions/checkout@v7 @@ -71,6 +73,27 @@ jobs: - '.github/actions/**' - '.github/workflows/ci.yml' + - name: Decide which suites run + id: decide + # FORCE is true on push and workflow_dispatch — run every suite there. + # On PRs honor the path filter. + env: + FORCE: ${{ github.event_name != 'pull_request' }} + RUST: ${{ steps.filter.outputs.rust }} + PYTHON: ${{ steps.filter.outputs.python }} + NODE: ${{ steps.filter.outputs.node }} + run: | + decide() { + if [ "$FORCE" = "true" ] || [ "$2" = "true" ]; then + echo "$1=true" >> "$GITHUB_OUTPUT" + else + echo "$1=false" >> "$GITHUB_OUTPUT" + fi + } + decide rust "$RUST" + decide python "$PYTHON" + decide node "$NODE" + lint: name: Lint & Static Analysis runs-on: ubuntu-latest @@ -129,24 +152,19 @@ jobs: rust-test: name: Rust Tests (SQLite) needs: changes - if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - - name: Check out repository - uses: actions/checkout@v7 - - - name: Set up Python (libpython for the linked extension) - uses: ./.github/actions/setup-python - with: - sync: "false" - - - name: Set up Rust - uses: ./.github/actions/setup-rust + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust-prelude + # $pythonLocation is exported to the job env by setup-python at runtime. - name: Run Rust test suite - # $pythonLocation is exported to the job env by setup-python at runtime. run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace + - name: Run mesh crate tests + run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test -p taskito-mesh + - name: Check build with native-async features run: cargo check --workspace --features native-async @@ -156,7 +174,7 @@ jobs: rust-test-postgres: name: Rust Tests (PostgreSQL) needs: changes - if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest services: postgres: @@ -173,16 +191,8 @@ jobs: --health-timeout 5s --health-retries 10 steps: - - name: Check out repository - uses: actions/checkout@v7 - - - name: Set up Python (libpython for the linked extension) - uses: ./.github/actions/setup-python - with: - sync: "false" - - - name: Set up Rust - uses: ./.github/actions/setup-rust + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust-prelude - name: Run Rust test suite (PostgreSQL backend) run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace --features postgres,workflows @@ -192,7 +202,7 @@ jobs: rust-test-redis: name: Rust Tests (Redis) needs: changes - if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest services: redis: @@ -205,46 +215,18 @@ jobs: --health-timeout 3s --health-retries 10 steps: - - name: Check out repository - uses: actions/checkout@v7 - - - name: Set up Python (libpython for the linked extension) - uses: ./.github/actions/setup-python - with: - sync: "false" - - - name: Set up Rust - uses: ./.github/actions/setup-rust + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust-prelude - name: Run Rust test suite (Redis backend) run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test --workspace --features redis,workflows env: TASKITO_REDIS_TEST_URL: redis://localhost:6379/15 - rust-test-mesh: - name: Rust Tests (Mesh) - needs: changes - if: needs.changes.outputs.rust == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v7 - - - name: Set up Python (libpython for the linked extension) - uses: ./.github/actions/setup-python - with: - sync: "false" - - - name: Set up Rust - uses: ./.github/actions/setup-rust - - - name: Run mesh crate tests - run: LD_LIBRARY_PATH="$pythonLocation/lib" cargo test -p taskito-mesh - node-test: name: Node SDK Tests needs: changes - if: needs.changes.outputs.node == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: needs.changes.outputs.node == 'true' runs-on: ubuntu-latest steps: - name: Check out repository @@ -283,7 +265,7 @@ jobs: test: name: Python Tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) needs: [lint, changes] - if: needs.changes.outputs.python == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: needs.changes.outputs.python == 'true' runs-on: ${{ matrix.os }} env: # setup-python on macOS upgrades certifi via pip; pip's HTTP cache @@ -349,7 +331,7 @@ jobs: ci-status: name: CI status if: always() - needs: [changes, lint, rust-test, rust-test-postgres, rust-test-redis, rust-test-mesh, node-test, test] + needs: [changes, lint, rust-test, rust-test-postgres, rust-test-redis, node-test, test] runs-on: ubuntu-latest steps: - name: Check that no required job failed diff --git a/.github/workflows/publish-node.yml b/.github/workflows/publish-node.yml index 2a1ccd1c..7c76f26c 100644 --- a/.github/workflows/publish-node.yml +++ b/.github/workflows/publish-node.yml @@ -2,8 +2,10 @@ name: Publish to npm # Builds the napi addon as prebuilt per-platform `.node` binaries on native # runners (no cross-compilation — the same approach the Python publish.yml uses -# for wheels), then publishes the per-platform `taskito-` packages plus -# the main `taskito` package to npm via OIDC trusted publishing + provenance. +# for wheels), then publishes the per-platform `@byteveda/taskito-` +# packages plus the main `@byteveda/taskito` package to npm via OIDC trusted +# publishing + provenance. The npm scope sidesteps the spam-detection 403 that +# unscoped per-platform names trip on first publish. on: push: @@ -272,27 +274,35 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Publish per-platform packages + id: platforms if: ${{ !inputs.dry-run }} working-directory: sdks/node # Skip any package already live at this version so a rerun after a - # partial publish can finish the rest instead of aborting on the first. + # partial publish can finish the rest. A single platform failing (e.g. + # npm spam-detection 403) must NOT abort the run before the main package + # publishes — the SDK still installs on every platform that did land. + # Record failures and surface them at the end of the job instead. run: | + failed="" for dir in npm/*/; do pkg=$(node -p "require('./${dir}package.json').name") if npm view "${pkg}@${VERSION}" version >/dev/null 2>&1; then echo "::notice::${pkg}@${VERSION} already published — skipping" + elif npm publish "$dir" --provenance --access public; then + echo "Published ${pkg}@${VERSION}" else - echo "Publishing ${pkg}@${VERSION}" - npm publish "$dir" --provenance --access public + echo "::warning::Failed to publish ${pkg}@${VERSION}" + failed="${failed} ${pkg}" fi done + echo "failed=${failed# }" >> "$GITHUB_OUTPUT" - name: Publish main package if: ${{ !inputs.dry-run }} working-directory: sdks/node run: | - if npm view "taskito@${VERSION}" version >/dev/null 2>&1; then - echo "::notice::taskito@${VERSION} already published — skipping" + if npm view "@byteveda/taskito@${VERSION}" version >/dev/null 2>&1; then + echo "::notice::@byteveda/taskito@${VERSION} already published — skipping" else npm publish --provenance --access public fi @@ -306,13 +316,13 @@ jobs: if: ${{ !inputs.dry-run }} run: | for _attempt in 1 2 3 4 5; do - if npm view "taskito@${VERSION}" version >/dev/null 2>&1; then - echo "taskito@${VERSION} live on npm" + if npm view "@byteveda/taskito@${VERSION}" version >/dev/null 2>&1; then + echo "@byteveda/taskito@${VERSION} live on npm" exit 0 fi sleep 15 done - echo "::error::taskito@${VERSION} not found on npm" + echo "::error::@byteveda/taskito@${VERSION} not found on npm" exit 1 - name: Create git tag @@ -336,3 +346,17 @@ jobs: --title "taskito (Node) v${VERSION}" \ --generate-notes fi + + # The main package + tag + release are done; only now flag any platform + # binary that didn't publish, so a missing target turns the job red for a + # maintainer to chase without having blocked the otherwise-usable release. + - name: Fail if any platform package was skipped + if: ${{ always() && !inputs.dry-run && steps.platforms.outputs.failed != '' }} + # Pass the list via env, not inline ${{ }} expansion, so a package name + # can't break out of the shell script (template-injection defense). + env: + FAILED_PLATFORMS: ${{ steps.platforms.outputs.failed }} + run: | + echo "::error::Platform packages that did not publish: ${FAILED_PLATFORMS}" + echo "::error::Install on those platforms will fail until they are republished." + exit 1 diff --git a/docs/app/components/landing/sections.tsx b/docs/app/components/landing/sections.tsx index 51114862..404859f2 100644 --- a/docs/app/components/landing/sections.tsx +++ b/docs/app/components/landing/sections.tsx @@ -411,7 +411,7 @@ export function CTA() {

- +
diff --git a/docs/content/docs/node/api-reference/context.mdx b/docs/content/docs/node/api-reference/context.mdx index d9a8b870..abe5bba6 100644 --- a/docs/content/docs/node/api-reference/context.mdx +++ b/docs/content/docs/node/api-reference/context.mdx @@ -4,7 +4,7 @@ description: "currentJob() — the running task's job context." --- ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; const job = currentJob(); // JobContext | undefined ``` @@ -36,7 +36,7 @@ cancellation flow. ## `useResource` ```ts -import { useResource } from "taskito"; +import { useResource } from "@byteveda/taskito"; const db = await useResource("db"); // inside a running task ``` diff --git a/docs/content/docs/node/api-reference/errors.mdx b/docs/content/docs/node/api-reference/errors.mdx index e447af6c..ef4c1e77 100644 --- a/docs/content/docs/node/api-reference/errors.mdx +++ b/docs/content/docs/node/api-reference/errors.mdx @@ -7,7 +7,7 @@ Every error the SDK throws extends `TaskitoError`, so a single `catch` can scope all of them; the specific subclasses let you branch on what went wrong. ```ts -import { TaskitoError, ResourceNotFoundError } from "taskito"; +import { TaskitoError, ResourceNotFoundError } from "@byteveda/taskito"; try { await runJob(); diff --git a/docs/content/docs/node/api-reference/queue.mdx b/docs/content/docs/node/api-reference/queue.mdx index 9bb7c1ff..df34c064 100644 --- a/docs/content/docs/node/api-reference/queue.mdx +++ b/docs/content/docs/node/api-reference/queue.mdx @@ -4,7 +4,7 @@ description: "Construct a queue and the full producer/admin API." --- ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; const queue = new Queue(options); ``` diff --git a/docs/content/docs/node/api-reference/serializers.mdx b/docs/content/docs/node/api-reference/serializers.mdx index f347f10b..b0951ab9 100644 --- a/docs/content/docs/node/api-reference/serializers.mdx +++ b/docs/content/docs/node/api-reference/serializers.mdx @@ -4,8 +4,8 @@ description: "JsonSerializer, MsgpackSerializer, and the Serializer interface." --- ```ts -import { Queue, JsonSerializer, MsgpackSerializer } from "taskito"; -import type { Serializer } from "taskito"; +import { Queue, JsonSerializer, MsgpackSerializer } from "@byteveda/taskito"; +import type { Serializer } from "@byteveda/taskito"; ``` ## Built-ins diff --git a/docs/content/docs/node/getting-started/installation.mdx b/docs/content/docs/node/getting-started/installation.mdx index 8c304f08..45654cfa 100644 --- a/docs/content/docs/node/getting-started/installation.mdx +++ b/docs/content/docs/node/getting-started/installation.mdx @@ -7,7 +7,7 @@ The Node.js SDK is a thin [napi-rs](https://napi.rs) shell over the Taskito Rust core — peer to the Python SDK, with **no Python dependency**. ```bash -pnpm add taskito +pnpm add @byteveda/taskito ``` Requires Node.js >= 18. Ships as dual **ESM + CommonJS** with bundled type @@ -35,7 +35,7 @@ the `postgres,redis,workflows,mesh` cargo features enabled. ## Pick a backend ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; new Queue({ dbPath: "taskito.db" }); // SQLite (default) new Queue({ backend: "postgres", dsn: process.env.PG_URL, schema: "taskito" }); diff --git a/docs/content/docs/node/getting-started/quickstart.mdx b/docs/content/docs/node/getting-started/quickstart.mdx index 39c6d0b2..13eb03ff 100644 --- a/docs/content/docs/node/getting-started/quickstart.mdx +++ b/docs/content/docs/node/getting-started/quickstart.mdx @@ -6,7 +6,7 @@ description: "Define a task, enqueue a job, run a worker, await the result — i Four steps: construct a queue, register a task, enqueue a job, run a worker. ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "taskito.db" }); diff --git a/docs/content/docs/node/guides/core/cancellation.mdx b/docs/content/docs/node/guides/core/cancellation.mdx index e432a76f..787ec5c5 100644 --- a/docs/content/docs/node/guides/core/cancellation.mdx +++ b/docs/content/docs/node/guides/core/cancellation.mdx @@ -7,7 +7,7 @@ Cancellation is **cooperative**. A running task reads its context via `currentJob()` and watches the `AbortSignal`: ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; queue.task("download", async (url: string) => { const { signal } = currentJob() ?? {}; diff --git a/docs/content/docs/node/guides/core/predicates.mdx b/docs/content/docs/node/guides/core/predicates.mdx index 77a7cbef..efe53be4 100644 --- a/docs/content/docs/node/guides/core/predicates.mdx +++ b/docs/content/docs/node/guides/core/predicates.mdx @@ -34,7 +34,7 @@ queue.gate("charge", ({ args }) => args[0] <= 10_000); Combine predicates with `allOf`, `anyOf`, and `not`: ```ts -import { allOf, anyOf, not } from "taskito"; +import { allOf, anyOf, not } from "@byteveda/taskito"; const businessHours = () => { const h = new Date().getHours(); diff --git a/docs/content/docs/node/guides/core/streaming.mdx b/docs/content/docs/node/guides/core/streaming.mdx index 7f7b42ac..b5725019 100644 --- a/docs/content/docs/node/guides/core/streaming.mdx +++ b/docs/content/docs/node/guides/core/streaming.mdx @@ -13,7 +13,7 @@ Call `currentJob().publish(value)` from inside the task. The value must be JSON-serializable. ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; queue.task("train", async (epochs: number) => { const job = currentJob(); diff --git a/docs/content/docs/node/guides/extensibility/serializers.mdx b/docs/content/docs/node/guides/extensibility/serializers.mdx index eba847c7..d2c29cb8 100644 --- a/docs/content/docs/node/guides/extensibility/serializers.mdx +++ b/docs/content/docs/node/guides/extensibility/serializers.mdx @@ -15,7 +15,7 @@ concern. for confidentiality **and** integrity. ```ts -import { Queue, MsgpackSerializer } from "taskito"; +import { Queue, MsgpackSerializer } from "@byteveda/taskito"; new Queue({ dbPath: "taskito.db", serializer: new MsgpackSerializer() }); ``` @@ -25,7 +25,7 @@ new Queue({ dbPath: "taskito.db", serializer: new MsgpackSerializer() }); Implement the `Serializer` interface — `serialize` to bytes, `deserialize` back: ```ts -import type { Serializer } from "taskito"; +import type { Serializer } from "@byteveda/taskito"; const mySerializer: Serializer = { serialize: (value) => Buffer.from(JSON.stringify(value)), diff --git a/docs/content/docs/node/guides/integrations/express.mdx b/docs/content/docs/node/guides/integrations/express.mdx index 9fe71e13..94644969 100644 --- a/docs/content/docs/node/guides/integrations/express.mdx +++ b/docs/content/docs/node/guides/integrations/express.mdx @@ -7,7 +7,7 @@ Peer dependency: `express`. Import from the `taskito/contrib/express` subpath. ```ts import express from "express"; -import { taskitoRouter, taskitoDashboard } from "taskito/contrib/express"; +import { taskitoRouter, taskitoDashboard } from "@byteveda/taskito/contrib/express"; const app = express(); diff --git a/docs/content/docs/node/guides/integrations/fastify.mdx b/docs/content/docs/node/guides/integrations/fastify.mdx index c2140a8d..c191ebe5 100644 --- a/docs/content/docs/node/guides/integrations/fastify.mdx +++ b/docs/content/docs/node/guides/integrations/fastify.mdx @@ -10,7 +10,7 @@ import Fastify from "fastify"; import { taskitoFastify, taskitoDashboardPlugin, -} from "taskito/contrib/fastify"; +} from "@byteveda/taskito/contrib/fastify"; const app = Fastify(); diff --git a/docs/content/docs/node/guides/integrations/nest.mdx b/docs/content/docs/node/guides/integrations/nest.mdx index 03d8d3a5..61acea83 100644 --- a/docs/content/docs/node/guides/integrations/nest.mdx +++ b/docs/content/docs/node/guides/integrations/nest.mdx @@ -8,7 +8,7 @@ Peer dependencies: `@nestjs/common` + `reflect-metadata`. Import from the ```ts import { Module } from "@nestjs/common"; -import { TaskitoModule } from "taskito/contrib/nest"; +import { TaskitoModule } from "@byteveda/taskito/contrib/nest"; @Module({ imports: [TaskitoModule.forRoot(queue)], @@ -23,7 +23,7 @@ export class AppModule {} ```ts import { Injectable } from "@nestjs/common"; -import { TaskitoService } from "taskito/contrib/nest"; +import { TaskitoService } from "@byteveda/taskito/contrib/nest"; @Injectable() export class ReportService { diff --git a/docs/content/docs/node/guides/integrations/otel.mdx b/docs/content/docs/node/guides/integrations/otel.mdx index 506f5d38..6715cd09 100644 --- a/docs/content/docs/node/guides/integrations/otel.mdx +++ b/docs/content/docs/node/guides/integrations/otel.mdx @@ -7,7 +7,7 @@ Peer dependency: `@opentelemetry/api`. Import from the `taskito/contrib/otel` subpath — it is not exported from the main barrel. ```ts -import { otelMiddleware } from "taskito/contrib/otel"; +import { otelMiddleware } from "@byteveda/taskito/contrib/otel"; queue.use(otelMiddleware()); ``` diff --git a/docs/content/docs/node/guides/integrations/prometheus.mdx b/docs/content/docs/node/guides/integrations/prometheus.mdx index cf321c3b..ce4932c2 100644 --- a/docs/content/docs/node/guides/integrations/prometheus.mdx +++ b/docs/content/docs/node/guides/integrations/prometheus.mdx @@ -10,7 +10,7 @@ subpath. import { prometheusMiddleware, PrometheusStatsCollector, -} from "taskito/contrib/prometheus"; +} from "@byteveda/taskito/contrib/prometheus"; import { register } from "prom-client"; import express from "express"; diff --git a/docs/content/docs/node/guides/integrations/sentry.mdx b/docs/content/docs/node/guides/integrations/sentry.mdx index 4f6eb75e..3db58bad 100644 --- a/docs/content/docs/node/guides/integrations/sentry.mdx +++ b/docs/content/docs/node/guides/integrations/sentry.mdx @@ -8,7 +8,7 @@ registering the middleware. Import from the `taskito/contrib/sentry` subpath. ```ts import * as Sentry from "@sentry/node"; -import { sentryMiddleware } from "taskito/contrib/sentry"; +import { sentryMiddleware } from "@byteveda/taskito/contrib/sentry"; Sentry.init({ dsn: process.env.SENTRY_DSN }); diff --git a/docs/content/docs/node/guides/observability/logging.mdx b/docs/content/docs/node/guides/observability/logging.mdx index c5f9e760..e420b519 100644 --- a/docs/content/docs/node/guides/observability/logging.mdx +++ b/docs/content/docs/node/guides/observability/logging.mdx @@ -8,7 +8,7 @@ default, so it never pollutes stdout (the CLI's `--json` output and piped data stay clean). Taskito uses it internally; it is exported for your own use too. ```ts -import { createLogger } from "taskito"; +import { createLogger } from "@byteveda/taskito"; const log = createLogger("billing"); // tagged [taskito:billing] @@ -24,7 +24,7 @@ queue.task("charge", async (amount: number) => { read once from `TASKITO_LOG_LEVEL`; override it at runtime: ```ts -import { setLogLevel } from "taskito"; +import { setLogLevel } from "@byteveda/taskito"; setLogLevel("debug"); // global, immediate ``` @@ -58,7 +58,7 @@ Replace the output sink to route logs to a file, JSON transport, or a test buffer — globally and immediately: ```ts -import { setLogSink } from "taskito"; +import { setLogSink } from "@byteveda/taskito"; setLogSink((level, line) => myTransport.write({ level, line })); ``` diff --git a/docs/content/docs/node/guides/observability/monitoring.mdx b/docs/content/docs/node/guides/observability/monitoring.mdx index c36ed74b..560c30d1 100644 --- a/docs/content/docs/node/guides/observability/monitoring.mdx +++ b/docs/content/docs/node/guides/observability/monitoring.mdx @@ -34,7 +34,7 @@ A running task reports progress through its job context; inspection and the dashboard surface it live: ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; queue.task("import", async (rows: Row[]) => { const job = currentJob(); diff --git a/docs/content/docs/node/guides/operations/dashboard.mdx b/docs/content/docs/node/guides/operations/dashboard.mdx index 4ad5f154..3ef31f2f 100644 --- a/docs/content/docs/node/guides/operations/dashboard.mdx +++ b/docs/content/docs/node/guides/operations/dashboard.mdx @@ -14,7 +14,7 @@ taskito --db taskito.db dashboard --port 8787 Or programmatically: ```ts -import { Queue, serveDashboard } from "taskito"; +import { Queue, serveDashboard } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "taskito.db" }); const server = serveDashboard(queue, { port: 8787 }); diff --git a/docs/content/docs/node/guides/operations/keda.mdx b/docs/content/docs/node/guides/operations/keda.mdx index fdaf2a9f..5bed8a32 100644 --- a/docs/content/docs/node/guides/operations/keda.mdx +++ b/docs/content/docs/node/guides/operations/keda.mdx @@ -9,7 +9,7 @@ depth. The SDK ships a scaler server that KEDA's `metrics-api` scaler polls. ## Scaler server ```ts -import { Queue, serveScaler } from "taskito"; +import { Queue, serveScaler } from "@byteveda/taskito"; const queue = new Queue({ backend: "postgres", dsn: process.env.DATABASE_URL }); serveScaler(queue, { port: 9091, targetQueueDepth: 10 }); diff --git a/docs/content/docs/node/guides/operations/testing.mdx b/docs/content/docs/node/guides/operations/testing.mdx index 29240b8c..f2f77b1b 100644 --- a/docs/content/docs/node/guides/operations/testing.mdx +++ b/docs/content/docs/node/guides/operations/testing.mdx @@ -12,7 +12,7 @@ import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, expect, it } from "vitest"; -import { Queue, type Worker } from "taskito"; +import { Queue, type Worker } from "@byteveda/taskito"; let worker: Worker | undefined; afterEach(() => { @@ -59,7 +59,7 @@ the test — the worker injects whatever is registered, so no real connection is opened: ```ts -import { useResource } from "taskito"; +import { useResource } from "@byteveda/taskito"; queue.resource("db", () => fakeDb); queue.task("sync", async () => { diff --git a/docs/content/docs/node/guides/operations/troubleshooting.mdx b/docs/content/docs/node/guides/operations/troubleshooting.mdx index de5bd9bf..bef9af10 100644 --- a/docs/content/docs/node/guides/operations/troubleshooting.mdx +++ b/docs/content/docs/node/guides/operations/troubleshooting.mdx @@ -7,7 +7,7 @@ Turn on debug logs first — they show the worker claiming, running, and settlin jobs: ```ts -import { setLogLevel } from "taskito"; +import { setLogLevel } from "@byteveda/taskito"; setLogLevel("debug"); // or: TASKITO_LOG_LEVEL=debug node app.js ``` diff --git a/docs/content/docs/node/guides/reliability/guarantees.mdx b/docs/content/docs/node/guides/reliability/guarantees.mdx index af3eec43..f5e5771a 100644 --- a/docs/content/docs/node/guides/reliability/guarantees.mdx +++ b/docs/content/docs/node/guides/reliability/guarantees.mdx @@ -25,7 +25,7 @@ Because a job may run more than once, make side effects idempotent: [`uniqueKey`](/node/guides/reliability/idempotency). ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; queue.task("charge", async (orderId: string) => { const job = currentJob(); diff --git a/docs/content/docs/node/guides/resources/dependency-injection.mdx b/docs/content/docs/node/guides/resources/dependency-injection.mdx index 2e88cba4..cc845d7d 100644 --- a/docs/content/docs/node/guides/resources/dependency-injection.mdx +++ b/docs/content/docs/node/guides/resources/dependency-injection.mdx @@ -8,7 +8,7 @@ constructing those inside every handler, register them once as **resources** and let the worker build, share, and tear them down with the right lifetime. ```ts -import { Queue, useResource } from "taskito"; +import { Queue, useResource } from "@byteveda/taskito"; import { Pool } from "pg"; const queue = new Queue({ dbPath: "tasks.db" }); @@ -142,7 +142,7 @@ Register a stub factory to swap a real dependency in tests. `mockResource(value) wraps a value as a factory and records how often it was built: ```ts -import { mockResource } from "taskito"; +import { mockResource } from "@byteveda/taskito"; const db = mockResource({ query: async () => fakeRows }); queue.resource("db", db.factory); diff --git a/docs/content/docs/node/guides/resources/index.mdx b/docs/content/docs/node/guides/resources/index.mdx index e20e17d9..89cb202f 100644 --- a/docs/content/docs/node/guides/resources/index.mdx +++ b/docs/content/docs/node/guides/resources/index.mdx @@ -8,7 +8,7 @@ real dependencies (database pools, HTTP clients, cloud SDKs) are not. The Node SDK keeps those on the worker and passes only plain data through the queue. ```ts -import { Queue, useResource } from "taskito"; +import { Queue, useResource } from "@byteveda/taskito"; import { Pool } from "pg"; const queue = new Queue({ dbPath: "tasks.db" }); diff --git a/docs/content/docs/node/more/examples/benchmark.mdx b/docs/content/docs/node/more/examples/benchmark.mdx index 31347690..891d95f6 100644 --- a/docs/content/docs/node/more/examples/benchmark.mdx +++ b/docs/content/docs/node/more/examples/benchmark.mdx @@ -10,7 +10,7 @@ jobs, drains them with a worker, and reports the three rates that bound a queue. ## benchmark.ts ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; const N = 50_000; const queue = new Queue({ dbPath: "bench.db" }); diff --git a/docs/content/docs/node/more/examples/bulk-emails.mdx b/docs/content/docs/node/more/examples/bulk-emails.mdx index 7c3434f9..46bf0e4c 100644 --- a/docs/content/docs/node/more/examples/bulk-emails.mdx +++ b/docs/content/docs/node/more/examples/bulk-emails.mdx @@ -20,7 +20,7 @@ One job per recipient keeps retries and failures isolated — a bad address neve blocks the rest. The limits cap how fast and how many run at once. ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; export const queue = new Queue({ dbPath: "bulk-email.db" }); diff --git a/docs/content/docs/node/more/examples/data-pipeline.mdx b/docs/content/docs/node/more/examples/data-pipeline.mdx index b5fbd87e..c56f1b22 100644 --- a/docs/content/docs/node/more/examples/data-pipeline.mdx +++ b/docs/content/docs/node/more/examples/data-pipeline.mdx @@ -33,7 +33,7 @@ passing results down the graph — the standard ETL shape. `load` runs only afte both transforms complete. The long transform reports progress as it goes. ```ts -import { Queue, currentJob } from "taskito"; +import { Queue, currentJob } from "@byteveda/taskito"; export const queue = new Queue({ dbPath: "pipeline.db" }); diff --git a/docs/content/docs/node/more/examples/express-service.mdx b/docs/content/docs/node/more/examples/express-service.mdx index 9f2bd5ee..ab51a681 100644 --- a/docs/content/docs/node/more/examples/express-service.mdx +++ b/docs/content/docs/node/more/examples/express-service.mdx @@ -25,7 +25,7 @@ cancel request stops it cleanly. ```ts import express from "express"; -import { Queue, currentJob } from "taskito"; +import { Queue, currentJob } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "image-service.db" }); @@ -107,7 +107,7 @@ The worker is a separate process that shares the same database. It registers the same task so it knows how to run `processImage`. ```ts -import { Queue, currentJob } from "taskito"; +import { Queue, currentJob } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "image-service.db" }); diff --git a/docs/content/docs/node/more/examples/index.mdx b/docs/content/docs/node/more/examples/index.mdx index fbe10cb9..c04ffc9f 100644 --- a/docs/content/docs/node/more/examples/index.mdx +++ b/docs/content/docs/node/more/examples/index.mdx @@ -18,5 +18,5 @@ on the [core guides](/node/guides/core/tasks) — read those for the concepts, c | [Predicate-gated jobs](/node/more/examples/predicate-gated-jobs) | Enqueue-time gates with `allOf` / `anyOf` / `not` predicate composition | | [Benchmark](/node/more/examples/benchmark) | Measuring enqueue, processing, and end-to-end latency and throughput | -Every snippet targets the standalone Node SDK — `import { Queue } from "taskito"` — +Every snippet targets the standalone Node SDK — `import { Queue } from "@byteveda/taskito"` — with no Python in the loop. Pick the one closest to your problem and start there. diff --git a/docs/content/docs/node/more/examples/notifications.mdx b/docs/content/docs/node/more/examples/notifications.mdx index 91b05dbd..27a97107 100644 --- a/docs/content/docs/node/more/examples/notifications.mdx +++ b/docs/content/docs/node/more/examples/notifications.mdx @@ -21,7 +21,7 @@ notifications/ Each channel is its own task so they retry, rate-limit, and scale independently. ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; export const queue = new Queue({ dbPath: "notifications.db" }); diff --git a/docs/content/docs/node/more/examples/predicate-gated-jobs.mdx b/docs/content/docs/node/more/examples/predicate-gated-jobs.mdx index 869f1b3c..082cffbb 100644 --- a/docs/content/docs/node/more/examples/predicate-gated-jobs.mdx +++ b/docs/content/docs/node/more/examples/predicate-gated-jobs.mdx @@ -19,7 +19,7 @@ A predicate receives `{ taskName, args }` — the positional args after any `onEnqueue` rewrites. Compose them with `allOf` / `anyOf` / `not`. ```ts -import type { Predicate } from "taskito"; +import type { Predicate } from "@byteveda/taskito"; export const duringBusinessHours: Predicate = () => { const hour = new Date().getUTCHours(); @@ -39,7 +39,7 @@ export const underQuota: Predicate = ({ args }) => { `queue.gate` attaches one or more predicates to a task; every gate must pass. ```ts -import { Queue, allOf, not } from "taskito"; +import { Queue, allOf, not } from "@byteveda/taskito"; import { duringBusinessHours, featureEnabled, underQuota } from "./predicates"; export const queue = new Queue({ dbPath: "gated.db" }); @@ -58,7 +58,7 @@ queue.gate("exportData", allOf(underQuota, not(duringBusinessHours))); // off-pe A rejected enqueue throws synchronously — handle it where you submit. ```ts -import { PredicateRejectedError } from "taskito"; +import { PredicateRejectedError } from "@byteveda/taskito"; try { queue.enqueue("sendPromo", ["newsletter-subscribers"]); diff --git a/docs/content/docs/node/more/examples/saga-checkout.mdx b/docs/content/docs/node/more/examples/saga-checkout.mdx index dcfa7088..d4fbf93b 100644 --- a/docs/content/docs/node/more/examples/saga-checkout.mdx +++ b/docs/content/docs/node/more/examples/saga-checkout.mdx @@ -14,7 +14,7 @@ Declare each forward task and its rollback. A compensator receives the forward step's result as its single argument, so it knows exactly what to undo. ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; export const queue = new Queue({ dbPath: "checkout.db" }); diff --git a/docs/content/docs/node/more/examples/web-scraper.mdx b/docs/content/docs/node/more/examples/web-scraper.mdx index f6922356..6afef97b 100644 --- a/docs/content/docs/node/more/examples/web-scraper.mdx +++ b/docs/content/docs/node/more/examples/web-scraper.mdx @@ -24,7 +24,7 @@ backoff. Network tasks run on a dedicated `network` queue so a backlog there never starves CPU-bound extraction. ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; export const queue = new Queue({ dbPath: "scraper.db" }); diff --git a/sdks/node/README.md b/sdks/node/README.md index 195ee04a..c12f3379 100644 --- a/sdks/node/README.md +++ b/sdks/node/README.md @@ -8,19 +8,19 @@ share storage (SQLite, PostgreSQL, or Redis). ## Install ```bash -pnpm add taskito +pnpm add @byteveda/taskito ``` Requires Node.js >= 18. Ships as dual ESM + CommonJS. A prebuilt native binary is installed automatically for your platform via an optional per-platform package -(`taskito--`) — linux x64/arm64 (gnu + musl), macOS x64/arm64, and +(`@byteveda/taskito--`) — linux x64/arm64 (gnu + musl), macOS x64/arm64, and Windows x64. On a platform without a prebuilt, build from source with the Rust toolchain + napi-rs CLI (`pnpm build:native`). ## Quickstart ```ts -import { Queue } from "taskito"; +import { Queue } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "taskito.db" }); @@ -63,7 +63,7 @@ pending/running), `metadata`, `namespace`. Cancellation is cooperative. A running task reads its context via `currentJob()`: ```ts -import { currentJob } from "taskito"; +import { currentJob } from "@byteveda/taskito"; queue.task("download", async (url: string) => { const { signal } = currentJob() ?? {}; @@ -105,7 +105,7 @@ Args and results are serialized with a pluggable `Serializer` (default payloads as opaque bytes. ```ts -import { Queue, MsgpackSerializer } from "taskito"; +import { Queue, MsgpackSerializer } from "@byteveda/taskito"; new Queue({ dbPath: "taskito.db", serializer: new MsgpackSerializer() }); ``` @@ -358,7 +358,7 @@ taskito --db taskito.db dashboard --port 8787 Or programmatically: ```ts -import { Queue, serveDashboard } from "taskito"; +import { Queue, serveDashboard } from "@byteveda/taskito"; const queue = new Queue({ dbPath: "taskito.db" }); const server = serveDashboard(queue, { port: 8787 }); @@ -402,8 +402,8 @@ package or exported from the `taskito` barrel. ### Observability ```ts -import { otelMiddleware } from "taskito/contrib/otel"; // peer: @opentelemetry/api -import { prometheusMiddleware, PrometheusStatsCollector } from "taskito/contrib/prometheus"; // peer: prom-client +import { otelMiddleware } from "@byteveda/taskito/contrib/otel"; // peer: @opentelemetry/api +import { prometheusMiddleware, PrometheusStatsCollector } from "@byteveda/taskito/contrib/prometheus"; // peer: prom-client queue.use(otelMiddleware()); // one span per execution: taskito.execute. queue.use(prometheusMiddleware()); // taskito_jobs_total, _job_duration_seconds, _active_workers, _retries_total @@ -418,7 +418,7 @@ OTel options: `tracerName`, `attributePrefix`, `spanName(ctx)`, `extraAttributes (metrics for one namespace are built once per registry, so multiple middlewares are safe). ```ts -import { sentryMiddleware } from "taskito/contrib/sentry"; // peer: @sentry/node +import { sentryMiddleware } from "@byteveda/taskito/contrib/sentry"; // peer: @sentry/node queue.use(sentryMiddleware()); // call Sentry.init(...) yourself first ``` @@ -433,11 +433,11 @@ to also report each intermediate failure as a warning. Other options: `tagPrefix helper mounts the dashboard (SPA + `/api/*`) into your app. ```ts -import { taskitoRouter, taskitoDashboard } from "taskito/contrib/express"; // peer: express +import { taskitoRouter, taskitoDashboard } from "@byteveda/taskito/contrib/express"; // peer: express app.use("/tasks", taskitoRouter(queue)); // POST /enqueue, GET /stats, /jobs/:id, ... app.use("/admin", taskitoDashboard(queue)); // dashboard SPA + /api/* -import { taskitoFastify, taskitoDashboardPlugin } from "taskito/contrib/fastify"; // peer: fastify +import { taskitoFastify, taskitoDashboardPlugin } from "@byteveda/taskito/contrib/fastify"; // peer: fastify app.register(taskitoFastify, { queue, prefix: "/tasks" }); app.register(taskitoDashboardPlugin, { queue, prefix: "/admin" }); ``` @@ -449,7 +449,7 @@ and `resultTimeoutMs`. NestJS exposes an injectable service: ```ts -import { TaskitoModule, TaskitoService } from "taskito/contrib/nest"; // peers: @nestjs/common, reflect-metadata +import { TaskitoModule, TaskitoService } from "@byteveda/taskito/contrib/nest"; // peers: @nestjs/common, reflect-metadata @Module({ imports: [TaskitoModule.forRoot(queue)] }) export class AppModule {} diff --git a/sdks/node/package.json b/sdks/node/package.json index 29de4707..cd383143 100644 --- a/sdks/node/package.json +++ b/sdks/node/package.json @@ -1,5 +1,5 @@ { - "name": "taskito", + "name": "@byteveda/taskito", "version": "0.16.3", "description": "Rust-powered task queue for Node.js — no broker required.", "license": "MIT",