Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b592961
codegen metadata
stainless-app[bot] Jun 10, 2025
1678c34
fix: publish script — handle NPM errors correctly
stainless-app[bot] Jun 13, 2025
9a95185
chore(internal): make base APIResource abstract
stainless-app[bot] Jun 13, 2025
0e61c32
feat(client): add support for endpoint-specific base URLs
stainless-app[bot] Jun 16, 2025
77a5b1e
chore(ci): enable for pull requests
stainless-app[bot] Jun 17, 2025
63e9ce1
feat(api): api update
stainless-app[bot] Jun 17, 2025
81496fc
feat(api): api update
stainless-app[bot] Jun 17, 2025
44d2c02
feat(api): api update
stainless-app[bot] Jun 18, 2025
509fcfb
refactor(types): replace Record with mapped types
stainless-app[bot] Jun 23, 2025
fc96b5b
feat(api): api update
stainless-app[bot] Jun 26, 2025
d9bddc1
fix(ci): release-doctor — report correct token name
stainless-app[bot] Jun 26, 2025
5b3d838
chore(ci): only run for pushes and fork pull requests
stainless-app[bot] Jun 27, 2025
132b903
chore: mention unit type in timeout docs
stainless-app[bot] Jul 2, 2025
33ba457
fix(client): don't send `Content-Type` for bodyless methods
stainless-app[bot] Jul 2, 2025
ecfef87
feat(api): api update
stainless-app[bot] Jul 4, 2025
b4f8f97
feat(api): api update
stainless-app[bot] Jul 8, 2025
c7e0dab
chore: make some internal functions async
stainless-app[bot] Jul 9, 2025
54fb67f
feat(api): api update
stainless-app[bot] Jul 16, 2025
1939793
feat(api): api update
stainless-app[bot] Jul 16, 2025
5f861b0
feat(api): api update
stainless-app[bot] Jul 16, 2025
36c0248
codegen metadata
stainless-app[bot] Jul 17, 2025
d89af51
chore(internal): bump test timeout
TomerAberbach Jul 17, 2025
06586f7
release: 5.1.0
stainless-app[bot] Jul 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ on:
- 'integrated/**'
- 'stl-preview-head/**'
- 'stl-preview-base/**'
pull_request:
branches-ignore:
- 'stl-preview-head/**'
- 'stl-preview-base/**'

jobs:
lint:
timeout-minutes: 10
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/orb-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4

Expand All @@ -31,6 +36,7 @@ jobs:
timeout-minutes: 5
name: build
runs-on: ${{ github.repository == 'stainless-sdks/orb-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
permissions:
contents: read
id-token: write
Expand Down Expand Up @@ -66,6 +72,7 @@ jobs:
timeout-minutes: 10
name: test
runs-on: ${{ github.repository == 'stainless-sdks/orb-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "5.0.0"
".": "5.1.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 116
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-c2a4808c828c8288c5c8dfe2fdfa51d4d7c1bcc33cacc6b859d0cf4b35ce95cc.yml
openapi_spec_hash: a2b5a1bfabbd03dd1b411791576eb502
config_hash: 3c3524be9607afb24d2139ce26ce5389
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-612316c13276a207f56e2e2c7bbc68f4bb73de85e3661595a23f23d9ccc80276.yml
openapi_spec_hash: 6e125f05e40521ec485edf6e15beec2e
config_hash: 8c9a47f104c777e2a1e8f3fad15c093b
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Changelog

## 5.1.0 (2025-07-17)

Full Changelog: [v5.0.0...v5.1.0](https://github.com/orbcorp/orb-node/compare/v5.0.0...v5.1.0)

### Features

* **api:** api update ([5f861b0](https://github.com/orbcorp/orb-node/commit/5f861b076256a7172ff83a2adee978632324665b))
* **api:** api update ([1939793](https://github.com/orbcorp/orb-node/commit/19397931ab0cfc5a7582995af8027117878047ea))
* **api:** api update ([54fb67f](https://github.com/orbcorp/orb-node/commit/54fb67f3f221e4d10f388540e2181bc3f2048780))
* **api:** api update ([b4f8f97](https://github.com/orbcorp/orb-node/commit/b4f8f973a593a4d09e46035dca03475ac39b834c))
* **api:** api update ([ecfef87](https://github.com/orbcorp/orb-node/commit/ecfef87e64aaaf582ee484eb6a55d3c016c33d49))
* **api:** api update ([fc96b5b](https://github.com/orbcorp/orb-node/commit/fc96b5bb78c87225c26e9d02fd42b473daf6cb8c))
* **api:** api update ([44d2c02](https://github.com/orbcorp/orb-node/commit/44d2c02d887d0e09c32d3a7738de8b31c8421b1c))
* **api:** api update ([81496fc](https://github.com/orbcorp/orb-node/commit/81496fc6baf0b872a705372289effad7992fb025))
* **api:** api update ([63e9ce1](https://github.com/orbcorp/orb-node/commit/63e9ce18c1f8ed120cf360fcc58931f8ed9e7e7a))
* **client:** add support for endpoint-specific base URLs ([0e61c32](https://github.com/orbcorp/orb-node/commit/0e61c325001e2ce26b26b2a5f70658d8e91a4458))


### Bug Fixes

* **ci:** release-doctor — report correct token name ([d9bddc1](https://github.com/orbcorp/orb-node/commit/d9bddc13b51bb6854ef04e86da16300dd9a86606))
* **client:** don't send `Content-Type` for bodyless methods ([33ba457](https://github.com/orbcorp/orb-node/commit/33ba457255b066c8ee465be2e0fb63c3956de9cc))
* publish script — handle NPM errors correctly ([1678c34](https://github.com/orbcorp/orb-node/commit/1678c342b2abf1d2fc88e4e253b3c5ac7df4d9a0))


### Chores

* **ci:** enable for pull requests ([77a5b1e](https://github.com/orbcorp/orb-node/commit/77a5b1e22b94ce2c0a27bb2b4fb14359a4203ca4))
* **ci:** only run for pushes and fork pull requests ([5b3d838](https://github.com/orbcorp/orb-node/commit/5b3d838882117c0c23284a0c9f9c2bb2c4add313))
* **internal:** bump test timeout ([d89af51](https://github.com/orbcorp/orb-node/commit/d89af5103034c83a7b52b1a87f2bada06d118d66))
* **internal:** make base APIResource abstract ([9a95185](https://github.com/orbcorp/orb-node/commit/9a95185ee2c315169985d08e5696ef4dd38a3e2b))
* make some internal functions async ([c7e0dab](https://github.com/orbcorp/orb-node/commit/c7e0dab4ae701fa809d524fd9171d17359832fba))
* mention unit type in timeout docs ([132b903](https://github.com/orbcorp/orb-node/commit/132b90306594db72d0c447402ce231e287e7908e))


### Refactors

* **types:** replace Record with mapped types ([509fcfb](https://github.com/orbcorp/orb-node/commit/509fcfb895402525e2d6ce1d6e8ca994ead496a8))

## 5.0.0 (2025-06-09)

Full Changelog: [v4.74.0...v5.0.0](https://github.com/orbcorp/orb-node/compare/v4.74.0...v5.0.0)
Expand Down
2 changes: 1 addition & 1 deletion bin/check-release-environment
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
errors=()

if [ -z "${NPM_TOKEN}" ]; then
errors+=("The ORB_NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
errors+=("The NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
fi

lenErrors=${#errors[@]}
Expand Down
34 changes: 27 additions & 7 deletions bin/publish-npm
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,35 @@ npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN"
yarn build
cd dist

# Get package name and version from package.json
PACKAGE_NAME="$(jq -r -e '.name' ./package.json)"
VERSION="$(jq -r -e '.version' ./package.json)"

# Get latest version from npm
#
# If the package doesn't exist, yarn will return
# {"type":"error","data":"Received invalid response from npm."}
# where .data.version doesn't exist so LAST_VERSION will be an empty string.
LAST_VERSION="$(yarn info --json 2> /dev/null | jq -r '.data.version')"

# Get current version from package.json
VERSION="$(node -p "require('./package.json').version")"
# If the package doesn't exist, npm will return:
# {
# "error": {
# "code": "E404",
# "summary": "Unpublished on 2025-06-05T09:54:53.528Z",
# "detail": "'the_package' is not in this registry..."
# }
# }
NPM_INFO="$(npm view "$PACKAGE_NAME" version --json 2>/dev/null || true)"

# Check if we got an E404 error
if echo "$NPM_INFO" | jq -e '.error.code == "E404"' > /dev/null 2>&1; then
# Package doesn't exist yet, no last version
LAST_VERSION=""
elif echo "$NPM_INFO" | jq -e '.error' > /dev/null 2>&1; then
# Report other errors
echo "ERROR: npm returned unexpected data:"
echo "$NPM_INFO"
exit 1
else
# Success - get the version
LAST_VERSION=$(echo "$NPM_INFO" | jq -r '.') # strip quotes
fi

# Check if current version is pre-release (e.g. alpha / beta / rc)
CURRENT_IS_PRERELEASE=false
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orb-billing",
"version": "5.0.0",
"version": "5.1.0",
"description": "The official TypeScript library for the Orb API",
"author": "Orb <[email protected]>",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion scripts/build
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fi
node scripts/utils/make-dist-package-json.cjs > dist/package.json

# build to .js/.mjs/.d.ts files
npm exec tsc-multi
./node_modules/.bin/tsc-multi
# copy over handwritten .js/.mjs/.d.ts files
cp src/_shims/*.{d.ts,js,mjs,md} dist/_shims
cp src/_shims/auto/*.{d.ts,js,mjs} dist/_shims/auto
Expand Down
2 changes: 1 addition & 1 deletion scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ else
fi

echo "==> Running tests"
./node_modules/.bin/jest "$@"
./node_modules/.bin/jest --testTimeout=60000 "$@"
25 changes: 17 additions & 8 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export class APIPromise<T> extends Promise<T> {

export abstract class APIClient {
baseURL: string;
#baseURLOverridden: boolean;
maxRetries: number;
timeout: number;
httpAgent: Agent | undefined;
Expand All @@ -179,18 +180,21 @@ export abstract class APIClient {

constructor({
baseURL,
baseURLOverridden,
maxRetries = 2,
timeout = 60000, // 1 minute
httpAgent,
fetch: overriddenFetch,
}: {
baseURL: string;
baseURLOverridden: boolean;
maxRetries?: number | undefined;
timeout: number | undefined;
httpAgent: Agent | undefined;
fetch: Fetch | undefined;
}) {
this.baseURL = baseURL;
this.#baseURLOverridden = baseURLOverridden;
this.maxRetries = validatePositiveInteger('maxRetries', maxRetries);
this.timeout = validatePositiveInteger('timeout', timeout);
this.httpAgent = httpAgent;
Expand All @@ -213,7 +217,7 @@ export abstract class APIClient {
protected defaultHeaders(opts: FinalRequestOptions): Headers {
return {
Accept: 'application/json',
'Content-Type': 'application/json',
...(['head', 'get'].includes(opts.method) ? {} : { 'Content-Type': 'application/json' }),
'User-Agent': this.getUserAgent(),
...getPlatformHeaders(),
...this.authHeaders(opts),
Expand Down Expand Up @@ -295,12 +299,12 @@ export abstract class APIClient {
return null;
}

buildRequest<Req>(
async buildRequest<Req>(
inputOptions: FinalRequestOptions<Req>,
{ retryCount = 0 }: { retryCount?: number } = {},
): { req: RequestInit; url: string; timeout: number } {
): Promise<{ req: RequestInit; url: string; timeout: number }> {
const options = { ...inputOptions };
const { method, path, query, headers: headers = {} } = options;
const { method, path, query, defaultBaseURL, headers: headers = {} } = options;

const body =
ArrayBuffer.isView(options.body) || (options.__binaryRequest && typeof options.body === 'string') ?
Expand All @@ -310,7 +314,7 @@ export abstract class APIClient {
: null;
const contentLength = this.calculateContentLength(body);

const url = this.buildURL(path!, query);
const url = this.buildURL(path!, query, defaultBaseURL);
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
options.timeout = options.timeout ?? this.timeout;
const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
Expand Down Expand Up @@ -446,7 +450,9 @@ export abstract class APIClient {

await this.prepareOptions(options);

const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
const { req, url, timeout } = await this.buildRequest(options, {
retryCount: maxRetries - retriesRemaining,
});

await this.prepareRequest(req, { url, options });

Expand Down Expand Up @@ -503,11 +509,12 @@ export abstract class APIClient {
return new PagePromise<PageClass, Item>(this, request, Page);
}

buildURL<Req>(path: string, query: Req | null | undefined): string {
buildURL<Req>(path: string, query: Req | null | undefined, defaultBaseURL?: string | undefined): string {
const baseURL = (!this.#baseURLOverridden && defaultBaseURL) || this.baseURL;
const url =
isAbsoluteURL(path) ?
new URL(path)
: new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
: new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));

const defaultQuery = this.defaultQuery();
if (!isEmptyObj(defaultQuery)) {
Expand Down Expand Up @@ -792,6 +799,7 @@ export type RequestOptions<
query?: Req | undefined;
body?: Req | null | undefined;
headers?: Headers | undefined;
defaultBaseURL?: string | undefined;

maxRetries?: number;
stream?: boolean | undefined;
Expand All @@ -813,6 +821,7 @@ const requestOptionsKeys: KeysEnum<RequestOptions> = {
query: true,
body: true,
headers: true,
defaultBaseURL: true,

maxRetries: true,
stream: true,
Expand Down
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ export interface ClientOptions {
*
* Note that request timeouts are retried by default, so in a worst-case scenario you may wait
* much longer than this timeout before the promise succeeds or fails.
*
* @unit milliseconds
*/
timeout?: number | undefined;

Expand Down Expand Up @@ -295,6 +297,7 @@ export class Orb extends Core.APIClient {

super({
baseURL: options.baseURL!,
baseURLOverridden: baseURL ? baseURL !== 'https://api.withorb.com/v1' : false,
timeout: options.timeout ?? 60000 /* 1 minute */,
httpAgent: options.httpAgent,
maxRetries: options.maxRetries,
Expand Down Expand Up @@ -326,6 +329,13 @@ export class Orb extends Core.APIClient {
dimensionalPriceGroups: API.DimensionalPriceGroups = new API.DimensionalPriceGroups(this);
subscriptionChanges: API.SubscriptionChanges = new API.SubscriptionChanges(this);

/**
* Check whether the base URL is set to its default.
*/
#baseURLOverridden(): boolean {
return this.baseURL !== 'https://api.withorb.com/v1';
}

protected override defaultQuery(): Core.DefaultQuery | undefined {
return this._options.defaultQuery;
}
Expand Down
2 changes: 1 addition & 1 deletion src/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { Orb } from './index';

export class APIResource {
export abstract class APIResource {
protected _client: Orb;

constructor(client: Orb) {
Expand Down
58 changes: 58 additions & 0 deletions src/resources/credit-notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ export class CreditNotes extends APIResource {
/**
* This endpoint is used to create a single
* [`Credit Note`](/invoicing/credit-notes).
*
* The credit note service period configuration supports two explicit modes:
*
* 1. Global service periods: Specify start_date and end_date at the credit note
* level. These dates will be applied to all line items uniformly.
*
* 2. Individual service periods: Specify start_date and end_date for each line
* item. When using this mode, ALL line items must have individual periods
* specified.
*
* 3. Default behavior: If no service periods are specified (neither global nor
* individual), the original invoice line item service periods will be used.
*
* Note: Mixing global and individual service periods in the same request is not
* allowed to prevent confusion.
*
* Service period dates are normalized to the start of the day in the customer's
* timezone to ensure consistent handling across different timezones.
*
* Date Format: Use start_date and end_date with format "YYYY-MM-DD" (e.g.,
* "2023-09-22") to match other Orb APIs like /v1/invoice_line_items.
*
* Note: Both start_date and end_date are inclusive - the service period will cover
* both the start date and end date completely (from start of start_date to end of
* end_date).
*/
create(body: CreditNoteCreateParams, options?: Core.RequestOptions): Core.APIPromise<Shared.CreditNote> {
return this._client.post('/credit_notes', { body, ...options });
Expand Down Expand Up @@ -53,10 +78,26 @@ export interface CreditNoteCreateParams {
*/
reason: 'duplicate' | 'fraudulent' | 'order_change' | 'product_unsatisfactory';

/**
* A date string to specify the global credit note service period end date in the
* customer's timezone. This will be applied to all line items that don't have
* their own individual service periods specified. If not provided, line items will
* use their original invoice line item service periods. This date is inclusive.
*/
end_date?: string | null;

/**
* An optional memo to attach to the credit note.
*/
memo?: string | null;

/**
* A date string to specify the global credit note service period start date in the
* customer's timezone. This will be applied to all line items that don't have
* their own individual service periods specified. If not provided, line items will
* use their original invoice line item service periods. This date is inclusive.
*/
start_date?: string | null;
}

export namespace CreditNoteCreateParams {
Expand All @@ -70,6 +111,23 @@ export namespace CreditNoteCreateParams {
* The ID of the line item to credit.
*/
invoice_line_item_id: string;

/**
* A date string to specify this line item's credit note service period end date in
* the customer's timezone. If provided, this will be used for this specific line
* item. If not provided, will use the global end_date if available, otherwise
* defaults to the original invoice line item's end date. This date is inclusive.
*/
end_date?: string | null;

/**
* A date string to specify this line item's credit note service period start date
* in the customer's timezone. If provided, this will be used for this specific
* line item. If not provided, will use the global start_date if available,
* otherwise defaults to the original invoice line item's start date. This date is
* inclusive.
*/
start_date?: string | null;
}
}

Expand Down
Loading