Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
|
|
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
|
|
|
|
/**
|
|
* Minimal concrete runtime: records requests, "executes" by invoking every
|
|
* binding once in declaration order, and lets tests script the outcome. The
|
|
* Service Definition package ships no provider, so the contract is exercised through
|
|
* the smallest subclass that honors it.
|
|
*/
|
|
class StubRuntime extends CodeRuntime {
|
|
readonly language = 'typescript'
|
|
readonly isolation = 'in-process-stub'
|
|
requests: CodeRunRequest[] = []
|
|
nextResult: CodeRunResult = { logs: [] }
|
|
|
|
async run(request: CodeRunRequest): Promise<CodeRunResult> {
|
|
this.requests.push(request)
|
|
if (request.signal?.aborted) {
|
|
return { logs: [], error: { kind: 'abort', message: String(request.signal.reason) } }
|
|
}
|
|
for (const namespace of request.bindings) {
|
|
for (const fn of Object.values(namespace.functions)) {
|
|
await fn({ from: 'stub' })
|
|
}
|
|
}
|
|
return this.nextResult
|
|
}
|
|
}
|
|
|
|
async function setup() {
|
|
const ctx = new Context()
|
|
await ctx.plugin(StubRuntime)
|
|
const runtime = ctx.codeRuntime as StubRuntime
|
|
return { ctx, runtime }
|
|
}
|
|
|
|
describe('CodeRuntime service seam', () => {
|
|
it('registers as ctx.codeRuntime and serves the abstract API', async () => {
|
|
const { runtime } = await setup()
|
|
expect(runtime.language).toBe('typescript')
|
|
expect(runtime.isolation).toBe('in-process-stub')
|
|
|
|
const calls: unknown[] = []
|
|
const result = await runtime.run({
|
|
program: 'return 1',
|
|
bindings: [{ global: 'tools', functions: { probe: async (args) => { calls.push(args); return null } } }],
|
|
})
|
|
expect(result).toEqual({ logs: [] })
|
|
expect(calls).toEqual([{ from: 'stub' }])
|
|
expect(runtime.requests).toHaveLength(1)
|
|
})
|
|
|
|
it('reports a failed run as an error field on a resolved result, never a rejection', async () => {
|
|
const { runtime } = await setup()
|
|
runtime.nextResult = {
|
|
logs: ['boom'],
|
|
error: { kind: 'exception', message: 'boom' },
|
|
}
|
|
const result = await runtime.run({ program: 'throw new Error("boom")', bindings: [] })
|
|
expect(result.error).toEqual({ kind: 'exception', message: 'boom' })
|
|
expect(result.value).toBeUndefined()
|
|
})
|
|
|
|
it('reports a pre-aborted signal as an abort failure', async () => {
|
|
const { runtime } = await setup()
|
|
const controller = new AbortController()
|
|
controller.abort('cancelled')
|
|
const result = await runtime.run({ program: 'return 1', bindings: [], signal: controller.signal })
|
|
expect(result.error).toEqual({ kind: 'abort', message: 'cancelled' })
|
|
})
|
|
|
|
it('is removed from the context when the providing fiber disposes (HMR safety)', async () => {
|
|
const ctx = new Context()
|
|
const fiber = await ctx.plugin(StubRuntime)
|
|
expect(ctx.get('codeRuntime')).toBeInstanceOf(StubRuntime)
|
|
|
|
await fiber.dispose()
|
|
expect(ctx.get('codeRuntime')).toBeUndefined()
|
|
})
|
|
|
|
it('rejects a second implementation in the same context (duplicate service)', async () => {
|
|
const { ctx } = await setup()
|
|
await expect(ctx.plugin(StubRuntime)).rejects.toThrow(/registered/)
|
|
})
|
|
|
|
})
|