@siax/mobile-api (0.1.1)
Installation
@siax:registry=https://git.cloud.siax.io/api/packages/sax3l/npm/npm install @siax/mobile-api@0.1.1"@siax/mobile-api": "0.1.1"About this package
@siax/mobile-api
Transportlagret under den API-klient som genereras ur appens OpenAPI (API-04). Handskrivna dubbletter av en genererad operation är en avvikelse.
Status: transportlagret är implementerat och testat. Generatorn själv
finns inte — den hör till M3 (app-generatorn). Det som finns här är runtimet
generatorn ska skriva ut anrop till, plus en createClient som tar en
operationstabell. Paketet är ännu inte publicerat och inte kört mot ett riktigt
API.
Vad transporten gör åt varje anrop
| Kontroll | Vad som faktiskt sker | Var |
|---|---|---|
| VER-01 | x-siax-app-version, x-siax-app-build, x-siax-platform, x-siax-os-version, x-siax-runtime-version på varje anrop (+ valfria app-id, update-id, device-model) |
src/client-info.ts |
| API-10 | Idempotensnyckel på muterande metoder, samma nyckel över alla omförsök | src/idempotency.ts |
| API-07 | application/problem+json (RFC 9457) tolkas till ProblemDetailsError, utökningar bevarade |
src/problem.ts |
| API-09 | Cursor-paginering som asynkron iterator, med skydd mot upprepad cursor | src/pagination.ts |
| SCALE-05 | Strömbrytare: closed → open → half_open → closed | src/circuit-breaker.ts |
| SCALE-06 | Timeout per anrop, skild från användarens avbrott | src/transport.ts |
| OBS-03 | W3C traceparent; traceId finns på varje svar OCH varje fel |
src/trace.ts |
| — | Omförsök med exponentiell backoff, full jitter, Retry-After och retry-budget |
src/backoff.ts |
Använda paketet
import { createTransport, createClient, ProblemDetailsError } from '@siax/mobile-api';
const transport = createTransport({
baseUrl: process.env.EXPO_PUBLIC_API_BASE_URL!, // env-NAMN, aldrig ett värde i koden
client: {
appVersion: Application.nativeApplicationVersion!,
buildNumber: Application.nativeBuildVersion!,
platform: Platform.OS === 'ios' ? 'ios' : 'android',
osVersion: String(Platform.Version),
runtimeVersion: Updates.runtimeVersion!,
updateId: Updates.updateId ?? undefined,
},
// Kopplingen till ID0 — @siax/mobile-auth förnyar token vid behov.
getAuthorization: async () => `Bearer ${await session.getAccessToken()}`,
onAttempt: (t) => posthog.capture('api_attempt', { ...t, error: undefined }),
});
const api = createClient(transport, [
{ operationId: 'listOrders', method: 'GET', path: '/v1/orders', paginated: true },
{ operationId: 'createOrder', method: 'POST', path: '/v1/orders' },
] as const);
try {
await api.createOrder({ body: { amount: 50 } }); // får automatiskt en idempotensnyckel
} catch (error) {
if (error instanceof ProblemDetailsError) showToast(problemMessage(error.problem));
}
Paginering:
const listOrders = createPaginatedOperation<Order>(transport, {
operationId: 'listOrders', method: 'GET', path: '/v1/orders', paginated: true,
});
for await (const order of listOrders({ query: { limit: 50 } })) render(order);
Designbeslut värda att känna till
- Idempotensnyckeln genereras en gång per LOGISK begäran. En nyckel per försök ser identisk ut i koden och är funktionellt samma sak som ingen nyckel. Det finns ett test som hävdar att alla tre försöken bär samma nyckel.
POST/PATCHförsöks bara om när de har en nyckel. Sätter duidempotencyKey: falsestängs omförsöken av samtidigt — annars vore avstängningen en tyst risk för dubbel debitering.- 429 öppnar inte strömbrytaren. Servern mår bra; den skyddar sig. Att
bryta strömmen för det vore att straffa oss för vår egen anropstakt.
Retry-Afterrespekteras däremot, och går före backoff-beräkningen. - Retry-budget, inte bara maxförsök. En budget hindrar att alla klienter tredubblar lasten precis när servern minst tål det.
- Timeout ≠ avbrott. En timeout försöks om; ett avbrott från anroparen
(skärmen lämnades) försöks aldrig om. Skillnaden kräver en egen
AbortController—AbortSignal.timeout()kan inte skilja på fallen. - Kroppen läses innanför tidsgränsen. En server som skickar huvuden och sedan tystnar mitt i kroppen träffar också timeouten.
- Ett HTML-svar blir aldrig Problem Details. Bara
application/problem+jsontolkas som RFC 9457; en 502-sida från en proxy blir ett vanligtHttpError.
Tester
node --test "packages/mobile-api/test/*.test.ts"
59 tester, alla gröna (körning 2026-08-09, Node 24.14). Inga testberoenden:
node:test + node:assert, inget npm install krävs.
Runtime-krav: Node 22.18+ eller 24 — källkoden är ren TypeScript som
körs via Nodes inbyggda type stripping (erasableSyntaxOnly i tsconfig.json
håller koden inom vad strippningen klarar).
Vad som INTE är gjort (AR-5)
- Ingen OpenAPI-generator. Operationstabellen skrivs för hand tills M3
genererar den.
createClientreturnerarOperation<unknown>— de genererade typerna per operation kommer med generatorn. - Ingen offline-kö och ingen cachning. Ett anrop som misslyckas efter alla
omförsök kastar. Kö och konfliktlösning hör till
@siax/mobile-sync(M11). - Ingen automatisk omförhandling vid 401. Transporten hämtar
Authorizationper försök, så en förnyad token används vid omförsök — men ett 401 utlöser inte i sig ett refresh. Kopplingen görs igetAuthorization. - Strömbrytaren är per transport, inte per host eller endpoint. En app som pratar med flera bas-URL:er ska skapa en transport per URL.
- Ingen mätvärdesexport.
onAttemptär kroken; ingen PostHog- eller GlitchTip-koppling är inbyggd (det vore MOB-02-överträdelse att bygga en). - Ingen
tsc-körning.typescriptär inte ett beroende i repot i denna omgång, såtsconfig.jsonär ett kontrakt som ännu inte upprätthålls av CI. - Aldrig kört mot ett riktigt API. Alla tester går mot en injicerad transport.
Kärnbussgränsen (MOB-02)
Detta paket är en yta, inte en tjänst. Om du står i begrepp att implementera
något som ID0, INF0, ST0RE, N0D, AUD0, N0TIFY, B00K, CL0UD
eller 1P redan gör — bygg ytan, inte tjänsten.