Add progressive web app companion for cross-platform access

Vite + TypeScript PWA that mirrors the Android app's core features:
- Pre-processed shelter data (build-time UTM33N→WGS84 conversion)
- Leaflet map with shelter markers, user location, and offline tiles
- Canvas compass arrow (ported from DirectionArrowView.kt)
- IndexedDB shelter cache with 7-day staleness check
- Service worker with CacheFirst tiles and precached app shell
- i18n for en, nb, nn (ported from Android strings.xml)
- iOS/Android compass handling with low-pass filter
- Respects user map interaction (no auto-snap on pan/zoom)
- Build revision cache-breaker for reliable SW updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-08 17:41:38 +01:00
commit e8428de775
12051 changed files with 1799735 additions and 0 deletions

184
pwa/node_modules/ajv/lib/compile/errors.ts generated vendored Normal file
View file

@ -0,0 +1,184 @@
import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"
import type {SchemaCxt} from "./index"
import {CodeGen, _, str, strConcat, Code, Name} from "./codegen"
import {SafeExpr} from "./codegen/code"
import {getErrorPath, Type} from "./util"
import N from "./names"
export const keywordError: KeywordErrorDefinition = {
message: ({keyword}) => str`must pass "${keyword}" keyword validation`,
}
export const keyword$DataError: KeywordErrorDefinition = {
message: ({keyword, schemaType}) =>
schemaType
? str`"${keyword}" keyword must be ${schemaType} ($data)`
: str`"${keyword}" keyword is invalid ($data)`,
}
export interface ErrorPaths {
instancePath?: Code
schemaPath?: string
parentSchema?: boolean
}
export function reportError(
cxt: KeywordErrorCxt,
error: KeywordErrorDefinition = keywordError,
errorPaths?: ErrorPaths,
overrideAllErrors?: boolean
): void {
const {it} = cxt
const {gen, compositeRule, allErrors} = it
const errObj = errorObjectCode(cxt, error, errorPaths)
if (overrideAllErrors ?? (compositeRule || allErrors)) {
addError(gen, errObj)
} else {
returnErrors(it, _`[${errObj}]`)
}
}
export function reportExtraError(
cxt: KeywordErrorCxt,
error: KeywordErrorDefinition = keywordError,
errorPaths?: ErrorPaths
): void {
const {it} = cxt
const {gen, compositeRule, allErrors} = it
const errObj = errorObjectCode(cxt, error, errorPaths)
addError(gen, errObj)
if (!(compositeRule || allErrors)) {
returnErrors(it, N.vErrors)
}
}
export function resetErrorsCount(gen: CodeGen, errsCount: Name): void {
gen.assign(N.errors, errsCount)
gen.if(_`${N.vErrors} !== null`, () =>
gen.if(
errsCount,
() => gen.assign(_`${N.vErrors}.length`, errsCount),
() => gen.assign(N.vErrors, null)
)
)
}
export function extendErrors({
gen,
keyword,
schemaValue,
data,
errsCount,
it,
}: KeywordErrorCxt): void {
/* istanbul ignore if */
if (errsCount === undefined) throw new Error("ajv implementation error")
const err = gen.name("err")
gen.forRange("i", errsCount, N.errors, (i) => {
gen.const(err, _`${N.vErrors}[${i}]`)
gen.if(_`${err}.instancePath === undefined`, () =>
gen.assign(_`${err}.instancePath`, strConcat(N.instancePath, it.errorPath))
)
gen.assign(_`${err}.schemaPath`, str`${it.errSchemaPath}/${keyword}`)
if (it.opts.verbose) {
gen.assign(_`${err}.schema`, schemaValue)
gen.assign(_`${err}.data`, data)
}
})
}
function addError(gen: CodeGen, errObj: Code): void {
const err = gen.const("err", errObj)
gen.if(
_`${N.vErrors} === null`,
() => gen.assign(N.vErrors, _`[${err}]`),
_`${N.vErrors}.push(${err})`
)
gen.code(_`${N.errors}++`)
}
function returnErrors(it: SchemaCxt, errs: Code): void {
const {gen, validateName, schemaEnv} = it
if (schemaEnv.$async) {
gen.throw(_`new ${it.ValidationError as Name}(${errs})`)
} else {
gen.assign(_`${validateName}.errors`, errs)
gen.return(false)
}
}
const E = {
keyword: new Name("keyword"),
schemaPath: new Name("schemaPath"), // also used in JTD errors
params: new Name("params"),
propertyName: new Name("propertyName"),
message: new Name("message"),
schema: new Name("schema"),
parentSchema: new Name("parentSchema"),
}
function errorObjectCode(
cxt: KeywordErrorCxt,
error: KeywordErrorDefinition,
errorPaths?: ErrorPaths
): Code {
const {createErrors} = cxt.it
if (createErrors === false) return _`{}`
return errorObject(cxt, error, errorPaths)
}
function errorObject(
cxt: KeywordErrorCxt,
error: KeywordErrorDefinition,
errorPaths: ErrorPaths = {}
): Code {
const {gen, it} = cxt
const keyValues: [Name, SafeExpr | string][] = [
errorInstancePath(it, errorPaths),
errorSchemaPath(cxt, errorPaths),
]
extraErrorProps(cxt, error, keyValues)
return gen.object(...keyValues)
}
function errorInstancePath({errorPath}: SchemaCxt, {instancePath}: ErrorPaths): [Name, Code] {
const instPath = instancePath
? str`${errorPath}${getErrorPath(instancePath, Type.Str)}`
: errorPath
return [N.instancePath, strConcat(N.instancePath, instPath)]
}
function errorSchemaPath(
{keyword, it: {errSchemaPath}}: KeywordErrorCxt,
{schemaPath, parentSchema}: ErrorPaths
): [Name, string | Code] {
let schPath = parentSchema ? errSchemaPath : str`${errSchemaPath}/${keyword}`
if (schemaPath) {
schPath = str`${schPath}${getErrorPath(schemaPath, Type.Str)}`
}
return [E.schemaPath, schPath]
}
function extraErrorProps(
cxt: KeywordErrorCxt,
{params, message}: KeywordErrorDefinition,
keyValues: [Name, SafeExpr | string][]
): void {
const {keyword, data, schemaValue, it} = cxt
const {opts, propertyName, topSchemaRef, schemaPath} = it
keyValues.push(
[E.keyword, keyword],
[E.params, typeof params == "function" ? params(cxt) : params || _`{}`]
)
if (opts.messages) {
keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
}
if (opts.verbose) {
keyValues.push(
[E.schema, schemaValue],
[E.parentSchema, _`${topSchemaRef}${schemaPath}`],
[N.data, data]
)
}
if (propertyName) keyValues.push([E.propertyName, propertyName])
}