ci: coverage
This commit is contained in:
@@ -50,12 +50,13 @@ describe('connection client apply', () => {
|
|||||||
const handle = await mount()
|
const handle = await mount()
|
||||||
const original = globalThis.fetch
|
const original = globalThis.fetch
|
||||||
const seen: string[] = []
|
const seen: string[] = []
|
||||||
globalThis.fetch = ((input: URL | RequestInfo) => {
|
globalThis.fetch = (input: URL | RequestInfo) => {
|
||||||
seen.push(String(input))
|
seen.push(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url)
|
||||||
return Promise.resolve(new Response('{}', { status: 200 }))
|
return Promise.resolve(new Response('{}', { status: 200 }))
|
||||||
}) as typeof fetch
|
}
|
||||||
try {
|
try {
|
||||||
await (handle.api as WebApiClient).host.describe({}).catch(() => undefined) // schema rejection is fine — the transport hop is the assertion
|
// Schema rejection is fine — the transport hop is the assertion.
|
||||||
|
await (handle.api as WebApiClient).host.describe({}).catch(() => undefined)
|
||||||
} finally {
|
} finally {
|
||||||
globalThis.fetch = original
|
globalThis.fetch = original
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,12 +55,16 @@ export class FakeApiClient implements IApiClient {
|
|||||||
private readonly muxConns: StreamConn<MuxFrame>[] = []
|
private readonly muxConns: StreamConn<MuxFrame>[] = []
|
||||||
private readonly hostConns: StreamConn<HostFrame>[] = []
|
private readonly hostConns: StreamConn<HostFrame>[] = []
|
||||||
|
|
||||||
|
// Parameter annotations below are local structural types on purpose: the CI
|
||||||
|
// lint lane runs without built artifacts, where IApiClient's wire types
|
||||||
|
// (apiproxy subpath) resolve to any and inferred params trip no-unsafe-argument.
|
||||||
readonly sessions: IApiClient['sessions'] = {
|
readonly sessions: IApiClient['sessions'] = {
|
||||||
list: payload => this.record('session.list', payload, this.onList(payload)),
|
list: (payload: unknown) => this.record('session.list', payload, this.onList(payload)),
|
||||||
create: payload => this.record('session.create', payload, this.onCreate(payload)),
|
create: (payload: unknown) => this.record('session.create', payload, this.onCreate(payload)),
|
||||||
history: payload => this.record('session.history', payload, this.onHistory(payload)),
|
history: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number }) =>
|
||||||
prompt: payload => this.record('session.prompt', payload, this.onPrompt(payload)),
|
this.record('session.history', payload, this.onHistory(payload)),
|
||||||
cancel: payload => this.record('session.cancel', payload, this.onCancel(payload)),
|
prompt: (payload: unknown) => this.record('session.prompt', payload, this.onPrompt(payload)),
|
||||||
|
cancel: (payload: unknown) => this.record('session.cancel', payload, this.onCancel(payload)),
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly host: IApiClient['host'] = {
|
readonly host: IApiClient['host'] = {
|
||||||
@@ -82,8 +86,10 @@ export class FakeApiClient implements IApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly events: IApiClient['events'] = {
|
readonly events: IApiClient['events'] = {
|
||||||
mux: (_payload, signal, onOpen) => this.openStream(this.muxConns, signal, onOpen),
|
mux: (_payload: unknown, signal: AbortSignal, onOpen?: () => void) =>
|
||||||
host: (_payload, signal, onOpen) => this.openStream(this.hostConns, signal, onOpen),
|
this.openStream(this.muxConns, signal, onOpen),
|
||||||
|
host: (_payload: unknown, signal: AbortSignal, onOpen?: () => void) =>
|
||||||
|
this.openStream(this.hostConns, signal, onOpen),
|
||||||
}
|
}
|
||||||
|
|
||||||
respond(): Promise<{ accepted: false; reason: 'not-pending' }> {
|
respond(): Promise<{ accepted: false; reason: 'not-pending' }> {
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
* baseline replay, timing hooks) — this is the vitest-side drift detector for
|
* baseline replay, timing hooks) — this is the vitest-side drift detector for
|
||||||
* the hand-written fixture/host parallel implementations.
|
* the hand-written fixture/host parallel implementations.
|
||||||
*/
|
*/
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment,
|
||||||
|
@typescript-eslint/no-unsafe-member-access,
|
||||||
|
@typescript-eslint/no-unsafe-call,
|
||||||
|
@typescript-eslint/no-unsafe-argument,
|
||||||
|
@typescript-eslint/no-unsafe-return --
|
||||||
|
* CI's no-build lint lane cannot resolve the wire types this suite drives
|
||||||
|
* (they arrive through apiproxy's lib/types exports, absent without a build),
|
||||||
|
* so every contract-typed expression collapses to `any` there while the same
|
||||||
|
* code lints clean locally (hence the locally-unused directive). The suite is
|
||||||
|
* exactly a traversal of that cross-package contract face (unary table,
|
||||||
|
* stream replay, envelope tap); typecheck runs with project references and
|
||||||
|
* keeps the real type safety. */
|
||||||
|
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
import type { SessionId } from '../src/client/api.ts'
|
import type { SessionId } from '../src/client/api.ts'
|
||||||
|
|||||||
@@ -238,11 +238,11 @@ describe('DOM default seams (stubbed globals)', () => {
|
|||||||
}
|
}
|
||||||
const g = globalThis as { document?: unknown; fetch: typeof fetch }
|
const g = globalThis as { document?: unknown; fetch: typeof fetch }
|
||||||
g.document = fakeDoc
|
g.document = fakeDoc
|
||||||
g.fetch = ((url: URL | RequestInfo) => Promise.resolve(
|
g.fetch = (url: URL | RequestInfo) => Promise.resolve(
|
||||||
String(url).includes('bad')
|
(typeof url === 'string' ? url : url instanceof URL ? url.href : url.url).includes('bad')
|
||||||
? new Response('x', { status: 500 })
|
? new Response('x', { status: 500 })
|
||||||
: new Response('window.DSHClientProxy.loadPlugin(globalThis.__seamHandoff)', { status: 200 }),
|
: new Response('window.DSHClientProxy.loadPlugin(globalThis.__seamHandoff)', { status: 200 }),
|
||||||
)) as typeof fetch
|
)
|
||||||
try {
|
try {
|
||||||
delete win.DSHClientProxy
|
delete win.DSHClientProxy
|
||||||
const ctx = new Context()
|
const ctx = new Context()
|
||||||
|
|||||||
@@ -58,16 +58,20 @@ export class FakeApiClient implements IApiClient {
|
|||||||
private readonly muxConns: StreamConn<MuxFrame>[] = []
|
private readonly muxConns: StreamConn<MuxFrame>[] = []
|
||||||
private readonly hostConns: StreamConn<HostFrame>[] = []
|
private readonly hostConns: StreamConn<HostFrame>[] = []
|
||||||
|
|
||||||
|
// Parameters carry local structural annotations: the CI lint lane runs
|
||||||
|
// without built lib/, so IApiClient's indexed-access types collapse to any
|
||||||
|
// and inferred parameters would trip no-unsafe-argument.
|
||||||
readonly sessions: IApiClient['sessions'] = {
|
readonly sessions: IApiClient['sessions'] = {
|
||||||
list: payload => this.record('session.list', payload, this.onList(payload)),
|
list: (payload: unknown) => this.record('session.list', payload, this.onList(payload)),
|
||||||
create: payload => this.record('session.create', payload, this.onCreate(payload)),
|
create: (payload: unknown) => this.record('session.create', payload, this.onCreate(payload)),
|
||||||
history: payload => this.record('session.history', payload, this.onHistory(payload)),
|
history: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number }) =>
|
||||||
prompt: payload => this.record('session.prompt', payload, this.onPrompt(payload)),
|
this.record('session.history', payload, this.onHistory(payload)),
|
||||||
cancel: payload => this.record('session.cancel', payload, this.onCancel(payload)),
|
prompt: (payload: unknown) => this.record('session.prompt', payload, this.onPrompt(payload)),
|
||||||
|
cancel: (payload: unknown) => this.record('session.cancel', payload, this.onCancel(payload)),
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly host: IApiClient['host'] = {
|
readonly host: IApiClient['host'] = {
|
||||||
describe: payload => this.record('host.describe', payload, this.onDescribe(payload)),
|
describe: (payload: unknown) => this.record('host.describe', payload, this.onDescribe(payload)),
|
||||||
}
|
}
|
||||||
|
|
||||||
/** When true, streams never fire onOpen (misbehaving-carrier material for the handshake timeout guard). */
|
/** When true, streams never fire onOpen (misbehaving-carrier material for the handshake timeout guard). */
|
||||||
@@ -85,8 +89,8 @@ export class FakeApiClient implements IApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly events: IApiClient['events'] = {
|
readonly events: IApiClient['events'] = {
|
||||||
mux: (_payload, signal, onOpen) => this.openStream(this.muxConns, signal, onOpen),
|
mux: (_payload: unknown, signal: AbortSignal, onOpen?: () => void) => this.openStream(this.muxConns, signal, onOpen),
|
||||||
host: (_payload, signal, onOpen) => this.openStream(this.hostConns, signal, onOpen),
|
host: (_payload: unknown, signal: AbortSignal, onOpen?: () => void) => this.openStream(this.hostConns, signal, onOpen),
|
||||||
}
|
}
|
||||||
|
|
||||||
respond(): Promise<{ accepted: false; reason: 'not-pending' }> {
|
respond(): Promise<{ accepted: false; reason: 'not-pending' }> {
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ describe('node half + invariant companion', () => {
|
|||||||
it('invariant companion registers under the package name', async () => {
|
it('invariant companion registers under the package name', async () => {
|
||||||
const register = vi.fn().mockReturnValue(() => {})
|
const register = vi.fn().mockReturnValue(() => {})
|
||||||
const ctx = { invariants: { register } } as never
|
const ctx = { invariants: { register } } as never
|
||||||
const dispose = await invariant.apply(ctx)
|
// The /invariant subpath types live in lib/types (build product); assert
|
||||||
|
// the surface so the call stays typed where lint runs without a build.
|
||||||
|
const dispose = await (invariant as { apply: (ctx: never) => Promise<() => void> }).apply(ctx)
|
||||||
expect(register).toHaveBeenCalledWith('@deepseek-ai/dsh-client-ui-layout', expect.any(Function))
|
expect(register).toHaveBeenCalledWith('@deepseek-ai/dsh-client-ui-layout', expect.any(Function))
|
||||||
// The installer is the declared no-op — calling it must not throw.
|
// The installer is the declared no-op — calling it must not throw.
|
||||||
expect(() => { (register.mock.calls[0]![1] as (c: never) => void)(undefined as never) }).not.toThrow()
|
expect(() => { (register.mock.calls[0]![1] as (c: never) => void)(undefined as never) }).not.toThrow()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"@deepseek-ai/dsh-invariants": ["./packages/support/invariants/src/index.ts"],
|
"@deepseek-ai/dsh-invariants": ["./packages/support/invariants/src/index.ts"],
|
||||||
"@deepseek-ai/dsh-session/invariant": ["./packages/core/session/src/invariant.ts"],
|
"@deepseek-ai/dsh-session/invariant": ["./packages/core/session/src/invariant.ts"],
|
||||||
"@deepseek-ai/dsh-session/types": ["./packages/core/session/src/types.ts"],
|
"@deepseek-ai/dsh-session/types": ["./packages/core/session/src/types.ts"],
|
||||||
|
"@deepseek-ai/dsh-session/surface": ["./packages/core/session/src/surface.ts"],
|
||||||
"@deepseek-ai/dsh-llm/types": ["./packages/llm/llm/src/types.ts"],
|
"@deepseek-ai/dsh-llm/types": ["./packages/llm/llm/src/types.ts"],
|
||||||
"@deepseek-ai/dsh-llm/brand": ["./packages/llm/llm/src/brand.ts"],
|
"@deepseek-ai/dsh-llm/brand": ["./packages/llm/llm/src/brand.ts"],
|
||||||
"@deepseek-ai/dsh-tools/presentation": ["./packages/core/tools/src/presentation.ts"],
|
"@deepseek-ai/dsh-tools/presentation": ["./packages/core/tools/src/presentation.ts"],
|
||||||
@@ -82,6 +83,7 @@
|
|||||||
// here. The build graph's project references (tsconfig.build.json) stay
|
// here. The build graph's project references (tsconfig.build.json) stay
|
||||||
// explicit — TS project references have no wildcard form.
|
// explicit — TS project references have no wildcard form.
|
||||||
"@deepseek-ai/dsh-host-apiproxy": ["./packages/host/apiproxy/src"],
|
"@deepseek-ai/dsh-host-apiproxy": ["./packages/host/apiproxy/src"],
|
||||||
|
"@deepseek-ai/dsh-host-apiproxy/client": ["./packages/host/apiproxy/src/fetch/client.ts"],
|
||||||
"@deepseek-ai/dsh-host-apiproxy/*": ["./packages/host/apiproxy/src/*"],
|
"@deepseek-ai/dsh-host-apiproxy/*": ["./packages/host/apiproxy/src/*"],
|
||||||
"@deepseek-ai/dsh-host-runtime": ["./packages/host/runtime/src"],
|
"@deepseek-ai/dsh-host-runtime": ["./packages/host/runtime/src"],
|
||||||
"@deepseek-ai/dsh-host-webserver": ["./packages/host/webserver/src"],
|
"@deepseek-ai/dsh-host-webserver": ["./packages/host/webserver/src"],
|
||||||
@@ -105,6 +107,7 @@
|
|||||||
"@deepseek-ai/dsh-client-ui-theme": ["./packages/client/ui-theme/src"],
|
"@deepseek-ai/dsh-client-ui-theme": ["./packages/client/ui-theme/src"],
|
||||||
"@deepseek-ai/dsh-client-ui-theme/client": ["./packages/client/ui-theme/src/client"],
|
"@deepseek-ai/dsh-client-ui-theme/client": ["./packages/client/ui-theme/src/client"],
|
||||||
"@deepseek-ai/dsh-client-i18n": ["./packages/client/i18n/src"],
|
"@deepseek-ai/dsh-client-i18n": ["./packages/client/i18n/src"],
|
||||||
|
"@deepseek-ai/dsh-client-i18n/invariant": ["./packages/client/i18n/src/invariant"],
|
||||||
"@deepseek-ai/dsh-client-i18n/client": ["./packages/client/i18n/src/client"],
|
"@deepseek-ai/dsh-client-i18n/client": ["./packages/client/i18n/src/client"],
|
||||||
"@deepseek-ai/dsh-client-web": ["./packages/client/web/src"],
|
"@deepseek-ai/dsh-client-web": ["./packages/client/web/src"],
|
||||||
"@deepseek-ai/dsh-*": [
|
"@deepseek-ai/dsh-*": [
|
||||||
|
|||||||
Reference in New Issue
Block a user