fix(native): new lint warnings and the MapLibre protocol type after the dev merge

Awaits that sat in loops in the native-auth tests run together, the hand-off hook marks its navigations as ignored, the native tests drop their non-null assertions, and the offline protocol answers with the data type MapLibre 6.15 expects.
This commit is contained in:
Maurice
2026-10-25 17:16:02 +01:00
parent 8b6a65023f
commit a3b04887a1
6 changed files with 17 additions and 15 deletions
+3 -3
View File
@@ -43,11 +43,11 @@ describe('signInThroughBrowser', () => {
await signInThroughBrowser('oidc')
const opened = new URL(vi.mocked(TrekShell.authenticate).mock.calls[0]![0].url)
const opened = new URL(vi.mocked(TrekShell.authenticate).mock.calls[0][0].url)
expect(opened.pathname).toBe('/native-handoff')
expect(opened.searchParams.get('start')).toBe('oidc')
expect(vi.mocked(TrekShell.authenticate).mock.calls[0]![0].callbackScheme).toBe('com.liketrek.trek')
const { code, verifier } = vi.mocked(nativeAuthApi.exchange).mock.calls[0]![0]
expect(vi.mocked(TrekShell.authenticate).mock.calls[0][0].callbackScheme).toBe('com.liketrek.trek')
const { code, verifier } = vi.mocked(nativeAuthApi.exchange).mock.calls[0][0]
expect(code).toBe('abc')
expect(bytesToHex(sha256(new TextEncoder().encode(verifier)))).toBe(opened.searchParams.get('challenge'))
})
+2 -1
View File
@@ -18,7 +18,8 @@ vi.mock('../sync/networkMode', () => ({ isEffectivelyOffline: vi.fn() }))
function backButtonHandler(): (event: { canGoBack: boolean }) => void {
const call = vi.mocked(App.addListener).mock.calls.find(([name]) => name === 'backButton')
return call![1] as unknown as (event: { canGoBack: boolean }) => void
if (!call) throw new Error('no backButton listener')
return call[1] as unknown as (event: { canGoBack: boolean }) => void
}
beforeEach(() => {
+4 -2
View File
@@ -37,6 +37,8 @@ export function offlineStyle<T>(style: T): T {
}
type Doc = Record<string, unknown>
/** What a protocol handler may hand MapLibre back. */
type ProtocolData = Awaited<ReturnType<AddProtocolAction>>['data']
const isDoc = (value: unknown): value is Doc => typeof value === 'object' && value !== null && !Array.isArray(value)
const rewriteList = (value: unknown): unknown => (Array.isArray(value) ? value.map((v) => (typeof v === 'string' ? toOfflineUrl(v) : v)) : value)
@@ -90,11 +92,11 @@ async function load(url: string, networkFirst: boolean, signal: AbortSignal): Pr
throw new Error(`Not available offline: ${url}`)
}
export const offlineProtocol: AddProtocolAction = async (params: RequestParameters, abortController: AbortController): Promise<GetResourceResponse<unknown>> => {
export const offlineProtocol: AddProtocolAction = async (params: RequestParameters, abortController: AbortController): Promise<GetResourceResponse<ProtocolData>> => {
const url = fromOfflineUrl(params.url)
const response = await load(url, params.type === 'json', abortController.signal)
if (!response.ok) throw new Error(`HTTP ${response.status} for ${url}`)
if (params.type === 'json') return { data: rewriteDocument(await response.json()) }
if (params.type === 'json') return { data: rewriteDocument(await response.json()) as ProtocolData }
if (params.type === 'string') return { data: await response.text() }
return { data: await response.arrayBuffer() }
}
@@ -36,7 +36,7 @@ export function useNativeHandoff() {
window.location.replace('/api/auth/oidc/login?remember=1')
return
}
navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true })
void navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true })
}, [valid, isLoading, isAuthenticated, params, returnPath, navigate])
const openApp = async (): Promise<void> => {
@@ -51,7 +51,7 @@ export function useNativeHandoff() {
const switchAccount = async (): Promise<void> => {
await logout()
navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true })
void navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true })
}
return {
+1 -3
View File
@@ -129,9 +129,7 @@ describe('Native sign-in hand-off e2e', () => {
});
it('NATIVE-E2E-005: rate-limits the exchange per ip', async () => {
for (let i = 0; i < 20; i++) {
await exchange({ code: `nope-${i}`, verifier });
}
await Promise.all(Array.from({ length: 20 }, (_, i) => exchange({ code: `nope-${i}`, verifier })));
const blocked = await exchange({ code: 'nope', verifier });
expect(blocked.status).toBe(429);
});
@@ -130,16 +130,17 @@ describe('InMemoryNativeHandoffStore', () => {
it('NATIVE-STORE-001: holds at most its cap of live codes', async () => {
const store = new InMemoryNativeHandoffStore();
const later = Date.now() + 60_000;
for (let i = 0; i < MAX_PENDING_HANDOFFS; i++) {
expect(await store.put(`c${i}`, entry(later))).toBe(true);
}
const stored = await Promise.all(
Array.from({ length: MAX_PENDING_HANDOFFS }, (_, i) => store.put(`c${i}`, entry(later))),
);
expect(stored.every(Boolean)).toBe(true);
expect(await store.put('one-too-many', entry(later))).toBe(false);
});
it('NATIVE-STORE-002: expired codes are dropped on the next write and make room', async () => {
const store = new InMemoryNativeHandoffStore();
const past = Date.now() - 1;
for (let i = 0; i < MAX_PENDING_HANDOFFS; i++) await store.put(`c${i}`, entry(past));
await Promise.all(Array.from({ length: MAX_PENDING_HANDOFFS }, (_, i) => store.put(`c${i}`, entry(past))));
expect(await store.put('fresh', entry(Date.now() + 60_000))).toBe(true);
expect(await store.take('c0')).toBeNull();