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

@wattleaddr/js

v0.4.0

Published

JavaScript/TypeScript SDK for the WattleAddr address API: Australian address autocomplete and verification, served from Australia. Headless client + drop-in autocomplete widget, zero dependencies.

Readme

@wattleaddr/js

Official JavaScript/TypeScript SDK for the WattleAddr address API. It does Australian address autocomplete and verification, served from Australia on G-NAF.

  • Zero dependencies. One small module, ESM + CommonJS, full TypeScript types.
  • Runs anywhere fetch does: browsers and Node 18+.
  • Two ways in: a headless WattleAddrClient for full control, or a drop-in WattleAddr widget for any <input>.
npm install @wattleaddr/js

Using React? Use @wattleaddr/react instead.

Keys

Use a publishable key (waddr_pk_…), locked to your domains in the console, anywhere the code runs in a browser. A secret key (waddr_sk_…) is server-only. Never ship it to a browser.

Drop-in widget

Attach autocomplete to any text input. Handles debouncing, keyboard navigation, accessibility, and PO-Box notices for you, and bills one lookup per completed session (not per keystroke).

import { WattleAddr } from '@wattleaddr/js';

const wa = new WattleAddr('waddr_pk_live_…', { limit: 6, theme: 'light' });
wa.attach('#address', {
  onSelect(address) {
    console.log(address.formatted);           // "1 Martin Place, Sydney NSW 2000"
    console.log(address.components.postcode);  // "2000"
    console.log(address.geo);                  // { lat, lng } | null
  },
});

attach() also fires a native change event on the input, so it works inside most form libraries. Call wa.destroy() to tear everything down.

Fill your form

Pass fill to put the picked address into your own form fields. It maps address parts to a field id, a name, a CSS selector, or the element itself.

wa.attach('#address1', {
  fill: { address_line1: 'address1', address_line2: 'address2', suburb: 'city', state: 'state', postcode: 'zip' },
});

Units, shops, levels and lots go to the right place. With no line 2 or unit field, the unit joins line 1. Text the person typed in line 2 is never overwritten or cleared. A drop-down is set only to an option it already offers. An unknown part name throws when you call attach(). onSelect still runs after the fill. A form inside another iframe is not filled, and values are not shortened to a field's maxlength.

The parts are address_line1, address_line2, unit, building_name, street_number, street_name, suburb, state, state_name, postcode, country, full_address, latitude, longitude and address_id. The table with examples is at https://wattleaddr.com.au/docs#widget.

To get the parts without filling anything, call addressParts(record). It returns every part as a string, empty when the record has none. Pass { foldUnit: true } to put the unit in line 1.

Headless client

When you want to render your own UI, or call the API from a server, use the client directly.

import { WattleAddrClient, createSession } from '@wattleaddr/js';

const client = new WattleAddrClient('waddr_pk_live_…');

const session = createSession();                // ties a lookup together for billing
const { suggestions } = await client.autocomplete('1 mart', { session });
const address = await client.retrieve(suggestions[0].id, { session }); // the billable call
// Start a fresh session for the next lookup.

Verify a free-text address

verify takes an address you already hold and matches it against G-NAF. Branch on verdict. It is the field that tells you what to do:

Requires @wattleaddr/js 0.3.0 or later. 0.3.0 is the first release that types the verify assessment fields (verdict, match_score, match_level, elements, changed_elements, unmatched_tokens, dropped_tokens). The API returns them to every caller regardless of SDK version; older SDKs do not describe them. @wattleaddr/react 0.2.0 depends on this release and re-exports the same types.

const r = await client.verify('100 collins st melbourne vic 3000', { session: createSession() });

switch (r.verdict) {
  case 'verified':   // everything you supplied agrees, and no other candidate fits
  case 'corrected':  // agrees as far as it goes; you abbreviated or omitted something, or, with extended_corrections: true, one part was fixed (see r.changed_elements)
    save(r.match!);                     // r.match.formatted is the canonical form
    break;
  case 'ambiguous':  // an element was contradicted, input was discarded, or a runner-up fits too
    review(r.match, r.alternatives);    // never auto-accept
    break;
  case 'unverified': // nothing usable matched: let the customer proceed, flag the row
    flagForReview();
    break;
}

match_level says how precisely we identified it (subpremise → premise → thoroughfare → locality → none) and is independent of verdict. premise + ambiguous can happen, and means we found a building on a street whose suburb you contradicted. match_score (0–100) is there for your own ranking. We publish no accept/reject line, because the right one depends on what a wrong address costs you.

Re-prompt for one field, not the whole address. elements carries a per-field status, so the form can ask again for exactly what is wrong:

// `changed` = you asserted a different value. This is why the address may be wrong.
// e.g. ['postcode'] for "1 Martin Place Sydney NSW 2001", where the record says 2000.
for (const field of r.changed_elements) askAgain(field);

// `missing` = not corroborated: either you didn't supply it, or the record hasn't got one.
// So check the record before prompting: a freestanding house always reports `subdwelling: missing`.
if (r.elements.subdwelling === 'missing' && r.match?.components.flat_number) {
  askAgain('unit');   // the matched record IS a unit and you named none, so never `verified`
}

That last case is why a scalar is not enough. "100 Collins St Melbourne VIC 3000" against Unit 5, 100 Collins Street scores 92 with changed_elements: []. Every element you gave is correct, but the verdict is ambiguous. In Australian delivery the missing unit is the difference between a delivered parcel and a returned one.

Check dropped_tokens too. Non-empty means the match answers a shorter query than the one you sent, and such a result is never verified. When the dropped token is the street number, verify returns match: null. G-NAF has no such number on that street, and the house next door is not the one you asked about. (Autocomplete is looser. It suggests the nearest numbers it finds and flags it with a street_number_not_found notice. When the building matches but the unit you typed has no exact match, it leads with the building and flags it with a unit_not_found notice.) unmatched_tokens holds words whose role in the address could not be determined. They are reported rather than guessed into a field.

Migrating from confidence. Top-level confidence on a verify response is deprecated. It is now a bucketing of match_score. Before August 2026 it was hardcoded to "high" on any match, including one obtained by discarding part of your query, so code branching on it was branching on a constant. It is still returned, and still 'high' | 'medium' | 'low', because it is a required field in the published contract. Switch to verdict and elements. (The confidence inside result.match is a different field. It is G-NAF's own confidence in the record, not a match score, and is not deprecated.)

Server-side use

Set keyTransport: 'header' to send a secret key as Authorization: Bearer and keep it out of request URLs and logs:

const client = new WattleAddrClient(process.env.WATTLEADDR_SECRET_KEY, {
  keyTransport: 'header',
});

In older runtimes without a global fetch, pass one: new WattleAddrClient(key, { fetch }).

API

new WattleAddrClient(key, options?)

| Option | Type | Default | Notes | | --- | --- | --- | --- | | baseUrl | string | https://api.wattleaddr.com.au | Override for self-host/testing. | | limit | number | 8 | Default autocomplete result count (1–20). | | state | AuState | none | Default state filter (NSW, VIC, …). | | keyTransport | 'query' \| 'header' | 'query' | query avoids a CORS preflight (browser); header keeps secret keys out of URLs. | | fetch | typeof fetch | global | Inject a fetch implementation. |

  • client.autocomplete(query, { session?, limit?, state?, signal? }) → AutocompleteResponse
  • client.retrieve(id, { session?, signal? }) → AddressRecord (billable)
  • client.verify(address, { session?, signal? }) → VerifyResponse (billable on a match; requires a plan with verify)
  • client.status({ signal? }) → StatusResponse

Errors

Any non-2xx response throws a WattleAddrError with .code (e.g. rate_limited, quota_exceeded, invalid_key), .status, .docs, and .retryAfter (on 429). Aborted requests reject with the native AbortError.

import { WattleAddrError } from '@wattleaddr/js';

try {
  await client.retrieve(id, { session });
} catch (e) {
  if (e instanceof WattleAddrError && e.code === 'quota_exceeded') showUpgradePrompt();
}

Using this with a coding agent

This package ships an agent skill, skills/wattleaddr/SKILL.md. It covers the things an assistant otherwise gets wrong: session tokens and what they cost, which key belongs in the browser, PO Boxes being absent from G-NAF by design, branching a verify result on verdict rather than the deprecated confidence, and how to store the components. It contains no telemetry and phones nothing home.

Claude Code / Claude Desktop

mkdir -p .claude/skills/wattleaddr
cp node_modules/@wattleaddr/js/skills/wattleaddr/SKILL.md .claude/skills/wattleaddr/

Cursor, Copilot, Windsurf or any other assistant: point it at the file, or fetch it directly:

curl -o WATTLEADDR.md https://wattleaddr.com.au/skills/wattleaddr/SKILL.md

License

MIT © RR Sols Pty Ltd t/a WattleAddr. Address data is © the Commonwealth of Australia (G-NAF), used under licence and served by the API. This package only talks to the API.

Links

  • Documentation and quickstart: https://wattleaddr.com.au/docs
  • Interactive API reference (no key needed): https://wattleaddr.com.au/docs/reference
  • Error codes: https://wattleaddr.com.au/docs/errors
  • Pricing (AUD, GST included; free tier, no card): https://wattleaddr.com.au/pricing
  • What G-NAF is, and what it can and cannot do: https://wattleaddr.com.au/blog/gnaf-explained-what-it-can-and-cant-do
  • Shopify app: https://wattleaddr.com.au/integrations/shopify
  • Status: https://wattleaddr.com.au/status
  • Support: [email protected]

Australian addresses only. Built on the open G-NAF dataset, served entirely from Australia. Not a postal-deliverability or AMAS/PAF service, and G-NAF holds no PO Boxes.