@palbase/web 1.0.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +292 -0
  3. package/dist/analytics-facade-DkOwkEpi.d.ts +454 -0
  4. package/dist/analytics-facade-t6UrFdn7.d.cts +454 -0
  5. package/dist/chunk-JVT65V4E.js +3384 -0
  6. package/dist/chunk-JVT65V4E.js.map +1 -0
  7. package/dist/chunk-VJXFABBW.js +94 -0
  8. package/dist/chunk-VJXFABBW.js.map +1 -0
  9. package/dist/errors-fDoNdTrJ.d.cts +35 -0
  10. package/dist/errors-fDoNdTrJ.d.ts +35 -0
  11. package/dist/index.cjs +2394 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +11 -0
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.js +27 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/internal.cjs +3403 -0
  18. package/dist/internal.cjs.map +1 -0
  19. package/dist/internal.d.cts +49 -0
  20. package/dist/internal.d.ts +49 -0
  21. package/dist/internal.js +19 -0
  22. package/dist/internal.js.map +1 -0
  23. package/dist/next/client.cjs +3131 -0
  24. package/dist/next/client.cjs.map +1 -0
  25. package/dist/next/client.d.cts +19 -0
  26. package/dist/next/client.d.ts +19 -0
  27. package/dist/next/client.js +75 -0
  28. package/dist/next/client.js.map +1 -0
  29. package/dist/next/index.cjs +3680 -0
  30. package/dist/next/index.cjs.map +1 -0
  31. package/dist/next/index.d.cts +238 -0
  32. package/dist/next/index.d.ts +238 -0
  33. package/dist/next/index.js +301 -0
  34. package/dist/next/index.js.map +1 -0
  35. package/dist/pb-BmgkAe97.d.ts +54 -0
  36. package/dist/pb-Cudze7Kb.d.cts +54 -0
  37. package/dist/react/index.cjs +649 -0
  38. package/dist/react/index.cjs.map +1 -0
  39. package/dist/react/index.d.cts +86 -0
  40. package/dist/react/index.d.ts +86 -0
  41. package/dist/react/index.js +156 -0
  42. package/dist/react/index.js.map +1 -0
  43. package/dist/storage-BPaeSG8K.d.cts +21 -0
  44. package/dist/storage-BPaeSG8K.d.ts +21 -0
  45. package/package.json +123 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Palbase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # @palbase/web
2
+
3
+ The Palbase client SDK for web. One import, one entry point: **`pb`**.
4
+
5
+ `@palbase/web` is typed against **your own backend**. You run `palbase web link` once, it
6
+ generates a `palbe.gen.ts` file from your deployed endpoints, and from then on
7
+ `pb.todos.create(...)`, `pb.rooms.list(...)` and friends are fully typed — request
8
+ shapes, response shapes and per-endpoint errors, all inferred from the backend you
9
+ shipped. Auth, feature flags, realtime and analytics come built in.
10
+
11
+ ```ts
12
+ import { pb } from '@palbase/web';
13
+
14
+ await pb.auth.signIn({ email, password });
15
+ const todo = await pb.todos.create({ title: 'Ship it' }); // typed by your backend
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm i @palbase/web
24
+ ```
25
+
26
+ Then link your project and generate the typed client:
27
+
28
+ ```bash
29
+ npx palbase web link
30
+ ```
31
+
32
+ `palbase web link` writes `palbe.gen.ts` (the typed client config + namespaces),
33
+ wires `import './palbe.gen';` into your app entry, and adds a `predev`/`prebuild`
34
+ hook that keeps the types in sync. **Commit `palbe.gen.ts`** — it's the typed
35
+ client your app imports, not a build artifact.
36
+
37
+ The generated file calls `__configure(...)` with your project's URL and anon API
38
+ key at module load, so importing it once at startup is all the setup there is.
39
+ If you forget to import it, every `pb` call throws a guided `BackendError`
40
+ (`kind: 'notConfigured'`).
41
+
42
+ > Regenerate any time your backend changes: `npx palbase types` (or just run
43
+ > `dev`/`build` — the hook does it for you).
44
+
45
+ ---
46
+
47
+ ## Calling your backend
48
+
49
+ Codegen turns each backend controller into a typed namespace on `pb`. A
50
+ controller method `todos.create` becomes `pb.todos.create(input)`; a top-level
51
+ endpoint `getHello` becomes `pb.getHello()`. Path params come first, the request
52
+ body/query second:
53
+
54
+ ```ts
55
+ import { pb } from '@palbase/web';
56
+
57
+ // POST /todos/create — body typed, response typed
58
+ const todo = await pb.todos.create({ title: 'Buy milk', done: false });
59
+
60
+ // GET /todos/{id} — path param, then options
61
+ const one = await pb.todos.get('todo_123');
62
+
63
+ // GET /todos — typed query object
64
+ const list = await pb.todos.list({ limit: 20, done: false });
65
+ ```
66
+
67
+ ### Escape hatches
68
+
69
+ When you need to call an endpoint that isn't in the generated types yet (or do a
70
+ raw multipart upload), use `pb.call` and `pb.upload`:
71
+
72
+ ```ts
73
+ // Untyped POST — you supply the response type
74
+ const result = await pb.call<{ ok: boolean }>('todos/archive', { id: 'todo_123' });
75
+
76
+ // Multipart upload with progress
77
+ const uploaded = await pb.upload<{ url: string }>('files/avatar', {
78
+ file: blob,
79
+ filename: 'avatar.png',
80
+ onProgress: ({ sent, total }) => console.log(sent / total),
81
+ });
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Auth
87
+
88
+ `pb.auth` is the full authentication surface — email/password, OTP, magic links,
89
+ OAuth, password reset, email verification:
90
+
91
+ ```ts
92
+ await pb.auth.signUp({ email: 'a@b.com', password: 'secret123' });
93
+ const { user, session } = await pb.auth.signIn({ email: 'a@b.com', password: 'secret123' });
94
+ await pb.auth.signOut();
95
+
96
+ // React to session changes
97
+ const unsub = pb.auth.onAuthStateChange((state) => {
98
+ console.log(state.status); // 'signedIn' | 'signedOut'
99
+ });
100
+
101
+ pb.auth.isSignedIn; // boolean (token presence)
102
+ pb.auth.currentUser; // AuthUser | null
103
+ await pb.auth.refreshUser(); // re-fetch the profile (e.g. emailVerified flip)
104
+ ```
105
+
106
+ Phone OTP, magic links and OAuth:
107
+
108
+ ```ts
109
+ await pb.auth.signInWithOTP({ phone: '+1555…' });
110
+ await pb.auth.verifyOTP({ phone: '+1555…', token: '123456' });
111
+
112
+ await pb.auth.signInWithMagicLink('a@b.com');
113
+
114
+ // Redirects the browser to the provider; completes via @palbase/web/next (see below)
115
+ await pb.auth.signInWithOAuth({ provider: 'google', redirectTo: '/auth/callback' });
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Feature flags
121
+
122
+ `pb.flags` polls and caches your project's feature flags (auth-aware — flags
123
+ re-evaluate when the user signs in/out):
124
+
125
+ ```ts
126
+ if (pb.flags.isEnabled('new-checkout')) { /* … */ }
127
+
128
+ pb.flags.getString('theme', 'light');
129
+ pb.flags.getInt('max-items', 10);
130
+
131
+ await pb.flags.getVariant('pricing-experiment'); // multivariate → variant name | null
132
+
133
+ const off = pb.flags.onChange(() => console.log('flags changed', pb.flags.all()));
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Realtime
139
+
140
+ `pb.realtime` multiplexes every subscription over one auto-reconnecting
141
+ WebSocket. Channels are client-only (browser):
142
+
143
+ ```ts
144
+ const room = pb.realtime.channel('room:42');
145
+
146
+ const sub = room.on('message', (payload) => {
147
+ console.log('got', payload);
148
+ });
149
+
150
+ room.send('message', { text: 'hello' }); // broadcast to other subscribers
151
+
152
+ sub.cancel(); // last cancel on a channel leaves it
153
+ ```
154
+
155
+ Connection status is observable (`pb.realtime.status.state` /
156
+ `pb.realtime.status.onChange(...)`).
157
+
158
+ ---
159
+
160
+ ## Analytics
161
+
162
+ `pb.analytics` buffers events client-side and flushes in batches. It manages an
163
+ anonymous distinct id and stitches it to the user on sign-in automatically:
164
+
165
+ ```ts
166
+ pb.analytics.capture('checkout_started', { plan: 'pro' });
167
+ pb.analytics.screen('Dashboard');
168
+ pb.analytics.identify('user_123', { email: 'a@b.com' });
169
+ await pb.analytics.flush(); // force-send buffered events
170
+ pb.analytics.setOptOut(true); // GDPR opt-out (drops pending + future)
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Errors
176
+
177
+ Every failing call throws a `BackendError`. Inspect `kind` (a coarse category)
178
+ and `code` (the server's machine code):
179
+
180
+ ```ts
181
+ import { pb, BackendError, isBackendError } from '@palbase/web';
182
+
183
+ try {
184
+ await pb.todos.create({ title: '' });
185
+ } catch (e) {
186
+ if (isBackendError(e)) {
187
+ e.kind; // 'validation' | 'unauthorized' | 'rateLimited' | 'notConfigured'
188
+ // | 'network' | 'server' | 'decode'
189
+ e.code; // server machine code, e.g. 'invalid_input'
190
+ e.status; // HTTP status
191
+ e.fields; // field-level validation errors (when kind === 'validation')
192
+ e.retryAfter; // seconds (when kind === 'rateLimited')
193
+ }
194
+ }
195
+ ```
196
+
197
+ Generated code also gives you **typed per-endpoint error classes** (e.g.
198
+ `RoomsCreateRoomLockedError`) with typed `.data`, so you can `instanceof`-match the
199
+ specific failures a given endpoint documents.
200
+
201
+ ---
202
+
203
+ ## Next.js — `@palbase/web/next`
204
+
205
+ The Next.js App Router adapter shares one session cookie between the browser and
206
+ the server so Server Components, Route Handlers and middleware all see the same
207
+ auth state.
208
+
209
+ **1. Configure the browser client** (a client provider/component):
210
+
211
+ ```tsx
212
+ 'use client';
213
+ import { setupPalbeNext } from '@palbase/web/next/client';
214
+ import { useEffect } from 'react';
215
+
216
+ export function PalbeProvider({ children }: { children: React.ReactNode }) {
217
+ useEffect(() => { setupPalbeNext(); }, []);
218
+ return <>{children}</>;
219
+ }
220
+ ```
221
+
222
+ `setupPalbeNext()` swaps in `cookieSessionStorage` so the session is written to a
223
+ cookie the server can read.
224
+
225
+ **2. Refresh the session in middleware** (required for session-bearing RSC apps):
226
+
227
+ ```ts
228
+ // middleware.ts
229
+ import './palbe.gen';
230
+ import { palbeMiddleware } from '@palbase/web/next';
231
+ import type { NextRequest } from 'next/server';
232
+
233
+ export function middleware(request: NextRequest) {
234
+ return palbeMiddleware(request);
235
+ }
236
+
237
+ export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
238
+ ```
239
+
240
+ **3. Read data in Server Components** with `pbServer()` — a per-request,
241
+ session-isolated client:
242
+
243
+ ```tsx
244
+ import { pbServer } from '@palbase/web/next';
245
+
246
+ export default async function Page() {
247
+ const pb = await pbServer();
248
+ const todos = await pb.todos.list({ limit: 20 });
249
+ return <TodoList items={todos} />;
250
+ }
251
+ ```
252
+
253
+ **4. Complete OAuth** with a callback route handler:
254
+
255
+ ```ts
256
+ // app/auth/callback/route.ts
257
+ import { handleAuthCallback } from '@palbase/web/next';
258
+ export const GET = handleAuthCallback({ defaultNext: '/' });
259
+ ```
260
+
261
+ ---
262
+
263
+ ## React — `@palbase/web/react`
264
+
265
+ Thin, concurrent-safe hooks over the observable `pb.*` facades. `react` is an
266
+ **optional** peer dependency — importing `@palbase/web` never pulls React; only
267
+ `@palbase/web/react` does.
268
+
269
+ ```tsx
270
+ 'use client';
271
+ import { useUser, useSession, useFlag, useFlags, useChannel } from '@palbase/web/react';
272
+
273
+ function Profile() {
274
+ const user = useUser(); // AuthUser | null, re-renders on auth change
275
+ const { signedIn } = useSession(); // { signedIn, user }
276
+ const dark = useFlag('dark-mode', false);
277
+ const flags = useFlags(); // whole flag set
278
+
279
+ // Subscribe to a realtime event for the component's lifetime
280
+ const { status } = useChannel('room:42', 'message', (payload) => {
281
+ console.log(payload);
282
+ });
283
+
284
+ return signedIn ? <span>{user?.email}</span> : <SignInButton />;
285
+ }
286
+ ```
287
+
288
+ ---
289
+
290
+ ## License
291
+
292
+ MIT