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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@land-catalyst/batch-data-sdk

v1.1.12

Published

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

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(),
});

Basic Property Search

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);

Address-Based Search

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();

Building Criteria Search

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();

Assessment Criteria Search

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();

Demographics Criteria Search

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();

Complex Multi-Criteria Search

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 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);

Async Property Search

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();

Complex Nested Search

// 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 or Kinesis delivery 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