chore: consolidate new foundation and archive v1 (#1495)
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import type { StorageConfig } from '@mosaicstack/storage';
|
||||
import type { QueueAdapterConfig as QueueConfig } from '@mosaicstack/queue';
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Types */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export type StorageTier = 'local' | 'standalone' | 'federated';
|
||||
|
||||
export interface MemoryConfigRef {
|
||||
type: 'pgvector' | 'sqlite-vec' | 'keyword';
|
||||
}
|
||||
|
||||
export interface MosaicConfig {
|
||||
tier: StorageTier;
|
||||
storage: StorageConfig;
|
||||
queue: QueueConfig;
|
||||
memory: MemoryConfigRef;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Defaults */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export const DEFAULT_LOCAL_CONFIG: MosaicConfig = {
|
||||
tier: 'local',
|
||||
storage: { type: 'pglite', dataDir: '.mosaic/storage-pglite' },
|
||||
queue: { type: 'local', dataDir: '.mosaic/queue' },
|
||||
memory: { type: 'keyword' },
|
||||
};
|
||||
|
||||
export const DEFAULT_STANDALONE_CONFIG: MosaicConfig = {
|
||||
tier: 'standalone',
|
||||
storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' },
|
||||
queue: { type: 'bullmq' },
|
||||
memory: { type: 'keyword' },
|
||||
};
|
||||
|
||||
export const DEFAULT_FEDERATED_CONFIG: MosaicConfig = {
|
||||
tier: 'federated',
|
||||
storage: {
|
||||
type: 'postgres',
|
||||
url: 'postgresql://mosaic:mosaic@localhost:5433/mosaic',
|
||||
enableVector: true,
|
||||
},
|
||||
queue: { type: 'bullmq' },
|
||||
memory: { type: 'pgvector' },
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Validation */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
const VALID_TIERS = new Set<string>(['local', 'standalone', 'federated']);
|
||||
const VALID_STORAGE_TYPES = new Set<string>(['postgres', 'pglite', 'files']);
|
||||
const VALID_QUEUE_TYPES = new Set<string>(['bullmq', 'local']);
|
||||
const VALID_MEMORY_TYPES = new Set<string>(['pgvector', 'sqlite-vec', 'keyword']);
|
||||
|
||||
export function validateConfig(raw: unknown): MosaicConfig {
|
||||
if (typeof raw !== 'object' || raw === null) {
|
||||
throw new Error('MosaicConfig must be a non-null object');
|
||||
}
|
||||
|
||||
const obj = raw as Record<string, unknown>;
|
||||
|
||||
// tier
|
||||
let tier = obj['tier'];
|
||||
// Deprecated alias: 'team' → 'standalone' (kept for backward-compat with 0.0.x installs)
|
||||
if (tier === 'team') {
|
||||
process.stderr.write(
|
||||
'[mosaic] DEPRECATED: tier="team" is deprecated — use "standalone" instead. ' +
|
||||
'Update your mosaic.config.json.\n',
|
||||
);
|
||||
tier = 'standalone';
|
||||
}
|
||||
if (typeof tier !== 'string' || !VALID_TIERS.has(tier)) {
|
||||
throw new Error(
|
||||
`Invalid tier "${String(tier)}" — expected "local", "standalone", or "federated"`,
|
||||
);
|
||||
}
|
||||
|
||||
// storage
|
||||
const storage = obj['storage'];
|
||||
if (typeof storage !== 'object' || storage === null) {
|
||||
throw new Error('config.storage must be a non-null object');
|
||||
}
|
||||
const storageType = (storage as Record<string, unknown>)['type'];
|
||||
if (typeof storageType !== 'string' || !VALID_STORAGE_TYPES.has(storageType)) {
|
||||
throw new Error(`Invalid storage.type "${String(storageType)}"`);
|
||||
}
|
||||
|
||||
// queue
|
||||
const queue = obj['queue'];
|
||||
if (typeof queue !== 'object' || queue === null) {
|
||||
throw new Error('config.queue must be a non-null object');
|
||||
}
|
||||
const queueType = (queue as Record<string, unknown>)['type'];
|
||||
if (typeof queueType !== 'string' || !VALID_QUEUE_TYPES.has(queueType)) {
|
||||
throw new Error(`Invalid queue.type "${String(queueType)}"`);
|
||||
}
|
||||
|
||||
// memory
|
||||
const memory = obj['memory'];
|
||||
if (typeof memory !== 'object' || memory === null) {
|
||||
throw new Error('config.memory must be a non-null object');
|
||||
}
|
||||
const memoryType = (memory as Record<string, unknown>)['type'];
|
||||
if (typeof memoryType !== 'string' || !VALID_MEMORY_TYPES.has(memoryType)) {
|
||||
throw new Error(`Invalid memory.type "${String(memoryType)}"`);
|
||||
}
|
||||
|
||||
return {
|
||||
tier: tier as StorageTier,
|
||||
storage: storage as StorageConfig,
|
||||
queue: queue as QueueConfig,
|
||||
memory: memory as MemoryConfigRef,
|
||||
};
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Loader */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export function detectFromEnv(): MosaicConfig {
|
||||
const tier = process.env['MOSAIC_STORAGE_TIER'];
|
||||
|
||||
if (tier === 'federated') {
|
||||
if (process.env['DATABASE_URL']) {
|
||||
return {
|
||||
...DEFAULT_FEDERATED_CONFIG,
|
||||
storage: {
|
||||
type: 'postgres',
|
||||
url: process.env['DATABASE_URL'],
|
||||
enableVector: true,
|
||||
},
|
||||
queue: {
|
||||
type: 'bullmq',
|
||||
url: process.env['VALKEY_URL'],
|
||||
},
|
||||
};
|
||||
}
|
||||
// MOSAIC_STORAGE_TIER=federated without DATABASE_URL — use the default
|
||||
// federated config (port 5433, enableVector: true, pgvector memory).
|
||||
return DEFAULT_FEDERATED_CONFIG;
|
||||
}
|
||||
|
||||
if (tier === 'standalone') {
|
||||
if (process.env['DATABASE_URL']) {
|
||||
return {
|
||||
...DEFAULT_STANDALONE_CONFIG,
|
||||
storage: {
|
||||
type: 'postgres',
|
||||
url: process.env['DATABASE_URL'],
|
||||
},
|
||||
queue: {
|
||||
type: 'bullmq',
|
||||
url: process.env['VALKEY_URL'],
|
||||
},
|
||||
};
|
||||
}
|
||||
// MOSAIC_STORAGE_TIER=standalone without DATABASE_URL — use the default
|
||||
// standalone config instead of silently falling back to local.
|
||||
return DEFAULT_STANDALONE_CONFIG;
|
||||
}
|
||||
|
||||
// Legacy: DATABASE_URL set without MOSAIC_STORAGE_TIER — treat as standalone.
|
||||
if (process.env['DATABASE_URL']) {
|
||||
return {
|
||||
...DEFAULT_STANDALONE_CONFIG,
|
||||
storage: {
|
||||
type: 'postgres',
|
||||
url: process.env['DATABASE_URL'],
|
||||
},
|
||||
queue: {
|
||||
type: 'bullmq',
|
||||
url: process.env['VALKEY_URL'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_LOCAL_CONFIG;
|
||||
}
|
||||
|
||||
export function loadConfig(configPath?: string): MosaicConfig {
|
||||
// 1. Explicit path or default location
|
||||
const paths = configPath
|
||||
? [resolve(configPath)]
|
||||
: [
|
||||
resolve(process.cwd(), 'mosaic.config.json'),
|
||||
resolve(process.cwd(), '../../mosaic.config.json'), // monorepo root when cwd is apps/gateway
|
||||
];
|
||||
|
||||
for (const p of paths) {
|
||||
if (existsSync(p)) {
|
||||
const raw: unknown = JSON.parse(readFileSync(p, 'utf-8'));
|
||||
return validateConfig(raw);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fall back to env-var detection
|
||||
return detectFromEnv();
|
||||
}
|
||||
Reference in New Issue
Block a user