-
Notifications
You must be signed in to change notification settings - Fork 567
[SDK] Init new API namespaces #7637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# thirdweb API TypeScript wrapper | ||
|
||
This package is a thin OpenAPI wrapper for the thirdweb API | ||
|
||
## Configuration | ||
|
||
```ts | ||
import { configure } from "@thirdweb-dev/api"; | ||
|
||
// call this once at the startup of your application | ||
configure({ | ||
secretKey: "<PROJECT_SECRET_KEY>", | ||
}); | ||
``` | ||
|
||
## Example Usage | ||
|
||
```ts | ||
import { writeContract } from "@thirdweb-dev/api"; | ||
|
||
const result = await sendTransaction({ | ||
body: { | ||
from: "0x1234567891234567891234567891234567891234", | ||
chainId: "1", | ||
calls: [ | ||
{ | ||
contractAddress: "0x1234567890123456789012345678901234567890", | ||
method: "function transfer(address to, uint256 amount)", | ||
params: [ | ||
"0x1234567890123456789012345678901234567890", | ||
"1000000000000000000", | ||
], | ||
}, | ||
], | ||
}, | ||
}); | ||
``` | ||
|
||
This package was autogenerated from the [thirdweb openAPI spec](https://api.thirdweb.com/reference) using [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json", | ||
"linter": { | ||
"rules": { | ||
"correctness": { | ||
"useImportExtensions": { | ||
"fix": "safe", | ||
"level": "error", | ||
"options": { | ||
"forceJsExtensions": true | ||
} | ||
} | ||
Comment on lines
+6
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The rule is applied globally, so it will enforce adding Consider narrowing the rule scope or adding {
"forceJsExtensions": true,
"ignorePackageImports": true
} to avoid turning valid bare imports into invalid ones. 🤖 Prompt for AI Agents
|
||
} | ||
} | ||
}, | ||
"overrides": [ | ||
{ | ||
"assist": { | ||
"actions": { | ||
"source": { | ||
"useSortedKeys": "off" | ||
} | ||
} | ||
}, | ||
"includes": ["package.json"] | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,6 @@ | ||||||||||||||||||||
import { defineConfig } from "@hey-api/openapi-ts"; | ||||||||||||||||||||
|
||||||||||||||||||||
export default defineConfig({ | ||||||||||||||||||||
input: "https://api.thirdweb-dev.com/openapi.json", | ||||||||||||||||||||
output: { format: "biome", lint: "biome", path: "src/client" }, | ||||||||||||||||||||
}); | ||||||||||||||||||||
Comment on lines
+3
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPin the OpenAPI schema version to guarantee reproducible builds Fetching Store a copy in-repo (or pin to a tagged commit URL) and update intentionally. - input: "https://api.thirdweb-dev.com/openapi.json",
+ // Prefer a pinned revision for reproducibility
+ input: "./schemas/[email protected]", 🏁 Script executed: #!/bin/bash
# Search for a local 'schemas' directory
fd --type d --max-depth 3 schemas
# List all JSON files in the repo
fd --extension json
# Find JSON files mentioning 'openapi'
rg -l 'openapi' -g '*.json' || true Length of output: 13458 Pin OpenAPI schema for reproducible builds A live URL will produce drifting client types whenever the remote spec changes. To guarantee deterministic generations:
export default defineConfig({
- input: "https://api.thirdweb-dev.com/openapi.json",
+ // Point to a pinned, in-repo spec for stable builds
+ input: "./schemas/[email protected]",
output: { format: "biome", lint: "biome", path: "src/client" },
}); • Create the 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
{ | ||
"name": "@thirdweb-dev/api", | ||
"version": "0.0.1", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/thirdweb-dev/js.git#main" | ||
}, | ||
"author": "thirdweb eng <[email protected]>", | ||
"type": "module", | ||
"main": "./dist/cjs/exports/thirdweb.js", | ||
"module": "./dist/esm/exports/thirdweb.js", | ||
"types": "./dist/types/exports/thirdweb.d.ts", | ||
"typings": "./dist/types/exports/thirdweb.d.ts", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/thirdweb-dev/js/issues" | ||
}, | ||
"dependencies": { | ||
"@hey-api/client-fetch": "0.10.0" | ||
}, | ||
"engines": { | ||
"node": ">=18" | ||
}, | ||
"exports": { | ||
".": { | ||
"types": "./dist/types/exports/thirdweb.d.ts", | ||
"import": "./dist/esm/exports/thirdweb.js", | ||
"default": "./dist/cjs/exports/thirdweb.js" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"files": [ | ||
"dist/*", | ||
"src/*" | ||
], | ||
"devDependencies": { | ||
"@biomejs/biome": "2.0.6", | ||
"@hey-api/openapi-ts": "0.76.0", | ||
"rimraf": "6.0.1", | ||
"tslib": "^2.8.1" | ||
}, | ||
"peerDependencies": { | ||
"typescript": ">=5.0.4" | ||
}, | ||
"peerDependenciesMeta": { | ||
"typescript": { | ||
"optional": true | ||
} | ||
}, | ||
"scripts": { | ||
"build": "pnpm clean && pnpm build:cjs && pnpm build:esm && pnpm build:types", | ||
"build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json", | ||
"build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json", | ||
"build:generate": "openapi-ts && pnpm format && pnpm fix", | ||
"build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap", | ||
"clean": "rimraf dist", | ||
"fix": "biome check --write ./src", | ||
"format": "biome format --write ./src", | ||
"lint": "biome check ./src" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// This file is auto-generated by @hey-api/openapi-ts | ||
|
||
import { | ||
type Config, | ||
createClient, | ||
createConfig, | ||
type ClientOptions as DefaultClientOptions, | ||
} from "./client/index.js"; | ||
import type { ClientOptions } from "./types.gen.js"; | ||
|
||
/** | ||
* 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 DefaultClientOptions = ClientOptions> = | ||
( | ||
override?: Config<DefaultClientOptions & T>, | ||
) => Config<Required<DefaultClientOptions> & T>; | ||
|
||
export const client = createClient( | ||
createConfig<ClientOptions>({ | ||
baseUrl: "https://api.thirdweb-dev.com", | ||
}), | ||
); | ||
Comment on lines
+24
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify the hardcoded base URL configuration. The client is configured with a hardcoded base URL 🌐 Web query:
💡 Result: The production API base URL for thirdweb services varies depending on the specific service you are using. Here are the base URLs for some of thirdweb's key services:
Please note that some services may require additional configuration, such as specifying the chain ID in the Bundler API's base URL. For the most accurate and up-to-date information, refer to the official thirdweb documentation or the specific service's API reference. Citations:
Update hardcoded API base URL and enable configuration The generated client is currently pointing to the dev endpoint baseUrl: "https://api.thirdweb-dev.com", but none of our production services use that host. Per the thirdweb docs, production URLs include:
Please:
Suggested diff in packages/api/src/client/client.gen.ts (lines 24–28): -export const client = createClient(
- createConfig<ClientOptions>({
- baseUrl: "https://api.thirdweb-dev.com",
- }),
-);
+export const client = createClient(
+ createConfig<ClientOptions>({
+ baseUrl:
+ process.env.THIRDWEB_API_BASE_URL ??
+ "https://contract-api.thirdweb.com/", // update to correct service URL
+ }),
+); 🤖 Prompt for AI Agents
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,189 @@ | ||||||
import type { Client, Config, RequestOptions } from "./types.js"; | ||||||
import { | ||||||
buildUrl, | ||||||
createConfig, | ||||||
createInterceptors, | ||||||
getParseAs, | ||||||
mergeConfigs, | ||||||
mergeHeaders, | ||||||
setAuthParams, | ||||||
} from "./utils.js"; | ||||||
|
||||||
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, | ||||||
RequestOptions | ||||||
>(); | ||||||
|
||||||
const request: Client["request"] = async (options) => { | ||||||
const opts = { | ||||||
..._config, | ||||||
...options, | ||||||
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, | ||||||
headers: mergeHeaders(_config.headers, options.headers), | ||||||
}; | ||||||
|
||||||
if (opts.security) { | ||||||
await setAuthParams({ | ||||||
...opts, | ||||||
security: opts.security, | ||||||
}); | ||||||
} | ||||||
|
||||||
if (opts.body && opts.bodySerializer) { | ||||||
opts.body = opts.bodySerializer(opts.body); | ||||||
} | ||||||
|
||||||
// remove Content-Type header if body is empty to avoid sending invalid requests | ||||||
if (opts.body === undefined || opts.body === "") { | ||||||
opts.headers.delete("Content-Type"); | ||||||
} | ||||||
|
||||||
const url = buildUrl(opts); | ||||||
const requestInit: ReqInit = { | ||||||
redirect: "follow", | ||||||
...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 = await _fetch(request); | ||||||
|
||||||
for (const fn of interceptors.response._fns) { | ||||||
if (fn) { | ||||||
response = await fn(response, request, opts); | ||||||
} | ||||||
} | ||||||
|
||||||
const result = { | ||||||
request, | ||||||
response, | ||||||
}; | ||||||
|
||||||
if (response.ok) { | ||||||
if ( | ||||||
response.status === 204 || | ||||||
response.headers.get("Content-Length") === "0" | ||||||
) { | ||||||
return opts.responseStyle === "data" | ||||||
? {} | ||||||
: { | ||||||
data: {}, | ||||||
...result, | ||||||
}; | ||||||
} | ||||||
|
||||||
const parseAs = | ||||||
(opts.parseAs === "auto" | ||||||
? getParseAs(response.headers.get("Content-Type")) | ||||||
: opts.parseAs) ?? "json"; | ||||||
|
||||||
let data: any; | ||||||
switch (parseAs) { | ||||||
case "arrayBuffer": | ||||||
case "blob": | ||||||
case "formData": | ||||||
case "json": | ||||||
case "text": | ||||||
data = await response[parseAs](); | ||||||
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, | ||||||
}; | ||||||
} | ||||||
|
||||||
let error = await response.text(); | ||||||
|
||||||
try { | ||||||
error = JSON.parse(error); | ||||||
} catch { | ||||||
// noop | ||||||
} | ||||||
|
||||||
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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type inconsistency in error default assignment The fallback assigns an empty object to -finalError = finalError || ({} as string);
+finalError = finalError || ""; 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||
|
||||||
if (opts.throwOnError) { | ||||||
throw finalError; | ||||||
} | ||||||
|
||||||
// TODO: we probably want to return error and improve types | ||||||
return opts.responseStyle === "data" | ||||||
? undefined | ||||||
: { | ||||||
error: finalError, | ||||||
...result, | ||||||
}; | ||||||
}; | ||||||
|
||||||
return { | ||||||
buildUrl, | ||||||
connect: (options) => request({ ...options, method: "CONNECT" }), | ||||||
delete: (options) => request({ ...options, method: "DELETE" }), | ||||||
get: (options) => request({ ...options, method: "GET" }), | ||||||
getConfig, | ||||||
head: (options) => request({ ...options, method: "HEAD" }), | ||||||
interceptors, | ||||||
options: (options) => request({ ...options, method: "OPTIONS" }), | ||||||
patch: (options) => request({ ...options, method: "PATCH" }), | ||||||
post: (options) => request({ ...options, method: "POST" }), | ||||||
put: (options) => request({ ...options, method: "PUT" }), | ||||||
request, | ||||||
setConfig, | ||||||
trace: (options) => request({ ...options, method: "TRACE" }), | ||||||
}; | ||||||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the import/function call mismatch.
The example imports
writeContract
but callssendTransaction
, which will cause a runtime error.Apply this fix:
Or alternatively, if
writeContract
is the correct function:📝 Committable suggestion
🤖 Prompt for AI Agents