@insforge/sdk 1.4.2 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,11 +33,11 @@ yarn add @insforge/sdk
33
33
  ### Initialize the Client
34
34
 
35
35
  ```javascript
36
- import { createClient } from "@insforge/sdk";
36
+ import { createClient } from '@insforge/sdk';
37
37
 
38
38
  const insforge = createClient({
39
- baseUrl: "http://localhost:7130", // Your InsForge backend URL
40
- anonKey: "your-anon-key", // Optional public anon key
39
+ baseUrl: 'http://localhost:7130', // Your InsForge backend URL
40
+ anonKey: 'your-anon-key', // Optional public anon key
41
41
  });
42
42
  ```
43
43
 
@@ -46,10 +46,10 @@ const insforge = createClient({
46
46
  Use `createAdminClient()` only in trusted server code that needs project-admin privileges:
47
47
 
48
48
  ```typescript
49
- import { createAdminClient } from "@insforge/sdk";
49
+ import { createAdminClient } from '@insforge/sdk';
50
50
 
51
51
  const admin = createAdminClient({
52
- baseUrl: "http://localhost:7130",
52
+ baseUrl: 'http://localhost:7130',
53
53
  apiKey: process.env.INSFORGE_API_KEY!,
54
54
  });
55
55
  ```
@@ -62,7 +62,7 @@ In edge functions or other server code that receives a user's JWT, seed the clie
62
62
 
63
63
  ```javascript
64
64
  const insforge = createClient({
65
- baseUrl: "http://localhost:7130",
65
+ baseUrl: 'http://localhost:7130',
66
66
  accessToken: userJwt, // e.g. from the request's Authorization header
67
67
  });
68
68
  ```
@@ -74,22 +74,22 @@ All requests run as that user (RLS applies). The token is used as-is — the SDK
74
74
  ```javascript
75
75
  // Sign up a new user
76
76
  const { data, error } = await insforge.auth.signUp({
77
- email: "user@example.com",
78
- password: "securePassword123",
79
- name: "John Doe", // optional
80
- redirectTo: "http://localhost:3000/sign-in", // optional, recommended for link-based verification
77
+ email: 'user@example.com',
78
+ password: 'securePassword123',
79
+ name: 'John Doe', // optional
80
+ redirectTo: 'http://localhost:3000/sign-in', // optional, recommended for link-based verification
81
81
  });
82
82
 
83
83
  // Sign in with email/password
84
84
  const { data, error } = await insforge.auth.signInWithPassword({
85
- email: "user@example.com",
86
- password: "securePassword123",
85
+ email: 'user@example.com',
86
+ password: 'securePassword123',
87
87
  });
88
88
 
89
89
  // OAuth authentication (built-in or custom provider key)
90
- await insforge.auth.signInWithOAuth("google", {
91
- redirectTo: "http://localhost:3000/dashboard",
92
- additionalParams: { prompt: "select_account" },
90
+ await insforge.auth.signInWithOAuth('google', {
91
+ redirectTo: 'http://localhost:3000/dashboard',
92
+ additionalParams: { prompt: 'select_account' },
93
93
  });
94
94
  // additionalParams is for provider-specific hints only.
95
95
  // Do not pass client_id, scope, redirect_uri, code_challenge, state, or response_type;
@@ -99,13 +99,13 @@ await insforge.auth.signInWithOAuth("google", {
99
99
  const { data: currentUser } = await insforge.auth.getCurrentUser();
100
100
 
101
101
  // Get any user's profile by ID (public endpoint)
102
- const { data: profile, error } = await insforge.auth.getProfile("user-id-here");
102
+ const { data: profile, error } = await insforge.auth.getProfile('user-id-here');
103
103
 
104
104
  // Update current user's profile (requires authentication)
105
105
  const { data: updatedProfile, error } = await insforge.auth.setProfile({
106
- displayName: "John Doe",
107
- bio: "Software developer",
108
- avatarUrl: "https://example.com/avatar.jpg",
106
+ displayName: 'John Doe',
107
+ bio: 'Software developer',
108
+ avatarUrl: 'https://example.com/avatar.jpg',
109
109
  });
110
110
 
111
111
  // Sign out
@@ -117,31 +117,31 @@ await insforge.auth.signOut();
117
117
  ```javascript
118
118
  // Resend a verification email
119
119
  await insforge.auth.resendVerificationEmail({
120
- email: "user@example.com",
121
- redirectTo: "http://localhost:3000/sign-in", // optional, recommended for link-based verification
120
+ email: 'user@example.com',
121
+ redirectTo: 'http://localhost:3000/sign-in', // optional, recommended for link-based verification
122
122
  });
123
123
 
124
124
  // Verify email with a 6-digit code
125
125
  await insforge.auth.verifyEmail({
126
- email: "user@example.com",
127
- otp: "123456",
126
+ email: 'user@example.com',
127
+ otp: '123456',
128
128
  });
129
129
 
130
130
  // Send password reset email
131
131
  await insforge.auth.sendResetPasswordEmail({
132
- email: "user@example.com",
133
- redirectTo: "http://localhost:3000/reset-password", // optional, recommended for link-based reset
132
+ email: 'user@example.com',
133
+ redirectTo: 'http://localhost:3000/reset-password', // optional, recommended for link-based reset
134
134
  });
135
135
 
136
136
  // Code-based reset flow: exchange the code, then reset the password
137
137
  const { data: resetToken } = await insforge.auth.exchangeResetPasswordToken({
138
- email: "user@example.com",
139
- code: "123456",
138
+ email: 'user@example.com',
139
+ code: '123456',
140
140
  });
141
141
 
142
142
  if (resetToken) {
143
143
  await insforge.auth.resetPassword({
144
- newPassword: "newSecurePassword123",
144
+ newPassword: 'newSecurePassword123',
145
145
  otp: resetToken.token,
146
146
  });
147
147
  }
@@ -163,55 +163,67 @@ Those backend endpoints validate the token first, then redirect the browser to y
163
163
  ```javascript
164
164
  // Insert data
165
165
  const { data, error } = await insforge.database
166
- .from("posts")
167
- .insert([{ title: "My First Post", content: "Hello World!" }]);
166
+ .from('posts')
167
+ .insert([{ title: 'My First Post', content: 'Hello World!' }]);
168
168
 
169
169
  // Query data
170
- const { data, error } = await insforge.database
171
- .from("posts")
172
- .select("*")
173
- .eq("author_id", userId);
170
+ const { data, error } = await insforge.database.from('posts').select('*').eq('author_id', userId);
174
171
 
175
172
  // Update data
176
173
  const { data, error } = await insforge.database
177
- .from("posts")
178
- .update({ title: "Updated Title" })
179
- .eq("id", postId);
174
+ .from('posts')
175
+ .update({ title: 'Updated Title' })
176
+ .eq('id', postId);
180
177
 
181
178
  // Delete data
182
- const { data, error } = await insforge.database
183
- .from("posts")
184
- .delete()
185
- .eq("id", postId);
179
+ const { data, error } = await insforge.database.from('posts').delete().eq('id', postId);
180
+ ```
181
+
182
+ #### Selecting a schema
183
+
184
+ Queries hit the `public` schema by default. Target a custom schema by chaining `.schema()` — the table name stays bare (it maps to PostgREST's `Accept-Profile`/`Content-Profile` header):
185
+
186
+ ```javascript
187
+ const { data } = await insforge.database.schema('analytics').from('events').select('*');
188
+
189
+ await insforge.database.schema('analytics').rpc('rollup', { day: '2026-01-01' });
190
+ ```
191
+
192
+ Or set a default schema for every query when creating the client:
193
+
194
+ ```javascript
195
+ const insforge = createClient({
196
+ baseUrl: '...',
197
+ anonKey: '...',
198
+ db: { schema: 'analytics' },
199
+ });
186
200
  ```
187
201
 
202
+ The schema must be exposed by the backend, and access is still gated by grants + RLS like `public`. Older backends that don't expose the schema return PostgREST `PGRST106` rather than silently falling back.
203
+
188
204
  ### File Storage
189
205
 
190
206
  ```javascript
191
207
  // Upload a file
192
208
  const file = document.querySelector('input[type="file"]').files[0];
193
- const { data, error } = await insforge.storage.from("avatars").upload(file);
209
+ const { data, error } = await insforge.storage.from('avatars').upload(file);
194
210
 
195
211
  // Download a file
196
- const { data, error } = await insforge.storage
197
- .from("avatars")
198
- .download("user-avatar.png");
212
+ const { data, error } = await insforge.storage.from('avatars').download('user-avatar.png');
199
213
 
200
214
  // Delete a file
201
- const { data, error } = await insforge.storage
202
- .from("avatars")
203
- .remove(["user-avatar.png"]);
215
+ const { data, error } = await insforge.storage.from('avatars').remove(['user-avatar.png']);
204
216
 
205
217
  // List files
206
- const { data, error } = await insforge.storage.from("avatars").list();
218
+ const { data, error } = await insforge.storage.from('avatars').list();
207
219
  ```
208
220
 
209
221
  ### Edge Functions
210
222
 
211
223
  ```javascript
212
224
  // Invoke an edge function
213
- const { data, error } = await insforge.functions.invoke("my-function", {
214
- body: { key: "value" },
225
+ const { data, error } = await insforge.functions.invoke('my-function', {
226
+ body: { key: 'value' },
215
227
  });
216
228
  ```
217
229
 
@@ -219,60 +231,58 @@ const { data, error } = await insforge.functions.invoke("my-function", {
219
231
 
220
232
  ```javascript
221
233
  // Create and redirect to a Stripe Checkout Session
222
- const { data, error } = await insforge.payments.stripe.createCheckoutSession(
223
- "test",
224
- {
225
- mode: "payment",
226
- lineItems: [{ priceId: "price_123", quantity: 1 }],
227
- successUrl: `${window.location.origin}/success`,
228
- cancelUrl: `${window.location.origin}/pricing`,
229
- idempotencyKey: "cart_123",
230
- },
231
- );
234
+ const { data, error } = await insforge.payments.stripe.createCheckoutSession('test', {
235
+ mode: 'payment',
236
+ lineItems: [{ priceId: 'price_123', quantity: 1 }],
237
+ successUrl: `${window.location.origin}/success`,
238
+ cancelUrl: `${window.location.origin}/pricing`,
239
+ idempotencyKey: 'cart_123',
240
+ });
232
241
 
233
242
  if (!error && data?.checkoutSession.url) {
234
243
  window.location.assign(data.checkoutSession.url);
235
244
  }
236
245
 
237
246
  // Create a subscription checkout for an app billing subject
238
- const { data: subscriptionCheckout } =
239
- await insforge.payments.stripe.createCheckoutSession("test", {
240
- mode: "subscription",
241
- subject: { type: "team", id: "team_123" },
242
- lineItems: [{ priceId: "price_monthly_123", quantity: 1 }],
247
+ const { data: subscriptionCheckout } = await insforge.payments.stripe.createCheckoutSession(
248
+ 'test',
249
+ {
250
+ mode: 'subscription',
251
+ subject: { type: 'team', id: 'team_123' },
252
+ lineItems: [{ priceId: 'price_monthly_123', quantity: 1 }],
243
253
  successUrl: `${window.location.origin}/billing/success`,
244
254
  cancelUrl: `${window.location.origin}/billing`,
245
- });
255
+ }
256
+ );
246
257
 
247
258
  if (subscriptionCheckout?.checkoutSession.url) {
248
259
  window.location.assign(subscriptionCheckout.checkoutSession.url);
249
260
  }
250
261
 
251
262
  // Let an authenticated customer manage their subscription in Stripe Billing Portal
252
- const { data: portal } =
253
- await insforge.payments.stripe.createCustomerPortalSession("test", {
254
- subject: { type: "team", id: "team_123" },
255
- returnUrl: `${window.location.origin}/billing`,
256
- });
263
+ const { data: portal } = await insforge.payments.stripe.createCustomerPortalSession('test', {
264
+ subject: { type: 'team', id: 'team_123' },
265
+ returnUrl: `${window.location.origin}/billing`,
266
+ });
257
267
 
258
268
  if (portal?.customerPortalSession.url) {
259
269
  window.location.assign(portal.customerPortalSession.url);
260
270
  }
261
271
 
262
272
  // Create a Razorpay order, then pass checkoutOptions to Razorpay Checkout.js
263
- const { data: order } = await insforge.payments.razorpay.createOrder("test", {
273
+ const { data: order } = await insforge.payments.razorpay.createOrder('test', {
264
274
  amount: 200000,
265
- currency: "INR",
266
- subject: { type: "team", id: "team_123" },
267
- customerEmail: "ada@example.com",
268
- notes: { order_id: "order_123" },
275
+ currency: 'INR',
276
+ subject: { type: 'team', id: 'team_123' },
277
+ customerEmail: 'ada@example.com',
278
+ notes: { order_id: 'order_123' },
269
279
  });
270
280
 
271
281
  if (order) {
272
282
  const checkout = new Razorpay({
273
283
  ...order.checkoutOptions,
274
284
  handler: async (response) => {
275
- await insforge.payments.razorpay.verifyOrder("test", {
285
+ await insforge.payments.razorpay.verifyOrder('test', {
276
286
  orderId: response.razorpay_order_id,
277
287
  paymentId: response.razorpay_payment_id,
278
288
  signature: response.razorpay_signature,
@@ -283,13 +293,12 @@ if (order) {
283
293
  }
284
294
 
285
295
  // Create a Razorpay subscription checkout for an app billing subject
286
- const { data: subscription } =
287
- await insforge.payments.razorpay.createSubscription("test", {
288
- planId: "plan_123",
289
- totalCount: 12,
290
- subject: { type: "team", id: "team_123" },
291
- notes: { order_id: "order_123" },
292
- });
296
+ const { data: subscription } = await insforge.payments.razorpay.createSubscription('test', {
297
+ planId: 'plan_123',
298
+ totalCount: 12,
299
+ subject: { type: 'team', id: 'team_123' },
300
+ notes: { order_id: 'order_123' },
301
+ });
293
302
  ```
294
303
 
295
304
  ### AI Integration
@@ -299,32 +308,32 @@ AI methods return an OpenAI-like response object directly (not the `{ data, erro
299
308
  ```javascript
300
309
  // Chat completion
301
310
  const completion = await insforge.ai.chat.completions.create({
302
- model: "anthropic/claude-3.5-haiku",
303
- messages: [{ role: "user", content: "Write a hello world program" }],
311
+ model: 'anthropic/claude-3.5-haiku',
312
+ messages: [{ role: 'user', content: 'Write a hello world program' }],
304
313
  });
305
314
  console.log(completion.choices[0].message.content);
306
315
 
307
316
  // Streaming chat — returns an async iterable of chunks
308
317
  const stream = await insforge.ai.chat.completions.create({
309
- model: "anthropic/claude-3.5-haiku",
310
- messages: [{ role: "user", content: "Tell me a story" }],
318
+ model: 'anthropic/claude-3.5-haiku',
319
+ messages: [{ role: 'user', content: 'Tell me a story' }],
311
320
  stream: true,
312
321
  });
313
322
  for await (const chunk of stream) {
314
- process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
323
+ process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
315
324
  }
316
325
 
317
326
  // Generate an image — returns base64-encoded image data
318
327
  const image = await insforge.ai.images.generate({
319
- model: "google/gemini-3-pro-image-preview",
320
- prompt: "A sunset over mountains",
328
+ model: 'google/gemini-3-pro-image-preview',
329
+ prompt: 'A sunset over mountains',
321
330
  });
322
331
  const base64Png = image.data[0].b64_json;
323
332
 
324
333
  // Create embeddings
325
334
  const embeddings = await insforge.ai.embeddings.create({
326
- model: "openai/text-embedding-3-small",
327
- input: "Hello world",
335
+ model: 'openai/text-embedding-3-small',
336
+ input: 'Hello world',
328
337
  });
329
338
  console.log(embeddings.data[0].embedding); // number[]
330
339
  ```
@@ -342,8 +351,8 @@ The SDK supports the following configuration options:
342
351
 
343
352
  ```javascript
344
353
  const insforge = createClient({
345
- baseUrl: "http://localhost:7130", // Your InsForge backend URL
346
- anonKey: "your-anon-key", // Optional
354
+ baseUrl: 'http://localhost:7130', // Your InsForge backend URL
355
+ anonKey: 'your-anon-key', // Optional
347
356
  });
348
357
  ```
349
358
 
@@ -361,7 +370,7 @@ By default, the SSR helpers use:
361
370
 
362
371
  ```typescript
363
372
  // app/lib/insforge/client.ts
364
- import { createBrowserClient } from "@insforge/sdk/ssr";
373
+ import { createBrowserClient } from '@insforge/sdk/ssr';
365
374
 
366
375
  export const insforge = createBrowserClient();
367
376
  ```
@@ -373,8 +382,8 @@ server so the app can write server-owned auth cookies.
373
382
 
374
383
  ```typescript
375
384
  // app/lib/insforge/server.ts
376
- import { cookies } from "next/headers";
377
- import { createServerClient } from "@insforge/sdk/ssr";
385
+ import { cookies } from 'next/headers';
386
+ import { createServerClient } from '@insforge/sdk/ssr';
378
387
 
379
388
  export async function createInsForgeServerClient() {
380
389
  return createServerClient({ cookies: await cookies() });
@@ -383,7 +392,7 @@ export async function createInsForgeServerClient() {
383
392
 
384
393
  ```typescript
385
394
  // app/api/auth/refresh/route.ts
386
- import { createRefreshAuthRouter } from "@insforge/sdk/ssr";
395
+ import { createRefreshAuthRouter } from '@insforge/sdk/ssr';
387
396
 
388
397
  export const { POST } = createRefreshAuthRouter();
389
398
  ```
@@ -395,17 +404,17 @@ so access and refresh tokens stay server-owned.
395
404
 
396
405
  ```typescript
397
406
  // app/actions.ts
398
- "use server";
407
+ 'use server';
399
408
 
400
- import { cookies } from "next/headers";
401
- import { createAuthActions } from "@insforge/sdk/ssr";
409
+ import { cookies } from 'next/headers';
410
+ import { createAuthActions } from '@insforge/sdk/ssr';
402
411
 
403
412
  export async function signIn(formData: FormData) {
404
413
  const auth = createAuthActions({ cookies: await cookies() });
405
414
 
406
415
  const { data, error } = await auth.signInWithPassword({
407
- email: String(formData.get("email")),
408
- password: String(formData.get("password")),
416
+ email: String(formData.get('email')),
417
+ password: String(formData.get('password')),
409
418
  });
410
419
 
411
420
  return { user: data?.user ?? null, error };
@@ -418,32 +427,29 @@ verifier in an httpOnly app cookie and exchange the callback code with
418
427
 
419
428
  ```typescript
420
429
  // app/actions.ts
421
- "use server";
430
+ 'use server';
422
431
 
423
- import { cookies } from "next/headers";
424
- import { redirect } from "next/navigation";
425
- import { createAuthActions } from "@insforge/sdk/ssr";
432
+ import { cookies } from 'next/headers';
433
+ import { redirect } from 'next/navigation';
434
+ import { createAuthActions } from '@insforge/sdk/ssr';
426
435
 
427
436
  export async function signInWithGoogle() {
428
437
  const cookieStore = await cookies();
429
438
  const auth = createAuthActions({ cookies: cookieStore });
430
- const { data, error } = await auth.signInWithOAuth("google", {
431
- redirectTo: new URL(
432
- "/api/auth/callback",
433
- process.env.NEXT_PUBLIC_APP_URL
434
- ).toString(),
439
+ const { data, error } = await auth.signInWithOAuth('google', {
440
+ redirectTo: new URL('/api/auth/callback', process.env.NEXT_PUBLIC_APP_URL).toString(),
435
441
  skipBrowserRedirect: true,
436
442
  });
437
443
 
438
444
  if (error || !data.url || !data.codeVerifier) {
439
- throw new Error(error?.message ?? "OAuth init failed");
445
+ throw new Error(error?.message ?? 'OAuth init failed');
440
446
  }
441
447
 
442
- cookieStore.set("insforge_code_verifier", data.codeVerifier, {
448
+ cookieStore.set('insforge_code_verifier', data.codeVerifier, {
443
449
  httpOnly: true,
444
- secure: process.env.NODE_ENV === "production",
445
- sameSite: "lax",
446
- path: "/",
450
+ secure: process.env.NODE_ENV === 'production',
451
+ sameSite: 'lax',
452
+ path: '/',
447
453
  maxAge: 600,
448
454
  });
449
455
 
@@ -453,28 +459,28 @@ export async function signInWithGoogle() {
453
459
 
454
460
  ```typescript
455
461
  // app/api/auth/callback/route.ts
456
- import { cookies } from "next/headers";
457
- import { NextResponse, type NextRequest } from "next/server";
458
- import { createAuthActions } from "@insforge/sdk/ssr";
462
+ import { cookies } from 'next/headers';
463
+ import { NextResponse, type NextRequest } from 'next/server';
464
+ import { createAuthActions } from '@insforge/sdk/ssr';
459
465
 
460
466
  export async function GET(request: NextRequest) {
461
- const code = request.nextUrl.searchParams.get("insforge_code");
462
- const verifier = (await cookies()).get("insforge_code_verifier")?.value;
467
+ const code = request.nextUrl.searchParams.get('insforge_code');
468
+ const verifier = (await cookies()).get('insforge_code_verifier')?.value;
463
469
  if (!code || !verifier) {
464
- return NextResponse.redirect(new URL("/login?error=oauth", request.url));
470
+ return NextResponse.redirect(new URL('/login?error=oauth', request.url));
465
471
  }
466
472
 
467
- const response = NextResponse.redirect(new URL("/dashboard", request.url));
473
+ const response = NextResponse.redirect(new URL('/dashboard', request.url));
468
474
  const auth = createAuthActions({
469
475
  requestCookies: request.cookies,
470
476
  responseCookies: response.cookies,
471
477
  });
472
478
  const { error } = await auth.exchangeOAuthCode(code, verifier);
473
479
  if (error) {
474
- return NextResponse.redirect(new URL("/login?error=oauth", request.url));
480
+ return NextResponse.redirect(new URL('/login?error=oauth', request.url));
475
481
  }
476
482
 
477
- response.cookies.delete("insforge_code_verifier");
483
+ response.cookies.delete('insforge_code_verifier');
478
484
  return response;
479
485
  }
480
486
  ```
@@ -488,8 +494,8 @@ response cookies for writing the next session:
488
494
 
489
495
  ```typescript
490
496
  // app/api/auth/sign-out/route.ts
491
- import { NextResponse, type NextRequest } from "next/server";
492
- import { createAuthActions } from "@insforge/sdk/ssr";
497
+ import { NextResponse, type NextRequest } from 'next/server';
498
+ import { createAuthActions } from '@insforge/sdk/ssr';
493
499
 
494
500
  export async function POST(request: NextRequest) {
495
501
  const response = NextResponse.json({ ok: true });
@@ -513,7 +519,7 @@ export async function POST(request: NextRequest) {
513
519
  If your refresh route needs custom side effects:
514
520
 
515
521
  ```typescript
516
- import { refreshAuth } from "@insforge/sdk/ssr";
522
+ import { refreshAuth } from '@insforge/sdk/ssr';
517
523
 
518
524
  export async function POST(request: Request) {
519
525
  const result = await refreshAuth({ request });
@@ -526,8 +532,8 @@ For Next.js Proxy/Middleware, refresh before Server Components render:
526
532
 
527
533
  ```typescript
528
534
  // proxy.ts on Next.js 16+, middleware.ts on Next.js 15 and earlier
529
- import { NextResponse, type NextRequest } from "next/server";
530
- import { updateSession } from "@insforge/sdk/ssr/middleware";
535
+ import { NextResponse, type NextRequest } from 'next/server';
536
+ import { updateSession } from '@insforge/sdk/ssr/middleware';
531
537
 
532
538
  export async function proxy(request: NextRequest) {
533
539
  const response = NextResponse.next({ request });
@@ -549,10 +555,10 @@ the session refresh helpers and avoids bundling the full SDK client.
549
555
  The SDK is written in TypeScript and provides full type definitions:
550
556
 
551
557
  ```typescript
552
- import { createClient, InsForgeClient } from "@insforge/sdk";
558
+ import { createClient, InsForgeClient } from '@insforge/sdk';
553
559
 
554
560
  const insforge: InsForgeClient = createClient({
555
- baseUrl: "http://localhost:7130",
561
+ baseUrl: 'http://localhost:7130',
556
562
  });
557
563
 
558
564
  // Type-safe API calls