Compare commits

...

2 Commits

Author SHA1 Message Date
duffyduck 5b28a065c0 release: bump version to 0.1.3.7 2026-05-14 16:08:30 +02:00
duffyduck e74e1eaf70 fix(app): URLSearchParams crasht in Hermes — durch Mini-Query-Builder ersetzt
Inbox-Crash gefunden via App-Crash-Reporter (commit 21a315c):

  "URLSearchParams.set is not implemented"
  at MemoryBrowser → brainApi.listMemories

React Native's Hermes-Polyfill kennt zwar new URLSearchParams() aber
nicht die .set()-Methode darauf. Pickup-Bug — auf iOS / aelteren
Versionen geht's, Stefan's Android-Build crasht.

Fix: kleine _qs()-Helper im brainApi.ts der einen Query-String aus
einem flachen Object baut, ohne URLSearchParams:
  _qs({q:'cessna', k:5, type:'fact'}) → "?q=cessna&k=5&type=fact"

Plus: undefined/null/empty Werte werden ausgelassen — saubererer als
URLSearchParams.set wo man manuell prefilten muss.

ErrorBoundary aus 21a315c hat den Crash sauber abgefangen, statt der
App-Tot war ne Error-Box im Inbox-Modal mit der vollen Stack-Trace.
Stefan konnte den Log via tools/fetch-app-logs.sh holen ohne ADB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:06:21 +02:00
3 changed files with 32 additions and 18 deletions
+2 -2
View File
@@ -79,8 +79,8 @@ android {
applicationId "com.ariacockpit" applicationId "com.ariacockpit"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 10306 versionCode 10307
versionName "0.1.3.6" versionName "0.1.3.7"
// Fallback fuer Libraries mit Product Flavors // Fallback fuer Libraries mit Product Flavors
missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'react-native-camera', 'general'
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "aria-cockpit", "name": "aria-cockpit",
"version": "0.1.3.6", "version": "0.1.3.7",
"private": true, "private": true,
"scripts": { "scripts": {
"android": "react-native run-android", "android": "react-native run-android",
+29 -15
View File
@@ -54,6 +54,18 @@ function _newRequestId(): string {
return `brain_${Date.now().toString(36)}_${_nextId}`; return `brain_${Date.now().toString(36)}_${_nextId}`;
} }
/** Mini-Query-String-Builder ohne URLSearchParams (Hermes-Polyfill kennt
* kein URLSearchParams.set, crasht). Akzeptiert object mit string/number/
* bool-Values; undefined/null/leere Strings werden ausgelassen. */
function _qs(params: Record<string, unknown>): string {
const parts: string[] = [];
for (const [k, v] of Object.entries(params)) {
if (v === undefined || v === null || v === '') continue;
parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
}
return parts.length ? `?${parts.join('&')}` : '';
}
interface SendOpts { interface SendOpts {
method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'; method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
body?: AnyJson; body?: AnyJson;
@@ -119,29 +131,31 @@ export const brainApi = {
/** Liste aller Memories, optional nach Type gefiltert. */ /** Liste aller Memories, optional nach Type gefiltert. */
listMemories(opts: { type?: string; limit?: number } = {}): Promise<Memory[]> { listMemories(opts: { type?: string; limit?: number } = {}): Promise<Memory[]> {
const qs = new URLSearchParams(); const qs = _qs({ type: opts.type, limit: opts.limit || 500 });
if (opts.type) qs.set('type', opts.type); return _send(`/memory/list${qs}`);
qs.set('limit', String(opts.limit || 500));
return _send(`/memory/list?${qs.toString()}`);
}, },
/** Volltext-Substring-Suche. */ /** Volltext-Substring-Suche. */
searchText(q: string, opts: { type?: string; includePinned?: boolean; k?: number } = {}): Promise<Memory[]> { searchText(q: string, opts: { type?: string; includePinned?: boolean; k?: number } = {}): Promise<Memory[]> {
const qs = new URLSearchParams({ q }); const qs = _qs({
if (opts.type) qs.set('type', opts.type); q,
qs.set('include_pinned', String(opts.includePinned !== false)); type: opts.type,
qs.set('k', String(opts.k || 50)); include_pinned: opts.includePinned !== false,
return _send(`/memory/search-text?${qs.toString()}`); k: opts.k || 50,
});
return _send(`/memory/search-text${qs}`);
}, },
/** Semantische Suche (Embedder). */ /** Semantische Suche (Embedder). */
searchSemantic(q: string, opts: { type?: string; includePinned?: boolean; k?: number; threshold?: number } = {}): Promise<Memory[]> { searchSemantic(q: string, opts: { type?: string; includePinned?: boolean; k?: number; threshold?: number } = {}): Promise<Memory[]> {
const qs = new URLSearchParams({ q }); const qs = _qs({
if (opts.type) qs.set('type', opts.type); q,
qs.set('include_pinned', String(opts.includePinned !== false)); type: opts.type,
qs.set('k', String(opts.k || 10)); include_pinned: opts.includePinned !== false,
qs.set('score_threshold', String(opts.threshold ?? 0.30)); k: opts.k || 10,
return _send(`/memory/search?${qs.toString()}`); score_threshold: opts.threshold ?? 0.30,
});
return _send(`/memory/search${qs}`);
}, },
/** Memory anlegen. */ /** Memory anlegen. */