@basictech/react 0.12.0-beta.0 → 0.12.0-beta.2
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/CHANGELOG.md +603 -0
- package/README.md +125 -29
- package/dist/index.d.mts +120 -11
- package/dist/index.d.ts +120 -11
- package/dist/index.js +1168 -111
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1165 -110
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +557 -0
- package/dist/styles.css.map +1 -0
- package/dist/styles.d.mts +2 -0
- package/dist/styles.d.ts +2 -0
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -18,22 +18,24 @@ its package metadata.
|
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
This README describes the `0.12.0-beta.1` release candidate. After publication, install
|
|
22
|
+
matching versions of the React SDK and the schema package you import directly:
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
|
-
npm install @basictech/react @basictech/schema
|
|
25
|
+
npm install @basictech/react@0.12.0-beta.1 @basictech/schema@0.12.0-beta.1
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
React is a peer dependency and must already be installed by your application.
|
|
28
29
|
|
|
29
30
|
## Auth update guide
|
|
30
31
|
|
|
31
|
-
Upgrade the packages you use together
|
|
32
|
+
Upgrade the packages you use together. npm's `latest` channel remains on `0.11.0` and
|
|
33
|
+
does not contain all of the API changes described here:
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
|
-
npm install @basictech/react@
|
|
36
|
+
npm install @basictech/react@0.12.0-beta.1 @basictech/schema@0.12.0-beta.1
|
|
35
37
|
# If you import core directly, update that dependency too:
|
|
36
|
-
npm install @basictech/core@
|
|
38
|
+
npm install @basictech/core@0.12.0-beta.1
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
Keep the public packages on matching versions. No browser storage reset or persisted-profile
|
|
@@ -42,9 +44,8 @@ that an older installed beta contains the update.
|
|
|
42
44
|
|
|
43
45
|
### Update auth checks and editing controls
|
|
44
46
|
|
|
45
|
-
- Replace `useAuth().status === 'reauth_required'` with `status === 'expired'`.
|
|
46
|
-
|
|
47
|
-
retains the legacy compatibility state.
|
|
47
|
+
- Replace `useAuth().status === 'reauth_required'` with `status === 'expired'`. The
|
|
48
|
+
`reauth_required` state no longer exists anywhere, including low-level `AuthSession`.
|
|
48
49
|
- Use `auth.canWrite` to disable mutation controls and `auth.readOnlyReason` for explanation.
|
|
49
50
|
It is account eligibility, not a guarantee of connectivity or permission to a particular source.
|
|
50
51
|
Catch imperative mutation errors too: auth can expire after the UI renders. Newly attempted
|
|
@@ -72,8 +73,10 @@ function AuthNotice() {
|
|
|
72
73
|
|
|
73
74
|
### Render per-account status
|
|
74
75
|
|
|
75
|
-
`useAccounts().accounts`
|
|
76
|
-
|
|
76
|
+
`useAccounts().accounts` returns `BasicAccount[]`, including anonymous and expired accounts, and
|
|
77
|
+
`useAccounts().activeAccount` is this tab's selection. The former `profiles`/`users`/`activeUser`
|
|
78
|
+
snapshot aliases and the `active` hook field are gone. Each account adds
|
|
79
|
+
`auth: { status, reason, checkedAt }` to the existing profile fields:
|
|
77
80
|
|
|
78
81
|
| Account status | Meaning |
|
|
79
82
|
| --- | --- |
|
|
@@ -90,6 +93,11 @@ is expired; otherwise an unverified remembered account is checking. The active a
|
|
|
90
93
|
has `auth.status: 'anon'`, while top-level `useAuth()` reports `status: 'signed_out'` and
|
|
91
94
|
`isAnonymous: true`. REST exposes one remembered account, not a multi-account registry.
|
|
92
95
|
|
|
96
|
+
`account.auth.reason` is exactly `'AUTH_EXPIRED' | null`. Raw active-account diagnostics such as
|
|
97
|
+
`invalid_grant` remain available as `useAuth().error`. Sign-in prerequisite errors returned by
|
|
98
|
+
server-backed hooks are stable within each hook but intentionally distinct objects from that raw
|
|
99
|
+
diagnostic; an expired hook can report `AUTH_EXPIRED` while the auth diagnostic is `invalid_grant`.
|
|
100
|
+
|
|
93
101
|
Ordinary network loss is not expiry, and anonymous editing still works. `goOnline()` cannot
|
|
94
102
|
bypass expired read-only enforcement. Reauthorization resumes retained queued work; already
|
|
95
103
|
accepted server requests cannot be undone by client cancellation. REST has no offline cache.
|
|
@@ -278,7 +286,7 @@ The package also exports the adapters for explicit composition: `BrowserKeyValue
|
|
|
278
286
|
`browserStorage`, `BrowserTokenStore`, `PersistenceStore`, `browserUploadTransport`,
|
|
279
287
|
`createBrowserMessageChannel`, `createBrowserAuthChannelFactory`, `browserNavigate`,
|
|
280
288
|
`browserCurrentUrl`, and `browserReplaceUrl`. Most applications only need `createBasic`, which
|
|
281
|
-
installs them.
|
|
289
|
+
installs them. `supportsAccessTokenAdoption` is internal and is not a public React export.
|
|
282
290
|
|
|
283
291
|
### Token storage
|
|
284
292
|
|
|
@@ -320,6 +328,93 @@ With the default `renderWhileLoading={false}`, hooks under the Provider first re
|
|
|
320
328
|
client is ready. Setting it to `true` is useful for an application shell that reads `isReady` and
|
|
321
329
|
owns its loading UI.
|
|
322
330
|
|
|
331
|
+
## Prebuilt components
|
|
332
|
+
|
|
333
|
+
Drop-in account and sharing UI uses Base UI's accessible menu, avatar, and dialog primitives.
|
|
334
|
+
Components use the nearest `BasicProvider` (including `basic.Provider`); they do not create a
|
|
335
|
+
second auth client. Import the optional stylesheet once. Omit it to supply your own styles.
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
import {
|
|
339
|
+
BasicUIProvider, SignInButton, UserButton, AuthStatus, SyncStatus, SharesModal,
|
|
340
|
+
} from '@basictech/react'
|
|
341
|
+
import '@basictech/react/styles.css'
|
|
342
|
+
import { basic } from './basic'
|
|
343
|
+
|
|
344
|
+
export function AccountTools() {
|
|
345
|
+
return (
|
|
346
|
+
<basic.Provider>
|
|
347
|
+
<BasicUIProvider appearance={{ theme: 'light', accent: '#354f45', radius: '8px' }}>
|
|
348
|
+
<SignInButton />
|
|
349
|
+
<UserButton allowAddAccount />
|
|
350
|
+
<AuthStatus />
|
|
351
|
+
<SyncStatus />
|
|
352
|
+
<SharesModal scope={[{ table: 'todos', recordIds: ['todo-1'] }]} />
|
|
353
|
+
</BasicUIProvider>
|
|
354
|
+
</basic.Provider>
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
| Component | Options and behavior |
|
|
360
|
+
| --- | --- |
|
|
361
|
+
| `BasicButton` | Native button props/ref; `variant="solid\|outline\|ghost"`. Defaults to `type="button"`. |
|
|
362
|
+
| `SignInButton`, `SignOutButton` | Native button props and variant, pending/error feedback. Sign-in accepts `input` (handle/DID); `onClick.preventDefault()` cancels the action. Expired sessions can reauthorize or sign out. |
|
|
363
|
+
| `UserAvatar` | Borderless avatar trigger for the shared user menu. Current account or explicit `profile` (`null` means guest); `size`, `className`, `style`; image failure falls back to initials. Sync badge defaults on in sync mode; `showSyncBadge={false}` hides it. REST mode never shows a sync badge. |
|
|
364
|
+
| `UserButton` | Avatar, name, handle, and dropdown icon with a bordered hoverable trigger. `shape="rounded\|square"` defaults to rounded. Opens the shared user menu. Local accounts never show a handle. |
|
|
365
|
+
| `UserMenu` | Shared menu used by both triggers; `trigger="avatar\|button"` defaults to avatar. Current account at top, Manage account, other saved accounts, and auth actions. The switch section appears only when other accounts exist. Only expired auth gets an `Expired` warning; no signed-in badge. `showSyncStatus={false}` hides the header's sync label; REST mode always hides sync badges. `allowAddAccount` defaults to true, starting sign-in in a new profile in REST or sync mode. Local accounts offer Clear account with a destructive-action confirmation. Optional `accountSettingsUrl`. |
|
|
366
|
+
| `AuthStatus` | Loading, signed-in, guest, signed-out, recovering, and expired session labels. UI visibility is not authorization. |
|
|
367
|
+
| `SyncStatus` | Connectivity, pending counts, and conflict/rejection count; optional `source` for a repo or mount. An online queue is not labelled synced. |
|
|
368
|
+
| `AccountSettingsModal` | Profile tab edits the project display name with Cancel/Save changes; drafts survive tab switches. Advanced shows read-only DID, PDS URL, local account/storage identifiers, auth/write state, and sync counts. No tokens or credentials are rendered. Local accounts show explicit DID/PDS fallbacks. `projectProfile` supplies a complete preview hook result. Optional `accountSettingsUrl` links to universal identity settings; `children` adds host-owned Profile content. |
|
|
369
|
+
| `SharesModal` | Same identity-header layout with Outgoing/Incoming tabs. Required explicit `scope`, optional `repo` (default: `'default'`). Viewer/editor invitations by handle/DID, outgoing list, refresh, confirmed cancellation/revocation. Incoming acceptance and mount management remain in Basic ID. |
|
|
370
|
+
|
|
371
|
+
Both modals accept `open`/`onOpenChange`, an optional button element or label as `trigger`, and
|
|
372
|
+
`finalFocus` for externally controlled use. `trigger={null}` omits the trigger. Custom trigger
|
|
373
|
+
components must forward their ref and DOM props. Dialogs trap focus and close with Escape.
|
|
374
|
+
|
|
375
|
+
`BasicUIProvider` accepts `appearance: { theme, accent, radius, density }`; density is
|
|
376
|
+
`'comfortable'` or `'compact'`. Nested providers inherit appearance; portaled menus/dialogs retain
|
|
377
|
+
it. Choose an accent with sufficient contrast against white for solid buttons. Fonts inherit from
|
|
378
|
+
the host. Override the prefixed `basic-*` classes and `--basic-*` CSS tokens for finer styling.
|
|
379
|
+
|
|
380
|
+
For controlled previews, components accept complete hook results via `auth`, `accounts`, `sync`,
|
|
381
|
+
or `shares` as appropriate. These replace rendered state **and action callbacks**; a BasicProvider
|
|
382
|
+
is still required and hooks still subscribe/read. Do not use these props as an authorization
|
|
383
|
+
boundary. Real mutations remain subject to SDK and server permissions.
|
|
384
|
+
|
|
385
|
+
Share creation requires an explicit nonempty table/record scope and defaults to viewer access.
|
|
386
|
+
The list is labelled **all permissions**, not filtered to the new-invitation scope. Revocation cannot
|
|
387
|
+
retract downloaded data. Signing out removes the account's local cache and pending edits.
|
|
388
|
+
The incoming-invitations section links to Basic ID: inbox/accept/decline APIs require owner
|
|
389
|
+
credentials, not app tokens. Server project-profile GET/PATCH/DELETE APIs exist at
|
|
390
|
+
`/account/:project_id/profile` with `app:profile:read/update/delete` grants respectively.
|
|
391
|
+
Configure those requested OAuth scopes and reauthorize existing sessions when needed.
|
|
392
|
+
The shared menu shows sync status and any expiry warning with the current account in its header.
|
|
393
|
+
Inactive accounts show saved expiry state, not the active account's sync status.
|
|
394
|
+
Both `UserButton` and `UserAvatar` accept `showSyncBadge` (default follows SDK sync mode); circles use
|
|
395
|
+
green for synced, blue for connecting/pending, orange for errors, gray for offline/local/idle.
|
|
396
|
+
The demo's `?page=components` gallery offers live behavior, isolated simulated actions, design
|
|
397
|
+
controls, and a copyable usage recipe. Live OAuth/shares require a configured signed-in account.
|
|
398
|
+
|
|
399
|
+
### Project profile API
|
|
400
|
+
|
|
401
|
+
`basic.useProjectProfile()` (also exported as `useProjectProfile`) returns `data`, `isLoading`,
|
|
402
|
+
`error`, `refresh()`, `update(patch)`, and `reset()`. Pass `false` to disable automatic reads.
|
|
403
|
+
`data` is `{ profile: { name?, avatar_url?, status? }, meta? }` or null before loading/sign-in.
|
|
404
|
+
The core client exposes the same operations directly:
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
await basic.client.getProjectProfile()
|
|
408
|
+
await basic.client.updateProjectProfile({ name: 'Name in this app', status: null })
|
|
409
|
+
await basic.client.resetProjectProfile()
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Requests always target the configured client ID using the active app token. Omitted fields stay
|
|
413
|
+
unchanged; null removes an override. Metadata objects merge on the server; `meta: null` clears
|
|
414
|
+
metadata. Reset removes all project overrides and metadata. Universal profile data is untouched.
|
|
415
|
+
Permission failures propagate as SDK errors. Profile responses are not written into the local
|
|
416
|
+
account registry; consumers needing the updated project identity should use this hook's data.
|
|
417
|
+
|
|
323
418
|
## Hook reference
|
|
324
419
|
|
|
325
420
|
The schema-bound factory returns every hook below. Bound hooks are preferred because they reject
|
|
@@ -352,8 +447,8 @@ unknown table names and infer fields in reads, writes, and queries.
|
|
|
352
447
|
| `isAnonymous` | `boolean` | The active sync profile is anonymous and not signed in |
|
|
353
448
|
| `status` | `'bootstrapping' \| 'signed_out' \| 'authenticated' \| 'recovering' \| 'expired'` | Current public auth lifecycle |
|
|
354
449
|
| `canWrite` | `boolean` | Active-account write eligibility; source permissions still apply |
|
|
355
|
-
| `readOnlyReason` | `
|
|
356
|
-
| `error` | `BasicError \| null` |
|
|
450
|
+
| `readOnlyReason` | `ReadOnlyReason \| null` | `'AUTH_EXPIRED' \| 'AUTH_CHECKING' \| 'AUTHORIZATION_REQUIRED' \| 'CLIENT_NOT_STARTED'`; switch exhaustively for banners |
|
|
451
|
+
| `error` | `BasicError \| null` | Raw active auth diagnostic; same object while its code and expired state are unchanged |
|
|
357
452
|
| `user` | `AuthUser \| null` | OIDC user info (`sub`, `pds_url`, and optional `email`, `name`, `picture`, `handle`) |
|
|
358
453
|
| `did` | `string \| null` | Signed-in account DID |
|
|
359
454
|
| `handle` | `string \| null` | Resolved account handle |
|
|
@@ -399,31 +494,33 @@ replicas and returns to another local profile or a fresh anonymous one when enab
|
|
|
399
494
|
|
|
400
495
|
### `basic.useAccounts()`
|
|
401
496
|
|
|
402
|
-
`useAccounts()` returns `{ accounts,
|
|
497
|
+
`useAccounts()` returns `{ accounts, activeAccount, switchAccount, addAccount, removeAccount }`:
|
|
403
498
|
|
|
404
499
|
- `accounts: BasicAccount[]` lists local profiles with per-account auth summaries.
|
|
405
|
-
- `
|
|
500
|
+
- `activeAccount: BasicAccount | null` is this tab's selected profile and auth summary.
|
|
406
501
|
- `switchAccount(id): Promise<void>` changes the active profile in this tab.
|
|
407
|
-
- `addAccount(): Promise<BasicProfile>` creates and activates a new
|
|
408
|
-
|
|
502
|
+
- `addAccount({ signIn?: boolean }): Promise<BasicProfile>` creates and activates a new profile.
|
|
503
|
+
Omit options to stay local; pass `{ signIn: true }` to validate redirect configuration first and
|
|
504
|
+
start sign-in in the new profile. The prebuilt menu uses this sign-in option.
|
|
409
505
|
- `removeAccount(id): Promise<void>` removes that profile and its local credentials/replicas. If it
|
|
410
506
|
is active, this follows the sign-out path. It does not delete the server account.
|
|
411
507
|
|
|
412
508
|
`BasicProfile` contains `id`, `kind: 'anon' | 'account'`, `storagePrefix`, `createdAt`,
|
|
413
|
-
`lastActiveAt`, and optional/null `did`, `handle`, `email`, `name`, and `picture`.
|
|
414
|
-
`addAccount(
|
|
509
|
+
`lastActiveAt`, and optional/null `did`, `handle`, `email`, `name`, and `picture`. Saved accounts and
|
|
510
|
+
switching work in REST and sync modes. `addAccount({ signIn: true })` works in either mode;
|
|
511
|
+
`addAccount()` without sign-in still requires sync mode with anonymous profiles enabled.
|
|
415
512
|
|
|
416
513
|
```tsx
|
|
417
514
|
// file: AccountSwitcher.tsx
|
|
418
515
|
import { basic } from './basic'
|
|
419
516
|
|
|
420
517
|
export function AccountSwitcher() {
|
|
421
|
-
const { accounts,
|
|
518
|
+
const { accounts, activeAccount, switchAccount, addAccount, removeAccount } = basic.useAccounts()
|
|
422
519
|
|
|
423
520
|
return (
|
|
424
521
|
<section>
|
|
425
522
|
<select
|
|
426
|
-
value={
|
|
523
|
+
value={activeAccount?.id ?? ''}
|
|
427
524
|
onChange={(event) => void switchAccount(event.currentTarget.value)}
|
|
428
525
|
>
|
|
429
526
|
{accounts.map((account) => (
|
|
@@ -433,7 +530,7 @@ export function AccountSwitcher() {
|
|
|
433
530
|
))}
|
|
434
531
|
</select>
|
|
435
532
|
<button onClick={() => void addAccount()}>Add account</button>
|
|
436
|
-
{
|
|
533
|
+
{activeAccount && <button onClick={() => void removeAccount(activeAccount.id)}>Remove local profile</button>}
|
|
437
534
|
</section>
|
|
438
535
|
)
|
|
439
536
|
}
|
|
@@ -821,9 +918,8 @@ Shares v2 is capability-gated and requires sign-in. `useOutgoingShares()` return
|
|
|
821
918
|
```text
|
|
822
919
|
{
|
|
823
920
|
data: OutgoingShareInfo[]
|
|
824
|
-
outgoingShares: OutgoingShareInfo[] // alias of data
|
|
825
921
|
isLoading: boolean
|
|
826
|
-
error: BasicError | null
|
|
922
|
+
error: BasicError | null // load failure, or AUTHORIZATION_REQUIRED / AUTH_EXPIRED while not signed in
|
|
827
923
|
refresh(): void
|
|
828
924
|
create(input: CreateOutgoingShareInput): Promise<OutgoingShareInfo>
|
|
829
925
|
get(id: string): Promise<OutgoingShareInfo>
|
|
@@ -869,7 +965,7 @@ export function OutgoingShares() {
|
|
|
869
965
|
<button onClick={() => location.assign(shares.manageUrl())}>Manage in Basic ID</button>
|
|
870
966
|
{shares.error && <p>{shares.error.code}</p>}
|
|
871
967
|
<ul>
|
|
872
|
-
{shares.
|
|
968
|
+
{shares.data.map((share) => (
|
|
873
969
|
<li key={share.id}>
|
|
874
970
|
{share.recipientDid}: {share.effectiveState}
|
|
875
971
|
{share.state === 'pending' && (
|
|
@@ -907,9 +1003,8 @@ management belong there. `client.shares.leave()` is intentionally owner-only and
|
|
|
907
1003
|
```text
|
|
908
1004
|
{
|
|
909
1005
|
data: MountInfo[]
|
|
910
|
-
mounts: MountInfo[] // alias of data
|
|
911
1006
|
isLoading: boolean
|
|
912
|
-
error: BasicError | null
|
|
1007
|
+
error: BasicError | null // load failure, or AUTHORIZATION_REQUIRED / AUTH_EXPIRED while not signed in
|
|
913
1008
|
refresh(): void
|
|
914
1009
|
open(mountId: string): Promise<MountHandle<S>>
|
|
915
1010
|
manageUrl(): string
|
|
@@ -955,7 +1050,7 @@ export function IncomingMounts() {
|
|
|
955
1050
|
<section>
|
|
956
1051
|
<button onClick={() => location.assign(mounts.manageUrl())}>Manage mounts</button>
|
|
957
1052
|
<ul>
|
|
958
|
-
{mounts.
|
|
1053
|
+
{mounts.data.map((mount) => (
|
|
959
1054
|
<li key={mount.id}>
|
|
960
1055
|
{mount.role} from {mount.originOwnerDid}
|
|
961
1056
|
<button onClick={() => void open(mount.id)}>Open</button>
|
|
@@ -1090,7 +1185,8 @@ The unbound exports mirror the bound hooks:
|
|
|
1090
1185
|
| `useMounts()`, `useOutgoingShares()` | Same share results |
|
|
1091
1186
|
|
|
1092
1187
|
Every unbound hook must run below `BasicProvider`; otherwise it throws `Basic hooks must be used
|
|
1093
|
-
within a <BasicProvider>`.
|
|
1188
|
+
within a <BasicProvider>`. Bound hooks likewise throw outside their own `basic.Provider`, because
|
|
1189
|
+
the Provider is what starts and stops the client.
|
|
1094
1190
|
|
|
1095
1191
|
## Error handling
|
|
1096
1192
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ReactElement } from 'react';
|
|
3
|
-
import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, AuthStatus, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
|
|
2
|
+
import { ReactNode, ReactElement, RefObject, ButtonHTMLAttributes, HTMLAttributes } from 'react';
|
|
3
|
+
import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, ReadOnlyReason, AuthStatus as AuthStatus$1, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, ProjectProfile, ProjectProfilePatch, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthChannelContext, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
|
|
4
4
|
|
|
5
5
|
type BrowserBasicConfig<S extends BasicSchema = BasicSchema> = BasicConfig<S>;
|
|
6
6
|
/** Create a core client with the complete browser adapter set installed. */
|
|
@@ -8,9 +8,11 @@ declare function createBasicClient<S extends BasicSchema>(config: BrowserBasicCo
|
|
|
8
8
|
|
|
9
9
|
interface UseAccountsResult {
|
|
10
10
|
accounts: BasicAccount[];
|
|
11
|
-
|
|
11
|
+
activeAccount: BasicAccount | null;
|
|
12
12
|
switchAccount(id: string): Promise<void>;
|
|
13
|
-
addAccount(
|
|
13
|
+
addAccount(options?: {
|
|
14
|
+
signIn?: boolean;
|
|
15
|
+
}): Promise<BasicProfile>;
|
|
14
16
|
removeAccount(id: string): Promise<void>;
|
|
15
17
|
}
|
|
16
18
|
declare function useAccounts(): UseAccountsResult;
|
|
@@ -20,8 +22,9 @@ interface UseAuthResult {
|
|
|
20
22
|
isSignedIn: boolean;
|
|
21
23
|
isAnonymous: boolean;
|
|
22
24
|
canWrite: boolean;
|
|
23
|
-
readOnlyReason:
|
|
24
|
-
status: AuthStatus;
|
|
25
|
+
readOnlyReason: ReadOnlyReason | null;
|
|
26
|
+
status: AuthStatus$1;
|
|
27
|
+
/** Raw auth diagnostic; stable identity while its code and expired state are unchanged. */
|
|
25
28
|
error: BasicError | null;
|
|
26
29
|
user: AuthUser | null;
|
|
27
30
|
did: string | null;
|
|
@@ -64,6 +67,7 @@ declare function useFiles(query?: FileListQuery, options?: {
|
|
|
64
67
|
interface UseStorageInfoResult {
|
|
65
68
|
data: StorageInfo | null;
|
|
66
69
|
isLoading: boolean;
|
|
70
|
+
/** Load failure, or the sign-in requirement while no account is signed in. */
|
|
67
71
|
error: BasicError | null;
|
|
68
72
|
refresh(): void;
|
|
69
73
|
}
|
|
@@ -71,8 +75,8 @@ declare function useStorageInfo(): UseStorageInfoResult;
|
|
|
71
75
|
|
|
72
76
|
interface UseMountsResult<S extends BasicSchema = BasicSchema> {
|
|
73
77
|
data: MountInfo[];
|
|
74
|
-
mounts: MountInfo[];
|
|
75
78
|
isLoading: boolean;
|
|
79
|
+
/** Load failure, or the sign-in requirement while no account is signed in. */
|
|
76
80
|
error: BasicError | null;
|
|
77
81
|
refresh(): void;
|
|
78
82
|
open(mountId: string): Promise<MountHandle<S>>;
|
|
@@ -81,8 +85,8 @@ interface UseMountsResult<S extends BasicSchema = BasicSchema> {
|
|
|
81
85
|
declare function useMounts(query?: MountsQuery): UseMountsResult;
|
|
82
86
|
interface UseOutgoingSharesResult {
|
|
83
87
|
data: OutgoingShareInfo[];
|
|
84
|
-
outgoingShares: OutgoingShareInfo[];
|
|
85
88
|
isLoading: boolean;
|
|
89
|
+
/** Load failure, or the sign-in requirement while no account is signed in. */
|
|
86
90
|
error: BasicError | null;
|
|
87
91
|
refresh(): void;
|
|
88
92
|
create(input: CreateOutgoingShareInput): Promise<OutgoingShareInfo>;
|
|
@@ -116,6 +120,19 @@ interface UseReposResult {
|
|
|
116
120
|
}
|
|
117
121
|
declare function useRepos(): UseReposResult;
|
|
118
122
|
|
|
123
|
+
interface AsyncResult<T> {
|
|
124
|
+
data: T;
|
|
125
|
+
isLoading: boolean;
|
|
126
|
+
error: BasicError | null;
|
|
127
|
+
refresh(): void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface UseProjectProfileResult extends AsyncResult<ProjectProfile | null> {
|
|
131
|
+
update(patch: ProjectProfilePatch): Promise<ProjectProfile>;
|
|
132
|
+
reset(): Promise<ProjectProfile>;
|
|
133
|
+
}
|
|
134
|
+
declare function useProjectProfile(enabled?: boolean): UseProjectProfileResult;
|
|
135
|
+
|
|
119
136
|
type CreateBasicConfig<S extends BasicSchema> = BrowserBasicConfig<S> & {
|
|
120
137
|
schema: S;
|
|
121
138
|
};
|
|
@@ -145,6 +162,7 @@ interface CreatedBasic<S extends BasicSchema> {
|
|
|
145
162
|
useSyncStatus(source?: SourceRef): UseSyncStatusResult;
|
|
146
163
|
useSchemaStatus(source?: SourceRef): SchemaStatus;
|
|
147
164
|
useRepos(): UseReposResult;
|
|
165
|
+
useProjectProfile(enabled?: boolean): UseProjectProfileResult;
|
|
148
166
|
useFiles(query?: FileListQuery, options?: {
|
|
149
167
|
source?: SourceRef;
|
|
150
168
|
}): UseFilesResult;
|
|
@@ -178,6 +196,93 @@ declare function useCollection<V extends JsonObject = JsonObject>(name: string,
|
|
|
178
196
|
source?: SourceRef;
|
|
179
197
|
}): Collection<V>;
|
|
180
198
|
|
|
199
|
+
interface BasicAppearance {
|
|
200
|
+
theme?: 'light' | 'dark';
|
|
201
|
+
accent?: string;
|
|
202
|
+
radius?: string;
|
|
203
|
+
density?: 'compact' | 'comfortable';
|
|
204
|
+
}
|
|
205
|
+
/** Optional styling provider. CSS is opt-in via @basictech/react/styles.css. */
|
|
206
|
+
declare function BasicUIProvider({ appearance, children, }: {
|
|
207
|
+
appearance?: BasicAppearance;
|
|
208
|
+
children: ReactNode;
|
|
209
|
+
}): react.JSX.Element;
|
|
210
|
+
interface BasicButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
211
|
+
variant?: 'solid' | 'outline' | 'ghost';
|
|
212
|
+
}
|
|
213
|
+
declare const BasicButton: react.ForwardRefExoticComponent<BasicButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
214
|
+
interface AuthButtonProps extends BasicButtonProps {
|
|
215
|
+
auth?: UseAuthResult;
|
|
216
|
+
input?: string;
|
|
217
|
+
}
|
|
218
|
+
declare function SignInButton(props: AuthButtonProps): react.JSX.Element;
|
|
219
|
+
declare function SignOutButton(props: AuthButtonProps): react.JSX.Element;
|
|
220
|
+
interface UserAvatarProps extends HTMLAttributes<HTMLSpanElement> {
|
|
221
|
+
profile?: (Pick<BasicProfile, 'name' | 'handle' | 'email' | 'picture'> & Partial<Pick<BasicProfile, 'kind'>>) | null;
|
|
222
|
+
size?: number;
|
|
223
|
+
showSyncBadge?: boolean;
|
|
224
|
+
sync?: UseSyncStatusResult;
|
|
225
|
+
accounts?: UseAccountsResult;
|
|
226
|
+
auth?: UseAuthResult;
|
|
227
|
+
projectProfile?: UseProjectProfileResult;
|
|
228
|
+
allowAddAccount?: boolean;
|
|
229
|
+
}
|
|
230
|
+
declare function UserAvatar({ accounts, auth, projectProfile, allowAddAccount, ...avatarProps }: UserAvatarProps): react.JSX.Element;
|
|
231
|
+
declare function AuthStatus({ auth: supplied, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
|
|
232
|
+
auth?: UseAuthResult;
|
|
233
|
+
}): react.JSX.Element;
|
|
234
|
+
declare function SyncStatus({ sync: supplied, source, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
|
|
235
|
+
sync?: UseSyncStatusResult;
|
|
236
|
+
source?: SourceRef;
|
|
237
|
+
}): react.JSX.Element | null;
|
|
238
|
+
interface UserButtonProps {
|
|
239
|
+
accounts?: UseAccountsResult;
|
|
240
|
+
auth?: UseAuthResult;
|
|
241
|
+
sync?: UseSyncStatusResult;
|
|
242
|
+
/** Show synchronization status in the account menu. Defaults to true. */
|
|
243
|
+
showSyncStatus?: boolean;
|
|
244
|
+
/** Defaults to enabled in sync mode. */
|
|
245
|
+
showSyncBadge?: boolean;
|
|
246
|
+
shape?: 'rounded' | 'square';
|
|
247
|
+
projectProfile?: UseProjectProfileResult;
|
|
248
|
+
/** Show the action to sign in to another account. Defaults to true. */
|
|
249
|
+
allowAddAccount?: boolean;
|
|
250
|
+
className?: string;
|
|
251
|
+
accountSettingsUrl?: string;
|
|
252
|
+
}
|
|
253
|
+
declare function UserButton(props: UserButtonProps): react.JSX.Element;
|
|
254
|
+
interface UserMenuProps extends UserButtonProps {
|
|
255
|
+
trigger?: 'avatar' | 'button';
|
|
256
|
+
avatarProps?: UserAvatarProps;
|
|
257
|
+
}
|
|
258
|
+
declare function UserMenu({ accounts: supplied, auth: suppliedAuth, sync, showSyncStatus, showSyncBadge, shape, trigger, avatarProps, projectProfile, allowAddAccount, className, accountSettingsUrl, }: UserMenuProps): react.JSX.Element;
|
|
259
|
+
interface BasicModalProps {
|
|
260
|
+
open?: boolean;
|
|
261
|
+
onOpenChange?: (open: boolean) => void;
|
|
262
|
+
finalFocus?: RefObject<HTMLElement>;
|
|
263
|
+
/** Supply a button element; null omits the trigger for controlled use. */
|
|
264
|
+
trigger?: ReactNode;
|
|
265
|
+
}
|
|
266
|
+
interface AccountSettingsModalProps extends BasicModalProps {
|
|
267
|
+
auth?: UseAuthResult;
|
|
268
|
+
accounts?: UseAccountsResult;
|
|
269
|
+
projectProfile?: UseProjectProfileResult;
|
|
270
|
+
sync?: UseSyncStatusResult;
|
|
271
|
+
accountSettingsUrl?: string;
|
|
272
|
+
children?: ReactNode;
|
|
273
|
+
}
|
|
274
|
+
declare function AccountSettingsModal({ auth: suppliedAuth, accounts: suppliedAccounts, accountSettingsUrl, projectProfile, sync, children, ...props }: AccountSettingsModalProps): react.JSX.Element;
|
|
275
|
+
interface SharesModalProps extends BasicModalProps {
|
|
276
|
+
auth?: UseAuthResult;
|
|
277
|
+
accounts?: UseAccountsResult;
|
|
278
|
+
sync?: UseSyncStatusResult;
|
|
279
|
+
shares?: UseOutgoingSharesResult;
|
|
280
|
+
/** Explicit least-privilege scope; the component never infers all tables. */
|
|
281
|
+
scope: CreateOutgoingShareInput['scope'];
|
|
282
|
+
repo?: CreateOutgoingShareInput['repo'];
|
|
283
|
+
}
|
|
284
|
+
declare function SharesModal({ auth: supplied, accounts: suppliedAccounts, sync, shares, scope, repo, ...props }: SharesModalProps): react.JSX.Element;
|
|
285
|
+
|
|
181
286
|
/** Synchronous browser Storage adapted to core's key-value contract. */
|
|
182
287
|
declare class BrowserKeyValueStorage implements KeyValueStorage {
|
|
183
288
|
private readonly storage;
|
|
@@ -211,6 +316,8 @@ declare class BrowserTokenStore implements TokenStore {
|
|
|
211
316
|
private waitForAccessToken;
|
|
212
317
|
}
|
|
213
318
|
|
|
319
|
+
/** The subset of BrowserTokenStore the auth channel needs; any token store providing it works. */
|
|
320
|
+
type AccessTokenAdopter = Pick<BrowserTokenStore, 'accessToken' | 'adoptAccessToken'>;
|
|
214
321
|
interface BrowserMessageChannel {
|
|
215
322
|
postMessage(message: unknown): void;
|
|
216
323
|
close(): void;
|
|
@@ -223,9 +330,11 @@ declare function createBrowserMessageChannel(name: string): BrowserMessageChanne
|
|
|
223
330
|
/**
|
|
224
331
|
* Access tokens may cross tabs transiently but are never written to browser
|
|
225
332
|
* storage. The receiving adapter adopts the token before core handles the
|
|
226
|
-
* ordinary token-rotation notification.
|
|
333
|
+
* ordinary token-rotation notification. Core supplies the token key for the
|
|
334
|
+
* profile's auth channel; channels without a context (the accounts channel)
|
|
335
|
+
* are plain BroadcastChannels.
|
|
227
336
|
*/
|
|
228
|
-
declare function createBrowserAuthChannelFactory(
|
|
337
|
+
declare function createBrowserAuthChannelFactory(tokenStore: AccessTokenAdopter): (name: string, context?: AuthChannelContext) => AuthMessageChannel;
|
|
229
338
|
|
|
230
339
|
declare function browserNavigate(url: string): void;
|
|
231
340
|
declare function browserCurrentUrl(): string;
|
|
@@ -269,4 +378,4 @@ declare class PersistenceStore implements ReplicaStoreFactory {
|
|
|
269
378
|
/** Browser multipart transport with upload progress, adapted from the Drive UI. */
|
|
270
379
|
declare const browserUploadTransport: UploadTransportAdapter;
|
|
271
380
|
|
|
272
|
-
export { BasicProvider, type BasicProviderProps, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseQueryResult, type UseReposResult, type UseStorageInfoResult, type UseSyncStatusResult, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useQuery, useRepos, useSchemaStatus, useStorageInfo, useSyncStatus };
|
|
381
|
+
export { type AccessTokenAdopter, AccountSettingsModal, type AccountSettingsModalProps, type AuthButtonProps, AuthStatus, type BasicAppearance, BasicButton, type BasicButtonProps, type BasicModalProps, BasicProvider, type BasicProviderProps, BasicUIProvider, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, SharesModal, type SharesModalProps, SignInButton, SignOutButton, SyncStatus, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseProjectProfileResult, type UseQueryResult, type UseReposResult, type UseStorageInfoResult, type UseSyncStatusResult, UserAvatar, type UserAvatarProps, UserButton, type UserButtonProps, UserMenu, type UserMenuProps, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useProjectProfile, useQuery, useRepos, useSchemaStatus, useStorageInfo, useSyncStatus };
|