From c4d28cc30b4e1c9f32306b8375ada55cdb0ffdad Mon Sep 17 00:00:00 2001 From: KeKs0r Date: Fri, 29 May 2026 20:49:19 +0300 Subject: [PATCH 1/2] =?UTF-8?q?Fix=20ObsessionDB=20plugin=20for=20serviceI?= =?UTF-8?q?d=20=E2=86=92=20serviceSlug=20contract=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ObsessionDB platform moved every service-scoped RPC input from serviceId to serviceSlug, which broke migrate/generate/status/drift/ check/query and remote backfill against ObsessionDB targets. Refresh the bundled oRPC contract copies to match the current platform schemas and route serviceSlug through all layers: service selection and storage now key on the slug, the remote executor sends serviceSlug to workbench.query.execute, and the backfill flag is renamed to --service-slug. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/obsessiondb-service-slug.md | 11 +++ .../src/test/obsessiondb-service.e2e.test.ts | 6 +- packages/plugin-obsessiondb/src/auth/login.ts | 2 +- .../src/backfill/handler.test.ts | 2 +- .../src/backfill/handler.ts | 11 ++- .../plugin-obsessiondb/src/backfill/index.ts | 4 +- .../plugin-obsessiondb/src/contract/jobs.ts | 95 +++++++++++++++---- .../src/contract/services.ts | 14 ++- .../src/contract/workbench.ts | 15 ++- packages/plugin-obsessiondb/src/index.test.ts | 2 +- packages/plugin-obsessiondb/src/index.ts | 4 +- .../src/query/remote-executor.ts | 12 +-- .../src/service/commands.test.ts | 6 +- .../src/service/commands.ts | 4 +- .../plugin-obsessiondb/src/service/select.ts | 2 +- .../plugin-obsessiondb/src/service/storage.ts | 14 +-- .../plugin-obsessiondb/src/service/types.ts | 2 +- .../src/test/service-commands.test.ts | 3 +- .../src/test/service-storage.test.ts | 16 ++-- 19 files changed, 158 insertions(+), 67 deletions(-) create mode 100644 .changeset/obsessiondb-service-slug.md diff --git a/.changeset/obsessiondb-service-slug.md b/.changeset/obsessiondb-service-slug.md new file mode 100644 index 0000000..cc00133 --- /dev/null +++ b/.changeset/obsessiondb-service-slug.md @@ -0,0 +1,11 @@ +--- +"chkit": patch +"@chkit/plugin-obsessiondb": patch +--- + +Fix broken ObsessionDB integration after the platform moved service-scoped RPC endpoints from `serviceId` to `serviceSlug`. The plugin now sources and stores the service slug instead of the id and sends `serviceSlug` to `workbench.query.execute`, `jobs.list`/`submit`, and `services.get`. This repairs `migrate`, `generate`, `status`, `drift`, `check`, `query`, and remote backfill against ObsessionDB targets. The bundled oRPC contract copies were also refreshed to match the current platform schemas (services now expose `slug`, query results are array-of-arrays, job details carry per-task runs). + +Notes: + +- The selected-service file (`obsessiondb.json`) now stores `service_slug` instead of `service_id`. Existing selections will need to be re-run with `chkit obsessiondb service select` (and any aliases re-set). +- The remote backfill flag `--service-id` is renamed to `--service-slug`. diff --git a/packages/cli/src/test/obsessiondb-service.e2e.test.ts b/packages/cli/src/test/obsessiondb-service.e2e.test.ts index fe09325..687e187 100644 --- a/packages/cli/src/test/obsessiondb-service.e2e.test.ts +++ b/packages/cli/src/test/obsessiondb-service.e2e.test.ts @@ -41,10 +41,12 @@ async function runCliAsync( function service(overrides: { id: string name: string + slug?: string status?: 'running' | 'stopped' }) { return { id: overrides.id, + slug: overrides.slug ?? overrides.name, name: overrides.name, status: overrides.status ?? 'running', tier: 1, @@ -186,10 +188,10 @@ describe('@chkit/cli obsessiondb service e2e', () => { expect(aliasList.stdout).toContain(' login -> production') const stored = JSON.parse(await readFile(servicePath, 'utf8')) as { - aliases?: Record + aliases?: Record } expect(stored.aliases?.login).toEqual({ - service_id: 'svc-prod', + service_slug: 'production', service_name: 'production', }) expect(requests.map((request) => request.path)).toEqual([ diff --git a/packages/plugin-obsessiondb/src/auth/login.ts b/packages/plugin-obsessiondb/src/auth/login.ts index 9781856..e7b1148 100644 --- a/packages/plugin-obsessiondb/src/auth/login.ts +++ b/packages/plugin-obsessiondb/src/auth/login.ts @@ -41,7 +41,7 @@ async function promptServiceSelection( const selected = await selectServiceInteractive(organizations, print) if (selected) { await saveSelectedService(configPath, { - service_id: selected.service.id, + service_slug: selected.service.slug, service_name: selected.service.name, }) print(`Service selected: ${serviceChoiceLabel(selected)}`) diff --git a/packages/plugin-obsessiondb/src/backfill/handler.test.ts b/packages/plugin-obsessiondb/src/backfill/handler.test.ts index a477c8d..3d25c61 100644 --- a/packages/plugin-obsessiondb/src/backfill/handler.test.ts +++ b/packages/plugin-obsessiondb/src/backfill/handler.test.ts @@ -167,7 +167,7 @@ describe('handleBackfillCommand', () => { globalThis.fetch = mock(async () => orpcResponse(listResponse)) as typeof fetch - const { context, printed } = makeContext({ command: 'list', flags: { '--service-id': 'svc-1' } }) + const { context, printed } = makeContext({ command: 'list', flags: { '--service-slug': 'svc-1' } }) const result = await handleBackfillCommand(context) expect(result).toEqual({ handled: true, exitCode: 0 }) diff --git a/packages/plugin-obsessiondb/src/backfill/handler.ts b/packages/plugin-obsessiondb/src/backfill/handler.ts index bdedf65..fbc255a 100644 --- a/packages/plugin-obsessiondb/src/backfill/handler.ts +++ b/packages/plugin-obsessiondb/src/backfill/handler.ts @@ -60,21 +60,22 @@ async function dispatchCommand( flags: Record, ): Promise { const jobId = typeof flags['--job-id'] === 'string' ? flags['--job-id'] : undefined - const serviceId = typeof flags['--service-id'] === 'string' ? flags['--service-id'] : undefined + const serviceSlug = + typeof flags['--service-slug'] === 'string' ? flags['--service-slug'] : undefined switch (command) { case 'status': { if (jobId) return client.get({ jobId }) - if (serviceId) return client.list({ serviceId }) - throw new Error('Either --job-id or --service-id is required for remote status') + if (serviceSlug) return client.list({ serviceSlug }) + throw new Error('Either --job-id or --service-slug is required for remote status') } case 'cancel': { if (!jobId) throw new Error('--job-id is required for remote cancel') return client.cancel({ jobId }) } case 'list': { - if (!serviceId) throw new Error('--service-id is required for remote list') - return client.list({ serviceId }) + if (!serviceSlug) throw new Error('--service-slug is required for remote list') + return client.list({ serviceSlug }) } default: throw new Error(`Unsupported remote command: ${command}`) diff --git a/packages/plugin-obsessiondb/src/backfill/index.ts b/packages/plugin-obsessiondb/src/backfill/index.ts index cfdb64d..cfe5df0 100644 --- a/packages/plugin-obsessiondb/src/backfill/index.ts +++ b/packages/plugin-obsessiondb/src/backfill/index.ts @@ -21,9 +21,9 @@ export const BACKFILL_EXTEND_COMMANDS = [ description: 'Remote job ID for status/cancel', }, { - name: '--service-id', + name: '--service-slug', type: 'string' as const, - description: 'ObsessionDB service ID for listing jobs', + description: 'ObsessionDB service slug for listing jobs', }, ], }, diff --git a/packages/plugin-obsessiondb/src/contract/jobs.ts b/packages/plugin-obsessiondb/src/contract/jobs.ts index 0c87f43..27ee202 100644 --- a/packages/plugin-obsessiondb/src/contract/jobs.ts +++ b/packages/plugin-obsessiondb/src/contract/jobs.ts @@ -5,32 +5,71 @@ import { oc } from '@orpc/contract' import { z } from 'zod' -const jobStatusSchema = z.enum(['pending', 'running', 'completed', 'failed', 'cancelled']) +const jobStatusSchema = z.enum([ + 'pending', + 'running', + 'draining', + 'paused', + 'completed', + 'failed', + 'cancelled', +]) -const taskStatusSchema = z.enum(['pending', 'running', 'done', 'failed']) +const runTypeSchema = z.enum(['insert', 'cleanup']) -const jobTaskSchema = z.object({ +const runStatusSchema = z.enum(['pending', 'running', 'done', 'failed']) + +const cleanupTypeSchema = z.enum(['drop_partition', 'delete_range']) + +const taskRunSchema = z.object({ id: z.string(), - taskIndex: z.number().int(), - status: taskStatusSchema, - sql: z.string(), + attempt: z.number().int(), + type: runTypeSchema, + status: runStatusSchema, queryId: z.string().nullable(), - estimatedBytes: z.number().nullable(), + sql: z.string().nullable(), + settings: z.record(z.string(), z.string()).nullable(), + mutationId: z.string().nullable(), + readRows: z.number().nullable(), + readBytes: z.number().nullable(), + readBytesCompressed: z.number().nullable(), writtenRows: z.number().nullable(), writtenBytes: z.number().nullable(), + writtenBytesCompressed: z.number().nullable(), durationMs: z.number().nullable(), error: z.string().nullable(), + node: z.number().int().nullable(), + executingNode: z.number().int().nullable(), + executingHost: z.string().nullable(), startedAt: z.string().datetime().nullable(), finishedAt: z.string().datetime().nullable(), }) +const jobTaskSchema = z.object({ + id: z.string(), + taskIndex: z.number().int(), + taskGroup: z.string().nullable(), + sql: z.string(), + settings: z.record(z.string(), z.string()).nullable(), + cleanupSql: z.string().nullable(), + cleanupType: cleanupTypeSchema.nullable(), + maxRetries: z.number().int(), + estimatedBytes: z.number().nullable(), + estimatedBytesUncompressed: z.number().nullable(), + runs: z.array(taskRunSchema), +}) + const jobSummarySchema = z.object({ id: z.string(), serviceId: z.string(), + title: z.string().nullable(), type: z.string(), target: z.string(), status: jobStatusSchema, concurrency: z.number().int(), + taskLimit: z.number().int().nullable(), + pollIntervalBaseSec: z.number().int().nullable(), + pollIntervalMaxSec: z.number().int().nullable(), totalTasks: z.number().int(), completedTasks: z.number().int(), failedTasks: z.number().int(), @@ -39,8 +78,9 @@ const jobSummarySchema = z.object({ }) const jobDetailSchema = jobSummarySchema.extend({ + maxRetries: z.number().int(), workflowId: z.string().nullable(), - metadata: z.record(z.unknown()).nullable(), + metadata: z.record(z.string(), z.unknown()).nullable(), tasks: z.array(jobTaskSchema), }) @@ -48,18 +88,28 @@ export const jobsContract = { submit: oc .input( z.object({ - serviceId: z.string(), + serviceSlug: z.string(), + title: z.string().max(200).optional(), type: z.enum(['backfill']), target: z.string(), - concurrency: z.number().int().min(1).max(12).optional(), - tasks: z.array( - z.object({ - id: z.string(), - sql: z.string(), - estimatedBytes: z.number().optional(), - }), - ), - metadata: z.record(z.unknown()).optional(), + concurrency: z.number().int().min(0).max(48).optional(), + taskLimit: z.number().int().min(1).optional(), + tasks: z + .array( + z.object({ + id: z.string(), + sql: z.string(), + settings: z.record(z.string(), z.string()).optional(), + group: z.string().optional(), + estimatedBytes: z.number().optional(), + estimatedBytesUncompressed: z.number().optional(), + cleanupSql: z.string().optional(), + cleanupType: cleanupTypeSchema.optional(), + maxRetries: z.number().int().min(0).max(10).optional(), + }), + ) + .min(1), + metadata: z.record(z.string(), z.unknown()).optional(), }), ) .output(z.object({ jobId: z.string() })), @@ -69,8 +119,13 @@ export const jobsContract = { .output(jobDetailSchema), list: oc - .input(z.object({ serviceId: z.string() })) - .output(z.object({ jobs: z.array(jobSummarySchema) })), + .input(z.object({ serviceSlug: z.string() })) + .output( + z.object({ + jobs: z.array(jobSummarySchema), + total: z.number().int().min(0), + }), + ), cancel: oc .input(z.object({ jobId: z.string() })) diff --git a/packages/plugin-obsessiondb/src/contract/services.ts b/packages/plugin-obsessiondb/src/contract/services.ts index 7d26f38..29776ad 100644 --- a/packages/plugin-obsessiondb/src/contract/services.ts +++ b/packages/plugin-obsessiondb/src/contract/services.ts @@ -17,8 +17,20 @@ const serviceStatusSchema = z.enum([ 'error', ]) +const RESERVED_SLUGS = ['new', 'settings', 'select', 'members', 'profile'] as const + +export const serviceSlugSchema = z + .string() + .min(2) + .max(64) + .regex(/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$/) + .refine((s) => !RESERVED_SLUGS.includes(s as (typeof RESERVED_SLUGS)[number]), { + message: 'Reserved slug', + }) + export const serviceSchema = z.object({ id: z.string(), + slug: serviceSlugSchema, name: z.string(), status: serviceStatusSchema, tier: z.number().int(), @@ -51,6 +63,6 @@ export const servicesContract = { ), get: oc - .input(z.object({ serviceId: z.string() })) + .input(z.object({ serviceSlug: serviceSlugSchema })) .output(serviceSchema), } diff --git a/packages/plugin-obsessiondb/src/contract/workbench.ts b/packages/plugin-obsessiondb/src/contract/workbench.ts index 391b41c..226d1fb 100644 --- a/packages/plugin-obsessiondb/src/contract/workbench.ts +++ b/packages/plugin-obsessiondb/src/contract/workbench.ts @@ -5,9 +5,14 @@ import { oc } from '@orpc/contract' import { z } from 'zod' +const queryColumnMetaSchema = z.object({ + name: z.string(), + type: z.string(), +}) + const queryResultSchema = z.object({ - data: z.array(z.record(z.unknown())), - meta: z.array(z.object({ name: z.string(), type: z.string() })), + data: z.array(z.array(z.string().nullable())), + meta: z.array(queryColumnMetaSchema), rows: z.number().int(), statistics: z .object({ @@ -26,9 +31,11 @@ export const workbenchContract = { execute: oc .input( z.object({ - serviceId: z.string(), + serviceSlug: z.string(), query: z.string().min(1), - settings: z.record(z.union([z.string(), z.number()])).optional(), + settings: z + .record(z.string(), z.union([z.string(), z.number()])) + .optional(), database: z.string().optional(), }), ) diff --git a/packages/plugin-obsessiondb/src/index.test.ts b/packages/plugin-obsessiondb/src/index.test.ts index c803e86..31065c8 100644 --- a/packages/plugin-obsessiondb/src/index.test.ts +++ b/packages/plugin-obsessiondb/src/index.test.ts @@ -492,7 +492,7 @@ describe('obsessiondb getContext', () => { await setupAuth() const configPath = join(tempDir, 'project', 'clickhouse.config.ts') await saveServiceAlias(configPath, 'prod', { - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) diff --git a/packages/plugin-obsessiondb/src/index.ts b/packages/plugin-obsessiondb/src/index.ts index 8bf8964..94ed29a 100644 --- a/packages/plugin-obsessiondb/src/index.ts +++ b/packages/plugin-obsessiondb/src/index.ts @@ -140,7 +140,7 @@ async function resolveServiceOverride(input: { const services = await listServices(input.credentials) const service = services.find((candidate) => candidate.name === input.name) if (service) { - return { service_id: service.id, service_name: service.name } + return { service_slug: service.slug, service_name: service.name } } const aliases = await loadServiceAliases(input.configPath) @@ -276,7 +276,7 @@ function createObsessionDBPlugin( return { executor: createRemoteExecutor({ credentials: effectiveCreds, - serviceId: service.service_id, + serviceSlug: service.service_slug, }), } }, diff --git a/packages/plugin-obsessiondb/src/query/remote-executor.ts b/packages/plugin-obsessiondb/src/query/remote-executor.ts index 24b7583..9ef4c52 100644 --- a/packages/plugin-obsessiondb/src/query/remote-executor.ts +++ b/packages/plugin-obsessiondb/src/query/remote-executor.ts @@ -49,15 +49,15 @@ export function normalizeQueryJsonResult>( export function createRemoteExecutor(deps: { credentials: Credentials - serviceId: string + serviceSlug: string }): ClickHouseExecutor { - const { credentials, serviceId } = deps + const { credentials, serviceSlug } = deps const client = createApiClient(credentials) const executor: ClickHouseExecutor = { async command(sql) { const res = await client.workbench.query.execute({ - serviceId, + serviceSlug, query: sql, }) throwIfError(res) @@ -65,7 +65,7 @@ export function createRemoteExecutor(deps: { async query(sql: string): Promise { const res = await client.workbench.query.execute({ - serviceId, + serviceSlug, query: sql, }) throwIfError(res) @@ -76,7 +76,7 @@ export function createRemoteExecutor(deps: { sql: string, ): Promise> { const res = await client.workbench.query.execute({ - serviceId, + serviceSlug, query: sql, }) throwIfError(res) @@ -112,7 +112,7 @@ export function createRemoteExecutor(deps: { async submit(sql, queryId?) { const res = await client.workbench.query.execute({ - serviceId, + serviceSlug, query: sql, settings: queryId ? { query_id: queryId } : undefined, }) diff --git a/packages/plugin-obsessiondb/src/service/commands.test.ts b/packages/plugin-obsessiondb/src/service/commands.test.ts index 112076a..1b9275b 100644 --- a/packages/plugin-obsessiondb/src/service/commands.test.ts +++ b/packages/plugin-obsessiondb/src/service/commands.test.ts @@ -14,10 +14,12 @@ let originalXdg: string | undefined function service(overrides: { id: string name: string + slug?: string status?: 'running' | 'stopped' }) { return { id: overrides.id, + slug: overrides.slug ?? overrides.name, name: overrides.name, status: overrides.status ?? 'running', tier: 1, @@ -70,7 +72,7 @@ describe('service command', () => { test('lists services grouped by organization and marks the default', async () => { const configPath = await setupAuth() await saveSelectedService(configPath, { - service_id: 'svc-prod', + service_slug: 'prod', service_name: 'prod', }) mockOrganizations([ @@ -135,7 +137,7 @@ describe('service command', () => { 'Service selected: Acme (acme) / prod', ]) expect(await loadSelectedService(configPath)).toEqual({ - service_id: 'svc-prod', + service_slug: 'prod', service_name: 'prod', }) }) diff --git a/packages/plugin-obsessiondb/src/service/commands.ts b/packages/plugin-obsessiondb/src/service/commands.ts index 22e1b0d..5fcd33c 100644 --- a/packages/plugin-obsessiondb/src/service/commands.ts +++ b/packages/plugin-obsessiondb/src/service/commands.ts @@ -52,7 +52,7 @@ async function runServiceSelect( if (!selected) return 1 await saveSelectedService(context.configPath, { - service_id: selected.service.id, + service_slug: selected.service.slug, service_name: selected.service.name, }) @@ -226,7 +226,7 @@ async function runServiceAlias(context: PluginCommandContext): Promise { } await saveServiceAlias(context.configPath, alias, { - service_id: service.id, + service_slug: service.slug, service_name: service.name, }) print(`Service alias saved: ${alias} -> ${service.name}`) diff --git a/packages/plugin-obsessiondb/src/service/select.ts b/packages/plugin-obsessiondb/src/service/select.ts index 5e75cbd..a62e054 100644 --- a/packages/plugin-obsessiondb/src/service/select.ts +++ b/packages/plugin-obsessiondb/src/service/select.ts @@ -36,7 +36,7 @@ export function renderServiceOrganizations( lines.push(`${organizationLabel(org)}:`) for (const service of org.services) { const suffix = - selected?.service_id === service.id || + selected?.service_slug === service.slug || selected?.service_name === service.name ? ' [default]' : '' diff --git a/packages/plugin-obsessiondb/src/service/storage.ts b/packages/plugin-obsessiondb/src/service/storage.ts index 98c2bbb..11494dd 100644 --- a/packages/plugin-obsessiondb/src/service/storage.ts +++ b/packages/plugin-obsessiondb/src/service/storage.ts @@ -8,7 +8,7 @@ import { isSynthesizedConfigPath } from '@chkit/core' import type { SelectedService, ServiceAliases } from './types.js' interface ServiceConfig { - service_id?: string + service_slug?: string service_name?: string aliases?: ServiceAliases } @@ -44,13 +44,13 @@ function readSelectedService(parsed: unknown): SelectedService | null { if ( typeof parsed === 'object' && parsed !== null && - 'service_id' in parsed && + 'service_slug' in parsed && 'service_name' in parsed && - typeof (parsed as SelectedService).service_id === 'string' && + typeof (parsed as SelectedService).service_slug === 'string' && typeof (parsed as SelectedService).service_name === 'string' ) { return { - service_id: (parsed as SelectedService).service_id, + service_slug: (parsed as SelectedService).service_slug, service_name: (parsed as SelectedService).service_name, } } @@ -107,13 +107,13 @@ export async function loadSelectedService( ): Promise { const config = await loadServiceConfig(configPath) if ( - typeof config.service_id !== 'string' || + typeof config.service_slug !== 'string' || typeof config.service_name !== 'string' ) { return null } return { - service_id: config.service_id, + service_slug: config.service_slug, service_name: config.service_name, } } @@ -125,7 +125,7 @@ export async function saveSelectedService( const existing = await loadServiceConfig(configPath) await saveServiceConfig(configPath, { ...existing, - service_id: service.service_id, + service_slug: service.service_slug, service_name: service.service_name, }) } diff --git a/packages/plugin-obsessiondb/src/service/types.ts b/packages/plugin-obsessiondb/src/service/types.ts index 94afd3a..339f2c5 100644 --- a/packages/plugin-obsessiondb/src/service/types.ts +++ b/packages/plugin-obsessiondb/src/service/types.ts @@ -11,7 +11,7 @@ export interface ServiceOrganization { } export interface SelectedService { - service_id: string + service_slug: string service_name: string } diff --git a/packages/plugin-obsessiondb/src/test/service-commands.test.ts b/packages/plugin-obsessiondb/src/test/service-commands.test.ts index 5892798..5cded4f 100644 --- a/packages/plugin-obsessiondb/src/test/service-commands.test.ts +++ b/packages/plugin-obsessiondb/src/test/service-commands.test.ts @@ -52,6 +52,7 @@ describe('service alias command', () => { services: [ { id: 'svc-prod', + slug: 'production', name: 'production', status: 'running', tier: 1, @@ -84,7 +85,7 @@ describe('service alias command', () => { expect(printed).toContain('Service alias saved: prod -> production') expect(await loadServiceAliases(configPath)).toEqual({ prod: { - service_id: 'svc-prod', + service_slug: 'production', service_name: 'production', }, }) diff --git a/packages/plugin-obsessiondb/src/test/service-storage.test.ts b/packages/plugin-obsessiondb/src/test/service-storage.test.ts index b139a0c..73d63f0 100644 --- a/packages/plugin-obsessiondb/src/test/service-storage.test.ts +++ b/packages/plugin-obsessiondb/src/test/service-storage.test.ts @@ -85,27 +85,27 @@ describe('service aliases', () => { const configPath = join(sandbox.project, 'clickhouse.config.ts') try { await saveSelectedService(configPath, { - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) await saveServiceAlias(configPath, 'prod', { - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) const aliases = await loadServiceAliases(configPath) expect(aliases.prod).toEqual({ - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) const raw = JSON.parse( await readFile(getServicePath(configPath), 'utf8'), ) as { - service_id: string + service_slug: string aliases: Record } - expect(raw.service_id).toBe('svc-prod') + expect(raw.service_slug).toBe('svc-prod') expect(raw.aliases.prod).toBeDefined() } finally { sandbox.cleanup() @@ -117,17 +117,17 @@ describe('service aliases', () => { const configPath = join(sandbox.project, 'clickhouse.config.ts') try { await saveServiceAlias(configPath, 'prod', { - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) await saveSelectedService(configPath, { - service_id: 'svc-staging', + service_slug: 'svc-staging', service_name: 'staging', }) const aliases = await loadServiceAliases(configPath) expect(aliases.prod).toEqual({ - service_id: 'svc-prod', + service_slug: 'svc-prod', service_name: 'production', }) } finally { From 32657e94081e30f57bb3bf1791f1538c1f4acdd5 Mon Sep 17 00:00:00 2001 From: KeKs0r Date: Sat, 30 May 2026 00:32:14 +0300 Subject: [PATCH 2/2] Fix fallow dead export alert --- packages/plugin-obsessiondb/src/contract/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-obsessiondb/src/contract/services.ts b/packages/plugin-obsessiondb/src/contract/services.ts index 29776ad..dbb993d 100644 --- a/packages/plugin-obsessiondb/src/contract/services.ts +++ b/packages/plugin-obsessiondb/src/contract/services.ts @@ -19,7 +19,7 @@ const serviceStatusSchema = z.enum([ const RESERVED_SLUGS = ['new', 'settings', 'select', 'members', 'profile'] as const -export const serviceSlugSchema = z +const serviceSlugSchema = z .string() .min(2) .max(64)