diff --git a/.gitignore b/.gitignore index ded76b8..dbd651c 100644 --- a/.gitignore +++ b/.gitignore @@ -53,9 +53,9 @@ build/ *.dSYM/ # Maturin -python/sdk/*.so -python/sdk/__pycache__/ -python/sdk/*.dSYM/ +python/quicknode_sdk/*.so +python/quicknode_sdk/__pycache__/ +python/quicknode_sdk/*.dSYM/ # Node.js node_modules/ diff --git a/CLAUDE.md b/CLAUDE.md index b3fe58a..066a008 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,7 @@ cargo run --example admin -p quicknode-sdk # Run example (requires QN_AP ```bash just python-setup # Create .venv and uv sync (one-time) just python-build # Compile bindings + generate stubs -cp python/sdk/init_manual_override.pyi python/sdk/__init__.pyi # Manually override __init__ so we can overwrite the commands +cp python/quicknode_sdk/init_manual_override.pyi python/quicknode_sdk/__init__.pyi # Manually override __init__ so we can overwrite the commands ``` Both recipes are shell-agnostic — they invoke `maturin` via `uvx`, so no venv activation is required and they work in bash, zsh, or fish without per-shell setup. @@ -60,11 +60,11 @@ This is a polyglot SDK: one Rust core library with Python, Node.js, and Ruby bin ### Workspace Layout - `crates/core` — Pure Rust business logic (HTTP client, request/response types, errors) -- `crates/python` — PyO3 wrapper crate, compiles to `sdk._core` Python extension +- `crates/python` — PyO3 wrapper crate, compiles to `quicknode_sdk._core` Python extension - `crates/node` — napi-rs wrapper crate, compiles to native `.node` module - `crates/ruby` — Magnus wrapper crate, compiles to native `.bundle`/`.so` module - `crates/python-stubs` — Generates `.pyi` type stub files -- `python/sdk/` — Python package directory (distributed via maturin) +- `python/quicknode_sdk/` — Python package directory (distributed via maturin) - `npm/` — Node.js package directory - `ruby/` — Ruby package directory (`lib/quicknode_sdk.rb` entry point, `examples/`) @@ -133,7 +133,7 @@ Each binding owns its mapping in a dedicated `errors.rs` file: When adding a new `SdkError` variant: 1. Add the variant to `crates/core/src/errors.rs` and update `http_kind()` if it's transport-level. 2. Update the `match` in each binding's `map_*_err` function — the compiler will flag missing arms in Python and Ruby (Node's match is also exhaustive on the kind string). -3. If the new variant should surface as a new exception class, add it to all three bindings + `npm/errors.js` + the exports in `python/sdk/__init__.py` + `npm/sdk.d.ts` + `npm/sdk.mjs`. +3. If the new variant should surface as a new exception class, add it to all three bindings + `npm/errors.js` + the exports in `python/quicknode_sdk/__init__.py` + `npm/sdk.d.ts` + `npm/sdk.mjs`. 4. Update examples in all four languages to demonstrate the new class if user-facing. ### Python Binding Pattern @@ -163,12 +163,12 @@ Core clients are tested using mocked API calls with wiremock. All functions maki - **Discriminated unions (Rust enum-with-data)** cannot be annotated with `#[pyclass]` or `#[napi(object)]`. Pattern: keep the enum pure-Rust in `crates/core` with `#[serde(tag = "...", content = "...")]`, then in each binding crate provide a typed surface that converts to/from the enum at the FFI boundary. Any core struct that flattens such an enum (e.g. via `#[serde(flatten)]`) also loses its `#[pyclass]` / `#[napi(object)]` and needs a wrapper in each binding. Reference implementations: - `crates/core/src/streams/stream.rs::DestinationAttributes` — stream destinations, wrapped by `crates/{python,node}/src/streams_destination.rs`. - `crates/core/src/webhooks/webhook.rs::TemplateArgs` — webhook filter templates, wrapped by `crates/{python,node}/src/webhooks_template.rs`. Ruby accepts the wire JSON directly (`{"templateId":..., "templateArgs":...}`) and relies on serde to deserialize into the enum. -- `python/sdk/__init__.py` is **manually maintained** — it is NOT auto-generated. Every new public struct/type must be added to both the `from sdk._core import (...)` block and the `__all__` list in this file +- `python/quicknode_sdk/__init__.py` is **manually maintained** — it is NOT auto-generated. Every new public struct/type must be added to both the `from quicknode_sdk._core import (...)` block and the `__all__` list in this file - When adding a new type with `#[cfg_attr(feature = "node", napi(object))]`, also add it to the named `export type { ... }` block in `npm/sdk.d.ts` — this is the user-facing type file and is not auto-updated by napi-rs - When adding a new `#[napi(string_enum)]` Rust enum, it generates a TypeScript `const enum` in `npm/index.d.ts`. In `npm/sdk.d.ts`, these must be re-exported using a regular `export { ... }` (not `export type { ... }`), otherwise TypeScript consumers cannot use them as values (e.g., `StreamDataset.Block`) - When updating `sdk.js` wrapper methods, verify the argument types match the underlying napi-rs constructor/method signature (object vs primitive) - When adding a new export to `sdk.js`, also add it to the named exports in `npm/sdk.mjs` — ESM named exports cannot be spread dynamically and must be listed explicitly -- `python/sdk/__init__.pyi` is overwritten by `just python-build` — edit `init_manual_override.pyi` instead +- `python/quicknode_sdk/__init__.pyi` is overwritten by `just python-build` — edit `init_manual_override.pyi` instead - `ruby/sig/quicknode_sdk.rbs` is **manually maintained** — it is NOT auto-generated. It provides RBS type signatures so editor LSPs (VSCode Ruby LSP, Solargraph, RubyMine, Steep) autocomplete method names and keyword argument keys for `QuicknodeSdk::Admin/Streams/Webhooks/KvStore/DestinationAttributes` and the exception classes. Every change to method registration in `crates/ruby/src/lib.rs` (new method, renamed key, new arg, removed arg, type change) must be mirrored here in the same PR. Responses are typed as `untyped` because they're wrapped in `QuicknodeSdk::IndifferentHash` at the Ruby boundary — that's intentional, do not try to type response shapes. - Always update examples alongside the code changes diff --git a/Justfile b/Justfile index 47b6b7f..9d70acf 100644 --- a/Justfile +++ b/Justfile @@ -2,7 +2,7 @@ python-setup: uv venv && uv sync python-build: - uvx maturin develop && cargo run -p sdk-python-stubs && cp python/sdk/init_manual_override.pyi python/sdk/__init__.pyi + uvx maturin develop && cargo run -p sdk-python-stubs && cp python/quicknode_sdk/init_manual_override.pyi python/quicknode_sdk/__init__.pyi node-build: cd ./npm && npm install && npm run build && npm run test && cd .. diff --git a/README.md b/README.md index f9dfe07..0c3ac14 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A unified SDK for building on Quicknode. Rust SDK with Python, Node.js, and Ruby bindings. +> **Pre-1.0**: While on `0.x`, releases may contain breaking changes. Check the [release notes](https://github.com/quicknode/sdk/releases) before upgrading. + ## Table of Contents - [Per-language docs](#per-language-docs) @@ -41,7 +43,7 @@ sdk/ │ ├── python/ # PyO3 bindings │ ├── node/ # napi-rs bindings │ └── ruby/ # magnus bindings -├── python/sdk/ # Python package with type hints +├── python/quicknode_sdk/ # Python package with type hints ├── npm/ # Node.js package with TypeScript types ├── ruby/ # Ruby package └── pyproject.toml # maturin build config diff --git a/crates/core/README.md b/crates/core/README.md index 3626955..48154f1 100644 --- a/crates/core/README.md +++ b/crates/core/README.md @@ -4,6 +4,8 @@ The core Rust crate for the Quicknode SDK. This is one of four language bindings published from the same Rust core. See the [project README](https://github.com/quicknode/sdk/blob/main/README.md) for the polyglot overview, development setup, and release process. +> **Pre-1.0**: While on `0.x`, releases may contain breaking changes. Check the [release notes](https://github.com/quicknode/sdk/releases) before upgrading. + ## Table of Contents - [Installation](#installation) diff --git a/crates/python/pyproject.toml b/crates/python/pyproject.toml index 6fe7df3..d44bb1f 100644 --- a/crates/python/pyproject.toml +++ b/crates/python/pyproject.toml @@ -2,5 +2,5 @@ name = "quicknode-sdk" [tool.maturin] -module-name = "sdk._core" +module-name = "quicknode_sdk._core" python-source = "../../python" \ No newline at end of file diff --git a/npm/README.md b/npm/README.md index 9a662e7..5c85e1a 100644 --- a/npm/README.md +++ b/npm/README.md @@ -4,6 +4,8 @@ Node.js / TypeScript bindings for the Quicknode SDK. This is one of four language bindings published from the same Rust core. See the [project README](https://github.com/quicknode/sdk/blob/main/README.md) for the polyglot overview, development setup, and release process. +> **Pre-1.0**: While on `0.x`, releases may contain breaking changes. Check the [release notes](https://github.com/quicknode/sdk/releases) before upgrading. + ## Table of Contents - [Installation](#installation) diff --git a/pyproject.toml b/pyproject.toml index b500ede..3d3ad54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ Issues = "https://github.com/quicknode/sdk/issues" [tool.maturin] manifest-path = "crates/python/Cargo.toml" -module-name = "sdk._core" +module-name = "quicknode_sdk._core" python-source = "python" features = ["extension-module"] include = [ diff --git a/python/README.md b/python/README.md index f5ac866..093a79f 100644 --- a/python/README.md +++ b/python/README.md @@ -4,6 +4,8 @@ Python bindings for the Quicknode SDK. This is one of four language bindings published from the same Rust core. See the [project README](https://github.com/quicknode/sdk/blob/main/README.md) for the polyglot overview, development setup, and release process. +> **Pre-1.0**: While on `0.x`, releases may contain breaking changes. Check the [release notes](https://github.com/quicknode/sdk/releases) before upgrading. + ## Table of Contents - [Installation](#installation) @@ -58,7 +60,7 @@ Construct the SDK once, then reach into the four sub-clients (`admin`, `streams` ```python # Python import asyncio -from sdk import QuicknodeSdk +from quicknode_sdk import QuicknodeSdk async def main(): qn = QuicknodeSdk.from_env() @@ -76,7 +78,7 @@ There are two ways to configure the SDK. ```python # Python -from sdk import QuicknodeSdk, SdkFullConfig, HttpConfig +from quicknode_sdk import QuicknodeSdk, SdkFullConfig, HttpConfig qn = QuicknodeSdk(SdkFullConfig(api_key="your-key", http=HttpConfig(timeout_secs=30))) ``` @@ -111,7 +113,7 @@ quicknode-sdk-/ (-; - **Pre-1.0**: While on `0.x`, releases may contain breaking changes. Check the [release notes](https://github.com/quicknode/sdk/releases) before upgrading. + ## Table of Contents - [Installation](#installation)