@ai-matrx/data 0.18.42 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -4
- package/dist/session-identity.cjs +126 -0
- package/dist/session-identity.cjs.map +1 -0
- package/dist/session-identity.d.cts +100 -0
- package/dist/session-identity.d.ts +100 -0
- package/dist/session-identity.js +105 -0
- package/dist/session-identity.js.map +1 -0
- package/dist/testing.cjs +68 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +38 -0
- package/dist/testing.d.ts +38 -0
- package/dist/testing.js +47 -0
- package/dist/testing.js.map +1 -0
- package/package.json +23 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.19.2
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
- Re-release of 0.19.1 (its publish stopped at the frozen-lockfile install: the tagged commit's
|
|
6
|
+
lockfile and the messaging manifest disagreed). Same contents.
|
|
7
|
+
|
|
8
|
+
Consumer action: none.
|
|
9
|
+
|
|
10
|
+
## 0.19.1
|
|
11
|
+
|
|
12
|
+
- `./session-identity` and `./testing` are reachable: 0.19.0 built both entries but its manifest
|
|
13
|
+
shipped without their `exports` rows (a concurrent automatic release rewrote the manifest).
|
|
14
|
+
|
|
15
|
+
Consumer action: none.
|
|
8
16
|
|
|
9
17
|
## 0.19.0 — the one session identity
|
|
10
18
|
|
|
@@ -18,6 +26,12 @@ No source changes intended and no consumer action required.
|
|
|
18
26
|
|
|
19
27
|
Consumer action: none (additive).
|
|
20
28
|
|
|
29
|
+
## 0.18.42
|
|
30
|
+
|
|
31
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
32
|
+
`git diff npm/data/v0.18.41..npm/data/v0.18.42 -- apps/shared/data`).
|
|
33
|
+
No source changes intended and no consumer action required.
|
|
34
|
+
|
|
21
35
|
## 0.18.41
|
|
22
36
|
|
|
23
37
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -0,0 +1,126 @@
|
|
|
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/session-identity.ts
|
|
21
|
+
var session_identity_exports = {};
|
|
22
|
+
__export(session_identity_exports, {
|
|
23
|
+
SERVER_SESSION_IDENTITY: () => SERVER_SESSION_IDENTITY,
|
|
24
|
+
authOf: () => authOf,
|
|
25
|
+
createSessionIdentity: () => createSessionIdentity,
|
|
26
|
+
noAuthSourceMessage: () => noAuthSourceMessage,
|
|
27
|
+
sessionUserId: () => sessionUserId,
|
|
28
|
+
unreadableSessionMessage: () => unreadableSessionMessage
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(session_identity_exports);
|
|
31
|
+
var RESOLVING = Object.freeze({
|
|
32
|
+
state: "resolving",
|
|
33
|
+
userId: null
|
|
34
|
+
});
|
|
35
|
+
var SERVER_SESSION_IDENTITY = RESOLVING;
|
|
36
|
+
function authOf(client) {
|
|
37
|
+
if (client === null || typeof client !== "object" && typeof client !== "function") return null;
|
|
38
|
+
const candidate = client.auth;
|
|
39
|
+
if (candidate === null || typeof candidate !== "object") return null;
|
|
40
|
+
const auth = candidate;
|
|
41
|
+
if (typeof auth.getSession !== "function") return null;
|
|
42
|
+
if (typeof auth.onAuthStateChange !== "function") return null;
|
|
43
|
+
return auth;
|
|
44
|
+
}
|
|
45
|
+
function sessionUserId(session) {
|
|
46
|
+
const id = session?.user?.id;
|
|
47
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
48
|
+
}
|
|
49
|
+
function noAuthSourceMessage(words) {
|
|
50
|
+
return {
|
|
51
|
+
message: `${words.product} cannot tell who is signed in: the Supabase client it was given has no auth session source.`,
|
|
52
|
+
remedy: words.noAuthSourceRemedy
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function unreadableSessionMessage(words, error) {
|
|
56
|
+
return `${words.product} could not read the signed-in session: ${String(
|
|
57
|
+
error?.message ?? error
|
|
58
|
+
)}`;
|
|
59
|
+
}
|
|
60
|
+
function createSessionIdentity(options) {
|
|
61
|
+
const { client, onDiagnostic, words } = options;
|
|
62
|
+
const brand = options.toUserId ?? ((id) => id);
|
|
63
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
64
|
+
let current = RESOLVING;
|
|
65
|
+
let disposed = false;
|
|
66
|
+
const userIdOf = (session) => {
|
|
67
|
+
const id = sessionUserId(session);
|
|
68
|
+
return id === null ? null : brand(id);
|
|
69
|
+
};
|
|
70
|
+
const publish = (state, userId) => {
|
|
71
|
+
if (disposed) return;
|
|
72
|
+
if (current.state === state && current.userId === userId) return;
|
|
73
|
+
current = Object.freeze({ state, userId });
|
|
74
|
+
for (const listener of [...listeners]) listener();
|
|
75
|
+
};
|
|
76
|
+
const subscribeOnly = (hasAuthSource) => ({
|
|
77
|
+
hasAuthSource,
|
|
78
|
+
snapshot: () => current,
|
|
79
|
+
subscribe: (listener) => {
|
|
80
|
+
listeners.add(listener);
|
|
81
|
+
return () => {
|
|
82
|
+
listeners.delete(listener);
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
dispose: () => {
|
|
86
|
+
disposed = true;
|
|
87
|
+
listeners.clear();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const auth = authOf(client);
|
|
91
|
+
if (auth === null) {
|
|
92
|
+
onDiagnostic?.({ level: "error", ...noAuthSourceMessage(words) });
|
|
93
|
+
current = Object.freeze({ state: "resolved", userId: null });
|
|
94
|
+
return subscribeOnly(false);
|
|
95
|
+
}
|
|
96
|
+
void (async () => {
|
|
97
|
+
try {
|
|
98
|
+
const result = await auth.getSession();
|
|
99
|
+
publish("resolved", userIdOf(result?.data?.session));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
onDiagnostic?.({
|
|
102
|
+
level: "warn",
|
|
103
|
+
message: unreadableSessionMessage(words, error),
|
|
104
|
+
remedy: words.unreadableRemedy
|
|
105
|
+
});
|
|
106
|
+
publish("resolved", null);
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
const subscription = auth.onAuthStateChange((_event, session) => {
|
|
110
|
+
publish("resolved", userIdOf(session));
|
|
111
|
+
});
|
|
112
|
+
const base = subscribeOnly(true);
|
|
113
|
+
return {
|
|
114
|
+
hasAuthSource: true,
|
|
115
|
+
snapshot: base.snapshot,
|
|
116
|
+
subscribe: base.subscribe,
|
|
117
|
+
dispose: () => {
|
|
118
|
+
base.dispose();
|
|
119
|
+
try {
|
|
120
|
+
subscription.data.subscription.unsubscribe();
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=session-identity.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session-identity.ts"],"sourcesContent":["/**\n * WHO IS ACTING — read from the Supabase client's session, never handed in (DD-240, DD-241).\n *\n * THE CLASS THIS CLOSES. A package that takes the acting user's id from its host (a `userId` prop\n * or option) gets the host's copy, written once when the app boots. Nothing rewrites that copy\n * when the domain-wide auth cookie rotates to another account, when the person signs out in\n * another tab, or when a refresh returns a different subject — so the package goes on issuing\n * reads (meet, DD-240) and writes (`sender_id`, `created_by` in messaging, DD-241) on behalf of\n * somebody the session no longer is. The defect is that a user id can be handed in at all.\n *\n * The rules:\n * - The acting user is `getSession()`'s answer, then every `onAuthStateChange` after it — the\n * same read supabase-js uses to mint the next request's JWT.\n * - `resolving` until the first read answers; nothing user-scoped is issued on a guess.\n * - `snapshot()` returns THE SAME REFERENCE until the acting user moves (a\n * `useSyncExternalStore` source that returns a fresh object per call is \"Maximum update depth\n * exceeded\", not a warning).\n * - A client with no auth source is a named misconfiguration (`hasAuthSource: false`), never a\n * silent fall back to \"nobody\".\n *\n * One home (architecture step 11): messaging and meet each carried a copy until data 0.19.0.\n * Each product passes its own words (`words`) and, when it brands ids, its `toUserId`.\n */\n\n/** What a session looks like to this module. Nothing else is read from it. */\nexport interface AuthSessionLike {\n readonly user?: { readonly id?: string | null } | null | undefined;\n}\n\nexport interface AuthSubscriptionLike {\n readonly data: { readonly subscription: { unsubscribe(): void } };\n}\n\n/** The subset of `client.auth` read here — an internal contract, never a public prop type. */\nexport interface AuthLike {\n getSession(): PromiseLike<{\n data: { session?: AuthSessionLike | null | undefined };\n }>;\n onAuthStateChange(\n callback: (event: string, session: AuthSessionLike | null) => void,\n ): AuthSubscriptionLike;\n}\n\nexport interface SessionIdentitySnapshot<Id extends string = string> {\n /** `resolving` until the first `getSession()` answers. */\n readonly state: \"resolving\" | \"resolved\";\n /** Who the session says is acting, or `null` for nobody. */\n readonly userId: Id | null;\n}\n\nexport interface SessionIdentity<Id extends string = string> {\n /** False when the client has no auth source at all — a misconfiguration, not \"signed out\". */\n readonly hasAuthSource: boolean;\n /** Stable reference until the acting user actually changes. */\n snapshot(): SessionIdentitySnapshot<Id>;\n subscribe(listener: () => void): () => void;\n dispose(): void;\n}\n\nexport interface SessionIdentityDiagnostic {\n level: \"info\" | \"warn\" | \"error\";\n message: string;\n remedy?: string | undefined;\n}\n\n/** The sentences a product says about its own session. */\nexport interface SessionIdentityWords {\n /** The product's name as the subject of a sentence, e.g. \"Messaging\". */\n readonly product: string;\n /** Remedy when the client has no auth source. */\n readonly noAuthSourceRemedy: string;\n /** Remedy when the session could not be read. */\n readonly unreadableRemedy: string;\n}\n\nexport interface SessionIdentityOptions<Id extends string = string> {\n /** The host's browser Supabase client (anything with `auth.getSession` / `onAuthStateChange`). */\n readonly client: unknown;\n readonly words: SessionIdentityWords;\n /** Brand the raw id (default: the string itself). */\n readonly toUserId?: ((id: string) => Id) | undefined;\n readonly onDiagnostic?: ((event: SessionIdentityDiagnostic) => void) | undefined;\n}\n\nconst RESOLVING: SessionIdentitySnapshot<never> = Object.freeze({\n state: \"resolving\" as const,\n userId: null,\n});\n\n/** The snapshot every server render sees: nobody is acting on a server. */\nexport const SERVER_SESSION_IDENTITY: SessionIdentitySnapshot<never> = RESOLVING;\n\n/**\n * The one narrowing. A client whose `auth` is missing or not shaped like an auth client is a real\n * failure — the caller says so and stays `resolved` with nobody acting (inert, never wrong).\n */\nexport function authOf(client: unknown): AuthLike | null {\n if (client === null || (typeof client !== \"object\" && typeof client !== \"function\")) return null;\n const candidate = (client as { auth?: unknown }).auth;\n if (candidate === null || typeof candidate !== \"object\") return null;\n const auth = candidate as Partial<AuthLike>;\n if (typeof auth.getSession !== \"function\") return null;\n if (typeof auth.onAuthStateChange !== \"function\") return null;\n return auth as AuthLike;\n}\n\n/** The session's user id, or `null` for nobody. */\nexport function sessionUserId(session: AuthSessionLike | null | undefined): string | null {\n const id = session?.user?.id;\n return typeof id === \"string\" && id.length > 0 ? id : null;\n}\n\n/** The named failure a client with no auth source gets. */\nexport function noAuthSourceMessage(words: SessionIdentityWords): { message: string; remedy: string } {\n return {\n message:\n `${words.product} cannot tell who is signed in: the Supabase client it was given has no ` +\n \"auth session source.\",\n remedy: words.noAuthSourceRemedy,\n };\n}\n\n/** The sentence for a session read that threw. */\nexport function unreadableSessionMessage(words: SessionIdentityWords, error: unknown): string {\n return `${words.product} could not read the signed-in session: ${String(\n (error as Error)?.message ?? error,\n )}`;\n}\n\nexport function createSessionIdentity<Id extends string = string>(\n options: SessionIdentityOptions<Id>,\n): SessionIdentity<Id> {\n const { client, onDiagnostic, words } = options;\n const brand = options.toUserId ?? ((id: string) => id as Id);\n const listeners = new Set<() => void>();\n let current: SessionIdentitySnapshot<Id> = RESOLVING;\n let disposed = false;\n\n const userIdOf = (session: AuthSessionLike | null | undefined): Id | null => {\n const id = sessionUserId(session);\n return id === null ? null : brand(id);\n };\n\n const publish = (state: \"resolving\" | \"resolved\", userId: Id | null): void => {\n if (disposed) return;\n // SAME REFERENCE until the fact moves.\n if (current.state === state && current.userId === userId) return;\n current = Object.freeze({ state, userId });\n for (const listener of [...listeners]) listener();\n };\n\n const subscribeOnly = (hasAuthSource: boolean): SessionIdentity<Id> => ({\n hasAuthSource,\n snapshot: () => current,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose: () => {\n disposed = true;\n listeners.clear();\n },\n });\n\n const auth = authOf(client);\n if (auth === null) {\n onDiagnostic?.({ level: \"error\", ...noAuthSourceMessage(words) });\n current = Object.freeze({ state: \"resolved\" as const, userId: null });\n return subscribeOnly(false);\n }\n\n // The first read. `getSession()` awaits auth-js's own initialization and its single-flighted\n // refresh, so this is the same answer the next PostgREST request's JWT will carry.\n void (async () => {\n try {\n const result = await auth.getSession();\n publish(\"resolved\", userIdOf(result?.data?.session));\n } catch (error: unknown) {\n onDiagnostic?.({\n level: \"warn\",\n message: unreadableSessionMessage(words, error),\n remedy: words.unreadableRemedy,\n });\n publish(\"resolved\", null);\n }\n })();\n\n // …and every change after it: a sign-out, an account switch, a refresh with a different subject.\n const subscription = auth.onAuthStateChange((_event, session) => {\n publish(\"resolved\", userIdOf(session));\n });\n\n const base = subscribeOnly(true);\n return {\n hasAuthSource: true,\n snapshot: base.snapshot,\n subscribe: base.subscribe,\n dispose: () => {\n base.dispose();\n try {\n subscription.data.subscription.unsubscribe();\n } catch {\n // An already-torn-down subscription is not a failure worth a sentence.\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHO IS ACTING — read from the Supabase client's session, never handed in (DD-240, DD-241).
|
|
3
|
+
*
|
|
4
|
+
* THE CLASS THIS CLOSES. A package that takes the acting user's id from its host (a `userId` prop
|
|
5
|
+
* or option) gets the host's copy, written once when the app boots. Nothing rewrites that copy
|
|
6
|
+
* when the domain-wide auth cookie rotates to another account, when the person signs out in
|
|
7
|
+
* another tab, or when a refresh returns a different subject — so the package goes on issuing
|
|
8
|
+
* reads (meet, DD-240) and writes (`sender_id`, `created_by` in messaging, DD-241) on behalf of
|
|
9
|
+
* somebody the session no longer is. The defect is that a user id can be handed in at all.
|
|
10
|
+
*
|
|
11
|
+
* The rules:
|
|
12
|
+
* - The acting user is `getSession()`'s answer, then every `onAuthStateChange` after it — the
|
|
13
|
+
* same read supabase-js uses to mint the next request's JWT.
|
|
14
|
+
* - `resolving` until the first read answers; nothing user-scoped is issued on a guess.
|
|
15
|
+
* - `snapshot()` returns THE SAME REFERENCE until the acting user moves (a
|
|
16
|
+
* `useSyncExternalStore` source that returns a fresh object per call is "Maximum update depth
|
|
17
|
+
* exceeded", not a warning).
|
|
18
|
+
* - A client with no auth source is a named misconfiguration (`hasAuthSource: false`), never a
|
|
19
|
+
* silent fall back to "nobody".
|
|
20
|
+
*
|
|
21
|
+
* One home (architecture step 11): messaging and meet each carried a copy until data 0.19.0.
|
|
22
|
+
* Each product passes its own words (`words`) and, when it brands ids, its `toUserId`.
|
|
23
|
+
*/
|
|
24
|
+
/** What a session looks like to this module. Nothing else is read from it. */
|
|
25
|
+
interface AuthSessionLike {
|
|
26
|
+
readonly user?: {
|
|
27
|
+
readonly id?: string | null;
|
|
28
|
+
} | null | undefined;
|
|
29
|
+
}
|
|
30
|
+
interface AuthSubscriptionLike {
|
|
31
|
+
readonly data: {
|
|
32
|
+
readonly subscription: {
|
|
33
|
+
unsubscribe(): void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** The subset of `client.auth` read here — an internal contract, never a public prop type. */
|
|
38
|
+
interface AuthLike {
|
|
39
|
+
getSession(): PromiseLike<{
|
|
40
|
+
data: {
|
|
41
|
+
session?: AuthSessionLike | null | undefined;
|
|
42
|
+
};
|
|
43
|
+
}>;
|
|
44
|
+
onAuthStateChange(callback: (event: string, session: AuthSessionLike | null) => void): AuthSubscriptionLike;
|
|
45
|
+
}
|
|
46
|
+
interface SessionIdentitySnapshot<Id extends string = string> {
|
|
47
|
+
/** `resolving` until the first `getSession()` answers. */
|
|
48
|
+
readonly state: "resolving" | "resolved";
|
|
49
|
+
/** Who the session says is acting, or `null` for nobody. */
|
|
50
|
+
readonly userId: Id | null;
|
|
51
|
+
}
|
|
52
|
+
interface SessionIdentity<Id extends string = string> {
|
|
53
|
+
/** False when the client has no auth source at all — a misconfiguration, not "signed out". */
|
|
54
|
+
readonly hasAuthSource: boolean;
|
|
55
|
+
/** Stable reference until the acting user actually changes. */
|
|
56
|
+
snapshot(): SessionIdentitySnapshot<Id>;
|
|
57
|
+
subscribe(listener: () => void): () => void;
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
interface SessionIdentityDiagnostic {
|
|
61
|
+
level: "info" | "warn" | "error";
|
|
62
|
+
message: string;
|
|
63
|
+
remedy?: string | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** The sentences a product says about its own session. */
|
|
66
|
+
interface SessionIdentityWords {
|
|
67
|
+
/** The product's name as the subject of a sentence, e.g. "Messaging". */
|
|
68
|
+
readonly product: string;
|
|
69
|
+
/** Remedy when the client has no auth source. */
|
|
70
|
+
readonly noAuthSourceRemedy: string;
|
|
71
|
+
/** Remedy when the session could not be read. */
|
|
72
|
+
readonly unreadableRemedy: string;
|
|
73
|
+
}
|
|
74
|
+
interface SessionIdentityOptions<Id extends string = string> {
|
|
75
|
+
/** The host's browser Supabase client (anything with `auth.getSession` / `onAuthStateChange`). */
|
|
76
|
+
readonly client: unknown;
|
|
77
|
+
readonly words: SessionIdentityWords;
|
|
78
|
+
/** Brand the raw id (default: the string itself). */
|
|
79
|
+
readonly toUserId?: ((id: string) => Id) | undefined;
|
|
80
|
+
readonly onDiagnostic?: ((event: SessionIdentityDiagnostic) => void) | undefined;
|
|
81
|
+
}
|
|
82
|
+
/** The snapshot every server render sees: nobody is acting on a server. */
|
|
83
|
+
declare const SERVER_SESSION_IDENTITY: SessionIdentitySnapshot<never>;
|
|
84
|
+
/**
|
|
85
|
+
* The one narrowing. A client whose `auth` is missing or not shaped like an auth client is a real
|
|
86
|
+
* failure — the caller says so and stays `resolved` with nobody acting (inert, never wrong).
|
|
87
|
+
*/
|
|
88
|
+
declare function authOf(client: unknown): AuthLike | null;
|
|
89
|
+
/** The session's user id, or `null` for nobody. */
|
|
90
|
+
declare function sessionUserId(session: AuthSessionLike | null | undefined): string | null;
|
|
91
|
+
/** The named failure a client with no auth source gets. */
|
|
92
|
+
declare function noAuthSourceMessage(words: SessionIdentityWords): {
|
|
93
|
+
message: string;
|
|
94
|
+
remedy: string;
|
|
95
|
+
};
|
|
96
|
+
/** The sentence for a session read that threw. */
|
|
97
|
+
declare function unreadableSessionMessage(words: SessionIdentityWords, error: unknown): string;
|
|
98
|
+
declare function createSessionIdentity<Id extends string = string>(options: SessionIdentityOptions<Id>): SessionIdentity<Id>;
|
|
99
|
+
|
|
100
|
+
export { type AuthLike, type AuthSessionLike, type AuthSubscriptionLike, SERVER_SESSION_IDENTITY, type SessionIdentity, type SessionIdentityDiagnostic, type SessionIdentityOptions, type SessionIdentitySnapshot, type SessionIdentityWords, authOf, createSessionIdentity, noAuthSourceMessage, sessionUserId, unreadableSessionMessage };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHO IS ACTING — read from the Supabase client's session, never handed in (DD-240, DD-241).
|
|
3
|
+
*
|
|
4
|
+
* THE CLASS THIS CLOSES. A package that takes the acting user's id from its host (a `userId` prop
|
|
5
|
+
* or option) gets the host's copy, written once when the app boots. Nothing rewrites that copy
|
|
6
|
+
* when the domain-wide auth cookie rotates to another account, when the person signs out in
|
|
7
|
+
* another tab, or when a refresh returns a different subject — so the package goes on issuing
|
|
8
|
+
* reads (meet, DD-240) and writes (`sender_id`, `created_by` in messaging, DD-241) on behalf of
|
|
9
|
+
* somebody the session no longer is. The defect is that a user id can be handed in at all.
|
|
10
|
+
*
|
|
11
|
+
* The rules:
|
|
12
|
+
* - The acting user is `getSession()`'s answer, then every `onAuthStateChange` after it — the
|
|
13
|
+
* same read supabase-js uses to mint the next request's JWT.
|
|
14
|
+
* - `resolving` until the first read answers; nothing user-scoped is issued on a guess.
|
|
15
|
+
* - `snapshot()` returns THE SAME REFERENCE until the acting user moves (a
|
|
16
|
+
* `useSyncExternalStore` source that returns a fresh object per call is "Maximum update depth
|
|
17
|
+
* exceeded", not a warning).
|
|
18
|
+
* - A client with no auth source is a named misconfiguration (`hasAuthSource: false`), never a
|
|
19
|
+
* silent fall back to "nobody".
|
|
20
|
+
*
|
|
21
|
+
* One home (architecture step 11): messaging and meet each carried a copy until data 0.19.0.
|
|
22
|
+
* Each product passes its own words (`words`) and, when it brands ids, its `toUserId`.
|
|
23
|
+
*/
|
|
24
|
+
/** What a session looks like to this module. Nothing else is read from it. */
|
|
25
|
+
interface AuthSessionLike {
|
|
26
|
+
readonly user?: {
|
|
27
|
+
readonly id?: string | null;
|
|
28
|
+
} | null | undefined;
|
|
29
|
+
}
|
|
30
|
+
interface AuthSubscriptionLike {
|
|
31
|
+
readonly data: {
|
|
32
|
+
readonly subscription: {
|
|
33
|
+
unsubscribe(): void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** The subset of `client.auth` read here — an internal contract, never a public prop type. */
|
|
38
|
+
interface AuthLike {
|
|
39
|
+
getSession(): PromiseLike<{
|
|
40
|
+
data: {
|
|
41
|
+
session?: AuthSessionLike | null | undefined;
|
|
42
|
+
};
|
|
43
|
+
}>;
|
|
44
|
+
onAuthStateChange(callback: (event: string, session: AuthSessionLike | null) => void): AuthSubscriptionLike;
|
|
45
|
+
}
|
|
46
|
+
interface SessionIdentitySnapshot<Id extends string = string> {
|
|
47
|
+
/** `resolving` until the first `getSession()` answers. */
|
|
48
|
+
readonly state: "resolving" | "resolved";
|
|
49
|
+
/** Who the session says is acting, or `null` for nobody. */
|
|
50
|
+
readonly userId: Id | null;
|
|
51
|
+
}
|
|
52
|
+
interface SessionIdentity<Id extends string = string> {
|
|
53
|
+
/** False when the client has no auth source at all — a misconfiguration, not "signed out". */
|
|
54
|
+
readonly hasAuthSource: boolean;
|
|
55
|
+
/** Stable reference until the acting user actually changes. */
|
|
56
|
+
snapshot(): SessionIdentitySnapshot<Id>;
|
|
57
|
+
subscribe(listener: () => void): () => void;
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
interface SessionIdentityDiagnostic {
|
|
61
|
+
level: "info" | "warn" | "error";
|
|
62
|
+
message: string;
|
|
63
|
+
remedy?: string | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** The sentences a product says about its own session. */
|
|
66
|
+
interface SessionIdentityWords {
|
|
67
|
+
/** The product's name as the subject of a sentence, e.g. "Messaging". */
|
|
68
|
+
readonly product: string;
|
|
69
|
+
/** Remedy when the client has no auth source. */
|
|
70
|
+
readonly noAuthSourceRemedy: string;
|
|
71
|
+
/** Remedy when the session could not be read. */
|
|
72
|
+
readonly unreadableRemedy: string;
|
|
73
|
+
}
|
|
74
|
+
interface SessionIdentityOptions<Id extends string = string> {
|
|
75
|
+
/** The host's browser Supabase client (anything with `auth.getSession` / `onAuthStateChange`). */
|
|
76
|
+
readonly client: unknown;
|
|
77
|
+
readonly words: SessionIdentityWords;
|
|
78
|
+
/** Brand the raw id (default: the string itself). */
|
|
79
|
+
readonly toUserId?: ((id: string) => Id) | undefined;
|
|
80
|
+
readonly onDiagnostic?: ((event: SessionIdentityDiagnostic) => void) | undefined;
|
|
81
|
+
}
|
|
82
|
+
/** The snapshot every server render sees: nobody is acting on a server. */
|
|
83
|
+
declare const SERVER_SESSION_IDENTITY: SessionIdentitySnapshot<never>;
|
|
84
|
+
/**
|
|
85
|
+
* The one narrowing. A client whose `auth` is missing or not shaped like an auth client is a real
|
|
86
|
+
* failure — the caller says so and stays `resolved` with nobody acting (inert, never wrong).
|
|
87
|
+
*/
|
|
88
|
+
declare function authOf(client: unknown): AuthLike | null;
|
|
89
|
+
/** The session's user id, or `null` for nobody. */
|
|
90
|
+
declare function sessionUserId(session: AuthSessionLike | null | undefined): string | null;
|
|
91
|
+
/** The named failure a client with no auth source gets. */
|
|
92
|
+
declare function noAuthSourceMessage(words: SessionIdentityWords): {
|
|
93
|
+
message: string;
|
|
94
|
+
remedy: string;
|
|
95
|
+
};
|
|
96
|
+
/** The sentence for a session read that threw. */
|
|
97
|
+
declare function unreadableSessionMessage(words: SessionIdentityWords, error: unknown): string;
|
|
98
|
+
declare function createSessionIdentity<Id extends string = string>(options: SessionIdentityOptions<Id>): SessionIdentity<Id>;
|
|
99
|
+
|
|
100
|
+
export { type AuthLike, type AuthSessionLike, type AuthSubscriptionLike, SERVER_SESSION_IDENTITY, type SessionIdentity, type SessionIdentityDiagnostic, type SessionIdentityOptions, type SessionIdentitySnapshot, type SessionIdentityWords, authOf, createSessionIdentity, noAuthSourceMessage, sessionUserId, unreadableSessionMessage };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/session-identity.ts
|
|
2
|
+
var RESOLVING = Object.freeze({
|
|
3
|
+
state: "resolving",
|
|
4
|
+
userId: null
|
|
5
|
+
});
|
|
6
|
+
var SERVER_SESSION_IDENTITY = RESOLVING;
|
|
7
|
+
function authOf(client) {
|
|
8
|
+
if (client === null || typeof client !== "object" && typeof client !== "function") return null;
|
|
9
|
+
const candidate = client.auth;
|
|
10
|
+
if (candidate === null || typeof candidate !== "object") return null;
|
|
11
|
+
const auth = candidate;
|
|
12
|
+
if (typeof auth.getSession !== "function") return null;
|
|
13
|
+
if (typeof auth.onAuthStateChange !== "function") return null;
|
|
14
|
+
return auth;
|
|
15
|
+
}
|
|
16
|
+
function sessionUserId(session) {
|
|
17
|
+
const id = session?.user?.id;
|
|
18
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
19
|
+
}
|
|
20
|
+
function noAuthSourceMessage(words) {
|
|
21
|
+
return {
|
|
22
|
+
message: `${words.product} cannot tell who is signed in: the Supabase client it was given has no auth session source.`,
|
|
23
|
+
remedy: words.noAuthSourceRemedy
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function unreadableSessionMessage(words, error) {
|
|
27
|
+
return `${words.product} could not read the signed-in session: ${String(
|
|
28
|
+
error?.message ?? error
|
|
29
|
+
)}`;
|
|
30
|
+
}
|
|
31
|
+
function createSessionIdentity(options) {
|
|
32
|
+
const { client, onDiagnostic, words } = options;
|
|
33
|
+
const brand = options.toUserId ?? ((id) => id);
|
|
34
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
35
|
+
let current = RESOLVING;
|
|
36
|
+
let disposed = false;
|
|
37
|
+
const userIdOf = (session) => {
|
|
38
|
+
const id = sessionUserId(session);
|
|
39
|
+
return id === null ? null : brand(id);
|
|
40
|
+
};
|
|
41
|
+
const publish = (state, userId) => {
|
|
42
|
+
if (disposed) return;
|
|
43
|
+
if (current.state === state && current.userId === userId) return;
|
|
44
|
+
current = Object.freeze({ state, userId });
|
|
45
|
+
for (const listener of [...listeners]) listener();
|
|
46
|
+
};
|
|
47
|
+
const subscribeOnly = (hasAuthSource) => ({
|
|
48
|
+
hasAuthSource,
|
|
49
|
+
snapshot: () => current,
|
|
50
|
+
subscribe: (listener) => {
|
|
51
|
+
listeners.add(listener);
|
|
52
|
+
return () => {
|
|
53
|
+
listeners.delete(listener);
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
dispose: () => {
|
|
57
|
+
disposed = true;
|
|
58
|
+
listeners.clear();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const auth = authOf(client);
|
|
62
|
+
if (auth === null) {
|
|
63
|
+
onDiagnostic?.({ level: "error", ...noAuthSourceMessage(words) });
|
|
64
|
+
current = Object.freeze({ state: "resolved", userId: null });
|
|
65
|
+
return subscribeOnly(false);
|
|
66
|
+
}
|
|
67
|
+
void (async () => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await auth.getSession();
|
|
70
|
+
publish("resolved", userIdOf(result?.data?.session));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
onDiagnostic?.({
|
|
73
|
+
level: "warn",
|
|
74
|
+
message: unreadableSessionMessage(words, error),
|
|
75
|
+
remedy: words.unreadableRemedy
|
|
76
|
+
});
|
|
77
|
+
publish("resolved", null);
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
const subscription = auth.onAuthStateChange((_event, session) => {
|
|
81
|
+
publish("resolved", userIdOf(session));
|
|
82
|
+
});
|
|
83
|
+
const base = subscribeOnly(true);
|
|
84
|
+
return {
|
|
85
|
+
hasAuthSource: true,
|
|
86
|
+
snapshot: base.snapshot,
|
|
87
|
+
subscribe: base.subscribe,
|
|
88
|
+
dispose: () => {
|
|
89
|
+
base.dispose();
|
|
90
|
+
try {
|
|
91
|
+
subscription.data.subscription.unsubscribe();
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
SERVER_SESSION_IDENTITY,
|
|
99
|
+
authOf,
|
|
100
|
+
createSessionIdentity,
|
|
101
|
+
noAuthSourceMessage,
|
|
102
|
+
sessionUserId,
|
|
103
|
+
unreadableSessionMessage
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=session-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session-identity.ts"],"sourcesContent":["/**\n * WHO IS ACTING — read from the Supabase client's session, never handed in (DD-240, DD-241).\n *\n * THE CLASS THIS CLOSES. A package that takes the acting user's id from its host (a `userId` prop\n * or option) gets the host's copy, written once when the app boots. Nothing rewrites that copy\n * when the domain-wide auth cookie rotates to another account, when the person signs out in\n * another tab, or when a refresh returns a different subject — so the package goes on issuing\n * reads (meet, DD-240) and writes (`sender_id`, `created_by` in messaging, DD-241) on behalf of\n * somebody the session no longer is. The defect is that a user id can be handed in at all.\n *\n * The rules:\n * - The acting user is `getSession()`'s answer, then every `onAuthStateChange` after it — the\n * same read supabase-js uses to mint the next request's JWT.\n * - `resolving` until the first read answers; nothing user-scoped is issued on a guess.\n * - `snapshot()` returns THE SAME REFERENCE until the acting user moves (a\n * `useSyncExternalStore` source that returns a fresh object per call is \"Maximum update depth\n * exceeded\", not a warning).\n * - A client with no auth source is a named misconfiguration (`hasAuthSource: false`), never a\n * silent fall back to \"nobody\".\n *\n * One home (architecture step 11): messaging and meet each carried a copy until data 0.19.0.\n * Each product passes its own words (`words`) and, when it brands ids, its `toUserId`.\n */\n\n/** What a session looks like to this module. Nothing else is read from it. */\nexport interface AuthSessionLike {\n readonly user?: { readonly id?: string | null } | null | undefined;\n}\n\nexport interface AuthSubscriptionLike {\n readonly data: { readonly subscription: { unsubscribe(): void } };\n}\n\n/** The subset of `client.auth` read here — an internal contract, never a public prop type. */\nexport interface AuthLike {\n getSession(): PromiseLike<{\n data: { session?: AuthSessionLike | null | undefined };\n }>;\n onAuthStateChange(\n callback: (event: string, session: AuthSessionLike | null) => void,\n ): AuthSubscriptionLike;\n}\n\nexport interface SessionIdentitySnapshot<Id extends string = string> {\n /** `resolving` until the first `getSession()` answers. */\n readonly state: \"resolving\" | \"resolved\";\n /** Who the session says is acting, or `null` for nobody. */\n readonly userId: Id | null;\n}\n\nexport interface SessionIdentity<Id extends string = string> {\n /** False when the client has no auth source at all — a misconfiguration, not \"signed out\". */\n readonly hasAuthSource: boolean;\n /** Stable reference until the acting user actually changes. */\n snapshot(): SessionIdentitySnapshot<Id>;\n subscribe(listener: () => void): () => void;\n dispose(): void;\n}\n\nexport interface SessionIdentityDiagnostic {\n level: \"info\" | \"warn\" | \"error\";\n message: string;\n remedy?: string | undefined;\n}\n\n/** The sentences a product says about its own session. */\nexport interface SessionIdentityWords {\n /** The product's name as the subject of a sentence, e.g. \"Messaging\". */\n readonly product: string;\n /** Remedy when the client has no auth source. */\n readonly noAuthSourceRemedy: string;\n /** Remedy when the session could not be read. */\n readonly unreadableRemedy: string;\n}\n\nexport interface SessionIdentityOptions<Id extends string = string> {\n /** The host's browser Supabase client (anything with `auth.getSession` / `onAuthStateChange`). */\n readonly client: unknown;\n readonly words: SessionIdentityWords;\n /** Brand the raw id (default: the string itself). */\n readonly toUserId?: ((id: string) => Id) | undefined;\n readonly onDiagnostic?: ((event: SessionIdentityDiagnostic) => void) | undefined;\n}\n\nconst RESOLVING: SessionIdentitySnapshot<never> = Object.freeze({\n state: \"resolving\" as const,\n userId: null,\n});\n\n/** The snapshot every server render sees: nobody is acting on a server. */\nexport const SERVER_SESSION_IDENTITY: SessionIdentitySnapshot<never> = RESOLVING;\n\n/**\n * The one narrowing. A client whose `auth` is missing or not shaped like an auth client is a real\n * failure — the caller says so and stays `resolved` with nobody acting (inert, never wrong).\n */\nexport function authOf(client: unknown): AuthLike | null {\n if (client === null || (typeof client !== \"object\" && typeof client !== \"function\")) return null;\n const candidate = (client as { auth?: unknown }).auth;\n if (candidate === null || typeof candidate !== \"object\") return null;\n const auth = candidate as Partial<AuthLike>;\n if (typeof auth.getSession !== \"function\") return null;\n if (typeof auth.onAuthStateChange !== \"function\") return null;\n return auth as AuthLike;\n}\n\n/** The session's user id, or `null` for nobody. */\nexport function sessionUserId(session: AuthSessionLike | null | undefined): string | null {\n const id = session?.user?.id;\n return typeof id === \"string\" && id.length > 0 ? id : null;\n}\n\n/** The named failure a client with no auth source gets. */\nexport function noAuthSourceMessage(words: SessionIdentityWords): { message: string; remedy: string } {\n return {\n message:\n `${words.product} cannot tell who is signed in: the Supabase client it was given has no ` +\n \"auth session source.\",\n remedy: words.noAuthSourceRemedy,\n };\n}\n\n/** The sentence for a session read that threw. */\nexport function unreadableSessionMessage(words: SessionIdentityWords, error: unknown): string {\n return `${words.product} could not read the signed-in session: ${String(\n (error as Error)?.message ?? error,\n )}`;\n}\n\nexport function createSessionIdentity<Id extends string = string>(\n options: SessionIdentityOptions<Id>,\n): SessionIdentity<Id> {\n const { client, onDiagnostic, words } = options;\n const brand = options.toUserId ?? ((id: string) => id as Id);\n const listeners = new Set<() => void>();\n let current: SessionIdentitySnapshot<Id> = RESOLVING;\n let disposed = false;\n\n const userIdOf = (session: AuthSessionLike | null | undefined): Id | null => {\n const id = sessionUserId(session);\n return id === null ? null : brand(id);\n };\n\n const publish = (state: \"resolving\" | \"resolved\", userId: Id | null): void => {\n if (disposed) return;\n // SAME REFERENCE until the fact moves.\n if (current.state === state && current.userId === userId) return;\n current = Object.freeze({ state, userId });\n for (const listener of [...listeners]) listener();\n };\n\n const subscribeOnly = (hasAuthSource: boolean): SessionIdentity<Id> => ({\n hasAuthSource,\n snapshot: () => current,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose: () => {\n disposed = true;\n listeners.clear();\n },\n });\n\n const auth = authOf(client);\n if (auth === null) {\n onDiagnostic?.({ level: \"error\", ...noAuthSourceMessage(words) });\n current = Object.freeze({ state: \"resolved\" as const, userId: null });\n return subscribeOnly(false);\n }\n\n // The first read. `getSession()` awaits auth-js's own initialization and its single-flighted\n // refresh, so this is the same answer the next PostgREST request's JWT will carry.\n void (async () => {\n try {\n const result = await auth.getSession();\n publish(\"resolved\", userIdOf(result?.data?.session));\n } catch (error: unknown) {\n onDiagnostic?.({\n level: \"warn\",\n message: unreadableSessionMessage(words, error),\n remedy: words.unreadableRemedy,\n });\n publish(\"resolved\", null);\n }\n })();\n\n // …and every change after it: a sign-out, an account switch, a refresh with a different subject.\n const subscription = auth.onAuthStateChange((_event, session) => {\n publish(\"resolved\", userIdOf(session));\n });\n\n const base = subscribeOnly(true);\n return {\n hasAuthSource: true,\n snapshot: base.snapshot,\n subscribe: base.subscribe,\n dispose: () => {\n base.dispose();\n try {\n subscription.data.subscription.unsubscribe();\n } catch {\n // An already-torn-down subscription is not a failure worth a sentence.\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
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/testing.ts
|
|
21
|
+
var testing_exports = {};
|
|
22
|
+
__export(testing_exports, {
|
|
23
|
+
fakeSession: () => fakeSession,
|
|
24
|
+
signedInAs: () => signedInAs,
|
|
25
|
+
withSession: () => withSession
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(testing_exports);
|
|
28
|
+
function fakeSession(userId) {
|
|
29
|
+
let current = userId;
|
|
30
|
+
let reads = 0;
|
|
31
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
32
|
+
const sessionOf = (id) => id === null ? null : { user: { id } };
|
|
33
|
+
return {
|
|
34
|
+
auth: {
|
|
35
|
+
getSession: async () => {
|
|
36
|
+
reads += 1;
|
|
37
|
+
return { data: { session: sessionOf(current) } };
|
|
38
|
+
},
|
|
39
|
+
onAuthStateChange: (callback) => {
|
|
40
|
+
listeners.add(callback);
|
|
41
|
+
return {
|
|
42
|
+
data: {
|
|
43
|
+
subscription: {
|
|
44
|
+
unsubscribe: () => {
|
|
45
|
+
listeners.delete(callback);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
setUser: (next) => {
|
|
53
|
+
current = next;
|
|
54
|
+
for (const listener of [...listeners]) {
|
|
55
|
+
listener(next === null ? "SIGNED_OUT" : "SIGNED_IN", sessionOf(next));
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
reads: () => reads
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function withSession(client, session) {
|
|
62
|
+
client.auth = session.auth;
|
|
63
|
+
return client;
|
|
64
|
+
}
|
|
65
|
+
function signedInAs(client, userId) {
|
|
66
|
+
return withSession(client, fakeSession(userId));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Test helpers for packages that read the acting user from a Supabase client's session\n * (`@ai-matrx/data/session-identity`). Tests only — never imported by shipped runtime code.\n *\n * A test that wants a signed-in user says so the way the real world does: by giving the fake\n * client a session. `setUser` performs the thing that broke production — the account under the\n * tab changing while the tab stays open.\n */\nimport type { AuthSessionLike } from \"./session-identity\";\n\nexport interface FakeSession {\n readonly auth: {\n getSession(): Promise<{ data: { session: AuthSessionLike | null } }>;\n onAuthStateChange(\n callback: (event: string, session: AuthSessionLike | null) => void,\n ): { data: { subscription: { unsubscribe(): void } } };\n };\n /** Change who is signed in, exactly as a cookie rotation or a switch does. */\n setUser(userId: string | null): void;\n /** How many times the session has actually been read. */\n readonly reads: () => number;\n}\n\nexport function fakeSession(userId: string | null): FakeSession {\n let current: string | null = userId;\n let reads = 0;\n const listeners = new Set<(event: string, session: AuthSessionLike | null) => void>();\n const sessionOf = (id: string | null): AuthSessionLike | null => (id === null ? null : { user: { id } });\n return {\n auth: {\n getSession: async () => {\n reads += 1;\n return { data: { session: sessionOf(current) } };\n },\n onAuthStateChange: (callback) => {\n listeners.add(callback);\n return {\n data: {\n subscription: {\n unsubscribe: () => {\n listeners.delete(callback);\n },\n },\n },\n };\n },\n },\n setUser: (next) => {\n current = next;\n for (const listener of [...listeners]) {\n listener(next === null ? \"SIGNED_OUT\" : \"SIGNED_IN\", sessionOf(next));\n }\n },\n reads: () => reads,\n };\n}\n\n/** Attach a session to any fake client without disturbing its other methods. */\nexport function withSession<T>(client: T, session: FakeSession): T {\n (client as unknown as { auth: unknown }).auth = session.auth;\n return client;\n}\n\n/** The signed-in fake in one line, for the many tests that need nothing more. */\nexport function signedInAs<T>(client: T, userId: string): T {\n return withSession(client, fakeSession(userId));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBO,SAAS,YAAY,QAAoC;AAC9D,MAAI,UAAyB;AAC7B,MAAI,QAAQ;AACZ,QAAM,YAAY,oBAAI,IAA8D;AACpF,QAAM,YAAY,CAAC,OAA+C,OAAO,OAAO,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE;AACtG,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,YAAY,YAAY;AACtB,iBAAS;AACT,eAAO,EAAE,MAAM,EAAE,SAAS,UAAU,OAAO,EAAE,EAAE;AAAA,MACjD;AAAA,MACA,mBAAmB,CAAC,aAAa;AAC/B,kBAAU,IAAI,QAAQ;AACtB,eAAO;AAAA,UACL,MAAM;AAAA,YACJ,cAAc;AAAA,cACZ,aAAa,MAAM;AACjB,0BAAU,OAAO,QAAQ;AAAA,cAC3B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS,CAAC,SAAS;AACjB,gBAAU;AACV,iBAAW,YAAY,CAAC,GAAG,SAAS,GAAG;AACrC,iBAAS,SAAS,OAAO,eAAe,aAAa,UAAU,IAAI,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAGO,SAAS,YAAe,QAAW,SAAyB;AACjE,EAAC,OAAwC,OAAO,QAAQ;AACxD,SAAO;AACT;AAGO,SAAS,WAAc,QAAW,QAAmB;AAC1D,SAAO,YAAY,QAAQ,YAAY,MAAM,CAAC;AAChD;","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AuthSessionLike } from './session-identity.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test helpers for packages that read the acting user from a Supabase client's session
|
|
5
|
+
* (`@ai-matrx/data/session-identity`). Tests only — never imported by shipped runtime code.
|
|
6
|
+
*
|
|
7
|
+
* A test that wants a signed-in user says so the way the real world does: by giving the fake
|
|
8
|
+
* client a session. `setUser` performs the thing that broke production — the account under the
|
|
9
|
+
* tab changing while the tab stays open.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface FakeSession {
|
|
13
|
+
readonly auth: {
|
|
14
|
+
getSession(): Promise<{
|
|
15
|
+
data: {
|
|
16
|
+
session: AuthSessionLike | null;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
onAuthStateChange(callback: (event: string, session: AuthSessionLike | null) => void): {
|
|
20
|
+
data: {
|
|
21
|
+
subscription: {
|
|
22
|
+
unsubscribe(): void;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** Change who is signed in, exactly as a cookie rotation or a switch does. */
|
|
28
|
+
setUser(userId: string | null): void;
|
|
29
|
+
/** How many times the session has actually been read. */
|
|
30
|
+
readonly reads: () => number;
|
|
31
|
+
}
|
|
32
|
+
declare function fakeSession(userId: string | null): FakeSession;
|
|
33
|
+
/** Attach a session to any fake client without disturbing its other methods. */
|
|
34
|
+
declare function withSession<T>(client: T, session: FakeSession): T;
|
|
35
|
+
/** The signed-in fake in one line, for the many tests that need nothing more. */
|
|
36
|
+
declare function signedInAs<T>(client: T, userId: string): T;
|
|
37
|
+
|
|
38
|
+
export { type FakeSession, fakeSession, signedInAs, withSession };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AuthSessionLike } from './session-identity.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test helpers for packages that read the acting user from a Supabase client's session
|
|
5
|
+
* (`@ai-matrx/data/session-identity`). Tests only — never imported by shipped runtime code.
|
|
6
|
+
*
|
|
7
|
+
* A test that wants a signed-in user says so the way the real world does: by giving the fake
|
|
8
|
+
* client a session. `setUser` performs the thing that broke production — the account under the
|
|
9
|
+
* tab changing while the tab stays open.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface FakeSession {
|
|
13
|
+
readonly auth: {
|
|
14
|
+
getSession(): Promise<{
|
|
15
|
+
data: {
|
|
16
|
+
session: AuthSessionLike | null;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
onAuthStateChange(callback: (event: string, session: AuthSessionLike | null) => void): {
|
|
20
|
+
data: {
|
|
21
|
+
subscription: {
|
|
22
|
+
unsubscribe(): void;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** Change who is signed in, exactly as a cookie rotation or a switch does. */
|
|
28
|
+
setUser(userId: string | null): void;
|
|
29
|
+
/** How many times the session has actually been read. */
|
|
30
|
+
readonly reads: () => number;
|
|
31
|
+
}
|
|
32
|
+
declare function fakeSession(userId: string | null): FakeSession;
|
|
33
|
+
/** Attach a session to any fake client without disturbing its other methods. */
|
|
34
|
+
declare function withSession<T>(client: T, session: FakeSession): T;
|
|
35
|
+
/** The signed-in fake in one line, for the many tests that need nothing more. */
|
|
36
|
+
declare function signedInAs<T>(client: T, userId: string): T;
|
|
37
|
+
|
|
38
|
+
export { type FakeSession, fakeSession, signedInAs, withSession };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/testing.ts
|
|
2
|
+
function fakeSession(userId) {
|
|
3
|
+
let current = userId;
|
|
4
|
+
let reads = 0;
|
|
5
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6
|
+
const sessionOf = (id) => id === null ? null : { user: { id } };
|
|
7
|
+
return {
|
|
8
|
+
auth: {
|
|
9
|
+
getSession: async () => {
|
|
10
|
+
reads += 1;
|
|
11
|
+
return { data: { session: sessionOf(current) } };
|
|
12
|
+
},
|
|
13
|
+
onAuthStateChange: (callback) => {
|
|
14
|
+
listeners.add(callback);
|
|
15
|
+
return {
|
|
16
|
+
data: {
|
|
17
|
+
subscription: {
|
|
18
|
+
unsubscribe: () => {
|
|
19
|
+
listeners.delete(callback);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
setUser: (next) => {
|
|
27
|
+
current = next;
|
|
28
|
+
for (const listener of [...listeners]) {
|
|
29
|
+
listener(next === null ? "SIGNED_OUT" : "SIGNED_IN", sessionOf(next));
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
reads: () => reads
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function withSession(client, session) {
|
|
36
|
+
client.auth = session.auth;
|
|
37
|
+
return client;
|
|
38
|
+
}
|
|
39
|
+
function signedInAs(client, userId) {
|
|
40
|
+
return withSession(client, fakeSession(userId));
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
fakeSession,
|
|
44
|
+
signedInAs,
|
|
45
|
+
withSession
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Test helpers for packages that read the acting user from a Supabase client's session\n * (`@ai-matrx/data/session-identity`). Tests only — never imported by shipped runtime code.\n *\n * A test that wants a signed-in user says so the way the real world does: by giving the fake\n * client a session. `setUser` performs the thing that broke production — the account under the\n * tab changing while the tab stays open.\n */\nimport type { AuthSessionLike } from \"./session-identity\";\n\nexport interface FakeSession {\n readonly auth: {\n getSession(): Promise<{ data: { session: AuthSessionLike | null } }>;\n onAuthStateChange(\n callback: (event: string, session: AuthSessionLike | null) => void,\n ): { data: { subscription: { unsubscribe(): void } } };\n };\n /** Change who is signed in, exactly as a cookie rotation or a switch does. */\n setUser(userId: string | null): void;\n /** How many times the session has actually been read. */\n readonly reads: () => number;\n}\n\nexport function fakeSession(userId: string | null): FakeSession {\n let current: string | null = userId;\n let reads = 0;\n const listeners = new Set<(event: string, session: AuthSessionLike | null) => void>();\n const sessionOf = (id: string | null): AuthSessionLike | null => (id === null ? null : { user: { id } });\n return {\n auth: {\n getSession: async () => {\n reads += 1;\n return { data: { session: sessionOf(current) } };\n },\n onAuthStateChange: (callback) => {\n listeners.add(callback);\n return {\n data: {\n subscription: {\n unsubscribe: () => {\n listeners.delete(callback);\n },\n },\n },\n };\n },\n },\n setUser: (next) => {\n current = next;\n for (const listener of [...listeners]) {\n listener(next === null ? \"SIGNED_OUT\" : \"SIGNED_IN\", sessionOf(next));\n }\n },\n reads: () => reads,\n };\n}\n\n/** Attach a session to any fake client without disturbing its other methods. */\nexport function withSession<T>(client: T, session: FakeSession): T {\n (client as unknown as { auth: unknown }).auth = session.auth;\n return client;\n}\n\n/** The signed-in fake in one line, for the many tests that need nothing more. */\nexport function signedInAs<T>(client: T, userId: string): T {\n return withSession(client, fakeSession(userId));\n}\n"],"mappings":";AAuBO,SAAS,YAAY,QAAoC;AAC9D,MAAI,UAAyB;AAC7B,MAAI,QAAQ;AACZ,QAAM,YAAY,oBAAI,IAA8D;AACpF,QAAM,YAAY,CAAC,OAA+C,OAAO,OAAO,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE;AACtG,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,YAAY,YAAY;AACtB,iBAAS;AACT,eAAO,EAAE,MAAM,EAAE,SAAS,UAAU,OAAO,EAAE,EAAE;AAAA,MACjD;AAAA,MACA,mBAAmB,CAAC,aAAa;AAC/B,kBAAU,IAAI,QAAQ;AACtB,eAAO;AAAA,UACL,MAAM;AAAA,YACJ,cAAc;AAAA,cACZ,aAAa,MAAM;AACjB,0BAAU,OAAO,QAAQ;AAAA,cAC3B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS,CAAC,SAAS;AACjB,gBAAU;AACV,iBAAW,YAAY,CAAC,GAAG,SAAS,GAAG;AACrC,iBAAS,SAAS,OAAO,eAAe,aAAa,UAAU,IAAI,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAGO,SAAS,YAAe,QAAW,SAAyB;AACjE,EAAC,OAAwC,OAAO,QAAQ;AACxD,SAAO;AACT;AAGO,SAAS,WAAc,QAAW,QAAmB;AAC1D,SAAO,YAAY,QAAQ,YAAY,MAAM,CAAC;AAChD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/data",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "The opinionated AI Matrx data layer: the /db tier's hard-won Supabase primitives — optimistic concurrency, complete-list reads, JSONB merge, session-retry, the domain-wide auth cookie, realtime channel hygiene — the /net resilience trio, and the /files lane: the Matrx file-communication client (durable-ref resolution, the mx_files_session bootstrap, authenticated blob fetch, uploads, the public-link door).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -68,6 +68,16 @@
|
|
|
68
68
|
"default": "./dist/net.cjs"
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
+
"./session-identity": {
|
|
72
|
+
"import": {
|
|
73
|
+
"types": "./dist/session-identity.d.ts",
|
|
74
|
+
"default": "./dist/session-identity.js"
|
|
75
|
+
},
|
|
76
|
+
"require": {
|
|
77
|
+
"types": "./dist/session-identity.d.cts",
|
|
78
|
+
"default": "./dist/session-identity.cjs"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
71
81
|
"./files": {
|
|
72
82
|
"import": {
|
|
73
83
|
"types": "./dist/files.d.ts",
|
|
@@ -78,6 +88,16 @@
|
|
|
78
88
|
"default": "./dist/files.cjs"
|
|
79
89
|
}
|
|
80
90
|
},
|
|
91
|
+
"./testing": {
|
|
92
|
+
"import": {
|
|
93
|
+
"types": "./dist/testing.d.ts",
|
|
94
|
+
"default": "./dist/testing.js"
|
|
95
|
+
},
|
|
96
|
+
"require": {
|
|
97
|
+
"types": "./dist/testing.d.cts",
|
|
98
|
+
"default": "./dist/testing.cjs"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
81
101
|
"./next": {
|
|
82
102
|
"import": {
|
|
83
103
|
"types": "./dist/next.d.ts",
|
|
@@ -145,7 +165,8 @@
|
|
|
145
165
|
"/net resilience",
|
|
146
166
|
"/files byte transport",
|
|
147
167
|
"session cookie",
|
|
148
|
-
"generated DB types (target)"
|
|
168
|
+
"generated DB types (target)",
|
|
169
|
+
"session identity"
|
|
149
170
|
],
|
|
150
171
|
"mustNotOwn": [
|
|
151
172
|
"feature state",
|