feat: add official TypeScript SDK to sdks/#64
feat: add official TypeScript SDK to sdks/#64KushagraJaiswar02 merged 2 commits intogeturbackend:mainfrom
Conversation
Adds the first official SDK for urBackend — a fully typed
TypeScript package wrapping all three core API modules.
Location: sdks/urbackend-sdk/
Package: @urbackend/sdk
Features:
- Zero runtime dependencies (native fetch only)
- Full TypeScript generics: db.getAll<Product>('products')
- Typed errors: AuthError, NotFoundError, RateLimitError
- ESM + CJS dual build via tsup
- 16 unit tests, all passing
- Node.js and browser compatible
Modules: auth (signUp/login/me/logout), db (CRUD), storage
|
@KushagraJaiswar02 is attempting to deploy a commit to the Yash Pouranik's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughA new TypeScript SDK package Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant Client as UrBackendClient
participant Auth as AuthModule
participant API as Backend API
App->>Client: urBackend({ apiKey })
Client-->>App: client instance
App->>Auth: client.auth.login(credentials)
Auth->>Client: client.request('POST', '/api/userAuth/login', {body})
Client->>API: POST /api/userAuth/login<br/>(with x-api-key header)
API-->>Client: {success: true, data: {token, user}}
Client-->>Auth: AuthResponse
Auth->>Auth: sessionToken = token
Auth-->>App: AuthResponse
App->>Auth: client.auth.me()
Auth->>Client: client.request('GET', '/api/userAuth/me',<br/>{token: sessionToken})
Client->>API: GET /api/userAuth/me<br/>(with Authorization header)
API-->>Client: {success: true, data: user}
Client-->>Auth: AuthUser
Auth-->>App: AuthUser
sequenceDiagram
participant App as Application
participant Client as UrBackendClient
participant DB as DatabaseModule
participant API as Backend API
App->>Client: urBackend({ apiKey })
Client-->>App: client instance
App->>DB: client.db.getAll('users')
DB->>Client: client.request('GET', '/api/data/users')
Client->>API: GET /api/data/users<br/>(with x-api-key header)
API-->>Client: {success: true, data: [...]}
Client-->>DB: T[]
DB-->>App: T[]
App->>DB: client.db.insert('users', {name, email})
DB->>Client: client.request('POST', '/api/data/users',<br/>{body: payload})
Client->>API: POST /api/data/users<br/>(with JSON body)
API-->>Client: {success: true, data: {_id, ...}}
Client-->>DB: T
DB-->>App: T
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces the official TypeScript SDK for urBackend, providing structured modules for authentication, database management, and storage services. The implementation includes custom error handling, type definitions, and a Vitest-based test suite. Feedback focuses on correcting invalid dependency versions in package.json, replacing hardcoded browser headers with more appropriate SDK identifiers, and improving type safety during file uploads to prevent runtime errors.
There was a problem hiding this comment.
Pull request overview
Adds a new first-party TypeScript SDK package under sdks/urbackend-sdk (@urbackend/sdk) to provide typed wrappers around urBackend auth, database, and storage APIs.
Changes:
- Introduces
UrBackendClientwithauth,db, andstoragemodules plus typed error classes and shared request handler. - Adds Vitest unit tests for auth/db/storage behavior and tsup-based dual ESM+CJS build config.
- Adds SDK-local tooling/config (TypeScript config, ESLint flat config, Prettier config, README).
Reviewed changes
Copilot reviewed 19 out of 21 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| sdks/urbackend-sdk/vitest.config.ts | Vitest configuration with Node environment + coverage. |
| sdks/urbackend-sdk/tsup.config.ts | Dual-build (CJS/ESM) bundling configuration. |
| sdks/urbackend-sdk/tsconfig.json | TS build configuration for SDK source. |
| sdks/urbackend-sdk/tests/tsconfig.json | TS config for tests (Vitest globals). |
| sdks/urbackend-sdk/tests/storage.test.ts | Storage module tests (upload/delete/error). |
| sdks/urbackend-sdk/tests/database.test.ts | Database module tests (CRUD + error mapping). |
| sdks/urbackend-sdk/tests/auth.test.ts | Auth module tests (signup/login/me/logout + errors). |
| sdks/urbackend-sdk/src/types/index.ts | Public SDK types for config/payloads/responses. |
| sdks/urbackend-sdk/src/modules/storage.ts | Storage API wrapper (upload/delete). |
| sdks/urbackend-sdk/src/modules/database.ts | Database CRUD wrapper. |
| sdks/urbackend-sdk/src/modules/auth.ts | Auth wrapper (signup/login/me/logout). |
| sdks/urbackend-sdk/src/index.ts | SDK entrypoint + exports. |
| sdks/urbackend-sdk/src/errors.ts | Typed error classes + HTTP error parsing. |
| sdks/urbackend-sdk/src/client.ts | Core request implementation and module accessors. |
| sdks/urbackend-sdk/package.json | Package metadata, scripts, and devDependencies. |
| sdks/urbackend-sdk/package-lock.json | Lockfile for SDK workspace dependencies. |
| sdks/urbackend-sdk/lint.txt | Committed lint output artifact (currently corrupted/binary). |
| sdks/urbackend-sdk/eslint.config.js | ESLint flat config for the SDK. |
| sdks/urbackend-sdk/README.md | SDK usage docs + examples. |
| sdks/urbackend-sdk/.prettierrc | Prettier configuration. |
| sdks/urbackend-sdk/.gitignore | SDK-local ignores. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (4)
sdks/urbackend-sdk/package.json (1)
1-52: Consider adding afilesfield to control npm package contents.The package.json is well-configured for dual ESM/CJS builds, but lacks a
filesfield. Without it, npm will publish everything not excluded by.npmignore. Adding"files": ["dist"]ensures only the build output is published, reducing package size and avoiding accidental inclusion of tests or source files.📦 Proposed addition
"types": "./dist/index.d.ts", + "files": [ + "dist" + ], "exports": {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdks/urbackend-sdk/package.json` around lines 1 - 52, Add a "files" field to package.json to limit published contents to the build output (e.g., "files": ["dist"]) so only the compiled artifacts referenced by "main", "module", and "types" are published; update the top-level package.json object near the existing "exports"/"scripts" entries to include this field and ensure .npmignore or other publish settings remain consistent.sdks/urbackend-sdk/src/modules/database.ts (1)
11-20: Consider documenting the empty array fallback behavior.The
getAllmethod silently returns an empty array when aNotFoundErroroccurs. This is a reasonable UX choice, but callers may want to distinguish between "collection exists but is empty" vs "collection not found." Consider adding a JSDoc note about this behavior.📝 Suggested documentation update
/** * Fetch all documents from a collection + * `@remarks` Returns an empty array if the collection does not exist */ public async getAll<T extends DocumentData>(collection: string): Promise<T[]> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdks/urbackend-sdk/src/modules/database.ts` around lines 11 - 20, Document that getAll<T extends DocumentData>(collection: string) returns an empty array when the collection is not found (NotFoundError) so callers cannot distinguish "collection missing" from "collection exists but empty"; add a JSDoc block above the getAll method that clearly states this fallback behavior, the return type (T[]), and a suggested alternative (e.g., use a separate exists/getMetadata method or catch NotFoundError) so callers know how to detect a missing collection if needed.sdks/urbackend-sdk/tests/auth.test.ts (1)
4-6: Shared client instance may cause test order dependencies.The client is instantiated at module scope and reused across all tests. Since
AuthModulestoressessionTokeninternally, tests that calllogin()orlogout()affect subsequent tests. For example, the test at line 80-83 depends on the state left by previous tests.Consider using
beforeEachto create a fresh client instance, or explicitly reset state in each test.♻️ Alternative approach with fresh client per test
const mockApiKey = 'test-api-key'; -const client = urBackend({ apiKey: mockApiKey }); + +let client: ReturnType<typeof urBackend>; + +beforeEach(() => { + client = urBackend({ apiKey: mockApiKey }); +});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdks/urbackend-sdk/tests/auth.test.ts` around lines 4 - 6, The test suite currently creates a shared client at module scope (const client = urBackend({ apiKey: mockApiKey })) causing AuthModule state (sessionToken) leaks between tests; change tests to instantiate a fresh client before each test (use beforeEach to set client = urBackend({ apiKey: mockApiKey })) or explicitly clear AuthModule state by calling logout()/reset on the client between tests so login()/logout() calls do not affect other tests; update references to the module-scope client variable and ensure tests use the per-test client instance returned by urBackend and its AuthModule methods (login, logout, sessionToken) when asserting behavior.sdks/urbackend-sdk/README.md (1)
36-42: Add blank lines around tables for markdown consistency.The tables in the API Reference section are missing blank lines before them, which causes markdownlint warnings (MD058). This applies to Auth (line 37), Database (line 45), and Storage (line 54) tables.
📝 Proposed fix
### Auth + | Method | Params | Returns |Apply the same pattern before each table in the document.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdks/urbackend-sdk/README.md` around lines 36 - 42, The markdown tables under the API Reference (Auth, Database, Storage) violate MD058 because they lack a blank line above them; edit the README.md to insert a single blank line immediately before each table (the Auth table starting with "| Method | Params | Returns |", the Database table block, and the Storage table block) so each table is preceded by an empty line for proper markdownlint compliance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@sdks/urbackend-sdk/src/client.ts`:
- Around line 71-74: The current branch uses a truthiness check (else if
(options.body)) which drops valid falsy payloads like 0, false, '', and null;
change the condition to detect presence rather than truthiness (e.g., use "if
('body' in options)" or "if (options.body !== undefined)") so
headers['Content-Type'] is set and requestBody = JSON.stringify(options.body)
runs for those values; update the branch around headers['Content-Type'] /
requestBody in client.ts accordingly.
- Around line 55-60: The hardcoded 'Origin' and 'Referer' entries in the headers
object override browser behavior and are forbidden by Fetch; remove those keys
from the headers (the headers: Record<string,string> declaration in client.ts
where 'x-api-key' and 'User-Agent' are set) so the browser can control Origin
and, if you need to set referer, use the fetch option referrer or leave it
unset; ensure the rest of the code that builds the request (the same method that
uses this.headers and this.apiKey) still passes the remaining headers unchanged.
In `@sdks/urbackend-sdk/src/errors.ts`:
- Around line 75-77: The code handling status === 429 currently reads
Retry-After via response.headers.get('Retry-After') and uses
parseInt(retryAfter, 10), which only supports delta-seconds; update this logic
in the same block to detect whether retryAfter is a numeric string (use
/^\s*\d+\s*$/) and parseInt when so, otherwise attempt to parse it as an
HTTP-date using Date.parse(retryAfter) and compute seconds as
Math.ceil((Date.parse(retryAfter) - Date.now())/1000) only if the parsed time is
valid (not NaN); pass that seconds value (or undefined if invalid/negative) into
the RateLimitError constructor instead of blindly using parseInt.
In `@sdks/urbackend-sdk/src/modules/storage.ts`:
- Around line 22-25: The browser/Blob branch calls formData.append('file', file
as unknown as Blob, filename) but doesn't fallback when filename is undefined;
change the call in the same conditional to pass filename ?? 'file' (or
equivalent) so it defaults to 'file' like the Node Buffer branch; update the
formData.append usage referencing the filename variable and the file Blob to
ensure a consistent default filename in all environments.
In `@sdks/urbackend-sdk/tests/database.test.ts`:
- Around line 86-104: The test currently uses a try/catch so it will silently
succeed if client.db.getAll('products') doesn't throw; replace the try/catch
with an assertion that the promise rejects (e.g., use await
expect(client.db.getAll('products')).rejects...) and assert the error
shape—check name === 'RateLimitError' and retryAfter === 60 (or use
rejects.toMatchObject / rejects.toHaveProperty) so the test fails if no error is
thrown; locate this change around the test block named "RateLimitError thrown on
429" and the call to client.db.getAll('products').
---
Nitpick comments:
In `@sdks/urbackend-sdk/package.json`:
- Around line 1-52: Add a "files" field to package.json to limit published
contents to the build output (e.g., "files": ["dist"]) so only the compiled
artifacts referenced by "main", "module", and "types" are published; update the
top-level package.json object near the existing "exports"/"scripts" entries to
include this field and ensure .npmignore or other publish settings remain
consistent.
In `@sdks/urbackend-sdk/README.md`:
- Around line 36-42: The markdown tables under the API Reference (Auth,
Database, Storage) violate MD058 because they lack a blank line above them; edit
the README.md to insert a single blank line immediately before each table (the
Auth table starting with "| Method | Params | Returns |", the Database table
block, and the Storage table block) so each table is preceded by an empty line
for proper markdownlint compliance.
In `@sdks/urbackend-sdk/src/modules/database.ts`:
- Around line 11-20: Document that getAll<T extends DocumentData>(collection:
string) returns an empty array when the collection is not found (NotFoundError)
so callers cannot distinguish "collection missing" from "collection exists but
empty"; add a JSDoc block above the getAll method that clearly states this
fallback behavior, the return type (T[]), and a suggested alternative (e.g., use
a separate exists/getMetadata method or catch NotFoundError) so callers know how
to detect a missing collection if needed.
In `@sdks/urbackend-sdk/tests/auth.test.ts`:
- Around line 4-6: The test suite currently creates a shared client at module
scope (const client = urBackend({ apiKey: mockApiKey })) causing AuthModule
state (sessionToken) leaks between tests; change tests to instantiate a fresh
client before each test (use beforeEach to set client = urBackend({ apiKey:
mockApiKey })) or explicitly clear AuthModule state by calling logout()/reset on
the client between tests so login()/logout() calls do not affect other tests;
update references to the module-scope client variable and ensure tests use the
per-test client instance returned by urBackend and its AuthModule methods
(login, logout, sessionToken) when asserting behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 49c138fb-2cc4-4186-9118-53bde5a2d435
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonsdks/urbackend-sdk/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (20)
sdks/urbackend-sdk/.gitignoresdks/urbackend-sdk/.prettierrcsdks/urbackend-sdk/README.mdsdks/urbackend-sdk/eslint.config.jssdks/urbackend-sdk/lint.txtsdks/urbackend-sdk/package.jsonsdks/urbackend-sdk/src/client.tssdks/urbackend-sdk/src/errors.tssdks/urbackend-sdk/src/index.tssdks/urbackend-sdk/src/modules/auth.tssdks/urbackend-sdk/src/modules/database.tssdks/urbackend-sdk/src/modules/storage.tssdks/urbackend-sdk/src/types/index.tssdks/urbackend-sdk/tests/auth.test.tssdks/urbackend-sdk/tests/database.test.tssdks/urbackend-sdk/tests/storage.test.tssdks/urbackend-sdk/tests/tsconfig.jsonsdks/urbackend-sdk/tsconfig.jsonsdks/urbackend-sdk/tsup.config.tssdks/urbackend-sdk/vitest.config.ts
| const headers: Record<string, string> = { | ||
| 'x-api-key': this.apiKey, | ||
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', | ||
| 'Origin': 'https://urbackend.bitbros.in', | ||
| 'Referer': 'https://urbackend.bitbros.in/', | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and examine the client.ts file
fd -e ts -p "*client.ts" | grep -E "sdks/urbackend-sdk"Repository: yash-pouranik/urBackend
Length of output: 356
🏁 Script executed:
# If found, let's read the relevant section (lines 55-60)
cat -n sdks/urbackend-sdk/src/client.ts | sed -n '50,75p'Repository: yash-pouranik/urBackend
Length of output: 1110
🌐 Web query:
Fetch Standard forbidden request headers Origin Referer
💡 Result:
In the Fetch Standard, certain HTTP request headers known as "forbidden request-headers" cannot be set programmatically via the Fetch API (e.g., fetch or Headers object). This restriction ensures the user agent (browser) retains control over these headers for security and consistency. Both the "Origin" and "Referer" headers are explicitly forbidden request-headers. From the WHATWG Fetch Standard (last updated March 2026): A header is forbidden if its name matches (case-insensitive) names like Origin, and Referer is handled similarly in the spec's forbidden checks. Headers starting with Sec-, Proxy-, and others like Host, Cookie, Set-Cookie are also forbidden. MDN documentation confirms the full list includes: - Accept-Charset - Accept-Encoding - Access-Control-Request-Headers - Access-Control-Request-Method - Connection - Content-Length - Cookie - Date - DNT - Expect - Host - Keep-Alive - Origin - Proxy-* - Sec-* - Referer - Set-Cookie - TE - Trailer - Transfer-Encoding - Upgrade - Via - Certain X-* method override headers containing forbidden methods (CONNECT, TRACE, TRACK) Note: While "Referer" is spec-forbidden for direct setting, it can be indirectly controlled via the referrer option in fetch. "Origin" cannot be set at all by scripts.
Citations:
- 1: https://fetch.spec.whatwg.org/
- 2: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
- 3: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header
- 4: https://fetch.spec.whatwg.org/review-drafts/2024-06/
Remove manual Origin/Referer headers.
Fetch treats Origin and Referer as forbidden request-header names, so browser-side calls will ignore these hardcoded values. The browser controls Origin entirely, while Referer can only be indirectly set via the referrer option in the Fetch API. This causes a runtime discrepancy where the SDK behaves differently than its code suggests in the advertised browser environment.
Suggested fix
const headers: Record<string, string> = {
'x-api-key': this.apiKey,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
- 'Origin': 'https://urbackend.bitbros.in',
- 'Referer': 'https://urbackend.bitbros.in/',
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const headers: Record<string, string> = { | |
| 'x-api-key': this.apiKey, | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', | |
| 'Origin': 'https://urbackend.bitbros.in', | |
| 'Referer': 'https://urbackend.bitbros.in/', | |
| }; | |
| const headers: Record<string, string> = { | |
| 'x-api-key': this.apiKey, | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdks/urbackend-sdk/src/client.ts` around lines 55 - 60, The hardcoded
'Origin' and 'Referer' entries in the headers object override browser behavior
and are forbidden by Fetch; remove those keys from the headers (the headers:
Record<string,string> declaration in client.ts where 'x-api-key' and
'User-Agent' are set) so the browser can control Origin and, if you need to set
referer, use the fetch option referrer or leave it unset; ensure the rest of the
code that builds the request (the same method that uses this.headers and
this.apiKey) still passes the remaining headers unchanged.
| } else if (options.body) { | ||
| headers['Content-Type'] = 'application/json'; | ||
| requestBody = JSON.stringify(options.body); | ||
| } |
There was a problem hiding this comment.
Don't drop falsy JSON payloads.
This branch skips valid payloads like 0, false, '', and null because it checks truthiness instead of whether a body was provided.
Suggested fix
- } else if (options.body) {
+ } else if (options.body !== undefined) {
headers['Content-Type'] = 'application/json';
requestBody = JSON.stringify(options.body);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (options.body) { | |
| headers['Content-Type'] = 'application/json'; | |
| requestBody = JSON.stringify(options.body); | |
| } | |
| } else if (options.body !== undefined) { | |
| headers['Content-Type'] = 'application/json'; | |
| requestBody = JSON.stringify(options.body); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdks/urbackend-sdk/src/client.ts` around lines 71 - 74, The current branch
uses a truthiness check (else if (options.body)) which drops valid falsy
payloads like 0, false, '', and null; change the condition to detect presence
rather than truthiness (e.g., use "if ('body' in options)" or "if (options.body
!== undefined)") so headers['Content-Type'] is set and requestBody =
JSON.stringify(options.body) runs for those values; update the branch around
headers['Content-Type'] / requestBody in client.ts accordingly.
| if (status === 429) { | ||
| const retryAfter = response.headers.get('Retry-After'); | ||
| return new RateLimitError(message, endpoint, retryAfter ? parseInt(retryAfter, 10) : undefined); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n sdks/urbackend-sdk/src/errors.ts | head -100 | tail -50Repository: yash-pouranik/urBackend
Length of output: 1771
🏁 Script executed:
wc -l sdks/urbackend-sdk/src/errors.tsRepository: yash-pouranik/urBackend
Length of output: 102
🌐 Web query:
RFC 9110 Retry-After header format specification
💡 Result:
The Retry-After header in RFC 9110 (HTTP Semantics) is defined in Section 10.2.3. It indicates the minimum time that the recipient ought to wait before making a follow-up request. Format specification (ABNF): Retry-After = HTTP-date / delta-seconds where: - HTTP-date is defined in Section 5.6.7 as IMF-fixdate / obs-date (preferred: IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT) - delta-seconds = 1*DIGIT (non-negative integer number of seconds to delay) Examples: Retry-After: 120 (wait 120 seconds) Retry-After: Sat, 31 Oct 2026 18:00:00 GMT (wait until that HTTP-date) Used primarily with 503 Service Unavailable, 429 Too Many Requests, and 3xx redirection status codes.
Citations:
- 1: https://datatracker.ietf.org/doc/html/rfc9110
- 2: https://http.dev/retry-after
- 3: https://www.rfc-editor.org/rfc/rfc9110.txt
🏁 Script executed:
rg -A 5 -B 5 "Retry-After" sdks/urbackend-sdk/src/Repository: yash-pouranik/urBackend
Length of output: 772
🏁 Script executed:
fd -e test.ts -e spec.ts -e test.js -e spec.js sdks/urbackend-sdk | xargs rg -l "RateLimitError\|Retry-After" 2>/dev/nullRepository: yash-pouranik/urBackend
Length of output: 446
🏁 Script executed:
rg "class RateLimitError" sdks/urbackend-sdk/src/errors.ts -A 5Repository: yash-pouranik/urBackend
Length of output: 291
🏁 Script executed:
fd . sdks/urbackend-sdk --full-path -e test.ts -e spec.ts -e test.js -e spec.jsRepository: yash-pouranik/urBackend
Length of output: 187
🏁 Script executed:
rg "429\|RateLimitError" sdks/urbackend-sdk/ -t ts -t jsRepository: yash-pouranik/urBackend
Length of output: 49
🏁 Script executed:
cat sdks/urbackend-sdk/tests/auth.test.ts | head -100Repository: yash-pouranik/urBackend
Length of output: 3006
🏁 Script executed:
rg "429" sdks/urbackend-sdk/tests/Repository: yash-pouranik/urBackend
Length of output: 220
🏁 Script executed:
rg "Retry-After\|retryAfter" sdks/urbackend-sdk/Repository: yash-pouranik/urBackend
Length of output: 49
🏁 Script executed:
cat -n sdks/urbackend-sdk/tests/database.test.ts | grep -A 20 "RateLimitError"Repository: yash-pouranik/urBackend
Length of output: 1240
🏁 Script executed:
cat sdks/urbackend-sdk/tests/database.test.ts | grep -A 20 "429"Repository: yash-pouranik/urBackend
Length of output: 750
Handle both Retry-After formats defined in RFC 9110.
The Retry-After header allows two formats: numeric seconds (delta-seconds) or an HTTP date. The current code only handles numeric values via parseInt(), which silently fails when a server sends an HTTP-date (e.g., Sat, 31 Oct 2026 18:00:00 GMT). This results in NaN, which evaluates to undefined when passed to RateLimitError, causing callers to lose retry timing information.
Suggested fix
if (status === 429) {
const retryAfter = response.headers.get('Retry-After');
- return new RateLimitError(message, endpoint, retryAfter ? parseInt(retryAfter, 10) : undefined);
+ let retryAfterSeconds: number | undefined;
+ if (retryAfter) {
+ if (/^\d+$/.test(retryAfter)) {
+ retryAfterSeconds = Number(retryAfter);
+ } else {
+ const retryAt = Date.parse(retryAfter);
+ if (!Number.isNaN(retryAt)) {
+ retryAfterSeconds = Math.max(0, Math.ceil((retryAt - Date.now()) / 1000));
+ }
+ }
+ }
+ return new RateLimitError(message, endpoint, retryAfterSeconds);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (status === 429) { | |
| const retryAfter = response.headers.get('Retry-After'); | |
| return new RateLimitError(message, endpoint, retryAfter ? parseInt(retryAfter, 10) : undefined); | |
| if (status === 429) { | |
| const retryAfter = response.headers.get('Retry-After'); | |
| let retryAfterSeconds: number | undefined; | |
| if (retryAfter) { | |
| if (/^\d+$/.test(retryAfter)) { | |
| retryAfterSeconds = Number(retryAfter); | |
| } else { | |
| const retryAt = Date.parse(retryAfter); | |
| if (!Number.isNaN(retryAt)) { | |
| retryAfterSeconds = Math.max(0, Math.ceil((retryAt - Date.now()) / 1000)); | |
| } | |
| } | |
| } | |
| return new RateLimitError(message, endpoint, retryAfterSeconds); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdks/urbackend-sdk/src/errors.ts` around lines 75 - 77, The code handling
status === 429 currently reads Retry-After via
response.headers.get('Retry-After') and uses parseInt(retryAfter, 10), which
only supports delta-seconds; update this logic in the same block to detect
whether retryAfter is a numeric string (use /^\s*\d+\s*$/) and parseInt when so,
otherwise attempt to parse it as an HTTP-date using Date.parse(retryAfter) and
compute seconds as Math.ceil((Date.parse(retryAfter) - Date.now())/1000) only if
the parsed time is valid (not NaN); pass that seconds value (or undefined if
invalid/negative) into the RateLimitError constructor instead of blindly using
parseInt.
| } else { | ||
| // Browser File/Blob or Node.js Blob/File | ||
| formData.append('file', file as unknown as Blob, filename); | ||
| } |
There was a problem hiding this comment.
Provide a default filename in the browser path.
When filename is undefined, the formData.append() call passes undefined as the third argument. Unlike the Node.js Buffer branch (line 21) which defaults to 'file', the browser path has no fallback. Some environments may handle this inconsistently.
🔧 Proposed fix
} else {
// Browser File/Blob or Node.js Blob/File
- formData.append('file', file as unknown as Blob, filename);
+ formData.append('file', file as unknown as Blob, filename || 'file');
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else { | |
| // Browser File/Blob or Node.js Blob/File | |
| formData.append('file', file as unknown as Blob, filename); | |
| } | |
| } else { | |
| // Browser File/Blob or Node.js Blob/File | |
| formData.append('file', file as unknown as Blob, filename || 'file'); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdks/urbackend-sdk/src/modules/storage.ts` around lines 22 - 25, The
browser/Blob branch calls formData.append('file', file as unknown as Blob,
filename) but doesn't fallback when filename is undefined; change the call in
the same conditional to pass filename ?? 'file' (or equivalent) so it defaults
to 'file' like the Node Buffer branch; update the formData.append usage
referencing the filename variable and the file Blob to ensure a consistent
default filename in all environments.
| test('RateLimitError thrown on 429', async () => { | ||
| vi.stubGlobal( | ||
| 'fetch', | ||
| vi.fn().mockResolvedValue({ | ||
| ok: false, | ||
| status: 429, | ||
| url: 'https://api.urbackend.bitbros.in/api/data/products', | ||
| headers: new Headers({ 'Retry-After': '60' }), | ||
| json: () => Promise.resolve({ success: false, message: 'Too Many Requests' }), | ||
| }), | ||
| ); | ||
|
|
||
| try { | ||
| await client.db.getAll('products'); | ||
| } catch (error: any) { | ||
| expect(error.name).toBe('RateLimitError'); | ||
| expect(error.retryAfter).toBe(60); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Test will pass even if no error is thrown.
The try/catch pattern here doesn't guarantee the catch block executes. If client.db.getAll() succeeds unexpectedly, the test passes without any assertions being run.
🔧 Proposed fix using expect().rejects
test('RateLimitError thrown on 429', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue({
ok: false,
status: 429,
url: 'https://api.urbackend.bitbros.in/api/data/products',
headers: new Headers({ 'Retry-After': '60' }),
json: () => Promise.resolve({ success: false, message: 'Too Many Requests' }),
}),
);
- try {
- await client.db.getAll('products');
- } catch (error: any) {
- expect(error.name).toBe('RateLimitError');
- expect(error.retryAfter).toBe(60);
- }
+ await expect(client.db.getAll('products')).rejects.toMatchObject({
+ name: 'RateLimitError',
+ retryAfter: 60,
+ });
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test('RateLimitError thrown on 429', async () => { | |
| vi.stubGlobal( | |
| 'fetch', | |
| vi.fn().mockResolvedValue({ | |
| ok: false, | |
| status: 429, | |
| url: 'https://api.urbackend.bitbros.in/api/data/products', | |
| headers: new Headers({ 'Retry-After': '60' }), | |
| json: () => Promise.resolve({ success: false, message: 'Too Many Requests' }), | |
| }), | |
| ); | |
| try { | |
| await client.db.getAll('products'); | |
| } catch (error: any) { | |
| expect(error.name).toBe('RateLimitError'); | |
| expect(error.retryAfter).toBe(60); | |
| } | |
| }); | |
| test('RateLimitError thrown on 429', async () => { | |
| vi.stubGlobal( | |
| 'fetch', | |
| vi.fn().mockResolvedValue({ | |
| ok: false, | |
| status: 429, | |
| url: 'https://api.urbackend.bitbros.in/api/data/products', | |
| headers: new Headers({ 'Retry-After': '60' }), | |
| json: () => Promise.resolve({ success: false, message: 'Too Many Requests' }), | |
| }), | |
| ); | |
| await expect(client.db.getAll('products')).rejects.toMatchObject({ | |
| name: 'RateLimitError', | |
| retryAfter: 60, | |
| }); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdks/urbackend-sdk/tests/database.test.ts` around lines 86 - 104, The test
currently uses a try/catch so it will silently succeed if
client.db.getAll('products') doesn't throw; replace the try/catch with an
assertion that the promise rejects (e.g., use await
expect(client.db.getAll('products')).rejects...) and assert the error
shape—check name === 'RateLimitError' and retryAfter === 60 (or use
rejects.toMatchObject / rejects.toHaveProperty) so the test fails if no error is
thrown; locate this change around the test block named "RateLimitError thrown on
429" and the call to client.db.getAll('products').
|
|
||
| constructor(config: UrBackendConfig) { | ||
| this.apiKey = config.apiKey; | ||
| this.baseUrl = config.baseUrl || 'https://api.urbackend.bitbros.in'; |
There was a problem hiding this comment.
Our public api base URL is - https://api.ub.bitbros.in
| @@ -0,0 +1,52 @@ | |||
| { | |||
| "name": "@urbackend/sdk", | |||
There was a problem hiding this comment.
name should be - @urbackend/ts-sdk or @urbackend/client
i think client name is more valid
| @@ -0,0 +1,52 @@ | |||
| { | |||
| "name": "@urbackend/sdk", | |||
| "version": "1.0.0", | |||
There was a problem hiding this comment.
version should start from 0.1.0 then we will upgrade gradually
| "typescript", | ||
| "backend-as-a-service" | ||
| ], | ||
| "author": "urBackend Contributors", |
There was a problem hiding this comment.
update author field -
"author": "UrBackend Team urbackend@bitbros.in"
| Official TypeScript SDK for [urBackend](https://urbackend.bitbros.in) — the instant Backend-as-a-Service for frontend developers. | ||
|
|
||
| ## Installation | ||
| npm install @urbackend/sdk |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@copilot apply changes based on the comments in this thread |
Adds the first official SDK for urBackend — a fully typed TypeScript package wrapping all three core API modules.
Location: sdks/urbackend-sdk/
Package: @urbackend/sdk
Features:
Modules: auth (signUp/login/me/logout), db (CRUD), storage
Built with ❤️ for urBackend.
Summary by CodeRabbit
New Features
@urbackend/sdkTypeScript SDK with user authentication, database operations, and file storageTests