@buildbase/sdk 0.0.88 → 0.0.90

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 (58) hide show
  1. package/AGENTS.md +7 -3
  2. package/CHANGELOG.md +515 -0
  3. package/README.md +120 -3
  4. package/dist/cli.mjs +1 -1
  5. package/dist/css/styles.css +1 -1
  6. package/dist/index.cjs +1 -1
  7. package/dist/index.d.ts +1351 -12
  8. package/dist/index.mjs +1 -1
  9. package/dist/mcp/index.cjs +1 -1
  10. package/dist/mcp/index.d.ts +120 -2
  11. package/dist/mcp/index.mjs +1 -1
  12. package/dist/react/chunks/SettingsDialog-BVyV5Zfm.mjs +2 -0
  13. package/dist/react/chunks/SettingsDialog-Ch8X6WXy.cjs +2 -0
  14. package/dist/react/chunks/{SubscriptionDialog-B8FTFRtp.cjs → SubscriptionDialog-CGIvMKpT.cjs} +1 -1
  15. package/dist/react/chunks/SubscriptionDialog-g0LwHhbe.mjs +2 -0
  16. package/dist/react/chunks/ar-3aqWucom.mjs +2 -0
  17. package/dist/react/chunks/ar-DQVB89r8.cjs +2 -0
  18. package/dist/react/chunks/de-DO10bhnU.cjs +2 -0
  19. package/dist/react/chunks/de-HXQk5D6k.mjs +2 -0
  20. package/dist/react/chunks/es-BZsmhBN3.mjs +2 -0
  21. package/dist/react/chunks/es-Cxg-qhUh.cjs +2 -0
  22. package/dist/react/chunks/fr-COCMy3vW.cjs +2 -0
  23. package/dist/react/chunks/fr-rdnNHzxy.mjs +2 -0
  24. package/dist/react/chunks/hi-BB0Z-Dgn.cjs +2 -0
  25. package/dist/react/chunks/hi-t3RH7ITu.mjs +2 -0
  26. package/dist/react/chunks/ja-CDyla2g0.mjs +2 -0
  27. package/dist/react/chunks/ja-D92JgUlk.cjs +2 -0
  28. package/dist/react/chunks/react-DTq7A_PT.mjs +2 -0
  29. package/dist/react/chunks/react-DeapSbq6.cjs +2 -0
  30. package/dist/react/chunks/zh-C5HABZZV.mjs +2 -0
  31. package/dist/react/chunks/zh-DK2yA5wB.cjs +2 -0
  32. package/dist/react/index.cjs +1 -1
  33. package/dist/react/index.d.ts +1517 -14
  34. package/dist/react/index.mjs +1 -1
  35. package/docs/ERROR_CODES.md +218 -0
  36. package/docs/THEMING.md +89 -21
  37. package/docs/UI-CONFIG.md +23 -4
  38. package/docs/USER-IMPORT.md +320 -0
  39. package/package.json +2 -1
  40. package/dist/react/chunks/SettingsDialog-CKVE1PpH.mjs +0 -2
  41. package/dist/react/chunks/SettingsDialog-DrDmxFEj.cjs +0 -2
  42. package/dist/react/chunks/SubscriptionDialog-UN7o9Hqb.mjs +0 -2
  43. package/dist/react/chunks/ar-B2l90OZJ.cjs +0 -2
  44. package/dist/react/chunks/ar-DVVNZhXD.mjs +0 -2
  45. package/dist/react/chunks/de-BspJtvAC.mjs +0 -2
  46. package/dist/react/chunks/de-dURUqbl0.cjs +0 -2
  47. package/dist/react/chunks/es-DArjREi5.cjs +0 -2
  48. package/dist/react/chunks/es-vu4BmI3g.mjs +0 -2
  49. package/dist/react/chunks/fr-DVm5WCvZ.mjs +0 -2
  50. package/dist/react/chunks/fr-DhYDaYrX.cjs +0 -2
  51. package/dist/react/chunks/hi-C_nv_c1T.cjs +0 -2
  52. package/dist/react/chunks/hi-VW8xCj_K.mjs +0 -2
  53. package/dist/react/chunks/ja-C-b88dvw.mjs +0 -2
  54. package/dist/react/chunks/ja-CqtDkLHr.cjs +0 -2
  55. package/dist/react/chunks/react-CHivaJhA.mjs +0 -2
  56. package/dist/react/chunks/react-Ch_Suxxy.cjs +0 -2
  57. package/dist/react/chunks/zh-D1yfhUf0.mjs +0 -2
  58. package/dist/react/chunks/zh-D_tMpsNt.cjs +0 -2
@@ -0,0 +1,320 @@
1
+ # Importing users: `buildbase import`
2
+
3
+ Move your users from Clerk, Auth0, Firebase or Supabase into BuildBase with
4
+ one command. It reads the vendor's own export, converts each user, and sends
5
+ them to your BuildBase app in batches of 500:
6
+
7
+ ```bash
8
+ BUILDBASE_SERVER_URL=https://api.console.buildbase.app \
9
+ BUILDBASE_API_TOKEN=$BUILDBASE_API_TOKEN \
10
+ npx buildbase import --from clerk users.csv --dry-run
11
+ ```
12
+
13
+ Run it with `--dry-run` first: it converts the whole file, prints the first
14
+ three users as they would be sent (hashes redacted) and every record it would
15
+ leave out, and sends nothing. Then run it again without the flag.
16
+
17
+ ## What carries over
18
+
19
+ | From the old system | In BuildBase |
20
+ | ------------------------------------------------------ | -------------------------------------------------------------------------- |
21
+ | Email address | The user's email. A record without one is not imported |
22
+ | Whether the address was verified | `emailVerified` |
23
+ | Name | The user's name |
24
+ | The user's id | `externalId` on the user, and on `useSaaSAuth().user` |
25
+ | Phone number, and whether it was verified | The user's phone (see [Phone numbers](#phone-numbers)) |
26
+ | Password hash, when we can check it | Their old password keeps working (see [Password hashes](#password-hashes)) |
27
+ | Google, GitHub, Microsoft, Apple and LinkedIn sign-ins | Linked accounts: signing in with the same provider finds the same user |
28
+
29
+ Not carried over: two-step sign-in secrets, metadata, and sign-ins with
30
+ providers BuildBase does not have (Facebook, X, Discord and others), each of
31
+ which is reported as a warning. Imported users get no welcome or verification
32
+ email. Each one fires the `user.imported` webhook, not `user.registered`, with
33
+ `source` set to the `--from` value, and counts toward your plan's end-user
34
+ limit; users past the limit come back as failed rows with `limit_reached`. A
35
+ user imported with a phone number also fires `user.phone_added`.
36
+
37
+ Microsoft, Apple and LinkedIn give each app (Apple: each team) its own account
38
+ ids, so an imported Microsoft, Apple or LinkedIn sign-in matches only while
39
+ your BuildBase app uses the same Microsoft app registration, Apple team or
40
+ LinkedIn app as the old system did. The server's report says so after the
41
+ totals when the export has any, one sentence per provider, and `--dry-run`
42
+ says the same sentences before anything is sent.
43
+
44
+ ## Options
45
+
46
+ | Option | What it does |
47
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------ |
48
+ | `--from <source>` | `clerk`, `auth0`, `firebase` or `supabase` (required) |
49
+ | `<file>` | The export: CSV, JSON or NDJSON, gzipped or not |
50
+ | `--passwords <file>` | Auth0: the password hash export from Auth0 support. Clerk: the dashboard CSV, beside API JSON |
51
+ | `--overwrite` | Update users that already exist instead of skipping them |
52
+ | `--dry-run` | Convert and print the first users; send nothing. Needs no token |
53
+ | `--server-url <url>` | Overrides `BUILDBASE_SERVER_URL`, as for `content pull` |
54
+ | `--signer-key <base64>` | Firebase only: `base64_signer_key`. Prefer `BUILDBASE_FIREBASE_SIGNER_KEY`, which stays out of shell history |
55
+ | `--salt-separator <base64>` | Firebase only: `base64_salt_separator` |
56
+ | `--rounds <n>` | Firebase only: `rounds` |
57
+ | `--mem-cost <n>` | Firebase only: `mem_cost` |
58
+
59
+ | Environment | What it is |
60
+ | ---------------------- | ---------------------------------------------------------------------------- |
61
+ | `BUILDBASE_API_TOKEN` | An org API token (console, Settings, API tokens) whose role can create users |
62
+ | `BUILDBASE_SERVER_URL` | Your BuildBase API, for example `https://api.console.buildbase.app` |
63
+
64
+ ## What it prints
65
+
66
+ ```
67
+ buildbase: read 1204 records from users.json (clerk): 1201 to import
68
+ buildbase: batch 1 of 3 done
69
+ buildbase: batch 2 of 3 done
70
+ buildbase: batch 3 of 3 done
71
+
72
+ buildbase: 1180 created, 0 updated, 15 skipped, 6 failed (1201 users sent, 3 not sent)
73
+ note: Microsoft user ids are per app registration: imported Microsoft links match only if ...
74
+
75
+ Not sent:
76
+ record 212: invalid_email: no email address, so this user is not imported
77
+ ...
78
+
79
+ Failed:
80
+ record 17 (ada@example.com): limit_reached: Your plan allows 1,000 end users.
81
+ ...
82
+
83
+ Warnings:
84
+ record 5 (bob@example.com): unsupported_hasher: Clerk's md5 hashes cannot be imported, ...
85
+ record 9 (cy@example.com): The google sign-in belongs to another user and was skipped.
86
+ ```
87
+
88
+ A record number is the user's place in your export, counting from 1: the row
89
+ after the header in a CSV, the line in NDJSON, the item in a JSON array.
90
+ `Not sent` lists the users the command left out before sending (no email, a
91
+ plaintext password, deleted upstream), the "not sent" of the totals, and
92
+ `--dry-run` lists the same users under the same heading. `Failed` lists the
93
+ users the server refused.
94
+ `Warnings` lists users that went in without something, whatever their status:
95
+ a password hash that cannot be checked, a sign-in with a provider BuildBase
96
+ does not have, a sign-in that already belongs to another user, a phone number
97
+ that is not E.164 or already belongs to another user, or a password
98
+ `--overwrite` did not apply. Skipped users (`exists`) were already there and
99
+ are not listed. Notes about the whole run (`note:`) are printed once, after
100
+ the totals.
101
+
102
+ The command exits 0 when every user went in or was skipped, 1 when any failed
103
+ or a request was refused, and 2 when it could not start (a bad flag, a file it
104
+ cannot read, no token).
105
+
106
+ **Running it again is safe.** Users that already exist are skipped, matched by
107
+ email and then by `externalId`, so a run that stopped half way (a network
108
+ error, a refused batch) is finished by running the same command again. With
109
+ `--overwrite` they are updated instead. Batches are never retried on their own.
110
+
111
+ ## Phone numbers
112
+
113
+ A user's phone number is imported as E.164 (`+14155550123`), with
114
+ `phoneVerified` true when the old system had proven it with a code. A verified
115
+ number counts as a way to sign in where your app offers phone sign-in, and can
116
+ receive text message codes for two-step sign-in. Only a value already in E.164
117
+ form is sent: any other (`(415) 555-0123`, a number without its country code)
118
+ is left out with an `invalid_phone` warning, and the user is imported without
119
+ it. One number belongs to one account, so a number another user already has is
120
+ skipped by the server with a warning, and that user is imported without it
121
+ too.
122
+
123
+ | Source | Where the number comes from | Verified when |
124
+ | -------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
125
+ | Clerk | API: the `phone_numbers[]` entry `primary_phone_number_id` names, or the first. CSV: `primary_phone_number`, or the first listed | API: `verification.status` is `verified`. CSV: it is in `verified_phone_numbers` |
126
+ | Auth0 | `phone_number` | `phone_verified` is true |
127
+ | Firebase | `phoneNumber` | Always: Firebase sets it through phone sign-in, which proves it |
128
+ | Supabase | `phone`, which Supabase keeps without its `+`; the import adds it back | `phone_confirmed_at` is set |
129
+
130
+ ## Password hashes
131
+
132
+ Imported users sign in with their old password. The first time they do,
133
+ BuildBase checks it against the imported hash and, when it matches, stores
134
+ its own bcrypt hash in its place, so the old hash is used once at most.
135
+
136
+ | Hasher | Comes from |
137
+ | ----------------------------- | ----------------------------------------------------------------------------------------------- |
138
+ | bcrypt | Clerk `bcrypt`, Auth0's hash export, Supabase `encrypted_password` |
139
+ | argon2id, argon2i | Clerk `argon2id` and `argon2i` (PHC strings, `$argon2id$v=19$m=...`) |
140
+ | pbkdf2_sha256 (Django's form) | Clerk `pbkdf2_sha256_django`, and Clerk `pbkdf2_sha256` whose base64 salt decodes to plain text |
141
+ | Firebase scrypt | Firebase's export with the project's parameters, and Clerk `scrypt_firebase` |
142
+
143
+ Any other hasher (Clerk's `md5`, `sha256`, `phpass`, `bcrypt_sha256_django`,
144
+ `scrypt_werkzeug` and the rest) imports the user without a password, with a
145
+ warning; they set one with "Forgot password?" or sign in another way. A value
146
+ in a hash column that does not have its hasher's shape is treated the same
147
+ way and never sent.
148
+
149
+ **A plaintext password is never sent.** A record that carries one (a
150
+ `password` field) is left out entirely and listed as failed with
151
+ `plaintext_password`; remove the column from the export and run again.
152
+
153
+ ## Clerk
154
+
155
+ Clerk splits what you need across two exports: the dashboard CSV has the
156
+ password hashes and no social sign-ins, and the Backend API has the social
157
+ sign-ins and no hashes. Use whichever you need, or both.
158
+
159
+ **Passwords only.** In the Clerk Dashboard, open your instance's
160
+ **Settings**, then **User exports**, choose **Export users**, and download the
161
+ CSV when it is ready. Then:
162
+
163
+ ```bash
164
+ npx buildbase import --from clerk clerk-export.csv
165
+ ```
166
+
167
+ It reads `id`, `first_name`, `last_name`, `username` (when there is no name),
168
+ `primary_email_address`, `verified_email_addresses` (the primary address is
169
+ verified when it appears there), `primary_phone_number`,
170
+ `verified_phone_numbers` and `unverified_phone_numbers` (with no primary
171
+ number, the first verified one, then the first unverified one),
172
+ `password_digest` and `password_hasher`.
173
+
174
+ **With social sign-ins.** Save every user from the Backend API as one JSON
175
+ array, then pass the dashboard CSV with `--passwords` to keep the hashes:
176
+
177
+ ```bash
178
+ for offset in 0 500 1000 1500; do # one page per 500 users
179
+ curl -s "https://api.clerk.com/v1/users?limit=500&offset=$offset" \
180
+ -H "Authorization: Bearer $CLERK_SECRET_KEY"
181
+ done | jq -s 'add' > clerk-users.json
182
+
183
+ npx buildbase import --from clerk clerk-users.json --passwords clerk-export.csv
184
+ ```
185
+
186
+ From the API it reads `id`, `first_name`, `last_name`,
187
+ `primary_email_address_id` and `email_addresses[]` (`email_address`, and
188
+ `verification.status` of `verified`), `primary_phone_number_id` and
189
+ `phone_numbers[]` (`phone_number`, and `verification.status` of `verified`),
190
+ and `external_accounts[]` (`provider` and `provider_user_id`). Hashes from the
191
+ CSV are matched by user id, then by email. Providers: `oauth_google`,
192
+ `oauth_github`, `oauth_microsoft`, `oauth_apple`, and `oauth_linkedin_oidc` or
193
+ `oauth_linkedin`.
194
+
195
+ Check against your export: Clerk does not document how
196
+ `verified_email_addresses` and `verified_phone_numbers` separate several values
197
+ (any address inside the cell is read, and numbers split on commas, semicolons
198
+ or pipes), or whether a dashboard CSV can carry external accounts. A
199
+ legacy `oauth_linkedin` account id may not be the id LinkedIn's OpenID sign-in
200
+ gives the same person, in which case that link will not match.
201
+
202
+ ## Auth0
203
+
204
+ **Users.** Auth0's bulk export does not include password hashes. Start a
205
+ users-exports job with the Management API (or the User Import / Export
206
+ extension), asking for the fields below as JSON, and download the file at the
207
+ job's `location` once it completes. It is gzipped; pass it as it is.
208
+
209
+ ```bash
210
+ curl -X POST "https://$AUTH0_DOMAIN/api/v2/jobs/users-exports" \
211
+ -H "Authorization: Bearer $AUTH0_MANAGEMENT_TOKEN" \
212
+ -H "Content-Type: application/json" \
213
+ -d '{"format":"json","fields":[{"name":"user_id"},{"name":"email"},{"name":"email_verified"},{"name":"name"},{"name":"given_name"},{"name":"family_name"},{"name":"phone_number"},{"name":"phone_verified"},{"name":"identities"}]}'
214
+ # then GET /api/v2/jobs/<id> until status is "completed", and download its location
215
+ ```
216
+
217
+ It reads `user_id`, `email`, `email_verified`, `name` (Auth0 copies the email
218
+ into `name` for a database user, which is not imported as a name),
219
+ `given_name`, `family_name`, `phone_number`, `phone_verified`, and
220
+ `identities[]` (`provider` and `user_id`).
221
+ Providers: `google-oauth2`, `github`, `windowslive` (Microsoft), `linkedin` and
222
+ `apple`. `auth0`, `email` and `sms` are ways to sign in rather than accounts to
223
+ link. Without `identities`, each user's main sign-in still comes from
224
+ `user_id` (`google-oauth2|1100...`).
225
+
226
+ **Password hashes.** Open a ticket with Auth0 support asking for a password
227
+ hash export of your database connections (Auth0 offers it on paid plans and
228
+ sends it PGP-encrypted; decrypt it first). It is NDJSON, one user per line,
229
+ with `_id` (a string or `{ "$oid": "..." }`), `email`, `passwordHash`
230
+ (bcrypt) and, for users from a custom database, `alt_id`. Pass it with
231
+ `--passwords`:
232
+
233
+ ```bash
234
+ npx buildbase import --from auth0 users.json.gz --passwords auth0-hashes.json
235
+ ```
236
+
237
+ A hash is matched to a user by `auth0|<alt_id>` or `auth0|<_id>` against the
238
+ bulk export's `user_id`, then by email. A database user with no hash in the
239
+ file imports without a password and is listed as a warning; without
240
+ `--passwords` at all, the command says how many database users that is.
241
+
242
+ Check against your export: whether `{ "name": "identities" }` exports the
243
+ whole array in your tenant (if not, a user's second and later linked sign-ins
244
+ are not carried over), and the exact field names in the hash file Auth0
245
+ support sends.
246
+
247
+ ## Firebase
248
+
249
+ 1. Export with the Firebase CLI, as JSON (the CSV leaves out Microsoft and
250
+ Apple sign-ins, so the import does not read it). Exporting hashes needs the
251
+ Editor or Owner role on the project.
252
+
253
+ ```bash
254
+ firebase auth:export users.json --format=JSON --project <project-id>
255
+ ```
256
+
257
+ 2. Copy the project's hash parameters: in the Firebase console, open
258
+ **Authentication**, then **Users**, the menu above the list of users, and
259
+ **Password hash parameters**. They read `base64_signer_key`,
260
+ `base64_salt_separator`, `rounds` and `mem_cost`. Treat the signer key as a
261
+ secret.
262
+
263
+ 3. Import, with the signer key in the environment:
264
+
265
+ ```bash
266
+ BUILDBASE_FIREBASE_SIGNER_KEY='<base64_signer_key>' \
267
+ npx buildbase import --from firebase users.json \
268
+ --salt-separator 'Bw==' --rounds 8 --mem-cost 14
269
+ ```
270
+
271
+ It reads `users[]`: `localId`, `email`, `emailVerified`, `displayName`,
272
+ `phoneNumber` (imported as verified, since Firebase sets it through phone
273
+ sign-in), `passwordHash` and `salt`, and `providerUserInfo[]` (`providerId`
274
+ and `rawId`). If your own code set numbers with the Admin SDK without a code,
275
+ those are imported as verified too; check before relying on them. Providers:
276
+ `google.com`, `github.com`, `microsoft.com` and `apple.com`; `password` and
277
+ `phone` are not links. An export that holds password hashes is refused without
278
+ all four parameters, rather than importing everyone without a password.
279
+
280
+ Firebase exports only hashes it made with its own scrypt. A user imported into
281
+ Firebase with another hasher, who has not signed in since, comes out with an
282
+ empty hash and imports without a password.
283
+
284
+ ## Supabase
285
+
286
+ Export `auth.users` with each user's sign-ins from `auth.identities`, as CSV
287
+ (the SQL editor's download, or `\copy` in psql) or JSON. For a large table use
288
+ psql, which has no row limit:
289
+
290
+ ```sql
291
+ select u.id, u.email, u.email_confirmed_at, u.phone, u.phone_confirmed_at,
292
+ u.encrypted_password, u.raw_user_meta_data, u.deleted_at,
293
+ coalesce((select json_agg(json_build_object(
294
+ 'provider', i.provider,
295
+ 'provider_id', i.provider_id,
296
+ 'identity_data', i.identity_data))
297
+ from auth.identities i where i.user_id = u.id), '[]'::json) as identities
298
+ from auth.users u
299
+ ```
300
+
301
+ ```bash
302
+ psql "$SUPABASE_DB_URL" -c "\copy (<the query above>) to 'supabase-users.csv' csv header"
303
+ npx buildbase import --from supabase supabase-users.csv
304
+ ```
305
+
306
+ For JSON, wrap the query as `select json_agg(t) from (<the query>) t` and save
307
+ the result; the SQL editor's one-cell answer is read too.
308
+
309
+ It reads `id`, `email`, `email_confirmed_at` (verified when set), `phone`
310
+ (stored without its `+`, which is added back) and `phone_confirmed_at`
311
+ (verified when set), `encrypted_password` (bcrypt),
312
+ `raw_user_meta_data.full_name` or `.name`, `deleted_at` (a deleted user is not
313
+ imported), and `identities[]` (`provider`, and `provider_id` or
314
+ `identity_data.sub`). Providers: `google`, `github`, `azure` (Microsoft),
315
+ `linkedin_oidc` or `linkedin`, and `apple`; `email` and `phone` are not links.
316
+ JSON columns may be text in a CSV; both are read.
317
+
318
+ Check against your export: `auth.identities.provider_id` exists on projects
319
+ created or upgraded since late 2023; on an older one, leave it out of the
320
+ query and the id comes from `identity_data.sub`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildbase/sdk",
3
- "version": "0.0.88",
3
+ "version": "0.0.90",
4
4
  "type": "module",
5
5
  "description": "A SDK for Buildbase",
6
6
  "main": "dist/index.cjs",
@@ -123,6 +123,7 @@
123
123
  "docs/MCP-AND-AGENT-READINESS.md",
124
124
  "docs/THEMING.md",
125
125
  "docs/UI-CONFIG.md",
126
+ "docs/USER-IMPORT.md",
126
127
  "docs/SERVER-CONTENT.md"
127
128
  ],
128
129
  "sideEffects": [