npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@osmix/change

v0.2.0

Published

An OSM changeset generator and merger built on top of @osmix/core

Readme

@osmix/change

@osmix/change plans and applies merges of OpenStreetMap datasets on top of @osmix/core: one plan per merge, reviewed by imported feature and applied in a single build.

The merge-process guide is the authoritative reference for merge rules, defaults, examples, workflows, and known limitations. This README describes the package API.

Highlights

  • Plan once, apply once. planMerge records every change a merge would make as proposals grouped by imported feature; applyPlan builds the result in a single pass and validates routing integrity.
  • Decide what needs you. Identical-point merges, matching actions and crossings are proposals with a status, an effect and reasons. setMergePlanDecisions accepts or rejects them and replans only the phases they affect.
  • OSM ID convention. Negative patch IDs are new features and move below the base's lowest ID, so independent imports never collide; positive IDs edit the base entity with that ID.
  • One call. merge(base, patch, options) plans and applies with the Merge app's defaults.
  • Review output. generateMergePlanOsc writes a plan as osmChange; plan.diagnostics reports routing topology and integrity problems before anything is built.

Installation

pnpm add @osmix/change

Application code can import these APIs from the osmix facade. Install the granular package directly when building a lower-level package integration.

Usage

Merge in one call

import { merge } from "osmix";

const combined = await merge(base, patch);
const matched = await merge(base, patch, {
  matching: { propertyKeys: ["name", "surface"], attachNetwork: true },
});
console.log(combined.id, matched.id);

By default identical points merge into the base, imported ways connect where they cross, and matching runs only when configured. See the plan's phases and defaults for every rule.

Plan, decide, apply

import { applyPlan, generateMergePlanOsc, planMerge, setMergePlanDecisions } from "osmix";

const plan = planMerge(base, patch, {
  mergeIdenticalPoints: false,
  matching: { propertyKeys: ["name"], attachNetwork: true, automatic: "none" },
});

// Features whose proposals wait for a decision.
const waiting = plan.features.filter((feature) => feature.outcome === "needs-decision");
const decisions = waiting.flatMap((feature) =>
  feature.proposalIds
    .map((id) => plan.proposals.get(id))
    .filter((proposal) => proposal?.effect === "needs-decision")
    .map((proposal) => ({ proposalId: proposal!.id, action: "accept" as const })),
);
setMergePlanDecisions(plan, decisions);

const osc = generateMergePlanOsc(plan);
const { osm, summary } = applyPlan(plan);
console.log(osc.length, summary.features, osm.id);

A plan is live: keep it in the process that made it. Proposal IDs are built from original patch IDs and base IDs (exact:n-5>n123, connect:n-5>n123, copy:w-9>w44, remove:w-9>w44, xnode:w-9|[email protected],43.7312345), so decisions survive replans and restarts. Decisions naming proposals the plan no longer has are listed in plan.staleDecisions.

Plan options

  • patchIds ("osm" or "new", default "osm"): how patch IDs are read; "new" treats every patch entity as a new feature.
  • mergeIdenticalPoints (default true): merge imported points at identical coordinates, and ways that then match, automatically. When false those merges wait for a decision.
  • createIntersections (default true): connect imported ways to the ways they cross at the same grade.
  • matching (optional): match imported features to nearby base features. propertyKeys and attachNetwork are required; maxDistanceMeters defaults to 1, automatic to "high-confidence", and allowWayRemoval to false (removal is always a manual choice).
  • automation ("conservative", "recommended" or "aggressive", default "recommended"): how much the planner decides without a person. Its decisions are marked automated on the proposals and are replaced by any decision in decisions; see MP-M6 in the merge-process guide.
  • decisions: { proposalId, action: "accept" | "reject" }[], a person's decisions.

getMergePlanChoices(plan) groups the features that need a decision by why they wait (MP-M7), and pickNearestMergePlanDecisions(plan, proposalIds) returns a person's decisions picking the clearly nearest candidate of each choice, by the automation levels' margin.

Discover matching candidates

discoverConflationCandidates(base, patch, options) returns the matching candidates between two untouched datasets, with evidence, tag differences and reason codes, without planning a merge.

Within one dataset

planWithinDatasetDeduplication(osm) returns the changes that remove duplicate nodes and ways inside one dataset with the same rules; applyChangesetToOsm builds the cleaned dataset.

Augmented diffs

Change records include an oldEntity for modifications and deletions, following the Overpass API Augmented Diffs format.

generateOscChanges(changes, options?)

Generates OSC (OSM Change) XML from change records, such as within-dataset deduplication's.

import { generateOscChanges, planWithinDatasetDeduplication } from "osmix";

const changeset = planWithinDatasetDeduplication(base);

// Generate standard OSC for API uploads (default)
const osc = generateOscChanges(changeset);

// Generate augmented diff with old/new sections
const augmentedOsc = generateOscChanges(changeset, { augmented: true });
console.log(osc.length, augmentedOsc.length);

Options:

  • augmented (boolean, default: false): When true, modifications include <old> and <new> sections, and deletions include <old> sections with the full entity data.

Related Packages

  • @osmix/core – Typed-array index powering the change operations.
  • @osmix/pbf – Streaming helpers used to read and write .osm.pbf data.
  • @osmix/json – JSON entity adapters that pair with change workflows.
  • Osmix Merge app – Browser UI built on top of the change pipeline.

Environment and limitations

  • Requires runtimes compatible with @osmix/core (Node 24+, Bun, or modern browsers) since the same typed-array data structures are used.
  • Deduplication helpers assume datasets store dense node blocks and rely on spatial indexes built via Osm.buildIndexes().
  • Intersection eligibility and its area-filtering limitation are specified in the merge-process guide.
  • PBFs produced by older Osmix versions may already contain topology changes caused by automatic within-input deduplication. Those files cannot be repaired reliably without their source inputs and should be regenerated from the original base and patch files.

Development

  • pnpm run test packages/change
  • pnpm exec oxlint packages/change

Run pnpm run verify at the repo root before publishing.