@ai-matrx/sharing 0.0.0 → 0.1.1

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.
@@ -0,0 +1,779 @@
1
+ import * as react from 'react';
2
+ import react__default, { ReactNode, ComponentType } from 'react';
3
+ import { ConfirmOptions } from '@ai-matrx/kit/confirm-opener';
4
+ import { ResourceType, PermissionWithDetails, PermissionLevel, ShareActionResult, SharingCopyContext, AccessSummary, ShownTo, WhoCanSee, LaneChoice, Permission, CheckPermissionOptions } from '@ai-matrx/sharing/core';
5
+ import { EntityTypeToken } from '@ai-matrx/associations';
6
+
7
+ type LooseProps = {
8
+ [key: string]: any;
9
+ };
10
+ interface ShareSheetOrganization {
11
+ id: string;
12
+ name: string;
13
+ [key: string]: unknown;
14
+ }
15
+ interface ShareSheetConnection {
16
+ user_id: string;
17
+ email: string | null;
18
+ display_name: string | null;
19
+ avatar_url: string | null;
20
+ source: "conversation" | "organization" | "invitation";
21
+ sourceDetails?: string | undefined;
22
+ }
23
+ interface ShareSheetConnections {
24
+ connections: ShareSheetConnection[];
25
+ isLoading: boolean;
26
+ error: string | null;
27
+ partialFailures: string[];
28
+ refresh: () => Promise<void> | void;
29
+ }
30
+ interface ShareSheetToastOptions {
31
+ title?: ReactNode | undefined;
32
+ description?: ReactNode | undefined;
33
+ variant?: "default" | "destructive" | "success" | string | undefined;
34
+ }
35
+ interface ShareSheetUi {
36
+ /** Messages. Default: `console` + `window.alert` for errors (never swallowed). */
37
+ toast: (options: ShareSheetToastOptions) => void;
38
+ /** The signed-in person's id. Default: read once from the sharing client's session. */
39
+ useCurrentUserId: () => string | null;
40
+ /** The organizations the person belongs to (`{id, name}`). Default: `organizations` read once. */
41
+ useOrganizations: () => ShareSheetOrganization[];
42
+ /** Contacts offered in the People tab. Default: the organization's roster. */
43
+ useUserConnections: (options: {
44
+ organizationId?: string;
45
+ }) => ShareSheetConnections;
46
+ confirm: (options: ConfirmOptions) => Promise<boolean>;
47
+ CopyButtons: ComponentType<LooseProps>;
48
+ csvExportItem: (...args: any[]) => any;
49
+ jsonExportItem: (...args: any[]) => any;
50
+ usePageCapture: (build: () => unknown, options?: {
51
+ enabled?: boolean;
52
+ }) => void;
53
+ dialogCapture: (input: any) => unknown;
54
+ PageCaptureButton: ComponentType<LooseProps>;
55
+ ErrorAlchemyMenu: ComponentType<LooseProps>;
56
+ InfoHint: ComponentType<LooseProps>;
57
+ EntityDoorControls: ComponentType<LooseProps>;
58
+ EntityRef: ComponentType<LooseProps>;
59
+ UserAvatarDisplay: ComponentType<LooseProps>;
60
+ ConnectionsReadNotice: ComponentType<LooseProps>;
61
+ UserSearchField: ComponentType<LooseProps>;
62
+ /** The vault identity the secure-link panel seals with (`@ai-matrx/vault`). */
63
+ vaultIdentity: unknown;
64
+ humanLines: (...args: any[]) => string;
65
+ /** Where dialogs portal (the web: its pop-out window container). Default: the document body. */
66
+ usePortalContainer: () => HTMLElement | null | undefined;
67
+ }
68
+ /** Merge host UI over the defaults (boot-time; tests may call it again). */
69
+ declare function configureShareSheetUi(next: Partial<ShareSheetUi>): void;
70
+ declare function shareSheetUi(): ShareSheetUi;
71
+ /** Test seam: forget every cached answer. */
72
+ declare function resetShareSheetCache(): void;
73
+
74
+ interface ShareModalProps {
75
+ /** The level picker's words: "notion" = Full access · Edit · Comment · View. */
76
+ levelWording?: "roles" | "notion" | undefined;
77
+ isOpen: boolean;
78
+ onClose: () => void;
79
+ resourceType: ResourceType;
80
+ resourceId: string;
81
+ resourceName: string;
82
+ /**
83
+ * The organization this resource belongs to. Pass it whenever the caller knows: it scopes
84
+ * the contact picker to that organization's members (FIX-7B), and the share's in-app
85
+ * notification is filed there (ACCESS-FIX-18) — without it, a share from a page with no
86
+ * organization picked raised "Which workspace is this for?". It is NOT used for the grant —
87
+ * the store still resolves a record's organization off the row itself.
88
+ */
89
+ organizationId?: string | undefined;
90
+ /**
91
+ * What to CALL this thing on screen, when the caller knows better than the
92
+ * registry does. In the unified store a Table IS a `custom.record` row, so
93
+ * `resourceType="record"` is correct and the registry's label — "Record" —
94
+ * is the wrong noun for a table: the seventh-pass verdict read "Share
95
+ * Record" above a table's own name. Omit it and the registry's label stands.
96
+ */
97
+ resourceNoun?: string | undefined;
98
+ /**
99
+ * OPTIONAL override. Leave it out — the modal resolves ownership itself.
100
+ *
101
+ * Only pass this when the caller already holds an authoritative, resolved
102
+ * answer (e.g. it just created the row). Passing a stale or defaulted
103
+ * `false` is what renders this dialog as a dead, empty shell for a user who
104
+ * owns the record.
105
+ */
106
+ isOwner?: boolean | undefined;
107
+ /**
108
+ * SHARING WITH SOMEBODY OUTSIDE THE ORGANIZATION (lane SHARE-OUT, 21 September).
109
+ *
110
+ * Pass this when the subject is a TABLE in the unified record store and the
111
+ * caller knows which organization it belongs to. The Users tab then carries
112
+ * the outside lane: invite by email, "invited, not yet joined", resend and
113
+ * revoke. Leave it out and the dialog is exactly what it was — this is not a
114
+ * capability every resource type has, and a panel that could not work is a
115
+ * panel that must not be drawn.
116
+ *
117
+ * It is NOT a second share surface: the grant it produces is an ordinary
118
+ * `iam.permissions` row read by the same ladder, and the pending state is an
119
+ * `iam.invitations` row, the platform's one invitation primitive.
120
+ */
121
+ outsideShare?: {
122
+ organizationId: string;
123
+ tableId: string;
124
+ };
125
+ }
126
+ /**
127
+ * ShareModal - Main sharing interface
128
+ *
129
+ * Generic modal that works with ANY resource type.
130
+ * Provides tabs for sharing with people or making public. A share names a PERSON, never an
131
+ * organization (SHARE-PEOPLE-ONLY, 2026-09-25): the old Organizations tab is gone, and "Add
132
+ * everyone in <organization>" inside the People tab names each current member instead.
133
+ *
134
+ * This is the ONE sharing dialog in the app. Do not build a feature-specific
135
+ * variant — extend this one. It self-resolves ownership, so every call site is
136
+ * a three-prop drop-in.
137
+ *
138
+ * @example
139
+ * <ShareModal
140
+ * isOpen={isOpen}
141
+ * onClose={() => setIsOpen(false)}
142
+ * resourceType="workflow"
143
+ * resourceId={workflowId}
144
+ * resourceName="My Workflow"
145
+ * />
146
+ */
147
+ declare function ShareModal({ levelWording, isOpen, onClose, resourceType, resourceId, resourceName, organizationId, resourceNoun, isOwner: isOwnerOverride, outsideShare, }: ShareModalProps): react__default.JSX.Element;
148
+
149
+ interface PermissionsListProps {
150
+ permissions: PermissionWithDetails[];
151
+ isOwner: boolean;
152
+ onUpdateLevel: (options: {
153
+ userId?: string | undefined;
154
+ organizationId?: string | undefined;
155
+ isPublic?: boolean | undefined;
156
+ }, newLevel: PermissionLevel) => Promise<ShareActionResult>;
157
+ onRevoke: (options: {
158
+ userId?: string | undefined;
159
+ organizationId?: string | undefined;
160
+ isPublic?: boolean | undefined;
161
+ }) => Promise<ShareActionResult>;
162
+ loading?: boolean | undefined;
163
+ /**
164
+ * Identity + the page's leading KPIs, so every copied grant states what it
165
+ * belongs to. Optional: without it the list still copies, falling back to the
166
+ * identity carried on the permission rows themselves — but a page that shows
167
+ * KPIs above this list should pass them so the payloads mirror the screen.
168
+ */
169
+ copy?: SharingCopyContext | undefined;
170
+ /** Which slice this list renders, e.g. "Users" / "Organizations". */
171
+ listLabel?: string | undefined;
172
+ /**
173
+ * 🚨 WHAT ELSE IS ON THIS SCREEN THAT IS NOT A GRANT (FIX-10C, VERIFIER-10
174
+ * F13, 2026-09-22).
175
+ *
176
+ * This list only ever knew about DIRECT grants, and said so honestly in its
177
+ * scope note. But after an outside invitation lands, the dialog read
178
+ * "Current Access — Not shared with anyone — No one has been granted access
179
+ * here" with the invited person's row drawn directly underneath it. Both
180
+ * halves were true and the screen contradicted itself, which is the one thing
181
+ * a screen may not do.
182
+ *
183
+ * An invitation is not a grant — it confers nothing until it is accepted, and
184
+ * making this list count it would be the same lie pointing the other way. So
185
+ * the empty state stays an empty state and simply stops pretending it is the
186
+ * whole screen: the caller that also draws the pending rows says how many
187
+ * there are, and the sentence names them.
188
+ */
189
+ alsoPending?: {
190
+ count: number;
191
+ one: string;
192
+ many: string;
193
+ } | undefined;
194
+ }
195
+ /**
196
+ * PermissionsList - Display and manage current permissions
197
+ *
198
+ * Shows all users, organizations, and public access for a resource.
199
+ * Allows owners to update permission levels or revoke access.
200
+ *
201
+ * @example
202
+ * <PermissionsList
203
+ * permissions={permissions}
204
+ * isOwner={isOwner}
205
+ * onUpdateLevel={updateLevel}
206
+ * onRevoke={revokeAccess}
207
+ * />
208
+ */
209
+ declare function PermissionsList({ permissions, isOwner, onUpdateLevel, onRevoke, loading, copy, listLabel, alsoPending, }: PermissionsListProps): react__default.JSX.Element;
210
+
211
+ interface PermissionBadgeProps {
212
+ level: PermissionLevel | "owner";
213
+ variant?: "default" | "compact" | undefined;
214
+ showIcon?: boolean | undefined;
215
+ }
216
+ /**
217
+ * PermissionBadge - Visual indicator for permission levels
218
+ *
219
+ * Displays color-coded badges for different permission levels
220
+ * Used throughout the app to show user permissions
221
+ *
222
+ * @example
223
+ * <PermissionBadge level="editor" />
224
+ * <PermissionBadge level="owner" showIcon />
225
+ */
226
+ declare function PermissionBadge({ level, variant, showIcon, }: PermissionBadgeProps): react__default.JSX.Element;
227
+ /**
228
+ * PublicBadge - Badge specifically for public resources
229
+ */
230
+ declare function PublicBadge({ variant, }: {
231
+ variant?: "default" | "compact" | undefined;
232
+ }): react__default.JSX.Element;
233
+ /**
234
+ * PermissionLevelDescription - Show what each level can do
235
+ */
236
+ declare function PermissionLevelDescription({ level, }: {
237
+ level: PermissionLevel;
238
+ }): react__default.JSX.Element;
239
+ /**
240
+ * GuestBadge — a person who is not a member of the thing's organization (CLOSE-SHARE, Notion's "Guest").
241
+ * The store decides (`isGuest` from get_resource_permissions; a Space's presence carries its own answer).
242
+ * One marker for every share list and avatar row.
243
+ */
244
+ declare function GuestBadge(): react__default.JSX.Element;
245
+
246
+ /** What the panel currently knows, for pages that mirror it in their payload. */
247
+ interface AccessSummaryState {
248
+ summary: AccessSummary | null;
249
+ error: string | null;
250
+ loading: boolean;
251
+ }
252
+ interface AccessSummaryPanelProps {
253
+ entityType: EntityTypeToken;
254
+ entityId: string | null;
255
+ /** Load only when the surface is actually visible. */
256
+ enabled?: boolean | undefined;
257
+ /**
258
+ * Change this value to force a refetch. Required on surfaces that mutate
259
+ * grants beside the panel (a Share tab) — pass a signature of the grant
260
+ * state so the summary can never contradict the list next to it.
261
+ */
262
+ refreshToken?: unknown | undefined;
263
+ className?: string | undefined;
264
+ /**
265
+ * Identity + the page's leading KPIs, mirrored verbatim into this panel's
266
+ * payloads. Optional — without it the panel derives what it can from the
267
+ * summary itself.
268
+ */
269
+ copy?: SharingCopyContext | undefined;
270
+ /**
271
+ * Lets the page that composes this panel mirror the reachability answer it
272
+ * is rendering, without paying for a second round trip. Pass a stable
273
+ * callback (a `useState` setter is ideal).
274
+ */
275
+ onSummaryChange?: (state: AccessSummaryState) => void;
276
+ }
277
+ declare function AccessSummaryPanel({ entityType, entityId, enabled, refreshToken, className, copy, onSummaryChange, }: AccessSummaryPanelProps): react.JSX.Element | null;
278
+
279
+ interface RowControlsProps {
280
+ resourceType: ResourceType;
281
+ resourceId: string;
282
+ /** The person may change this thing's sharing. */
283
+ canChange: boolean;
284
+ /** `useSharing().shownTo` — undefined when the type does not carry it. */
285
+ shownTo: ShownTo | null | undefined;
286
+ /** `useSharing().isPublic` — published to the web. */
287
+ isPublic: boolean;
288
+ /** `useSharing().childRecord` — a child follows its parent: nothing is drawn. */
289
+ childRecord?: boolean | undefined;
290
+ /**
291
+ * `false` when this thing is never published to the web although its type is: a whole Table is a
292
+ * `record` too, and only a ROW has a page (/p/e/record/<id>), so a Table draws no publish switch
293
+ * at all (access ladder T-40; the database refuses it too). Omitted → the type decides.
294
+ */
295
+ publishable?: boolean | undefined;
296
+ onSetShownTo: (next: ShownTo | null) => Promise<ShareActionResult>;
297
+ onPublish: () => Promise<ShareActionResult>;
298
+ onStopPublishing: () => Promise<ShareActionResult>;
299
+ }
300
+ declare function RowControls({ resourceType, resourceId, canChange, shownTo, isPublic, childRecord, publishable, onSetShownTo, onPublish, onStopPublishing, }: RowControlsProps): react__default.JSX.Element | null;
301
+
302
+ /**
303
+ * features/sharing/components/WhoCanSeeThis.tsx
304
+ *
305
+ * "WHO CAN SEE THIS" — THE LANE CONTROL AT THE TOP OF THE SHARE DIALOG'S PEOPLE TAB
306
+ * (lane SHARE-LANE-CONTROL, VERIFIER-23 item 3). Record-store things only; every other kind gets
307
+ * its row controls ("Shown to", "Published to the web") in the same place through `row`.
308
+ *
309
+ * The record store has had the door since lane SHARE (`custom.share_lane_set`: mine |
310
+ * organization | world). Under the access ladder law (T-36, chair ruling 2026-10-01) "mine" is the
311
+ * hide it is, "Shown to: Only me": the thing leaves coworkers' lists and still opens for members
312
+ * who have the link. It is never a lock; real separation is another organization or a
313
+ * Confidential/Private Table type. The words are `SHOWN_TO_WORDS.only_me`, the ones RowControls uses.
314
+ * Nothing on screen reached it: a door with no control is a dead end. This is that control, ONE
315
+ * primitive every share host renders (ShareModal, ShareModalWindow, AgentSharePanel,
316
+ * SiteAccessWorkspace) from `useSharing().whoCanSee` — it never reads or writes on its own.
317
+ *
318
+ * · Only me — lane mine: hidden from coworkers' lists; opens by link.
319
+ * · Everyone in <organization> — the organization's default, at the level it grants.
320
+ * · Anyone with the link — the world lane, drawn ONLY where it would be accepted
321
+ * (`world_open`) or is already the state. Otherwise absent,
322
+ * never disabled-looking.
323
+ *
324
+ * The current state is always shown. A person who cannot change sharing sees it as one line of
325
+ * text, not as buttons. Hiding takes nobody's access away, so it applies without a confirm.
326
+ * Named people are never touched.
327
+ */
328
+
329
+ interface WhoCanSeeThisProps {
330
+ /** `useSharing().whoCanSee`. Null → the kind has no lane door, and nothing is drawn. */
331
+ whoCanSee: WhoCanSee | null;
332
+ /** The person may change this thing's sharing (the host's owner/Admin answer). */
333
+ canChange: boolean;
334
+ /** `useSharing().setWhoCanSee`. */
335
+ onChoose: (choice: LaneChoice) => Promise<ShareActionResult>;
336
+ /**
337
+ * Every kind outside the record store: its row controls ("Shown to", "Published to the web"),
338
+ * drawn in this same place. `RowControls` draws nothing for a Private, Confidential or child
339
+ * record (access ladder T-13 phase 5).
340
+ */
341
+ row?: Omit<RowControlsProps, "canChange">;
342
+ }
343
+ declare function WhoCanSeeThis({ whoCanSee, canChange, onChoose, row, }: WhoCanSeeThisProps): react__default.JSX.Element | null;
344
+
345
+ interface ShareLinkPanelProps {
346
+ resourceType: ResourceType;
347
+ resourceId: string;
348
+ isOwner: boolean;
349
+ /** Whether this resource type offers no-login links (admin policy). Hidden when false. */
350
+ enabled?: boolean | undefined;
351
+ }
352
+ /**
353
+ * "Anyone with the link" — mint / copy / revoke no-login share links.
354
+ *
355
+ * A share link is an opaque token that lets anyone view the resource with ZERO
356
+ * sign-in (resolved by the anon `resolve_share_token` RPC). This is the canonical
357
+ * link-carries-everything path — distinct from publishing to the web (which
358
+ * needs a public render route). Owner-only.
359
+ */
360
+ declare function ShareLinkPanel({ resourceType, resourceId, isOwner, enabled, }: ShareLinkPanelProps): react__default.JSX.Element | null;
361
+
362
+ type GrantableLevels = {
363
+ loading: boolean;
364
+ /** Could not be asked (a failure is not a refusal). */
365
+ error: string | null;
366
+ /** level -> null when allowed, else the reason. Absent while loading or on error. */
367
+ levels: Partial<Record<PermissionLevel, string | null>>;
368
+ /** May share outside the organization: null when allowed, else the reason. */
369
+ outside: string | null;
370
+ /** True when at least one level may be given - i.e. the person may share at all. */
371
+ maySharePeople: boolean;
372
+ };
373
+ declare function useGrantableLevels(resourceType: string, resourceId: string, enabled: boolean, refreshKey?: string | undefined): GrantableLevels;
374
+
375
+ /** One current member of the chosen organization, as the dialog shows them. */
376
+ interface OrgMemberPerson {
377
+ userId: string;
378
+ email: string;
379
+ name: string;
380
+ }
381
+ interface AddEveryoneInOrgProps {
382
+ /** Grants ONE person. The same door the single-person share uses. */
383
+ grantPerson: (person: OrgMemberPerson, level: PermissionLevel) => Promise<ShareActionResult>;
384
+ /** The level picked in the People tab: the block's own level starts here, and follows it until the person picks one here. */
385
+ level: PermissionLevel;
386
+ /** Why a level may not be given on this thing (null: may). Those levels are shown disabled with the reason. */
387
+ whyNotLevel?: (level: PermissionLevel) => string | null;
388
+ /** People who already hold a grant on this thing. They are shown, not re-granted. */
389
+ alreadySharedUserIds?: string[] | undefined;
390
+ /** Called once after a run that granted at least one person. */
391
+ onDone?: () => void;
392
+ /**
393
+ * The organization the thing lives in, when the dialog knows it. It is chosen first, so "Add
394
+ * everyone in <that organization>" is one press away; any other organization the person
395
+ * belongs to stays one pick away.
396
+ */
397
+ defaultOrgId?: string | undefined;
398
+ }
399
+ declare function AddEveryoneInOrg({ grantPerson, level: sectionLevel, whyNotLevel, alreadySharedUserIds, onDone, defaultOrgId, }: AddEveryoneInOrgProps): react__default.JSX.Element | null;
400
+
401
+ interface ShareWithUserTabProps {
402
+ onShare: (userId: string, level: PermissionLevel) => Promise<ShareActionResult>;
403
+ onSuccess: () => void;
404
+ resourceType: ResourceType;
405
+ resourceId: string;
406
+ /**
407
+ * THE ORGANIZATION THIS THING BELONGS TO (FIX-7B). With it, the contact list is that
408
+ * organization's members and nobody else. Without it the list is every organization the
409
+ * viewer belongs to, which is what a brand-new organization's share dialog looked like when
410
+ * the seventh pass found it offering "people from all over the database".
411
+ */
412
+ organizationId?: string | undefined;
413
+ /** Identity + the page's leading KPIs, mirrored into the failure payload. */
414
+ copy?: SharingCopyContext | undefined;
415
+ /**
416
+ * "ADD EVERYONE IN <ORGANIZATION>" (SHARE-PEOPLE-ONLY). How ONE listed member is granted.
417
+ * Omitted → `onShare(person.userId, level)`, the same door as the form above. A record-store
418
+ * table passes the outside door here, because its members may sit outside the table's own
419
+ * organization.
420
+ */
421
+ grantEveryonePerson?: (person: OrgMemberPerson, level: PermissionLevel) => Promise<ShareActionResult>;
422
+ /** People who already hold a grant; "Add everyone" shows them and skips them. */
423
+ alreadySharedUserIds?: string[] | undefined;
424
+ /**
425
+ * "notion": the level picker reads the ladder's sentence labels, highest first — Full access · Edit ·
426
+ * Comment · View (Spaces copies Notion's Share menu). Default: the role words.
427
+ */
428
+ levelWording?: "roles" | "notion" | undefined;
429
+ /**
430
+ * The levels the doors will accept from THIS person (iam.may_grant_as). A level with a reason is shown
431
+ * disabled with that reason; omitted, every level is offered and the door answers (as before).
432
+ */
433
+ grantable?: GrantableLevels | undefined;
434
+ }
435
+ /**
436
+ * ShareWithUserTab - Form to share with a specific user.
437
+ * Shows the user's contacts from conversations, organizations, and invitations
438
+ * for quick selection, with a manual email input fallback.
439
+ */
440
+ declare function ShareWithUserTab({ levelWording, grantable, onShare, onSuccess, resourceType, resourceId, organizationId, copy, grantEveryonePerson, alreadySharedUserIds, }: ShareWithUserTabProps): react__default.JSX.Element;
441
+
442
+ interface PublicAccessTabProps {
443
+ /** The public permission row from the permissions table, if any (kept for hosts' tab dot). */
444
+ publicPermission?: Permission | undefined;
445
+ isOwner: boolean;
446
+ resourceType: ResourceType;
447
+ resourceId: string;
448
+ resourceName: string;
449
+ /**
450
+ * Identity + the page's leading KPIs, mirrored into this tab's payloads so a
451
+ * copied answer is interpretable on its own.
452
+ */
453
+ copy?: SharingCopyContext | undefined;
454
+ }
455
+ /**
456
+ * What "Anyone" means for this type, in words that never promise more than the
457
+ * platform delivers. "No sign-in" only where a signed-out page exists
458
+ * (utils/permissions/publicLane.ts); everywhere else public means every signed-in
459
+ * person finds it under Public, and a signed-out visitor reaches it only through a
460
+ * no-login link — when the type has one. Pure — exported for tests.
461
+ */
462
+ declare function anyoneReachWords(typeLabel: string, reach: {
463
+ publicPage: boolean;
464
+ noLoginLink: boolean;
465
+ }): string;
466
+ /**
467
+ * PublicAccessTab — "Anyone with the link" (access ladder Words table, Link row).
468
+ *
469
+ * An Anyone link opens the record at its permission for whoever holds it, at EVERY level —
470
+ * Private and Confidential records included — and is never indexed. The row controls ("Shown to",
471
+ * "Published to the web") are not here: they sit at the top of the people tab
472
+ * (`RowControls`), and only on Organization and Public records (T-13 phase 5).
473
+ */
474
+ declare function PublicAccessTab({ isOwner, resourceType, resourceId, resourceName, copy, }: PublicAccessTabProps): react__default.JSX.Element;
475
+
476
+ /** The one sentence a shut lane gets. Exported so the test asserts the words. */
477
+ declare const LANE_CLOSED_SAY = "This organization's administrator has turned off sharing with people outside it.";
478
+ interface OutsideSharePanelProps {
479
+ organizationId: string;
480
+ tableId: string;
481
+ tableName: string;
482
+ /**
483
+ * 🚨 SO THE LIST ABOVE CANNOT CONTRADICT THE ROWS BELOW (FIX-10C, VERIFIER-10
484
+ * F13). The grant list up the page knows only about grants, which is correct
485
+ * and was still how "Not shared with anyone" came to stand directly over an
486
+ * invited person. This panel tells its host how many people are invited and
487
+ * not yet joined; the host passes that to the grant list's empty state.
488
+ */
489
+ onPendingChange?: ((count: number) => void) | undefined;
490
+ /**
491
+ * An address already inside the organization is GRANTED the table rather than
492
+ * invited (FIX-10C, VERIFIER-10 F7), and that grant belongs in the list at the
493
+ * top of this dialog, not here. So the host is told to re-read it — otherwise
494
+ * the person gets a sentence saying it happened and an unchanged screen.
495
+ */
496
+ onGranted?: (() => void) | undefined;
497
+ }
498
+ declare function OutsideSharePanel({ organizationId, tableId, tableName, onPendingChange, onGranted, }: OutsideSharePanelProps): react__default.JSX.Element;
499
+
500
+ interface SecureLinkPanelProps {
501
+ resourceType: string;
502
+ resourceId: string;
503
+ resourceName: string;
504
+ }
505
+ /**
506
+ * Sending a password is a reveal: it needs the SAME fresh sign-in the vault asks for, through the
507
+ * vault's ONE "Confirm it's you" (Google, password or an emailed code) — never a second dialog.
508
+ */
509
+ declare function SecureLinkPanel(props: SecureLinkPanelProps): react.JSX.Element;
510
+
511
+ /**
512
+ * features/sharing/components/OrgAvailabilityNote.tsx
513
+ *
514
+ * WHAT AN ORGANIZATION ROW IS NOW (SHARE-PEOPLE-ONLY, chair ruling 2026-09-25). A share names a
515
+ * person. An organization grantee is written only as organization AVAILABILITY: an agent bound
516
+ * to an organization's surface, an item contributed to its library, or the HR directory. That is
517
+ * organization configuration, not something this dialog grants. The row still gives access,
518
+ * though, so the dialog says so in one read-only line and never hides it. It is changed where it
519
+ * was set, in the organization.
520
+ */
521
+
522
+ declare function OrgAvailabilityNote({ permissions, organizationDefault, }: {
523
+ permissions: PermissionWithDetails[];
524
+ /**
525
+ * SHARE-TAILS (chair ruling 2026-09-25): a thing nobody set to "Only me" is the organization's
526
+ * default, and every member reaches it without a share. "Not shared with anyone" above is true
527
+ * of shares; this line says who else really reaches it. Under "Only me" (access ladder T-36,
528
+ * chair ruling 2026-10-01) members still open it by link but no longer find it in lists, so the
529
+ * door sends no default and the lane control's "Only me" hint says the rest.
530
+ */
531
+ organizationDefault?: {
532
+ level: string;
533
+ organizationName: string;
534
+ organizationId?: string | undefined;
535
+ } | null;
536
+ }): react__default.JSX.Element | null;
537
+
538
+ interface Props {
539
+ resourceType: string;
540
+ resourceId: string;
541
+ /**
542
+ * Re-read whenever this changes — pass the host's own published state so the control
543
+ * appears the moment the record is published (and leaves when it is unpublished).
544
+ */
545
+ publishedHint?: boolean | undefined;
546
+ className?: string | undefined;
547
+ }
548
+ declare function SearchEngineIndexedSwitch({ resourceType, resourceId, publishedHint, className, }: Props): react.JSX.Element | null;
549
+
550
+ interface ShareOptions {
551
+ title: string;
552
+ text?: string | undefined;
553
+ /** `null` omits a separate URL field when the reviewed text already contains it. */
554
+ url?: string | null | undefined;
555
+ /** Full text to copy when native sharing is unavailable. */
556
+ copyText?: string | undefined;
557
+ fallbackTitle?: string | undefined;
558
+ fallbackDescription?: string | undefined;
559
+ }
560
+ /**
561
+ * Which door actually opened. Callers that announce the result ("Link copied")
562
+ * must not guess: a manual-fallback run copied nothing, and a toast claiming
563
+ * otherwise is a screen that lies.
564
+ */
565
+ type ShareOutcome = "shared" | "copied" | "manual" | "cancelled";
566
+ interface UseShareResult {
567
+ share: (options: ShareOptions) => Promise<ShareOutcome>;
568
+ copy: (text: string, fallback?: {
569
+ title?: string | undefined;
570
+ description?: string | undefined;
571
+ }) => Promise<"copied" | "manual">;
572
+ copied: boolean;
573
+ fallbackDialog: ReactNode;
574
+ }
575
+ /** Native share-sheet first, clipboard second, accessible manual copy last. */
576
+ declare function useShare(): UseShareResult;
577
+
578
+ /**
579
+ * features/sharing/hooks/useAccessSummary.ts
580
+ *
581
+ * One-entity-at-a-time effective-access loader. `enabled` exists so callers can
582
+ * mount the panel inside a collapsed tab and only pay for the round trip when
583
+ * the user actually looks — this query is NOT cheap enough to fire per row.
584
+ */
585
+
586
+ interface UseAccessSummaryArgs {
587
+ entityType: EntityTypeToken;
588
+ entityId: string | null;
589
+ /** Load only while true. Defaults to true. */
590
+ enabled?: boolean | undefined;
591
+ /**
592
+ * Change this value to force a refetch — for surfaces that mutate grants
593
+ * beside the panel (a Share tab), pass a signature of the grant state so
594
+ * the summary can never contradict the freshly-refreshed list next to it.
595
+ */
596
+ refreshToken?: unknown | undefined;
597
+ }
598
+ interface UseAccessSummaryResult {
599
+ summary: AccessSummary | null;
600
+ loading: boolean;
601
+ error: string | null;
602
+ reload: () => void;
603
+ }
604
+ declare function useAccessSummary({ entityType, entityId, enabled, refreshToken, }: UseAccessSummaryArgs): UseAccessSummaryResult;
605
+
606
+ /**
607
+ * Permission Hooks
608
+ *
609
+ * React hooks for working with permissions in components.
610
+ * These hooks provide reactive state management for permissions.
611
+ */
612
+
613
+ /**
614
+ * Hook to get all permissions for a resource
615
+ * @param resourceType Resource type
616
+ * @param resourceId Resource ID
617
+ * @returns Permissions, loading state, and refresh function
618
+ */
619
+ declare function usePermissions(resourceType: ResourceType, resourceId: string, enabled?: boolean): {
620
+ permissions: PermissionWithDetails[];
621
+ loading: boolean;
622
+ error: string | null;
623
+ refresh: () => Promise<void>;
624
+ };
625
+ /**
626
+ * Hook to get permissions with user/org details
627
+ * @param resourceType Resource type
628
+ * @param resourceId Resource ID
629
+ * @returns Permissions with details, loading state, and refresh function
630
+ */
631
+ declare function useResourcePermissions(resourceType: ResourceType, resourceId: string): {
632
+ permissions: PermissionWithDetails[];
633
+ loading: boolean;
634
+ error: string | null;
635
+ refresh: () => Promise<void>;
636
+ };
637
+ /**
638
+ * Hook to check if current user has permission for a resource
639
+ * @param options Check options
640
+ * @returns Permission check result and loading state
641
+ */
642
+ declare function usePermissionCheck(options: CheckPermissionOptions): {
643
+ loading: boolean;
644
+ hasAccess: boolean;
645
+ level?: PermissionLevel | undefined;
646
+ isOwner: boolean;
647
+ reason?: string | undefined;
648
+ };
649
+ /**
650
+ * Hook to check if current user can edit a resource
651
+ * @param resourceType Resource type
652
+ * @param resourceId Resource ID
653
+ * @returns Whether user can edit and loading state
654
+ */
655
+ declare function useCanEdit(resourceType: ResourceType, resourceId: string): {
656
+ canEdit: boolean;
657
+ loading: boolean;
658
+ };
659
+ /**
660
+ * Hook to check if current user can delete/admin a resource
661
+ * @param resourceType Resource type
662
+ * @param resourceId Resource ID
663
+ * @returns Whether user has admin access and loading state
664
+ */
665
+ declare function useCanAdmin(resourceType: ResourceType, resourceId: string): {
666
+ canAdmin: boolean;
667
+ loading: boolean;
668
+ };
669
+ /**
670
+ * Hook to check whether the current user MAY DECIDE WHO ELSE SEES a resource.
671
+ *
672
+ * It kept its name because every call site means "may this person manage
673
+ * sharing?" and none of them meant "did this person create the row". Since
674
+ * 2026-09-19 (lane SHARE) it asks the database's own `may_manage_sharing`
675
+ * predicate — the Owner rung OR the `admin` rung of VIS-17's one ladder, the
676
+ * same question the six sharing RPCs enforce — instead of reading `created_by`
677
+ * from the resource table. Before that an `admin` on a thing was shown a
678
+ * read-only dialog and an empty grant list, and any resource whose table
679
+ * carries no client SELECT grant (the whole record store) could not be asked at
680
+ * all.
681
+ *
682
+ * Returns three distinct states — `loading` (still resolving), `error`
683
+ * (could NOT be determined), and `isOwner` — because collapsing them into one
684
+ * boolean is what makes owner-gated UI render as if the user were a stranger
685
+ * to their own record. Never gate a surface on `!isOwner` alone; check
686
+ * `loading` and `error` first.
687
+ *
688
+ * @param resourceType Resource type
689
+ * @param resourceId Resource ID
690
+ */
691
+ declare function useIsOwner(resourceType: ResourceType, resourceId: string): {
692
+ isOwner: boolean;
693
+ loading: boolean;
694
+ error: string | null;
695
+ };
696
+ /**
697
+ * Hook for sharing operations
698
+ * Provides functions to share, revoke, and update permissions
699
+ * @param resourceType Resource type
700
+ * @param resourceId Resource ID
701
+ * @returns Sharing functions and state
702
+ */
703
+ declare function useSharing(resourceType: ResourceType, resourceId: string, enabled?: boolean, resourceName?: string | undefined,
704
+ /**
705
+ * The organization the object lives in, from the page that opened the dialog (its own
706
+ * resolver). Carried to the share's in-app notification so no action on an object ever asks
707
+ * which workspace it is for (ACCESS-FIX-18).
708
+ */
709
+ organizationId?: string | null | undefined): {
710
+ permissions: PermissionWithDetails[];
711
+ /** Published to the web (or the card's own publish). */
712
+ isPublic: boolean;
713
+ /**
714
+ * "Shown to" — undefined when the type does not carry it (Private, Confidential, child, or a
715
+ * table without the column); null when the type's default applies.
716
+ */
717
+ shownTo: ShownTo | null | undefined;
718
+ /** A child record (a file attached to a chat): no row control of its own. */
719
+ childRecord: boolean;
720
+ /** Membership alone reaches it — said under Current Access. The record store's door names it. */
721
+ organizationDefault: {
722
+ level: string;
723
+ organizationName: string;
724
+ organizationId?: string | undefined;
725
+ } | null;
726
+ whoCanSee: WhoCanSee | null;
727
+ /** The thing's own organization, read off its row; null when unknown. */
728
+ homeOrganizationId: string | null;
729
+ setWhoCanSee: (choice: LaneChoice) => Promise<ShareActionResult>;
730
+ loading: boolean;
731
+ error: string | null;
732
+ shareWithUser: (userId: string, permissionLevel: PermissionLevel) => Promise<ShareActionResult>;
733
+ makePublic: (permissionLevel?: PermissionLevel) => Promise<ShareActionResult>;
734
+ setShownTo: (next: ShownTo | null) => Promise<ShareActionResult>;
735
+ revokeAccess: (options: {
736
+ userId?: string | undefined;
737
+ organizationId?: string | undefined;
738
+ isPublic?: boolean | undefined;
739
+ }) => Promise<ShareActionResult>;
740
+ revokeOrgAccess: (organizationId: string) => Promise<ShareActionResult>;
741
+ updateLevel: (options: {
742
+ userId?: string | undefined;
743
+ organizationId?: string | undefined;
744
+ }, newLevel: PermissionLevel) => Promise<ShareActionResult>;
745
+ refresh: () => Promise<void>;
746
+ refreshRowState: () => Promise<void>;
747
+ };
748
+ /**
749
+ * Hook to get resources shared with current user
750
+ * @param resourceType Optional filter by resource type
751
+ * @returns Shared permissions and loading state
752
+ */
753
+ declare function useSharedWithMe(resourceType?: ResourceType | undefined): {
754
+ permissions: Permission[];
755
+ loading: boolean;
756
+ error: string | null;
757
+ refresh: () => Promise<void>;
758
+ };
759
+ /**
760
+ * Hook to get sharing status summary for a resource.
761
+ *
762
+ * Intentionally lightweight — only fetches is_public from the resource row.
763
+ * Safe to call from list-item components like ShareButton (single cheap query).
764
+ *
765
+ * Does NOT call get_resource_permissions (the expensive SECURITY DEFINER RPC).
766
+ * Full permission details are loaded inside useSharing() when the modal opens.
767
+ */
768
+ declare function useSharingStatus(resourceType: ResourceType, resourceId: string, enabled?: boolean): {
769
+ isPublic: any;
770
+ loading: boolean;
771
+ error: string | null;
772
+ refresh: () => Promise<void>;
773
+ };
774
+
775
+ declare function WhoCouldSeeOn({ recordId }: {
776
+ recordId: string;
777
+ }): react.JSX.Element;
778
+
779
+ export { AccessSummaryPanel, type AccessSummaryPanelProps, type AccessSummaryState, AddEveryoneInOrg, type AddEveryoneInOrgProps, type GrantableLevels, GuestBadge, LANE_CLOSED_SAY, OrgAvailabilityNote, type OrgMemberPerson, OutsideSharePanel, type OutsideSharePanelProps, PermissionBadge, PermissionLevelDescription, PermissionsList, PublicAccessTab, PublicBadge, RowControls, type RowControlsProps, SearchEngineIndexedSwitch, SecureLinkPanel, ShareLinkPanel, ShareModal, type ShareOutcome, type ShareSheetConnection, type ShareSheetConnections, type ShareSheetOrganization, type ShareSheetToastOptions, type ShareSheetUi, ShareWithUserTab, type UseAccessSummaryArgs, type UseAccessSummaryResult, type UseShareResult, WhoCanSeeThis, type WhoCanSeeThisProps, WhoCouldSeeOn, anyoneReachWords, configureShareSheetUi, resetShareSheetCache, shareSheetUi, useAccessSummary, useCanAdmin, useCanEdit, useGrantableLevels, useIsOwner, usePermissionCheck, usePermissions, useResourcePermissions, useShare, useSharedWithMe, useSharing, useSharingStatus };