{"_id":"@autumnsgrove/infra","_rev":"2-4a0e39f1955a7a593414e44775ed3019","name":"@autumnsgrove/infra","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@autumnsgrove/infra","version":"0.1.0","keywords":["grove","infra","infrastructure","cloudflare","abstraction","adapters"],"author":{"name":"AutumnsGrove"},"license":"AGPL-3.0-only","_id":"@autumnsgrove/infra@0.1.0","maintainers":[{"name":"autumnsgrove","email":"autumnbrown23@pm.me"}],"homepage":"https://github.com/AutumnsGrove/Lattice#readme","bugs":{"url":"https://github.com/AutumnsGrove/Lattice/issues"},"dist":{"shasum":"453fc9305fb02f54c02d292535148a2832c5907d","tarball":"https://registry.npmjs.org/@autumnsgrove/infra/-/infra-0.1.0.tgz","fileCount":29,"integrity":"sha512-TZQO8DAyVVH2L9zsjezsQR1yKiRGpkGID/obyQgYBQoVUiNeouQWKRLkmnQ1D3a+xNcZNF8McAQOzfKhzNpTCQ==","signatures":[{"sig":"MEYCIQC9Yq1KUKAm21qxqZeMOP6DuE3hht3aFnKkUBcpvy+JZQIhAN/kvOYC8DUveTSsey0cfAFR0u2JBjLrTJNjKv+FYYFL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138208},"main":"./src/index.ts","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","default":"./src/index.ts"},"./vite":{"default":"./src/vite/grove-vite-config.js"},"./testing":{"types":"./src/testing/index.ts","default":"./src/testing/index.ts"},"./response":{"types":"./src/response.ts","default":"./src/response.ts"},"./cloudflare":{"types":"./src/cloudflare/index.ts","default":"./src/cloudflare/index.ts"},"./middleware":{"types":"./src/middleware/auth.ts","default":"./src/middleware/auth.ts"}},"gitHead":"4c31481bd2edbec7f614baad6065bf5ea2d9b702","scripts":{"test":"vitest","check":"tsc --noEmit","test:run":"vitest run"},"_npmUser":{"name":"autumnsgrove","email":"autumnbrown23@pm.me"},"repository":{"url":"git+https://github.com/AutumnsGrove/Lattice.git","type":"git","directory":"libs/infra"},"_npmVersion":"11.16.0","description":"Infrastructure abstraction layer for Grove — the roots run deep, the tree stands anywhere","directories":{},"_nodeVersion":"26.3.0","dependencies":{"hono":"^4.11.9","@autumnsgrove/grove-errors":"workspace:*"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.1","vitest":"^4.1.0","typescript":"^5.9.3","@sveltejs/kit":"^2.50.2","@cloudflare/workers-types":"^4.20260317.1"},"_npmOperationalInternal":{"tmp":"tmp/infra_0.1.0_1782495467453_0.9005677800919736","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@autumnsgrove/infra","version":"0.1.1","description":"Infrastructure abstraction layer for Grove — the roots run deep, the tree stands anywhere","author":{"name":"AutumnsGrove"},"license":"AGPL-3.0-only","repository":{"type":"git","url":"git+https://github.com/AutumnsGrove/Lattice.git","directory":"libs/infra"},"type":"module","main":"./src/index.ts","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","default":"./src/index.ts"},"./cloudflare":{"types":"./src/cloudflare/index.ts","default":"./src/cloudflare/index.ts"},"./testing":{"types":"./src/testing/index.ts","default":"./src/testing/index.ts"},"./response":{"types":"./src/response.ts","default":"./src/response.ts"},"./middleware":{"types":"./src/middleware/auth.ts","default":"./src/middleware/auth.ts"},"./vite":{"default":"./src/vite/grove-vite-config.js"}},"dependencies":{"hono":"^4.11.9","@autumnsgrove/grove-errors":"0.1.0"},"devDependencies":{"@cloudflare/workers-types":"^4.20260317.1","@sveltejs/kit":"^2.50.2","typescript":"^5.9.3","vite":"^7.3.1","vitest":"^4.1.0"},"keywords":["grove","infra","infrastructure","cloudflare","abstraction","adapters"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"scripts":{"check":"tsc --noEmit","test":"vitest","test:run":"vitest run"},"_id":"@autumnsgrove/infra@0.1.1","bugs":{"url":"https://github.com/AutumnsGrove/Lattice/issues"},"homepage":"https://github.com/AutumnsGrove/Lattice#readme","_integrity":"sha512-86IRy1n6G3DLMSyO3V2WFgsfFYyEKYJLvUBobAfaSV5OIy4g6On90D+ehj5+haXQaYlY6+33KCAHIPheubUcJg==","_resolved":"/private/var/folders/qy/7dsqmqmj0v71d0dty0l80sm00000gn/T/6e016772a67b307b2a4b57a57b2151d8/autumnsgrove-infra-0.1.1.tgz","_from":"file:autumnsgrove-infra-0.1.1.tgz","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-86IRy1n6G3DLMSyO3V2WFgsfFYyEKYJLvUBobAfaSV5OIy4g6On90D+ehj5+haXQaYlY6+33KCAHIPheubUcJg==","shasum":"eb088d60b67a7d0a82471cbf783c68b32ab3b3ed","tarball":"https://registry.npmjs.org/@autumnsgrove/infra/-/infra-0.1.1.tgz","fileCount":30,"unpackedSize":157160,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDMZDguoc1838tJIvEdQKFalSuvfsCmhi/rbisz9rTSMQIgAXOnDsBw+il/JBenMRgjh2gBkC64ixYgz6mPpnXYeu4="}]},"_npmUser":{"name":"autumnsgrove","email":"autumnbrown23@pm.me"},"directories":{},"maintainers":[{"name":"autumnsgrove","email":"autumnbrown23@pm.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/infra_0.1.1_1782497558983_0.7573758236331356"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-26T17:37:47.317Z","modified":"2026-06-26T18:12:39.271Z","0.1.0":"2026-06-26T17:37:47.589Z","0.1.1":"2026-06-26T18:12:39.135Z"},"bugs":{"url":"https://github.com/AutumnsGrove/Lattice/issues"},"author":{"name":"AutumnsGrove"},"license":"AGPL-3.0-only","homepage":"https://github.com/AutumnsGrove/Lattice#readme","keywords":["grove","infra","infrastructure","cloudflare","abstraction","adapters"],"repository":{"type":"git","url":"git+https://github.com/AutumnsGrove/Lattice.git","directory":"libs/infra"},"description":"Infrastructure abstraction layer for Grove — the roots run deep, the tree stands anywhere","maintainers":[{"name":"autumnsgrove","email":"autumnbrown23@pm.me"}],"readme":"# Infra SDK\n\n> _The roots run deep. The tree stands anywhere._\n\nInfrastructure abstraction layer that wraps each primitive (database, storage, key-value, scheduling, service calls, configuration) in a clean TypeScript interface. Today, Cloudflare adapters power everything. Tomorrow, the same application code could run on any cloud. The interface stays the same. Only the roots change.\n\n## Installation\n\n```bash\n# Direct dependency\npnpm add @autumnsgrove/infra\n\n# Or via Lattice monorepo (recommended for Grove apps)\nimport type { GroveContext } from \"@autumnsgrove/lattice/infra\";\nimport { createCloudflareContext } from \"@autumnsgrove/lattice/infra/cloudflare\";\nimport { createMockContext } from \"@autumnsgrove/lattice/infra/testing\";\n```\n\n## Quick Start\n\n```typescript\nimport { createCloudflareContext } from \"@autumnsgrove/infra/cloudflare\";\n\nexport default {\n\tasync fetch(request: Request, env: Env): Promise<Response> {\n\t\tconst ctx = createCloudflareContext({\n\t\t\tdb: env.DB,\n\t\t\tstorage: env.STORAGE,\n\t\t\tkv: env.CACHE_KV,\n\t\t\tservices: { auth: env.AUTH, amber: env.AMBER },\n\t\t\tenv,\n\t\t});\n\t\treturn handleRequest(request, ctx);\n\t},\n};\n```\n\nApplication code only sees `GroveContext` — never Cloudflare-specific types:\n\n```typescript\nasync function handleRequest(request: Request, ctx: GroveContext): Promise<Response> {\n\tconst posts = await ctx.db.execute(\"SELECT * FROM posts WHERE tenant_id = ?\", [tenantId]);\n\tconst avatar = await ctx.storage.get(`${tenantId}/avatar.webp`);\n\tconst cached = await ctx.kv.get(`cache:${tenantId}:settings`);\n\t// ...\n}\n```\n\n## Interfaces\n\n| Interface         | `ctx.*`         | CF Adapter             | What It Wraps       |\n| ----------------- | --------------- | ---------------------- | ------------------- |\n| `GroveDatabase`   | `ctx.db`        | `CloudflareDatabase`   | D1 (SQLite)         |\n| `GroveStorage`    | `ctx.storage`   | `CloudflareStorage`    | R2 (S3-compatible)  |\n| `GroveKV`         | `ctx.kv`        | `CloudflareKV`         | Workers KV          |\n| `GroveServiceBus` | `ctx.services`  | `CloudflareServiceBus` | Service Bindings    |\n| `GroveScheduler`  | `ctx.scheduler` | `CloudflareScheduler`  | Cron Triggers       |\n| `GroveConfig`     | `ctx.config`    | `CloudflareConfig`     | Worker env bindings |\n\n## Export Paths\n\n| Path                             | Contents                                | Use In                            |\n| -------------------------------- | --------------------------------------- | --------------------------------- |\n| `@autumnsgrove/infra`            | Interfaces + types + error catalog      | Application code, type signatures |\n| `@autumnsgrove/infra/cloudflare` | CF adapters + `createCloudflareContext` | Worker entry points only          |\n| `@autumnsgrove/infra/testing`    | In-memory mocks + `createMockContext`   | Test files                        |\n\nVia Lattice re-exports: `@autumnsgrove/lattice/infra`, `@autumnsgrove/lattice/infra/cloudflare`, `@autumnsgrove/lattice/infra/testing`.\n\n## Testing\n\n```typescript\nimport { describe, it, expect, beforeEach } from \"vitest\";\nimport { createMockContext, type MockGroveContext } from \"@autumnsgrove/infra/testing\";\n\ndescribe(\"PostService\", () => {\n\tlet ctx: MockGroveContext;\n\n\tbeforeEach(() => {\n\t\tctx = createMockContext();\n\t});\n\n\tit(\"should fetch posts from database\", async () => {\n\t\tctx.db.whenQuery(\"SELECT\", [{ id: 1, title: \"Hello\" }]);\n\t\tconst result = await ctx.db.execute(\"SELECT * FROM posts\");\n\t\texpect(result.results).toHaveLength(1);\n\t});\n\n\tit(\"should upload to storage\", async () => {\n\t\tawait ctx.storage.put(\"tenant/file.txt\", \"hello\");\n\t\texpect(ctx.storage.has(\"tenant/file.txt\")).toBe(true);\n\t});\n\n\tit(\"should read config\", () => {\n\t\tctx.config.set(\"STRIPE_KEY\", \"sk_test_123\");\n\t\texpect(ctx.config.require(\"STRIPE_KEY\")).toBe(\"sk_test_123\");\n\t});\n});\n```\n\n## Error Codes\n\nThe SDK uses `SRV-XXX` Signpost error codes. All errors flow through `logGroveError` with warm user-facing messages and detailed admin messages.\n\n| Range                 | Category                        | Examples                                             |\n| --------------------- | ------------------------------- | ---------------------------------------------------- |\n| `SRV-001` – `SRV-019` | Infrastructure & initialization | Missing bindings, context init failure               |\n| `SRV-020` – `SRV-039` | Auth & sessions                 | Reserved for future use                              |\n| `SRV-040` – `SRV-059` | Business logic / operations     | Query failures, upload errors, service call failures |\n| `SRV-060` – `SRV-079` | Rate limiting                   | Reserved for Threshold integration                   |\n| `SRV-080` – `SRV-099` | Internal / catch-all            | Adapter errors, serialization, timeouts              |\n\nSee `docs/specs/server-sdk-spec.md` for the full catalog.\n\n## Architecture\n\nThe SDK follows the **Ports and Adapters** pattern (hexagonal architecture):\n\n- **Ports** = the TypeScript interfaces (`GroveDatabase`, `GroveStorage`, etc.)\n- **Adapters** = the platform-specific implementations (`CloudflareDatabase`, etc.)\n- **Context** = the wiring point that connects ports to adapters (`createCloudflareContext`)\n\nApplication code depends only on ports. Adapters are injected at startup. To support a new platform, you write new adapters — the application code doesn't change.\n\n```\n  Application Code  →  GroveContext (interfaces)\n                              │\n                    ┌─────────┼─────────┐\n                    ▼         ▼         ▼\n              Cloudflare   Node.js   Testing\n              adapters    adapters    mocks\n```\n\n## Related\n\n- **Spec:** `docs/specs/server-sdk-spec.md`\n- **Agent guide:** `AgentUsage/server_sdk_guide.md`\n- **Error handling:** `AgentUsage/error_handling.md`\n- **Loom SDK:** `packages/engine/src/lib/loom/` (Durable Object coordination, integrates with Infra SDK)\n","readmeFilename":"README.md"}