@ai-matrx/auth 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/guest.cjs ADDED
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/guest.ts
21
+ var guest_exports = {};
22
+ __export(guest_exports, {
23
+ DEFAULT_GUEST_CARRIER_COOKIE: () => DEFAULT_GUEST_CARRIER_COOKIE,
24
+ createGuestHandover: () => createGuestHandover,
25
+ createGuestSessionEnsurer: () => createGuestSessionEnsurer,
26
+ guestClientCookieOptions: () => guestClientCookieOptions,
27
+ isGuestCookieName: () => isGuestCookieName,
28
+ looksLikeVisitorId: () => looksLikeVisitorId
29
+ });
30
+ module.exports = __toCommonJS(guest_exports);
31
+ var import_ssr = require("@supabase/ssr");
32
+ var VISITOR_ID = /^[A-Za-z0-9]{16,200}$/;
33
+ function looksLikeVisitorId(id) {
34
+ return typeof id === "string" && !id.startsWith("temp_") && VISITOR_ID.test(id);
35
+ }
36
+ function isGuestCookieName(guestCookieName, name) {
37
+ return name === guestCookieName || name.startsWith(`${guestCookieName}.`);
38
+ }
39
+ var DEFAULT_GUEST_CARRIER_COOKIE = "matrx_guest_visitor";
40
+ function createGuestHandover(options) {
41
+ const carrier = options.carrierCookieName ?? DEFAULT_GUEST_CARRIER_COOKIE;
42
+ const loud = options.log ?? ((m) => console.error(`[session-handover] LOUD: ${m}`));
43
+ const isGuestCookie = (name) => isGuestCookieName(options.guestCookieName, name);
44
+ const msg = (err) => err instanceof Error ? err.message : String(err);
45
+ async function readClaims(jar) {
46
+ if (options.readGuestClaims) return options.readGuestClaims(jar);
47
+ if (!options.supabase) return null;
48
+ const guest = (0, import_ssr.createServerClient)(options.supabase.url, options.supabase.publishableKey, {
49
+ cookieOptions: { name: options.guestCookieName },
50
+ cookies: { getAll: () => jar.getAll(), setAll: () => void 0 }
51
+ });
52
+ const { data } = await guest.auth.getClaims();
53
+ return data?.claims ?? null;
54
+ }
55
+ async function readGuestPossession(visitorId) {
56
+ const jar = await options.cookies();
57
+ if (jar.getAll().some((c) => isGuestCookie(c.name))) {
58
+ try {
59
+ const claims = await readClaims(jar);
60
+ if (claims?.sub && claims.is_anonymous === true) return { guestId: claims.sub, proof: "guest_session" };
61
+ } catch (err) {
62
+ loud(`the guest cookie could not be verified: ${msg(err)}`);
63
+ }
64
+ }
65
+ const candidates = [visitorId, options.visitorCookieName ? jar.get(options.visitorCookieName)?.value : void 0].filter(looksLikeVisitorId);
66
+ if (!candidates.length) return null;
67
+ const { data: ids, error } = await options.store.findGuestIdsByVisitor(candidates);
68
+ if (error) {
69
+ loud(`the visitor's guest could not be looked up: ${error.message}`);
70
+ return null;
71
+ }
72
+ for (const id of ids ?? []) {
73
+ if (await options.store.isAnonymousUser(id)) return { guestId: id, proof: "visitor_id" };
74
+ }
75
+ return null;
76
+ }
77
+ async function dropGuestCookies() {
78
+ try {
79
+ const jar = await options.cookies();
80
+ for (const c of jar.getAll()) if (isGuestCookie(c.name)) jar.delete(c.name);
81
+ } catch {
82
+ }
83
+ }
84
+ async function claimGuestInto(guest, accountId, via) {
85
+ if (guest.guestId === accountId) return { status: "same_account" };
86
+ const { data, error } = await options.store.claimWorkspace({ guestId: guest.guestId, targetId: accountId, via });
87
+ if (error) {
88
+ loud(`the guest's records were NOT brought into ${accountId} (${via}); the guest cookie is kept so the next sign-in retries: ${error.message}`);
89
+ return { status: "failed", guestId: guest.guestId, message: error.message };
90
+ }
91
+ await dropGuestCookies();
92
+ const records = typeof data?.records === "number" ? data.records : 0;
93
+ return { status: "claimed", guestId: guest.guestId, records };
94
+ }
95
+ return {
96
+ looksLikeVisitorId,
97
+ readGuestPossession,
98
+ claimGuestInto,
99
+ async handOverSession(via, replace, opts = {}) {
100
+ const guest = await readGuestPossession(opts.visitorId);
101
+ const result = await replace();
102
+ const user = result.error ? null : result.data?.user ?? null;
103
+ let guestClaim = { status: "none" };
104
+ if (guest && user && user.is_anonymous !== true) guestClaim = await claimGuestInto(guest, user.id, via);
105
+ return { ...result, guestClaim };
106
+ },
107
+ async settlePendingGuestClaim(accountId) {
108
+ const jar = await options.cookies();
109
+ if (!jar.getAll().some((c) => isGuestCookie(c.name))) return { status: "none" };
110
+ const guest = await readGuestPossession(null);
111
+ if (!guest || guest.proof !== "guest_session") return { status: "none" };
112
+ return claimGuestInto(guest, accountId, "password_login");
113
+ },
114
+ async promoteGuest(args) {
115
+ const guest = await readGuestPossession(args.visitorId);
116
+ if (!guest) return { kind: "no_guest" };
117
+ const revive = async () => {
118
+ const { error } = await options.store.revive(guest.guestId);
119
+ if (error) throw new Error(`the guest could not be reopened: ${error.message}`);
120
+ };
121
+ try {
122
+ await revive();
123
+ const { error } = await options.store.promoteUser(guest.guestId, { email: args.email, password: args.password });
124
+ if (error) {
125
+ const m = error.message.toLowerCase();
126
+ if (error.code === "email_exists" || m.includes("already been registered") || m.includes("already registered") || m.includes("already exists")) {
127
+ return { kind: "email_in_use" };
128
+ }
129
+ loud(`guest promotion failed \u2014 the guest is kept, no new account is made: ${error.message}`);
130
+ return { kind: "failed", message: error.message };
131
+ }
132
+ await revive();
133
+ const { error: stampErr } = await options.store.stampConverted(guest.guestId);
134
+ if (stampErr) loud(`promotion succeeded but the registry row was not stamped: ${stampErr.message}`);
135
+ return { kind: "promoted", userId: guest.guestId };
136
+ } catch (err) {
137
+ loud(`guest promotion failed \u2014 the guest is kept: ${msg(err)}`);
138
+ return { kind: "failed", message: msg(err) };
139
+ }
140
+ },
141
+ finishPromotion: dropGuestCookies,
142
+ async rememberGuestVisitorId(formData) {
143
+ try {
144
+ const id = formData?.get("guestFingerprint")?.toString();
145
+ if (!looksLikeVisitorId(id)) return;
146
+ (await options.cookies()).set(carrier, id, {
147
+ httpOnly: true,
148
+ sameSite: "lax",
149
+ secure: options.secureCookies ?? false,
150
+ path: "/",
151
+ maxAge: 600
152
+ });
153
+ } catch (err) {
154
+ loud(`the visitor id could not be kept for the OAuth round trip: ${msg(err)}`);
155
+ }
156
+ },
157
+ async takeGuestVisitorId() {
158
+ const jar = await options.cookies();
159
+ const id = jar.get(carrier)?.value ?? null;
160
+ if (id) {
161
+ try {
162
+ jar.delete(carrier);
163
+ } catch {
164
+ }
165
+ }
166
+ return looksLikeVisitorId(id) ? id : null;
167
+ }
168
+ };
169
+ }
170
+ function guestClientCookieOptions(cookieName, secure = typeof window !== "undefined" && window.location.protocol === "https:") {
171
+ return { name: cookieName, path: "/", sameSite: "lax", secure, maxAge: 60 * 60 * 24 * 400 };
172
+ }
173
+ function createGuestSessionEnsurer(options) {
174
+ const doFetch = options.fetch ?? fetch;
175
+ let inFlight = null;
176
+ async function existing() {
177
+ const { data } = await options.auth.getSession();
178
+ const user = data.session?.user;
179
+ if (!user?.is_anonymous) return null;
180
+ const orgs = [...new Set((await options.listWorkspaceIds(user.id)).filter((o) => typeof o === "string"))];
181
+ return orgs.length === 1 ? { organizationId: orgs[0], adoptedFrom: null } : null;
182
+ }
183
+ async function mint() {
184
+ const have = await existing();
185
+ if (have) return have;
186
+ const base = options.serverBaseUrl;
187
+ if (!base) throw new Error("The AI Matrx server address is not configured, so a guest session cannot be made.");
188
+ const fingerprint = await options.getFingerprint();
189
+ const res = await doFetch(`${base.replace(/\/$/, "")}/auth/guest/session`, {
190
+ method: "POST",
191
+ headers: { "Content-Type": "application/json" },
192
+ body: JSON.stringify({ fingerprint })
193
+ });
194
+ if (!res.ok) {
195
+ const body = await res.json().catch(() => null);
196
+ throw new Error(body?.detail?.message ?? `A guest session could not be made (HTTP ${res.status}).`);
197
+ }
198
+ const s = await res.json();
199
+ const { error } = await options.auth.setSession({ access_token: s.access_token, refresh_token: s.refresh_token });
200
+ if (error) throw new Error(`The guest session could not be kept: ${error.message}`);
201
+ if (!s.organization_id) throw new Error("The guest session has no workspace to save in.");
202
+ return { organizationId: s.organization_id, adoptedFrom: s.adopted_from };
203
+ }
204
+ return () => {
205
+ inFlight ??= (async () => {
206
+ const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
207
+ return locks ? locks.request(options.lockName ?? "matrx-guest-session", () => mint()) : mint();
208
+ })().finally(() => {
209
+ inFlight = null;
210
+ });
211
+ return inFlight;
212
+ };
213
+ }
@@ -0,0 +1,189 @@
1
+ type HandoverVia = "password_login" | "oauth_callback" | "email_otp" | "dev_login" | "sign_up";
2
+ interface GuestPossession {
3
+ guestId: string;
4
+ proof: "guest_session" | "visitor_id";
5
+ }
6
+ type GuestClaimOutcome = {
7
+ status: "none";
8
+ } | {
9
+ status: "same_account";
10
+ } | {
11
+ status: "claimed";
12
+ guestId: string;
13
+ records: number;
14
+ } | {
15
+ status: "failed";
16
+ guestId: string;
17
+ message: string;
18
+ };
19
+ type GuestPromotion = {
20
+ kind: "no_guest";
21
+ } | {
22
+ kind: "promoted";
23
+ userId: string;
24
+ } | {
25
+ kind: "email_in_use";
26
+ } | {
27
+ kind: "failed";
28
+ message: string;
29
+ };
30
+ /** A guessable `temp_` fallback id is never a proof of possession. */
31
+ declare function looksLikeVisitorId(id: string | null | undefined): id is string;
32
+ /** The guest cookie and its @supabase/ssr chunks (`<name>.0`, `<name>.1`, …). */
33
+ declare function isGuestCookieName(guestCookieName: string, name: string): boolean;
34
+ /** A request's cookie jar (Next's `cookies()` result fits). */
35
+ interface GuestCookieJar {
36
+ getAll(): {
37
+ name: string;
38
+ value: string;
39
+ }[];
40
+ get(name: string): {
41
+ value: string;
42
+ } | undefined;
43
+ set(name: string, value: string, options?: Record<string, unknown>): unknown;
44
+ delete(name: string): unknown;
45
+ }
46
+ type DbAnswer<T> = {
47
+ data: T | null;
48
+ error: {
49
+ message: string;
50
+ } | null;
51
+ };
52
+ /** What the host's server-side store answers (admin/service lane; the host owns the credentials). */
53
+ interface GuestStore {
54
+ /** Guest user ids the registry bound to any of these visitor ids. */
55
+ findGuestIdsByVisitor(visitorIds: string[]): Promise<DbAnswer<string[]>>;
56
+ /** True when the account is an anonymous (guest) one. */
57
+ isAnonymousUser(userId: string): Promise<boolean>;
58
+ /** `claim_guest_workspace`: membership moves, no row moves, all or nothing. */
59
+ claimWorkspace(args: {
60
+ guestId: string;
61
+ targetId: string;
62
+ via: string;
63
+ }): Promise<DbAnswer<{
64
+ records?: number;
65
+ status?: string;
66
+ }>>;
67
+ /** `guest_revive`: reopen a guest retention closed. */
68
+ revive(guestId: string): Promise<DbAnswer<unknown>>;
69
+ /** Make the guest's account permanent in place. `error.code` may carry `email_exists`. */
70
+ promoteUser(guestId: string, credentials: {
71
+ email: string;
72
+ password: string;
73
+ }): Promise<{
74
+ error: {
75
+ message: string;
76
+ code?: string;
77
+ } | null;
78
+ }>;
79
+ /** Stamp the registry row converted. */
80
+ stampConverted(guestId: string): Promise<{
81
+ error: {
82
+ message: string;
83
+ } | null;
84
+ }>;
85
+ }
86
+ interface GuestHandoverOptions {
87
+ /** The guest session's own cookie name (never the main auth cookie). */
88
+ guestCookieName: string;
89
+ /** First-party visitor cookie the guest lane already accepts, if the host has one. */
90
+ visitorCookieName?: string;
91
+ /** Short-lived carrier for the visitor id across an OAuth round trip. Default `matrx_guest_visitor`. */
92
+ carrierCookieName?: string;
93
+ /** Server cookie jar for this request. */
94
+ cookies(): Promise<GuestCookieJar>;
95
+ store: GuestStore;
96
+ /** Verified claims of the guest cookie. Default: a server client over `supabase` url + key. */
97
+ readGuestClaims?(jar: GuestCookieJar): Promise<{
98
+ sub?: string;
99
+ is_anonymous?: boolean;
100
+ } | null>;
101
+ supabase?: {
102
+ url: string;
103
+ publishableKey: string;
104
+ };
105
+ /** `Secure` on the carrier cookie. */
106
+ secureCookies?: boolean;
107
+ log?(message: string): void;
108
+ }
109
+ interface GuestHandover {
110
+ looksLikeVisitorId: typeof looksLikeVisitorId;
111
+ readGuestPossession(visitorId?: string | null): Promise<GuestPossession | null>;
112
+ claimGuestInto(guest: GuestPossession, accountId: string, via: HandoverVia): Promise<GuestClaimOutcome>;
113
+ handOverSession<R extends {
114
+ data: {
115
+ user?: {
116
+ id: string;
117
+ is_anonymous?: boolean;
118
+ } | null;
119
+ } | null;
120
+ error: unknown;
121
+ }>(via: HandoverVia, replace: () => Promise<R>, opts?: {
122
+ visitorId?: string | null;
123
+ }): Promise<R & {
124
+ guestClaim: GuestClaimOutcome;
125
+ }>;
126
+ settlePendingGuestClaim(accountId: string): Promise<GuestClaimOutcome>;
127
+ promoteGuest(args: {
128
+ email: string;
129
+ password: string;
130
+ visitorId?: string | null;
131
+ }): Promise<GuestPromotion>;
132
+ finishPromotion(): Promise<void>;
133
+ rememberGuestVisitorId(formData: FormData | undefined): Promise<void>;
134
+ takeGuestVisitorId(): Promise<string | null>;
135
+ }
136
+ declare const DEFAULT_GUEST_CARRIER_COOKIE = "matrx_guest_visitor";
137
+ declare function createGuestHandover(options: GuestHandoverOptions): GuestHandover;
138
+ /**
139
+ * Cookie options for the guest's browser client: its OWN cookie name, never the app singleton's. Pass as
140
+ * `createBrowserClient(url, key, { isSingleton: false, cookieOptions: guestClientCookieOptions(name) })`.
141
+ */
142
+ declare function guestClientCookieOptions(cookieName: string, secure?: boolean): {
143
+ name: string;
144
+ path: string;
145
+ sameSite: "lax";
146
+ secure: boolean;
147
+ maxAge: number;
148
+ };
149
+ interface GuestSessionAnswer {
150
+ organizationId: string;
151
+ adoptedFrom: string | null;
152
+ }
153
+ interface GuestSessionEnsurerOptions {
154
+ /** The guest browser client's `auth` (its own cookie). */
155
+ auth: {
156
+ getSession(): Promise<{
157
+ data: {
158
+ session: {
159
+ user?: {
160
+ id: string;
161
+ is_anonymous?: boolean;
162
+ };
163
+ } | null;
164
+ };
165
+ }>;
166
+ setSession(tokens: {
167
+ access_token: string;
168
+ refresh_token: string;
169
+ }): Promise<{
170
+ error: {
171
+ message: string;
172
+ } | null;
173
+ }>;
174
+ };
175
+ /** Workspace (organization) ids the guest belongs to. */
176
+ listWorkspaceIds(userId: string): Promise<string[]>;
177
+ /** Base URL of the server that mints guests (`POST <base>/auth/guest/session`). */
178
+ serverBaseUrl: string | null | undefined;
179
+ getFingerprint(): Promise<string>;
180
+ fetch?: typeof fetch;
181
+ lockName?: string;
182
+ }
183
+ /**
184
+ * The visitor's guest session, made once. Called on a signed-out visitor's FIRST save or job — never on a
185
+ * page view. Single flight per tab, and per browser where Web Locks exist (two tabs never mint two guests).
186
+ */
187
+ declare function createGuestSessionEnsurer(options: GuestSessionEnsurerOptions): () => Promise<GuestSessionAnswer | null>;
188
+
189
+ export { DEFAULT_GUEST_CARRIER_COOKIE, type GuestClaimOutcome, type GuestCookieJar, type GuestHandover, type GuestHandoverOptions, type GuestPossession, type GuestPromotion, type GuestSessionAnswer, type GuestSessionEnsurerOptions, type GuestStore, type HandoverVia, createGuestHandover, createGuestSessionEnsurer, guestClientCookieOptions, isGuestCookieName, looksLikeVisitorId };
@@ -0,0 +1,189 @@
1
+ type HandoverVia = "password_login" | "oauth_callback" | "email_otp" | "dev_login" | "sign_up";
2
+ interface GuestPossession {
3
+ guestId: string;
4
+ proof: "guest_session" | "visitor_id";
5
+ }
6
+ type GuestClaimOutcome = {
7
+ status: "none";
8
+ } | {
9
+ status: "same_account";
10
+ } | {
11
+ status: "claimed";
12
+ guestId: string;
13
+ records: number;
14
+ } | {
15
+ status: "failed";
16
+ guestId: string;
17
+ message: string;
18
+ };
19
+ type GuestPromotion = {
20
+ kind: "no_guest";
21
+ } | {
22
+ kind: "promoted";
23
+ userId: string;
24
+ } | {
25
+ kind: "email_in_use";
26
+ } | {
27
+ kind: "failed";
28
+ message: string;
29
+ };
30
+ /** A guessable `temp_` fallback id is never a proof of possession. */
31
+ declare function looksLikeVisitorId(id: string | null | undefined): id is string;
32
+ /** The guest cookie and its @supabase/ssr chunks (`<name>.0`, `<name>.1`, …). */
33
+ declare function isGuestCookieName(guestCookieName: string, name: string): boolean;
34
+ /** A request's cookie jar (Next's `cookies()` result fits). */
35
+ interface GuestCookieJar {
36
+ getAll(): {
37
+ name: string;
38
+ value: string;
39
+ }[];
40
+ get(name: string): {
41
+ value: string;
42
+ } | undefined;
43
+ set(name: string, value: string, options?: Record<string, unknown>): unknown;
44
+ delete(name: string): unknown;
45
+ }
46
+ type DbAnswer<T> = {
47
+ data: T | null;
48
+ error: {
49
+ message: string;
50
+ } | null;
51
+ };
52
+ /** What the host's server-side store answers (admin/service lane; the host owns the credentials). */
53
+ interface GuestStore {
54
+ /** Guest user ids the registry bound to any of these visitor ids. */
55
+ findGuestIdsByVisitor(visitorIds: string[]): Promise<DbAnswer<string[]>>;
56
+ /** True when the account is an anonymous (guest) one. */
57
+ isAnonymousUser(userId: string): Promise<boolean>;
58
+ /** `claim_guest_workspace`: membership moves, no row moves, all or nothing. */
59
+ claimWorkspace(args: {
60
+ guestId: string;
61
+ targetId: string;
62
+ via: string;
63
+ }): Promise<DbAnswer<{
64
+ records?: number;
65
+ status?: string;
66
+ }>>;
67
+ /** `guest_revive`: reopen a guest retention closed. */
68
+ revive(guestId: string): Promise<DbAnswer<unknown>>;
69
+ /** Make the guest's account permanent in place. `error.code` may carry `email_exists`. */
70
+ promoteUser(guestId: string, credentials: {
71
+ email: string;
72
+ password: string;
73
+ }): Promise<{
74
+ error: {
75
+ message: string;
76
+ code?: string;
77
+ } | null;
78
+ }>;
79
+ /** Stamp the registry row converted. */
80
+ stampConverted(guestId: string): Promise<{
81
+ error: {
82
+ message: string;
83
+ } | null;
84
+ }>;
85
+ }
86
+ interface GuestHandoverOptions {
87
+ /** The guest session's own cookie name (never the main auth cookie). */
88
+ guestCookieName: string;
89
+ /** First-party visitor cookie the guest lane already accepts, if the host has one. */
90
+ visitorCookieName?: string;
91
+ /** Short-lived carrier for the visitor id across an OAuth round trip. Default `matrx_guest_visitor`. */
92
+ carrierCookieName?: string;
93
+ /** Server cookie jar for this request. */
94
+ cookies(): Promise<GuestCookieJar>;
95
+ store: GuestStore;
96
+ /** Verified claims of the guest cookie. Default: a server client over `supabase` url + key. */
97
+ readGuestClaims?(jar: GuestCookieJar): Promise<{
98
+ sub?: string;
99
+ is_anonymous?: boolean;
100
+ } | null>;
101
+ supabase?: {
102
+ url: string;
103
+ publishableKey: string;
104
+ };
105
+ /** `Secure` on the carrier cookie. */
106
+ secureCookies?: boolean;
107
+ log?(message: string): void;
108
+ }
109
+ interface GuestHandover {
110
+ looksLikeVisitorId: typeof looksLikeVisitorId;
111
+ readGuestPossession(visitorId?: string | null): Promise<GuestPossession | null>;
112
+ claimGuestInto(guest: GuestPossession, accountId: string, via: HandoverVia): Promise<GuestClaimOutcome>;
113
+ handOverSession<R extends {
114
+ data: {
115
+ user?: {
116
+ id: string;
117
+ is_anonymous?: boolean;
118
+ } | null;
119
+ } | null;
120
+ error: unknown;
121
+ }>(via: HandoverVia, replace: () => Promise<R>, opts?: {
122
+ visitorId?: string | null;
123
+ }): Promise<R & {
124
+ guestClaim: GuestClaimOutcome;
125
+ }>;
126
+ settlePendingGuestClaim(accountId: string): Promise<GuestClaimOutcome>;
127
+ promoteGuest(args: {
128
+ email: string;
129
+ password: string;
130
+ visitorId?: string | null;
131
+ }): Promise<GuestPromotion>;
132
+ finishPromotion(): Promise<void>;
133
+ rememberGuestVisitorId(formData: FormData | undefined): Promise<void>;
134
+ takeGuestVisitorId(): Promise<string | null>;
135
+ }
136
+ declare const DEFAULT_GUEST_CARRIER_COOKIE = "matrx_guest_visitor";
137
+ declare function createGuestHandover(options: GuestHandoverOptions): GuestHandover;
138
+ /**
139
+ * Cookie options for the guest's browser client: its OWN cookie name, never the app singleton's. Pass as
140
+ * `createBrowserClient(url, key, { isSingleton: false, cookieOptions: guestClientCookieOptions(name) })`.
141
+ */
142
+ declare function guestClientCookieOptions(cookieName: string, secure?: boolean): {
143
+ name: string;
144
+ path: string;
145
+ sameSite: "lax";
146
+ secure: boolean;
147
+ maxAge: number;
148
+ };
149
+ interface GuestSessionAnswer {
150
+ organizationId: string;
151
+ adoptedFrom: string | null;
152
+ }
153
+ interface GuestSessionEnsurerOptions {
154
+ /** The guest browser client's `auth` (its own cookie). */
155
+ auth: {
156
+ getSession(): Promise<{
157
+ data: {
158
+ session: {
159
+ user?: {
160
+ id: string;
161
+ is_anonymous?: boolean;
162
+ };
163
+ } | null;
164
+ };
165
+ }>;
166
+ setSession(tokens: {
167
+ access_token: string;
168
+ refresh_token: string;
169
+ }): Promise<{
170
+ error: {
171
+ message: string;
172
+ } | null;
173
+ }>;
174
+ };
175
+ /** Workspace (organization) ids the guest belongs to. */
176
+ listWorkspaceIds(userId: string): Promise<string[]>;
177
+ /** Base URL of the server that mints guests (`POST <base>/auth/guest/session`). */
178
+ serverBaseUrl: string | null | undefined;
179
+ getFingerprint(): Promise<string>;
180
+ fetch?: typeof fetch;
181
+ lockName?: string;
182
+ }
183
+ /**
184
+ * The visitor's guest session, made once. Called on a signed-out visitor's FIRST save or job — never on a
185
+ * page view. Single flight per tab, and per browser where Web Locks exist (two tabs never mint two guests).
186
+ */
187
+ declare function createGuestSessionEnsurer(options: GuestSessionEnsurerOptions): () => Promise<GuestSessionAnswer | null>;
188
+
189
+ export { DEFAULT_GUEST_CARRIER_COOKIE, type GuestClaimOutcome, type GuestCookieJar, type GuestHandover, type GuestHandoverOptions, type GuestPossession, type GuestPromotion, type GuestSessionAnswer, type GuestSessionEnsurerOptions, type GuestStore, type HandoverVia, createGuestHandover, createGuestSessionEnsurer, guestClientCookieOptions, isGuestCookieName, looksLikeVisitorId };