Skip to content

Commit

Permalink
feat(api): api update (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-app[bot] authored and stainless-bot committed Nov 1, 2024
1 parent 580c9c3 commit 5d82d2d
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 133 deletions.
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
configured_endpoints: 7
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-1f0d266ba97b03672f10d33a6dc6e324af9a95646f978ffbff6a31f3907bbfe7.yml
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-e832198c20514199804ae71f3213d3747cfea8dbae37304e852fd58912abb2c1.yml
54 changes: 27 additions & 27 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@ Other package managers may work but are not officially supported for development

To set up the repository, run:

```bash
yarn
yarn build
```sh
$ yarn
$ yarn build
```

This will install all the required dependencies and build output files to `dist/`.

## Modifying/Adding code

Most of the SDK is generated code, and any modified code will be overridden on the next generation. The
`src/lib/` and `examples/` directories are exceptions and will never be overridden.
Most of the SDK is generated code. Modifications to code will be persisted between generations, but may
result in merge conflicts between manual patches and changes from the generator. The generator will never
modify the contents of the `src/lib/` and `examples/` directories.

## Adding and running examples

All files in the `examples/` directory are not modified by the Stainless generator and can be freely edited or
added to.
All files in the `examples/` directory are not modified by the generator and can be freely edited or added to.

```bash
```ts
// add an example to examples/<your-example>.ts

#!/usr/bin/env -S npm run tsn -T
Expand All @@ -41,38 +41,38 @@ If you’d like to use the repository from source, you can either install from g

To install via git:

```bash
npm install git+ssh://git@github.com:groq/groq-typescript.git
```sh
$ npm install git+ssh://git@github.com:groq/groq-typescript.git
```

Alternatively, to link a local copy of the repo:

```bash
```sh
# Clone
git clone https://www.github.com/groq/groq-typescript
cd groq-typescript
$ git clone https://www.github.com/groq/groq-typescript
$ cd groq-typescript

# With yarn
yarn link
cd ../my-package
yarn link groq-sdk
$ yarn link
$ cd ../my-package
$ yarn link groq-sdk

# With pnpm
pnpm link --global
cd ../my-package
pnpm link -—global groq-sdk
$ pnpm link --global
$ cd ../my-package
$ pnpm link -—global groq-sdk
```

## 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.

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

```bash
yarn run test
```sh
$ yarn run test
```

## Linting and formatting
Expand All @@ -82,14 +82,14 @@ This repository uses [prettier](https://www.npmjs.com/package/prettier) and

To lint:

```bash
yarn lint
```sh
$ yarn lint
```

To format and fix all lint issues automatically:

```bash
yarn fix
```sh
$ yarn fix
```

## Publishing and releases
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,18 +330,28 @@ The following runtimes are supported:
- Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time).
- Nitro v2.6 or greater.
- Web browsers: disabled by default to avoid exposing your secret API credentials. Enable browser support by explicitly setting `dangerouslyAllowBrowser` to true'.
<details>
<summary>More explanation</summary>
<details>
<summary>More explanation</summary>

### Why is this dangerous?

Enabling the `dangerouslyAllowBrowser` option can be dangerous because it exposes your secret API credentials in the client-side code. Web browsers are inherently less secure than server environments,
any user with access to the browser can potentially inspect, extract, and misuse these credentials. This could lead to unauthorized access using your credentials and potentially compromise sensitive data or functionality.

### When might this not be dangerous?

In certain scenarios where enabling browser support might not pose significant risks:

- Internal Tools: If the application is used solely within a controlled internal environment where the users are trusted, the risk of credential exposure can be mitigated.
- Public APIs with Limited Scope: If your API has very limited scope and the exposed credentials do not grant access to sensitive data or critical operations, the potential impact of exposure is reduced.
- Development or debugging purpose: Enabling this feature temporarily might be acceptable, provided the credentials are short-lived, aren't also used in production environments, or are frequently rotated.

</details>

Note that React Native is not supported at this time.

If you are interested in other runtime environments, please open or upvote an issue on GitHub.

## Contributing

See [the contributing documentation](./CONTRIBUTING.md).
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"license": "Apache-2.0",
"packageManager": "yarn@1.22.22",
"files": [
"*"
"**/*"
],
"private": false,
"scripts": {
Expand Down Expand Up @@ -41,6 +41,7 @@
"eslint": "^8.49.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-unused-imports": "^3.0.0",
"iconv-lite": "^0.6.3",
"jest": "^29.4.0",
"prettier": "^3.0.0",
"ts-jest": "^29.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/_shims/node-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as fd from 'formdata-node';
export { type Agent } from 'node:http';
export { type Readable } from 'node:stream';
export { type ReadStream as FsReadStream } from 'node:fs';
export { ReadableStream } from 'web-streams-polyfill';
export { ReadableStream } from 'node:stream/web';

export const fetch: typeof nf.default;

Expand Down
47 changes: 39 additions & 8 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ export class APIPromise<T> extends Promise<T> {
});
}

_thenUnwrap<U>(transform: (data: T) => U): APIPromise<U> {
return new APIPromise(this.responsePromise, async (props) => transform(await this.parseResponse(props)));
_thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
return new APIPromise(this.responsePromise, async (props) =>
transform(await this.parseResponse(props), props),
);
}

/**
Expand Down Expand Up @@ -288,7 +290,10 @@ export abstract class APIClient {
return null;
}

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

const body =
Expand Down Expand Up @@ -320,7 +325,7 @@ export abstract class APIClient {
headers[this.idempotencyHeader] = options.idempotencyKey;
}

const reqHeaders = this.buildHeaders({ options, headers, contentLength });
const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });

const req: RequestInit = {
method,
Expand All @@ -339,10 +344,12 @@ export abstract class APIClient {
options,
headers,
contentLength,
retryCount,
}: {
options: FinalRequestOptions;
headers: Record<string, string | null | undefined>;
contentLength: string | null | undefined;
retryCount: number;
}): Record<string, string> {
const reqHeaders: Record<string, string> = {};
if (contentLength) {
Expand All @@ -358,6 +365,16 @@ export abstract class APIClient {
delete reqHeaders['content-type'];
}

// Don't set the retry count header if it was already set or removed through default headers or by the
// caller. We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to
// account for the removal case.
if (
getHeader(defaultHeaders, 'x-stainless-retry-count') === undefined &&
getHeader(headers, 'x-stainless-retry-count') === undefined
) {
reqHeaders['x-stainless-retry-count'] = String(retryCount);
}

this.validateHeaders(reqHeaders, headers);

return reqHeaders;
Expand Down Expand Up @@ -409,13 +426,14 @@ export abstract class APIClient {
retriesRemaining: number | null,
): Promise<APIResponseProps> {
const options = await optionsInput;
const maxRetries = options.maxRetries ?? this.maxRetries;
if (retriesRemaining == null) {
retriesRemaining = options.maxRetries ?? this.maxRetries;
retriesRemaining = maxRetries;
}

await this.prepareOptions(options);

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

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

Expand Down Expand Up @@ -994,6 +1012,11 @@ const validatePositiveInteger = (name: string, n: unknown): number => {

export const castToError = (err: any): Error => {
if (err instanceof Error) return err;
if (typeof err === 'object' && err !== null) {
try {
return new Error(JSON.stringify(err));
} catch {}
}
return new Error(err);
};

Expand Down Expand Up @@ -1131,7 +1154,15 @@ export const isHeadersProtocol = (headers: any): headers is HeadersProtocol => {
return typeof headers?.get === 'function';
};

export const getRequiredHeader = (headers: HeadersLike, header: string): string => {
export const getRequiredHeader = (headers: HeadersLike | Headers, header: string): string => {
const foundHeader = getHeader(headers, header);
if (foundHeader === undefined) {
throw new Error(`Could not find ${header} header`);
}
return foundHeader;
};

export const getHeader = (headers: HeadersLike | Headers, header: string): string | undefined => {
const lowerCasedHeader = header.toLowerCase();
if (isHeadersProtocol(headers)) {
// to deal with the case where the header looks like Stainless-Event-Id
Expand All @@ -1157,7 +1188,7 @@ export const getRequiredHeader = (headers: HeadersLike, header: string): string
}
}

throw new Error(`Could not find ${header} header`);
return undefined;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class APIError extends GroqError {
headers: Headers | undefined,
) {
if (!status) {
return new APIConnectionError({ cause: castToError(errorResponse) });
return new APIConnectionError({ message, cause: castToError(errorResponse) });
}

const error = errorResponse as Record<string, any>;
Expand Down Expand Up @@ -101,7 +101,7 @@ export class APIUserAbortError extends APIError {
export class APIConnectionError extends APIError {
override readonly status: undefined = undefined;

constructor({ message, cause }: { message?: string; cause?: Error | undefined }) {
constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) {
super(undefined, undefined, message || 'Connection error.', undefined);
// in some environments the 'cause' property is already declared
// @ts-ignore
Expand Down
8 changes: 5 additions & 3 deletions src/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ export async function toFile(
// If it's a promise, resolve it.
value = await value;

// Use the file's options if there isn't one provided
options ??= isFileLike(value) ? { lastModified: value.lastModified, type: value.type } : {};
// If we've been given a `File` we don't need to do anything
if (isFileLike(value)) {
return value;
}

if (isResponseLike(value)) {
const blob = await value.blob();
Expand All @@ -126,7 +128,7 @@ export async function toFile(

name ||= getName(value) ?? 'unknown_file';

if (!options.type) {
if (!options?.type) {
const type = (bits[0] as any)?.type;
if (typeof type === 'string') {
options = { ...options, type };
Expand Down
Loading

0 comments on commit 5d82d2d

Please sign in to comment.