diff --git a/apps/backend/src/services/session/session-activity.js b/apps/backend/src/services/session/session-activity.ts
similarity index 68%
rename from apps/backend/src/services/session/session-activity.js
rename to apps/backend/src/services/session/session-activity.ts
index 5572f6fa..5010cf5a 100644
--- a/apps/backend/src/services/session/session-activity.js
+++ b/apps/backend/src/services/session/session-activity.ts
@@ -1,9 +1,83 @@
-// @ts-nocheck
+///
import { extractHostState, reloadActivityPageForPresentation } from './session-page.js'
import { shouldRefreshLoggedInPresentation } from './session-state.js'
-export function createEmptyActivityInfo() {
+type BrowserPageLike = {
+ evaluate: (callback: any) => Promise
+ waitForTimeout: (milliseconds: number) => Promise
+}
+
+type HostState = {
+ loginText?: string
+ unloginVisible?: boolean
+ loginedVisible?: boolean
+}
+
+export type ActivityInfo = {
+ nickname: string
+ role: {
+ ready: boolean
+ roleName: string
+ roleId: string
+ area: string
+ partition: string
+ platId: string
+ md5str: string
+ checkparam: string
+ }
+ form: {
+ cdkeyInputId: string
+ cdkeyValue: string
+ verifyInputId: string
+ verifyValue: string
+ verifyImgId: string
+ submitId: string
+ }
+ verify: {
+ visible: boolean
+ src: string
+ naturalWidth: number
+ naturalHeight: number
+ }
+ popup: {
+ visible: boolean
+ text: string
+ detail: string
+ }
+}
+
+type SessionForPresentation = {
+ page: BrowserPageLike
+ presentationSyncAttempts: number
+}
+
+type EnsureLoggedInPresentationOptions = {
+ hostState?: HostState | null
+ credentialReady?: boolean
+ activityUrl?: string
+ extractHostState?: (page: BrowserPageLike) => Promise
+ reloadActivityPageForPresentation?: (page: BrowserPageLike, activityUrl: string) => Promise
+ shouldRefreshLoggedInPresentation?: (hostState: HostState) => boolean
+}
+
+type EnsureActivityInfoReadyOptions = {
+ triggerRender?: boolean
+ timeoutMs?: number
+ extractActivityInfo?: (page: BrowserPageLike) => Promise
+ triggerActivityRoleRender?: (page: BrowserPageLike) => Promise
+ now?: () => number
+}
+
+declare global {
+ interface Window {
+ renderCdkey?: () => void
+ roleData?: Record
+ isRole?: unknown
+ }
+}
+
+export function createEmptyActivityInfo(): ActivityInfo {
return {
nickname: '',
role: {
@@ -39,7 +113,7 @@ export function createEmptyActivityInfo() {
}
export async function ensureLoggedInPresentation(
- session,
+ session: SessionForPresentation,
{
hostState = null,
credentialReady = false,
@@ -47,8 +121,8 @@ export async function ensureLoggedInPresentation(
extractHostState: extractHostStateImpl = extractHostState,
reloadActivityPageForPresentation: reloadActivityPageForPresentationImpl = reloadActivityPageForPresentation,
shouldRefreshLoggedInPresentation: shouldRefreshLoggedInPresentationImpl = shouldRefreshLoggedInPresentation,
- } = {},
-) {
+ }: EnsureLoggedInPresentationOptions = {},
+): Promise<{ reloaded: boolean; hostState: HostState }> {
let nextHostState = hostState || (await extractHostStateImpl(session.page))
if (!credentialReady || !shouldRefreshLoggedInPresentationImpl(nextHostState)) {
@@ -76,15 +150,15 @@ export async function ensureLoggedInPresentation(
}
export async function ensureActivityInfoReady(
- page,
+ page: BrowserPageLike,
{
triggerRender = false,
timeoutMs = 8_000,
extractActivityInfo: extractActivityInfoImpl = extractActivityInfo,
triggerActivityRoleRender: triggerActivityRoleRenderImpl = triggerActivityRoleRender,
now = () => Date.now(),
- } = {},
-) {
+ }: EnsureActivityInfoReadyOptions = {},
+): Promise {
if (triggerRender) {
await triggerActivityRoleRenderImpl(page)
}
@@ -104,7 +178,7 @@ export async function ensureActivityInfoReady(
return lastInfo
}
-export async function triggerActivityRoleRender(page) {
+export async function triggerActivityRoleRender(page: BrowserPageLike): Promise {
try {
await page.evaluate(() => {
const maybeRender = window.renderCdkey
@@ -118,11 +192,11 @@ export async function triggerActivityRoleRender(page) {
}
}
-export async function extractActivityInfo(page) {
+export async function extractActivityInfo(page: BrowserPageLike): Promise {
try {
return await page.evaluate(() => {
- const asString = (value) => (typeof value === 'string' ? value.trim() : '')
- const isVisible = (element) => {
+ const asString = (value: unknown) => (typeof value === 'string' ? value.trim() : '')
+ const isVisible = (element: Element | null) => {
if (!element) {
return false
}
@@ -131,8 +205,11 @@ export async function extractActivityInfo(page) {
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'
}
- const pick = (selector) => document.querySelector(selector)
- const pickBest = (selector, predicate = null) => {
+ const pick = (selector: string) => document.querySelector(selector)
+ const pickBest = (
+ selector: string,
+ predicate: ((element: Element) => boolean) | null = null,
+ ): Element | null => {
const matches = [...document.querySelectorAll(selector)]
const preferred = matches.find((element) => {
if (predicate && !predicate(element)) {
@@ -170,7 +247,7 @@ export async function extractActivityInfo(page) {
const roleSelector = pick('[id^="milo_role_selector_"]')
const roleNameText =
textOf('#role_name') ||
- asString(roleSelector?.selectedOptions?.[0]?.textContent || '')
+ asString(roleSelector instanceof HTMLSelectElement ? roleSelector.selectedOptions?.[0]?.textContent || '' : '')
const verifyImg = pickBest('[id^="milo_verifyImg_"]', (node) => node instanceof HTMLImageElement)
const popup = pick('#pop2')
const cdkeyInput = pickBest(
@@ -215,8 +292,8 @@ export async function extractActivityInfo(page) {
verify: {
visible: isVisible(verifyImg),
src: asString(verifyImg?.getAttribute('src')),
- naturalWidth: Number(verifyImg?.naturalWidth || 0),
- naturalHeight: Number(verifyImg?.naturalHeight || 0),
+ naturalWidth: verifyImg instanceof HTMLImageElement ? Number(verifyImg.naturalWidth || 0) : 0,
+ naturalHeight: verifyImg instanceof HTMLImageElement ? Number(verifyImg.naturalHeight || 0) : 0,
},
popup: {
visible: isVisible(popup),
diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md
index 0caa7fe7..08930aea 100644
--- a/docs/backend-typescript-migration-plan.md
+++ b/docs/backend-typescript-migration-plan.md
@@ -488,6 +488,14 @@
- `npm run typecheck`
- `npm run build`
- `npm test` 共 139 个用例通过
+104. 腾讯浏览器会话 activity 信息模块迁移到 `.ts`:
+ - `src/services/session/session-activity.ts`
+105. 活动角色 / 表单 / 验证码 / 弹窗信息结构、登录态 presentation 同步、角色渲染触发与轮询已显式类型化
+106. Docker 内验证通过:
+ - `src/services/session/session.test.js` 共 14 个用例通过
+ - `npm run typecheck`
+ - `npm run build`
+ - `npm test` 共 139 个用例通过
## 下一步建议