@ai-matrx/vault 0.1.49 → 0.1.51

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 CHANGED
@@ -1,9 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.51
4
+
5
+ - Recorded two-factor methods now show Not recorded for unsupported or duplicate metadata, in both draft and saved-item views. Existing `sms` and `push_or_other` values keep their meaning. Consumer action: none; no new stored value or choice is introduced.
6
+
7
+ ## 0.1.50
8
+
9
+ - Confirmation dialogs now isolate dismissed/identity-disposed attempts from reopened prompts. `VaultIdentity.cancel()` optionally invalidates unfinished host work; it must preserve completed reusable grants. Consumer action: implement this synchronous, idempotent hook for hosts with isolated confirmation attempts. Existing hosts without it keep their existing methods.
10
+
3
11
  ## 0.1.49
4
12
 
5
- UNVERIFIED: this release has no Unreleased entry and the package-input comparison could not be made (the caller supplied no source comparison). Whether package inputs changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/vault`.
13
+ PACKAGE INPUTS CHANGED WITH NO RELEASE NOTE. This release includes package inputs that nobody described in an Unreleased entry,
14
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
15
+ Diff: `git diff 942928c4ca d6fb12ef87 -- apps/shared/vault`
16
+
17
+ Changed package inputs (1):
18
+ - `apps/shared/vault/src/auth/IdentityGate.tsx`
7
19
 
8
20
  ## 0.1.48
9
21
 
@@ -294,6 +294,7 @@ function IdentityGate({ identity, children }) {
294
294
  setAsking(false);
295
295
  return () => {
296
296
  activeIdentity.current = null;
297
+ identity?.cancel?.();
297
298
  for (const waiter of waiters.current.splice(0)) waiter.reject(new Error(CANCELLED));
298
299
  };
299
300
  }, [identityVersion]);
@@ -342,6 +343,7 @@ function IdentityGate({ identity, children }) {
342
343
  };
343
344
  const settle = (ok) => {
344
345
  if (activeIdentity.current !== identityVersion) return;
346
+ if (!ok) identity?.cancel?.();
345
347
  const w = waiters.current;
346
348
  waiters.current = [];
347
349
  if (ok) confirmationVersion.current += 1;
@@ -351,7 +353,7 @@ function IdentityGate({ identity, children }) {
351
353
  };
352
354
  return /* @__PURE__ */ jsxs3(Ctx2.Provider, { value: confirmed, children: [
353
355
  children,
354
- identity ? /* @__PURE__ */ jsx3(ConfirmIdentityDialog, { open: asking, identity, onDone: () => settle(true), onCancel: () => settle(false) }, identityVersion.key) : null,
356
+ identity && asking ? /* @__PURE__ */ jsx3(ConfirmIdentityDialog, { open: true, identity, onDone: () => settle(true), onCancel: () => settle(false) }, identityVersion.key) : null,
355
357
  /* @__PURE__ */ jsx3(Dialog, { open: returnProblem !== null, onOpenChange: (o) => o ? void 0 : setReturnProblem(null), children: /* @__PURE__ */ jsxs3(DialogContent, { size: "sm", children: [
356
358
  /* @__PURE__ */ jsxs3(DialogHeader, { children: [
357
359
  /* @__PURE__ */ jsx3(DialogTitle, { children: "Different account" }),
@@ -363,6 +365,16 @@ function IdentityGate({ identity, children }) {
363
365
  }
364
366
  var wasCancelled = (e) => e instanceof Error && e.message === CANCELLED;
365
367
  function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
368
+ const live = useRef2(false);
369
+ useLayoutEffect(() => {
370
+ live.current = true;
371
+ return () => {
372
+ live.current = false;
373
+ };
374
+ }, []);
375
+ const finish = () => {
376
+ if (live.current) onDone();
377
+ };
366
378
  const [account, setAccount] = useState2(null);
367
379
  const [password, setPassword] = useState2("");
368
380
  const [code, setCode] = useState2("");
@@ -375,18 +387,29 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
375
387
  setCode("");
376
388
  setCodeSent(false);
377
389
  setError(null);
378
- identity.account().then(setAccount, (e) => setError(e instanceof Error ? e.message : "Your account could not be read."));
390
+ let current = true;
391
+ identity.account().then(
392
+ (value) => {
393
+ if (current && live.current) setAccount(value);
394
+ },
395
+ (e) => {
396
+ if (current && live.current) setError(e instanceof Error ? e.message : "Your account could not be read.");
397
+ }
398
+ );
399
+ return () => {
400
+ current = false;
401
+ };
379
402
  }, [open, identity]);
380
403
  const attempt = async (step, done = true) => {
381
404
  setBusy(true);
382
405
  setError(null);
383
406
  try {
384
407
  await step();
385
- if (done) onDone();
408
+ if (done) finish();
386
409
  } catch (e) {
387
- setError(e instanceof Error ? e.message : "That did not work. Try again.");
410
+ if (live.current) setError(e instanceof Error ? e.message : "That did not work. Try again.");
388
411
  } finally {
389
- setBusy(false);
412
+ if (live.current) setBusy(false);
390
413
  }
391
414
  };
392
415
  return /* @__PURE__ */ jsx3(Dialog, { open, onOpenChange: (o) => o ? void 0 : onCancel(), children: /* @__PURE__ */ jsxs3(DialogContent, { size: "sm", children: [
@@ -409,7 +432,7 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
409
432
  variant: identity.dev ? "outline" : "primary",
410
433
  disabled: busy,
411
434
  onClick: () => void attempt(async () => {
412
- if (await identity.withProvider(p) === "confirmed") onDone();
435
+ if (await identity.withProvider(p) === "confirmed") finish();
413
436
  }, false),
414
437
  children: [
415
438
  "Continue with ",
@@ -433,7 +456,7 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
433
456
  /* @__PURE__ */ jsx3(Button, { variant: "outline", type: "submit", disabled: busy || !code.trim(), children: "Confirm" })
434
457
  ] }) : /* @__PURE__ */ jsx3(Button, { variant: "quiet", disabled: busy, onClick: () => void attempt(async () => {
435
458
  await identity.sendEmailCode();
436
- setCodeSent(true);
459
+ if (live.current) setCodeSent(true);
437
460
  }, false), children: "Email me a code instead" }) : null,
438
461
  error ? /* @__PURE__ */ jsx3("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null
439
462
  ] })
@@ -497,4 +520,4 @@ export {
497
520
  IdentityGate,
498
521
  wasCancelled
499
522
  };
500
- //# sourceMappingURL=chunk-LXFJBJR6.js.map
523
+ //# sourceMappingURL=chunk-YDYTU3NY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/notice.tsx","../src/auth/identity.ts","../src/auth/IdentityGate.tsx","../src/glyphs.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"@ai-matrx/design-system\";\nimport { copyText } from \"@ai-matrx/kit/clipboard\";\nimport type { VaultClipboard } from \"./lock\";\n\nexport type NoticeKind = \"success\" | \"error\" | \"info\";\nexport type Notify = (message: string, kind?: NoticeKind) => void;\n\nconst Ctx = createContext<Notify>(() => undefined);\n\n/** Say something short to the person (\"Password copied\"). Hosts with their own toasts pass `notify`. */\nexport function useNotify(): Notify {\n return useContext(Ctx);\n}\n\nexport interface CopyOptions {\n /** A secret (a password, a code, a key): the host's clipboard takes custody. Default true. */\n secret?: boolean;\n}\n\n/**\n * Writes `text` and says so (or says why not). Through the host's clipboard when it has one (custody:\n * the host clears a secret later), else the browser's. Pure over its inputs, so it is tested alone.\n */\nexport async function copyValue(text: string, label: string, options: CopyOptions, deps: { clipboard?: VaultClipboard | undefined; notify: Notify }): Promise<boolean> {\n const secret = options.secret ?? true;\n if (deps.clipboard) {\n try {\n const copied = await deps.clipboard.copy(text, { secret });\n deps.notify(copiedLine(label, copied?.clearsAtMs ?? null), \"success\");\n return true;\n } catch (e) {\n deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, \"error\");\n return false;\n }\n }\n return copyText(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => deps.notify(m, k === \"error\" ? \"error\" : \"success\") });\n}\n\n/** \"Password copied\", or \"Password copied. Clears at 3:42 PM\" when the host says when it clears it. */\nexport function copiedLine(label: string, clearsAtMs: number | null): string {\n if (clearsAtMs === null || !Number.isFinite(clearsAtMs)) return `${label} copied`;\n const at = new Date(clearsAtMs).toLocaleTimeString(undefined, { hour: \"numeric\", minute: \"2-digit\" });\n return `${label} copied. Clears at ${at}`;\n}\n\nconst ClipboardCtx = createContext<VaultClipboard | undefined>(undefined);\n\n/** The host's clipboard (`host.clipboard`), for every copy below it. */\nexport function ClipboardProvider({ clipboard, children }: { clipboard: VaultClipboard | undefined; children: ReactNode }) {\n return <ClipboardCtx.Provider value={clipboard}>{children}</ClipboardCtx.Provider>;\n}\n\n/** THE copy for every vault screen: writes, then says so (or says why not). */\nexport function useCopy(): (text: string, label: string, options?: CopyOptions) => Promise<boolean> {\n const notify = useNotify();\n const clipboard = useContext(ClipboardCtx);\n return useCallback((text, label, options = {}) => copyValue(text, label, options, { clipboard, notify }), [notify, clipboard]);\n}\n\n/**\n * The vault's own notice line, so feedback exists in every host (an extension side panel has no app\n * toaster). A host that has one passes `notify` and this draws nothing.\n */\nexport function NoticeProvider({ notify, children }: { notify?: Notify | undefined; children: ReactNode }) {\n const [shown, setShown] = useState<{ message: string; kind: NoticeKind; n: number } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const local = useCallback<Notify>((message, kind = \"success\") => {\n if (timer.current) clearTimeout(timer.current);\n setShown((s) => ({ message, kind, n: (s?.n ?? 0) + 1 }));\n timer.current = setTimeout(() => setShown(null), kind === \"error\" ? 5000 : 2200);\n }, []);\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), []);\n return (\n <Ctx.Provider value={notify ?? local}>\n {children}\n {!notify ? (\n <div aria-live=\"polite\" className=\"pointer-events-none absolute inset-x-0 bottom-4 z-30 flex justify-center px-4\">\n {shown ? (\n <div key={shown.n} role=\"status\" className={cn(\"rounded-full border border-border bg-popover px-4 py-2 text-sm shadow-lg\", shown.kind === \"error\" ? \"text-destructive\" : \"text-foreground\")}>\n {shown.message}\n </div>\n ) : null}\n </div>\n ) : null}\n </Ctx.Provider>\n );\n}\n","/**\n * CONFIRM IT'S YOU. The server shows or exports a secret only after a sign-in from the last few\n * minutes (`recent_auth_required`). Every host signs people in with Supabase, so the one way to\n * produce a fresh sign-in lives here, over the host's own auth client — whichever way the person\n * signs in: Google (or any connected provider), a password, or a code sent to their email.\n */\n\n/** How a person can prove it is them again. Ordered by what the account actually uses. */\nexport type IdentityMethod = \"provider\" | \"password\" | \"email_code\" | \"dev\";\n\nexport interface IdentityAccount {\n email: string | null;\n /** Connected sign-in providers on the account (`google`, `github`, …), never `email`. */\n providers: readonly string[];\n hasPassword: boolean;\n}\n\nexport interface VaultIdentity {\n account(): Promise<IdentityAccount>;\n /** Synchronously invalidate an unfinished attempt on dismissal or identity disposal.\n * Late work cleans up its own resources and cannot confirm a newer attempt.\n * A completed confirmation remains reusable across ordinary workspace navigation. */\n cancel?: () => void;\n /**\n * Confirms with that provider. On the web it leaves the page and comes back to the same address\n * (resolves with nothing: the page is going away). A host that confirms IN PLACE (the desktop's\n * Touch ID, a browser sign-in it waits for) resolves `\"confirmed\"`, and the gate closes and runs\n * the refused call again — one confirmation, never a second click.\n */\n withProvider(provider: string): Promise<void | \"confirmed\">;\n withPassword(password: string): Promise<void>;\n sendEmailCode(): Promise<void>;\n withEmailCode(code: string): Promise<void>;\n /** A host-only path with no human step (the playground's dev server). */\n dev?: () => Promise<void>;\n /**\n * Run once when the page loads. After a provider confirmation left the page and came back, it\n * checks the SAME person is signed in. A different account is signed out of this device and the\n * sentence to show is returned; otherwise null.\n */\n checkReturn?: () => Promise<string | null>;\n}\n\n/** The slice of supabase-js `auth` this needs, so the package never depends on supabase-js itself. */\nexport interface SupabaseAuthLike {\n getUser(): Promise<{ data: { user: { id: string; email?: string | null; identities?: { provider: string }[] | null; app_metadata?: { providers?: string[] } } | null }; error: unknown }>;\n signInWithPassword(c: { email: string; password: string }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOtp(c: { email: string; options?: { shouldCreateUser?: boolean } }): Promise<{ error: { message: string } | null }>;\n verifyOtp(c: { email: string; token: string; type: \"email\" }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOAuth(c: { provider: never; options?: { redirectTo?: string } }): Promise<{ error: { message: string } | null }>;\n /** Used only when the host gives no `onDifferentPerson`: ends THIS device's session, never others. */\n signOut?(o: { scope: \"local\" }): Promise<{ error: unknown }>;\n}\n\n/** Where the person confirming is remembered while the page is away at the provider. */\nexport interface ReturnStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface SupabaseIdentityOptions {\n /** The host's accepted OAuth return address carrying this page. */\n redirectTo?: () => string;\n dev?: () => Promise<void>;\n /**\n * A provider confirmation came back signed in as someone else. The host ends that session on THIS\n * device only (its own device-scoped sign-out) and tells the person; `message` says what happened.\n * Without it the package signs out locally through `auth.signOut({ scope: \"local\" })`.\n */\n onDifferentPerson?: (message: string) => Promise<void> | void;\n /** Default: the tab's `sessionStorage`. */\n store?: ReturnStore;\n /** Default `Date.now`. */\n now?: () => number;\n}\n\n/** The key the expected person is kept under across the provider's redirect. */\nexport const CONFIRM_RETURN_KEY = \"matrx-vault:confirming-as\";\n/** A remembered confirmation older than this is stale (the person never came back from it). */\nconst RETURN_WINDOW_MS = 15 * 60_000;\n\nconst SAME_PERSON = \"That signed in a different account. Use the account this vault belongs to.\";\n\n/** THE identity confirmation for every host: give it the host's `supabase.auth`. */\nexport function createSupabaseIdentity(auth: SupabaseAuthLike, options: SupabaseIdentityOptions = {}): VaultIdentity {\n let me: { id: string; email: string | null } | null = null;\n const now = options.now ?? Date.now;\n const store = (): ReturnStore | null => {\n if (options.store) return options.store;\n try {\n return (globalThis as { sessionStorage?: ReturnStore }).sessionStorage ?? null;\n } catch {\n return null;\n }\n };\n const who = async () => {\n const { data } = await auth.getUser();\n if (!data.user) throw new Error(\"You are signed out. Sign in again.\");\n me = { id: data.user.id, email: data.user.email ?? null };\n return data.user;\n };\n const sameUser = (id: string | undefined) => {\n if (!me || id !== me.id) throw new Error(SAME_PERSON);\n };\n const identity: VaultIdentity = {\n async account() {\n const u = await who();\n const names = new Set([...(u.identities ?? []).map((i) => i.provider), ...(u.app_metadata?.providers ?? [])]);\n return { email: u.email ?? null, providers: [...names].filter((p) => p !== \"email\" && p !== \"phone\"), hasPassword: names.has(\"email\") };\n },\n async withProvider(provider) {\n const u = await who();\n // Remembered across the redirect, so the page that loads back can check the same person came back.\n store()?.setItem(CONFIRM_RETURN_KEY, JSON.stringify({ id: u.id, email: u.email ?? null, at: now() }));\n const redirectTo = options.redirectTo?.() ?? globalThis.location?.href;\n // `provider` is a supabase-js Provider string; the structural type keeps supabase-js out of the package.\n const { error } = await auth.signInWithOAuth({ provider: provider as never, ...(redirectTo ? { options: { redirectTo } } : {}) });\n if (error) {\n store()?.removeItem(CONFIRM_RETURN_KEY);\n throw new Error(error.message);\n }\n },\n async checkReturn() {\n const s = store();\n const raw = s?.getItem(CONFIRM_RETURN_KEY) ?? null;\n if (!s || raw === null) return null;\n s.removeItem(CONFIRM_RETURN_KEY);\n let expected: { id?: unknown; email?: unknown; at?: unknown };\n try {\n expected = JSON.parse(raw) as typeof expected;\n } catch {\n return null;\n }\n if (typeof expected.id !== \"string\" || typeof expected.at !== \"number\" || now() - expected.at > RETURN_WINDOW_MS) return null;\n const { data } = await auth.getUser();\n if (!data.user || data.user.id === expected.id) return null;\n const email = typeof expected.email === \"string\" && expected.email ? expected.email : null;\n const message = `That signed in a different account, so it was signed out here.${email ? ` Sign in again as ${email}.` : \" Sign in again.\"}`;\n me = null;\n if (options.onDifferentPerson) await options.onDifferentPerson(message);\n else await auth.signOut?.({ scope: \"local\" });\n return message;\n },\n async withPassword(password) {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to confirm with.\");\n const { data, error } = await auth.signInWithPassword({ email: u.email, password });\n if (error) throw new Error(\"That password was not accepted.\");\n sameUser(data.user?.id);\n },\n async sendEmailCode() {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to send a code to.\");\n const { error } = await auth.signInWithOtp({ email: u.email, options: { shouldCreateUser: false } });\n if (error) throw new Error(error.message);\n },\n async withEmailCode(code) {\n const u = me ?? (await who());\n if (!u.email) throw new Error(\"This account has no email.\");\n const { data, error } = await auth.verifyOtp({ email: u.email, token: code.trim(), type: \"email\" });\n if (error) throw new Error(\"That code was not accepted. Check it, or send a new one.\");\n sameUser(data.user?.id);\n },\n };\n if (options.dev) identity.dev = options.dev;\n return identity;\n}\n\n/** True when the server refused because the sign-in is not recent enough. */\nexport function needsRecentSignIn(e: unknown): boolean {\n const err = e as { code?: unknown; status?: unknown; message?: unknown; serverDetail?: unknown } | null;\n if (!err) return false;\n if (err.code === \"recent_auth_required\") return true;\n // Some routes answer with only a sentence (the shown message is a generic \"sign in\"; the server's\n // own words say which): read those too.\n const said = [err.message, JSON.stringify(err.serverDetail ?? \"\")].filter((x): x is string => typeof x === \"string\").join(\" \");\n return /recent auth|recent sign|re-authenticate|authentication within the last/i.test(said);\n}\n","import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@ai-matrx/design-system\";\nimport { Button, Field } from \"@ai-matrx/design-system/controls\";\nimport { ShieldGlyph } from \"../glyphs\";\nimport { useNotify } from \"../notice\";\nimport { needsRecentSignIn, type IdentityAccount, type VaultIdentity } from \"./identity\";\n\n/**\n * Wrap any call that shows, copies or exports a secret. When the server answers \"sign in again\",\n * the person confirms it is them (Google, password, or an emailed code) and the call runs again —\n * they never see a dead \"Authentication required\" in the row.\n */\nexport type Confirmed = <T>(run: () => Promise<T>) => Promise<T>;\n\nconst CANCELLED = \"Not confirmed.\";\n/** Said once when the person closes \"Confirm it's you\": whatever asked for it did not happen. */\nexport const NOT_CONFIRMED_NOTICE = \"Not confirmed, so nothing was done\";\nconst Ctx = createContext<Confirmed>((run) => run());\n\nexport function useConfirmed(): Confirmed {\n return useContext(Ctx);\n}\n\nconst PROVIDER_LABEL: Record<string, string> = { google: \"Google\", github: \"GitHub\", azure: \"Microsoft\", apple: \"Apple\", linkedin_oidc: \"LinkedIn\", slack_oidc: \"Slack\", browser: \"Browser\", touch_id: \"Touch ID\" };\nconst providerLabel = (p: string) => PROVIDER_LABEL[p] ?? p.charAt(0).toUpperCase() + p.slice(1);\n\nexport function IdentityGate({ identity, children }: { identity: VaultIdentity | undefined; children: ReactNode }) {\n const [asking, setAsking] = useState(false);\n const notify = useNotify();\n const waiters = useRef<{ resolve: () => void; reject: (e: Error) => void }[]>([]);\n const confirmationVersion = useRef(0);\n const identityVersion = useMemo(() => ({ key: crypto.randomUUID() }), [identity]);\n const activeIdentity = useRef<object | null>(null);\n useLayoutEffect(() => {\n activeIdentity.current = identityVersion;\n setAsking(false);\n return () => {\n activeIdentity.current = null;\n identity?.cancel?.();\n for (const waiter of waiters.current.splice(0)) waiter.reject(new Error(CANCELLED));\n };\n }, [identityVersion]);\n // Back from a provider's confirmation: a different account that came back is signed out and said.\n const [returnProblem, setReturnProblem] = useState<string | null>(null);\n useEffect(() => {\n if (!identity?.checkReturn) return;\n let live = true;\n identity.checkReturn().then(\n (problem) => live && problem && setReturnProblem(problem),\n (e: unknown) => live && setReturnProblem(e instanceof Error ? e.message : \"The account that came back could not be checked.\"),\n );\n return () => {\n live = false;\n };\n }, [identity]);\n\n const confirmed: Confirmed = async (run) => {\n const startedAtVersion = confirmationVersion.current;\n const checkIdentity = () => {\n if (activeIdentity.current !== identityVersion) throw new Error(CANCELLED);\n };\n const runAsIdentity = async () => {\n checkIdentity();\n const result = await run();\n checkIdentity();\n return result;\n };\n try {\n return await runAsIdentity();\n } catch (e) {\n checkIdentity();\n if (!identity || !needsRecentSignIn(e)) throw e;\n // A response sent with the old bearer can arrive after another call's confirmation.\n // Give the current bearer/grant one try before asking the person to confirm again.\n if (startedAtVersion !== confirmationVersion.current) {\n try {\n return await runAsIdentity();\n } catch (retryError) {\n if (!needsRecentSignIn(retryError)) throw retryError;\n }\n }\n await new Promise<void>((resolve, reject) => {\n waiters.current.push({ resolve, reject });\n setAsking(true);\n });\n checkIdentity();\n return runAsIdentity();\n }\n };\n\n const settle = (ok: boolean) => {\n if (activeIdentity.current !== identityVersion) return;\n if (!ok) identity?.cancel?.();\n const w = waiters.current;\n waiters.current = [];\n if (ok) confirmationVersion.current += 1;\n setAsking(false);\n for (const x of w) (ok ? x.resolve() : x.reject(new Error(CANCELLED)));\n // Never silent: every caller stays quiet about a cancel (`wasCancelled`), so the gate says it once.\n if (!ok && w.length > 0) notify(NOT_CONFIRMED_NOTICE, \"info\");\n };\n\n return (\n <Ctx.Provider value={confirmed}>\n {children}\n {identity && asking ? <ConfirmIdentityDialog key={identityVersion.key} open identity={identity} onDone={() => settle(true)} onCancel={() => settle(false)} /> : null}\n <Dialog open={returnProblem !== null} onOpenChange={(o) => (o ? undefined : setReturnProblem(null))}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <DialogTitle>Different account</DialogTitle>\n <DialogDescription>{returnProblem}</DialogDescription>\n </DialogHeader>\n <div className=\"flex justify-end\">\n <Button variant=\"primary\" onClick={() => setReturnProblem(null)}>OK</Button>\n </div>\n </DialogContent>\n </Dialog>\n </Ctx.Provider>\n );\n}\n\n/** True for the rejection a canceled confirmation produces (callers stay quiet about it). */\nexport const wasCancelled = (e: unknown) => e instanceof Error && e.message === CANCELLED;\n\nfunction ConfirmIdentityDialog({ open, identity, onDone, onCancel }: { open: boolean; identity: VaultIdentity; onDone: () => void; onCancel: () => void }) {\n const live = useRef(false);\n useLayoutEffect(() => {\n live.current = true;\n return () => { live.current = false; };\n }, []);\n const finish = () => { if (live.current) onDone(); };\n const [account, setAccount] = useState<IdentityAccount | null>(null);\n const [password, setPassword] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [codeSent, setCodeSent] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!open) return;\n setPassword(\"\");\n setCode(\"\");\n setCodeSent(false);\n setError(null);\n let current = true;\n identity.account().then(\n (value) => { if (current && live.current) setAccount(value); },\n (e: unknown) => { if (current && live.current) setError(e instanceof Error ? e.message : \"Your account could not be read.\"); },\n );\n return () => { current = false; };\n }, [open, identity]);\n\n const attempt = async (step: () => Promise<void>, done = true) => {\n setBusy(true);\n setError(null);\n try {\n await step();\n if (done) finish();\n } catch (e) {\n if (live.current) setError(e instanceof Error ? e.message : \"That did not work. Try again.\");\n } finally {\n if (live.current) setBusy(false);\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={(o) => (o ? undefined : onCancel())}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <div className=\"flex items-center gap-2\">\n <ShieldGlyph className=\"size-5 text-primary\" />\n <DialogTitle>Confirm it’s you</DialogTitle>\n </div>\n <DialogDescription>This needs a recent sign-in{account?.email ? ` to ${account.email}` : \"\"}.</DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-3\">\n {identity.dev ? (\n <Button variant=\"primary\" disabled={busy} onClick={() => void attempt(() => identity.dev?.() ?? Promise.resolve())}>\n Sign in again (dev)\n </Button>\n ) : null}\n {account?.providers.map((p) => (\n <Button\n key={p}\n variant={identity.dev ? \"outline\" : \"primary\"}\n disabled={busy}\n onClick={() =>\n void attempt(async () => {\n if ((await identity.withProvider(p)) === \"confirmed\") finish();\n }, false)\n }\n >\n Continue with {providerLabel(p)}\n </Button>\n ))}\n {account?.hasPassword ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (password) void attempt(() => identity.withPassword(password)); }}>\n <Field className=\"min-w-0 flex-1\" type=\"password\" autoComplete=\"current-password\" placeholder=\"Your password\" aria-label=\"Your password\" value={password} onChange={(e) => setPassword(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !password}>Confirm</Button>\n </form>\n ) : null}\n {account?.email ? (\n codeSent ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (code.trim()) void attempt(() => identity.withEmailCode(code)); }}>\n <Field className=\"min-w-0 flex-1\" inputMode=\"numeric\" autoComplete=\"one-time-code\" placeholder=\"Code from your email\" aria-label=\"Code from your email\" value={code} onChange={(e) => setCode(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !code.trim()}>Confirm</Button>\n </form>\n ) : (\n <Button variant=\"quiet\" disabled={busy} onClick={() => void attempt(async () => { await identity.sendEmailCode(); if (live.current) setCodeSent(true); }, false)}>\n Email me a code instead\n </Button>\n )\n ) : null}\n {error ? <p role=\"alert\" className=\"text-sm text-destructive\">{error}</p> : null}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\n\nimport {\n GlobeIcon,\n ArchiveIcon,\n ArrowUpDownIcon,\n BuildingIcon,\n CameraIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n ClockIcon,\n CodeIcon,\n CreditCardIcon,\n DatabaseIcon,\n DownloadIcon,\n ExternalLinkIcon,\n EyeIcon,\n EyeOffIcon,\n FilePageIcon,\n FileTextIcon,\n FingerprintPatternIcon,\n KeyIcon,\n LayoutGridIcon,\n ListIcon,\n LockIcon,\n MoreHorizontalIcon,\n MoveIcon,\n PencilIcon,\n PlugIcon,\n PlusIcon,\n PowerIcon,\n QrCodeIcon,\n RefreshCwIcon,\n RotateCcwIcon,\n SendIcon,\n ServerIcon,\n ShareIcon,\n ShieldIcon,\n SlidersVerticalIcon,\n StarIcon,\n TagIcon,\n TerminalIcon,\n TrashIcon,\n UploadIcon,\n UserIcon,\n UserRoundIcon,\n XIcon,\n withGlyphDefaults,\n} from \"@ai-matrx/design-system/icons\";\n\n/**\n * The vault's glyphs. Every lucide drawing lives once, in `@ai-matrx/design-system/icons` (the one lucide-free\n * glyph home, which also carries the optical-trim class); each name here is an alias of it. `SafeGlyph` is the\n * vault's own drawing (a strongbox: no lucide twin) and is the only one drawn in this file.\n */\nexport type Glyph = (props: { className?: string }) => ReactNode;\n\n/**\n * `air` = the empty 24-grid units left and right of the drawn ink (stroke included), measured with\n * getBBox. The design system's optical trim reads it (`--matrx-glyph-air-l/-r`) so an icon + label\n * button sits its INK, not its box, the same distance from both edges — exactly as it does for lucide.\n */\nfunction glyph(paths: ReactNode, air: readonly [number, number] = [1, 1]): Glyph {\n const style = { \"--matrx-glyph-air-l\": air[0], \"--matrx-glyph-air-r\": air[1] } as CSSProperties;\n return function VaultGlyph({ className }) {\n return (\n <svg className={className} style={style} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {paths}\n </svg>\n );\n };\n}\n\nexport const KeyGlyph: Glyph = withGlyphDefaults(KeyIcon, {}, { air: [1, 2] });\nexport const NoteGlyph: Glyph = withGlyphDefaults(FileTextIcon, {}, { air: [3, 3] });\nexport const CardGlyph: Glyph = withGlyphDefaults(CreditCardIcon, {}, { air: [1, 1] });\nexport const PersonGlyph: Glyph = withGlyphDefaults(UserRoundIcon, {}, { air: [3, 3] });\nexport const DatabaseGlyph: Glyph = withGlyphDefaults(DatabaseIcon, {}, { air: [2, 2] });\nexport const ServerGlyph: Glyph = withGlyphDefaults(ServerIcon, {}, { air: [1, 1] });\nexport const TerminalGlyph: Glyph = withGlyphDefaults(TerminalIcon, {}, { air: [3, 3] });\nexport const FileGlyph: Glyph = withGlyphDefaults(FilePageIcon, {}, { air: [3, 3] });\nexport const CodeGlyph: Glyph = withGlyphDefaults(CodeIcon, {}, { air: [1, 1] });\nexport const StarGlyph: Glyph = withGlyphDefaults(StarIcon, {}, { air: [1, 1] });\nexport const PlusGlyph: Glyph = withGlyphDefaults(PlusIcon, {}, { air: [4, 4] });\nexport const EyeGlyph: Glyph = withGlyphDefaults(EyeIcon, {}, { air: [1, 1] });\nexport const EyeOffGlyph: Glyph = withGlyphDefaults(EyeOffIcon, {}, { air: [1, 1] });\nexport const LockGlyph: Glyph = withGlyphDefaults(LockIcon, {}, { air: [2, 2] });\nexport const ExternalGlyph: Glyph = withGlyphDefaults(ExternalLinkIcon, {}, { air: [2, 2] });\nexport const TrashGlyph: Glyph = withGlyphDefaults(TrashIcon, {}, { air: [2, 2] });\nexport const ArchiveGlyph: Glyph = withGlyphDefaults(ArchiveIcon, {}, { air: [1, 1] });\nexport const TagGlyph: Glyph = withGlyphDefaults(TagIcon, {}, { air: [1, 1] });\nexport const ShieldGlyph: Glyph = withGlyphDefaults(ShieldIcon, {}, { air: [3, 3] });\nexport const BuildingGlyph: Glyph = withGlyphDefaults(BuildingIcon, {}, { air: [3, 3] });\nexport const ChevronLeftGlyph: Glyph = withGlyphDefaults(ChevronLeftIcon, {}, { air: [8, 8] });\nexport const GridGlyph: Glyph = withGlyphDefaults(LayoutGridIcon, {}, { air: [2, 2] });\nexport const PlugGlyph: Glyph = withGlyphDefaults(PlugIcon, {}, { air: [5, 5] });\n\nconst FAMILY_GLYPHS: Record<string, Glyph> = {\n databases: DatabaseGlyph,\n server_network: ServerGlyph,\n hosting_deployment: ServerGlyph,\n source_control: CodeGlyph,\n signing_files: FileGlyph,\n identity_security: ShieldGlyph,\n cloud_infrastructure: ServerGlyph,\n};\n\n/** A type's glyph: logins are a globe, then by family, else a key. */\nexport function definitionGlyph(key: string, family: string): Glyph {\n if (key === \"username_password\" || key === \"website_login\") return GlobeIcon;\n return FAMILY_GLYPHS[family] ?? KeyGlyph;\n}\nexport const XGlyph: Glyph = withGlyphDefaults(XIcon, {}, { air: [5, 5] });\nexport const PencilGlyph: Glyph = withGlyphDefaults(PencilIcon, {}, { air: [2, 2] });\nexport const ClockGlyph: Glyph = withGlyphDefaults(ClockIcon, {}, { air: [1, 1] });\n// ── core workspace glyphs ────────────────────────────────────────────────────\nexport const MoreGlyph: Glyph = withGlyphDefaults(MoreHorizontalIcon, {}, { air: [3, 3] });\nexport const ShareGlyph: Glyph = withGlyphDefaults(ShareIcon, {}, { air: [3, 3] });\nexport const ListGlyph: Glyph = withGlyphDefaults(ListIcon, {}, { air: [2, 2] });\nexport const ImportGlyph: Glyph = withGlyphDefaults(DownloadIcon, {}, { air: [2, 2] });\nexport const ExportGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\nexport const ChevronDownGlyph: Glyph = withGlyphDefaults(ChevronDownIcon, {}, { air: [5, 5] });\nexport const ChevronRightGlyph: Glyph = withGlyphDefaults(ChevronRightIcon, {}, { air: [8, 8] });\nexport const SortGlyph: Glyph = withGlyphDefaults(ArrowUpDownIcon, {}, { air: [2, 2] });\nexport const UserGlyph: Glyph = withGlyphDefaults(UserIcon, {}, { air: [5, 5] });\nexport const MoveGlyph: Glyph = withGlyphDefaults(MoveIcon, {}, { air: [1, 1] });\nexport const SafeGlyph = glyph(<><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" /><circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 8v1M12 15v1M8 12h1M15 12h1\" /></>, [2, 2]);\nexport const RestoreGlyph: Glyph = withGlyphDefaults(RotateCcwIcon, {}, { air: [2, 2] });\n// ── one-time passwords ───────────────────────────────────────────────────────\nexport const QrGlyph: Glyph = withGlyphDefaults(QrCodeIcon, {}, { air: [2, 2] });\nexport const CameraGlyph: Glyph = withGlyphDefaults(CameraIcon, {}, { air: [1, 1] });\nexport const CheckGlyph: Glyph = withGlyphDefaults(CheckIcon, {}, { air: [3, 3] });\nexport const PowerGlyph: Glyph = withGlyphDefaults(PowerIcon, {}, { air: [2, 2] });\n// ── generator and field settings ─────────────────────────────────────────────\nexport const RefreshGlyph: Glyph = withGlyphDefaults(RefreshCwIcon, {}, { air: [2, 2] });\nexport const SlidersGlyph: Glyph = withGlyphDefaults(SlidersVerticalIcon, {}, { air: [0, 0] });\nexport const UploadGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\n// ── browsers that can fill ───────────────────────────────────────────────────\nexport const FingerprintGlyph: Glyph = withGlyphDefaults(FingerprintPatternIcon, {}, { air: [1, 1] });\nexport const SendGlyph: Glyph = withGlyphDefaults(SendIcon, {}, { air: [1, 1] });\n"],"mappings":"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","names":["createContext","useContext","useEffect","useRef","useState","jsx","jsxs","jsx","jsxs","Ctx","createContext","useContext","useState","useRef","useEffect"]}
package/dist/identity.cjs CHANGED
@@ -222,6 +222,7 @@ function IdentityGate({ identity, children }) {
222
222
  setAsking(false);
223
223
  return () => {
224
224
  activeIdentity.current = null;
225
+ identity?.cancel?.();
225
226
  for (const waiter of waiters.current.splice(0)) waiter.reject(new Error(CANCELLED));
226
227
  };
227
228
  }, [identityVersion]);
@@ -270,6 +271,7 @@ function IdentityGate({ identity, children }) {
270
271
  };
271
272
  const settle = (ok) => {
272
273
  if (activeIdentity.current !== identityVersion) return;
274
+ if (!ok) identity?.cancel?.();
273
275
  const w = waiters.current;
274
276
  waiters.current = [];
275
277
  if (ok) confirmationVersion.current += 1;
@@ -279,7 +281,7 @@ function IdentityGate({ identity, children }) {
279
281
  };
280
282
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Ctx2.Provider, { value: confirmed, children: [
281
283
  children,
282
- identity ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ConfirmIdentityDialog, { open: asking, identity, onDone: () => settle(true), onCancel: () => settle(false) }, identityVersion.key) : null,
284
+ identity && asking ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ConfirmIdentityDialog, { open: true, identity, onDone: () => settle(true), onCancel: () => settle(false) }, identityVersion.key) : null,
283
285
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Dialog, { open: returnProblem !== null, onOpenChange: (o) => o ? void 0 : setReturnProblem(null), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.DialogContent, { size: "sm", children: [
284
286
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.DialogHeader, { children: [
285
287
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.DialogTitle, { children: "Different account" }),
@@ -291,6 +293,16 @@ function IdentityGate({ identity, children }) {
291
293
  }
292
294
  var wasCancelled = (e) => e instanceof Error && e.message === CANCELLED;
293
295
  function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
296
+ const live = (0, import_react2.useRef)(false);
297
+ (0, import_react2.useLayoutEffect)(() => {
298
+ live.current = true;
299
+ return () => {
300
+ live.current = false;
301
+ };
302
+ }, []);
303
+ const finish = () => {
304
+ if (live.current) onDone();
305
+ };
294
306
  const [account, setAccount] = (0, import_react2.useState)(null);
295
307
  const [password, setPassword] = (0, import_react2.useState)("");
296
308
  const [code, setCode] = (0, import_react2.useState)("");
@@ -303,18 +315,29 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
303
315
  setCode("");
304
316
  setCodeSent(false);
305
317
  setError(null);
306
- identity.account().then(setAccount, (e) => setError(e instanceof Error ? e.message : "Your account could not be read."));
318
+ let current = true;
319
+ identity.account().then(
320
+ (value) => {
321
+ if (current && live.current) setAccount(value);
322
+ },
323
+ (e) => {
324
+ if (current && live.current) setError(e instanceof Error ? e.message : "Your account could not be read.");
325
+ }
326
+ );
327
+ return () => {
328
+ current = false;
329
+ };
307
330
  }, [open, identity]);
308
331
  const attempt = async (step, done = true) => {
309
332
  setBusy(true);
310
333
  setError(null);
311
334
  try {
312
335
  await step();
313
- if (done) onDone();
336
+ if (done) finish();
314
337
  } catch (e) {
315
- setError(e instanceof Error ? e.message : "That did not work. Try again.");
338
+ if (live.current) setError(e instanceof Error ? e.message : "That did not work. Try again.");
316
339
  } finally {
317
- setBusy(false);
340
+ if (live.current) setBusy(false);
318
341
  }
319
342
  };
320
343
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.Dialog, { open, onOpenChange: (o) => o ? void 0 : onCancel(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.DialogContent, { size: "sm", children: [
@@ -337,7 +360,7 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
337
360
  variant: identity.dev ? "outline" : "primary",
338
361
  disabled: busy,
339
362
  onClick: () => void attempt(async () => {
340
- if (await identity.withProvider(p) === "confirmed") onDone();
363
+ if (await identity.withProvider(p) === "confirmed") finish();
341
364
  }, false),
342
365
  children: [
343
366
  "Continue with ",
@@ -361,7 +384,7 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
361
384
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls.Button, { variant: "outline", type: "submit", disabled: busy || !code.trim(), children: "Confirm" })
362
385
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls.Button, { variant: "quiet", disabled: busy, onClick: () => void attempt(async () => {
363
386
  await identity.sendEmailCode();
364
- setCodeSent(true);
387
+ if (live.current) setCodeSent(true);
365
388
  }, false), children: "Email me a code instead" }) : null,
366
389
  error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null
367
390
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/identity.ts","../src/auth/identity.ts","../src/auth/IdentityGate.tsx","../src/glyphs.tsx","../src/notice.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * `@ai-matrx/vault/identity` — \"Confirm it's you\" without the Vault workspace.\n *\n * The identity seam and its gate dialog, for a host that only needs a recent-sign-in check (the\n * share sheet's secure links). Importing them from the root pulled the whole Vault (every screen,\n * the importers, zip.js) into that host's build graph; this entry reaches only the gate.\n */\nexport { CONFIRM_RETURN_KEY, createSupabaseIdentity, needsRecentSignIn, type IdentityAccount, type IdentityMethod, type ReturnStore, type SupabaseAuthLike, type SupabaseIdentityOptions, type VaultIdentity } from \"./auth/identity\";\nexport { IdentityGate, useConfirmed, wasCancelled, type Confirmed } from \"./auth/IdentityGate\";\n","/**\n * CONFIRM IT'S YOU. The server shows or exports a secret only after a sign-in from the last few\n * minutes (`recent_auth_required`). Every host signs people in with Supabase, so the one way to\n * produce a fresh sign-in lives here, over the host's own auth client — whichever way the person\n * signs in: Google (or any connected provider), a password, or a code sent to their email.\n */\n\n/** How a person can prove it is them again. Ordered by what the account actually uses. */\nexport type IdentityMethod = \"provider\" | \"password\" | \"email_code\" | \"dev\";\n\nexport interface IdentityAccount {\n email: string | null;\n /** Connected sign-in providers on the account (`google`, `github`, …), never `email`. */\n providers: readonly string[];\n hasPassword: boolean;\n}\n\nexport interface VaultIdentity {\n account(): Promise<IdentityAccount>;\n /**\n * Confirms with that provider. On the web it leaves the page and comes back to the same address\n * (resolves with nothing: the page is going away). A host that confirms IN PLACE (the desktop's\n * Touch ID, a browser sign-in it waits for) resolves `\"confirmed\"`, and the gate closes and runs\n * the refused call again — one confirmation, never a second click.\n */\n withProvider(provider: string): Promise<void | \"confirmed\">;\n withPassword(password: string): Promise<void>;\n sendEmailCode(): Promise<void>;\n withEmailCode(code: string): Promise<void>;\n /** A host-only path with no human step (the playground's dev server). */\n dev?: () => Promise<void>;\n /**\n * Run once when the page loads. After a provider confirmation left the page and came back, it\n * checks the SAME person is signed in. A different account is signed out of this device and the\n * sentence to show is returned; otherwise null.\n */\n checkReturn?: () => Promise<string | null>;\n}\n\n/** The slice of supabase-js `auth` this needs, so the package never depends on supabase-js itself. */\nexport interface SupabaseAuthLike {\n getUser(): Promise<{ data: { user: { id: string; email?: string | null; identities?: { provider: string }[] | null; app_metadata?: { providers?: string[] } } | null }; error: unknown }>;\n signInWithPassword(c: { email: string; password: string }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOtp(c: { email: string; options?: { shouldCreateUser?: boolean } }): Promise<{ error: { message: string } | null }>;\n verifyOtp(c: { email: string; token: string; type: \"email\" }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOAuth(c: { provider: never; options?: { redirectTo?: string } }): Promise<{ error: { message: string } | null }>;\n /** Used only when the host gives no `onDifferentPerson`: ends THIS device's session, never others. */\n signOut?(o: { scope: \"local\" }): Promise<{ error: unknown }>;\n}\n\n/** Where the person confirming is remembered while the page is away at the provider. */\nexport interface ReturnStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface SupabaseIdentityOptions {\n /** The host's accepted OAuth return address carrying this page. */\n redirectTo?: () => string;\n dev?: () => Promise<void>;\n /**\n * A provider confirmation came back signed in as someone else. The host ends that session on THIS\n * device only (its own device-scoped sign-out) and tells the person; `message` says what happened.\n * Without it the package signs out locally through `auth.signOut({ scope: \"local\" })`.\n */\n onDifferentPerson?: (message: string) => Promise<void> | void;\n /** Default: the tab's `sessionStorage`. */\n store?: ReturnStore;\n /** Default `Date.now`. */\n now?: () => number;\n}\n\n/** The key the expected person is kept under across the provider's redirect. */\nexport const CONFIRM_RETURN_KEY = \"matrx-vault:confirming-as\";\n/** A remembered confirmation older than this is stale (the person never came back from it). */\nconst RETURN_WINDOW_MS = 15 * 60_000;\n\nconst SAME_PERSON = \"That signed in a different account. Use the account this vault belongs to.\";\n\n/** THE identity confirmation for every host: give it the host's `supabase.auth`. */\nexport function createSupabaseIdentity(auth: SupabaseAuthLike, options: SupabaseIdentityOptions = {}): VaultIdentity {\n let me: { id: string; email: string | null } | null = null;\n const now = options.now ?? Date.now;\n const store = (): ReturnStore | null => {\n if (options.store) return options.store;\n try {\n return (globalThis as { sessionStorage?: ReturnStore }).sessionStorage ?? null;\n } catch {\n return null;\n }\n };\n const who = async () => {\n const { data } = await auth.getUser();\n if (!data.user) throw new Error(\"You are signed out. Sign in again.\");\n me = { id: data.user.id, email: data.user.email ?? null };\n return data.user;\n };\n const sameUser = (id: string | undefined) => {\n if (!me || id !== me.id) throw new Error(SAME_PERSON);\n };\n const identity: VaultIdentity = {\n async account() {\n const u = await who();\n const names = new Set([...(u.identities ?? []).map((i) => i.provider), ...(u.app_metadata?.providers ?? [])]);\n return { email: u.email ?? null, providers: [...names].filter((p) => p !== \"email\" && p !== \"phone\"), hasPassword: names.has(\"email\") };\n },\n async withProvider(provider) {\n const u = await who();\n // Remembered across the redirect, so the page that loads back can check the same person came back.\n store()?.setItem(CONFIRM_RETURN_KEY, JSON.stringify({ id: u.id, email: u.email ?? null, at: now() }));\n const redirectTo = options.redirectTo?.() ?? globalThis.location?.href;\n // `provider` is a supabase-js Provider string; the structural type keeps supabase-js out of the package.\n const { error } = await auth.signInWithOAuth({ provider: provider as never, ...(redirectTo ? { options: { redirectTo } } : {}) });\n if (error) {\n store()?.removeItem(CONFIRM_RETURN_KEY);\n throw new Error(error.message);\n }\n },\n async checkReturn() {\n const s = store();\n const raw = s?.getItem(CONFIRM_RETURN_KEY) ?? null;\n if (!s || raw === null) return null;\n s.removeItem(CONFIRM_RETURN_KEY);\n let expected: { id?: unknown; email?: unknown; at?: unknown };\n try {\n expected = JSON.parse(raw) as typeof expected;\n } catch {\n return null;\n }\n if (typeof expected.id !== \"string\" || typeof expected.at !== \"number\" || now() - expected.at > RETURN_WINDOW_MS) return null;\n const { data } = await auth.getUser();\n if (!data.user || data.user.id === expected.id) return null;\n const email = typeof expected.email === \"string\" && expected.email ? expected.email : null;\n const message = `That signed in a different account, so it was signed out here.${email ? ` Sign in again as ${email}.` : \" Sign in again.\"}`;\n me = null;\n if (options.onDifferentPerson) await options.onDifferentPerson(message);\n else await auth.signOut?.({ scope: \"local\" });\n return message;\n },\n async withPassword(password) {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to confirm with.\");\n const { data, error } = await auth.signInWithPassword({ email: u.email, password });\n if (error) throw new Error(\"That password was not accepted.\");\n sameUser(data.user?.id);\n },\n async sendEmailCode() {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to send a code to.\");\n const { error } = await auth.signInWithOtp({ email: u.email, options: { shouldCreateUser: false } });\n if (error) throw new Error(error.message);\n },\n async withEmailCode(code) {\n const u = me ?? (await who());\n if (!u.email) throw new Error(\"This account has no email.\");\n const { data, error } = await auth.verifyOtp({ email: u.email, token: code.trim(), type: \"email\" });\n if (error) throw new Error(\"That code was not accepted. Check it, or send a new one.\");\n sameUser(data.user?.id);\n },\n };\n if (options.dev) identity.dev = options.dev;\n return identity;\n}\n\n/** True when the server refused because the sign-in is not recent enough. */\nexport function needsRecentSignIn(e: unknown): boolean {\n const err = e as { code?: unknown; status?: unknown; message?: unknown; serverDetail?: unknown } | null;\n if (!err) return false;\n if (err.code === \"recent_auth_required\") return true;\n // Some routes answer with only a sentence (the shown message is a generic \"sign in\"; the server's\n // own words say which): read those too.\n const said = [err.message, JSON.stringify(err.serverDetail ?? \"\")].filter((x): x is string => typeof x === \"string\").join(\" \");\n return /recent auth|recent sign|re-authenticate|authentication within the last/i.test(said);\n}\n","import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@ai-matrx/design-system\";\nimport { Button, Field } from \"@ai-matrx/design-system/controls\";\nimport { ShieldGlyph } from \"../glyphs\";\nimport { useNotify } from \"../notice\";\nimport { needsRecentSignIn, type IdentityAccount, type VaultIdentity } from \"./identity\";\n\n/**\n * Wrap any call that shows, copies or exports a secret. When the server answers \"sign in again\",\n * the person confirms it is them (Google, password, or an emailed code) and the call runs again —\n * they never see a dead \"Authentication required\" in the row.\n */\nexport type Confirmed = <T>(run: () => Promise<T>) => Promise<T>;\n\nconst CANCELLED = \"Not confirmed.\";\n/** Said once when the person closes \"Confirm it's you\": whatever asked for it did not happen. */\nexport const NOT_CONFIRMED_NOTICE = \"Not confirmed, so nothing was done\";\nconst Ctx = createContext<Confirmed>((run) => run());\n\nexport function useConfirmed(): Confirmed {\n return useContext(Ctx);\n}\n\nconst PROVIDER_LABEL: Record<string, string> = { google: \"Google\", github: \"GitHub\", azure: \"Microsoft\", apple: \"Apple\", linkedin_oidc: \"LinkedIn\", slack_oidc: \"Slack\", browser: \"Browser\", touch_id: \"Touch ID\" };\nconst providerLabel = (p: string) => PROVIDER_LABEL[p] ?? p.charAt(0).toUpperCase() + p.slice(1);\n\nexport function IdentityGate({ identity, children }: { identity: VaultIdentity | undefined; children: ReactNode }) {\n const [asking, setAsking] = useState(false);\n const notify = useNotify();\n const waiters = useRef<{ resolve: () => void; reject: (e: Error) => void }[]>([]);\n const confirmationVersion = useRef(0);\n const identityVersion = useMemo(() => ({ key: crypto.randomUUID() }), [identity]);\n const activeIdentity = useRef<object | null>(null);\n useLayoutEffect(() => {\n activeIdentity.current = identityVersion;\n setAsking(false);\n return () => {\n activeIdentity.current = null;\n for (const waiter of waiters.current.splice(0)) waiter.reject(new Error(CANCELLED));\n };\n }, [identityVersion]);\n // Back from a provider's confirmation: a different account that came back is signed out and said.\n const [returnProblem, setReturnProblem] = useState<string | null>(null);\n useEffect(() => {\n if (!identity?.checkReturn) return;\n let live = true;\n identity.checkReturn().then(\n (problem) => live && problem && setReturnProblem(problem),\n (e: unknown) => live && setReturnProblem(e instanceof Error ? e.message : \"The account that came back could not be checked.\"),\n );\n return () => {\n live = false;\n };\n }, [identity]);\n\n const confirmed: Confirmed = async (run) => {\n const startedAtVersion = confirmationVersion.current;\n const checkIdentity = () => {\n if (activeIdentity.current !== identityVersion) throw new Error(CANCELLED);\n };\n const runAsIdentity = async () => {\n checkIdentity();\n const result = await run();\n checkIdentity();\n return result;\n };\n try {\n return await runAsIdentity();\n } catch (e) {\n checkIdentity();\n if (!identity || !needsRecentSignIn(e)) throw e;\n // A response sent with the old bearer can arrive after another call's confirmation.\n // Give the current bearer/grant one try before asking the person to confirm again.\n if (startedAtVersion !== confirmationVersion.current) {\n try {\n return await runAsIdentity();\n } catch (retryError) {\n if (!needsRecentSignIn(retryError)) throw retryError;\n }\n }\n await new Promise<void>((resolve, reject) => {\n waiters.current.push({ resolve, reject });\n setAsking(true);\n });\n checkIdentity();\n return runAsIdentity();\n }\n };\n\n const settle = (ok: boolean) => {\n if (activeIdentity.current !== identityVersion) return;\n const w = waiters.current;\n waiters.current = [];\n if (ok) confirmationVersion.current += 1;\n setAsking(false);\n for (const x of w) (ok ? x.resolve() : x.reject(new Error(CANCELLED)));\n // Never silent: every caller stays quiet about a cancel (`wasCancelled`), so the gate says it once.\n if (!ok && w.length > 0) notify(NOT_CONFIRMED_NOTICE, \"info\");\n };\n\n return (\n <Ctx.Provider value={confirmed}>\n {children}\n {identity ? <ConfirmIdentityDialog key={identityVersion.key} open={asking} identity={identity} onDone={() => settle(true)} onCancel={() => settle(false)} /> : null}\n <Dialog open={returnProblem !== null} onOpenChange={(o) => (o ? undefined : setReturnProblem(null))}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <DialogTitle>Different account</DialogTitle>\n <DialogDescription>{returnProblem}</DialogDescription>\n </DialogHeader>\n <div className=\"flex justify-end\">\n <Button variant=\"primary\" onClick={() => setReturnProblem(null)}>OK</Button>\n </div>\n </DialogContent>\n </Dialog>\n </Ctx.Provider>\n );\n}\n\n/** True for the rejection a canceled confirmation produces (callers stay quiet about it). */\nexport const wasCancelled = (e: unknown) => e instanceof Error && e.message === CANCELLED;\n\nfunction ConfirmIdentityDialog({ open, identity, onDone, onCancel }: { open: boolean; identity: VaultIdentity; onDone: () => void; onCancel: () => void }) {\n const [account, setAccount] = useState<IdentityAccount | null>(null);\n const [password, setPassword] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [codeSent, setCodeSent] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!open) return;\n setPassword(\"\");\n setCode(\"\");\n setCodeSent(false);\n setError(null);\n identity.account().then(setAccount, (e: unknown) => setError(e instanceof Error ? e.message : \"Your account could not be read.\"));\n }, [open, identity]);\n\n const attempt = async (step: () => Promise<void>, done = true) => {\n setBusy(true);\n setError(null);\n try {\n await step();\n if (done) onDone();\n } catch (e) {\n setError(e instanceof Error ? e.message : \"That did not work. Try again.\");\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={(o) => (o ? undefined : onCancel())}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <div className=\"flex items-center gap-2\">\n <ShieldGlyph className=\"size-5 text-primary\" />\n <DialogTitle>Confirm it’s you</DialogTitle>\n </div>\n <DialogDescription>This needs a recent sign-in{account?.email ? ` to ${account.email}` : \"\"}.</DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-3\">\n {identity.dev ? (\n <Button variant=\"primary\" disabled={busy} onClick={() => void attempt(() => identity.dev?.() ?? Promise.resolve())}>\n Sign in again (dev)\n </Button>\n ) : null}\n {account?.providers.map((p) => (\n <Button\n key={p}\n variant={identity.dev ? \"outline\" : \"primary\"}\n disabled={busy}\n onClick={() =>\n void attempt(async () => {\n if ((await identity.withProvider(p)) === \"confirmed\") onDone();\n }, false)\n }\n >\n Continue with {providerLabel(p)}\n </Button>\n ))}\n {account?.hasPassword ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (password) void attempt(() => identity.withPassword(password)); }}>\n <Field className=\"min-w-0 flex-1\" type=\"password\" autoComplete=\"current-password\" placeholder=\"Your password\" aria-label=\"Your password\" value={password} onChange={(e) => setPassword(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !password}>Confirm</Button>\n </form>\n ) : null}\n {account?.email ? (\n codeSent ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (code.trim()) void attempt(() => identity.withEmailCode(code)); }}>\n <Field className=\"min-w-0 flex-1\" inputMode=\"numeric\" autoComplete=\"one-time-code\" placeholder=\"Code from your email\" aria-label=\"Code from your email\" value={code} onChange={(e) => setCode(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !code.trim()}>Confirm</Button>\n </form>\n ) : (\n <Button variant=\"quiet\" disabled={busy} onClick={() => void attempt(async () => { await identity.sendEmailCode(); setCodeSent(true); }, false)}>\n Email me a code instead\n </Button>\n )\n ) : null}\n {error ? <p role=\"alert\" className=\"text-sm text-destructive\">{error}</p> : null}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\n\nimport {\n GlobeIcon,\n ArchiveIcon,\n ArrowUpDownIcon,\n BuildingIcon,\n CameraIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n ClockIcon,\n CodeIcon,\n CreditCardIcon,\n DatabaseIcon,\n DownloadIcon,\n ExternalLinkIcon,\n EyeIcon,\n EyeOffIcon,\n FilePageIcon,\n FileTextIcon,\n FingerprintPatternIcon,\n KeyIcon,\n LayoutGridIcon,\n ListIcon,\n LockIcon,\n MoreHorizontalIcon,\n MoveIcon,\n PencilIcon,\n PlugIcon,\n PlusIcon,\n PowerIcon,\n QrCodeIcon,\n RefreshCwIcon,\n RotateCcwIcon,\n SendIcon,\n ServerIcon,\n ShareIcon,\n ShieldIcon,\n SlidersVerticalIcon,\n StarIcon,\n TagIcon,\n TerminalIcon,\n TrashIcon,\n UploadIcon,\n UserIcon,\n UserRoundIcon,\n XIcon,\n withGlyphDefaults,\n} from \"@ai-matrx/design-system/icons\";\n\n/**\n * The vault's glyphs. Every lucide drawing lives once, in `@ai-matrx/design-system/icons` (the one lucide-free\n * glyph home, which also carries the optical-trim class); each name here is an alias of it. `SafeGlyph` is the\n * vault's own drawing (a strongbox: no lucide twin) and is the only one drawn in this file.\n */\nexport type Glyph = (props: { className?: string }) => ReactNode;\n\n/**\n * `air` = the empty 24-grid units left and right of the drawn ink (stroke included), measured with\n * getBBox. The design system's optical trim reads it (`--matrx-glyph-air-l/-r`) so an icon + label\n * button sits its INK, not its box, the same distance from both edges — exactly as it does for lucide.\n */\nfunction glyph(paths: ReactNode, air: readonly [number, number] = [1, 1]): Glyph {\n const style = { \"--matrx-glyph-air-l\": air[0], \"--matrx-glyph-air-r\": air[1] } as CSSProperties;\n return function VaultGlyph({ className }) {\n return (\n <svg className={className} style={style} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {paths}\n </svg>\n );\n };\n}\n\nexport const KeyGlyph: Glyph = withGlyphDefaults(KeyIcon, {}, { air: [1, 2] });\nexport const NoteGlyph: Glyph = withGlyphDefaults(FileTextIcon, {}, { air: [3, 3] });\nexport const CardGlyph: Glyph = withGlyphDefaults(CreditCardIcon, {}, { air: [1, 1] });\nexport const PersonGlyph: Glyph = withGlyphDefaults(UserRoundIcon, {}, { air: [3, 3] });\nexport const DatabaseGlyph: Glyph = withGlyphDefaults(DatabaseIcon, {}, { air: [2, 2] });\nexport const ServerGlyph: Glyph = withGlyphDefaults(ServerIcon, {}, { air: [1, 1] });\nexport const TerminalGlyph: Glyph = withGlyphDefaults(TerminalIcon, {}, { air: [3, 3] });\nexport const FileGlyph: Glyph = withGlyphDefaults(FilePageIcon, {}, { air: [3, 3] });\nexport const CodeGlyph: Glyph = withGlyphDefaults(CodeIcon, {}, { air: [1, 1] });\nexport const StarGlyph: Glyph = withGlyphDefaults(StarIcon, {}, { air: [1, 1] });\nexport const PlusGlyph: Glyph = withGlyphDefaults(PlusIcon, {}, { air: [4, 4] });\nexport const EyeGlyph: Glyph = withGlyphDefaults(EyeIcon, {}, { air: [1, 1] });\nexport const EyeOffGlyph: Glyph = withGlyphDefaults(EyeOffIcon, {}, { air: [1, 1] });\nexport const LockGlyph: Glyph = withGlyphDefaults(LockIcon, {}, { air: [2, 2] });\nexport const ExternalGlyph: Glyph = withGlyphDefaults(ExternalLinkIcon, {}, { air: [2, 2] });\nexport const TrashGlyph: Glyph = withGlyphDefaults(TrashIcon, {}, { air: [2, 2] });\nexport const ArchiveGlyph: Glyph = withGlyphDefaults(ArchiveIcon, {}, { air: [1, 1] });\nexport const TagGlyph: Glyph = withGlyphDefaults(TagIcon, {}, { air: [1, 1] });\nexport const ShieldGlyph: Glyph = withGlyphDefaults(ShieldIcon, {}, { air: [3, 3] });\nexport const BuildingGlyph: Glyph = withGlyphDefaults(BuildingIcon, {}, { air: [3, 3] });\nexport const ChevronLeftGlyph: Glyph = withGlyphDefaults(ChevronLeftIcon, {}, { air: [8, 8] });\nexport const GridGlyph: Glyph = withGlyphDefaults(LayoutGridIcon, {}, { air: [2, 2] });\nexport const PlugGlyph: Glyph = withGlyphDefaults(PlugIcon, {}, { air: [5, 5] });\n\nconst FAMILY_GLYPHS: Record<string, Glyph> = {\n databases: DatabaseGlyph,\n server_network: ServerGlyph,\n hosting_deployment: ServerGlyph,\n source_control: CodeGlyph,\n signing_files: FileGlyph,\n identity_security: ShieldGlyph,\n cloud_infrastructure: ServerGlyph,\n};\n\n/** A type's glyph: logins are a globe, then by family, else a key. */\nexport function definitionGlyph(key: string, family: string): Glyph {\n if (key === \"username_password\" || key === \"website_login\") return GlobeIcon;\n return FAMILY_GLYPHS[family] ?? KeyGlyph;\n}\nexport const XGlyph: Glyph = withGlyphDefaults(XIcon, {}, { air: [5, 5] });\nexport const PencilGlyph: Glyph = withGlyphDefaults(PencilIcon, {}, { air: [2, 2] });\nexport const ClockGlyph: Glyph = withGlyphDefaults(ClockIcon, {}, { air: [1, 1] });\n// ── core workspace glyphs ────────────────────────────────────────────────────\nexport const MoreGlyph: Glyph = withGlyphDefaults(MoreHorizontalIcon, {}, { air: [3, 3] });\nexport const ShareGlyph: Glyph = withGlyphDefaults(ShareIcon, {}, { air: [3, 3] });\nexport const ListGlyph: Glyph = withGlyphDefaults(ListIcon, {}, { air: [2, 2] });\nexport const ImportGlyph: Glyph = withGlyphDefaults(DownloadIcon, {}, { air: [2, 2] });\nexport const ExportGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\nexport const ChevronDownGlyph: Glyph = withGlyphDefaults(ChevronDownIcon, {}, { air: [5, 5] });\nexport const ChevronRightGlyph: Glyph = withGlyphDefaults(ChevronRightIcon, {}, { air: [8, 8] });\nexport const SortGlyph: Glyph = withGlyphDefaults(ArrowUpDownIcon, {}, { air: [2, 2] });\nexport const UserGlyph: Glyph = withGlyphDefaults(UserIcon, {}, { air: [5, 5] });\nexport const MoveGlyph: Glyph = withGlyphDefaults(MoveIcon, {}, { air: [1, 1] });\nexport const SafeGlyph = glyph(<><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" /><circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 8v1M12 15v1M8 12h1M15 12h1\" /></>, [2, 2]);\nexport const RestoreGlyph: Glyph = withGlyphDefaults(RotateCcwIcon, {}, { air: [2, 2] });\n// ── one-time passwords ───────────────────────────────────────────────────────\nexport const QrGlyph: Glyph = withGlyphDefaults(QrCodeIcon, {}, { air: [2, 2] });\nexport const CameraGlyph: Glyph = withGlyphDefaults(CameraIcon, {}, { air: [1, 1] });\nexport const CheckGlyph: Glyph = withGlyphDefaults(CheckIcon, {}, { air: [3, 3] });\nexport const PowerGlyph: Glyph = withGlyphDefaults(PowerIcon, {}, { air: [2, 2] });\n// ── generator and field settings ─────────────────────────────────────────────\nexport const RefreshGlyph: Glyph = withGlyphDefaults(RefreshCwIcon, {}, { air: [2, 2] });\nexport const SlidersGlyph: Glyph = withGlyphDefaults(SlidersVerticalIcon, {}, { air: [0, 0] });\nexport const UploadGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\n// ── browsers that can fill ───────────────────────────────────────────────────\nexport const FingerprintGlyph: Glyph = withGlyphDefaults(FingerprintPatternIcon, {}, { air: [1, 1] });\nexport const SendGlyph: Glyph = withGlyphDefaults(SendIcon, {}, { air: [1, 1] });\n","import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"@ai-matrx/design-system\";\nimport { copyText } from \"@ai-matrx/kit/clipboard\";\nimport type { VaultClipboard } from \"./lock\";\n\nexport type NoticeKind = \"success\" | \"error\" | \"info\";\nexport type Notify = (message: string, kind?: NoticeKind) => void;\n\nconst Ctx = createContext<Notify>(() => undefined);\n\n/** Say something short to the person (\"Password copied\"). Hosts with their own toasts pass `notify`. */\nexport function useNotify(): Notify {\n return useContext(Ctx);\n}\n\nexport interface CopyOptions {\n /** A secret (a password, a code, a key): the host's clipboard takes custody. Default true. */\n secret?: boolean;\n}\n\n/**\n * Writes `text` and says so (or says why not). Through the host's clipboard when it has one (custody:\n * the host clears a secret later), else the browser's. Pure over its inputs, so it is tested alone.\n */\nexport async function copyValue(text: string, label: string, options: CopyOptions, deps: { clipboard?: VaultClipboard | undefined; notify: Notify }): Promise<boolean> {\n const secret = options.secret ?? true;\n if (deps.clipboard) {\n try {\n const copied = await deps.clipboard.copy(text, { secret });\n deps.notify(copiedLine(label, copied?.clearsAtMs ?? null), \"success\");\n return true;\n } catch (e) {\n deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, \"error\");\n return false;\n }\n }\n return copyText(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => deps.notify(m, k === \"error\" ? \"error\" : \"success\") });\n}\n\n/** \"Password copied\", or \"Password copied. Clears at 3:42 PM\" when the host says when it clears it. */\nexport function copiedLine(label: string, clearsAtMs: number | null): string {\n if (clearsAtMs === null || !Number.isFinite(clearsAtMs)) return `${label} copied`;\n const at = new Date(clearsAtMs).toLocaleTimeString(undefined, { hour: \"numeric\", minute: \"2-digit\" });\n return `${label} copied. Clears at ${at}`;\n}\n\nconst ClipboardCtx = createContext<VaultClipboard | undefined>(undefined);\n\n/** The host's clipboard (`host.clipboard`), for every copy below it. */\nexport function ClipboardProvider({ clipboard, children }: { clipboard: VaultClipboard | undefined; children: ReactNode }) {\n return <ClipboardCtx.Provider value={clipboard}>{children}</ClipboardCtx.Provider>;\n}\n\n/** THE copy for every vault screen: writes, then says so (or says why not). */\nexport function useCopy(): (text: string, label: string, options?: CopyOptions) => Promise<boolean> {\n const notify = useNotify();\n const clipboard = useContext(ClipboardCtx);\n return useCallback((text, label, options = {}) => copyValue(text, label, options, { clipboard, notify }), [notify, clipboard]);\n}\n\n/**\n * The vault's own notice line, so feedback exists in every host (an extension side panel has no app\n * toaster). A host that has one passes `notify` and this draws nothing.\n */\nexport function NoticeProvider({ notify, children }: { notify?: Notify | undefined; children: ReactNode }) {\n const [shown, setShown] = useState<{ message: string; kind: NoticeKind; n: number } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const local = useCallback<Notify>((message, kind = \"success\") => {\n if (timer.current) clearTimeout(timer.current);\n setShown((s) => ({ message, kind, n: (s?.n ?? 0) + 1 }));\n timer.current = setTimeout(() => setShown(null), kind === \"error\" ? 5000 : 2200);\n }, []);\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), []);\n return (\n <Ctx.Provider value={notify ?? local}>\n {children}\n {!notify ? (\n <div aria-live=\"polite\" className=\"pointer-events-none absolute inset-x-0 bottom-4 z-30 flex justify-center px-4\">\n {shown ? (\n <div key={shown.n} role=\"status\" className={cn(\"rounded-full border border-border bg-popover px-4 py-2 text-sm shadow-lg\", shown.kind === \"error\" ? \"text-destructive\" : \"text-foreground\")}>\n {shown.message}\n </div>\n ) : null}\n </div>\n ) : null}\n </Ctx.Provider>\n );\n}\n"],"mappings":"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","names":["import_react","import_design_system","import_jsx_runtime","import_jsx_runtime","Ctx"]}
1
+ {"version":3,"sources":["../src/identity.ts","../src/auth/identity.ts","../src/auth/IdentityGate.tsx","../src/glyphs.tsx","../src/notice.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * `@ai-matrx/vault/identity` — \"Confirm it's you\" without the Vault workspace.\n *\n * The identity seam and its gate dialog, for a host that only needs a recent-sign-in check (the\n * share sheet's secure links). Importing them from the root pulled the whole Vault (every screen,\n * the importers, zip.js) into that host's build graph; this entry reaches only the gate.\n */\nexport { CONFIRM_RETURN_KEY, createSupabaseIdentity, needsRecentSignIn, type IdentityAccount, type IdentityMethod, type ReturnStore, type SupabaseAuthLike, type SupabaseIdentityOptions, type VaultIdentity } from \"./auth/identity\";\nexport { IdentityGate, useConfirmed, wasCancelled, type Confirmed } from \"./auth/IdentityGate\";\n","/**\n * CONFIRM IT'S YOU. The server shows or exports a secret only after a sign-in from the last few\n * minutes (`recent_auth_required`). Every host signs people in with Supabase, so the one way to\n * produce a fresh sign-in lives here, over the host's own auth client — whichever way the person\n * signs in: Google (or any connected provider), a password, or a code sent to their email.\n */\n\n/** How a person can prove it is them again. Ordered by what the account actually uses. */\nexport type IdentityMethod = \"provider\" | \"password\" | \"email_code\" | \"dev\";\n\nexport interface IdentityAccount {\n email: string | null;\n /** Connected sign-in providers on the account (`google`, `github`, …), never `email`. */\n providers: readonly string[];\n hasPassword: boolean;\n}\n\nexport interface VaultIdentity {\n account(): Promise<IdentityAccount>;\n /** Synchronously invalidate an unfinished attempt on dismissal or identity disposal.\n * Late work cleans up its own resources and cannot confirm a newer attempt.\n * A completed confirmation remains reusable across ordinary workspace navigation. */\n cancel?: () => void;\n /**\n * Confirms with that provider. On the web it leaves the page and comes back to the same address\n * (resolves with nothing: the page is going away). A host that confirms IN PLACE (the desktop's\n * Touch ID, a browser sign-in it waits for) resolves `\"confirmed\"`, and the gate closes and runs\n * the refused call again — one confirmation, never a second click.\n */\n withProvider(provider: string): Promise<void | \"confirmed\">;\n withPassword(password: string): Promise<void>;\n sendEmailCode(): Promise<void>;\n withEmailCode(code: string): Promise<void>;\n /** A host-only path with no human step (the playground's dev server). */\n dev?: () => Promise<void>;\n /**\n * Run once when the page loads. After a provider confirmation left the page and came back, it\n * checks the SAME person is signed in. A different account is signed out of this device and the\n * sentence to show is returned; otherwise null.\n */\n checkReturn?: () => Promise<string | null>;\n}\n\n/** The slice of supabase-js `auth` this needs, so the package never depends on supabase-js itself. */\nexport interface SupabaseAuthLike {\n getUser(): Promise<{ data: { user: { id: string; email?: string | null; identities?: { provider: string }[] | null; app_metadata?: { providers?: string[] } } | null }; error: unknown }>;\n signInWithPassword(c: { email: string; password: string }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOtp(c: { email: string; options?: { shouldCreateUser?: boolean } }): Promise<{ error: { message: string } | null }>;\n verifyOtp(c: { email: string; token: string; type: \"email\" }): Promise<{ data: { user: { id: string } | null }; error: { message: string } | null }>;\n signInWithOAuth(c: { provider: never; options?: { redirectTo?: string } }): Promise<{ error: { message: string } | null }>;\n /** Used only when the host gives no `onDifferentPerson`: ends THIS device's session, never others. */\n signOut?(o: { scope: \"local\" }): Promise<{ error: unknown }>;\n}\n\n/** Where the person confirming is remembered while the page is away at the provider. */\nexport interface ReturnStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface SupabaseIdentityOptions {\n /** The host's accepted OAuth return address carrying this page. */\n redirectTo?: () => string;\n dev?: () => Promise<void>;\n /**\n * A provider confirmation came back signed in as someone else. The host ends that session on THIS\n * device only (its own device-scoped sign-out) and tells the person; `message` says what happened.\n * Without it the package signs out locally through `auth.signOut({ scope: \"local\" })`.\n */\n onDifferentPerson?: (message: string) => Promise<void> | void;\n /** Default: the tab's `sessionStorage`. */\n store?: ReturnStore;\n /** Default `Date.now`. */\n now?: () => number;\n}\n\n/** The key the expected person is kept under across the provider's redirect. */\nexport const CONFIRM_RETURN_KEY = \"matrx-vault:confirming-as\";\n/** A remembered confirmation older than this is stale (the person never came back from it). */\nconst RETURN_WINDOW_MS = 15 * 60_000;\n\nconst SAME_PERSON = \"That signed in a different account. Use the account this vault belongs to.\";\n\n/** THE identity confirmation for every host: give it the host's `supabase.auth`. */\nexport function createSupabaseIdentity(auth: SupabaseAuthLike, options: SupabaseIdentityOptions = {}): VaultIdentity {\n let me: { id: string; email: string | null } | null = null;\n const now = options.now ?? Date.now;\n const store = (): ReturnStore | null => {\n if (options.store) return options.store;\n try {\n return (globalThis as { sessionStorage?: ReturnStore }).sessionStorage ?? null;\n } catch {\n return null;\n }\n };\n const who = async () => {\n const { data } = await auth.getUser();\n if (!data.user) throw new Error(\"You are signed out. Sign in again.\");\n me = { id: data.user.id, email: data.user.email ?? null };\n return data.user;\n };\n const sameUser = (id: string | undefined) => {\n if (!me || id !== me.id) throw new Error(SAME_PERSON);\n };\n const identity: VaultIdentity = {\n async account() {\n const u = await who();\n const names = new Set([...(u.identities ?? []).map((i) => i.provider), ...(u.app_metadata?.providers ?? [])]);\n return { email: u.email ?? null, providers: [...names].filter((p) => p !== \"email\" && p !== \"phone\"), hasPassword: names.has(\"email\") };\n },\n async withProvider(provider) {\n const u = await who();\n // Remembered across the redirect, so the page that loads back can check the same person came back.\n store()?.setItem(CONFIRM_RETURN_KEY, JSON.stringify({ id: u.id, email: u.email ?? null, at: now() }));\n const redirectTo = options.redirectTo?.() ?? globalThis.location?.href;\n // `provider` is a supabase-js Provider string; the structural type keeps supabase-js out of the package.\n const { error } = await auth.signInWithOAuth({ provider: provider as never, ...(redirectTo ? { options: { redirectTo } } : {}) });\n if (error) {\n store()?.removeItem(CONFIRM_RETURN_KEY);\n throw new Error(error.message);\n }\n },\n async checkReturn() {\n const s = store();\n const raw = s?.getItem(CONFIRM_RETURN_KEY) ?? null;\n if (!s || raw === null) return null;\n s.removeItem(CONFIRM_RETURN_KEY);\n let expected: { id?: unknown; email?: unknown; at?: unknown };\n try {\n expected = JSON.parse(raw) as typeof expected;\n } catch {\n return null;\n }\n if (typeof expected.id !== \"string\" || typeof expected.at !== \"number\" || now() - expected.at > RETURN_WINDOW_MS) return null;\n const { data } = await auth.getUser();\n if (!data.user || data.user.id === expected.id) return null;\n const email = typeof expected.email === \"string\" && expected.email ? expected.email : null;\n const message = `That signed in a different account, so it was signed out here.${email ? ` Sign in again as ${email}.` : \" Sign in again.\"}`;\n me = null;\n if (options.onDifferentPerson) await options.onDifferentPerson(message);\n else await auth.signOut?.({ scope: \"local\" });\n return message;\n },\n async withPassword(password) {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to confirm with.\");\n const { data, error } = await auth.signInWithPassword({ email: u.email, password });\n if (error) throw new Error(\"That password was not accepted.\");\n sameUser(data.user?.id);\n },\n async sendEmailCode() {\n const u = await who();\n if (!u.email) throw new Error(\"This account has no email to send a code to.\");\n const { error } = await auth.signInWithOtp({ email: u.email, options: { shouldCreateUser: false } });\n if (error) throw new Error(error.message);\n },\n async withEmailCode(code) {\n const u = me ?? (await who());\n if (!u.email) throw new Error(\"This account has no email.\");\n const { data, error } = await auth.verifyOtp({ email: u.email, token: code.trim(), type: \"email\" });\n if (error) throw new Error(\"That code was not accepted. Check it, or send a new one.\");\n sameUser(data.user?.id);\n },\n };\n if (options.dev) identity.dev = options.dev;\n return identity;\n}\n\n/** True when the server refused because the sign-in is not recent enough. */\nexport function needsRecentSignIn(e: unknown): boolean {\n const err = e as { code?: unknown; status?: unknown; message?: unknown; serverDetail?: unknown } | null;\n if (!err) return false;\n if (err.code === \"recent_auth_required\") return true;\n // Some routes answer with only a sentence (the shown message is a generic \"sign in\"; the server's\n // own words say which): read those too.\n const said = [err.message, JSON.stringify(err.serverDetail ?? \"\")].filter((x): x is string => typeof x === \"string\").join(\" \");\n return /recent auth|recent sign|re-authenticate|authentication within the last/i.test(said);\n}\n","import { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@ai-matrx/design-system\";\nimport { Button, Field } from \"@ai-matrx/design-system/controls\";\nimport { ShieldGlyph } from \"../glyphs\";\nimport { useNotify } from \"../notice\";\nimport { needsRecentSignIn, type IdentityAccount, type VaultIdentity } from \"./identity\";\n\n/**\n * Wrap any call that shows, copies or exports a secret. When the server answers \"sign in again\",\n * the person confirms it is them (Google, password, or an emailed code) and the call runs again —\n * they never see a dead \"Authentication required\" in the row.\n */\nexport type Confirmed = <T>(run: () => Promise<T>) => Promise<T>;\n\nconst CANCELLED = \"Not confirmed.\";\n/** Said once when the person closes \"Confirm it's you\": whatever asked for it did not happen. */\nexport const NOT_CONFIRMED_NOTICE = \"Not confirmed, so nothing was done\";\nconst Ctx = createContext<Confirmed>((run) => run());\n\nexport function useConfirmed(): Confirmed {\n return useContext(Ctx);\n}\n\nconst PROVIDER_LABEL: Record<string, string> = { google: \"Google\", github: \"GitHub\", azure: \"Microsoft\", apple: \"Apple\", linkedin_oidc: \"LinkedIn\", slack_oidc: \"Slack\", browser: \"Browser\", touch_id: \"Touch ID\" };\nconst providerLabel = (p: string) => PROVIDER_LABEL[p] ?? p.charAt(0).toUpperCase() + p.slice(1);\n\nexport function IdentityGate({ identity, children }: { identity: VaultIdentity | undefined; children: ReactNode }) {\n const [asking, setAsking] = useState(false);\n const notify = useNotify();\n const waiters = useRef<{ resolve: () => void; reject: (e: Error) => void }[]>([]);\n const confirmationVersion = useRef(0);\n const identityVersion = useMemo(() => ({ key: crypto.randomUUID() }), [identity]);\n const activeIdentity = useRef<object | null>(null);\n useLayoutEffect(() => {\n activeIdentity.current = identityVersion;\n setAsking(false);\n return () => {\n activeIdentity.current = null;\n identity?.cancel?.();\n for (const waiter of waiters.current.splice(0)) waiter.reject(new Error(CANCELLED));\n };\n }, [identityVersion]);\n // Back from a provider's confirmation: a different account that came back is signed out and said.\n const [returnProblem, setReturnProblem] = useState<string | null>(null);\n useEffect(() => {\n if (!identity?.checkReturn) return;\n let live = true;\n identity.checkReturn().then(\n (problem) => live && problem && setReturnProblem(problem),\n (e: unknown) => live && setReturnProblem(e instanceof Error ? e.message : \"The account that came back could not be checked.\"),\n );\n return () => {\n live = false;\n };\n }, [identity]);\n\n const confirmed: Confirmed = async (run) => {\n const startedAtVersion = confirmationVersion.current;\n const checkIdentity = () => {\n if (activeIdentity.current !== identityVersion) throw new Error(CANCELLED);\n };\n const runAsIdentity = async () => {\n checkIdentity();\n const result = await run();\n checkIdentity();\n return result;\n };\n try {\n return await runAsIdentity();\n } catch (e) {\n checkIdentity();\n if (!identity || !needsRecentSignIn(e)) throw e;\n // A response sent with the old bearer can arrive after another call's confirmation.\n // Give the current bearer/grant one try before asking the person to confirm again.\n if (startedAtVersion !== confirmationVersion.current) {\n try {\n return await runAsIdentity();\n } catch (retryError) {\n if (!needsRecentSignIn(retryError)) throw retryError;\n }\n }\n await new Promise<void>((resolve, reject) => {\n waiters.current.push({ resolve, reject });\n setAsking(true);\n });\n checkIdentity();\n return runAsIdentity();\n }\n };\n\n const settle = (ok: boolean) => {\n if (activeIdentity.current !== identityVersion) return;\n if (!ok) identity?.cancel?.();\n const w = waiters.current;\n waiters.current = [];\n if (ok) confirmationVersion.current += 1;\n setAsking(false);\n for (const x of w) (ok ? x.resolve() : x.reject(new Error(CANCELLED)));\n // Never silent: every caller stays quiet about a cancel (`wasCancelled`), so the gate says it once.\n if (!ok && w.length > 0) notify(NOT_CONFIRMED_NOTICE, \"info\");\n };\n\n return (\n <Ctx.Provider value={confirmed}>\n {children}\n {identity && asking ? <ConfirmIdentityDialog key={identityVersion.key} open identity={identity} onDone={() => settle(true)} onCancel={() => settle(false)} /> : null}\n <Dialog open={returnProblem !== null} onOpenChange={(o) => (o ? undefined : setReturnProblem(null))}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <DialogTitle>Different account</DialogTitle>\n <DialogDescription>{returnProblem}</DialogDescription>\n </DialogHeader>\n <div className=\"flex justify-end\">\n <Button variant=\"primary\" onClick={() => setReturnProblem(null)}>OK</Button>\n </div>\n </DialogContent>\n </Dialog>\n </Ctx.Provider>\n );\n}\n\n/** True for the rejection a canceled confirmation produces (callers stay quiet about it). */\nexport const wasCancelled = (e: unknown) => e instanceof Error && e.message === CANCELLED;\n\nfunction ConfirmIdentityDialog({ open, identity, onDone, onCancel }: { open: boolean; identity: VaultIdentity; onDone: () => void; onCancel: () => void }) {\n const live = useRef(false);\n useLayoutEffect(() => {\n live.current = true;\n return () => { live.current = false; };\n }, []);\n const finish = () => { if (live.current) onDone(); };\n const [account, setAccount] = useState<IdentityAccount | null>(null);\n const [password, setPassword] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [codeSent, setCodeSent] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!open) return;\n setPassword(\"\");\n setCode(\"\");\n setCodeSent(false);\n setError(null);\n let current = true;\n identity.account().then(\n (value) => { if (current && live.current) setAccount(value); },\n (e: unknown) => { if (current && live.current) setError(e instanceof Error ? e.message : \"Your account could not be read.\"); },\n );\n return () => { current = false; };\n }, [open, identity]);\n\n const attempt = async (step: () => Promise<void>, done = true) => {\n setBusy(true);\n setError(null);\n try {\n await step();\n if (done) finish();\n } catch (e) {\n if (live.current) setError(e instanceof Error ? e.message : \"That did not work. Try again.\");\n } finally {\n if (live.current) setBusy(false);\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={(o) => (o ? undefined : onCancel())}>\n <DialogContent size=\"sm\">\n <DialogHeader>\n <div className=\"flex items-center gap-2\">\n <ShieldGlyph className=\"size-5 text-primary\" />\n <DialogTitle>Confirm it’s you</DialogTitle>\n </div>\n <DialogDescription>This needs a recent sign-in{account?.email ? ` to ${account.email}` : \"\"}.</DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-3\">\n {identity.dev ? (\n <Button variant=\"primary\" disabled={busy} onClick={() => void attempt(() => identity.dev?.() ?? Promise.resolve())}>\n Sign in again (dev)\n </Button>\n ) : null}\n {account?.providers.map((p) => (\n <Button\n key={p}\n variant={identity.dev ? \"outline\" : \"primary\"}\n disabled={busy}\n onClick={() =>\n void attempt(async () => {\n if ((await identity.withProvider(p)) === \"confirmed\") finish();\n }, false)\n }\n >\n Continue with {providerLabel(p)}\n </Button>\n ))}\n {account?.hasPassword ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (password) void attempt(() => identity.withPassword(password)); }}>\n <Field className=\"min-w-0 flex-1\" type=\"password\" autoComplete=\"current-password\" placeholder=\"Your password\" aria-label=\"Your password\" value={password} onChange={(e) => setPassword(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !password}>Confirm</Button>\n </form>\n ) : null}\n {account?.email ? (\n codeSent ? (\n <form className=\"flex gap-2\" onSubmit={(e) => { e.preventDefault(); if (code.trim()) void attempt(() => identity.withEmailCode(code)); }}>\n <Field className=\"min-w-0 flex-1\" inputMode=\"numeric\" autoComplete=\"one-time-code\" placeholder=\"Code from your email\" aria-label=\"Code from your email\" value={code} onChange={(e) => setCode(e.target.value)} autoFocus />\n <Button variant=\"outline\" type=\"submit\" disabled={busy || !code.trim()}>Confirm</Button>\n </form>\n ) : (\n <Button variant=\"quiet\" disabled={busy} onClick={() => void attempt(async () => { await identity.sendEmailCode(); if (live.current) setCodeSent(true); }, false)}>\n Email me a code instead\n </Button>\n )\n ) : null}\n {error ? <p role=\"alert\" className=\"text-sm text-destructive\">{error}</p> : null}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { CSSProperties, ReactNode } from \"react\";\n\nimport {\n GlobeIcon,\n ArchiveIcon,\n ArrowUpDownIcon,\n BuildingIcon,\n CameraIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n ClockIcon,\n CodeIcon,\n CreditCardIcon,\n DatabaseIcon,\n DownloadIcon,\n ExternalLinkIcon,\n EyeIcon,\n EyeOffIcon,\n FilePageIcon,\n FileTextIcon,\n FingerprintPatternIcon,\n KeyIcon,\n LayoutGridIcon,\n ListIcon,\n LockIcon,\n MoreHorizontalIcon,\n MoveIcon,\n PencilIcon,\n PlugIcon,\n PlusIcon,\n PowerIcon,\n QrCodeIcon,\n RefreshCwIcon,\n RotateCcwIcon,\n SendIcon,\n ServerIcon,\n ShareIcon,\n ShieldIcon,\n SlidersVerticalIcon,\n StarIcon,\n TagIcon,\n TerminalIcon,\n TrashIcon,\n UploadIcon,\n UserIcon,\n UserRoundIcon,\n XIcon,\n withGlyphDefaults,\n} from \"@ai-matrx/design-system/icons\";\n\n/**\n * The vault's glyphs. Every lucide drawing lives once, in `@ai-matrx/design-system/icons` (the one lucide-free\n * glyph home, which also carries the optical-trim class); each name here is an alias of it. `SafeGlyph` is the\n * vault's own drawing (a strongbox: no lucide twin) and is the only one drawn in this file.\n */\nexport type Glyph = (props: { className?: string }) => ReactNode;\n\n/**\n * `air` = the empty 24-grid units left and right of the drawn ink (stroke included), measured with\n * getBBox. The design system's optical trim reads it (`--matrx-glyph-air-l/-r`) so an icon + label\n * button sits its INK, not its box, the same distance from both edges — exactly as it does for lucide.\n */\nfunction glyph(paths: ReactNode, air: readonly [number, number] = [1, 1]): Glyph {\n const style = { \"--matrx-glyph-air-l\": air[0], \"--matrx-glyph-air-r\": air[1] } as CSSProperties;\n return function VaultGlyph({ className }) {\n return (\n <svg className={className} style={style} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {paths}\n </svg>\n );\n };\n}\n\nexport const KeyGlyph: Glyph = withGlyphDefaults(KeyIcon, {}, { air: [1, 2] });\nexport const NoteGlyph: Glyph = withGlyphDefaults(FileTextIcon, {}, { air: [3, 3] });\nexport const CardGlyph: Glyph = withGlyphDefaults(CreditCardIcon, {}, { air: [1, 1] });\nexport const PersonGlyph: Glyph = withGlyphDefaults(UserRoundIcon, {}, { air: [3, 3] });\nexport const DatabaseGlyph: Glyph = withGlyphDefaults(DatabaseIcon, {}, { air: [2, 2] });\nexport const ServerGlyph: Glyph = withGlyphDefaults(ServerIcon, {}, { air: [1, 1] });\nexport const TerminalGlyph: Glyph = withGlyphDefaults(TerminalIcon, {}, { air: [3, 3] });\nexport const FileGlyph: Glyph = withGlyphDefaults(FilePageIcon, {}, { air: [3, 3] });\nexport const CodeGlyph: Glyph = withGlyphDefaults(CodeIcon, {}, { air: [1, 1] });\nexport const StarGlyph: Glyph = withGlyphDefaults(StarIcon, {}, { air: [1, 1] });\nexport const PlusGlyph: Glyph = withGlyphDefaults(PlusIcon, {}, { air: [4, 4] });\nexport const EyeGlyph: Glyph = withGlyphDefaults(EyeIcon, {}, { air: [1, 1] });\nexport const EyeOffGlyph: Glyph = withGlyphDefaults(EyeOffIcon, {}, { air: [1, 1] });\nexport const LockGlyph: Glyph = withGlyphDefaults(LockIcon, {}, { air: [2, 2] });\nexport const ExternalGlyph: Glyph = withGlyphDefaults(ExternalLinkIcon, {}, { air: [2, 2] });\nexport const TrashGlyph: Glyph = withGlyphDefaults(TrashIcon, {}, { air: [2, 2] });\nexport const ArchiveGlyph: Glyph = withGlyphDefaults(ArchiveIcon, {}, { air: [1, 1] });\nexport const TagGlyph: Glyph = withGlyphDefaults(TagIcon, {}, { air: [1, 1] });\nexport const ShieldGlyph: Glyph = withGlyphDefaults(ShieldIcon, {}, { air: [3, 3] });\nexport const BuildingGlyph: Glyph = withGlyphDefaults(BuildingIcon, {}, { air: [3, 3] });\nexport const ChevronLeftGlyph: Glyph = withGlyphDefaults(ChevronLeftIcon, {}, { air: [8, 8] });\nexport const GridGlyph: Glyph = withGlyphDefaults(LayoutGridIcon, {}, { air: [2, 2] });\nexport const PlugGlyph: Glyph = withGlyphDefaults(PlugIcon, {}, { air: [5, 5] });\n\nconst FAMILY_GLYPHS: Record<string, Glyph> = {\n databases: DatabaseGlyph,\n server_network: ServerGlyph,\n hosting_deployment: ServerGlyph,\n source_control: CodeGlyph,\n signing_files: FileGlyph,\n identity_security: ShieldGlyph,\n cloud_infrastructure: ServerGlyph,\n};\n\n/** A type's glyph: logins are a globe, then by family, else a key. */\nexport function definitionGlyph(key: string, family: string): Glyph {\n if (key === \"username_password\" || key === \"website_login\") return GlobeIcon;\n return FAMILY_GLYPHS[family] ?? KeyGlyph;\n}\nexport const XGlyph: Glyph = withGlyphDefaults(XIcon, {}, { air: [5, 5] });\nexport const PencilGlyph: Glyph = withGlyphDefaults(PencilIcon, {}, { air: [2, 2] });\nexport const ClockGlyph: Glyph = withGlyphDefaults(ClockIcon, {}, { air: [1, 1] });\n// ── core workspace glyphs ────────────────────────────────────────────────────\nexport const MoreGlyph: Glyph = withGlyphDefaults(MoreHorizontalIcon, {}, { air: [3, 3] });\nexport const ShareGlyph: Glyph = withGlyphDefaults(ShareIcon, {}, { air: [3, 3] });\nexport const ListGlyph: Glyph = withGlyphDefaults(ListIcon, {}, { air: [2, 2] });\nexport const ImportGlyph: Glyph = withGlyphDefaults(DownloadIcon, {}, { air: [2, 2] });\nexport const ExportGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\nexport const ChevronDownGlyph: Glyph = withGlyphDefaults(ChevronDownIcon, {}, { air: [5, 5] });\nexport const ChevronRightGlyph: Glyph = withGlyphDefaults(ChevronRightIcon, {}, { air: [8, 8] });\nexport const SortGlyph: Glyph = withGlyphDefaults(ArrowUpDownIcon, {}, { air: [2, 2] });\nexport const UserGlyph: Glyph = withGlyphDefaults(UserIcon, {}, { air: [5, 5] });\nexport const MoveGlyph: Glyph = withGlyphDefaults(MoveIcon, {}, { air: [1, 1] });\nexport const SafeGlyph = glyph(<><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" /><circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 8v1M12 15v1M8 12h1M15 12h1\" /></>, [2, 2]);\nexport const RestoreGlyph: Glyph = withGlyphDefaults(RotateCcwIcon, {}, { air: [2, 2] });\n// ── one-time passwords ───────────────────────────────────────────────────────\nexport const QrGlyph: Glyph = withGlyphDefaults(QrCodeIcon, {}, { air: [2, 2] });\nexport const CameraGlyph: Glyph = withGlyphDefaults(CameraIcon, {}, { air: [1, 1] });\nexport const CheckGlyph: Glyph = withGlyphDefaults(CheckIcon, {}, { air: [3, 3] });\nexport const PowerGlyph: Glyph = withGlyphDefaults(PowerIcon, {}, { air: [2, 2] });\n// ── generator and field settings ─────────────────────────────────────────────\nexport const RefreshGlyph: Glyph = withGlyphDefaults(RefreshCwIcon, {}, { air: [2, 2] });\nexport const SlidersGlyph: Glyph = withGlyphDefaults(SlidersVerticalIcon, {}, { air: [0, 0] });\nexport const UploadGlyph: Glyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });\n// ── browsers that can fill ───────────────────────────────────────────────────\nexport const FingerprintGlyph: Glyph = withGlyphDefaults(FingerprintPatternIcon, {}, { air: [1, 1] });\nexport const SendGlyph: Glyph = withGlyphDefaults(SendIcon, {}, { air: [1, 1] });\n","import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"@ai-matrx/design-system\";\nimport { copyText } from \"@ai-matrx/kit/clipboard\";\nimport type { VaultClipboard } from \"./lock\";\n\nexport type NoticeKind = \"success\" | \"error\" | \"info\";\nexport type Notify = (message: string, kind?: NoticeKind) => void;\n\nconst Ctx = createContext<Notify>(() => undefined);\n\n/** Say something short to the person (\"Password copied\"). Hosts with their own toasts pass `notify`. */\nexport function useNotify(): Notify {\n return useContext(Ctx);\n}\n\nexport interface CopyOptions {\n /** A secret (a password, a code, a key): the host's clipboard takes custody. Default true. */\n secret?: boolean;\n}\n\n/**\n * Writes `text` and says so (or says why not). Through the host's clipboard when it has one (custody:\n * the host clears a secret later), else the browser's. Pure over its inputs, so it is tested alone.\n */\nexport async function copyValue(text: string, label: string, options: CopyOptions, deps: { clipboard?: VaultClipboard | undefined; notify: Notify }): Promise<boolean> {\n const secret = options.secret ?? true;\n if (deps.clipboard) {\n try {\n const copied = await deps.clipboard.copy(text, { secret });\n deps.notify(copiedLine(label, copied?.clearsAtMs ?? null), \"success\");\n return true;\n } catch (e) {\n deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, \"error\");\n return false;\n }\n }\n return copyText(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => deps.notify(m, k === \"error\" ? \"error\" : \"success\") });\n}\n\n/** \"Password copied\", or \"Password copied. Clears at 3:42 PM\" when the host says when it clears it. */\nexport function copiedLine(label: string, clearsAtMs: number | null): string {\n if (clearsAtMs === null || !Number.isFinite(clearsAtMs)) return `${label} copied`;\n const at = new Date(clearsAtMs).toLocaleTimeString(undefined, { hour: \"numeric\", minute: \"2-digit\" });\n return `${label} copied. Clears at ${at}`;\n}\n\nconst ClipboardCtx = createContext<VaultClipboard | undefined>(undefined);\n\n/** The host's clipboard (`host.clipboard`), for every copy below it. */\nexport function ClipboardProvider({ clipboard, children }: { clipboard: VaultClipboard | undefined; children: ReactNode }) {\n return <ClipboardCtx.Provider value={clipboard}>{children}</ClipboardCtx.Provider>;\n}\n\n/** THE copy for every vault screen: writes, then says so (or says why not). */\nexport function useCopy(): (text: string, label: string, options?: CopyOptions) => Promise<boolean> {\n const notify = useNotify();\n const clipboard = useContext(ClipboardCtx);\n return useCallback((text, label, options = {}) => copyValue(text, label, options, { clipboard, notify }), [notify, clipboard]);\n}\n\n/**\n * The vault's own notice line, so feedback exists in every host (an extension side panel has no app\n * toaster). A host that has one passes `notify` and this draws nothing.\n */\nexport function NoticeProvider({ notify, children }: { notify?: Notify | undefined; children: ReactNode }) {\n const [shown, setShown] = useState<{ message: string; kind: NoticeKind; n: number } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const local = useCallback<Notify>((message, kind = \"success\") => {\n if (timer.current) clearTimeout(timer.current);\n setShown((s) => ({ message, kind, n: (s?.n ?? 0) + 1 }));\n timer.current = setTimeout(() => setShown(null), kind === \"error\" ? 5000 : 2200);\n }, []);\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), []);\n return (\n <Ctx.Provider value={notify ?? local}>\n {children}\n {!notify ? (\n <div aria-live=\"polite\" className=\"pointer-events-none absolute inset-x-0 bottom-4 z-30 flex justify-center px-4\">\n {shown ? (\n <div key={shown.n} role=\"status\" className={cn(\"rounded-full border border-border bg-popover px-4 py-2 text-sm shadow-lg\", shown.kind === \"error\" ? \"text-destructive\" : \"text-foreground\")}>\n {shown.message}\n </div>\n ) : null}\n </div>\n ) : null}\n </Ctx.Provider>\n );\n}\n"],"mappings":"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","names":["import_react","import_design_system","import_jsx_runtime","import_jsx_runtime","Ctx"]}
@@ -17,6 +17,10 @@ interface IdentityAccount {
17
17
  }
18
18
  interface VaultIdentity {
19
19
  account(): Promise<IdentityAccount>;
20
+ /** Synchronously invalidate an unfinished attempt on dismissal or identity disposal.
21
+ * Late work cleans up its own resources and cannot confirm a newer attempt.
22
+ * A completed confirmation remains reusable across ordinary workspace navigation. */
23
+ cancel?: () => void;
20
24
  /**
21
25
  * Confirms with that provider. On the web it leaves the page and comes back to the same address
22
26
  * (resolves with nothing: the page is going away). A host that confirms IN PLACE (the desktop's
@@ -17,6 +17,10 @@ interface IdentityAccount {
17
17
  }
18
18
  interface VaultIdentity {
19
19
  account(): Promise<IdentityAccount>;
20
+ /** Synchronously invalidate an unfinished attempt on dismissal or identity disposal.
21
+ * Late work cleans up its own resources and cannot confirm a newer attempt.
22
+ * A completed confirmation remains reusable across ordinary workspace navigation. */
23
+ cancel?: () => void;
20
24
  /**
21
25
  * Confirms with that provider. On the web it leaves the page and comes back to the same address
22
26
  * (resolves with nothing: the page is going away). A host that confirms IN PLACE (the desktop's
package/dist/identity.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  needsRecentSignIn,
7
7
  useConfirmed,
8
8
  wasCancelled
9
- } from "./chunk-LXFJBJR6.js";
9
+ } from "./chunk-YDYTU3NY.js";
10
10
  export {
11
11
  CONFIRM_RETURN_KEY,
12
12
  IdentityGate,