@ai-matrx/auth 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +31 -1
- package/dist/auth-cookie-DWx6Q3xo.d.cts +168 -0
- package/dist/auth-cookie-DWx6Q3xo.d.ts +168 -0
- package/dist/cookies.cjs +233 -0
- package/dist/cookies.d.cts +109 -0
- package/dist/cookies.d.ts +109 -0
- package/dist/cookies.js +210 -0
- package/dist/credentials.cjs +68 -0
- package/dist/credentials.d.cts +82 -0
- package/dist/credentials.d.ts +82 -0
- package/dist/credentials.js +47 -0
- package/dist/index.cjs +189 -0
- package/dist/index.d.cts +86 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +166 -0
- package/dist/next.cjs +655 -0
- package/dist/next.d.cts +389 -0
- package/dist/next.d.ts +389 -0
- package/dist/next.js +637 -0
- package/dist/node.cjs +61 -0
- package/dist/node.d.cts +33 -0
- package/dist/node.d.ts +33 -0
- package/dist/node.js +36 -0
- package/dist/organizations.cjs +389 -0
- package/dist/organizations.d.cts +226 -0
- package/dist/organizations.d.ts +226 -0
- package/dist/organizations.js +366 -0
- package/dist/react.cjs +206 -0
- package/dist/react.d.cts +163 -0
- package/dist/react.d.ts +163 -0
- package/dist/react.js +186 -0
- package/dist/session-DhRO4euL.d.cts +102 -0
- package/dist/session-DhRO4euL.d.ts +102 -0
- package/dist/session-identity.cjs +125 -0
- package/dist/session-identity.d.cts +100 -0
- package/dist/session-identity.d.ts +100 -0
- package/dist/session-identity.js +104 -0
- package/dist/testing.cjs +67 -0
- package/dist/testing.d.cts +38 -0
- package/dist/testing.d.ts +38 -0
- package/dist/testing.js +46 -0
- package/package.json +186 -6
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// src/organization-ladder.ts
|
|
2
|
+
function resolveActiveOrganization(input) {
|
|
3
|
+
const { memberships } = input;
|
|
4
|
+
if (memberships.length === 0) return null;
|
|
5
|
+
const member = (id) => id ? memberships.find((m) => m.id === id) ?? null : null;
|
|
6
|
+
const held = member(input.held);
|
|
7
|
+
if (held) return { organization: held, rung: "held" };
|
|
8
|
+
const last = member(input.account?.lastActiveOrganizationId);
|
|
9
|
+
if (last) return { organization: last, rung: "last-active" };
|
|
10
|
+
const startup = member(input.account?.startupOrganizationId);
|
|
11
|
+
if (startup) return { organization: startup, rung: "startup" };
|
|
12
|
+
const first = input.first && member(input.first.id) || oldestMembership(memberships);
|
|
13
|
+
return first ? { organization: first, rung: "first" } : null;
|
|
14
|
+
}
|
|
15
|
+
async function resolveActiveOrganizationLazily(input) {
|
|
16
|
+
const { memberships } = input;
|
|
17
|
+
if (memberships.length === 0) return null;
|
|
18
|
+
const quick = resolveActiveOrganization({ memberships, held: input.held ?? null, account: null, first: null });
|
|
19
|
+
if (quick?.rung === "held") return quick;
|
|
20
|
+
const account = await input.readAccount();
|
|
21
|
+
const named = resolveActiveOrganization({ memberships, held: null, account, first: null });
|
|
22
|
+
if (named && named.rung !== "first") return named;
|
|
23
|
+
const first = input.first ? await input.first() : null;
|
|
24
|
+
return resolveActiveOrganization({ memberships, held: null, account: null, first });
|
|
25
|
+
}
|
|
26
|
+
function oldestMembership(memberships) {
|
|
27
|
+
let best = null;
|
|
28
|
+
for (const m of memberships) {
|
|
29
|
+
if (!best) best = m;
|
|
30
|
+
else if (m.joinedAt && (!best.joinedAt || m.joinedAt < best.joinedAt)) best = m;
|
|
31
|
+
}
|
|
32
|
+
return best;
|
|
33
|
+
}
|
|
34
|
+
function orderOrganizations(organizations, favouriteIds) {
|
|
35
|
+
const favourites = new Set(favouriteIds);
|
|
36
|
+
const rank = (o) => favourites.has(o.id) ? 0 : o.isOwn ? 1 : 2;
|
|
37
|
+
return [...organizations].sort((a, b) => rank(a) - rank(b));
|
|
38
|
+
}
|
|
39
|
+
function organizationMatches(org, query) {
|
|
40
|
+
const q = query.trim().toLowerCase();
|
|
41
|
+
if (!q) return true;
|
|
42
|
+
if (org.name.toLowerCase().includes(q)) return true;
|
|
43
|
+
const abbreviation = org.abbreviation?.trim().toLowerCase();
|
|
44
|
+
return !!abbreviation && abbreviation.includes(q);
|
|
45
|
+
}
|
|
46
|
+
var MEMBERSHIP_PAGE = 500;
|
|
47
|
+
var ID_CHUNK = 100;
|
|
48
|
+
async function readMemberships(db, userId) {
|
|
49
|
+
const memberships = [];
|
|
50
|
+
for (let from = 0; ; from += MEMBERSHIP_PAGE) {
|
|
51
|
+
const { data, error } = await db.rpc("mbr_for_user", { p_container_type: "organization" }).order("id", { ascending: true }).range(from, from + MEMBERSHIP_PAGE - 1);
|
|
52
|
+
if (error) throw new Error(`Your organizations could not be read: ${error.message}`);
|
|
53
|
+
const page = Array.isArray(data) ? data : [];
|
|
54
|
+
memberships.push(...page);
|
|
55
|
+
if (page.length < MEMBERSHIP_PAGE) break;
|
|
56
|
+
}
|
|
57
|
+
const active = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const m of memberships) {
|
|
59
|
+
if (m.status !== "active") continue;
|
|
60
|
+
const seen = active.get(m.container_id);
|
|
61
|
+
if (!seen || m.created_at < seen.created_at) active.set(m.container_id, m);
|
|
62
|
+
}
|
|
63
|
+
const ids = [...active.keys()];
|
|
64
|
+
if (ids.length === 0) return [];
|
|
65
|
+
const rows = [];
|
|
66
|
+
for (let i = 0; i < ids.length; i += ID_CHUNK) {
|
|
67
|
+
const { data, error } = await db.schema("iam").from("organizations").select("id, name, slug, abbreviation, logo_url, created_by, settings, archived_at").in("id", ids.slice(i, i + ID_CHUNK));
|
|
68
|
+
if (error) throw new Error(`Your organizations could not be read: ${error.message}`);
|
|
69
|
+
rows.push(...data ?? []);
|
|
70
|
+
}
|
|
71
|
+
return rows.filter((r) => !r.archived_at).map((r) => {
|
|
72
|
+
const m = active.get(r.id);
|
|
73
|
+
const settings = r.settings && typeof r.settings === "object" ? r.settings : {};
|
|
74
|
+
return {
|
|
75
|
+
id: r.id,
|
|
76
|
+
name: r.name?.trim() || "Untitled organization",
|
|
77
|
+
slug: r.slug ?? null,
|
|
78
|
+
abbreviation: r.abbreviation?.trim() || null,
|
|
79
|
+
logoUrl: r.logo_url?.trim() || null,
|
|
80
|
+
role: m?.role ?? "member",
|
|
81
|
+
isOwn: !!r.created_by && r.created_by === userId,
|
|
82
|
+
isTestFixture: settings["test_fixture"] === true,
|
|
83
|
+
joinedAt: m?.created_at ?? ""
|
|
84
|
+
};
|
|
85
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
86
|
+
}
|
|
87
|
+
function toAccountOrganizationChoices(row) {
|
|
88
|
+
return {
|
|
89
|
+
lastActiveOrganizationId: row?.last_active_organization_id ?? null,
|
|
90
|
+
startupOrganizationId: row?.startup_organization_id ?? null
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function readAccountOrganizationChoices(db, userId) {
|
|
94
|
+
const { data, error } = await db.schema("users").from("user_preferences").select("last_active_organization_id, startup_organization_id").eq("user_id", userId).maybeSingle();
|
|
95
|
+
if (error) throw new Error(`The account's organization read failed: ${error.message}`);
|
|
96
|
+
return toAccountOrganizationChoices(data);
|
|
97
|
+
}
|
|
98
|
+
async function writeLastActiveOrganization(db, organizationId) {
|
|
99
|
+
const { error } = await db.schema("users").rpc("set_last_active_organization", { p_organization_id: organizationId });
|
|
100
|
+
if (error) throw new Error(error.message);
|
|
101
|
+
}
|
|
102
|
+
async function writeStartupOrganization(db, organizationId) {
|
|
103
|
+
const { error } = await db.schema("users").rpc("set_startup_organization", { p_organization_id: organizationId });
|
|
104
|
+
if (error) throw new Error(error.message);
|
|
105
|
+
}
|
|
106
|
+
var ORGANIZATION_ENTITY_TYPE = "organization";
|
|
107
|
+
async function readOrganizationFavourites(db) {
|
|
108
|
+
const { data, error } = await db.rpc("ues_list", { p_kind: "favorite" });
|
|
109
|
+
if (error) throw new Error(`Your favourite organizations could not be read: ${error.message}`);
|
|
110
|
+
return (Array.isArray(data) ? data : []).filter((r) => r.entity_type === ORGANIZATION_ENTITY_TYPE && r.is_favorite).map((r) => r.entity_id);
|
|
111
|
+
}
|
|
112
|
+
async function setOrganizationFavourite(db, organizationId, favourite) {
|
|
113
|
+
const { error } = await db.rpc("ues_set", { p_entity_type: ORGANIZATION_ENTITY_TYPE, p_entity_id: organizationId, p_is_favorite: favourite });
|
|
114
|
+
if (error) throw new Error(error.message);
|
|
115
|
+
}
|
|
116
|
+
function classifyOrganizationRefusal(status, body) {
|
|
117
|
+
const detail = body && typeof body === "object" ? body["detail"] ?? body : null;
|
|
118
|
+
const admission = detail && typeof detail === "object" ? detail["organization_admission"] : null;
|
|
119
|
+
if (status === 400 && (admission === "denied" || admission === "unknown" || admission === "archived")) return "lapsed";
|
|
120
|
+
if (status === 503 && admission === "unverifiable") return "retry";
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/session.ts
|
|
125
|
+
var SignInError = class extends Error {
|
|
126
|
+
door;
|
|
127
|
+
status;
|
|
128
|
+
code;
|
|
129
|
+
constructor(door, message, status, code) {
|
|
130
|
+
super(`${door}: ${message}`);
|
|
131
|
+
this.name = "SignInError";
|
|
132
|
+
this.door = door;
|
|
133
|
+
if (status !== void 0) this.status = status;
|
|
134
|
+
if (code !== void 0) this.code = code;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function check(door, data, error) {
|
|
138
|
+
if (error) throw new SignInError(door, error.message, error.status, error.code);
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
async function getPerson(client) {
|
|
142
|
+
const { data, error } = await client.auth.getUser();
|
|
143
|
+
if (error && (error.name === "AuthSessionMissingError" || error.status === 401)) return null;
|
|
144
|
+
return check("getPerson", data, error).user;
|
|
145
|
+
}
|
|
146
|
+
function onSessionChange(client, cb) {
|
|
147
|
+
const { data } = client.auth.onAuthStateChange(cb);
|
|
148
|
+
return () => data.subscription.unsubscribe();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/active-organization.ts
|
|
152
|
+
var NoActiveOrganizationError = class extends Error {
|
|
153
|
+
constructor(reason, detail) {
|
|
154
|
+
super(
|
|
155
|
+
reason === "signed-out" ? "No organization: nobody is signed in." : reason === "no-memberships" ? "No organization: this account is not an active member of any organization." : `No organization: your organizations could not be checked${detail ? ` (${detail})` : ""}.`
|
|
156
|
+
);
|
|
157
|
+
this.reason = reason;
|
|
158
|
+
this.name = "NoActiveOrganizationError";
|
|
159
|
+
}
|
|
160
|
+
reason;
|
|
161
|
+
};
|
|
162
|
+
var NotAMemberError = class extends Error {
|
|
163
|
+
constructor(organizationId) {
|
|
164
|
+
super(`You are not an active member of organization ${organizationId}.`);
|
|
165
|
+
this.organizationId = organizationId;
|
|
166
|
+
this.name = "NotAMemberError";
|
|
167
|
+
}
|
|
168
|
+
organizationId;
|
|
169
|
+
};
|
|
170
|
+
var HELD_KEY = "ai-matrx.auth.active-organization";
|
|
171
|
+
function storageHeldOrganization(storage, key = HELD_KEY) {
|
|
172
|
+
return {
|
|
173
|
+
read(userId) {
|
|
174
|
+
try {
|
|
175
|
+
const raw = storage.getItem(key);
|
|
176
|
+
if (!raw) return null;
|
|
177
|
+
const parsed = JSON.parse(raw);
|
|
178
|
+
return parsed.userId === userId && typeof parsed.organizationId === "string" ? parsed.organizationId : null;
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
write(userId, organizationId) {
|
|
184
|
+
storage.setItem(key, JSON.stringify({ userId, organizationId }));
|
|
185
|
+
},
|
|
186
|
+
clear() {
|
|
187
|
+
storage.removeItem(key);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function memoryHeldOrganization() {
|
|
192
|
+
let held = null;
|
|
193
|
+
return {
|
|
194
|
+
read: (userId) => held && held.userId === userId ? held.organizationId : null,
|
|
195
|
+
write: (userId, organizationId) => {
|
|
196
|
+
held = { userId, organizationId };
|
|
197
|
+
},
|
|
198
|
+
clear: () => {
|
|
199
|
+
held = null;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function defaultHeld() {
|
|
204
|
+
const session = globalThis.sessionStorage;
|
|
205
|
+
return session ? storageHeldOrganization(session) : memoryHeldOrganization();
|
|
206
|
+
}
|
|
207
|
+
var IDLE = Object.freeze({
|
|
208
|
+
status: "idle",
|
|
209
|
+
userId: null,
|
|
210
|
+
organizationId: null,
|
|
211
|
+
organization: null,
|
|
212
|
+
memberships: Object.freeze([]),
|
|
213
|
+
rung: null,
|
|
214
|
+
error: null
|
|
215
|
+
});
|
|
216
|
+
function createActiveOrganization(options) {
|
|
217
|
+
const { client } = options;
|
|
218
|
+
const held = options.held ?? defaultHeld();
|
|
219
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
220
|
+
let current = IDLE;
|
|
221
|
+
let inflight = null;
|
|
222
|
+
let generation = 0;
|
|
223
|
+
let waiters = [];
|
|
224
|
+
let disposed = false;
|
|
225
|
+
const publish = (next) => {
|
|
226
|
+
current = Object.freeze(next);
|
|
227
|
+
if (current.status !== "resolving" && current.status !== "idle") {
|
|
228
|
+
const ready = waiters;
|
|
229
|
+
waiters = [];
|
|
230
|
+
for (const w of ready) w();
|
|
231
|
+
}
|
|
232
|
+
for (const l of [...listeners]) l();
|
|
233
|
+
};
|
|
234
|
+
const run = async (gen) => {
|
|
235
|
+
publish({ ...current, status: "resolving", error: null });
|
|
236
|
+
try {
|
|
237
|
+
const person = await getPerson(client);
|
|
238
|
+
if (gen !== generation) return current;
|
|
239
|
+
if (!person) {
|
|
240
|
+
publish({ ...IDLE, status: "none", error: "signed-out" });
|
|
241
|
+
return current;
|
|
242
|
+
}
|
|
243
|
+
const memberships = await readMemberships(client, person.id);
|
|
244
|
+
if (gen !== generation) return current;
|
|
245
|
+
const linked = options.linked?.() ?? null;
|
|
246
|
+
const heldId = (linked && memberships.some((m) => m.id === linked) ? linked : null) ?? await held.read(person.id);
|
|
247
|
+
const resolved = await resolveActiveOrganizationLazily({
|
|
248
|
+
memberships,
|
|
249
|
+
held: heldId,
|
|
250
|
+
readAccount: () => readAccountOrganizationChoices(client, person.id)
|
|
251
|
+
});
|
|
252
|
+
if (gen !== generation) return current;
|
|
253
|
+
if (!resolved) {
|
|
254
|
+
publish({ ...IDLE, status: "none", userId: person.id, error: "no-memberships" });
|
|
255
|
+
return current;
|
|
256
|
+
}
|
|
257
|
+
await held.write(person.id, resolved.organization.id);
|
|
258
|
+
publish({
|
|
259
|
+
status: "ready",
|
|
260
|
+
userId: person.id,
|
|
261
|
+
organizationId: resolved.organization.id,
|
|
262
|
+
organization: resolved.organization,
|
|
263
|
+
memberships,
|
|
264
|
+
rung: resolved.rung,
|
|
265
|
+
error: null
|
|
266
|
+
});
|
|
267
|
+
return current;
|
|
268
|
+
} catch (e) {
|
|
269
|
+
if (gen !== generation) return current;
|
|
270
|
+
publish({ ...current, status: "error", error: e?.message ?? String(e) });
|
|
271
|
+
return current;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const load = () => {
|
|
275
|
+
if (disposed) return Promise.resolve(current);
|
|
276
|
+
generation += 1;
|
|
277
|
+
inflight = run(generation).finally(() => {
|
|
278
|
+
inflight = null;
|
|
279
|
+
});
|
|
280
|
+
return inflight;
|
|
281
|
+
};
|
|
282
|
+
const stopSession = onSessionChange(client, (event, session) => {
|
|
283
|
+
if (event === "SIGNED_OUT" || !session) {
|
|
284
|
+
generation += 1;
|
|
285
|
+
void held.clear();
|
|
286
|
+
publish({ ...IDLE, status: "none", error: "signed-out" });
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const userId = session.user?.id ?? null;
|
|
290
|
+
if (event === "SIGNED_IN" && userId !== current.userId) void load();
|
|
291
|
+
});
|
|
292
|
+
const settled = async () => {
|
|
293
|
+
if (current.status === "idle") return load();
|
|
294
|
+
if (current.status === "resolving") {
|
|
295
|
+
await new Promise((resolve) => waiters.push(resolve));
|
|
296
|
+
}
|
|
297
|
+
return current;
|
|
298
|
+
};
|
|
299
|
+
const api = {
|
|
300
|
+
snapshot: () => current,
|
|
301
|
+
subscribe(listener) {
|
|
302
|
+
listeners.add(listener);
|
|
303
|
+
return () => {
|
|
304
|
+
listeners.delete(listener);
|
|
305
|
+
};
|
|
306
|
+
},
|
|
307
|
+
load,
|
|
308
|
+
current: () => current.status === "ready" ? current.organizationId : null,
|
|
309
|
+
async ensureOrgId(recordOrganizationId) {
|
|
310
|
+
if (recordOrganizationId) return recordOrganizationId;
|
|
311
|
+
const snap = await settled();
|
|
312
|
+
if (snap.status === "ready" && snap.organizationId) return snap.organizationId;
|
|
313
|
+
if (snap.status === "error") throw new NoActiveOrganizationError("unreadable", snap.error ?? void 0);
|
|
314
|
+
throw new NoActiveOrganizationError(snap.error === "signed-out" ? "signed-out" : "no-memberships");
|
|
315
|
+
},
|
|
316
|
+
async switchTo(organizationId) {
|
|
317
|
+
const snap = await settled();
|
|
318
|
+
const organization = snap.memberships.find((m) => m.id === organizationId);
|
|
319
|
+
if (!organization || !snap.userId) throw new NotAMemberError(organizationId);
|
|
320
|
+
generation += 1;
|
|
321
|
+
await held.write(snap.userId, organizationId);
|
|
322
|
+
publish({ ...snap, status: "ready", organizationId, organization, rung: "switched", error: null });
|
|
323
|
+
await writeLastActiveOrganization(client, organizationId);
|
|
324
|
+
},
|
|
325
|
+
async handleRefusal(status, body) {
|
|
326
|
+
const kind = classifyOrganizationRefusal(status, body);
|
|
327
|
+
if (kind === "lapsed") {
|
|
328
|
+
if (current.userId) await held.clear();
|
|
329
|
+
await load();
|
|
330
|
+
}
|
|
331
|
+
return kind;
|
|
332
|
+
},
|
|
333
|
+
dispose() {
|
|
334
|
+
disposed = true;
|
|
335
|
+
generation += 1;
|
|
336
|
+
stopSession();
|
|
337
|
+
listeners.clear();
|
|
338
|
+
const pending = waiters;
|
|
339
|
+
waiters = [];
|
|
340
|
+
for (const w of pending) w();
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
if (options.autoLoad ?? true) void load();
|
|
344
|
+
return api;
|
|
345
|
+
}
|
|
346
|
+
export {
|
|
347
|
+
NoActiveOrganizationError,
|
|
348
|
+
NotAMemberError,
|
|
349
|
+
ORGANIZATION_ENTITY_TYPE,
|
|
350
|
+
classifyOrganizationRefusal,
|
|
351
|
+
createActiveOrganization,
|
|
352
|
+
memoryHeldOrganization,
|
|
353
|
+
oldestMembership,
|
|
354
|
+
orderOrganizations,
|
|
355
|
+
organizationMatches,
|
|
356
|
+
readAccountOrganizationChoices,
|
|
357
|
+
readMemberships,
|
|
358
|
+
readOrganizationFavourites,
|
|
359
|
+
resolveActiveOrganization,
|
|
360
|
+
resolveActiveOrganizationLazily,
|
|
361
|
+
setOrganizationFavourite,
|
|
362
|
+
storageHeldOrganization,
|
|
363
|
+
toAccountOrganizationChoices,
|
|
364
|
+
writeLastActiveOrganization,
|
|
365
|
+
writeStartupOrganization
|
|
366
|
+
};
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/react.ts
|
|
23
|
+
var react_exports = {};
|
|
24
|
+
__export(react_exports, {
|
|
25
|
+
useActiveOrganization: () => useActiveOrganization,
|
|
26
|
+
useSession: () => useSession,
|
|
27
|
+
useSessionIdentity: () => useSessionIdentity
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(react_exports);
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
|
|
32
|
+
// src/session.ts
|
|
33
|
+
var SignInError = class extends Error {
|
|
34
|
+
door;
|
|
35
|
+
status;
|
|
36
|
+
code;
|
|
37
|
+
constructor(door, message, status, code) {
|
|
38
|
+
super(`${door}: ${message}`);
|
|
39
|
+
this.name = "SignInError";
|
|
40
|
+
this.door = door;
|
|
41
|
+
if (status !== void 0) this.status = status;
|
|
42
|
+
if (code !== void 0) this.code = code;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function check(door, data, error) {
|
|
46
|
+
if (error) throw new SignInError(door, error.message, error.status, error.code);
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
async function getSession(client) {
|
|
50
|
+
const { data, error } = await client.auth.getSession();
|
|
51
|
+
return check("getSession", data, error).session;
|
|
52
|
+
}
|
|
53
|
+
function onSessionChange(client, cb) {
|
|
54
|
+
const { data } = client.auth.onAuthStateChange(cb);
|
|
55
|
+
return () => data.subscription.unsubscribe();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/session-identity.ts
|
|
59
|
+
var RESOLVING = Object.freeze({
|
|
60
|
+
state: "resolving",
|
|
61
|
+
userId: null
|
|
62
|
+
});
|
|
63
|
+
var SERVER_SESSION_IDENTITY = RESOLVING;
|
|
64
|
+
function authOf(client) {
|
|
65
|
+
if (client === null || typeof client !== "object" && typeof client !== "function") return null;
|
|
66
|
+
const candidate = client.auth;
|
|
67
|
+
if (candidate === null || typeof candidate !== "object") return null;
|
|
68
|
+
const auth = candidate;
|
|
69
|
+
if (typeof auth.getSession !== "function") return null;
|
|
70
|
+
if (typeof auth.onAuthStateChange !== "function") return null;
|
|
71
|
+
return auth;
|
|
72
|
+
}
|
|
73
|
+
function sessionUserId(session) {
|
|
74
|
+
const id = session?.user?.id;
|
|
75
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
76
|
+
}
|
|
77
|
+
function noAuthSourceMessage(words) {
|
|
78
|
+
return {
|
|
79
|
+
message: `${words.product} cannot tell who is signed in: the Supabase client it was given has no auth session source.`,
|
|
80
|
+
remedy: words.noAuthSourceRemedy
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function unreadableSessionMessage(words, error) {
|
|
84
|
+
return `${words.product} could not read the signed-in session: ${String(
|
|
85
|
+
error?.message ?? error
|
|
86
|
+
)}`;
|
|
87
|
+
}
|
|
88
|
+
function createSessionIdentity(options) {
|
|
89
|
+
const { client, onDiagnostic, words } = options;
|
|
90
|
+
const brand = options.toUserId ?? ((id) => id);
|
|
91
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
92
|
+
let current = RESOLVING;
|
|
93
|
+
let disposed = false;
|
|
94
|
+
const userIdOf = (session) => {
|
|
95
|
+
const id = sessionUserId(session);
|
|
96
|
+
return id === null ? null : brand(id);
|
|
97
|
+
};
|
|
98
|
+
const publish = (state, userId) => {
|
|
99
|
+
if (disposed) return;
|
|
100
|
+
if (current.state === state && current.userId === userId) return;
|
|
101
|
+
current = Object.freeze({ state, userId });
|
|
102
|
+
for (const listener of [...listeners]) listener();
|
|
103
|
+
};
|
|
104
|
+
const subscribeOnly = (hasAuthSource) => ({
|
|
105
|
+
hasAuthSource,
|
|
106
|
+
snapshot: () => current,
|
|
107
|
+
subscribe: (listener) => {
|
|
108
|
+
listeners.add(listener);
|
|
109
|
+
return () => {
|
|
110
|
+
listeners.delete(listener);
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
dispose: () => {
|
|
114
|
+
disposed = true;
|
|
115
|
+
listeners.clear();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const auth = authOf(client);
|
|
119
|
+
if (auth === null) {
|
|
120
|
+
onDiagnostic?.({ level: "error", ...noAuthSourceMessage(words) });
|
|
121
|
+
current = Object.freeze({ state: "resolved", userId: null });
|
|
122
|
+
return subscribeOnly(false);
|
|
123
|
+
}
|
|
124
|
+
void (async () => {
|
|
125
|
+
try {
|
|
126
|
+
const result = await auth.getSession();
|
|
127
|
+
publish("resolved", userIdOf(result?.data?.session));
|
|
128
|
+
} catch (error) {
|
|
129
|
+
onDiagnostic?.({
|
|
130
|
+
level: "warn",
|
|
131
|
+
message: unreadableSessionMessage(words, error),
|
|
132
|
+
remedy: words.unreadableRemedy
|
|
133
|
+
});
|
|
134
|
+
publish("resolved", null);
|
|
135
|
+
}
|
|
136
|
+
})();
|
|
137
|
+
const subscription = auth.onAuthStateChange((_event, session) => {
|
|
138
|
+
publish("resolved", userIdOf(session));
|
|
139
|
+
});
|
|
140
|
+
const base = subscribeOnly(true);
|
|
141
|
+
return {
|
|
142
|
+
hasAuthSource: true,
|
|
143
|
+
snapshot: base.snapshot,
|
|
144
|
+
subscribe: base.subscribe,
|
|
145
|
+
dispose: () => {
|
|
146
|
+
base.dispose();
|
|
147
|
+
try {
|
|
148
|
+
subscription.data.subscription.unsubscribe();
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/react.ts
|
|
156
|
+
var RESOLVING2 = Object.freeze({ status: "resolving", session: null, person: null, error: null });
|
|
157
|
+
function useSession(client) {
|
|
158
|
+
const [state, setState] = (0, import_react.useState)(RESOLVING2);
|
|
159
|
+
(0, import_react.useEffect)(() => {
|
|
160
|
+
let alive = true;
|
|
161
|
+
const apply = (session) => {
|
|
162
|
+
if (!alive) return;
|
|
163
|
+
setState(
|
|
164
|
+
session ? { status: "signed-in", session, person: session.user, error: null } : { status: "signed-out", session: null, person: null, error: null }
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
getSession(client).then(apply, (e) => {
|
|
168
|
+
if (alive) setState({ status: "error", session: null, person: null, error: e?.message ?? String(e) });
|
|
169
|
+
});
|
|
170
|
+
const stop = onSessionChange(client, (_event, session) => apply(session));
|
|
171
|
+
return () => {
|
|
172
|
+
alive = false;
|
|
173
|
+
stop();
|
|
174
|
+
};
|
|
175
|
+
}, [client]);
|
|
176
|
+
return state;
|
|
177
|
+
}
|
|
178
|
+
var SERVER_ACTIVE = Object.freeze({
|
|
179
|
+
status: "idle",
|
|
180
|
+
userId: null,
|
|
181
|
+
organizationId: null,
|
|
182
|
+
organization: null,
|
|
183
|
+
memberships: Object.freeze([]),
|
|
184
|
+
rung: null,
|
|
185
|
+
error: null
|
|
186
|
+
});
|
|
187
|
+
function useActiveOrganization(store) {
|
|
188
|
+
return (0, import_react.useSyncExternalStore)(store.subscribe, store.snapshot, () => SERVER_ACTIVE);
|
|
189
|
+
}
|
|
190
|
+
var NO_IDENTITY = {
|
|
191
|
+
subscribe: () => () => {
|
|
192
|
+
},
|
|
193
|
+
snapshot: () => SERVER_SESSION_IDENTITY
|
|
194
|
+
};
|
|
195
|
+
function useSessionIdentity(options) {
|
|
196
|
+
const latest = (0, import_react.useRef)(options);
|
|
197
|
+
latest.current = options;
|
|
198
|
+
const [identity, setIdentity] = (0, import_react.useState)(null);
|
|
199
|
+
(0, import_react.useEffect)(() => {
|
|
200
|
+
const made = createSessionIdentity(latest.current);
|
|
201
|
+
setIdentity(made);
|
|
202
|
+
return () => made.dispose();
|
|
203
|
+
}, [options.client]);
|
|
204
|
+
const source = identity ?? NO_IDENTITY;
|
|
205
|
+
return (0, import_react.useSyncExternalStore)(source.subscribe, source.snapshot, () => SERVER_SESSION_IDENTITY);
|
|
206
|
+
}
|