Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules/
.dev.vars
.env
about.json
openapi-ts-error-*.log
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
drizzle/
bun.lock
node_modules/
*.md
*.md
dist/
build/
out/
441 changes: 411 additions & 30 deletions bun.lock

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions client/ts/.gitignore
Comment thread
madkarmaa marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# dependencies (bun install)
node_modules

# output
out
dist
*.tgz

# code coverage
coverage
*.lcov

# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# caches
.eslintcache
.cache
*.tsbuildinfo

# IntelliJ based IDEs
.idea

# Finder (MacOS) folder config
.DS_Store
25 changes: 25 additions & 0 deletions client/ts/client.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file is auto-generated by @hey-api/openapi-ts

import {
type ClientOptions,
type Config,
createClient,
createConfig
} from './client';
import type { ClientOptions as ClientOptions2 } from './types.gen';

/**
* The `createClientConfig()` function will be called on client initialization
* and the returned object will become the client's initial configuration.
*
* You may want to initialize your client this way instead of calling
* `setConfig()`. This is useful for example if you're using Next.js
* to ensure your client always has the correct values.
*/
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
override?: Config<ClientOptions & T>
) => Config<Required<ClientOptions> & T>;

export const client = createClient(
createConfig<ClientOptions2>({ baseUrl: 'https://api.revanced.app' })
);
321 changes: 321 additions & 0 deletions client/ts/client/client.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createSseClient } from '../core/serverSentEvents.gen';
import type { HttpMethod } from '../core/types.gen';
import { getValidRequestBody } from '../core/utils.gen';
import type {
Client,
Config,
RequestOptions,
ResolvedRequestOptions
} from './types.gen';
import {
buildUrl,
createConfig,
createInterceptors,
getParseAs,
mergeConfigs,
mergeHeaders,
setAuthParams
} from './utils.gen';

type ReqInit = Omit<RequestInit, 'body' | 'headers'> & {
body?: any;
headers: ReturnType<typeof mergeHeaders>;
};

export const createClient = (config: Config = {}): Client => {
let _config = mergeConfigs(createConfig(), config);

const getConfig = (): Config => ({ ..._config });

const setConfig = (config: Config): Config => {
_config = mergeConfigs(_config, config);
return getConfig();
};

const interceptors = createInterceptors<
Request,
Response,
unknown,
ResolvedRequestOptions
>();

const beforeRequest = async (options: RequestOptions) => {
const opts = {
..._config,
...options,
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
headers: mergeHeaders(_config.headers, options.headers),
serializedBody: undefined as string | undefined
};

if (opts.security) {
await setAuthParams({
...opts,
security: opts.security
});
}

if (opts.requestValidator) {
await opts.requestValidator(opts);
}

if (opts.body !== undefined && opts.bodySerializer) {
opts.serializedBody = opts.bodySerializer(opts.body) as
| string
| undefined;
}

// remove Content-Type header if body is empty to avoid sending invalid requests
if (opts.body === undefined || opts.serializedBody === '') {
opts.headers.delete('Content-Type');
}

const url = buildUrl(opts);

return { opts, url };
};

const request: Client['request'] = async (options) => {
// @ts-expect-error
const { opts, url } = await beforeRequest(options);
const requestInit: ReqInit = {
redirect: 'follow',
...opts,
body: getValidRequestBody(opts)
};

let request = new Request(url, requestInit);

for (const fn of interceptors.request.fns) {
if (fn) {
request = await fn(request, opts);
}
}

// fetch must be assigned here, otherwise it would throw the error:
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
const _fetch = opts.fetch!;
let response: Response;

try {
response = await _fetch(request);
} catch (error) {
// Handle fetch exceptions (AbortError, network errors, etc.)
let finalError = error;

for (const fn of interceptors.error.fns) {
if (fn) {
finalError = (await fn(
error,
undefined as any,
request,
opts
)) as unknown;
}
}

finalError = finalError || ({} as unknown);

if (opts.throwOnError) {
throw finalError;
}

// Return error response
return opts.responseStyle === 'data'
? undefined
: {
error: finalError,
request,
response: undefined as any
};
}

for (const fn of interceptors.response.fns) {
if (fn) {
response = await fn(response, request, opts);
}
}

const result = {
request,
response
};

if (response.ok) {
const parseAs =
(opts.parseAs === 'auto'
? getParseAs(response.headers.get('Content-Type'))
: opts.parseAs) ?? 'json';

if (
response.status === 204 ||
response.headers.get('Content-Length') === '0'
) {
let emptyData: any;
switch (parseAs) {
case 'arrayBuffer':
case 'blob':
case 'text':
emptyData = await response[parseAs]();
break;
case 'formData':
emptyData = new FormData();
break;
case 'stream':
emptyData = response.body;
break;
case 'json':
default:
emptyData = {};
break;
}
return opts.responseStyle === 'data'
? emptyData
: {
data: emptyData,
...result
};
}

let data: any;
switch (parseAs) {
case 'arrayBuffer':
case 'blob':
case 'formData':
case 'text':
data = await response[parseAs]();
break;
case 'json': {
// Some servers return 200 with no Content-Length and empty body.
// response.json() would throw; read as text and parse if non-empty.
const text = await response.text();
data = text ? JSON.parse(text) : {};
break;
}
case 'stream':
return opts.responseStyle === 'data'
? response.body
: {
data: response.body,
...result
};
}

if (parseAs === 'json') {
if (opts.responseValidator) {
await opts.responseValidator(data);
}

if (opts.responseTransformer) {
data = await opts.responseTransformer(data);
}
}

return opts.responseStyle === 'data'
? data
: {
data,
...result
};
}

const textError = await response.text();
let jsonError: unknown;

try {
jsonError = JSON.parse(textError);
} catch {
// noop
}

const error = jsonError ?? textError;
let finalError = error;

for (const fn of interceptors.error.fns) {
if (fn) {
finalError = (await fn(
error,
response,
request,
opts
)) as string;
}
}

finalError = finalError || ({} as string);

if (opts.throwOnError) {
throw finalError;
}

// TODO: we probably want to return error and improve types
return opts.responseStyle === 'data'
? undefined
: {
error: finalError,
...result
};
};

const makeMethodFn =
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
request({ ...options, method });

const makeSseFn =
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
const { opts, url } = await beforeRequest(options);
return createSseClient({
...opts,
body: opts.body as BodyInit | null | undefined,
headers: opts.headers as unknown as Record<string, string>,
method,
onRequest: async (url, init) => {
let request = new Request(url, init);
for (const fn of interceptors.request.fns) {
if (fn) {
request = await fn(request, opts);
}
}
return request;
},
serializedBody: getValidRequestBody(opts) as
| BodyInit
| null
| undefined,
url
});
};

const _buildUrl: Client['buildUrl'] = (options) =>
buildUrl({ ..._config, ...options });

return {
buildUrl: _buildUrl,
connect: makeMethodFn('CONNECT'),
delete: makeMethodFn('DELETE'),
get: makeMethodFn('GET'),
getConfig,
head: makeMethodFn('HEAD'),
interceptors,
options: makeMethodFn('OPTIONS'),
patch: makeMethodFn('PATCH'),
post: makeMethodFn('POST'),
put: makeMethodFn('PUT'),
request,
setConfig,
sse: {
connect: makeSseFn('CONNECT'),
delete: makeSseFn('DELETE'),
get: makeSseFn('GET'),
head: makeSseFn('HEAD'),
options: makeSseFn('OPTIONS'),
patch: makeSseFn('PATCH'),
post: makeSseFn('POST'),
put: makeSseFn('PUT'),
trace: makeSseFn('TRACE')
},
trace: makeMethodFn('TRACE')
} as Client;
};
Loading
Loading