diff --git a/apps/stage-tamagotchi/src/renderer/components/WithScreenCapture.vue b/apps/stage-tamagotchi/src/renderer/components/WithScreenCapture.vue index 2c04b6a7c..a053ec131 100644 --- a/apps/stage-tamagotchi/src/renderer/components/WithScreenCapture.vue +++ b/apps/stage-tamagotchi/src/renderer/components/WithScreenCapture.vue @@ -26,6 +26,7 @@ const { checkMacOSPermission, requestMacOSPermission, } = useElectronScreenCapture(window.electron.ipcRenderer, sourcesOptions) + const focused = useWindowFocus() async function checkPermissions() { diff --git a/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue b/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue index a94c490f1..57fad5c44 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/devtools/screen-capture.vue @@ -28,10 +28,7 @@ const sourcesOptions = ref({ }) const { t } = useI18n() -const { - getSources, - selectWithSource, -} = useElectronScreenCapture(window.electron.ipcRenderer, sourcesOptions) +const { getSources, selectWithSource } = useElectronScreenCapture(window.electron.ipcRenderer, sourcesOptions) const categoryOptions = [ { label: 'Applications', value: 'applications', icon: 'i-solar:window-frame-line-duotone' }, @@ -90,13 +87,16 @@ function toObjectUrl(bytes: Uint8Array, mime: string) { async function startCapture(source: SerializableDesktopCapturerSource) { try { - await selectWithSource(() => source.id, async () => { - const stream = await navigator.mediaDevices.getDisplayMedia({ - video: true, - audio: true, - }) - activeStreams.value.push(stream) - }) + await selectWithSource( + () => source.id, + async () => { + const stream = await navigator.mediaDevices.getDisplayMedia({ + video: true, + audio: true, + }) + activeStreams.value.push(stream) + }, + ) } catch (err) { console.error('Error selecting source:', err) diff --git a/cspell.config.yaml b/cspell.config.yaml index 291503875..e78747652 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -146,6 +146,7 @@ words: - kirakira - knip - kwaa + - Landmarker - lemonneko - lemonnekogh - lerp @@ -182,6 +183,7 @@ words: - minisearch - mkdist - mobileprovision + - Mocap - modelcontextprotocol - modnet - moeru diff --git a/integrations/vscode/vscode-airi/tsconfig.json b/integrations/vscode/vscode-airi/tsconfig.json index d18892ab1..124cda59a 100644 --- a/integrations/vscode/vscode-airi/tsconfig.json +++ b/integrations/vscode/vscode-airi/tsconfig.json @@ -6,6 +6,9 @@ "module": "ESNext", "moduleResolution": "bundler", "resolveJsonModule": true, + "types": [ + "node" + ], "strict": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, diff --git a/packages/ccc/tsconfig.json b/packages/ccc/tsconfig.json index 00dcfd807..4af3ec64c 100644 --- a/packages/ccc/tsconfig.json +++ b/packages/ccc/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ESNext", "lib": [ + "DOM", "ESNext" ], "module": "ESNext", diff --git a/packages/electron-screen-capture/src/renderer.ts b/packages/electron-screen-capture/src/renderer.ts index d04abc3b0..d24002301 100644 --- a/packages/electron-screen-capture/src/renderer.ts +++ b/packages/electron-screen-capture/src/renderer.ts @@ -8,7 +8,7 @@ import { defineInvoke } from '@moeru/eventa' import { screenCaptureCheckMacOSPermission, screenCaptureGetSources, screenCaptureRequestMacOSPermission, screenCaptureResetSource, screenCaptureSetSourceEx } from '.' export interface SourceOptionsWithRequest { - sourcesOptions?: SourcesOptions + sourcesOptions: SourcesOptions request?: Omit } @@ -27,17 +27,17 @@ export function setupElectronScreenCapture(context: ReturnType( selectFn: (sources: SerializableDesktopCapturerSource[]) => string | Promise, useFn: () => R | Promise, - options?: SourceOptionsWithRequest, + options: SourceOptionsWithRequest, ): Promise { - const sources = await getSources(options?.sourcesOptions) + const sources = await getSources(options.sourcesOptions) const sourceId = await selectFn(sources) let handle: string | undefined try { handle = await setSource({ - options: options?.sourcesOptions, + options: options.sourcesOptions, sourceId, - timeout: options?.request?.timeout, + timeout: options.request?.timeout, }) return await useFn() } diff --git a/packages/model-driver-lipsync/src/shared/wlipsync/index.ts b/packages/model-driver-lipsync/src/shared/wlipsync/index.ts index 3d9f032ad..003d78958 100644 --- a/packages/model-driver-lipsync/src/shared/wlipsync/index.ts +++ b/packages/model-driver-lipsync/src/shared/wlipsync/index.ts @@ -1,2 +1,2 @@ -export { default as wlipsyncProfile } from './profile.json' assert { type: 'json' } +export { default as wlipsyncProfile } from './profile.json' with { type: 'json' } export type { Profile } from 'wlipsync' diff --git a/packages/pipelines-audio/src/processors/tts-chunker.ts b/packages/pipelines-audio/src/processors/tts-chunker.ts index 49f3147ee..2cdbeb968 100644 --- a/packages/pipelines-audio/src/processors/tts-chunker.ts +++ b/packages/pipelines-audio/src/processors/tts-chunker.ts @@ -9,6 +9,8 @@ import { createPushStream } from '../stream' export const TTS_FLUSH_INSTRUCTION = '\u200B' export const TTS_SPECIAL_TOKEN = '\u2063' +const regexpAnySingleDigit = /\d/ + const keptPunctuations = new Set('??!!') const hardPunctuations = new Set('.。??!!…⋯~~\n\t\r') const softPunctuations = new Set(',,、–—::;;《》「」') @@ -83,9 +85,9 @@ export async function* chunkTtsInput( switch (value) { case '.': case ',': { - if (previousValue !== undefined && /\d/.test(previousValue)) { + if (previousValue !== undefined && regexpAnySingleDigit.test(previousValue)) { next = await iterator.next() - if (!next.done && next.value && /\d/.test(next.value)) { + if (!next.done && next.value && regexpAnySingleDigit.test(next.value)) { buffer += value current = next next = undefined diff --git a/packages/plugin-protocol/tsconfig.json b/packages/plugin-protocol/tsconfig.json index 00dcfd807..4af3ec64c 100644 --- a/packages/plugin-protocol/tsconfig.json +++ b/packages/plugin-protocol/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ESNext", "lib": [ + "DOM", "ESNext" ], "module": "ESNext", diff --git a/packages/plugin-sdk/src/channels/shared.ts b/packages/plugin-sdk/src/channels/shared.ts index 0fd8783a0..8be168c72 100644 --- a/packages/plugin-sdk/src/channels/shared.ts +++ b/packages/plugin-sdk/src/channels/shared.ts @@ -1,3 +1,3 @@ -import type { createContext } from '@moeru/eventa' +import type { EventContext } from '@moeru/eventa' -export type ChannelHost = ReturnType +export type ChannelHost = EventContext diff --git a/packages/plugin-sdk/src/plugin-host/core.ts b/packages/plugin-sdk/src/plugin-host/core.ts index f647f28dc..d044eee9c 100644 --- a/packages/plugin-sdk/src/plugin-host/core.ts +++ b/packages/plugin-sdk/src/plugin-host/core.ts @@ -633,9 +633,13 @@ export class PluginHost { const lifecycle = createActor(pluginLifecycleMachine) lifecycle.start() - const permissionSnapshot = this.permissions.initialize(id, manifest.permissions, { - persisted: this.persistedPermissionGrants.get(identity.plugin.id), - }) + const permissionSnapshot = this.permissions.initialize( + id, + manifest.permissions, + { + persisted: this.persistedPermissionGrants.get(identity.plugin.id), + }, + ) const session: PluginHostSession = { manifest, diff --git a/packages/plugin-sdk/src/plugin-host/shared/types.ts b/packages/plugin-sdk/src/plugin-host/shared/types.ts index 25900d182..b24264e9f 100644 --- a/packages/plugin-sdk/src/plugin-host/shared/types.ts +++ b/packages/plugin-sdk/src/plugin-host/shared/types.ts @@ -54,7 +54,7 @@ export interface ManifestV1 { apiVersion: 'v1' kind: 'manifest.plugin.airi.moeru.ai' name: string - permissions?: ModulePermissionDeclaration + permissions: ModulePermissionDeclaration entrypoints: { default?: string electron?: string @@ -76,7 +76,7 @@ export const manifestV1Schema = object({ apiVersion: literal('v1'), kind: literal('manifest.plugin.airi.moeru.ai'), name: string(), - permissions: optional(object({ + permissions: object({ apis: optional(array(object({ key: string(), actions: array(picklist(['invoke', 'emit'])), @@ -112,7 +112,7 @@ export const manifestV1Schema = object({ label: optional(localizableSchema), required: optional(boolean()), }))), - })), + }), entrypoints: object({ default: optional(string()), electron: optional(string()), diff --git a/packages/plugin-sdk/tsconfig.json b/packages/plugin-sdk/tsconfig.json index 6869a6239..c58de42e2 100644 --- a/packages/plugin-sdk/tsconfig.json +++ b/packages/plugin-sdk/tsconfig.json @@ -7,6 +7,9 @@ ], "module": "ESNext", "moduleResolution": "bundler", + "types": [ + "node" + ], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/packages/server-sdk/src/client.ts b/packages/server-sdk/src/client.ts index 511fda904..03e1c2ca1 100644 --- a/packages/server-sdk/src/client.ts +++ b/packages/server-sdk/src/client.ts @@ -49,8 +49,8 @@ export interface ClientOptions { name: string token?: string websocketConstructor?: WebSocketLikeConstructor - connectTimeoutMs?: number + connectTimeoutMs?: number possibleEvents?: Array> identity?: MetadataEventSource dependencies?: ModuleDependency[] @@ -126,9 +126,9 @@ export class Client { private readonly heartbeat: Required private readonly websocketConstructor: WebSocketLikeConstructor - private readonly opts: Required, 'token' | 'heartbeat' | 'websocketConstructor'>> & Pick, 'token'> & { - heartbeat: Required - } + private readonly opts: + & Required, 'token' | 'heartbeat' | 'websocketConstructor' | 'configSchema'>> + & Pick, 'token' | 'heartbeat' | 'configSchema'> private readonly eventListeners = new Map< keyof WebSocketEvents, diff --git a/packages/server-sdk/src/websocket-like.ts b/packages/server-sdk/src/websocket-like.ts index 95e4867bc..d5e0763aa 100644 --- a/packages/server-sdk/src/websocket-like.ts +++ b/packages/server-sdk/src/websocket-like.ts @@ -3,7 +3,7 @@ export interface WebSocketMessageEventLike { } export interface WebSocketErrorEventLike { - error?: Error + error?: Error | unknown } export interface WebSocketLike { diff --git a/packages/server-sdk/test/client.test.ts b/packages/server-sdk/test/client.test.ts index 06e3b84ba..28c3bdd64 100644 --- a/packages/server-sdk/test/client.test.ts +++ b/packages/server-sdk/test/client.test.ts @@ -1,4 +1,4 @@ -import type { WebSocketEvent } from '@proj-airi/server-shared/types' +import type { WebSocketEvent, WebSocketEventOf } from '@proj-airi/server-shared/types' import superjson from 'superjson' @@ -12,18 +12,18 @@ class MockWebSocket { static instances: MockWebSocket[] = [] - readonly sent: string[] = [] + readonly sent: Array> = [] readyState = MockWebSocket.CONNECTING onclose?: () => void - onerror?: (event: { error?: Error }) => void - onmessage?: (event: { data: string }) => void + onerror?: (event: { error?: Error } | unknown) => void + onmessage?: (event: { data: string | ArrayBufferLike | ArrayBufferView }) => void onopen?: () => void constructor(public readonly url: string) { MockWebSocket.instances.push(this) } - send(data: string) { + send(data: string | ArrayBufferLike | ArrayBufferView) { this.sent.push(data) } @@ -65,8 +65,14 @@ function parseSent(socket: MockWebSocket, index = -1) { if (!payload) { throw new Error(`No sent payload at index ${index}`) } + if (typeof payload === 'string') { + return superjson.parse(payload) + } - return superjson.parse(payload) + const textDecoder = new TextDecoder() + const decoded = textDecoder.decode(payload) + + return superjson.parse(decoded) } function emitOpen(socket: MockWebSocket) { @@ -114,7 +120,8 @@ describe('client', () => { }, }) - const announceEvent = parseSent(socket) + const announceEvent = parseSent(socket) as WebSocketEventOf<'module:announced'> + expect(announceEvent).toMatchObject({ type: 'module:announce', data: { name: 'test-plugin' }, @@ -195,7 +202,7 @@ describe('client', () => { } emitOpen(socket) - const announceEvent = parseSent(socket) + const announceEvent = parseSent(socket) as WebSocketEventOf<'module:announced'> emitMessage(socket, { type: 'module:announced', @@ -229,7 +236,7 @@ describe('client', () => { await timedOutAssertion emitOpen(socket) - const announceEvent = parseSent(socket) + const announceEvent = parseSent(socket) as WebSocketEventOf<'module:announced'> emitMessage(socket, { type: 'module:announced', @@ -278,7 +285,8 @@ describe('client', () => { emitOpen(socket) - const announceEvent = parseSent(socket) + const announceEvent = parseSent(socket) as WebSocketEventOf<'module:announced'> + emitMessage(socket, { type: 'module:announced', data: { @@ -322,7 +330,8 @@ describe('client', () => { const secondSocket = lastSocket() emitOpen(secondSocket) - const announceEvent = parseSent(secondSocket) + + const announceEvent = parseSent(secondSocket) as WebSocketEventOf<'module:announced'> emitMessage(secondSocket, { type: 'module:announced', @@ -353,7 +362,8 @@ describe('client', () => { const socket = lastSocket() emitOpen(socket) - const announceEvent = parseSent(socket) + const announceEvent = parseSent(socket) as WebSocketEventOf<'module:announced'> + const selfIdentity = announceEvent.data.identity emitMessage(socket, { diff --git a/packages/server-sdk/tsconfig.json b/packages/server-sdk/tsconfig.json index 07486031b..a69e581ad 100644 --- a/packages/server-sdk/tsconfig.json +++ b/packages/server-sdk/tsconfig.json @@ -14,6 +14,9 @@ "../server-shared/src/*" ] }, + "types": [ + "node" + ], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/packages/stage-layouts/tsconfig.json b/packages/stage-layouts/tsconfig.json index e66da7a63..4d141a440 100644 --- a/packages/stage-layouts/tsconfig.json +++ b/packages/stage-layouts/tsconfig.json @@ -4,8 +4,8 @@ "jsx": "preserve", "lib": [ "DOM", - "ESNext", - "DOM.AsyncIterable" + "DOM.AsyncIterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "Bundler", diff --git a/packages/stage-pages/tsconfig.json b/packages/stage-pages/tsconfig.json index ed02e9ad2..d9d96e750 100644 --- a/packages/stage-pages/tsconfig.json +++ b/packages/stage-pages/tsconfig.json @@ -4,8 +4,8 @@ "jsx": "preserve", "lib": [ "DOM", - "ESNext", - "DOM.AsyncIterable" + "DOM.AsyncIterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "Bundler", diff --git a/packages/stage-ui-live2d/tsconfig.json b/packages/stage-ui-live2d/tsconfig.json index c53f50ef3..2365daacf 100644 --- a/packages/stage-ui-live2d/tsconfig.json +++ b/packages/stage-ui-live2d/tsconfig.json @@ -4,8 +4,8 @@ "jsx": "preserve", "lib": [ "DOM", - "ESNext", - "DOM.AsyncIterable" + "DOM.AsyncIterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "Bundler", diff --git a/packages/stage-ui/tsconfig.json b/packages/stage-ui/tsconfig.json index 3a2ef96fb..01462e7b7 100644 --- a/packages/stage-ui/tsconfig.json +++ b/packages/stage-ui/tsconfig.json @@ -4,8 +4,8 @@ "jsx": "preserve", "lib": [ "DOM", - "ESNext", - "DOM.AsyncIterable" + "DOM.AsyncIterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "Bundler", diff --git a/packages/stream-kit/tsconfig.json b/packages/stream-kit/tsconfig.json index 886b8007b..dadd49f15 100644 --- a/packages/stream-kit/tsconfig.json +++ b/packages/stream-kit/tsconfig.json @@ -1,7 +1,11 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "lib": ["ESNext", "DOM", "DOM.Iterable"], + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], "types": [] }, "include": [ diff --git a/packages/ui-loading-screens/tsconfig.json b/packages/ui-loading-screens/tsconfig.json index 28ccc823c..d0778188a 100644 --- a/packages/ui-loading-screens/tsconfig.json +++ b/packages/ui-loading-screens/tsconfig.json @@ -2,9 +2,9 @@ "compilerOptions": { "target": "ESNext", "lib": [ - "ESNext", "DOM", - "DOM.Iterable" + "DOM.Iterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "bundler", diff --git a/packages/ui-transitions/tsconfig.json b/packages/ui-transitions/tsconfig.json index 28ccc823c..d0778188a 100644 --- a/packages/ui-transitions/tsconfig.json +++ b/packages/ui-transitions/tsconfig.json @@ -2,9 +2,9 @@ "compilerOptions": { "target": "ESNext", "lib": [ - "ESNext", "DOM", - "DOM.Iterable" + "DOM.Iterable", + "ESNext" ], "module": "ESNext", "moduleResolution": "bundler", diff --git a/packages/vite-plugin-warpdrive/src/index.ts b/packages/vite-plugin-warpdrive/src/index.ts index 13b6093b2..f9dca4938 100644 --- a/packages/vite-plugin-warpdrive/src/index.ts +++ b/packages/vite-plugin-warpdrive/src/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable e18e/prefer-static-regex */ import type { OutputAsset } from 'rollup' import type { Plugin, RenderBuiltAssetUrl, ResolvedConfig } from 'vite' @@ -8,6 +9,8 @@ import { rm } from 'node:fs/promises' import { join, resolve } from 'node:path' import { cwd } from 'node:process' +import { errorCauseFrom, errorMessageFrom, errorNameFrom, errorStackFrom } from '@moeru/std' + export { createS3Provider } from './providers/s3' export type { S3ProviderOptions } from './providers/s3' export type { UploadProvider } from './providers/types' @@ -248,9 +251,17 @@ export function WarpDrivePlugin(options: WarpDrivePluginOptions): Plugin { try { await options.provider.upload(localPath, key, contentType) } - catch (error) { - resolvedConfig.logger.error(`[${pluginName}] upload failed, file: ${fileName} -> ${key}: ${error}`, { error }) - throw error + catch (err) { + const error = new Error(errorMessageFrom(err)) + error.name = errorNameFrom(err) || 'UploadError' + const stack = errorStackFrom(err) + if (stack) + error.stack = stack + + error.cause = errorCauseFrom(err) + + resolvedConfig.logger.error(`[${pluginName}] upload failed, file: ${fileName} -> ${key}: ${err}`, { error }) + throw err } if (shouldDeleteLocalAsset) { diff --git a/packages/vite-plugin-warpdrive/src/providers/s3.test.ts b/packages/vite-plugin-warpdrive/src/providers/s3.test.ts index 8ea143ee8..a03efe50d 100644 --- a/packages/vite-plugin-warpdrive/src/providers/s3.test.ts +++ b/packages/vite-plugin-warpdrive/src/providers/s3.test.ts @@ -15,14 +15,14 @@ describe('s3', (test) => { describe('S3Provider', () => { it('should skip upload', async () => { const s3 = createS3Provider({ - endpoint: env.S3_ENDPOINT, - accessKeyId: env.S3_ACCESS_KEY_ID, - secretAccessKey: env.S3_SECRET_ACCESS_KEY, + endpoint: env.S3_ENDPOINT!, + accessKeyId: env.S3_ACCESS_KEY_ID!, + secretAccessKey: env.S3_SECRET_ACCESS_KEY!, region: env.S3_REGION, publicBaseUrl: env.WARP_DRIVE_PUBLIC_BASE ?? env.S3_ENDPOINT, }) - expect(await s3.shouldSkipUpload(join(cwd(), 'packages', 'stage-ui', 'src', 'assets', 'live2d', 'models', 'hiyori_free_zh.zip'), '/proj-airi/stage-web/assets/hiyori_free_zh-D9UJNK98.zip')).toBe(true) + expect(await s3.shouldSkipUpload?.(join(cwd(), 'packages', 'stage-ui', 'src', 'assets', 'live2d', 'models', 'hiyori_free_zh.zip'), '/proj-airi/stage-web/assets/hiyori_free_zh-D9UJNK98.zip')).toBe(true) }) }) }) diff --git a/packages/vite-plugin-warpdrive/src/providers/s3.ts b/packages/vite-plugin-warpdrive/src/providers/s3.ts index 23056d81e..316ef699f 100644 --- a/packages/vite-plugin-warpdrive/src/providers/s3.ts +++ b/packages/vite-plugin-warpdrive/src/providers/s3.ts @@ -1,3 +1,4 @@ +/* eslint-disable e18e/prefer-static-regex */ import type { Buffer } from 'node:buffer' import type { UploadProvider } from './types' diff --git a/services/discord-bot/tsconfig.json b/services/discord-bot/tsconfig.json index 00dcfd807..c36b13b4b 100644 --- a/services/discord-bot/tsconfig.json +++ b/services/discord-bot/tsconfig.json @@ -6,6 +6,9 @@ ], "module": "ESNext", "moduleResolution": "bundler", + "types": [ + "node" + ], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/services/telegram-bot/tsconfig.json b/services/telegram-bot/tsconfig.json index f50aea53b..b4d7ff92c 100644 --- a/services/telegram-bot/tsconfig.json +++ b/services/telegram-bot/tsconfig.json @@ -7,6 +7,9 @@ "moduleDetection": "force", "module": "ESNext", "moduleResolution": "bundler", + "types": [ + "node" + ], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/services/twitter-services/tsconfig.json b/services/twitter-services/tsconfig.json index b7208af96..c8fc9f1ec 100644 --- a/services/twitter-services/tsconfig.json +++ b/services/twitter-services/tsconfig.json @@ -8,6 +8,9 @@ "module": "ESNext", "moduleResolution": "bundler", "resolveJsonModule": true, + "types": [ + "node" + ], "strict": true, "strictNullChecks": true, "noImplicitAny": true,