Skip to content

Commit 735984c

Browse files
authored
Merge pull request #62 from MacPaw/feat/update-migration
feat: update migration
2 parents 37792f7 + bbe914b commit 735984c

File tree

11 files changed

+64
-10
lines changed

11 files changed

+64
-10
lines changed

MIGRATION.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This package is a **small Vercel AI SDK extension** for MacPaw AI Gateway. Upstream **`ai`**, **`@ai-sdk/openai`**, **`@ai-sdk/react`** (or **`ai/react`**) stay the source for core APIs and hooks.
44

5+
That also means upstream UI/hooks/version migrations stay upstream. If your installed `ai` / `@ai-sdk/react` version changes hook or schema APIs, follow the upstream versioned docs for those packages rather than expecting `@macpaw/ai-sdk` to redefine them.
6+
57
## Entry points
68

79
| Path | Role |
@@ -26,10 +28,14 @@ Replace `createAIGatewayClient` with:
2628

2729
Types for request/response bodies can come from your app, from OpenAI SDK types, or from gateway OpenAPI — they are not re-exported from this package today.
2830

31+
If you want the default production Gateway URL in fetch-only flows, use the public `resolveGatewayBaseURL()` helper and pass the resolved value into `createGatewayFetch()`.
32+
2933
## If you used `@macpaw/ai-sdk/runtime`
3034

3135
Internals (`executeRequestPipeline`, etc.) are not part of the public API. Prefer `createGatewayFetch` or provider options (`middleware`, `retry`, `timeout`, `fetch`).
3236

37+
The same applies to source-only error helpers: if a helper is not exported from `@macpaw/ai-sdk`, treat it as internal even if you see it in the repository source.
38+
3339
## Quick import cheatsheet
3440

3541
```ts

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ npm install @macpaw/ai-sdk
3030

3131
Also install upstream packages you call directly, for example `ai`, `@ai-sdk/openai`, `@ai-sdk/react`.
3232

33+
This package does not pin or wrap the upstream UI/hooks API from `ai` / `@ai-sdk/react`. Follow the versioned upstream docs for the exact major version you install there. If your chosen upstream version requires version-specific imports or patterns (for example schema helpers), use the upstream guidance for those APIs.
34+
3335
## Quick start (Vercel AI SDK)
3436

3537
```ts
@@ -88,9 +90,9 @@ Internal resolution: `resolveConfig()` in `gateway-config.ts`.
8890
Same auth, retry, middleware, and error normalization as the provider path. Use **relative** URLs under the gateway root (e.g. `'/api/v1/images/edits'`) or absolute URLs that stay under the same gateway origin.
8991

9092
```ts
91-
import { createGatewayFetch } from '@macpaw/ai-sdk';
93+
import { createGatewayFetch, resolveGatewayBaseURL } from '@macpaw/ai-sdk';
9294

93-
const baseURL = 'https://api.macpaw.com/ai'; // or resolve via env: 'production'
95+
const baseURL = resolveGatewayBaseURL(undefined, 'production', 'gatewayFetch');
9496
const gatewayFetch = createGatewayFetch({
9597
baseURL,
9698
getAuthToken: async () => token,
@@ -106,6 +108,8 @@ const res = await gatewayFetch('/api/v1/images/edits', { method: 'POST', body: f
106108

107109
Non-gateway absolute URLs are passed through without injecting Bearer auth (placeholder key is stripped). See `gateway-fetch.ts`.
108110

111+
`createGatewayFetch` requires a resolved `baseURL`. Use the exported `resolveGatewayBaseURL()` helper if you want the same `'production'` shortcut that provider factories support.
112+
109113
## `createGatewayProvider` — prefixed model IDs
110114

111115
Bare model IDs get a default Gateway prefix per provider constant; IDs that already contain `/` are unchanged.
@@ -147,6 +151,8 @@ Extends `GatewayProviderSettings` plus OpenAI provider settings (without `apiKey
147151
- `normalizeErrors` — default `true`; non-OK Gateway responses throw typed errors
148152
- `createOpenAI` — optional override of `createOpenAI` from `@ai-sdk/openai` (tests/advanced)
149153

154+
Use `normalizeErrors: false` only when you intentionally want to inspect raw failed `Response` objects in provider-driven tests or adapters. Auth refresh and retry behavior still stay on; only typed non-OK error throwing is relaxed.
155+
150156
## Middleware
151157

152158
```ts
@@ -199,6 +205,8 @@ AIGatewayModule.forRoot({
199205
});
200206
```
201207

208+
If your Nest app uses TypeScript subpath exports strictly, make sure its `tsconfig` uses a modern resolver such as `moduleResolution: "Node16"`, `"NodeNext"`, or `"bundler"` so `@macpaw/ai-sdk/nestjs` resolves correctly.
209+
202210
Inject **`GatewayProviderSettings`** (not an HTTP client) and build providers in the service:
203211

204212
```ts
@@ -225,6 +233,8 @@ export class ChatService {
225233

226234
`AIGatewayExceptionFilter` maps `AIGatewayError` to JSON HTTP responses. See `examples/nestjs/` for a copy-paste skeleton.
227235

236+
Only documented root exports are public API. Source-level helpers such as `parseErrorResponseFromResponse` and `parseStreamErrorPayload` may exist internally, but they are not supported import targets unless exported from `@macpaw/ai-sdk`.
237+
228238
## Examples
229239

230240
From the repo root:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
},
5757
"packageManager": "pnpm@10.15.1",
5858
"publishConfig": {
59-
"access": "restricted"
59+
"access": "public"
6060
},
6161
"scripts": {
6262
"build": "tsup",

src/__tests__/gateway-fetch.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,35 @@ describe('createGatewayFetch', () => {
353353
expect(headers.get('Authorization')).toBe('Bearer secret-bearer');
354354
});
355355

356+
it('strips placeholder Authorization before middleware on gateway requests', async () => {
357+
let headersSeenByMiddleware: Record<string, string> = {};
358+
359+
const customFetch = createGatewayFetch({
360+
baseURL: 'https://api.macpaw.com/ai',
361+
getAuthToken: async () => 'secret-bearer',
362+
middleware: [
363+
async (request, next) => {
364+
headersSeenByMiddleware = { ...request.headers };
365+
return next(request);
366+
},
367+
],
368+
});
369+
370+
await customFetch(
371+
new Request('https://api.macpaw.com/ai/api/v1/chat/completions', {
372+
method: 'POST',
373+
headers: { Authorization: 'Bearer ai-gateway-auth-via-fetch,' },
374+
}),
375+
);
376+
377+
const authHeader = Object.keys(headersSeenByMiddleware).find((k) => k.toLowerCase() === 'authorization');
378+
expect(authHeader).toBeUndefined();
379+
380+
const fetchCall = (globalThis.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
381+
const headers = new Headers(fetchCall[1].headers);
382+
expect(headers.get('Authorization')).toBe('Bearer secret-bearer');
383+
});
384+
356385
it('smoke: retries on 429 with Retry-After header and succeeds on second attempt', async () => {
357386
// Verifies Retry-After code path executes (retryAfterSeconds > 0).
358387
// Timing precision is not asserted — see withRetry unit tests for that.

src/__tests__/package-exports.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ describe('package exports', () => {
3737

3838
it('publishes the folders required for built code', () => {
3939
expect(packageJson.files).toContain('dist');
40+
expect(packageJson.files).toContain('scripts');
41+
expect(packageJson.files).toContain('templates');
4042
expect(packageJson.files).not.toContain('shims');
4143
});
4244
});

src/__tests__/root-entry.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ describe('root entry', () => {
99
expect(root.createGatewayProvider).toBeDefined();
1010
expect(root.GATEWAY_PROVIDERS).toBeDefined();
1111
expect(root.createGatewayFetch).toBeDefined();
12+
expect(root.resolveGatewayBaseURL).toBeDefined();
13+
expect(root.DEFAULT_BASE_URLS).toBeDefined();
1214
expect(root.GATEWAY_PLACEHOLDER_API_KEY).toBeDefined();
1315
});
1416

src/gateway-fetch.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ export function createGatewayFetch(
117117
}
118118
}
119119

120-
if (!isGatewayRequest) {
121-
stripPlaceholderAuthorization(headers, GATEWAY_PLACEHOLDER_API_KEY);
122-
}
120+
// Always strip the OpenAI placeholder before entering the shared pipeline.
121+
// For gateway requests the real Bearer is injected later via getAuthToken();
122+
// for non-gateway requests we also avoid leaking the sentinel downstream.
123+
stripPlaceholderAuthorization(headers, GATEWAY_PLACEHOLDER_API_KEY);
123124

124125
return executeRequestPipeline(
125126
resolvedConfig,

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ export type {
4242
} from './gateway-errors';
4343

4444
// Config types (consumers need these to configure the SDK)
45-
export type { GatewayProviderSettings, Middleware, RetryConfig } from './gateway-config';
45+
export { DEFAULT_BASE_URLS, resolveGatewayBaseURL } from './gateway-config';
46+
export type { Environment, GatewayProviderSettings, Middleware, RetryConfig } from './gateway-config';

templates/AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Apply these rules when integrating MacPaw AI Gateway.
99
- `@macpaw/ai-sdk/nestjs`: NestJS module and decorators.
1010
- Install `@ai-sdk/openai` when using `createAIGatewayProvider` or `createGatewayProvider`.
1111
- Never use `createAIGatewayClient`, `@macpaw/ai-sdk/client`, `runtime`, `types`, or `testing`.
12+
- For UI hooks or schema helpers, follow the versioned upstream docs for the installed `ai` / `@ai-sdk/react` major version; this package does not redefine those APIs.
1213

1314
## Choose one integration path
1415

@@ -22,7 +23,7 @@ Apply these rules when integrating MacPaw AI Gateway.
2223
- Do not invent a token source. Ask once if unclear.
2324
- Do not place gateway tokens in browser-only code.
2425
- `env` supports only `'production'`; use `baseURL` for staging/custom hosts.
25-
- `createGatewayFetch` requires `baseURL`.
26+
- `createGatewayFetch` requires a resolved `baseURL`; prefer `resolveGatewayBaseURL()` when you want the default production host.
2627
- Remove legacy imports and dependencies only after confirming all usages are migrated.
2728

2829
## Canonical snippets

templates/CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ Use this guidance when integrating MacPaw AI Gateway into this project.
99
- Keep generation primitives on upstream `ai` / `@ai-sdk/*`.
1010
- Install `@ai-sdk/openai` when using `createAIGatewayProvider` or `createGatewayProvider`; those paths depend on the OpenAI-compatible provider package.
1111
- Do not use `createAIGatewayClient`, `@macpaw/ai-sdk/client`, `runtime`, `types`, or `testing`; those surfaces do not exist.
12+
- For UI hooks or schema helpers, follow the versioned upstream docs for the installed `ai` / `@ai-sdk/react` major version; this package does not redefine those APIs.
1213

1314
## Guardrails
1415

1516
- Do not invent a token source. If server-side token retrieval is unclear, ask one concise question.
1617
- Do not put real gateway tokens in browser-only code.
1718
- Use `env: 'production'` only for the default MacPaw host. Use `baseURL` for staging or custom hosts.
18-
- `createGatewayFetch` requires a resolved `baseURL`; do not pass only `env`.
19+
- `createGatewayFetch` requires a resolved `baseURL`; do not pass only `env`. Prefer `resolveGatewayBaseURL()` when you want the default production host.
1920
- Remove old provider dependencies only after verifying there are no remaining usages.
2021

2122
## Preferred paths

0 commit comments

Comments
 (0)