{"_id":"@ejosterberg/opensalestax","_rev":"3-c8a7b2e2faa74651aab4d49d7c879255","name":"@ejosterberg/opensalestax","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@ejosterberg/opensalestax","version":"0.1.0","keywords":["opensalestax","tax","sales-tax","us-tax","sdk","self-hosted"],"author":{"name":"Eric Osterberg","email":"ejosterberg@gmail.com"},"license":"Apache-2.0","_id":"@ejosterberg/opensalestax@0.1.0","maintainers":[{"name":"ejosterberg","email":"ejosterberg@gmail.com"}],"homepage":"https://github.com/ejosterberg/opensalestax-js","bugs":{"url":"https://github.com/ejosterberg/opensalestax-js/issues"},"dist":{"shasum":"816679b53606c9b0fbb4c2c4648c65a24305493b","tarball":"https://registry.npmjs.org/@ejosterberg/opensalestax/-/opensalestax-0.1.0.tgz","fileCount":66,"integrity":"sha512-+h7AuLSgLwM+OKstwk/cKhsdqz8K0lATJoVSpwm7pxuWoyXyDLn9nigain6nuOncXPYgyEMUagiJiDPZxbxzRg==","signatures":[{"sig":"MEUCIQDOUYv53C+ngTiSKLgn15aTI+Eok+fOG+KqEMfPAVYy7QIgPn4HmrDsOOURGCe3uWeTCHGCveMlztAL3aK/v1thh3o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":148563},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"gitHead":"31c4d3e7b250f986c9ca9ad2c7af81e23341c5dc","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"jest","build":"npm run build:types && npm run build:esm && npm run build:cjs && node scripts/fix-cjs.cjs","check":"npm run lint && npm run typecheck && npm test -- --coverage && npm audit --omit=dev --audit-level=high","clean":"rimraf dist coverage","build:cjs":"tsc -p tsconfig.build.json --module CommonJS --outDir dist/cjs --declaration --declarationMap","build:esm":"tsc -p tsconfig.build.json --module ESNext --outDir dist/esm --declaration --declarationMap","typecheck":"tsc --noEmit","build:types":"tsc -p tsconfig.build.json --emitDeclarationOnly --declaration --declarationDir dist/types","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"ejosterberg","email":"ejosterberg@gmail.com"},"repository":{"url":"git+https://github.com/ejosterberg/opensalestax-js.git","type":"git"},"_npmVersion":"11.4.2","description":"TypeScript SDK for the self-hosted OpenSalesTax engine — destination-based US sales tax via a v1 HTTP API","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^9.18.0","rimraf":"^6.0.1","ts-jest":"^29.2.5","@eslint/js":"^9.39.4","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^20.17.10","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/opensalestax_0.1.0_1778856667981_0.4584558253822826","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ejosterberg/opensalestax","version":"0.2.0","keywords":["opensalestax","tax","sales-tax","us-tax","sdk","self-hosted"],"author":{"name":"Eric Osterberg","email":"ejosterberg@gmail.com"},"license":"Apache-2.0","_id":"@ejosterberg/opensalestax@0.2.0","maintainers":[{"name":"ejosterberg","email":"ejosterberg@gmail.com"}],"homepage":"https://github.com/ejosterberg/opensalestax-js","bugs":{"url":"https://github.com/ejosterberg/opensalestax-js/issues"},"dist":{"shasum":"c6e3ace1b0fdeb5de71328dfb486378bb80e515f","tarball":"https://registry.npmjs.org/@ejosterberg/opensalestax/-/opensalestax-0.2.0.tgz","fileCount":66,"integrity":"sha512-lQkMgoPBTBD4Qv5fuQZYJuJOj4L9vJkCjldeuZPQ1oT+qrwwYRQ5+8Iq8AmE3/5s1d6Gk2svB/1XoFY9nh4UHg==","signatures":[{"sig":"MEYCIQDUghhJ7G/uxjkFOgQn5j/F/sRZWpqSZRivAPn9bHUBLQIhALoZniWtWjIRM9WB8RRjLa16KNptzwdGzwQWKcQc9Gjm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ejosterberg%2fopensalestax@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":174830},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"gitHead":"6bb08e1de0769bf8d17ba9140bc94ecfbf5eae3e","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"jest","build":"npm run build:types && npm run build:esm && npm run build:cjs && node scripts/fix-cjs.cjs","check":"npm run lint && npm run typecheck && npm test -- --coverage && npm audit --omit=dev --audit-level=high","clean":"rimraf dist coverage","build:cjs":"tsc -p tsconfig.build.json --module CommonJS --outDir dist/cjs --declaration --declarationMap","build:esm":"tsc -p tsconfig.build.json --module ESNext --outDir dist/esm --declaration --declarationMap","typecheck":"tsc --noEmit","build:types":"tsc -p tsconfig.build.json --emitDeclarationOnly --declaration --declarationDir dist/types","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0bc28aca-30b7-472e-a9d7-aa05ac90be85"}},"repository":{"url":"git+https://github.com/ejosterberg/opensalestax-js.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript SDK for the self-hosted OpenSalesTax engine — destination-based US sales tax via a v1 HTTP API","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^9.18.0","rimraf":"^6.0.1","ts-jest":"^29.2.5","@eslint/js":"^9.39.4","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^20.17.10","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/opensalestax_0.2.0_1779163839882_0.24061924159074732","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ejosterberg/opensalestax","version":"0.3.0","description":"TypeScript SDK for the self-hosted OpenSalesTax engine — destination-based US sales tax via a v1 HTTP API","license":"Apache-2.0","author":{"name":"Eric Osterberg","email":"ejosterberg@gmail.com"},"homepage":"https://github.com/ejosterberg/opensalestax-js","bugs":{"url":"https://github.com/ejosterberg/opensalestax-js/issues"},"repository":{"type":"git","url":"git+https://github.com/ejosterberg/opensalestax-js.git"},"keywords":["opensalestax","tax","sales-tax","us-tax","sdk","self-hosted"],"engines":{"node":">=20"},"type":"module","main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/types/index.d.ts","exports":{".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}}},"scripts":{"build:esm":"tsc -p tsconfig.build.json --module ESNext --outDir dist/esm --declaration --declarationMap","build:cjs":"tsc -p tsconfig.build.json --module CommonJS --outDir dist/cjs --declaration --declarationMap","build:types":"tsc -p tsconfig.build.json --emitDeclarationOnly --declaration --declarationDir dist/types","build":"npm run build:types && npm run build:esm && npm run build:cjs && node scripts/fix-cjs.cjs","clean":"rimraf dist coverage","test":"jest","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","typecheck":"tsc --noEmit","check":"npm run lint && npm run typecheck && npm test -- --coverage && npm audit --omit=dev --audit-level=high","prepublishOnly":"npm run clean && npm run build"},"devDependencies":{"@eslint/js":"^9.39.4","@types/jest":"^29.5.14","@types/node":"^20.17.10","eslint":"^9.18.0","jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.5","typescript":"^5.7.2","typescript-eslint":"^8.20.0"},"gitHead":"08ad3e47ec129094b19a74dcb3594f7bfe231d2a","_id":"@ejosterberg/opensalestax@0.3.0","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-NF/3phNcaOhDeJnk5ZE13bggwjQzOyMW1MSRskuac2KFpxXZUjrg1HCsuZcoM9CK886R+C7IMPjv5MWItJ/BgQ==","shasum":"8c4685babd70770e42bff0720ccc53d809bd0eba","tarball":"https://registry.npmjs.org/@ejosterberg/opensalestax/-/opensalestax-0.3.0.tgz","fileCount":66,"unpackedSize":199190,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ejosterberg%2fopensalestax@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCo87U3vNKlHJHY4BhoqDrrDKMZU3zXUQV5EB7nVlHMUQIgOomQlPf+MPRaOu0xZrYK5SB+5P4e5jvOvYgdy3DrXoU="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0bc28aca-30b7-472e-a9d7-aa05ac90be85"}},"directories":{},"maintainers":[{"name":"ejosterberg","email":"ejosterberg@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/opensalestax_0.3.0_1779212696836_0.4735481452100445"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-15T14:51:07.863Z","modified":"2026-05-19T17:44:57.223Z","0.1.0":"2026-05-15T14:51:08.139Z","0.2.0":"2026-05-19T04:10:40.033Z","0.3.0":"2026-05-19T17:44:56.967Z"},"bugs":{"url":"https://github.com/ejosterberg/opensalestax-js/issues"},"author":{"name":"Eric Osterberg","email":"ejosterberg@gmail.com"},"license":"Apache-2.0","homepage":"https://github.com/ejosterberg/opensalestax-js","keywords":["opensalestax","tax","sales-tax","us-tax","sdk","self-hosted"],"repository":{"type":"git","url":"git+https://github.com/ejosterberg/opensalestax-js.git"},"description":"TypeScript SDK for the self-hosted OpenSalesTax engine — destination-based US sales tax via a v1 HTTP API","maintainers":[{"name":"ejosterberg","email":"ejosterberg@gmail.com"}],"readme":"# @ejosterberg/opensalestax\n\n[![npm](https://img.shields.io/npm/v/@ejosterberg/opensalestax.svg)](https://www.npmjs.com/package/@ejosterberg/opensalestax)\n[![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n\nTypeScript SDK for the self-hosted **OpenSalesTax** engine — destination-based\nUS sales tax via the v1 HTTP API.\n\n> Calculation only. The merchant is solely responsible for tax-collection\n> accuracy and remittance to the appropriate jurisdictions. Verify against\n> your state Department of Revenue before remitting.\n\nThis is the **JavaScript / TypeScript SDK**. Sibling packages cover other\nlanguages:\n\n- Python — [`opensalestax`](https://pypi.org/project/opensalestax/) on PyPI\n- PHP — [`ejosterberg/opensalestax`](https://packagist.org/packages/ejosterberg/opensalestax) on Packagist\n\n## Install\n\n```bash\nnpm install @ejosterberg/opensalestax\n```\n\nRequires Node 20+ (uses built-in `fetch`, `URL`, `AbortController`).\nZero runtime dependencies.\n\n## Quick start\n\n```ts\nimport { OpenSalesTaxClient } from '@ejosterberg/opensalestax';\n\nconst client = new OpenSalesTaxClient({\n  baseUrl: 'https://engine.example.com',\n});\n\nconst health = await client.health();\nconsole.log(health); // { status: 'ok', version: '0.55.4', databaseConnected: true }\n\nconst result = await client.calculate(\n  { zip5: '55401' },\n  [{ amount: '100.00', category: 'general' }],\n);\nconsole.log(result.taxTotal);     // \"9.0250\"\nconsole.log(result.lines[0].ratePct); // \"9.02500\"\nfor (const j of result.lines[0].jurisdictions) {\n  console.log(`${j.type}: ${j.name} @ ${j.ratePct}% → $${j.tax}`);\n}\n```\n\n## API\n\n### `new OpenSalesTaxClient(options)`\n\n| Option | Type | Default | Notes |\n|---|---|---|---|\n| `baseUrl` | `string` | (required) | Engine base URL, e.g. `https://engine.example.com` |\n| `apiKey` | `string \\| null` | `null` | Sent as `Authorization: Bearer <key>` |\n| `timeoutMs` | `number` | `10000` | Per-request timeout via `AbortController` |\n| `userAgent` | `string \\| null` | `null` | Suffix appended to default `opensalestax-js/x.y.z` UA |\n| `verify` | `boolean` | `true` | Reserved — TLS bypass requires injecting a custom `fetch` (see below) |\n| `allowPrivate` | `boolean` | `false` | Permit loopback / RFC-1918 / link-local engine URLs |\n| `fetch` | `FetchFn` | `globalThis.fetch` | Injectable `fetch`; useful for tests + instrumentation |\n\n### Methods\n\n| Method | Returns | Endpoint |\n|---|---|---|\n| `health()` | `Promise<HealthResponse>` | `GET /v1/health` |\n| `healthCheck()` | `Promise<HealthCheckResult>` | `GET /v1/health` — **never throws**; returns `{ ok, rttMs, ... }` |\n| `states()` | `Promise<StatesResponse>` | `GET /v1/states` |\n| `rates(zip5, zip4?)` | `Promise<RateStack>` | `GET /v1/rates?zip5=...&zip4=...` |\n| `calculate(address, lineItems)` | `Promise<CalculationResult>` | `POST /v1/calculate` |\n| `close()` | `void` | no-op; provided for cross-SDK parity |\n\n### Errors\n\n```\nOpenSalesTaxError\n├── OpenSalesTaxNetworkError    (timeout / DNS / TCP RST / TLS)\n├── OpenSalesTaxAPIError        (engine returned non-2xx; .statusCode + .responseBody)\n├── OpenSalesTaxValidationError (response shape mismatch — likely engine/SDK version drift)\n└── NonUSDError                 (consumer passed non-US data)\n```\n\nTypical consumer pattern:\n\n```ts\nimport {\n  OpenSalesTaxClient,\n  OpenSalesTaxAPIError,\n  OpenSalesTaxNetworkError,\n} from '@ejosterberg/opensalestax';\n\ntry {\n  const result = await client.calculate(address, lineItems);\n} catch (err) {\n  if (err instanceof OpenSalesTaxNetworkError) {\n    // fail-soft: log + continue with empty tax lines (per-connector policy)\n  } else if (err instanceof OpenSalesTaxAPIError && err.statusCode >= 500) {\n    // engine glitch — fail-soft\n  } else if (err instanceof OpenSalesTaxAPIError) {\n    // 4xx — surface to operator (bad config / bad data)\n  } else {\n    throw err;\n  }\n}\n```\n\n## Data shape\n\nMoney and rates are **decimal strings** end-to-end. The engine quantizes\nper-jurisdiction, and the SDK never coerces to float. Use a decimal\nlibrary (`decimal.js`, `big.js`) for math; convert to `Number` only at the\nlast possible step.\n\n| Wire | TS surface | Type |\n|---|---|---|\n| `subtotal` | `subtotal` | `string` (`\"100.00\"`) |\n| `tax_total` | `taxTotal` | `string` (`\"9.0250\"`) |\n| `rate_pct` | `ratePct` | `string` (`\"6.87500\"` means 6.875%) |\n| `database_connected` | `databaseConnected` | `boolean` |\n| `has_sales_tax` | `hasSalesTax` | `boolean` |\n| `sst_member` | `sstMember` | `boolean` |\n| `combined_rate_pct` | `combinedRatePct` | `string` |\n\n## SSRF defense\n\nThe SDK refuses to instantiate a client whose `baseUrl` resolves to:\n\n- A non-`http:` / non-`https:` scheme (no `file:`, `javascript:`, `gopher:`)\n- A loopback hostname (`localhost`, `127.0.0.0/8`, `::1`)\n- A private IPv4 range (`10/8`, `172.16/12`, `192.168/16`, `169.254/16`)\n- An IPv6 unique-local (`fc00::/7`) or link-local (`fe80::/10`)\n\n…unless you pass `allowPrivate: true`. This blocks the\ncommon operator-misconfig SSRF where an engine URL ends up pointing back\ninside the cluster (e.g. accidentally at `http://169.254.169.254/`, the\ncloud metadata endpoint).\n\nMost deployments need `allowPrivate: true` — the engine usually lives on\nthe same private network as the merchant's store. The opt-in forces the\noperator to acknowledge the network shape.\n\nDNS rebinding is **not** in scope: the engine URL is set by the merchant\nin their own process, not by an end user.\n\n## License\n\n[Apache-2.0](LICENSE).\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md). DCO sign-off (`git commit -s`)\nrequired on every commit; CI enforces.\n\n## Security\n\nSee [`SECURITY.md`](SECURITY.md) for the disclosure policy.\n","readmeFilename":"README.md"}