@doable/shared¶
Cross-cutting types, constants, AI provider catalog, the KV-store abstraction, and a small security helper. Lives at packages/shared/. Both services/api and apps/web import from it; if a value or type is used in both, it belongs here.
What's inside¶
| File / dir | Purpose |
|---|---|
index.ts |
Re-exports everything below |
constants.ts |
Plan + role metadata, PLAN_LIMITS, ordering helpers (compareRoles, comparePlans) |
kv-store.ts |
KVStore interface + in-memory and Redis implementations, picked by REDIS_URL |
types/index.ts |
Core entity types (User, Workspace, WorkspaceMember, Project, AiSession, Credits, FeatureFlag, GitHubCopilotAccount, AiProvider, WorkspaceAiSettings, …) plus the WORKSPACE_PLANS / WORKSPACE_ROLES source-of-truth arrays |
types/api.ts |
API request and response shapes |
types/ai.ts |
ConversationMessage, StreamEvent, ToolCall, EngineOptions, Plan, ClarificationQuestion |
types/mcp.ts |
MCP connector and tool types shared by api + web |
types/ws.ts |
WebSocket message envelope types |
ai/index.ts |
Re-exports the provider catalog and types |
ai/provider-types.ts |
ProviderPreset, AuthMethod, model and tag enums |
ai/provider-catalog.ts |
Single source of truth for the 60+ supported AI providers; surfaces PROVIDER_CATALOG, PROVIDER_COUNT |
ai/provider-data-cloud-major.ts |
OpenAI, Anthropic, Google, Azure, Bedrock, Vertex preset data |
ai/provider-data-cloud-regional.ts |
Moonshot, DashScope, Zhipu, MiniMax, Cerebras, Hyperbolic, … |
ai/provider-data-cloud-special.ts |
Aggregators (OpenRouter, Together, Fireworks) and infra providers |
ai/provider-data-local.ts |
Ollama, LM Studio, vLLM, llama.cpp, Jan, LocalAI, GPT4All, … |
security/env-perms-check.ts |
checkEnvFilePerms(): warns when .env is group or world readable in production |
KV store¶
import { getKVStore } from "@doable/shared";
const kv = getKVStore();
await kv.set("rate:user-42", 1, 60_000); // ttl in ms
const v = await kv.get<number>("rate:user-42");
const next = await kv.incr("counter:logins");
The interface (kv-store.ts) is get, set, delete, incr, close. Both implementations honour TTL.
- MemoryStore: a
Map<string, MemEntry>withsetIntervalsweep every 30 seconds. The interval is.unref()'d so it does not keep the process alive. - RedisStore: lazy-imports
ioredisonly whenREDIS_URLis set, JSON-encodes values, usesSET key value PX ttlfor expiry andINCR+PEXPIRE key ttlfor atomic counters.
The factory getKVStore() is the singleton entry point. The CLAUDE.md rule "No Redis required, in-memory KV works out of the box" relies on this: rate limiting, presence cache, OAuth state, and per-request idempotency keys all consume the same interface so swapping in Redis is one env var.
resetKVStore() exists for tests and clears the singleton.
Plans, roles, and limits¶
types/index.ts holds the source-of-truth arrays:
export const WORKSPACE_PLANS = ["free", "pro", "business", "enterprise"] as const;
export const WORKSPACE_ROLES = ["viewer", "member", "admin", "owner"] as const;
export const PLATFORM_ADMIN_ROLES = ["admin", "owner"] as const;
Adding a new plan or role means:
- Append to the array.
- Add a row to
PLAN_METAorROLE_METAinconstants.ts. - Run a Postgres migration (
ALTER TYPE workspace_plan ADD VALUE '<new>').
All Zod schemas, labels, dropdowns, role hierarchies, and plan-limit lookups derive from the array automatically. compareRoles(a, b) and comparePlans(a, b) return signed indices; use them instead of raw === comparisons.
PLAN_LIMITS (constants.ts) holds the shipping defaults that the platform admin can override per plan in /admin/plan-limits: maxProjects, maxMembers, dailyCredits, monthlyCredits, maxFileSize, customDomains, analytics, prioritySupport.
AI provider catalog¶
ai/provider-catalog.ts is the single source of truth for the 60+ supported AI providers shown in the setup wizard, in /ai-settings, and in the Docker setup.sh env-var sniffer. Each ProviderPreset carries:
id,name,description,tags(popular,regional,local, …)category(cloud-major,cloud-regional,cloud-special,local)authMethod(apiKey,oauth,nonefor local providers)defaultBaseUrl,baseUrlEditable,baseUrlTemplatedefaultModelsandapiKeyHelpUrl- Optional
warningsandfreeTierflag
PROVIDER_COUNT is a constant; the setup wizard renders it ("Show all N providers") so it stays in sync without manual count fixes.
Security helper¶
security/env-perms-check.ts exports checkEnvFilePerms(envPath?). When NODE_ENV=production and the .env file's mode lets group or other read (mask 0o044), it logs a [SECURITY] warning to stderr. It also warns when the file's owner UID differs from the process UID (perms drift). The check is non-fatal and is safe to call at API startup.
When to add to shared¶
Add a type or constant here when two or more packages in the monorepo need it. If only the API uses it, keep it in services/api. If only the frontend uses it, keep it in apps/web/src/lib. Do not import from services/api into apps/web directly; route the shared piece through this package.