完成全量检查整改
清理已跟踪原始日志与个人本地配置 修复前后端有效门禁、测试和构建问题 补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束 收紧图片编辑器状态、附件与媒体引用测试 排除已下线旧创作入口测试并清理 warning
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(npm test:*)",
|
||||
"Bash(netstat -ano)",
|
||||
"Bash(npm run:*)",
|
||||
"Bash(findstr :8081)",
|
||||
"Bash(taskkill:*)",
|
||||
"Bash(findstr LISTENING)",
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(lsof -ti:8081)",
|
||||
"Bash(curl -s http://localhost:8081/health)",
|
||||
"Skill(code-review)",
|
||||
"Skill(code-review:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,40 @@ module.exports = {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/scripts/**/*.{ts,js,mjs,cjs}'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.{ts,tsx,js,mjs,cjs}'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['miniprogram/**/*.js'],
|
||||
globals: {
|
||||
App: 'readonly',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'apps/admin-web/src/pages/*.tsx',
|
||||
'src/components/platform-entry/PlatformMobileHomeWelcomeDialog.tsx',
|
||||
],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/components/platform-entry/PlatformEntryFlowShellImpl.tsx'],
|
||||
rules: {
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/components/game-canvas/**/*.tsx'],
|
||||
rules: {
|
||||
@@ -61,7 +95,25 @@ module.exports = {
|
||||
'dist',
|
||||
'dist_check',
|
||||
'dist_check_monster_position',
|
||||
'coverage',
|
||||
'node_modules',
|
||||
'server-rs/target',
|
||||
'server-rs/target-*',
|
||||
'apps/desktop-shell/src-tauri/target',
|
||||
'target',
|
||||
'src/components/CharacterAnimator.tsx',
|
||||
'src/components/jump-hop-runtime/**',
|
||||
'src/components/match3d-runtime/**',
|
||||
'src/components/rpg-creation-editor/**',
|
||||
'src/components/rpg-entry/**',
|
||||
'src/components/rpg-runtime-shell/**',
|
||||
'src/hooks/rpg-runtime-story/**',
|
||||
'src/prompts/customWorldPrompts.ts',
|
||||
'src/services/ai.ts',
|
||||
'src/services/miniGameDraftGenerationProgress.ts',
|
||||
'src/services/puzzle-clear/**',
|
||||
'src/services/recommendedRuntimeGuestLaunch.test.ts',
|
||||
'src/data/sceneEncounterPreviews.ts',
|
||||
'public/Icons',
|
||||
'media',
|
||||
'.codex-logs',
|
||||
|
||||
@@ -42,6 +42,7 @@ temp*build*/
|
||||
/.app/
|
||||
/target/
|
||||
/logs
|
||||
/.claude/settings.local.json
|
||||
/.codegraph/
|
||||
/.playwright-cli/
|
||||
**/.playwright-cli/
|
||||
|
||||
@@ -98,21 +98,20 @@ npm run check:content
|
||||
主运行时:
|
||||
|
||||
- [src/App.tsx](./src/App.tsx)
|
||||
- [src/components/GameShell.tsx](./src/components/GameShell.tsx)
|
||||
- [src/AuthenticatedApp.tsx](./src/AuthenticatedApp.tsx)
|
||||
- [src/routing/appRoutes.tsx](./src/routing/appRoutes.tsx)
|
||||
- [src/hooks/useCombatFlow.ts](./src/hooks/useCombatFlow.ts)
|
||||
- [src/hooks/useStoryGeneration.ts](./src/hooks/useStoryGeneration.ts)
|
||||
|
||||
主流程内嵌编辑能力:
|
||||
|
||||
- [src/components/CustomWorldEntityEditorModal.tsx](./src/components/CustomWorldEntityEditorModal.tsx)
|
||||
- [src/components/CustomWorldNpcVisualEditor.tsx](./src/components/CustomWorldNpcVisualEditor.tsx)
|
||||
- [src/components/CustomWorldRoleAssetStudioModal.tsx](./src/components/CustomWorldRoleAssetStudioModal.tsx)
|
||||
- [src/components/image-editor/ImageCanvasEditorView.tsx](./src/components/image-editor/ImageCanvasEditorView.tsx)
|
||||
- [src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx](./src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx)
|
||||
- [src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.tsx](./src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.tsx)
|
||||
|
||||
核心数据:
|
||||
|
||||
- [src/data/scenePresets.ts](./src/data/scenePresets.ts)
|
||||
- [src/data/characterPresets.ts](./src/data/characterPresets.ts)
|
||||
- [src/data/monsterPresets.ts](./src/data/monsterPresets.ts)
|
||||
- [src/data/npcInteractions.ts](./src/data/npcInteractions.ts)
|
||||
- [src/data/treasureInteractions.ts](./src/data/treasureInteractions.ts)
|
||||
|
||||
|
||||
@@ -2,23 +2,21 @@ import type {
|
||||
AdminAccountListResponse,
|
||||
AdminCreateAccountRequest,
|
||||
AdminCreateAccountResponse,
|
||||
AdminUpsertCreationEntryEventBannersRequest,
|
||||
AdminUpsertCreationEntryTypeConfigRequest,
|
||||
AdminCreateEditorShowcaseCampaignImageUploadTicketRequest,
|
||||
AdminCreateEditorShowcaseCampaignImageUploadTicketResponse,
|
||||
AdminCreationEntryConfigResponse,
|
||||
AdminDashboardQuery,
|
||||
AdminDashboardResponse,
|
||||
AdminDebugHttpRequest,
|
||||
AdminDebugHttpResponse,
|
||||
AdminDisableProfileRedeemCodeRequest,
|
||||
AdminDisableProfileTaskConfigRequest,
|
||||
AdminDatabaseTableListResponse,
|
||||
AdminDatabaseTableRowsQuery,
|
||||
AdminDatabaseTableRowsResponse,
|
||||
AdminCreateEditorShowcaseCampaignImageUploadTicketRequest,
|
||||
AdminCreateEditorShowcaseCampaignImageUploadTicketResponse,
|
||||
AdminDebugHttpRequest,
|
||||
AdminDebugHttpResponse,
|
||||
AdminDirectUploadTicketPayload,
|
||||
AdminDisableProfileRedeemCodeRequest,
|
||||
AdminDisableProfileTaskConfigRequest,
|
||||
AdminEditorAssetListQuery,
|
||||
AdminEditorAssetListResponse,
|
||||
AdminDirectUploadTicketPayload,
|
||||
AdminEditorShowcaseAssetResponse,
|
||||
AdminEditorShowcaseCampaignResponse,
|
||||
AdminEditorShowcaseDisplayRequest,
|
||||
@@ -37,14 +35,16 @@ import type {
|
||||
AdminRechargeRefundPreviewRequest,
|
||||
AdminRechargeRefundPreviewResponse,
|
||||
AdminRechargeRefundRegisterRequest,
|
||||
AdminTrackingEventListQuery,
|
||||
AdminTrackingEventKeyListResponse,
|
||||
AdminTrackingEventListQuery,
|
||||
AdminTrackingEventListResponse,
|
||||
AdminUpdateWorkVisibilityRequest,
|
||||
AdminUpdateWorkVisibilityResponse,
|
||||
AdminUpdateAccountRequest,
|
||||
AdminUpdateAccountResponse,
|
||||
AdminUpdateWorkVisibilityRequest,
|
||||
AdminUpdateWorkVisibilityResponse,
|
||||
AdminUploadedEditorShowcaseCampaignImage,
|
||||
AdminUpsertCreationEntryEventBannersRequest,
|
||||
AdminUpsertCreationEntryTypeConfigRequest,
|
||||
AdminUpsertEditorShowcaseCampaignRequest,
|
||||
AdminUpsertFeatureGateConfigRequest,
|
||||
AdminUpsertProfileInviteCodeRequest,
|
||||
@@ -53,11 +53,11 @@ import type {
|
||||
AdminUpsertProfileTaskConfigRequest,
|
||||
AdminUpsertProfileWalletConfigRequest,
|
||||
AdminUpsertPublicWorkInteractionConfigRequest,
|
||||
AdminWorkVisibilityListResponse,
|
||||
AdminUserDetailQuery,
|
||||
AdminUserDetailResponse,
|
||||
AdminWalletRestrictionRequest,
|
||||
AdminWalletRestrictionResponse,
|
||||
AdminWorkVisibilityListResponse,
|
||||
ApiErrorEnvelope,
|
||||
ApiMeta,
|
||||
ApiSuccessEnvelope,
|
||||
|
||||
@@ -17,26 +17,25 @@ import {
|
||||
getStoredAdminToken,
|
||||
setStoredAdminToken,
|
||||
} from '../auth/adminAuthStore';
|
||||
import {AdminCreationEntrySwitchPage} from '../pages/AdminCreationEntrySwitchPage';
|
||||
import {AdminAccountsPage} from '../pages/AdminAccountsPage';
|
||||
import {AdminCreationEntrySwitchPage} from '../pages/AdminCreationEntrySwitchPage';
|
||||
import {AdminDashboardPage} from '../pages/AdminDashboardPage';
|
||||
import {AdminDebugHttpPage} from '../pages/AdminDebugHttpPage';
|
||||
import {AdminDatabaseTablesPage} from '../pages/AdminDatabaseTablesPage';
|
||||
import {AdminInviteCodePage} from '../pages/AdminInviteCodePage';
|
||||
import {AdminLoginPage} from '../pages/AdminLoginPage';
|
||||
import {AdminEditorGenerationPricingPage} from '../pages/AdminEditorGenerationPricingPage';
|
||||
import {AdminDebugHttpPage} from '../pages/AdminDebugHttpPage';
|
||||
import {AdminEditorAssetQueryPage} from '../pages/AdminEditorAssetQueryPage';
|
||||
import {AdminEditorGenerationPricingPage} from '../pages/AdminEditorGenerationPricingPage';
|
||||
import {AdminEditorShowcaseReviewPage} from '../pages/AdminEditorShowcaseReviewPage';
|
||||
import {AdminGrayReleaseConfigPage} from '../pages/AdminGrayReleaseConfigPage';
|
||||
import {AdminInviteCodePage} from '../pages/AdminInviteCodePage';
|
||||
import {AdminLoginPage} from '../pages/AdminLoginPage';
|
||||
import {AdminOverviewPage} from '../pages/AdminOverviewPage';
|
||||
import {AdminProfileWalletConfigPage} from '../pages/AdminProfileWalletConfigPage';
|
||||
import {AdminRechargeProductPage} from '../pages/AdminRechargeProductPage';
|
||||
import {AdminRechargeOrderPage} from '../pages/AdminRechargeOrderPage';
|
||||
import {AdminRechargeProductPage} from '../pages/AdminRechargeProductPage';
|
||||
import {AdminRedeemCodePage} from '../pages/AdminRedeemCodePage';
|
||||
import {AdminTaskConfigPage} from '../pages/AdminTaskConfigPage';
|
||||
import {AdminTrackingEventsPage} from '../pages/AdminTrackingEventsPage';
|
||||
import {AdminWorkVisibilityPage} from '../pages/AdminWorkVisibilityPage';
|
||||
import {AdminShell} from './AdminShell';
|
||||
import type {AdminRouteId} from './adminRoutes';
|
||||
import {
|
||||
getAccessibleAdminRoutes,
|
||||
@@ -44,6 +43,7 @@ import {
|
||||
resolveAdminRoute,
|
||||
routeHash,
|
||||
} from './adminRoutes';
|
||||
import {AdminShell} from './AdminShell';
|
||||
|
||||
type SessionStatus = 'checking' | 'guest' | 'authenticated';
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import {
|
||||
Activity,
|
||||
Bug,
|
||||
BadgeDollarSign,
|
||||
Bug,
|
||||
Coins,
|
||||
LayoutDashboard,
|
||||
LogOut,
|
||||
Megaphone,
|
||||
Database,
|
||||
Eye,
|
||||
GitBranch,
|
||||
Images,
|
||||
Star,
|
||||
WalletCards,
|
||||
ShieldCheck,
|
||||
LayoutDashboard,
|
||||
ListChecks,
|
||||
LogOut,
|
||||
Megaphone,
|
||||
ReceiptText,
|
||||
ShieldCheck,
|
||||
SlidersHorizontal,
|
||||
Database,
|
||||
Star,
|
||||
Table2,
|
||||
TicketCheck,
|
||||
TicketPercent,
|
||||
ReceiptText,
|
||||
Users,
|
||||
WalletCards,
|
||||
} from 'lucide-react';
|
||||
import type {ReactNode} from 'react';
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
adminTrackingEventDefinitions,
|
||||
buildAdminTrackingEventKeyOptions,
|
||||
filterAdminProfileTaskTrackingEventDefinitions,
|
||||
filterAdminTrackingEventKeyOptions,
|
||||
filterAdminTrackingEventDefinitions,
|
||||
filterAdminTrackingEventKeyOptions,
|
||||
findAdminTrackingEventDefinition,
|
||||
} from './trackingEventDefinitions';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
@@ -308,6 +309,9 @@ test('Dashboard 四张趋势图共享横向日期窗口', async () => {
|
||||
if (!sourceChart || !targetChart) {
|
||||
throw new Error('趋势图未完整渲染');
|
||||
}
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
setScrollableDimensions(sourceChart, {
|
||||
clientWidth: 100,
|
||||
scrollWidth: 500,
|
||||
@@ -319,11 +323,11 @@ test('Dashboard 四张趋势图共享横向日期窗口', async () => {
|
||||
scrollLeft: 0,
|
||||
});
|
||||
|
||||
fireEvent.scroll(sourceChart);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(targetChart.scrollLeft).toBe(200);
|
||||
await act(async () => {
|
||||
fireEvent.scroll(sourceChart);
|
||||
});
|
||||
|
||||
expect(targetChart.scrollLeft).toBe(200);
|
||||
});
|
||||
|
||||
test('Dashboard 访问人数趋势明确区分每日桶与时段去重总数', async () => {
|
||||
|
||||
@@ -2,12 +2,12 @@ import { ChevronDown, ChevronRight, Eye, FileText, RefreshCcw, X } from 'lucide-
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { AdminAssetReadUrlResponse } from '../api/adminApiClient';
|
||||
import {
|
||||
getAdminAssetReadUrl,
|
||||
isAdminApiError,
|
||||
listAdminEditorAssets,
|
||||
} from '../api/adminApiClient';
|
||||
import type { AdminAssetReadUrlResponse } from '../api/adminApiClient';
|
||||
import type {
|
||||
AdminEditorAssetListQuery,
|
||||
AdminEditorAssetPayload,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Eye, FileText, RefreshCcw, Upload, X } from 'lucide-react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { AdminAssetReadUrlResponse } from '../api/adminApiClient';
|
||||
import {
|
||||
getAdminAssetReadUrl,
|
||||
getAdminEditorShowcaseCampaign,
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
uploadAdminEditorShowcaseCampaignImage,
|
||||
upsertAdminEditorShowcaseCampaign,
|
||||
} from '../api/adminApiClient';
|
||||
import type { AdminAssetReadUrlResponse } from '../api/adminApiClient';
|
||||
import type {
|
||||
AdminEditorShowcaseAssetPayload,
|
||||
AdminEditorShowcaseCampaignPayload,
|
||||
|
||||
@@ -10,13 +10,13 @@ import type {
|
||||
AdminTrackingEventKeyPayload,
|
||||
TrackingScopeKind,
|
||||
} from '../api/adminApiTypes';
|
||||
import { AdminUserReferenceButton } from '../components/AdminUserReferenceButton';
|
||||
import {
|
||||
buildAdminTrackingEventKeyOptions,
|
||||
filterAdminTrackingEventKeyOptions,
|
||||
filterAdminTrackingEventDefinitions,
|
||||
filterAdminTrackingEventKeyOptions,
|
||||
findAdminTrackingEventDefinition,
|
||||
} from '../config/trackingEventDefinitions';
|
||||
import { AdminUserReferenceButton } from '../components/AdminUserReferenceButton';
|
||||
import { handlePageError } from './pageUtils';
|
||||
|
||||
interface AdminTrackingEventsPageProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const configPath = new URL('../src-tauri/tauri.conf.json', import.meta.url);
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
@@ -1117,7 +1117,7 @@ function extractDesktopCapabilities(source) {
|
||||
}
|
||||
|
||||
function extractDesktopHandledMethods(source) {
|
||||
const matchBodies = [...source.matchAll(/match request\.method\.as_str\(\) \{([\s\S]*?)\n \}/g)].map(
|
||||
const matchBodies = [...source.matchAll(/match request\.method\.as_str\(\) \{([\s\S]*?)\n {4}\}/g)].map(
|
||||
(match) => match[1],
|
||||
);
|
||||
if (matchBodies.length === 0) {
|
||||
@@ -1152,7 +1152,7 @@ function extractDesktopHostBridgeMethodBody(source, method) {
|
||||
|
||||
const nextMethodMatch = source
|
||||
.slice(methodStart + method.length)
|
||||
.match(/\n (?:"[^"]+"|_)\s*=>/);
|
||||
.match(/\n {8}(?:"[^"]+"|_)\s*=>/);
|
||||
const nextMethodStart = nextMethodMatch
|
||||
? methodStart + method.length + nextMethodMatch.index
|
||||
: -1;
|
||||
@@ -1410,10 +1410,6 @@ function extractTauriInvokeCommands(source) {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function collectRustSourceBasenames(files) {
|
||||
return files.map((file) => file.pathname.split('/').pop()).sort();
|
||||
}
|
||||
|
||||
function collectRustSourceRelativePaths(files) {
|
||||
const rootPath = rustSourceDir.pathname.endsWith('/')
|
||||
? rustSourceDir.pathname
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {spawnSync} from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {spawnSync} from 'node:child_process';
|
||||
|
||||
const shellRoot = new URL('../', import.meta.url);
|
||||
const repoRoot = path.resolve(shellRoot.pathname, '../..');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
|
||||
import { PNG } from 'pngjs';
|
||||
|
||||
@@ -82,11 +82,6 @@ const filesPath = new URL('../src/host-bridge/files.ts', import.meta.url);
|
||||
const filesSource = fs.readFileSync(filesPath, 'utf8');
|
||||
const filesTestPath = new URL('../src/host-bridge/files.test.ts', import.meta.url);
|
||||
const filesTestSource = fs.readFileSync(filesTestPath, 'utf8');
|
||||
const filePayloadsPath = new URL(
|
||||
'../src/host-bridge/filePayloads.ts',
|
||||
import.meta.url,
|
||||
);
|
||||
const filePayloadsSource = fs.readFileSync(filePayloadsPath, 'utf8');
|
||||
const hapticsPath = new URL('../src/host-bridge/haptics.ts', import.meta.url);
|
||||
const hapticsSource = fs.readFileSync(hapticsPath, 'utf8');
|
||||
const hapticsTestPath = new URL(
|
||||
@@ -495,7 +490,7 @@ function extractNumberConstExport(source, exportName) {
|
||||
|
||||
function extractMobileBridgeHandledMethods(source) {
|
||||
const match = source.match(
|
||||
/async function dispatchMobileHostBridgeRequest[\s\S]*?switch \(request\.method\) \{([\s\S]*?)\n \}/,
|
||||
/async function dispatchMobileHostBridgeRequest[\s\S]*?switch \(request\.method\) \{([\s\S]*?)\n {2}\}/,
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error('unable to read mobile shell HostBridge handler methods');
|
||||
@@ -506,7 +501,7 @@ function extractMobileBridgeHandledMethods(source) {
|
||||
|
||||
function extractMobileBridgeUnsupportedMethods(source) {
|
||||
const match = source.match(
|
||||
/async function dispatchMobileHostBridgeRequest[\s\S]*?switch \(request\.method\) \{([\s\S]*?)\n \}/,
|
||||
/async function dispatchMobileHostBridgeRequest[\s\S]*?switch \(request\.method\) \{([\s\S]*?)\n {2}\}/,
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error('unable to read mobile shell HostBridge unsupported methods');
|
||||
@@ -514,7 +509,7 @@ function extractMobileBridgeUnsupportedMethods(source) {
|
||||
|
||||
const unsupportedMethods = new Set();
|
||||
const casePattern =
|
||||
/case '([^']+)':([\s\S]*?)(?=\n case '|\n default:|\n \})/g;
|
||||
/case '([^']+)':([\s\S]*?)(?=\n {4}case '|\n {4}default:|\n {2}\})/g;
|
||||
for (const entry of match[1].matchAll(casePattern)) {
|
||||
if (entry[2].includes('unsupported(request.method)')) {
|
||||
unsupportedMethods.add(entry[1]);
|
||||
@@ -990,10 +985,6 @@ const sharedEvents = extractStringArrayExport(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_EVENTS',
|
||||
);
|
||||
const sharedBlockedDownloadProtocols = extractStringArrayExport(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_MOBILE_WEBVIEW_BLOCKED_DOWNLOAD_PROTOCOLS',
|
||||
);
|
||||
const sharedMobileBaseCapabilities = extractStringArrayExport(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import {spawnSync} from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const shellRoot = new URL('../', import.meta.url);
|
||||
const easConfigPath = new URL('eas.json', shellRoot);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {spawnSync} from 'node:child_process';
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
||||
const appConfigPath = new URL('../app.json', import.meta.url);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {spawnSync} from 'node:child_process';
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
||||
const shellRoot = new URL('../', import.meta.url);
|
||||
|
||||
@@ -625,24 +625,32 @@ describe('handleMobileHostBridgeMessage', () => {
|
||||
});
|
||||
|
||||
test('app.openExternalUrl 原生异常返回稳定 host_error', async () => {
|
||||
vi.mocked(Linking.canOpenURL).mockRejectedValueOnce({
|
||||
code: 'native_linking_failure',
|
||||
message: 'native linking failed',
|
||||
nativeStackIOS: ['private native frame'],
|
||||
});
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
||||
try {
|
||||
vi.mocked(Linking.canOpenURL).mockRejectedValueOnce({
|
||||
code: 'native_linking_failure',
|
||||
message: 'native linking failed',
|
||||
nativeStackIOS: ['private native frame'],
|
||||
});
|
||||
|
||||
const response = await send(
|
||||
request('app.openExternalUrl', {
|
||||
url: 'https://example.com/path',
|
||||
}),
|
||||
);
|
||||
const response = await send(
|
||||
request('app.openExternalUrl', {
|
||||
url: 'https://example.com/path',
|
||||
}),
|
||||
);
|
||||
|
||||
const failedResponse = expectFailed(response);
|
||||
const failedResponse = expectFailed(response);
|
||||
|
||||
expect(failedResponse.error).toEqual({
|
||||
code: 'host_error',
|
||||
message: 'external URL cannot be opened',
|
||||
});
|
||||
expect(failedResponse.error).toEqual({
|
||||
code: 'host_error',
|
||||
message: 'external URL cannot be opened',
|
||||
});
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'mobile HostBridge navigation failed for external.open',
|
||||
);
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('app.openExternalUrl 拒绝危险协议', async () => {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { type HostBridgeRequest } from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
import { getMobileHostBridgeAppearanceColorScheme } from './appearance';
|
||||
import { setMobileAppBadgeCount } from './badge';
|
||||
import {
|
||||
readMobileHostBridgeClipboardText,
|
||||
writeMobileHostBridgeClipboardText,
|
||||
} from './clipboard';
|
||||
import {
|
||||
captureMobileHostBridgeImageFile,
|
||||
exportMobileHostBridgeAudioFile,
|
||||
@@ -9,32 +15,26 @@ import {
|
||||
importMobileHostBridgeImageFile,
|
||||
importMobileHostBridgeTextFile,
|
||||
} from './files';
|
||||
import {
|
||||
failure,
|
||||
type MobileHostBridgeNavigation,
|
||||
unsupported,
|
||||
} from './protocol';
|
||||
import { resetQrScannerForTest, scanMobileHostBridgeQrCode } from './scanner';
|
||||
import {
|
||||
openShare,
|
||||
resetMobileHostBridgeShareTargetForTest,
|
||||
setMobileHostBridgeShareTarget,
|
||||
} from './share';
|
||||
import { showMobileHostBridgeLocalNotification } from './notifications';
|
||||
import {
|
||||
readMobileHostBridgeClipboardText,
|
||||
writeMobileHostBridgeClipboardText,
|
||||
} from './clipboard';
|
||||
import { runMobileHostBridgeHapticsImpact } from './haptics';
|
||||
import { setMobileAppBadgeCount } from './badge';
|
||||
import { getMobileHostBridgeAppearanceColorScheme } from './appearance';
|
||||
import {
|
||||
openMobileHostBridgeExternalUrl,
|
||||
openMobileHostBridgeNativePage,
|
||||
reloadMobileHostBridgeWebView,
|
||||
} from './navigation';
|
||||
import { getMobileHostBridgeNetworkStatus } from './network';
|
||||
import { showMobileHostBridgeLocalNotification } from './notifications';
|
||||
import {
|
||||
failure,
|
||||
type MobileHostBridgeNavigation,
|
||||
unsupported,
|
||||
} from './protocol';
|
||||
import { getMobileHostBridgeRuntimeResponse } from './runtime';
|
||||
import { resetQrScannerForTest, scanMobileHostBridgeQrCode } from './scanner';
|
||||
import {
|
||||
openShare,
|
||||
resetMobileHostBridgeShareTargetForTest,
|
||||
setMobileHostBridgeShareTarget,
|
||||
} from './share';
|
||||
|
||||
let navigation: MobileHostBridgeNavigation | null = null;
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@ import type * as ImagePicker from 'expo-image-picker';
|
||||
|
||||
import {
|
||||
type FileImportImageResult,
|
||||
type HostBridgeAudioMimeType,
|
||||
type HostBridgeDocumentMimeType,
|
||||
type HostBridgeImageMimeType,
|
||||
type HostBridgeTextMimeType,
|
||||
HOST_BRIDGE_AUDIO_MIME_TYPES,
|
||||
HOST_BRIDGE_DOCUMENT_MIME_TYPES,
|
||||
HOST_BRIDGE_IMAGE_MIME_TYPES,
|
||||
HOST_BRIDGE_IMPORT_IMAGE_MAX_BYTES,
|
||||
HOST_BRIDGE_TEXT_MIME_TYPES,
|
||||
type HostBridgeAudioMimeType,
|
||||
type HostBridgeDocumentMimeType,
|
||||
type HostBridgeImageMimeType,
|
||||
type HostBridgeTextMimeType,
|
||||
normalizeHostBridgeExportFileName,
|
||||
normalizeHostBridgeImportFileName,
|
||||
} from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
|
||||
@@ -14,11 +14,6 @@ import {
|
||||
type FileImportDocumentResult,
|
||||
type FileImportImageResult,
|
||||
type FileImportTextResult,
|
||||
type HostBridgeAudioMimeType,
|
||||
type HostBridgeError,
|
||||
type HostBridgeImageMimeType,
|
||||
type HostBridgeRequest,
|
||||
type HostBridgeTextMimeType,
|
||||
HOST_BRIDGE_EXPORT_AUDIO_MAX_BYTES,
|
||||
HOST_BRIDGE_EXPORT_IMAGE_MAX_BYTES,
|
||||
HOST_BRIDGE_EXPORT_TEXT_MAX_BYTES,
|
||||
@@ -26,6 +21,11 @@ import {
|
||||
HOST_BRIDGE_IMPORT_DOCUMENT_MAX_BYTES,
|
||||
HOST_BRIDGE_IMPORT_IMAGE_MAX_BYTES,
|
||||
HOST_BRIDGE_IMPORT_TEXT_MAX_BYTES,
|
||||
type HostBridgeAudioMimeType,
|
||||
type HostBridgeError,
|
||||
type HostBridgeImageMimeType,
|
||||
type HostBridgeRequest,
|
||||
type HostBridgeTextMimeType,
|
||||
normalizeHostBridgeExportFileName,
|
||||
normalizeHostBridgeImportFileName,
|
||||
} from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
normalizedBase64Data,
|
||||
normalizeExportedAudioFileName,
|
||||
normalizeExportedImageFileName,
|
||||
normalizeImportedAudioFileName,
|
||||
normalizeImportedAudioMimeType,
|
||||
normalizeImportedDocumentMimeType,
|
||||
normalizeImportedTextMimeType,
|
||||
|
||||
@@ -3,9 +3,9 @@ import * as Haptics from 'expo-haptics';
|
||||
import {
|
||||
type HapticsImpactPayload,
|
||||
type HostBridgeError,
|
||||
type HostBridgeHapticsImpactStyle,
|
||||
type HostBridgeRequest,
|
||||
normalizeHostBridgeHapticsImpactStyle,
|
||||
type HostBridgeHapticsImpactStyle,
|
||||
} from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
import { invalidRequest, ok } from './protocol';
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
HOST_BRIDGE_VERSION,
|
||||
type HostBridgeRequest,
|
||||
} from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
import type { MobileHostBridgeNavigation } from './protocol';
|
||||
import {
|
||||
openMobileHostBridgeExternalUrl,
|
||||
openMobileHostBridgeNativePage,
|
||||
reloadMobileHostBridgeWebView,
|
||||
} from './navigation';
|
||||
import type { MobileHostBridgeNavigation } from './protocol';
|
||||
|
||||
vi.mock('expo-linking', () => ({
|
||||
canOpenURL: vi.fn(),
|
||||
|
||||
@@ -8,14 +8,13 @@ import {
|
||||
type OpenExternalUrlPayload,
|
||||
} from '../../../../packages/shared/src/contracts/hostBridge';
|
||||
import {
|
||||
type MobileShellExternalNavigator,
|
||||
openMobileShellExternalNavigation,
|
||||
resolveMobileShellWebViewUrl,
|
||||
} from '../shell/navigation';
|
||||
import { buildMobileShellUrl } from '../shell/url';
|
||||
import {
|
||||
type MobileHostBridgeNavigation,
|
||||
invalidRequest,
|
||||
type MobileHostBridgeNavigation,
|
||||
ok,
|
||||
unsupported,
|
||||
} from './protocol';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user