style: lint

This commit is contained in:
Neko Ayaka
2026-08-26 19:49:58 +08:00
parent e60a04a4ec
commit 98f40d7d0b
1625 changed files with 75216 additions and 75203 deletions
+47 -47
View File
@@ -7,8 +7,8 @@ import { Client } from '../src/client'
class Deferred<T> {
promise: Promise<T>
resolve!: (value: T) => void
reject!: (error: unknown) => void
resolve!: (value: T) => void
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
@@ -19,17 +19,12 @@ class Deferred<T> {
}
class FakeConnection<C = undefined> implements ClientConnection<WebSocketEvent<C>> {
readonly sent: Array<WebSocketEvent<C>> = []
readonly pongs: number[] = []
closed = false
readonly pongs: number[] = []
readonly sent: Array<WebSocketEvent<C>> = []
constructor(private readonly events: ClientEvents<WebSocketEvent<C>>) {}
send(message: WebSocketEvent<C>) {
this.sent.push(message)
return true
}
close() {
if (this.closed) {
return
@@ -43,13 +38,18 @@ class FakeConnection<C = undefined> implements ClientConnection<WebSocketEvent<C
this.pongs.push(Date.now())
return true
}
send(message: WebSocketEvent<C>) {
this.sent.push(message)
return true
}
}
class FakeConnector<C = undefined> implements ClientConnector<WebSocketEvent<C>> {
readonly attempts: Array<{
connection?: FakeConnection<C>
deferred: Deferred<ClientConnection<WebSocketEvent<C>>>
events: ClientEvents<WebSocketEvent<C>>
connection?: FakeConnection<C>
}> = []
connect(events: ClientEvents<WebSocketEvent<C>>) {
@@ -58,6 +58,15 @@ class FakeConnector<C = undefined> implements ClientConnector<WebSocketEvent<C>>
return deferred.promise
}
emit(message: WebSocketEvent<C>, index = this.attempts.length - 1) {
const attempt = this.attempts[index]
if (!attempt) {
throw new Error(`Missing fake connector attempt at index ${index}.`)
}
attempt.events.message(message)
}
open(index = this.attempts.length - 1) {
const attempt = this.attempts[index]
if (!attempt) {
@@ -78,29 +87,11 @@ class FakeConnector<C = undefined> implements ClientConnector<WebSocketEvent<C>>
attempt.deferred.reject(error)
}
emit(message: WebSocketEvent<C>, index = this.attempts.length - 1) {
const attempt = this.attempts[index]
if (!attempt) {
throw new Error(`Missing fake connector attempt at index ${index}.`)
}
attempt.events.message(message)
}
}
function serverEvent<E extends WebSocketEvent['type']>(
type: E,
data: WebSocketEventOf<E>['data'],
): WebSocketEventOf<E> {
return {
type,
data,
metadata: {
source: { kind: 'plugin', plugin: { id: 'server' }, id: 'server-1' },
event: { id: `${type}-1` },
},
} as WebSocketEventOf<E>
async function flushAsyncTasks() {
await flushMicrotasks()
await new Promise(resolve => setTimeout(resolve, 0))
}
async function flushMicrotasks() {
@@ -108,9 +99,18 @@ async function flushMicrotasks() {
await Promise.resolve()
}
async function flushAsyncTasks() {
await flushMicrotasks()
await new Promise(resolve => setTimeout(resolve, 0))
function serverEvent<E extends WebSocketEvent['type']>(
type: E,
data: WebSocketEventOf<E>['data'],
): WebSocketEventOf<E> {
return {
data,
metadata: {
event: { id: `${type}-1` },
source: { id: 'server-1', kind: 'plugin', plugin: { id: 'server' } },
},
type,
} as WebSocketEventOf<E>
}
afterEach(() => {
@@ -165,8 +165,8 @@ describe('client', () => {
expect(client.connectionStatus).toBe('authenticating')
expect(connection.sent.at(-1)).toMatchObject({
type: 'module:authenticate',
data: { token: 'secret' },
type: 'module:authenticate',
})
connector.emit(serverEvent('module:authenticated', { authenticated: true }))
@@ -176,13 +176,13 @@ describe('client', () => {
expect(client.connectionStatus).toBe('announcing')
expect(announceEvent).toMatchObject({
type: 'extension:module:announce',
data: { name: 'test-plugin' },
type: 'extension:module:announce',
})
connector.emit(serverEvent('extension:module:announced', {
name: 'test-plugin',
identity: announceEvent.data.identity,
name: 'test-plugin',
}))
await expect(connected).resolves.toBeUndefined()
@@ -208,11 +208,11 @@ describe('client', () => {
const announceEvent = connection.sent.at(-1) as WebSocketEventOf<'extension:module:announce'>
connector.emit(serverEvent('registry:modules:sync', {
modules: [{ name: 'test-plugin', identity: announceEvent.data.identity }],
modules: [{ identity: announceEvent.data.identity, name: 'test-plugin' }],
}))
connector.emit(serverEvent('extension:module:announced', {
name: 'test-plugin',
identity: announceEvent.data.identity,
name: 'test-plugin',
}))
await expect(connected).resolves.toBeUndefined()
@@ -293,22 +293,22 @@ describe('client', () => {
await connected
const sent = client.send({
type: 'input:text',
data: { text: 'hello' },
type: 'input:text',
})
expect(sent).toBe(true)
expect(connection.sent.at(-1)).toMatchObject({
type: 'input:text',
data: { text: 'hello' },
metadata: {
event: { id: expect.any(String) },
source: {
kind: 'plugin',
id: expect.any(String),
kind: 'plugin',
plugin: { id: 'test-extension' },
},
event: { id: expect.any(String) },
},
type: 'input:text',
})
expect(onAnySend).toHaveBeenCalledWith(connection.sent.at(-1))
})
@@ -370,8 +370,8 @@ describe('client', () => {
expect(listener).toHaveBeenCalledWith(input)
expect(onAnyMessage).toHaveBeenCalledWith(input)
expect(connection.sent.at(-1)).toMatchObject({
type: 'transport:connection:heartbeat',
data: { kind: 'pong' },
type: 'transport:connection:heartbeat',
})
})
@@ -390,11 +390,11 @@ describe('client', () => {
await connected
client.send({
type: 'input:text',
data: { text: 'hello' },
metadata: {
event: { id: undefined },
},
type: 'input:text',
})
expect(connection.sent.at(-1)?.metadata.event.id).toEqual(expect.any(String))
@@ -406,8 +406,8 @@ describe('client', () => {
autoConnect: false,
autoReconnect: false,
connector,
heartbeat: false,
handshake: 'manual',
heartbeat: false,
name: 'test-extension',
})
@@ -440,8 +440,8 @@ describe('client', () => {
const announceEvent = connection.sent.at(-1) as WebSocketEventOf<'extension:module:announce'>
connector.emit(serverEvent('extension:module:announced', {
name: 'test-plugin',
identity: announceEvent.data.identity,
name: 'test-plugin',
}))
await expect(client.ensureConnected()).resolves.toBeUndefined()
@@ -470,8 +470,8 @@ describe('client', () => {
const announceEvent = connection.sent.at(-1) as WebSocketEventOf<'extension:module:announce'>
connector.emit(serverEvent('extension:module:announced', {
name: 'test-plugin',
identity: announceEvent.data.identity,
name: 'test-plugin',
}))
await expect(client.ready()).resolves.toBeUndefined()
+5 -5
View File
@@ -8,12 +8,12 @@ import { InvalidMessageError, parseEvent, stringifyEvent } from '../src/codec'
describe('server-sdk codec', () => {
it('parses SuperJSON and plain JSON protocol events', () => {
const event: WebSocketEvent = {
type: 'module:authenticate',
data: { token: 'secret' },
metadata: {
source: { kind: 'plugin', plugin: { id: 'plugin-1' }, id: 'plugin-1' },
event: { id: 'event-1' },
source: { id: 'plugin-1', kind: 'plugin', plugin: { id: 'plugin-1' } },
},
type: 'module:authenticate',
}
expect(parseEvent(stringifySuperJson(event))).toEqual(event)
@@ -21,7 +21,7 @@ describe('server-sdk codec', () => {
})
it('throws debuggable errors for invalid messages', () => {
const source = { type: 'module:authenticate', data: 'secret' }
const source = { data: 'secret', type: 'module:authenticate' }
try {
parseEvent(JSON.stringify(source))
@@ -35,7 +35,7 @@ describe('server-sdk codec', () => {
})
it('rejects array event data with validation context', () => {
const source = { type: 'module:authenticate', data: ['secret'] }
const source = { data: ['secret'], type: 'module:authenticate' }
expect(() => parseEvent(JSON.stringify(source))).toThrow(InvalidMessageError)
@@ -64,8 +64,8 @@ describe('server-sdk codec', () => {
it('stringifies protocol events with SuperJSON', () => {
expect(stringifyEvent({
type: 'module:authenticate',
data: { token: 'secret' },
type: 'module:authenticate',
})).toContain('module:authenticate')
})
})
+30 -30
View File
@@ -7,26 +7,17 @@ import { describe, expect, it, vi } from 'vitest'
import { createWebSocketExtensionPeer } from '../src/extension-peer'
type Listener = (data: WebSocketBaseEvent<string, unknown>) => void | Promise<void>
type Listener = (data: WebSocketBaseEvent<string, unknown>) => Promise<void> | void
class FakeClient implements ExtensionPeerClient {
readonly sent: WebSocketEventOptionalSource[] = []
readonly connect = vi.fn(async () => {})
readonly close = vi.fn(() => {})
readonly connect = vi.fn(async () => {})
readonly listeners = new Map<keyof WebSocketEvents, Set<Listener>>()
send(data: WebSocketEventOptionalSource): boolean {
this.sent.push(data)
return true
}
sendOrThrow(data: WebSocketEventOptionalSource): void {
this.sent.push(data)
}
readonly sent: WebSocketEventOptionalSource[] = []
onEvent<E extends keyof WebSocketEvents>(
event: E,
callback: (data: WebSocketBaseEvent<E, WebSocketEvents[E]>) => void | Promise<void>,
callback: (data: WebSocketBaseEvent<E, WebSocketEvents[E]>) => Promise<void> | void,
) {
let listeners = this.listeners.get(event)
if (!listeners) {
@@ -41,21 +32,30 @@ class FakeClient implements ExtensionPeerClient {
listeners?.delete(listener)
}
}
send(data: WebSocketEventOptionalSource): boolean {
this.sent.push(data)
return true
}
sendOrThrow(data: WebSocketEventOptionalSource): void {
this.sent.push(data)
}
}
class FakeConnector implements ClientConnector<WebSocketEvent> {
readonly attempts: Array<{
events: ClientEvents<WebSocketEvent>
connection: ClientConnection<WebSocketEvent>
events: ClientEvents<WebSocketEvent>
}> = []
connect(events: ClientEvents<WebSocketEvent>) {
const connection: ClientConnection<WebSocketEvent> = {
send: () => true,
close: () => events.close({ code: 1000, reason: 'closed', wasClean: true }),
send: () => true,
}
this.attempts.push({ events, connection })
this.attempts.push({ connection, events })
return connection
}
}
@@ -69,16 +69,16 @@ describe('websocket extension peer', () => {
it('authenticates the websocket peer separately from the extension session', async () => {
const fakeClient = new FakeClient()
const peer = createWebSocketExtensionPeer({
client: fakeClient,
extension: {
id: 'airi-extension-chess',
version: '1.0.0',
sessionId: 'session-1',
version: '1.0.0',
},
client: fakeClient,
})
await peer.connect()
peer.authenticatePeer({ token: 'secret', peerId: 'peer-1' })
peer.authenticatePeer({ peerId: 'peer-1', token: 'secret' })
peer.announceExtension()
expect(fakeClient.connect).toHaveBeenCalled()
@@ -87,32 +87,32 @@ describe('websocket extension peer', () => {
'extension:announce',
])
expect(fakeClient.sent[0]).toMatchObject({
type: 'peer:authenticate',
data: {
token: 'secret',
peerId: 'peer-1',
token: 'secret',
},
type: 'peer:authenticate',
})
expect(fakeClient.sent[1]).toMatchObject({
type: 'extension:announce',
data: {
identity: {
id: 'airi-extension-chess',
version: '1.0.0',
sessionId: 'session-1',
version: '1.0.0',
},
},
type: 'extension:announce',
})
})
it('announces extension modules under the owning extension identity', () => {
const fakeClient = new FakeClient()
const peer = createWebSocketExtensionPeer({
client: fakeClient,
extension: {
id: 'airi-extension-chess',
sessionId: 'session-1',
},
client: fakeClient,
})
peer.announceModule({
@@ -122,31 +122,31 @@ describe('websocket extension peer', () => {
})
expect(fakeClient.sent[0]).toMatchObject({
type: 'extension:module:announce',
data: {
name: 'Chess Gamelet',
identity: {
id: 'chess-gamelet',
extension: {
id: 'airi-extension-chess',
sessionId: 'session-1',
},
id: 'chess-gamelet',
},
name: 'Chess Gamelet',
possibleEvents: [],
},
type: 'extension:module:announce',
})
})
it('creates a manual peer client without auto-connect or auto-reconnect by default', async () => {
const connector = new FakeConnector()
const peer = createWebSocketExtensionPeer({
clientOptions: {
connector,
},
extension: {
id: 'airi-extension-chess',
sessionId: 'session-1',
},
clientOptions: {
connector,
},
})
expect(connector.attempts).toHaveLength(0)