Skip to content

Releases: iamkm1/Norway-Open-Data

v0.8.0 — Geospatial Norway

Choose a tag to compare

@iamkm1 iamkm1 released this 26 Jul 19:37
546f043

0.8.0 — 2026-07-26

Minor Changes

  • Geospatial Norway. Adds three anonymous, experimental namespaces for focused catalogue,
    environmental and land-resource use. The SDK deliberately remains a curated data client rather
    than a general GIS proxy.

    sdk.geodata — Geonorge / Kartverket catalogue. searchDatasets() and searchServices()
    return bounded, deterministically ordered metadata results; searchDatasetsAll() and
    searchServicesAll() expose caller-bounded async iterators. getMetadata(), getDataset() and
    getService() normalize publisher, themes, licence, geographic extent, update metadata,
    distributions and related services. Discovery classifies WFS, WMS, OGC API Features, ArcGIS REST,
    downloadable files and other published links, but the SDK never follows a discovered URL or
    grants access to the resource behind it.

    sdk.environment — Miljødirektoratet / Naturbase. Adds point lookups, bounding-box searches
    and lazy bounded iterators for current protected areas, proposed protected areas, modern NiN
    nature localities and the January 2023 intervention-free nature status. The integration is
    intentionally limited to these useful public vector layers instead of exposing every map layer.

    sdk.land — NIBIO AR50. Adds point lookup, bounding-box search and a bounded iterator for the
    open generalized AR50 land-resource classification. Restricted agreement-based Geovekst AR5,
    parcel-precision data and soil/cultivation-suitability products are not included.

    profiles.natureAtLocation() — a partial cross-provider nature profile. Composes all five
    Naturbase/NIBIO datasets with the nearest official Kartverket place and municipality context.
    Each operation retains its real source, retrieval time and attribution. Provider failures degrade
    to component warnings without removing successful sections; caller cancellation still rejects
    the complete call.

    Profile-source compatibility and provenance. Existing profiles.company(),
    profiles.address(), profiles.municipality() and profiles.vessel() responses retain their
    synthetic top-level composite source identities. The new nature profile also uses a synthetic
    top-level source so a multi-provider result is not attributed to only one provider. The additive
    sources array is derived from successful components and carries their real provider descriptors,
    preserving separate entries when licence or mandatory attribution text differs; components
    retains per-operation provenance.

    Strict shared GeoJSON and bounded feature paging. Adds runtime validation for two-dimensional
    WGS84 Point, MultiPoint, LineString, Polygon, MultiPolygon, polygon holes, null geometry, feature
    identifiers, feature collections and CRS declarations. Naturbase and NIBIO request server-side
    WGS84 output while retaining the advertised source CRS. Unsupported CRS declarations are rejected;
    the SDK does not silently reinterpret coordinates and includes no client-side reprojection engine.
    Feature searches default to 100 results, expose truncation and continuation metadata, propagate
    AbortSignal, and stop repeated cursors, repeated pages and empty endless pages.

    Scope and verification. WMS image rendering, arbitrary caller-supplied upstream URLs,
    restricted datasets and full-country downloads remain out of scope. Deterministic offline tests
    cover geometry, CRS, ArcGIS/WFS/OGC paging, truncation, attribution and partial profiles; bounded
    anonymous live probes are opt-in through the existing RUN_LIVE_TESTS switch. No new credentials
    or environment variables are required.

Release verification

  • Public API: 22 namespaces and 118 supported methods, including Geonorge, Naturbase and NIBIO.
  • Verification: 1,177 tests passed before publication.

v0.7.0 — Maritime Norway

Choose a tag to compare

@iamkm1 iamkm1 released this 25 Jul 23:05
0532c18

Live AIS streaming, the vessel and aquaculture registers, a cross-provider vessel profile, and the reusable OAuth2 and NDJSON infrastructure they needed.

Two things to know before you start: BarentsWatch needs two separate credentials, and absent AIS data never means an absent vessel.

New namespaces

  • sdk.ais — BarentsWatch AIS. Vessel positions and identity (getVesselSnapshot, getVesselSnapshots, getLatestPositions), recorded tracks (getTrackLast24Hours, getTrack), area and name search (getMmsiInArea, searchVessels), the published coverage area (getCoverageArea), and live streaming (streamPositions, streamMessages) as a plain AsyncIterable with cancellation.
  • sdk.marine — BarentsWatch wave and sea-current point forecasts.
  • sdk.fisheries — Fiskeridirektoratet's fishing-vessel register and public aquaculture register, both open and needing no credentials.
  • profiles.vessel() — joins AIS to the fishing-vessel register on the radio call sign, plus MET conditions and the nearest Kartverket place name.

Infrastructure

Reusable OAuth2 client-credentials support with in-memory tokens, refresh ahead of expiry, single-flight concurrent refresh and abort support. Tokens are never persisted and never appear in errors or logs. A web-standard NDJSON parser returning AsyncIterable<T> with per-record validation, chunk-boundary reassembly, a configurable buffered-record limit and reader cleanup on early exit — no automatic reconnection in this release. Shared Coordinates and BoundingBox primitives.

Two credentials, not one

BarentsWatch issues separate AIS-clients and API-clients, and a secret registered for one is rejected by the other. Configure them independently as credentials["barentswatch-ais"] and credentials.barentswatch.

Coverage limits

AIS coverage excludes fishing vessels under 15 m and leisure vessels under 45 m, is limited to Norwegian waters, and retains 14 days. An empty track or a no-recent-data profile means only that BarentsWatch had nothing to publish — never that a vessel does not exist. ais.getCoverageArea() tells absence of data from absence of coverage.

Privacy

The vessel register's private-owner names, postal codes and towns are suppressed in the runtime schema, so raw payloads and cached values are covered too.

Verification

Verified end to end against the live BarentsWatch services with real credentials: both scopes, real tokens, 812 live positions, 403 snapshots, 54 track points, 40 streamed messages and all three marine endpoints validating with no undeclared fields. That live run caught four defects no offline test could reach — the met/hydro broadcast dropping 28 of its 43 fields, getWaveForecastSeries() mishandling both forms of "no coverage", message labels matched against a spelling the service does not use, and validated fields discarded in normalization.

1024 tests across 35 files; 96.55% lines, 81.21% branches, 99.18% functions. No breaking changes.

v0.6.0

Choose a tag to compare

@iamkm1 iamkm1 released this 24 Jul 23:52

Added

  • sdk.klass — Statistics Norway's SSB Klass API. Browse and search classifications, inspect dated versions, list version/date/range code lists, retrieve exact codes, and use the official selectCodes code-pattern syntax.
  • Correspondence tables and mappings. List correspondence-table metadata and page fixed, dated or ranged correspondence. Because Klass does not page these upstream, locally bounded pages are labelled pagination.upstreamPaged: false.
  • Ambiguity-preserving municipality and county resolution. Historical codes are resolved through official change graphs; merges, splits, multiple predecessors/successors, mixed-step ambiguity, missing context and no-match outcomes are all returned explicitly — no branch is silently selected.
  • Strict runtime-validated types and stable classification IDs for municipalities, counties, industry, occupations, education and three country-code systems.

No breaking changes. profiles.municipality() behaviour is unchanged. Published with npm provenance (SLSA); the registry gitHead matches this tag.

v0.5.3

Choose a tag to compare

@iamkm1 iamkm1 released this 24 Jul 19:34

Fixes

  • Incomplete SSB population years are no longer exposed as complete totals. profiles.municipality() aggregated SSB's per-sex, per-age population cells and skipped any suppressed (null) cell, so a year missing one or more components was published as a finished count — and year-over-year change was computed against that partial figure. Completeness is now tracked per year: a year yields a total only when every cell is numeric. Missing data is treated as unknown, never zero, so an incomplete year is omitted and change is only ever computed between two complete years.
  • Municipality input is validated before any provider request. An empty or whitespace-only query is now trimmed and rejected with InputValidationError before the HTTP client is invoked, instead of spending an SSB request and failing later with NotFoundError.

No public API or type changes. Published with npm provenance (SLSA); the registry gitHead matches this tag.

v0.5.2

Choose a tag to compare

@iamkm1 iamkm1 released this 23 Jul 06:31

Patch release fixing two defects in the shared HTTP core and documenting an ESM/CommonJS limitation. Each fix is covered by a regression test that fails without it.

Fixes

  • A public applicationName no longer corrupts provider responses. Providers that identify the caller through a public header — Entur's ET-Client-Name, NVDB's X-Client, MET Norway's User-Agent — had that header's value treated as a secret and stripped out of successful payloads before validation. An applicationName that also occurred in a provider's own data rewrote it: an application called vegvesen turned NVDB's continuation URL into https://nvdbapiles.atlas.[REDACTED].no/…, which then failed URL validation and made every roads.getRoadNetwork() and roads.getRoadNetworkAll() call raise ResponseValidationError. Caller identification is public by design, so it is no longer a redaction target. Genuine secrets are unaffected: contactEmail, configured apiKey values, and Authorization, Cookie and X-API-Key header values are still redacted from payloads and errors, and properties named after request headers are still dropped.

  • Retry-After is no longer shortened to the backoff cap. A provider asking for a 30-second pause was retried after 5 seconds, because the five-second cap on the SDK's own exponential backoff was also applied to the provider's explicit instruction. The two are now distinct:

    • with no Retry-After, capped jittered backoff as before;
    • with a Retry-After of up to 60 seconds, the stated duration is waited in full;
    • beyond 60 seconds the retry stops without waiting first.

    The status-to-error mapping is unchanged: RateLimitError for HTTP 429, the usual ProviderError for a retryable 5xx. Both carry retryAfter in seconds, which is the stable cross-status signal that a provider asked you to wait. Cancelling the caller's signal still rejects a waiting retry immediately. Retryable statuses and attempt counts are unchanged.

    This changes timing for callers that hit HTTP 429 or a retryable 5xx carrying Retry-After: those retries now happen later than before, or surface as an error instead of retrying. That is the intended correction.

  • Mixed ESM/CommonJS error identity is now documented. When one process loads both published builds, an error from one fails instanceof against the other build's class, because class identity is per-bundle. This is a property of dual-published packages and is documented rather than worked around — no symbol branding or prototype manipulation was introduced. The README recommends branching on the stable name, provider, statusCode and retryAfter fields where an error can cross that boundary, and the packed-package test now asserts same-build instanceof in both builds and verifies that fallback.

No public type or signature changed.

npm install norway-open-data-sdk@0.5.2

v0.5.1

Choose a tag to compare

@iamkm1 iamkm1 released this 23 Jul 05:30

Patch release fixing three defects found in the 0.5.0 request-budget and profile code, each now covered by a regression test.

Fixes

  • A provider's default request budget could be spent twice per window. A request naming a budget the provider never declared was keyed by the requested name, so it received its own limiter holding a copy of the default policy instead of sharing it. The limiter is now keyed by the resolved budget name. Declared budgets, such as Data.norge's resource, stay separate as before.
  • profiles.address() credited NVE in the composed source even when all three Varsom warning feeds had failed and contributed nothing. An empty hazards array next to an NVE attribution reads as an all-clear NVE never issued. NVE is now named only when at least one feed answered, matching the behaviour profiles.municipality() already had. Per-feed status remains available in components.
  • profiles.municipality() fetched SSB's table 07459 metadata twice per call — once to resolve the municipality and once inside the population query — spending two requests of SSB's documented 30-per-minute budget on identical bytes. The metadata is now fetched once and reused. The supported query() and queryRaw() surface is unchanged.

Also fixes pnpm check:portability leaving its generated probe file inside dist/ when the probe process failed, because process.exit in the error path pre-empted the cleanup.

No changes to the supported consumer surface. See CHANGELOG.md for full details.

npm install norway-open-data-sdk@0.5.1

v0.5.0

Choose a tag to compare

@iamkm1 iamkm1 released this 23 Jul 02:37
  • Providers are now declared once, in one place. Each provider owns a ProviderDescriptor under
    src/providers/<provider>/provider.ts holding its legal metadata, caller identification, request
    budgets and cache lifetimes, and src/providers/registry.ts collects them. The ProviderId union
    is derived from the registry's own keys, and the registry refuses to compile when a key and a
    descriptor id disagree, so one spelling of an identifier reaches error messages, response
    source.id, cache keys and configuration. Requests carry the descriptor rather than a provider
    name, making a mistyped provider a type error instead of a misleading message. Adding a provider now
    means writing its folder and one registry line; no file under src/core/ changes.

    Caller identification is declarative. A descriptor names the configuration values it needs and
    builds its own headers from them; the HTTP client verifies they are present and raises
    ConfigurationError with the provider's own instructions before any network access. The bespoke
    per-client identity plumbing for Entur, MET Norway, NVDB and NVE HydAPI is gone, and cross-provider
    profiles ask the same question to decide whether to skip a section as not-configured.

    New: per-provider request budgets, enforced by default. Every provider declares how often the SDK
    may call it, and one sliding-window limiter per budget is shared by all clients on a
    NorwayOpenData instance. Admission is serialized so concurrent callers cannot overshoot together.
    Waiting happens before the request timeout is armed, so a queued request is not charged for its
    wait; a cache hit costs no budget, a retry does, and a caller's signal rejects a queued request
    immediately. Budgets are named per operation class because providers publish different limits per
    service — Data.norge allows 10 searches per minute but 5 resource lookups per second, and those no
    longer throttle each other. basis distinguishes a provider's published number from a conservative
    budget the SDK chose. Disable with rateLimit: { enabled: false }.

    New: pluggable cache storage. cache.store accepts any CacheStore implementing get, set and
    clear, synchronously or asynchronously, so validated responses can be shared across instances,
    workers or hosts instead of being trapped in one process. The default in-memory cache is unchanged.

    Breaking changes. This is a minor release before 1.0, so a ^0.4.1 dependency range will not pick
    it up automatically; upgrade deliberately and read this list first:

    • Per-provider request budgets are enforced by default. Code that previously issued unbounded
      bursts now waits instead of failing, which is the intended behaviour but changes timing. Data.norge
      is the tightest at 10 search requests per minute, as that service documents. Opt out with
      rateLimit: { enabled: false }.
    • NorwayOpenData.clearCache() returns Promise<void> so it can await a custom store. Existing
      calls that ignore the result keep working; await it when you depend on the cache being empty.
    • providers is keyed by provider id rather than a camelCase alias, so providers.dataNorge and
      providers.norgesBank become providers["data-norge"] and providers["norges-bank"]. Its values
      are now ProviderMetadata rather than literal types, and expose rateLimit. Every other key is
      unchanged.
    • credentials is keyed by provider id and rejects unknown providers instead of ignoring them.
      credentials.nve.apiKey is unchanged.
    • A cancellation that arrives while the cache is being read now rejects before the request is sent
      rather than after. Cancellation semantics are otherwise unchanged.

    Provider rate limits were reconciled against the numbers recorded in PROVIDERS.md, and a
    regression test now fails if a documented budget drifts from its descriptor.

v0.4.1

Choose a tag to compare

@iamkm1 iamkm1 released this 23 Jul 01:09

Patch Changes

Verified and documented that the package runs unchanged on Node.js 22+, Deno, Bun, Cloudflare Workers and other edge hosts, and in browsers wherever the provider sends permissive CORS headers.

No runtime API changed and no capability was added — the SDK already used only web-standard APIs. The previous README claim that browser support was not guaranteed was simply inaccurate, and this release corrects it.

  • Distribution targets ES2022 on esbuild's neutral platform rather than node22. The ESM output is byte-identical and the CommonJS output is marginally smaller.
  • exports gains a trailing default condition, so resolvers matching neither import nor require still reach the ESM build. This can only widen resolution.
  • pnpm check:portability (part of pnpm verify) scans the built bundles for any Node built-in reference and exercises the request, cache, validation, error and cancellation paths against an object implementing only the standard Response surface.
  • CI now builds the package and executes it on real Deno 2 and Bun runtimes on every change.

Host runtimes must supply a spec-compliant fetch, structuredClone and a full-ICU Intl for Europe/Oslo dates and Norwegian locale casing. See the new Runtime support section in the README.


Full changelog: v0.4.0...v0.4.1

npm: norway-open-data-sdk@0.4.1

v0.4.0

Choose a tag to compare

@iamkm1 iamkm1 released this 23 Jul 00:01

Minor Changes

  • New profiles.municipality() composition answers one municipality from SSB, FHI, Brønnøysundregistrene and NVE in a single call.

    const kommune = await norway.profiles.municipality("Haugesund"); // or the code "1106"
    kommune.data.population;      // SDK-aggregated residents for the two newest years
    kommune.data.lifeExpectancy;  // FHI value, or years: null with a suppression flag
    kommune.data.companies;       // Brønnøysundregistrene organization count
    kommune.data.hazards;         // Exact NVE warning matches for the municipality

    It resolves a four-digit municipality code or an exact municipality name against SSB's region register — counties and the whole-country region never resolve, and duplicated names such as Herøy require SSB's county-qualified label, so a profile never silently resolves to the wrong kommune. Population totals are summed by the SDK from SSB's per-sex, per-age rows (an SDK aggregation, not an SSB-published figure). Life expectancy preserves FHI's suppression flag with years: null for small municipalities. Every optional section degrades to a provider-error component instead of failing the call; caller cancellation still rejects.

The hosted API reference is now published at https://iamkm1.github.io/Norway-Open-Data/.


Full changelog: v0.3.0...v0.4.0

npm: norway-open-data-sdk@0.4.0

v0.3.0

Choose a tag to compare

@iamkm1 iamkm1 released this 22 Jul 19:51

Minor Changes

  • New health namespace for the FHI Statistikk open API. getSources, getTables, getTableMetadata, getTableDimensions, query and queryRaw cover source and table discovery, provider-authored documentation, hierarchical dimensions and JSON-stat2 data queries across Norwegian health registers — the Cause of Death Registry, the Abortion Registry, drug wholesale statistics, municipal public-health indicators and more.

    FHI's cell-suppression flags are preserved rather than hidden: flagged observations normalize to value: null with the provider's flag symbol (for example ":" — anonymized), and every result carries FHI's flag legend. Flagged cells must stay suppressed in downstream use. The JSON-stat parsing core is now shared between SSB and FHI, with parseJsonStat and parseTableMetadata unchanged.

  • Cross-provider profiles now degrade optional provider failures to partial results instead of failing the whole call. If MET, NVDB or one Varsom warning feed errors at request time, profiles.address() returns every surviving section and reports the failing operation as an omitted component with the new provider-error reason and a sanitized error name and message; profiles.company() likewise returns a location-less profile when the Kartverket lookup fails. Required operations still throw, and caller cancellation always rejects the whole call.


Full changelog: v0.2.2...v0.3.0

npm: norway-open-data-sdk@0.3.0