@siax/mobile-config (0.1.1)
Installation
@siax:registry=https://git.cloud.siax.io/api/packages/sax3l/npm/npm install @siax/mobile-config@0.1.1"@siax/mobile-config": "0.1.1"About this package
@siax/mobile-config
Fjärrconfig och feature flags för K6-appar. Bär minimumVersion /
recommendedVersion (VER-04 / VER-05) och läser flaggor som Unleash
redan utvärderat.
Status: implementerad och testad. 56/56 tester gröna (node --test),
tsc --strict rent. Inte publicerad till Giteas npm-registry, och
services/config är inte rest (M10) — transporten är därför injicerbar och
paketet fungerar fullt ut mot en påhittad transport i test.
Kärnbussgränsen (MOB-02)
Detta paket är en yta, inte en tjänst.
| Vad | Vem äger det | Vad vi gör |
|---|---|---|
| Feature flags | Unleash (server10) | läser ett färdigutvärderat dokument. Ingen strategimotor, ingen flaggdatabas, inget adminläge |
| Versionspolicy | services/config i detta repo |
hämtar och cachar |
| Identitet / token | ID0 | tar emot ett token från en injicerad funktion |
| Hemligheter | Infisical | inga. baseUrl är en parameter, inget nyckelvärde finns i koden (SEC-01/MSEC-01) |
Det paketet gör själv — för att ingen annan gör det åt en mobilklient — är semver-jämförelsen, grindbeslutet och offlinecachen.
Snabbstart
import {
MobileConfigClient,
createHttpConfigTransport,
} from '@siax/mobile-config';
const config = new MobileConfigClient({
appVersion: '1.2.0', // ur bundlen
platform: 'ios',
storage: minAsyncStorageAdapter, // valfri men KRÄVS för offline
transport: createHttpConfigTransport({
baseUrl: process.env.EXPO_PUBLIC_CONFIG_URL!, // env-NAMN, aldrig ett värde
getAccessToken: () => auth.accessToken(), // ID0, via @siax/mobile-auth
}),
});
await config.hydrate(); // läs cachen INNAN första renderingen
const gate = await config.versionGate();
if (gate.blocking) {
// VER-04: spärrskärm UTAN stängknapp. gate.storeUrl är garanterat ifylld här.
visaSpärrskärm({ storeUrl: gate.storeUrl });
} else if (gate.status === 'update-recommended') {
// VER-05: avfärdbar banner. Tydligt skild från spärrskärmen.
visaUppmaning({ storeUrl: gate.storeUrl, avfärdbar: true });
}
if (config.isEnabled('ny-onboarding')) { /* … */ }
VER-04 / VER-05
evaluateVersionGate() är ren och synkron, så beslutet går att testa uttömmande
och återge exakt i en incident.
| Läge | status |
blocking |
dismissible |
|---|---|---|---|
Under minimumVersion |
update-required |
true | false |
Under recommendedVersion |
update-recommended |
false | true |
| Aktuell / ingen policy | ok |
false | false |
De två är ömsesidigt uteslutande — det är testat, inte bara påstått.
Semver på riktigt
semver.ts implementerar semver 2.0.0 §11 fullt ut, utan externt beroende:
1.10.0 > 1.9.0(inte strängjämförelse)1.0.0-beta < 1.0.0(prerelease har lägre precedens)- hela kedjan
alpha < alpha.1 < alpha.beta < beta < beta.2 < beta.11 < rc.1 < 1.0.0 - numerisk identifierare < alfanumerisk; fler fält vinner vid lika prefix
- byggmetadata (
+sha.abc) ignoreras alltid
Tre fail-safe-regler som är medvetna, inte olyckor
- Oläsbar appversion ⇒ ingen spärr. Kan vi inte parsa vår egen version kan vi inte veta att den är för gammal. Att gissa låser ute hela installerade basen på ett byggfel.
- Oläsbar
minimumVersion⇒ fältet faller bort (redan ischema.ts). Alternativet — att tolka skräp som "oändligt högt" — hade spärrat alla användare på en felskrivning i Unleash. - Ingen butiks-URL ⇒ ingen tvingande spärr. VER-04 kräver en fungerande
väg till butiken. Saknas den mildras beslutet till
update-recommendedochdegradedFrom: 'update-required'sätts, så att avvikelsen syns i telemetrin i stället för att bli en app utan utgång.
Butiks-URL:en är säkerhetskänslig
storeUrl kan komma från fjärrconfigen. En spärrskärm som inte går att avfärda,
med en knapp som leder vart som helst, vore en perfekt nätfiskeyta. Därför
valideras en fjärrsatt URL mot en allowlist som appen äger
(DEFAULT_ALLOWED_STORE_HOSTS: apps.apple.com, itunes.apple.com,
testflight.apple.com, play.google.com). Endast https, inga inbäddade
uppgifter. En förkastad override faller tillbaka på den härledda URL:en och
loggar store-url-rejected:<plattform>.
Cachestrategi
| Ålder | Källa | Beteende |
|---|---|---|
≤ ttlSeconds |
cache |
ingen nätverkstrafik |
≤ maxAgeSeconds |
cache |
returneras direkt, revalidering i bakgrunden (stale-while-revalidate) |
> maxAgeSeconds |
stale-cache |
används fortfarande — senast kända minimumVersion gäller |
| inget dokument | none |
ingen spärr kan resas |
Den tredje raden är hela poängen med fail-safe, inte fail-open: en app som varit offline i en månad respekterar fortfarande den senast kända gränsen. Den fjärde är dess motsats och lika medveten — utan data spärrar vi ingen.
Andra egenskaper som är testade: samtidiga hämtningar deduplicerar till en förfrågan · ett nätverksfel raderar aldrig en fungerande cache · en korrupt cache slängs och appen startar ändå · en klocka som hoppar bakåt ger inte negativ ålder · ett skrivfel i lagringen fäller inte hämtningen.
Tolerant schema
parseRemoteConfigDocument() förkastar bara det som inte ens är ett objekt.
Allt annat degraderar fältvis med en varning: en trasig flagga tas bort, resten
överlever; en nyare schemaVersion från servern bricker inte en äldre klient.
En delvis trasig config ska ge en delvis fungerande app — inte en spärrad.
Feature flags
isEnabled() är synkron (flaggor läses i render), deny-by-default, och
faller tillbaka på defaultFlags innan servern svarat. Varianter speglar
Unleashs form ({ name, enabled, payload: { type, value } }) — vi hittar inte
på ett eget format.
Filer
| Fil | Ansvar |
|---|---|
src/semver.ts |
semver 2.0.0-parsning och -jämförelse |
src/version-gate.ts |
VER-04/VER-05-beslutet |
src/store.ts |
butiks-URL:er + allowlist |
src/schema.ts |
tolerant parsning av configdokumentet |
src/client.ts |
cache, stale-while-revalidate, flaggläsning |
src/http-transport.ts |
valfri HTTP-adapter mot services/config |
src/storage.ts |
lagringsadaptrar (minne, synkront KV) |
Tester
cd packages/mobile-config
node --test # 56 tester, inga beroenden, ingen npm install
tsc -p tsconfig.json
Kör från repo-roten: node --test "packages/mobile-config/**/*.test.ts".
(Ett katalogargument — node --test packages/mobile-config — fungerar inte
på Node 24: positionsargument tolkas som globmönster, och en katalog matchar
bara sig själv.)
Kräver Node ≥ 22.18 för inbyggd typradering — testerna importerar .ts
direkt. Node 20 kan inte köra dem.
Medvetet ogjort
- Ej publicerad till
https://git.siax.io/api/packages/sax3l/npm/. - Ingen exempelapp (MOB-13 kräver en i CI). Det hör till M2 och behöver app-generatorn.
services/configär inte byggd av detta paket — den ägs av M10 och en parallell session. Kontraktet som antas här:GET /v1/config?platform=…&appVersion=…som svarar medschema.ts-dokumentet.- Typkontroll av testfilerna kräver
@types/node, som medvetet inte installerats i denna omgång.tsconfig.jsontäcker därförsrc/**— testerna är verifierade separat och är rena bortsett från saknade Node-typer. - Ingen
AbortSignal-genomgång från anroparen tillrefresh(); timeouten ligger i HTTP-transporten.