feat(core): improve mobile perf (#15317)

#### PR Dependency Tree


* **PR #15317** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Virtualized mobile navigation with shell navigation and interactive
swipe menus; coordinated mobile back handling with interactive
phases/state restoration.
* Added shared auth request proxy and message-port based token handling
across mobile and worker flows.
* **Bug Fixes**
  * Hydrated remote worker error stacks for calls and observable errors.
* Improved SQLite FTS/indexer and nbstore optional text handling;
refined docs-search ref parsing and notification loading/retry.
* **Refactor / UX**
* Modal focus-preservation and pointer behavior updates; improved mobile
menu controls and back gesture plugins.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-07-23 00:23:21 +08:00
committed by GitHub
parent 02e75862cc
commit 1d36e2e4b2
160 changed files with 6660 additions and 1890 deletions
@@ -11,6 +11,7 @@ interface TestOps extends OpSchema {
bin: [Uint8Array, Uint8Array];
sub: [Uint8Array, number];
init: [{ fastText?: boolean } | undefined, { ok: true }];
any: [undefined, unknown];
}
declare module 'vitest' {
@@ -242,6 +243,41 @@ describe('op client', () => {
sub.unsubscribe();
});
it('hydrates worker stacks for calls and observables', async ctx => {
const marker = 'WORKER_STACK_MARKER';
const remoteError = {
name: 'TypeError',
message: marker,
stacktrace: `TypeError: ${marker}\n at workerTask (worker.ts:42:7)`,
} as unknown as Error;
const call = ctx.producer.call('any', undefined);
ctx.handlers.return({
type: 'return',
id: 'any:1',
error: remoteError,
});
await expect(call).rejects.toMatchObject({
name: 'TypeError',
message: marker,
stack: expect.stringContaining('worker.ts:42:7'),
});
const observableError = new Promise<unknown>(resolve => {
ctx.producer.ob$('any').subscribe({ error: resolve });
});
ctx.handlers.error({
type: 'error',
id: 'any:2',
error: remoteError,
});
await expect(observableError).resolves.toMatchObject({
name: 'TypeError',
message: marker,
stack: expect.stringContaining('worker.ts:42:7'),
});
});
it('should unsubscribe subscription op', ctx => {
const sub = ctx.producer.ob$('sub', new Uint8Array([1, 2, 3])).subscribe({
next: vi.fn(),
@@ -101,6 +101,44 @@ describe('op consumer', () => {
expect(message).toContain('"code":"E_PANIC"');
});
it('serializes worker stacks for calls and observables', async ctx => {
for (const kind of ['call', 'observable'] as const) {
ctx.postMessage.mockClear();
const marker = 'WORKER_STACK_MARKER';
const error = new TypeError(marker);
error.stack = `TypeError: ${marker}\n at workerTask (worker.ts:42:7)`;
ctx.consumer.register('any', () => {
if (kind === 'observable') {
return new Observable(observer => observer.error(error));
}
throw error;
});
if (kind === 'observable') {
ctx.handlers.subscribe({
type: 'subscribe',
id: 'any:1',
name: 'any',
payload: undefined,
});
} else {
ctx.handlers.call({
type: 'call',
id: 'any:1',
name: 'any',
payload: undefined,
});
}
await vi.advanceTimersToNextTimerAsync();
expect(ctx.postMessage.mock.calls[0][0].error).toMatchObject({
name: 'TypeError',
message: marker,
stacktrace: expect.stringContaining('worker.ts:42:7'),
});
}
});
it('should handle cancel message', async ctx => {
ctx.consumer.register('add', ({ a, b }, { signal }) => {
const { reject, resolve, promise } = Promise.withResolvers<number>();
+14 -2
View File
@@ -32,6 +32,18 @@ export interface OpClientOptions {
timeout?: number;
}
function hydrateRemoteError(error: Error) {
const hydrated = Object.assign(new Error(), error);
if (
'stacktrace' in error &&
typeof error.stacktrace === 'string' &&
error.stacktrace.length > 0
) {
hydrated.stack = error.stacktrace;
}
return hydrated;
}
export class OpClient<Ops extends OpSchema> extends AutoMessageHandler {
private readonly callIds = new Map<OpNames<Ops>, number>();
private readonly pendingCalls = new Map<string, PendingCall>();
@@ -61,7 +73,7 @@ export class OpClient<Ops extends OpSchema> extends AutoMessageHandler {
}
if ('error' in msg) {
pending.reject(Object.assign(new Error(), msg.error));
pending.reject(hydrateRemoteError(msg.error));
} else {
pending.resolve(msg.data);
}
@@ -86,7 +98,7 @@ export class OpClient<Ops extends OpSchema> extends AutoMessageHandler {
return;
}
ob.error(Object.assign(new Error(), msg.error));
ob.error(hydrateRemoteError(msg.error));
};
private readonly handleSubscriptionCompleteMessage: MessageHandlers['complete'] =