{"_id":"@anushdsouza/world-heroku","name":"@anushdsouza/world-heroku","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@anushdsouza/world-heroku","version":"0.1.0","description":"A personal, unofficial community World adapter for running Workflow DevKit applications on Heroku Postgres.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./schema":{"types":"./dist/schema.d.ts","default":"./dist/schema.js"},"./package.json":"./package.json"},"bin":{"bootstrap":"bin/bootstrap.js","workflow-heroku-bootstrap":"bin/bootstrap.js"},"scripts":{"audit":"npm audit --audit-level=high","build":"tsc && chmod +x bin/bootstrap.js","check":"npm run lint && npm run typecheck && npm test && npm run build && npm run test:exports && npm run pack:check && npm run audit && npm run release:safety","clean":"tsc --build --clean","dev":"tsc --watch","format":"biome check --write .","lint":"biome check .","pack:check":"npm pack --dry-run","prepack":"npm run build","release:safety":"node scripts/public-release-check.mjs","test":"vitest run","test:exports":"node --input-type=module -e \"const world = await import('@anushdsouza/world-heroku'); const schema = await import('@anushdsouza/world-heroku/schema'); if (typeof world.createWorld !== 'function' || !('runs' in schema)) process.exit(1)\"","test:integration":"npm run build && node scripts/run-integration.mjs","test:release":"npm run check && npm run test:integration","typecheck":"tsc --noEmit"},"engines":{"node":">=22"},"packageManager":"npm@11.17.0","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dsouzaAnush/world-heroku.git"},"homepage":"https://github.com/dsouzaAnush/world-heroku#readme","bugs":{"url":"https://github.com/dsouzaAnush/world-heroku/issues"},"license":"Apache-2.0","author":{"name":"Anush D'Souza","email":"anush11395@gmail.com","url":"https://github.com/dsouzaAnush"},"keywords":["workflow","workflow-devkit","world","heroku","postgres","durable-execution","durable-functions"],"dependencies":{"@workflow/world-postgres":"4.3.3"},"overrides":{"@hono/node-server":"1.19.15","hono":"4.12.34","nanoid":"5.1.16","undici":"7.29.0"},"peerDependencies":{"workflow":">=4.8.2 <5"},"devDependencies":{"@biomejs/biome":"2.5.8","@types/node":"^24.0.0","@workflow/world-testing":"4.1.17","typescript":"5.9.3","vitest":"4.1.10","workflow":"4.8.2"},"gitHead":"e14298514df381c28baac3dcb70f72eca46e228f","_id":"@anushdsouza/world-heroku@0.1.0","_nodeVersion":"26.5.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-8kLQZ5+KHUYpHeysOQT+zf9hYOuPQf37Ng5CkArVlNYCdkUVKCb42dEh/x9lLfeYp+kjqzzw0nXub3ExpDUbaw==","shasum":"8efcc53efb5293b0af3c1645495717c86ee76bea","tarball":"https://registry.npmjs.org/@anushdsouza/world-heroku/-/world-heroku-0.1.0.tgz","fileCount":11,"unpackedSize":35613,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIE+rXA/JWqtwuamEPE81TXE9S+GtqiHd6MDrmAjB8niIAiAgdbHnlYoH3a8D9W1HcrPKPuc2P+fuEvw0FA7izTLKVQ=="}]},"_npmUser":{"name":"anushdsouza","email":"anush11395@gmail.com"},"directories":{},"maintainers":[{"name":"anushdsouza","email":"anush11395@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/world-heroku_0.1.0_1787252011659_0.612774789833584"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-20T18:53:31.382Z","0.1.0":"2026-08-20T18:53:31.789Z","modified":"2026-08-20T18:53:32.020Z"},"maintainers":[{"name":"anushdsouza","email":"anush11395@gmail.com"}],"description":"A personal, unofficial community World adapter for running Workflow DevKit applications on Heroku Postgres.","homepage":"https://github.com/dsouzaAnush/world-heroku#readme","keywords":["workflow","workflow-devkit","world","heroku","postgres","durable-execution","durable-functions"],"repository":{"type":"git","url":"git+https://github.com/dsouzaAnush/world-heroku.git"},"author":{"name":"Anush D'Souza","email":"anush11395@gmail.com","url":"https://github.com/dsouzaAnush"},"bugs":{"url":"https://github.com/dsouzaAnush/world-heroku/issues"},"license":"Apache-2.0","readme":"<h1 align=\"center\">Workflow World for Heroku</h1>\n\n<p align=\"center\">\n  Run durable Workflow DevKit applications on Heroku with Heroku Postgres.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://workflow-sdk.dev/worlds\">Workflow Worlds</a>\n  ·\n  <a href=\"./docs/architecture.md\">Architecture</a>\n  ·\n  <a href=\"./docs/publishing.md\">Release guide</a>\n</p>\n\n[![CI](https://github.com/dsouzaAnush/world-heroku/actions/workflows/ci.yml/badge.svg)](https://github.com/dsouzaAnush/world-heroku/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/@anushdsouza/world-heroku.svg)](https://www.npmjs.com/package/@anushdsouza/world-heroku)\n[![Apache-2.0 license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)\n\n`@anushdsouza/world-heroku` is a personal, unofficial community\n[World](https://workflow-sdk.dev/worlds) adapter for the\n[Workflow DevKit](https://workflow-sdk.dev) on Heroku. It uses Heroku Postgres\nfor workflow history, steps, hooks, streams, durable jobs, retries, and timers.\n\nThe adapter composes the official\n[`@workflow/world-postgres`](https://github.com/vercel/workflow/tree/main/packages/world-postgres)\nimplementation and adds the provider boundary that Heroku applications need:\n\n- zero-argument configuration from Heroku's `DATABASE_URL` config var;\n- Heroku-specific concurrency, pool, and queue-prefix config vars;\n- fail-fast production configuration instead of a silent localhost fallback;\n- a database bootstrap command designed for Heroku's release phase; and\n- Heroku-specific deployment and capacity guidance.\n\n> [!IMPORTANT]\n> This is a personal, experimental community project maintained by Anush\n> D'Souza. It is not affiliated with, endorsed by, or supported by Heroku,\n> Salesforce, or Vercel, and it is not a managed durable-execution service.\n> “Heroku” is used only to describe platform compatibility. The current\n> implementation inherits the Postgres World's embedded worker model and its\n> limitations.\n\n## Install\n\n```bash\nnpm install workflow @anushdsouza/world-heroku\n```\n\nThe package uses its maintainer's npm scope, following the Workflow ecosystem\nconvention for independently published community Worlds. The `@workflow/*`\nnamespace is reserved for packages published by the Workflow project.\n\n## Deploy on Heroku\n\nAttach Heroku Postgres to your application, then select the World:\n\n```bash\nheroku config:set \\\n  WORKFLOW_TARGET_WORLD=\"@anushdsouza/world-heroku\" \\\n  --app your-app\n```\n\nHeroku Postgres supplies `DATABASE_URL` automatically. Bootstrap the Workflow\nschema before starting application dynos by adding this command to the\napplication's release phase:\n\n```text\nrelease: npx --no-install workflow-heroku-bootstrap\nweb: npm start\n```\n\nThe bootstrap is idempotent. A failed release command prevents the new release\nfrom replacing the currently running release.\n\n### Start the embedded worker\n\nThe Postgres-backed World has a background Graphile Worker. Applications must\ncall `world.start()` once during server initialization.\n\nFor Next.js, add a root `instrumentation.ts`:\n\n```typescript\nexport async function register() {\n  if (process.env.NEXT_RUNTIME !== 'edge') {\n    const { getWorld } = await import('workflow/runtime');\n    const world = await getWorld();\n    await world.start?.();\n  }\n}\n```\n\nConfigure Workflow DevKit in `next.config.ts` as usual:\n\n```typescript\nimport type { NextConfig } from 'next';\nimport { withWorkflow } from 'workflow/next';\n\nconst nextConfig: NextConfig = {};\n\nexport default withWorkflow(nextConfig);\n```\n\nFor other long-lived Node.js frameworks, call the same `getWorld()` and\n`start()` sequence from the framework's server-startup hook. The web process\nmust expose Workflow DevKit's generated HTTP routes and listen on Heroku's\n`PORT`.\n\n## Configuration\n\n`createWorld()` accepts the same programmatic configuration as the official\nPostgres World. With no arguments, it resolves these config vars in order:\n\n| Concern | Preferred variable | Compatible fallback | Default |\n| --- | --- | --- | --- |\n| PostgreSQL URL | `WORKFLOW_HEROKU_POSTGRES_URL` | `WORKFLOW_POSTGRES_URL`, then `DATABASE_URL` | Required |\n| Worker concurrency per process | `WORKFLOW_HEROKU_WORKER_CONCURRENCY` | `WORKFLOW_POSTGRES_WORKER_CONCURRENCY` | `10` |\n| Internal pool size per process | `WORKFLOW_HEROKU_MAX_POOL_SIZE` | `WORKFLOW_POSTGRES_MAX_POOL_SIZE` | `10` |\n| Graphile job prefix | `WORKFLOW_HEROKU_JOB_PREFIX` | `WORKFLOW_POSTGRES_JOB_PREFIX` | Unset |\n\nAll numeric values must be positive integers. Heroku-specific variables win\nwhen both forms are set.\n\nProgrammatic usage is also supported:\n\n```typescript\nimport { createWorld } from '@anushdsouza/world-heroku';\n\nconst world = createWorld({\n  connectionString: process.env.DATABASE_URL!,\n  jobPrefix: 'billing',\n  maxPoolSize: 10,\n  queueConcurrency: 8,\n});\n\nawait world.start();\n```\n\nThe package also re-exports the Postgres schema:\n\n```typescript\nimport { events, hooks, runs, steps, streams } from '@anushdsouza/world-heroku';\n// or: import * as schema from '@anushdsouza/world-heroku/schema';\n```\n\n## Architecture\n\n```text\nHeroku web dyno\n├── application server\n├── /.well-known/workflow/v1/* execution routes\n└── embedded Graphile Worker\n          │\n          ▼\nHeroku Postgres\n├── workflow run, event, step, hook, and stream tables\n└── durable Graphile jobs, retries, and scheduled wake-ups\n```\n\nThe queue worker sends workflow and step execution back to the HTTP server on\nthe same dyno through loopback. This is why the first release uses one\nlong-lived web process rather than presenting a separate Heroku `worker`\nprocess as if it were already supported by the upstream adapter.\n\nWorkflow history and queued work survive dyno replacement because they live in\nPostgres. External side effects can still be delivered at least once; steps\nthat charge a card, send a message, or mutate another system must use domain\nidempotency keys.\n\nRead [the architecture notes](./docs/architecture.md) before changing dyno\nformation, connection limits, or worker concurrency.\n\n## Operational checklist\n\nBefore using this adapter for a production workload:\n\n1. Run `workflow-heroku-bootstrap` in the release phase.\n2. Confirm server startup calls `world.start()` exactly once per process.\n3. Keep `maxPoolSize` within the database plan's total connection budget after\n   multiplying it by every web process.\n4. Keep worker concurrency within both application and downstream-system\n   capacity.\n5. Make every external side effect idempotent and safe to retry.\n6. Define run-history retention and cleanup outside this package.\n7. Add payload encryption, access control, tenant isolation, quotas, and audit\n   export appropriate to the application.\n8. Test deploy, restart, database-unavailable, delayed-retry, and hook-resume\n   recovery paths against the intended Heroku formation.\n\n## Known limits\n\n- This is a provider adapter over Postgres World, not a new execution engine.\n- The embedded worker is coupled to the web process and its loopback execution\n  routes.\n- Horizontal web scaling multiplies worker concurrency and database pools.\n- General workflow-history retention and cleanup are not implemented upstream.\n- Application payload encryption and tenant isolation are not added here.\n- Postgres World does not provide Vercel's deployment-affinity semantics.\n- Heroku's 30-second graceful-shutdown window still requires idempotent,\n  retry-safe handlers.\n\n## Development and verification\n\nRequirements: Node.js 22 or newer and npm 11.\n\n```bash\nnpm install\nnpm run check\nnpm run test:integration\nnpm run release:safety\n```\n\n`npm run check` validates formatting and lint rules, TypeScript declarations,\nunit tests, public package exports, the exact npm tarball contents, and known\ndependency vulnerabilities. `npm run test:integration` builds the package,\nstarts an isolated PostgreSQL 18 container when `TEST_DATABASE_URL` is not\nalready set, verifies that schema bootstrap is idempotent, and runs both the\nadapter smoke test and the official Workflow World conformance suite. CI runs\nthat same real-database gate on Node.js 22, 24, and 26. `npm run\nrelease:safety` scans the complete public source candidate for credential\npatterns, private filesystem paths, unsafe environment files, private keys,\nand oversized or unexpected release artifacts.\n\n## Release and Workflow community listing\n\nReleases are published from an exact `vX.Y.Z` tag on `main` after the full test\ngate has passed. The npm package and source repository must both be public\nbefore this World can be submitted to Workflow's ecosystem manifest.\n\nThe complete sequence—including the one-time npm bootstrap, trusted\npublishing, repository protection, release verification, and the upstream\n`worlds-manifest.json` pull request—is in\n[docs/publishing.md](./docs/publishing.md). The proposed manifest object is\n[docs/worlds-manifest-entry.json](./docs/worlds-manifest-entry.json).\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for the contribution workflow. The\nlatest evidence and outstanding publication gates are in\n[docs/release-readiness.md](./docs/release-readiness.md).\n\n## License\n\nApache License 2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).\n","readmeFilename":"README.md","_rev":"1-565c50d40025311aa26729940cb9e8c6"}