JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 217
  • Score
    100M100P100Q101924F
  • License MIT

TypeScript SDK for BatchData.io Property API - Types, Builders, and Utilities

Package Exports

  • @land-catalyst/batch-data-sdk
  • @land-catalyst/batch-data-sdk/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@land-catalyst/batch-data-sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@land-catalyst/batch-data-sdk

TypeScript SDK for BatchData.io Property API - Complete type definitions, fluent query builders, and utilities.

Installation

This package is published to the public npm registry. To install it:

npm install @land-catalyst/batch-data-sdk

Or add to package.json:

{
  "dependencies": {
    "@land-catalyst/batch-data-sdk": "^1.0.0"
  }
}

Features

  • Complete TypeScript Types - Full type definitions for all BatchData API endpoints and responses
  • Fluent Query Builders - Type-safe, chainable API for constructing search criteria
  • Property Search Response Types - Comprehensive types for property search results
  • HTTP Client - Ready-to-use API client for BatchData endpoints
  • Custom Error Classes - Specialized error handling for BatchData operations

Examples

Client Setup

import { BatchDataClient } from "@land-catalyst/batch-data-sdk";
import { ConsoleLogger } from "@land-catalyst/batch-data-sdk";

// Create a client instance
const client = new BatchDataClient({
  apiKey: process.env.BATCHDATA_API_KEY!,
  // Optional: provide custom logger
  logger: new ConsoleLogger(),
});
import {
  SearchCriteriaBuilder,
  PropertySearchRequestBuilder,
  PropertyLookupOptionsBuilder,
} from "@land-catalyst/batch-data-sdk";

// Simple search by location
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) => c.query("Maricopa County, AZ"))
  .options((o) => o.take(10).skip(0))
  .build();

const response = await client.searchProperties(request);
import {
  SearchCriteriaBuilder,
  PropertySearchRequestBuilder,
} from "@land-catalyst/batch-data-sdk";

// Search by address with filters
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix"))
          .state((state) => state.equals("AZ"))
          .zip((zip) => zip.inList(["85001", "85002", "85003"]))
          .street((street) => street.contains("Main"))
      )
  )
  .build();

const response = await client.searchProperties(request);

String Filter Examples

import { StringFilterBuilder } from "@land-catalyst/batch-data-sdk";

// All string filter methods can be chained
const filter = new StringFilterBuilder()
  .equals("Phoenix")
  .contains("Phx")
  .startsWith("Ph")
  .endsWith("ix")
  .matches(["Phoenix.*", ".*Arizona"])
  .inList(["Phoenix", "Tucson", "Flagstaff"])
  .build();

// Use in address search
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("AZ")
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix").contains("Phx"))
          .state((state) => state.inList(["AZ", "CA", "NV"]))
      )
  )
  .build();

Numeric Range Filter Examples

import { NumericRangeFilterBuilder } from "@land-catalyst/batch-data-sdk";

// Numeric ranges with min/max
const filter = new NumericRangeFilterBuilder()
  .min(100000)
  .max(500000)
  .build();

// Use in assessment search
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .assessment((a) =>
        a
          .totalAssessedValue((v) => v.min(100000).max(500000))
          .assessmentYear((y) => y.min(2020).max(2023))
      )
  )
  .build();

Date Range Filter Examples

import { DateRangeFilterBuilder } from "@land-catalyst/batch-data-sdk";

// Date ranges
const filter = new DateRangeFilterBuilder()
  .minDate("2020-01-01")
  .maxDate("2023-12-31")
  .build();

// Use in sale search
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("AZ")
      .sale((s) =>
        s.lastSaleDate((d) =>
          d.minDate("2020-01-01").maxDate("2023-12-31")
        )
      )
  )
  .build();
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .building((b) =>
        b
          .yearBuilt((y) => y.min(1990).max(2020))
          .bedroomCount((br) => br.min(3).max(5))
          .bathroomCount((ba) => ba.min(2).max(4))
          .totalBuildingAreaSquareFeet((area) => area.min(1500).max(3000))
          .buildingType((type) => type.equals("Single Family"))
          .pool((pool) => pool.equals("Yes"))
          .airConditioningSource((ac) => ac.contains("Central"))
      )
  )
  .build();
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .assessment((a) =>
        a
          .totalAssessedValue((v) => v.min(100000).max(500000))
          .totalMarketValue((v) => v.min(200000).max(600000))
          .assessmentYear((y) => y.min(2020).max(2023))
      )
  )
  .build();
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .demographics((d) =>
        d
          .age((a) => a.min(25).max(65))
          .income((i) => i.min(50000).max(150000))
          .netWorth((nw) => nw.min(100000).max(1000000))
          .householdSize((hs) => hs.min(2).max(5))
          .homeownerRenter((hr) => hr.equals("Homeowner"))
          .gender((g) => g.equals("M").inList(["M", "F"]))
          .hasChildren(true)
          .businessOwner((bo) => bo.equals("Yes"))
      )
  )
  .build();
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      // Address filters
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix"))
          .state((state) => state.equals("AZ"))
          .zip((zip) => zip.startsWith("85"))
      )
      // Building filters
      .building((b) =>
        b
          .yearBuilt((y) => y.min(1990).max(2020))
          .bedroomCount((br) => br.min(3).max(5))
          .bathroomCount((ba) => ba.min(2).max(4))
      )
      // Assessment filters
      .assessment((a) =>
        a
          .totalAssessedValue((v) => v.min(100000).max(500000))
          .assessmentYear((y) => y.min(2020).max(2023))
      )
      // Demographics filters
      .demographics((d) =>
        d
          .income((i) => i.min(50000).max(150000))
          .age((a) => a.min(25).max(65))
      )
      // Quick list
      .quickList("vacant")
  )
  .options((o) =>
    o
      .take(50)
      .skip(0)
      .bedrooms(3, 5)
      .bathrooms(2, 4)
      .yearBuilt(1990, 2020)
      .skipTrace(true)
      .images(true)
      .quicklistCounts(true)
  )
  .build();

Property Search with Options

const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) => c.query("Maricopa County, AZ"))
  .options((o) =>
    o
      // Pagination
      .pagination(0, 50)
      .skip(0)
      .take(50)
      // Distance filtering
      .distance(5) // 5 miles
      .distance(undefined, 8800, 26400) // yards and feet
      .distance(undefined, undefined, undefined, 8, 8047) // km and meters
      // Bounding box
      .boundingBox(
        { latitude: 33.5, longitude: -112.1 },
        { latitude: 33.4, longitude: -112.0 }
      )
      // Property features
      .bedrooms(3, 5)
      .bathrooms(2, 4)
      .stories(1, 2)
      .area(80, 120) // percentage
      .yearBuilt(1990, 2020)
      .lotSize(90, 110) // percentage
      // Flags
      .skipTrace(true)
      .aggregateLoanTypes(true)
      .images(true)
      .showRequests(true)
      .areaPolygon(true)
      .quicklistCounts(true)
      .useSubdivision(true)
      // Formatting
      .dateFormat("YYYY-MM-DD")
      // Sorting
      .sort("totalAssessedValue", "desc", "session-12345")
      .build()
  )
  .build();

Property Subscription

import {
  PropertySubscriptionBuilder,
  DeliveryConfigBuilder,
} from "@land-catalyst/batch-data-sdk";

// Webhook subscription
const subscription = new PropertySubscriptionBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix"))
          .state((state) => state.equals("AZ"))
      )
      .quickList("vacant")
  )
  .deliveryConfig((dc) =>
    dc.webhook("https://example.com/webhook", {
      "X-API-Key": "secret",
    })
  )
  .build();

const response = await client.createPropertySubscription(subscription);

Property Subscription with Kinesis

const subscription = new PropertySubscriptionBuilder()
  .searchCriteria((c) => c.query("US").quickList("owner-occupied"))
  .deliveryConfig((dc) =>
    dc.kinesis(
      "my-stream",
      "us-east-1",
      "access-key-id",
      "secret-access-key"
    )
  )
  .build();

Property Subscription with Event Hub

const subscription = new PropertySubscriptionBuilder()
  .searchCriteria((c) => c.query("Phoenix, AZ").orQuickLists(["on-market"]))
  .deliveryConfig((dc) =>
    dc.eventHub({
      fullyQualifiedNamespace: "mynamespace.servicebus.windows.net",
      clientId: "12345678-1234-1234-1234-123456789abc",
      tenantId: "87654321-4321-4321-4321-cba987654321",
      clientSecret: "your-client-secret",
      eventHubName: "property-events",
    })
  )
  .build();

Property Lookup by Address

import {
  PropertyLookupRequestBuilder,
  PropertyLookupRequestItemBuilder,
} from "@land-catalyst/batch-data-sdk";

// Single property lookup
const request = new PropertyLookupRequestBuilder()
  .addItem(
    new PropertyLookupRequestItemBuilder()
      .address({
        street: "2800 N 24th St",
        city: "Phoenix",
        state: "AZ",
        zip: "85008",
      })
      .build()
  )
  .options((o) => o.take(1).skipTrace(true))
  .build();

const response = await client.lookupProperty(request);

Property Lookup by Multiple Identifiers

const request = new PropertyLookupRequestBuilder()
  .addItem(
    new PropertyLookupRequestItemBuilder()
      .propertyId("12345")
      .build()
  )
  .addItem(
    new PropertyLookupRequestItemBuilder()
      .apn("123-45-678")
      .countyFipsCode("04013")
      .build()
  )
  .addItem(
    new PropertyLookupRequestItemBuilder()
      .hash("abc123def456")
      .build()
  )
  .options((o) =>
    o
      .take(10)
      .images(true)
      .skipTrace(true)
      .showRequests(true)
  )
  .build();

Async Property Lookup

import {
  PropertyLookupAsyncRequestBuilder,
  AsyncPropertyLookupOptionsBuilder,
} from "@land-catalyst/batch-data-sdk";

const request = new PropertyLookupAsyncRequestBuilder()
  .addItem(
    new PropertyLookupRequestItemBuilder()
      .address({
        street: "2800 N 24th St",
        city: "Phoenix",
        state: "AZ",
        zip: "85008",
      })
      .build()
  )
  .options(
    new AsyncPropertyLookupOptionsBuilder()
      .webhook(
        "https://example.com/webhook",
        "https://example.com/error-webhook"
      )
      .build()
  )
  .build();

const response = await client.lookupPropertyAsync(request);
import { PropertySearchAsyncRequestBuilder } from "@land-catalyst/batch-data-sdk";

const request = new PropertySearchAsyncRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix"))
          .state((state) => state.equals("AZ"))
      )
  )
  .options(
    new AsyncPropertyLookupOptionsBuilder()
      .webhook("https://example.com/webhook")
      .take(100)
      .build()
  )
  .build();

const response = await client.searchPropertiesAsync(request);

Address Verification

const request = {
  requests: [
    {
      street: "2800 N 24th St",
      city: "Phoenix",
      state: "Arizona",
      zip: "85008",
    },
  ],
};

const response = await client.verifyAddress(request);

Address Autocomplete

const request = {
  searchCriteria: {
    query: "2800 N 24th St Phoenix",
  },
  options: {
    skip: 0,
    take: 5,
  },
};

const response = await client.autocompleteAddress(request);

Geocoding

// Geocode address to coordinates
const geocodeRequest = {
  requests: [
    {
      address: "2800 N 24th St, Phoenix, AZ, 85008",
    },
  ],
};

const geocodeResponse = await client.geocodeAddress(geocodeRequest);

// Reverse geocode coordinates to address
const reverseGeocodeRequest = {
  request: {
    latitude: 33.47865,
    longitude: -112.03029,
  },
};

const reverseGeocodeResponse =
  await client.reverseGeocodeAddress(reverseGeocodeRequest);

Builder Accumulation Pattern

// Multiple calls to the same method accumulate changes
const criteria = new AddressSearchCriteriaBuilder()
  .street((s) => s.equals("Main"))
  .street((s) => s.contains("St"))
  .street((s) => s.startsWith("M"))
  .build();

// Result: { street: { equals: "Main", contains: "St", startsWith: "M" } }

// Direct values replace previous builder configuration
const criteria2 = new AddressSearchCriteriaBuilder()
  .street((s) => s.equals("Main St"))
  .street({ equals: "New Street" }) // Replaces previous
  .build();

// Result: { street: { equals: "New Street" } }

Using Direct Values vs Lambda Configurators

// Both patterns are supported - use whichever is more convenient

// Lambda pattern (recommended for complex configurations)
const request1 = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("AZ")
      .address((a) =>
        a
          .city((city) => city.equals("Phoenix").contains("Phx"))
          .state((state) => state.inList(["AZ", "CA"]))
      )
  )
  .build();

// Direct value pattern (useful for simple cases)
const request2 = new PropertySearchRequestBuilder()
  .searchCriteria({
    query: "AZ",
    address: {
      city: { equals: "Phoenix" },
      state: { equals: "AZ" },
    },
  })
  .options({
    take: 10,
    skip: 0,
  })
  .build();
// Ultra-complex search with all criteria types
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Maricopa County, AZ")
      // Address with multiple filters
      .address((a) =>
        a
          .street((s) =>
            s
              .equals("Main")
              .contains("Street")
              .startsWith("M")
              .endsWith("St")
              .inList(["Main St", "Main Street", "Main Ave"])
          )
          .city((city) =>
            city.equals("Phoenix").contains("Phx").matches(["Phoenix.*"])
          )
          .state((state) => state.equals("AZ"))
          .zip((zip) => zip.startsWith("85").endsWith("01"))
      )
      // Assessment with accumulation
      .assessment((a) =>
        a
          .assessmentYear((y) => y.min(2020).max(2023))
          .assessmentYear((y) => y.min(2019)) // Accumulates
          .totalAssessedValue((v) => v.min(100000).max(500000))
          .totalAssessedValue((v) => v.min(150000)) // Accumulates
      )
      // Building criteria
      .building((b) =>
        b
          .buildingType((t) =>
            t.equals("Single Family").inList([
              "Single Family",
              "Townhouse",
              "Condo",
            ])
          )
          .yearBuilt((y) => y.min(1990).max(2020))
          .bedroomCount((br) => br.min(3).max(5))
          .bathroomCount((ba) => ba.min(2).max(4))
          .pool((pool) => pool.equals("Yes"))
      )
      // Demographics
      .demographics((d) =>
        d
          .age((a) => a.min(25).max(65))
          .income((i) => i.min(50000).max(150000))
          .income((i) => i.min(60000)) // Accumulates
          .gender((g) => g.equals("M").inList(["M", "F"]))
          .hasChildren(true)
      )
      // Quick lists
      .quickList("vacant")
  )
  .options((o) =>
    o
      .pagination(100, 50)
      .distance(5, 8800, 26400, 8, 8047)
      .bedrooms(3, 5)
      .bathrooms(2, 4)
      .yearBuilt(1990, 2020)
      .skipTrace(true)
      .images(true)
      .sort("totalAssessedValue", "desc", "session-12345")
      .build()
  )
  .build();

Error Handling

import { PropertyCountLimitExceededError } from "@land-catalyst/batch-data-sdk";

try {
  const response = await client.searchProperties(request);
} catch (error) {
  if (error instanceof PropertyCountLimitExceededError) {
    console.log(`Limit exceeded: ${error.count} > ${error.limit}`);
    console.log(`Limit type: ${error.limitType}`); // "sync" or "async"
  } else if (error instanceof Error) {
    console.error("API error:", error.message);
  }
}

Using Builders with Existing Data

// Create builder from existing criteria
const existingCriteria: SearchCriteria = {
  query: "AZ",
  address: {
    city: { equals: "Phoenix" },
  },
};

const builder = SearchCriteriaBuilder.from(existingCriteria);
builder.address((a) => a.state((s) => s.equals("AZ")));

const updatedCriteria = builder.build();

GeoLocation Filters

import { GeoLocationFactory } from "@land-catalyst/batch-data-sdk";

// Distance-based search
const request = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Phoenix, AZ")
      .address((a) =>
        a.geoLocationDistance(33.4484, -112.074, {
          miles: "5",
        })
      )
  )
  .build();

// Bounding box search
const request2 = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Phoenix, AZ")
      .address((a) =>
        a.geoLocationBoundingBox(
          33.5, // NW latitude
          -112.1, // NW longitude
          33.4, // SE latitude
          -112.0 // SE longitude
        )
      )
  )
  .build();

// Polygon search
const request3 = new PropertySearchRequestBuilder()
  .searchCriteria((c) =>
    c
      .query("Phoenix, AZ")
      .address((a) =>
        a.geoLocationPolygon([
          { latitude: 33.5, longitude: -112.1 },
          { latitude: 33.4, longitude: -112.1 },
          { latitude: 33.4, longitude: -112.0 },
          { latitude: 33.5, longitude: -112.0 },
        ])
      )
  )
  .build();

All Search Criteria Types

// Foreclosure search
.searchCriteria((c) =>
  c
    .query("AZ")
    .foreclosure((f) =>
      f
        .status((s) => s.equals("Active"))
        .recordingDate((d) => d.minDate("2020-01-01"))
    )
)

// Listing search
.listing((l) =>
  l
    .price((p) => p.min(200000).max(500000))
    .status((s) => s.equals("Active"))
    .daysOnMarket((d) => d.min(0).max(90))
)

// Sale search
.sale((s) =>
  s
    .lastSalePrice((p) => p.min(100000).max(400000))
    .lastSaleDate((d) => d.minDate("2020-01-01"))
)

// Owner search
.owner((o) =>
  o
    .firstName((f) => f.equals("John"))
    .lastName((l) => l.equals("Doe"))
    .ownerOccupied(true)
)

// Open lien search
.openLien((ol) =>
  ol
    .totalOpenLienCount((c) => c.min(1).max(5))
    .totalOpenLienBalance((b) => b.min(50000).max(500000))
)

// Permit search
.permit((p) =>
  p
    .permitCount((c) => c.min(1).max(10))
    .totalJobValue((v) => v.min(10000).max(100000))
)

// Tax search
.tax((t) => t.taxDelinquentYear((y) => y.min(2020).max(2023)))

// Valuation search
.valuation((v) =>
  v
    .estimatedValue((ev) => ev.min(200000).max(600000))
    .ltv((ltv) => ltv.min(0).max(80))
)

// Intel search
.intel((i) =>
  i
    .lastSoldPrice((p) => p.min(150000).max(450000))
    .lastSoldDate((d) => d.minDate("2020-01-01"))
    .salePropensity((sp) => sp.min(50).max(100))
)

Quick Lists

// Single quick list
.quickList("vacant")

// Multiple quick lists (AND)
.quickLists(["vacant", "owner-occupied", "high-equity"])

// Multiple quick lists (OR)
.orQuickLists(["vacant", "preforeclosure"])

// Exclude quick list
.quickList("not-vacant")

Sorting and Pagination

.options((o) =>
  o
    // Pagination
    .pagination(0, 50) // skip 0, take 50
    .skip(100) // Override skip
    .take(25) // Override take
    // Sorting
    .sort("totalAssessedValue", "desc")
    .sort("yearBuilt", "asc", "session-12345") // With session ID
    .build()
)

Response Handling

const response = await client.searchProperties(request);

// Check status
if (response.status?.code === 200) {
  console.log("Success!");
}

// Access properties
if (response.results?.properties) {
  response.results.properties.forEach((property) => {
    console.log(property.address?.street);
    console.log(property.valuation?.estimatedValue);
    console.log(property.demographics?.income);
    console.log(property.building?.yearBuilt);
  });
}

// Access metadata
if (response.results?.meta) {
  console.log(
    `Found ${response.results.meta.results?.resultsFound} properties`
  );
  console.log(
    `Request took ${response.results.meta.performance?.totalRequestTime}ms`
  );
}

// Quick list counts
if (response.results?.quicklistCounts) {
  response.results.quicklistCounts.forEach((count) => {
    console.log(`${count.name}: ${count.count}`);
  });
}

API Documentation

Types

  • SearchCriteria - Complete search criteria structure
  • PropertySubscriptionRequest - Subscription creation request
  • PropertySubscriptionResponse - Subscription creation response
  • PropertySearchResponse - Property search API response
  • Property - Individual property object with all nested data
  • DeliveryConfig - Webhook, Kinesis, or Event Hub delivery configuration
  • EventHubConfig - Azure Event Hub configuration

Builders

All builders follow a fluent, chainable pattern:

  • SearchCriteriaBuilder - Main search criteria builder
  • AddressSearchCriteriaBuilder - Address filters
  • BuildingSearchCriteriaBuilder - Building/property filters
  • StringFilterBuilder - String comparison filters
  • NumericRangeFilterBuilder - Numeric range filters
  • DateRangeFilterBuilder - Date range filters
  • And many more...

Errors

  • PropertyCountLimitExceededError - Thrown when search results exceed configured limits

Development

# Install dependencies
npm install

# Build the package
npm run build

# Run tests
npm test

# Run integration tests (requires BATCHDATA_API_KEY)
npm run test:integration

# The built files will be in ./dist

Running Integration Tests

Integration tests make real API calls and require a valid API key:

export BATCHDATA_API_KEY=your_api_key_here
npm run test:integration

Publishing

This package is published to npm using trusted publishing (OIDC) via GitHub Actions. Publishing happens automatically when a version tag is pushed.

Manual Publishing (if needed)

If you need to publish manually, set up .npmrc to use your npm token:

  1. Create .npmrc file (add to .gitignore - already configured):

    # Option 1: Use environment variable (recommended)
    echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc
    
    # Option 2: Use token directly (less secure)
    echo "//registry.npmjs.org/:_authToken=your-token-here" > .npmrc
  2. Set your npm token as an environment variable:

    export NPM_TOKEN=your-npm-token-here
  3. Publish:

    npm run build
    npm publish --access public

Note: The .npmrc file is already in .gitignore so it won't be committed. See .npmrc.example for a template.

Setting Up GitHub Actions Publishing (Trusted Publishing)

The workflow uses npm trusted publishing (OIDC) - no tokens needed! To set it up:

  1. Initial Manual Publish (one-time, to create the package on npm):

    # Set up your .npmrc first (see Manual Publishing section above)
    npm run build
    npm publish --access public
  2. Enable Trusted Publishing on npm:

    • Go to npmjs.com and log in
    • Navigate to your package: @land-catalyst/batch-data-sdk
    • Go to Package SettingsAutomationTrusted Publishing
    • Click Add GitHub Actions workflow
    • Select repository: land-catalyst/batch-data-sdk
    • Select workflow file: .github/workflows/cd.yml
    • Click Approve
  3. Verify Setup:

    • After enabling, the workflow will automatically publish to npm when you push a version tag
    • No authentication tokens needed - OIDC handles it automatically!
    • The package will show a verified checkmark on npm

For detailed setup instructions, see SETUP_NPM_PUBLISHING.md.

Versioning

To create a new version:

# Patch version (1.1.12 -> 1.1.13)
npm run v:patch

# Minor version (1.1.12 -> 1.2.0)
npm run v:minor

# Major version (1.1.12 -> 2.0.0)
npm run v:major

This will:

  1. Update the version in package.json
  2. Create a git tag
  3. Push the tag to GitHub
  4. Trigger the CD workflow to publish to npm

License

MIT