feat(cosmos): add @bytelyst/cosmos package

- Singleton CosmosClient with env var config (COSMOS_ENDPOINT, COSMOS_KEY, COSMOS_DATABASE)
- Simple getContainer() for services
- Container registry with registerContainers(), getRegisteredContainer(), initializeAllContainers() for dashboards
- ContainerConfig type with partitionKeyPath and optional defaultTtl
- _resetClient() and _resetRegistry() for test isolation
- Peer dep: @azure/cosmos >=4.0.0
This commit is contained in:
saravanakumardb1 2026-02-12 11:19:42 -08:00
parent 9c0ab36171
commit 2e9dcf49a8
6 changed files with 167 additions and 0 deletions

View File

@ -0,0 +1,21 @@
{
"name": "@bytelyst/cosmos",
"version": "0.1.0",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "tsc",
"test": "vitest run"
},
"peerDependencies": {
"@azure/cosmos": ">=4.0.0"
}
}

View File

@ -0,0 +1,54 @@
/**
* Azure Cosmos DB client singleton.
*
* Reads COSMOS_ENDPOINT, COSMOS_KEY, and COSMOS_DATABASE from process.env.
* Provides getCosmosClient(), getDatabase(), and getContainer() for simple usage.
*/
import { CosmosClient, Database, Container } from "@azure/cosmos";
let _client: CosmosClient | null = null;
let _database: Database | null = null;
function getEnvOrThrow(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Environment variable ${name} is required`);
}
return value;
}
export function getCosmosClient(): CosmosClient {
if (!_client) {
_client = new CosmosClient({
endpoint: getEnvOrThrow("COSMOS_ENDPOINT"),
key: getEnvOrThrow("COSMOS_KEY"),
});
}
return _client;
}
export function getDatabase(): Database {
if (!_database) {
const dbId = process.env.COSMOS_DATABASE || "lysnrai";
_database = getCosmosClient().database(dbId);
}
return _database;
}
/**
* Get a container by name. Uses the singleton database.
* For simple services that don't need a container registry.
*/
export function getContainer(name: string): Container {
return getDatabase().container(name);
}
/**
* Reset the singleton (useful for testing).
* @internal
*/
export function _resetClient(): void {
_client = null;
_database = null;
}

View File

@ -0,0 +1,71 @@
/**
* Container registry for dashboards that need partition key validation
* and createIfNotExists support.
*/
import { Container, PartitionKeyDefinition } from "@azure/cosmos";
import { getDatabase, getCosmosClient } from "./client.js";
import type { ContainerConfig } from "./types.js";
const _registry: Map<string, ContainerConfig> = new Map();
const _containerCache: Map<string, Container> = new Map();
/**
* Register containers with their partition key configuration.
* Call once at app startup before any getRegisteredContainer() calls.
*/
export function registerContainers(
definitions: Record<string, ContainerConfig>,
): void {
for (const [name, config] of Object.entries(definitions)) {
_registry.set(name, config);
}
}
/**
* Get a container that was previously registered.
* Throws if the container name is unknown.
*/
export function getRegisteredContainer(name: string): Container {
if (!_registry.has(name)) {
throw new Error(
`Unknown container '${name}'. Valid: ${[..._registry.keys()].join(", ")}`,
);
}
let container = _containerCache.get(name);
if (!container) {
container = getDatabase().container(name);
_containerCache.set(name, container);
}
return container;
}
/**
* Create all registered containers if they don't exist.
* Call from a seed script or on first deploy.
*/
export async function initializeAllContainers(): Promise<void> {
const client = getCosmosClient();
const dbId = process.env.COSMOS_DATABASE || "lysnrai";
const { database } = await client.databases.createIfNotExists({ id: dbId });
for (const [name, config] of _registry.entries()) {
await database.containers.createIfNotExists({
id: name,
partitionKey: {
paths: [config.partitionKeyPath],
} as PartitionKeyDefinition,
...(config.defaultTtl != null && { defaultTtl: config.defaultTtl }),
});
}
}
/**
* Reset the registry (useful for testing).
* @internal
*/
export function _resetRegistry(): void {
_registry.clear();
_containerCache.clear();
}

View File

@ -0,0 +1,8 @@
export { getCosmosClient, getDatabase, getContainer, _resetClient } from "./client.js";
export {
registerContainers,
getRegisteredContainer,
initializeAllContainers,
_resetRegistry,
} from "./containers.js";
export type { ContainerConfig } from "./types.js";

View File

@ -0,0 +1,4 @@
export interface ContainerConfig {
partitionKeyPath: string;
defaultTtl?: number | null;
}

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}