Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/sunrise-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v4
Expand All @@ -41,7 +41,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v4
Expand All @@ -57,7 +57,7 @@ jobs:
- name: Get GitHub OIDC Token
if: github.repository == 'stainless-sdks/sunrise-node'
id: github-oidc
uses: actions/github-script@v6
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());

Expand All @@ -74,7 +74,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/sunrise-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-doctor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
if: github.repository == 'ContextualAI/contextual-client-node' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Check release environment
run: |
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 @@
{
".": "0.11.0"
".": "0.11.1"
}
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 0.11.1 (2026-02-27)

Full Changelog: [v0.11.0...v0.11.1](https://github.com/ContextualAI/contextual-client-node/compare/v0.11.0...v0.11.1)

### Bug Fixes

* **docs/contributing:** correct pnpm link command ([193076b](https://github.com/ContextualAI/contextual-client-node/commit/193076b45501a9b428ea98c25a5e3265bd3f730e))


### Chores

* **ci:** upgrade `actions/github-script` ([8b4191f](https://github.com/ContextualAI/contextual-client-node/commit/8b4191f69d28f415a6f904ae97f046466ce87b4f))
* **client:** do not parse responses with empty content-length ([1e472ad](https://github.com/ContextualAI/contextual-client-node/commit/1e472ad963cb097b2641aa680fcee5eb8b31c9a8))
* **internal:** move stringifyQuery implementation to internal function ([6e1c564](https://github.com/ContextualAI/contextual-client-node/commit/6e1c564f4ecafba59bb016bec0315ff5efd7dd05))
* **internal:** update `actions/checkout` version ([4956c34](https://github.com/ContextualAI/contextual-client-node/commit/4956c3404ec39514e420e40a5b2761d027d7add4))
* **internal:** upgrade babel, qs, js-yaml ([105be1e](https://github.com/ContextualAI/contextual-client-node/commit/105be1e876017babe277122c898b4c0d9f89810f))
* update mock server docs ([7f118be](https://github.com/ContextualAI/contextual-client-node/commit/7f118bebaedc0758ec29022ee051e2d9627212d9))

## 0.11.0 (2026-01-13)

Full Changelog: [v0.10.0...v0.11.0](https://github.com/ContextualAI/contextual-client-node/compare/v0.10.0...v0.11.0)
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ $ yarn link contextual-client
# With pnpm
$ pnpm link --global
$ cd ../my-package
$ pnpm link -global contextual-client
$ pnpm link --global contextual-client
```

## Running tests

Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.

```sh
$ npx prism mock path/to/your/openapi.yml
$ ./scripts/mock
```

```sh
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": "contextual-client",
"version": "0.11.0",
"version": "0.11.1",
"description": "The official TypeScript library for the Contextual AI API",
"author": "Contextual AI <support@contextual.ai>",
"types": "dist/index.d.ts",
Expand Down
26 changes: 10 additions & 16 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
APIConnectionTimeoutError,
APIUserAbortError,
} from './error';
import { stringifyQuery } from './internal/utils/query';
import {
kind as shimsKind,
type Readable,
Expand Down Expand Up @@ -70,6 +71,12 @@ async function defaultParseResponse<T>(props: APIResponseProps): Promise<T> {
const mediaType = contentType?.split(';')[0]?.trim();
const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
if (isJSON) {
const contentLength = response.headers.get('content-length');
if (contentLength === '0') {
// if there is no content we can't do anything
return undefined as T;
}

const json = await response.json();

debug('response', response.status, response.url, response.headers, json);
Expand Down Expand Up @@ -522,27 +529,14 @@ export abstract class APIClient {
}

if (typeof query === 'object' && query && !Array.isArray(query)) {
url.search = this.stringifyQuery(query as Record<string, unknown>);
url.search = this.stringifyQuery(query);
}

return url.toString();
}

protected stringifyQuery(query: Record<string, unknown>): string {
return Object.entries(query)
.filter(([_, value]) => typeof value !== 'undefined')
.map(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
if (value === null) {
return `${encodeURIComponent(key)}=`;
}
throw new ContextualAIError(
`Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
);
})
.join('&');
protected stringifyQuery(query: object | Record<string, unknown>): string {
return stringifyQuery(query);
}

async fetchWithTimeout(
Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { type Agent } from './_shims/index';
import * as qs from './internal/qs';
import { stringifyQuery } from './internal/utils/query';
import * as Core from './core';
import * as Errors from './error';
import * as Pagination from './pagination';
Expand Down Expand Up @@ -235,8 +235,8 @@ export class ContextualAI extends Core.APIClient {
return { Authorization: `Bearer ${this.apiKey}` };
}

protected override stringifyQuery(query: Record<string, unknown>): string {
return qs.stringify(query, { arrayFormat: 'repeat' });
protected override stringifyQuery(query: object | Record<string, unknown>): string {
return stringifyQuery(query);
}

static ContextualAI = this;
Expand Down
7 changes: 7 additions & 0 deletions src/internal/utils/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import * as qs from '../qs/stringify';

export function stringifyQuery(query: object | Record<string, unknown>) {
return qs.stringify(query, { arrayFormat: 'repeat' });
}
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = '0.11.0'; // x-release-please-version
export const VERSION = '0.11.1'; // x-release-please-version
6 changes: 2 additions & 4 deletions tests/stringifyQuery.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { ContextualAI } from 'contextual-client';

const { stringifyQuery } = ContextualAI.prototype as any;
import { stringifyQuery } from 'contextual-client/internal/utils/query';

describe(stringifyQuery, () => {
for (const [input, expected] of [
Expand All @@ -15,7 +13,7 @@ describe(stringifyQuery, () => {
'e=f',
)}=${encodeURIComponent('g&h')}`,
],
]) {
] as const) {
it(`${JSON.stringify(input)} -> ${expected}`, () => {
expect(stringifyQuery(input)).toEqual(expected);
});
Expand Down
Loading