@augmenting-integrations/auth 4.2.0 → 5.0.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/dist/client/AppUserProvider.cjs +73 -0
- package/dist/client/AppUserProvider.cjs.map +1 -0
- package/dist/client/AppUserProvider.d.ts +49 -0
- package/dist/client/AppUserProvider.d.ts.map +1 -0
- package/dist/client/AppUserProvider.js +36 -0
- package/dist/client/AppUserProvider.js.map +1 -0
- package/dist/client/ImpersonationBanner.cjs +103 -0
- package/dist/client/ImpersonationBanner.cjs.map +1 -0
- package/dist/client/ImpersonationBanner.d.ts +9 -0
- package/dist/client/ImpersonationBanner.d.ts.map +1 -0
- package/dist/client/ImpersonationBanner.js +69 -0
- package/dist/client/ImpersonationBanner.js.map +1 -0
- package/dist/client/SignOutButton.cjs +59 -0
- package/dist/client/SignOutButton.cjs.map +1 -0
- package/dist/client/SignOutButton.d.ts +10 -0
- package/dist/client/SignOutButton.d.ts.map +1 -0
- package/dist/client/SignOutButton.js +35 -0
- package/dist/client/SignOutButton.js.map +1 -0
- package/dist/client/UserMenu.cjs +54 -0
- package/dist/client/UserMenu.cjs.map +1 -0
- package/dist/client/UserMenu.d.ts +6 -0
- package/dist/client/UserMenu.d.ts.map +1 -0
- package/dist/client/UserMenu.js +30 -0
- package/dist/client/UserMenu.js.map +1 -0
- package/dist/client/use-impersonation.cjs +77 -0
- package/dist/client/use-impersonation.cjs.map +1 -0
- package/dist/client/use-impersonation.d.ts +39 -0
- package/dist/client/use-impersonation.d.ts.map +1 -0
- package/dist/client/use-impersonation.js +43 -0
- package/dist/client/use-impersonation.js.map +1 -0
- package/dist/client.cjs +47 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +23 -0
- package/dist/client.js.map +1 -0
- package/dist/{index.d.ts → server/createAuth.d.ts} +1 -1
- package/dist/server/createAuth.d.ts.map +1 -0
- package/dist/server/impersonation.d.ts +23 -0
- package/dist/server/impersonation.d.ts.map +1 -0
- package/dist/server/jit.d.ts +97 -0
- package/dist/server/jit.d.ts.map +1 -0
- package/dist/{index.cjs → server.cjs} +155 -7
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{index.js → server.js} +144 -3
- package/dist/server.js.map +1 -0
- package/package.json +22 -11
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var AppUserProvider_exports = {};
|
|
31
|
+
__export(AppUserProvider_exports, {
|
|
32
|
+
AppUserProvider: () => AppUserProvider,
|
|
33
|
+
useAppUser: () => useAppUser,
|
|
34
|
+
useDbAppUser: () => useDbAppUser,
|
|
35
|
+
useRole: () => useRole
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(AppUserProvider_exports);
|
|
38
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
var React = __toESM(require("react"));
|
|
40
|
+
const AppUserContext = React.createContext(null);
|
|
41
|
+
function AppUserProvider({
|
|
42
|
+
value,
|
|
43
|
+
children
|
|
44
|
+
}) {
|
|
45
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppUserContext.Provider, { value, children });
|
|
46
|
+
}
|
|
47
|
+
function useAppUser() {
|
|
48
|
+
const ctx = React.useContext(AppUserContext);
|
|
49
|
+
if (!ctx) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"useAppUser must be used inside <AppUserProvider />. Wrap your authed layout with <AppUserProvider value={...}>."
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return ctx;
|
|
55
|
+
}
|
|
56
|
+
function useDbAppUser() {
|
|
57
|
+
const state = useAppUser();
|
|
58
|
+
return state.kind === "db" ? state.user : null;
|
|
59
|
+
}
|
|
60
|
+
function useRole() {
|
|
61
|
+
const state = useAppUser();
|
|
62
|
+
if (state.kind === "db") return state.user.role;
|
|
63
|
+
if (state.kind === "session") return state.user.groups[0] ?? null;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
AppUserProvider,
|
|
69
|
+
useAppUser,
|
|
70
|
+
useDbAppUser,
|
|
71
|
+
useRole
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=AppUserProvider.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/AppUserProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n// =============================================================================\n// AppUserProvider + useAppUser hook\n//\n// Server layout fetches the User row via getOrCreateAppUser (db-backed) and\n// passes it via <AppUserProvider value={{kind:\"db\", user}}>. Client\n// components consume via useAppUser(). For apps that have no DB user table\n// (e.g. the apex auth broker), pass `{kind:\"session\", user: session.user}`.\n//\n// Discriminated union forces consumers to narrow before accessing role /\n// credit_balance / other DB-row fields. No \"minimal mode\" boolean.\n// =============================================================================\n\nexport type DbAppUser = {\n /** Stringified BigInt user id from the DB. */\n id: string;\n email: string;\n name: string;\n role: string;\n parent_id: string | null;\n credit_balance: number;\n is_active: boolean;\n must_change_password: boolean;\n /** Stringified BigInt admin id when this session is impersonated. */\n impersonatedBy?: string;\n};\n\nexport type SessionAppUser = {\n email: string;\n name?: string | null;\n groups: string[];\n};\n\nexport type AppUserState =\n | { kind: \"db\"; user: DbAppUser }\n | { kind: \"session\"; user: SessionAppUser }\n | { kind: \"anonymous\" };\n\nconst AppUserContext = React.createContext<AppUserState | null>(null);\n\nexport function AppUserProvider({\n value,\n children,\n}: {\n value: AppUserState;\n children: React.ReactNode;\n}) {\n return <AppUserContext.Provider value={value}>{children}</AppUserContext.Provider>;\n}\n\n/**\n * Read the current user state. Throws if no <AppUserProvider> ancestor\n * exists. Narrow with `state.kind` before accessing user fields.\n */\nexport function useAppUser(): AppUserState {\n const ctx = React.useContext(AppUserContext);\n if (!ctx) {\n throw new Error(\n \"useAppUser must be used inside <AppUserProvider />. Wrap your authed layout with <AppUserProvider value={...}>.\",\n );\n }\n return ctx;\n}\n\n/**\n * Convenience helper: returns the DB user if the state is `kind: \"db\"`,\n * otherwise null. Used by UI gates that only make sense with a DB row.\n */\nexport function useDbAppUser(): DbAppUser | null {\n const state = useAppUser();\n return state.kind === \"db\" ? state.user : null;\n}\n\n/**\n * Convenience helper: returns the user's primary role, regardless of state\n * shape. DB state -> `state.user.role`. Session state -> `groups[0] ?? null`.\n * Anonymous -> null.\n */\nexport function useRole(): string | null {\n const state = useAppUser();\n if (state.kind === \"db\") return state.user.role;\n if (state.kind === \"session\") return state.user.groups[0] ?? null;\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkDS;AAhDT,YAAuB;AAuCvB,MAAM,iBAAiB,MAAM,cAAmC,IAAI;AAE7D,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGG;AACD,SAAO,4CAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAC1D;AAMO,SAAS,aAA2B;AACzC,QAAM,MAAM,MAAM,WAAW,cAAc;AAC3C,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,eAAiC;AAC/C,QAAM,QAAQ,WAAW;AACzB,SAAO,MAAM,SAAS,OAAO,MAAM,OAAO;AAC5C;AAOO,SAAS,UAAyB;AACvC,QAAM,QAAQ,WAAW;AACzB,MAAI,MAAM,SAAS,KAAM,QAAO,MAAM,KAAK;AAC3C,MAAI,MAAM,SAAS,UAAW,QAAO,MAAM,KAAK,OAAO,CAAC,KAAK;AAC7D,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type DbAppUser = {
|
|
3
|
+
/** Stringified BigInt user id from the DB. */
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
name: string;
|
|
7
|
+
role: string;
|
|
8
|
+
parent_id: string | null;
|
|
9
|
+
credit_balance: number;
|
|
10
|
+
is_active: boolean;
|
|
11
|
+
must_change_password: boolean;
|
|
12
|
+
/** Stringified BigInt admin id when this session is impersonated. */
|
|
13
|
+
impersonatedBy?: string;
|
|
14
|
+
};
|
|
15
|
+
export type SessionAppUser = {
|
|
16
|
+
email: string;
|
|
17
|
+
name?: string | null;
|
|
18
|
+
groups: string[];
|
|
19
|
+
};
|
|
20
|
+
export type AppUserState = {
|
|
21
|
+
kind: "db";
|
|
22
|
+
user: DbAppUser;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "session";
|
|
25
|
+
user: SessionAppUser;
|
|
26
|
+
} | {
|
|
27
|
+
kind: "anonymous";
|
|
28
|
+
};
|
|
29
|
+
export declare function AppUserProvider({ value, children, }: {
|
|
30
|
+
value: AppUserState;
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
}): React.JSX.Element;
|
|
33
|
+
/**
|
|
34
|
+
* Read the current user state. Throws if no <AppUserProvider> ancestor
|
|
35
|
+
* exists. Narrow with `state.kind` before accessing user fields.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useAppUser(): AppUserState;
|
|
38
|
+
/**
|
|
39
|
+
* Convenience helper: returns the DB user if the state is `kind: "db"`,
|
|
40
|
+
* otherwise null. Used by UI gates that only make sense with a DB row.
|
|
41
|
+
*/
|
|
42
|
+
export declare function useDbAppUser(): DbAppUser | null;
|
|
43
|
+
/**
|
|
44
|
+
* Convenience helper: returns the user's primary role, regardless of state
|
|
45
|
+
* shape. DB state -> `state.user.role`. Session state -> `groups[0] ?? null`.
|
|
46
|
+
* Anonymous -> null.
|
|
47
|
+
*/
|
|
48
|
+
export declare function useRole(): string | null;
|
|
49
|
+
//# sourceMappingURL=AppUserProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppUserProvider.d.ts","sourceRoot":"","sources":["../../src/client/AppUserProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAc/B,MAAM,MAAM,SAAS,GAAG;IACtB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAI1B,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAQzC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAG/C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,MAAM,GAAG,IAAI,CAKvC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
const AppUserContext = React.createContext(null);
|
|
5
|
+
function AppUserProvider({
|
|
6
|
+
value,
|
|
7
|
+
children
|
|
8
|
+
}) {
|
|
9
|
+
return /* @__PURE__ */ jsx(AppUserContext.Provider, { value, children });
|
|
10
|
+
}
|
|
11
|
+
function useAppUser() {
|
|
12
|
+
const ctx = React.useContext(AppUserContext);
|
|
13
|
+
if (!ctx) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"useAppUser must be used inside <AppUserProvider />. Wrap your authed layout with <AppUserProvider value={...}>."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return ctx;
|
|
19
|
+
}
|
|
20
|
+
function useDbAppUser() {
|
|
21
|
+
const state = useAppUser();
|
|
22
|
+
return state.kind === "db" ? state.user : null;
|
|
23
|
+
}
|
|
24
|
+
function useRole() {
|
|
25
|
+
const state = useAppUser();
|
|
26
|
+
if (state.kind === "db") return state.user.role;
|
|
27
|
+
if (state.kind === "session") return state.user.groups[0] ?? null;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
AppUserProvider,
|
|
32
|
+
useAppUser,
|
|
33
|
+
useDbAppUser,
|
|
34
|
+
useRole
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=AppUserProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/AppUserProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n// =============================================================================\n// AppUserProvider + useAppUser hook\n//\n// Server layout fetches the User row via getOrCreateAppUser (db-backed) and\n// passes it via <AppUserProvider value={{kind:\"db\", user}}>. Client\n// components consume via useAppUser(). For apps that have no DB user table\n// (e.g. the apex auth broker), pass `{kind:\"session\", user: session.user}`.\n//\n// Discriminated union forces consumers to narrow before accessing role /\n// credit_balance / other DB-row fields. No \"minimal mode\" boolean.\n// =============================================================================\n\nexport type DbAppUser = {\n /** Stringified BigInt user id from the DB. */\n id: string;\n email: string;\n name: string;\n role: string;\n parent_id: string | null;\n credit_balance: number;\n is_active: boolean;\n must_change_password: boolean;\n /** Stringified BigInt admin id when this session is impersonated. */\n impersonatedBy?: string;\n};\n\nexport type SessionAppUser = {\n email: string;\n name?: string | null;\n groups: string[];\n};\n\nexport type AppUserState =\n | { kind: \"db\"; user: DbAppUser }\n | { kind: \"session\"; user: SessionAppUser }\n | { kind: \"anonymous\" };\n\nconst AppUserContext = React.createContext<AppUserState | null>(null);\n\nexport function AppUserProvider({\n value,\n children,\n}: {\n value: AppUserState;\n children: React.ReactNode;\n}) {\n return <AppUserContext.Provider value={value}>{children}</AppUserContext.Provider>;\n}\n\n/**\n * Read the current user state. Throws if no <AppUserProvider> ancestor\n * exists. Narrow with `state.kind` before accessing user fields.\n */\nexport function useAppUser(): AppUserState {\n const ctx = React.useContext(AppUserContext);\n if (!ctx) {\n throw new Error(\n \"useAppUser must be used inside <AppUserProvider />. Wrap your authed layout with <AppUserProvider value={...}>.\",\n );\n }\n return ctx;\n}\n\n/**\n * Convenience helper: returns the DB user if the state is `kind: \"db\"`,\n * otherwise null. Used by UI gates that only make sense with a DB row.\n */\nexport function useDbAppUser(): DbAppUser | null {\n const state = useAppUser();\n return state.kind === \"db\" ? state.user : null;\n}\n\n/**\n * Convenience helper: returns the user's primary role, regardless of state\n * shape. DB state -> `state.user.role`. Session state -> `groups[0] ?? null`.\n * Anonymous -> null.\n */\nexport function useRole(): string | null {\n const state = useAppUser();\n if (state.kind === \"db\") return state.user.role;\n if (state.kind === \"session\") return state.user.groups[0] ?? null;\n return null;\n}\n"],"mappings":";AAkDS;AAhDT,YAAY,WAAW;AAuCvB,MAAM,iBAAiB,MAAM,cAAmC,IAAI;AAE7D,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGG;AACD,SAAO,oBAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAC1D;AAMO,SAAS,aAA2B;AACzC,QAAM,MAAM,MAAM,WAAW,cAAc;AAC3C,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,eAAiC;AAC/C,QAAM,QAAQ,WAAW;AACzB,SAAO,MAAM,SAAS,OAAO,MAAM,OAAO;AAC5C;AAOO,SAAS,UAAyB;AACvC,QAAM,QAAQ,WAAW;AACzB,MAAI,MAAM,SAAS,KAAM,QAAO,MAAM,KAAK;AAC3C,MAAI,MAAM,SAAS,UAAW,QAAO,MAAM,KAAK,OAAO,CAAC,KAAK;AAC7D,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var ImpersonationBanner_exports = {};
|
|
31
|
+
__export(ImpersonationBanner_exports, {
|
|
32
|
+
ImpersonationBanner: () => ImpersonationBanner
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(ImpersonationBanner_exports);
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var React = __toESM(require("react"));
|
|
37
|
+
var import_use_impersonation = require("./use-impersonation.js");
|
|
38
|
+
function ImpersonationBanner({
|
|
39
|
+
endpoint = "/api/admin/users"
|
|
40
|
+
}) {
|
|
41
|
+
const { status, data, refresh } = (0, import_use_impersonation.useImpersonation)();
|
|
42
|
+
const [stopping, setStopping] = React.useState(false);
|
|
43
|
+
const [error, setError] = React.useState(null);
|
|
44
|
+
if (status !== "ready" || !data.impersonatedBy) return null;
|
|
45
|
+
const handleStop = async () => {
|
|
46
|
+
setStopping(true);
|
|
47
|
+
setError(null);
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(`${endpoint}/${data.user.id}/impersonate`, {
|
|
50
|
+
method: "DELETE",
|
|
51
|
+
credentials: "same-origin"
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const txt = await res.text().catch(() => "");
|
|
55
|
+
throw new Error(txt || `HTTP ${res.status}`);
|
|
56
|
+
}
|
|
57
|
+
await refresh();
|
|
58
|
+
window.location.reload();
|
|
59
|
+
} catch (err) {
|
|
60
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
61
|
+
} finally {
|
|
62
|
+
setStopping(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-md border border-warning bg-warning/10 p-3 text-sm text-warning-foreground", children: [
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
67
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
68
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "font-medium", children: [
|
|
69
|
+
"Impersonating ",
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: data.user.name }),
|
|
71
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-xs", children: [
|
|
72
|
+
" (",
|
|
73
|
+
data.user.email,
|
|
74
|
+
")"
|
|
75
|
+
] })
|
|
76
|
+
] }),
|
|
77
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs opacity-80", children: [
|
|
78
|
+
"Started by ",
|
|
79
|
+
data.impersonatedBy.name,
|
|
80
|
+
" (",
|
|
81
|
+
data.impersonatedBy.email,
|
|
82
|
+
"). Every action you take is logged against the impersonated user."
|
|
83
|
+
] })
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
86
|
+
"button",
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
className: "inline-flex h-8 items-center gap-1 rounded-md border border-warning bg-background px-2.5 text-xs font-medium text-foreground transition hover:bg-warning/10 disabled:opacity-50",
|
|
90
|
+
onClick: handleStop,
|
|
91
|
+
disabled: stopping,
|
|
92
|
+
children: stopping ? "Stopping\u2026" : "Stop impersonating"
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] }),
|
|
96
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-2 text-xs text-destructive", children: error }) : null
|
|
97
|
+
] });
|
|
98
|
+
}
|
|
99
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
100
|
+
0 && (module.exports = {
|
|
101
|
+
ImpersonationBanner
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=ImpersonationBanner.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/ImpersonationBanner.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { useImpersonation } from \"./use-impersonation.js\";\n\n// =============================================================================\n// ImpersonationBanner -- visible whenever the effective user is impersonated.\n//\n// Shows who is impersonating whom and provides a \"Stop\" button that DELETEs\n// /api/admin/users/:id/impersonate to clear the cookie. Spokes mount this in\n// the (authed)/admin layout (or wherever they want it visible).\n//\n// Pure CSS + DOM; no @augmenting-integrations/ui dependency so the auth\n// package doesn't require ui as a peer.\n// =============================================================================\n\nexport function ImpersonationBanner({\n endpoint = \"/api/admin/users\",\n}: {\n /**\n * Base endpoint where the impersonate DELETE lives. The component appends\n * `/${user.id}/impersonate`. Default matches the spoke's convention.\n */\n endpoint?: string;\n}) {\n const { status, data, refresh } = useImpersonation();\n const [stopping, setStopping] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n if (status !== \"ready\" || !data.impersonatedBy) return null;\n\n const handleStop = async () => {\n setStopping(true);\n setError(null);\n try {\n const res = await fetch(`${endpoint}/${data.user.id}/impersonate`, {\n method: \"DELETE\",\n credentials: \"same-origin\",\n });\n if (!res.ok) {\n const txt = await res.text().catch(() => \"\");\n throw new Error(txt || `HTTP ${res.status}`);\n }\n await refresh();\n // Reload so server components rendered against the impersonated user\n // (admin gate, KPI strip, etc) re-run with the admin session back in\n // place.\n window.location.reload();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setStopping(false);\n }\n };\n\n return (\n <div className=\"rounded-md border border-warning bg-warning/10 p-3 text-sm text-warning-foreground\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <p className=\"font-medium\">\n Impersonating <span className=\"font-mono\">{data.user.name}</span>\n <span className=\"text-xs\"> ({data.user.email})</span>\n </p>\n <p className=\"text-xs opacity-80\">\n Started by {data.impersonatedBy.name} ({data.impersonatedBy.email}). Every\n action you take is logged against the impersonated user.\n </p>\n </div>\n <button\n type=\"button\"\n className=\"inline-flex h-8 items-center gap-1 rounded-md border border-warning bg-background px-2.5 text-xs font-medium text-foreground transition hover:bg-warning/10 disabled:opacity-50\"\n onClick={handleStop}\n disabled={stopping}\n >\n {stopping ? \"Stopping…\" : \"Stop impersonating\"}\n </button>\n </div>\n {error ? <p className=\"mt-2 text-xs text-destructive\">{error}</p> : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6D0B;AA3D1B,YAAuB;AAEvB,+BAAiC;AAa1B,SAAS,oBAAoB;AAAA,EAClC,WAAW;AACb,GAMG;AACD,QAAM,EAAE,QAAQ,MAAM,QAAQ,QAAI,2CAAiB;AACnD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,MAAI,WAAW,WAAW,CAAC,KAAK,eAAgB,QAAO;AAEvD,QAAM,aAAa,YAAY;AAC7B,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,IAAI,KAAK,KAAK,EAAE,gBAAgB;AAAA,QACjE,QAAQ;AAAA,QACR,aAAa;AAAA,MACf,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC3C,cAAM,IAAI,MAAM,OAAO,QAAQ,IAAI,MAAM,EAAE;AAAA,MAC7C;AACA,YAAM,QAAQ;AAId,aAAO,SAAS,OAAO;AAAA,IACzB,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC3D,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,SACE,6CAAC,SAAI,WAAU,sFACb;AAAA,iDAAC,SAAI,WAAU,qDACb;AAAA,mDAAC,SACC;AAAA,qDAAC,OAAE,WAAU,eAAc;AAAA;AAAA,UACX,4CAAC,UAAK,WAAU,aAAa,eAAK,KAAK,MAAK;AAAA,UAC1D,6CAAC,UAAK,WAAU,WAAU;AAAA;AAAA,YAAG,KAAK,KAAK;AAAA,YAAM;AAAA,aAAC;AAAA,WAChD;AAAA,QACA,6CAAC,OAAE,WAAU,sBAAqB;AAAA;AAAA,UACpB,KAAK,eAAe;AAAA,UAAK;AAAA,UAAG,KAAK,eAAe;AAAA,UAAM;AAAA,WAEpE;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU;AAAA,UAET,qBAAW,mBAAc;AAAA;AAAA,MAC5B;AAAA,OACF;AAAA,IACC,QAAQ,4CAAC,OAAE,WAAU,iCAAiC,iBAAM,IAAO;AAAA,KACtE;AAEJ;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function ImpersonationBanner({ endpoint, }: {
|
|
3
|
+
/**
|
|
4
|
+
* Base endpoint where the impersonate DELETE lives. The component appends
|
|
5
|
+
* `/${user.id}/impersonate`. Default matches the spoke's convention.
|
|
6
|
+
*/
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
}): React.JSX.Element | null;
|
|
9
|
+
//# sourceMappingURL=ImpersonationBanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImpersonationBanner.d.ts","sourceRoot":"","sources":["../../src/client/ImpersonationBanner.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,wBAAgB,mBAAmB,CAAC,EAClC,QAA6B,GAC9B,EAAE;IACD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,4BAwDA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useImpersonation } from "./use-impersonation.js";
|
|
5
|
+
function ImpersonationBanner({
|
|
6
|
+
endpoint = "/api/admin/users"
|
|
7
|
+
}) {
|
|
8
|
+
const { status, data, refresh } = useImpersonation();
|
|
9
|
+
const [stopping, setStopping] = React.useState(false);
|
|
10
|
+
const [error, setError] = React.useState(null);
|
|
11
|
+
if (status !== "ready" || !data.impersonatedBy) return null;
|
|
12
|
+
const handleStop = async () => {
|
|
13
|
+
setStopping(true);
|
|
14
|
+
setError(null);
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(`${endpoint}/${data.user.id}/impersonate`, {
|
|
17
|
+
method: "DELETE",
|
|
18
|
+
credentials: "same-origin"
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
const txt = await res.text().catch(() => "");
|
|
22
|
+
throw new Error(txt || `HTTP ${res.status}`);
|
|
23
|
+
}
|
|
24
|
+
await refresh();
|
|
25
|
+
window.location.reload();
|
|
26
|
+
} catch (err) {
|
|
27
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
28
|
+
} finally {
|
|
29
|
+
setStopping(false);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-warning bg-warning/10 p-3 text-sm text-warning-foreground", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
35
|
+
/* @__PURE__ */ jsxs("p", { className: "font-medium", children: [
|
|
36
|
+
"Impersonating ",
|
|
37
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: data.user.name }),
|
|
38
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs", children: [
|
|
39
|
+
" (",
|
|
40
|
+
data.user.email,
|
|
41
|
+
")"
|
|
42
|
+
] })
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs opacity-80", children: [
|
|
45
|
+
"Started by ",
|
|
46
|
+
data.impersonatedBy.name,
|
|
47
|
+
" (",
|
|
48
|
+
data.impersonatedBy.email,
|
|
49
|
+
"). Every action you take is logged against the impersonated user."
|
|
50
|
+
] })
|
|
51
|
+
] }),
|
|
52
|
+
/* @__PURE__ */ jsx(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
type: "button",
|
|
56
|
+
className: "inline-flex h-8 items-center gap-1 rounded-md border border-warning bg-background px-2.5 text-xs font-medium text-foreground transition hover:bg-warning/10 disabled:opacity-50",
|
|
57
|
+
onClick: handleStop,
|
|
58
|
+
disabled: stopping,
|
|
59
|
+
children: stopping ? "Stopping\u2026" : "Stop impersonating"
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
] }),
|
|
63
|
+
error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-destructive", children: error }) : null
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
ImpersonationBanner
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=ImpersonationBanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/ImpersonationBanner.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { useImpersonation } from \"./use-impersonation.js\";\n\n// =============================================================================\n// ImpersonationBanner -- visible whenever the effective user is impersonated.\n//\n// Shows who is impersonating whom and provides a \"Stop\" button that DELETEs\n// /api/admin/users/:id/impersonate to clear the cookie. Spokes mount this in\n// the (authed)/admin layout (or wherever they want it visible).\n//\n// Pure CSS + DOM; no @augmenting-integrations/ui dependency so the auth\n// package doesn't require ui as a peer.\n// =============================================================================\n\nexport function ImpersonationBanner({\n endpoint = \"/api/admin/users\",\n}: {\n /**\n * Base endpoint where the impersonate DELETE lives. The component appends\n * `/${user.id}/impersonate`. Default matches the spoke's convention.\n */\n endpoint?: string;\n}) {\n const { status, data, refresh } = useImpersonation();\n const [stopping, setStopping] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n if (status !== \"ready\" || !data.impersonatedBy) return null;\n\n const handleStop = async () => {\n setStopping(true);\n setError(null);\n try {\n const res = await fetch(`${endpoint}/${data.user.id}/impersonate`, {\n method: \"DELETE\",\n credentials: \"same-origin\",\n });\n if (!res.ok) {\n const txt = await res.text().catch(() => \"\");\n throw new Error(txt || `HTTP ${res.status}`);\n }\n await refresh();\n // Reload so server components rendered against the impersonated user\n // (admin gate, KPI strip, etc) re-run with the admin session back in\n // place.\n window.location.reload();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setStopping(false);\n }\n };\n\n return (\n <div className=\"rounded-md border border-warning bg-warning/10 p-3 text-sm text-warning-foreground\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <p className=\"font-medium\">\n Impersonating <span className=\"font-mono\">{data.user.name}</span>\n <span className=\"text-xs\"> ({data.user.email})</span>\n </p>\n <p className=\"text-xs opacity-80\">\n Started by {data.impersonatedBy.name} ({data.impersonatedBy.email}). Every\n action you take is logged against the impersonated user.\n </p>\n </div>\n <button\n type=\"button\"\n className=\"inline-flex h-8 items-center gap-1 rounded-md border border-warning bg-background px-2.5 text-xs font-medium text-foreground transition hover:bg-warning/10 disabled:opacity-50\"\n onClick={handleStop}\n disabled={stopping}\n >\n {stopping ? \"Stopping…\" : \"Stop impersonating\"}\n </button>\n </div>\n {error ? <p className=\"mt-2 text-xs text-destructive\">{error}</p> : null}\n </div>\n );\n}\n"],"mappings":";AA6D0B,cACd,YADc;AA3D1B,YAAY,WAAW;AAEvB,SAAS,wBAAwB;AAa1B,SAAS,oBAAoB;AAAA,EAClC,WAAW;AACb,GAMG;AACD,QAAM,EAAE,QAAQ,MAAM,QAAQ,IAAI,iBAAiB;AACnD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,MAAI,WAAW,WAAW,CAAC,KAAK,eAAgB,QAAO;AAEvD,QAAM,aAAa,YAAY;AAC7B,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,IAAI,KAAK,KAAK,EAAE,gBAAgB;AAAA,QACjE,QAAQ;AAAA,QACR,aAAa;AAAA,MACf,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC3C,cAAM,IAAI,MAAM,OAAO,QAAQ,IAAI,MAAM,EAAE;AAAA,MAC7C;AACA,YAAM,QAAQ;AAId,aAAO,SAAS,OAAO;AAAA,IACzB,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC3D,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,sFACb;AAAA,yBAAC,SAAI,WAAU,qDACb;AAAA,2BAAC,SACC;AAAA,6BAAC,OAAE,WAAU,eAAc;AAAA;AAAA,UACX,oBAAC,UAAK,WAAU,aAAa,eAAK,KAAK,MAAK;AAAA,UAC1D,qBAAC,UAAK,WAAU,WAAU;AAAA;AAAA,YAAG,KAAK,KAAK;AAAA,YAAM;AAAA,aAAC;AAAA,WAChD;AAAA,QACA,qBAAC,OAAE,WAAU,sBAAqB;AAAA;AAAA,UACpB,KAAK,eAAe;AAAA,UAAK;AAAA,UAAG,KAAK,eAAe;AAAA,UAAM;AAAA,WAEpE;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU;AAAA,UAET,qBAAW,mBAAc;AAAA;AAAA,MAC5B;AAAA,OACF;AAAA,IACC,QAAQ,oBAAC,OAAE,WAAU,iCAAiC,iBAAM,IAAO;AAAA,KACtE;AAEJ;","names":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var SignOutButton_exports = {};
|
|
21
|
+
__export(SignOutButton_exports, {
|
|
22
|
+
SignOutButton: () => SignOutButton
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(SignOutButton_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_react = require("next-auth/react");
|
|
27
|
+
var import_lucide_react = require("lucide-react");
|
|
28
|
+
function SignOutButton({
|
|
29
|
+
callbackUrl = "/",
|
|
30
|
+
variant = "outline",
|
|
31
|
+
size = "sm",
|
|
32
|
+
showIcon = true,
|
|
33
|
+
className,
|
|
34
|
+
label = "Sign out"
|
|
35
|
+
}) {
|
|
36
|
+
const base = "inline-flex items-center gap-1.5 rounded-md text-sm font-medium transition";
|
|
37
|
+
const sized = size === "sm" ? "h-8 px-2.5" : "h-9 px-3";
|
|
38
|
+
const variantCls = variant === "outline" ? "border border-border bg-background hover:bg-muted text-foreground" : variant === "ghost" ? "text-foreground hover:bg-muted" : "bg-primary text-primary-foreground hover:bg-primary/90";
|
|
39
|
+
const merged = [base, sized, variantCls, className].filter(Boolean).join(" ");
|
|
40
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
41
|
+
"button",
|
|
42
|
+
{
|
|
43
|
+
type: "button",
|
|
44
|
+
className: merged,
|
|
45
|
+
onClick: () => {
|
|
46
|
+
void (0, import_react.signOut)({ callbackUrl });
|
|
47
|
+
},
|
|
48
|
+
children: [
|
|
49
|
+
showIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.LogOut, { className: "h-3.5 w-3.5" }) : null,
|
|
50
|
+
label
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
56
|
+
0 && (module.exports = {
|
|
57
|
+
SignOutButton
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=SignOutButton.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/SignOutButton.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { signOut } from \"next-auth/react\";\nimport { LogOut } from \"lucide-react\";\n\n// =============================================================================\n// SignOutButton -- standalone button calling next-auth/react signOut.\n//\n// Used both inside <UserMenu> and the mobile sidebar footer. Keeps the\n// signOut import in one place so consuming spokes never have to thread a\n// next-auth dep into their AuthedShell.\n// =============================================================================\n\nexport function SignOutButton({\n callbackUrl = \"/\",\n variant = \"outline\",\n size = \"sm\",\n showIcon = true,\n className,\n label = \"Sign out\",\n}: {\n callbackUrl?: string;\n variant?: \"outline\" | \"ghost\" | \"default\";\n size?: \"sm\" | \"md\";\n showIcon?: boolean;\n className?: string;\n label?: string;\n}) {\n const base =\n \"inline-flex items-center gap-1.5 rounded-md text-sm font-medium transition\";\n const sized = size === \"sm\" ? \"h-8 px-2.5\" : \"h-9 px-3\";\n const variantCls =\n variant === \"outline\"\n ? \"border border-border bg-background hover:bg-muted text-foreground\"\n : variant === \"ghost\"\n ? \"text-foreground hover:bg-muted\"\n : \"bg-primary text-primary-foreground hover:bg-primary/90\";\n const merged = [base, sized, variantCls, className].filter(Boolean).join(\" \");\n return (\n <button\n type=\"button\"\n className={merged}\n onClick={() => {\n void signOut({ callbackUrl });\n }}\n >\n {showIcon ? <LogOut className=\"h-3.5 w-3.5\" /> : null}\n {label}\n </button>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCI;AArCJ,mBAAwB;AACxB,0BAAuB;AAUhB,SAAS,cAAc;AAAA,EAC5B,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAOG;AACD,QAAM,OACJ;AACF,QAAM,QAAQ,SAAS,OAAO,eAAe;AAC7C,QAAM,aACJ,YAAY,YACR,sEACA,YAAY,UACV,mCACA;AACR,QAAM,SAAS,CAAC,MAAM,OAAO,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW;AAAA,MACX,SAAS,MAAM;AACb,iBAAK,sBAAQ,EAAE,YAAY,CAAC;AAAA,MAC9B;AAAA,MAEC;AAAA,mBAAW,4CAAC,8BAAO,WAAU,eAAc,IAAK;AAAA,QAChD;AAAA;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export declare function SignOutButton({ callbackUrl, variant, size, showIcon, className, label, }: {
|
|
3
|
+
callbackUrl?: string;
|
|
4
|
+
variant?: "outline" | "ghost" | "default";
|
|
5
|
+
size?: "sm" | "md";
|
|
6
|
+
showIcon?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
//# sourceMappingURL=SignOutButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignOutButton.d.ts","sourceRoot":"","sources":["../../src/client/SignOutButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,wBAAgB,aAAa,CAAC,EAC5B,WAAiB,EACjB,OAAmB,EACnB,IAAW,EACX,QAAe,EACf,SAAS,EACT,KAAkB,GACnB,EAAE;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,qBAuBA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { signOut } from "next-auth/react";
|
|
4
|
+
import { LogOut } from "lucide-react";
|
|
5
|
+
function SignOutButton({
|
|
6
|
+
callbackUrl = "/",
|
|
7
|
+
variant = "outline",
|
|
8
|
+
size = "sm",
|
|
9
|
+
showIcon = true,
|
|
10
|
+
className,
|
|
11
|
+
label = "Sign out"
|
|
12
|
+
}) {
|
|
13
|
+
const base = "inline-flex items-center gap-1.5 rounded-md text-sm font-medium transition";
|
|
14
|
+
const sized = size === "sm" ? "h-8 px-2.5" : "h-9 px-3";
|
|
15
|
+
const variantCls = variant === "outline" ? "border border-border bg-background hover:bg-muted text-foreground" : variant === "ghost" ? "text-foreground hover:bg-muted" : "bg-primary text-primary-foreground hover:bg-primary/90";
|
|
16
|
+
const merged = [base, sized, variantCls, className].filter(Boolean).join(" ");
|
|
17
|
+
return /* @__PURE__ */ jsxs(
|
|
18
|
+
"button",
|
|
19
|
+
{
|
|
20
|
+
type: "button",
|
|
21
|
+
className: merged,
|
|
22
|
+
onClick: () => {
|
|
23
|
+
void signOut({ callbackUrl });
|
|
24
|
+
},
|
|
25
|
+
children: [
|
|
26
|
+
showIcon ? /* @__PURE__ */ jsx(LogOut, { className: "h-3.5 w-3.5" }) : null,
|
|
27
|
+
label
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
SignOutButton
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=SignOutButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/SignOutButton.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { signOut } from \"next-auth/react\";\nimport { LogOut } from \"lucide-react\";\n\n// =============================================================================\n// SignOutButton -- standalone button calling next-auth/react signOut.\n//\n// Used both inside <UserMenu> and the mobile sidebar footer. Keeps the\n// signOut import in one place so consuming spokes never have to thread a\n// next-auth dep into their AuthedShell.\n// =============================================================================\n\nexport function SignOutButton({\n callbackUrl = \"/\",\n variant = \"outline\",\n size = \"sm\",\n showIcon = true,\n className,\n label = \"Sign out\",\n}: {\n callbackUrl?: string;\n variant?: \"outline\" | \"ghost\" | \"default\";\n size?: \"sm\" | \"md\";\n showIcon?: boolean;\n className?: string;\n label?: string;\n}) {\n const base =\n \"inline-flex items-center gap-1.5 rounded-md text-sm font-medium transition\";\n const sized = size === \"sm\" ? \"h-8 px-2.5\" : \"h-9 px-3\";\n const variantCls =\n variant === \"outline\"\n ? \"border border-border bg-background hover:bg-muted text-foreground\"\n : variant === \"ghost\"\n ? \"text-foreground hover:bg-muted\"\n : \"bg-primary text-primary-foreground hover:bg-primary/90\";\n const merged = [base, sized, variantCls, className].filter(Boolean).join(\" \");\n return (\n <button\n type=\"button\"\n className={merged}\n onClick={() => {\n void signOut({ callbackUrl });\n }}\n >\n {showIcon ? <LogOut className=\"h-3.5 w-3.5\" /> : null}\n {label}\n </button>\n );\n}\n"],"mappings":";AAwCI,SAOc,KAPd;AArCJ,SAAS,eAAe;AACxB,SAAS,cAAc;AAUhB,SAAS,cAAc;AAAA,EAC5B,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAOG;AACD,QAAM,OACJ;AACF,QAAM,QAAQ,SAAS,OAAO,eAAe;AAC7C,QAAM,aACJ,YAAY,YACR,sEACA,YAAY,UACV,mCACA;AACR,QAAM,SAAS,CAAC,MAAM,OAAO,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW;AAAA,MACX,SAAS,MAAM;AACb,aAAK,QAAQ,EAAE,YAAY,CAAC;AAAA,MAC9B;AAAA,MAEC;AAAA,mBAAW,oBAAC,UAAO,WAAU,eAAc,IAAK;AAAA,QAChD;AAAA;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var UserMenu_exports = {};
|
|
21
|
+
__export(UserMenu_exports, {
|
|
22
|
+
UserMenu: () => UserMenu
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(UserMenu_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_AppUserProvider = require("./AppUserProvider.js");
|
|
27
|
+
var import_SignOutButton = require("./SignOutButton.js");
|
|
28
|
+
function UserMenu({
|
|
29
|
+
signOutCallbackUrl = "/",
|
|
30
|
+
className
|
|
31
|
+
}) {
|
|
32
|
+
const state = (0, import_AppUserProvider.useAppUser)();
|
|
33
|
+
if (state.kind === "anonymous") return null;
|
|
34
|
+
const email = state.kind === "db" ? state.user.email : state.user.email;
|
|
35
|
+
const name = state.kind === "db" ? state.user.name : state.user.name ?? state.user.email;
|
|
36
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
37
|
+
"div",
|
|
38
|
+
{
|
|
39
|
+
className: ["hidden items-center gap-2 md:flex", className].filter(Boolean).join(" "),
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-right text-xs leading-tight", children: [
|
|
42
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-medium text-foreground", children: name ?? email }),
|
|
43
|
+
email && name && name !== email ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-muted-foreground", children: email }) : null
|
|
44
|
+
] }),
|
|
45
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_SignOutButton.SignOutButton, { callbackUrl: signOutCallbackUrl })
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
UserMenu
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=UserMenu.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\nimport { SignOutButton } from \"./SignOutButton.js\";\n\n// =============================================================================\n// UserMenu -- header chrome showing the current user's identity + sign-out.\n//\n// Renders nothing for anonymous state. For db / session state shows the\n// user's name (or email fallback) and a sign-out button. Keeps the layout\n// simple (no dropdown) so it works inside the AppShell header without extra\n// portal-positioning. Spokes that want a richer dropdown can compose\n// <SignOutButton> themselves.\n// =============================================================================\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.kind === \"db\" ? state.user.email : state.user.email;\n const name =\n state.kind === \"db\" ? state.user.name : (state.user.name ?? state.user.email);\n\n return (\n <div\n className={[\"hidden items-center gap-2 md:flex\", className]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"text-right text-xs leading-tight\">\n <p className=\"font-medium text-foreground\">{name ?? email}</p>\n {email && name && name !== email ? (\n <p className=\"text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <SignOutButton callbackUrl={signOutCallbackUrl} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCM;AAjCN,6BAA2B;AAC3B,2BAA8B;AAYvB,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,YAAQ,mCAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,SAAS,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK;AAClE,QAAM,OACJ,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,qCAAqC,SAAS,EACvD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX;AAAA,qDAAC,SAAI,WAAU,oCACb;AAAA,sDAAC,OAAE,WAAU,+BAA+B,kBAAQ,OAAM;AAAA,UACzD,SAAS,QAAQ,SAAS,QACzB,4CAAC,OAAE,WAAU,yBAAyB,iBAAM,IAC1C;AAAA,WACN;AAAA,QACA,4CAAC,sCAAc,aAAa,oBAAoB;AAAA;AAAA;AAAA,EAClD;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserMenu.d.ts","sourceRoot":"","sources":["../../src/client/UserMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAc/B,wBAAgB,QAAQ,CAAC,EACvB,kBAAwB,EACxB,SAAS,GACV,EAAE;IACD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,4BAuBA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useAppUser } from "./AppUserProvider.js";
|
|
4
|
+
import { SignOutButton } from "./SignOutButton.js";
|
|
5
|
+
function UserMenu({
|
|
6
|
+
signOutCallbackUrl = "/",
|
|
7
|
+
className
|
|
8
|
+
}) {
|
|
9
|
+
const state = useAppUser();
|
|
10
|
+
if (state.kind === "anonymous") return null;
|
|
11
|
+
const email = state.kind === "db" ? state.user.email : state.user.email;
|
|
12
|
+
const name = state.kind === "db" ? state.user.name : state.user.name ?? state.user.email;
|
|
13
|
+
return /* @__PURE__ */ jsxs(
|
|
14
|
+
"div",
|
|
15
|
+
{
|
|
16
|
+
className: ["hidden items-center gap-2 md:flex", className].filter(Boolean).join(" "),
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsxs("div", { className: "text-right text-xs leading-tight", children: [
|
|
19
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: name ?? email }),
|
|
20
|
+
email && name && name !== email ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: email }) : null
|
|
21
|
+
] }),
|
|
22
|
+
/* @__PURE__ */ jsx(SignOutButton, { callbackUrl: signOutCallbackUrl })
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
UserMenu
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=UserMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\nimport { SignOutButton } from \"./SignOutButton.js\";\n\n// =============================================================================\n// UserMenu -- header chrome showing the current user's identity + sign-out.\n//\n// Renders nothing for anonymous state. For db / session state shows the\n// user's name (or email fallback) and a sign-out button. Keeps the layout\n// simple (no dropdown) so it works inside the AppShell header without extra\n// portal-positioning. Spokes that want a richer dropdown can compose\n// <SignOutButton> themselves.\n// =============================================================================\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.kind === \"db\" ? state.user.email : state.user.email;\n const name =\n state.kind === \"db\" ? state.user.name : (state.user.name ?? state.user.email);\n\n return (\n <div\n className={[\"hidden items-center gap-2 md:flex\", className]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"text-right text-xs leading-tight\">\n <p className=\"font-medium text-foreground\">{name ?? email}</p>\n {email && name && name !== email ? (\n <p className=\"text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <SignOutButton callbackUrl={signOutCallbackUrl} />\n </div>\n );\n}\n"],"mappings":";AAoCM,SACE,KADF;AAjCN,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAYvB,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,QAAQ,WAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,SAAS,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK;AAClE,QAAM,OACJ,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,qCAAqC,SAAS,EACvD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX;AAAA,6BAAC,SAAI,WAAU,oCACb;AAAA,8BAAC,OAAE,WAAU,+BAA+B,kBAAQ,OAAM;AAAA,UACzD,SAAS,QAAQ,SAAS,QACzB,oBAAC,OAAE,WAAU,yBAAyB,iBAAM,IAC1C;AAAA,WACN;AAAA,QACA,oBAAC,iBAAc,aAAa,oBAAoB;AAAA;AAAA;AAAA,EAClD;AAEJ;","names":[]}
|