@flowselections/core 1.0.22 → 1.0.24
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/README.md +80 -0
- package/dist/auth/redirect.d.ts +33 -4
- package/dist/auth/redirect.d.ts.map +1 -1
- package/dist/auth/redirect.js +58 -7
- package/dist/auth/roles.d.ts +10 -0
- package/dist/auth/roles.d.ts.map +1 -0
- package/dist/auth/roles.js +11 -0
- package/dist/auth/useAuth.d.ts +1 -1
- package/dist/auth/useAuth.d.ts.map +1 -1
- package/dist/auth/useAuth.js +1 -1
- package/dist/auth/useIsSuperadmin.d.ts +19 -0
- package/dist/auth/useIsSuperadmin.d.ts.map +1 -0
- package/dist/auth/useIsSuperadmin.js +16 -0
- package/dist/components/instellingen/NotificationSettingsCard.js +3 -3
- package/dist/components/layout/AuthenticatedLayout.d.ts +19 -10
- package/dist/components/layout/AuthenticatedLayout.d.ts.map +1 -1
- package/dist/components/layout/AuthenticatedLayout.js +43 -9
- package/dist/components/layout/BugsDialog.js +10 -10
- package/dist/components/layout/HandoverPage.d.ts +5 -1
- package/dist/components/layout/HandoverPage.d.ts.map +1 -1
- package/dist/components/layout/HandoverPage.js +24 -7
- package/dist/components/layout/Header.js +4 -4
- package/dist/components/layout/InstellingenPage.d.ts +1 -1
- package/dist/components/layout/InstellingenPage.d.ts.map +1 -1
- package/dist/components/layout/InstellingenPage.js +15 -7
- package/dist/components/layout/LoginPage.js +7 -7
- package/dist/components/layout/NotificationBell.js +4 -4
- package/dist/components/layout/ResetWachtwoordPage.d.ts +3 -1
- package/dist/components/layout/ResetWachtwoordPage.d.ts.map +1 -1
- package/dist/components/layout/ResetWachtwoordPage.js +10 -8
- package/dist/components/layout/Sidebar.d.ts +31 -7
- package/dist/components/layout/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.js +33 -11
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/alert-dialog.js +2 -2
- package/dist/components/ui/alert.js +1 -1
- package/dist/components/ui/avatar.js +1 -1
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.js +1 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/calendar.d.ts +1 -1
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/calendar.js +2 -2
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/carousel.d.ts +2 -2
- package/dist/components/ui/carousel.d.ts.map +1 -1
- package/dist/components/ui/carousel.js +2 -2
- package/dist/components/ui/chart.d.ts +10 -10
- package/dist/components/ui/chart.js +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/command.js +2 -2
- package/dist/components/ui/context-menu.js +1 -1
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/drawer.js +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/form.js +2 -2
- package/dist/components/ui/hover-card.js +1 -1
- package/dist/components/ui/icon-picker.js +3 -3
- package/dist/components/ui/input-otp.d.ts.map +1 -1
- package/dist/components/ui/input-otp.js +1 -1
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/label.js +1 -1
- package/dist/components/ui/menubar.js +1 -1
- package/dist/components/ui/navigation-menu.js +1 -1
- package/dist/components/ui/pagination.d.ts +1 -1
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/pagination.js +2 -2
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/radio-group.js +1 -1
- package/dist/components/ui/resizable.js +1 -1
- package/dist/components/ui/scroll-area.js +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/separator.js +1 -1
- package/dist/components/ui/sheet.js +1 -1
- package/dist/components/ui/sidebar.d.ts +2 -2
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/sidebar.js +8 -8
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +1 -1
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/toggle-group.js +2 -2
- package/dist/components/ui/toggle.js +1 -1
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/extensions/ShellExtensions.d.ts +14 -1
- package/dist/extensions/ShellExtensions.d.ts.map +1 -1
- package/dist/extensions/ShellExtensions.js +21 -2
- package/dist/hooks/useLogoSrc.d.ts +7 -0
- package/dist/hooks/useLogoSrc.d.ts.map +1 -0
- package/dist/hooks/useLogoSrc.js +29 -0
- package/dist/index.d.ts +53 -44
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -35
- package/dist/permissions/resolve.d.ts +22 -0
- package/dist/permissions/resolve.d.ts.map +1 -0
- package/dist/permissions/resolve.js +76 -0
- package/dist/permissions/store.d.ts +38 -0
- package/dist/permissions/store.d.ts.map +1 -0
- package/dist/permissions/store.js +180 -0
- package/dist/permissions/useModulePermission.d.ts +52 -32
- package/dist/permissions/useModulePermission.d.ts.map +1 -1
- package/dist/permissions/useModulePermission.js +72 -104
- package/dist/routes/index.d.ts +50 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +63 -0
- package/dist/server/handover-handler.d.ts +24 -0
- package/dist/server/handover-handler.d.ts.map +1 -0
- package/dist/server/handover-handler.js +59 -0
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/permissions.server.d.ts +14 -8
- package/dist/server/permissions.server.d.ts.map +1 -1
- package/dist/server/permissions.server.js +46 -9
- package/dist/shell/ShellContext.d.ts +26 -0
- package/dist/shell/ShellContext.d.ts.map +1 -0
- package/dist/shell/ShellContext.js +14 -0
- package/dist/styles.css +127 -0
- package/dist/supabase/auth-middleware.d.ts +16 -16
- package/dist/supabase/auth-middleware.d.ts.map +1 -1
- package/dist/supabase/client.d.ts +16 -16
- package/dist/supabase/client.d.ts.map +1 -1
- package/dist/supabase/client.server.d.ts +16 -16
- package/dist/supabase/client.server.d.ts.map +1 -1
- package/dist/types/module.d.ts +39 -4
- package/dist/types/module.d.ts.map +1 -1
- package/dist/types/module.js +33 -5
- package/package.json +14 -5
- package/dist/auth/handover.functions.d.ts +0 -16
- package/dist/auth/handover.functions.d.ts.map +0 -1
- package/dist/auth/handover.functions.js +0 -23
|
@@ -1,137 +1,105 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { useQuery } from "@tanstack/react-query";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return "edit";
|
|
1
|
+
import { createElement, Fragment, useCallback, useContext } from "react";
|
|
2
|
+
import { QueryClient, QueryClientContext, useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { filterModules, RANK, resolveLevel, } from "./resolve.js";
|
|
4
|
+
import { getPermissionSnapshot, loadPermissions, usePermissionSnapshot } from "./store.js";
|
|
5
|
+
let fallbackClient = null;
|
|
6
|
+
function getFallbackClient() {
|
|
7
|
+
if (!fallbackClient)
|
|
8
|
+
fallbackClient = new QueryClient();
|
|
9
|
+
return fallbackClient;
|
|
11
10
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
return [];
|
|
15
|
-
return data.map((raw) => {
|
|
16
|
-
const row = (raw ?? {});
|
|
17
|
-
const rawModule = (row["module_id"] ?? row["moduleId"] ?? "");
|
|
18
|
-
const rawFeature = (row["feature_id"] ?? row["featureId"] ?? null);
|
|
19
|
-
return {
|
|
20
|
-
moduleId: navKey(String(rawModule ?? "")),
|
|
21
|
-
featureId: rawFeature ? String(rawFeature) : null,
|
|
22
|
-
permission: normalizeLevel(row["permission"] ?? row["level"] ?? row["permission_level"]),
|
|
23
|
-
};
|
|
24
|
-
});
|
|
11
|
+
function rolesAreAdmin(roles) {
|
|
12
|
+
return roles.includes("admin") || roles.includes("superadmin");
|
|
25
13
|
}
|
|
26
|
-
/**
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
const code = e.code ?? "";
|
|
30
|
-
const message = (e.message ?? "").toLowerCase();
|
|
31
|
-
return (code === "42883" ||
|
|
32
|
-
code === "PGRST202" ||
|
|
33
|
-
code === "PGRST302" ||
|
|
34
|
-
e.status === 404 ||
|
|
35
|
-
message.includes("does not exist") ||
|
|
36
|
-
message.includes("could not find the function") ||
|
|
37
|
-
message.includes("schema cache"));
|
|
14
|
+
/** Snapshot → bekend? Laden = onbekend; fout/time-out = bekend (leeg → allow). */
|
|
15
|
+
function isKnown(s) {
|
|
16
|
+
return s.status !== "loading";
|
|
38
17
|
}
|
|
39
18
|
/**
|
|
40
|
-
*
|
|
19
|
+
* Rechten van de ingelogde gebruiker via RPC `my_module_permissions()`.
|
|
41
20
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
21
|
+
* Draait op de gedeelde store; gebruikt React Query alleen als er een
|
|
22
|
+
* QueryClientProvider is, anders een interne client — crasht nooit zonder
|
|
23
|
+
* provider. Fail-open: fout of ontbrekende RPC → lege lijst → default-allow.
|
|
24
|
+
* Zichtbaarheid, geen beveiliging.
|
|
45
25
|
*/
|
|
46
26
|
export function useMyModulePermissions() {
|
|
27
|
+
const snap = usePermissionSnapshot();
|
|
28
|
+
const ctxClient = useContext(QueryClientContext);
|
|
47
29
|
const query = useQuery({
|
|
48
|
-
queryKey: ["my-module-permissions"],
|
|
30
|
+
queryKey: ["my-module-permissions", snap.userId],
|
|
49
31
|
queryFn: async () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (isMissingRpc(error))
|
|
53
|
-
return [];
|
|
54
|
-
// Ook andere fouten mogen niet blokkeren: fail-open, geen retry.
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
return normalizeRows(data);
|
|
32
|
+
await loadPermissions(getPermissionSnapshot().userId);
|
|
33
|
+
return getPermissionSnapshot().rows;
|
|
58
34
|
},
|
|
59
|
-
|
|
35
|
+
enabled: snap.status !== "loading",
|
|
36
|
+
initialData: isKnown(snap) ? snap.rows : undefined,
|
|
60
37
|
retry: false,
|
|
61
38
|
refetchOnWindowFocus: false,
|
|
62
39
|
staleTime: 5 * 60 * 1000,
|
|
63
|
-
});
|
|
40
|
+
}, ctxClient ?? getFallbackClient());
|
|
64
41
|
return {
|
|
65
42
|
...query,
|
|
66
|
-
|
|
43
|
+
data: isKnown(snap) ? snap.rows : query.data,
|
|
44
|
+
permissions: isKnown(snap) ? snap.rows : [],
|
|
67
45
|
/** True zolang we niets weten; callers behandelen dit als allow. */
|
|
68
|
-
isUnknown:
|
|
46
|
+
isUnknown: !isKnown(snap),
|
|
47
|
+
status: snap.status,
|
|
69
48
|
};
|
|
70
49
|
}
|
|
71
|
-
function resolveLevel(rows, moduleId, featureId) {
|
|
72
|
-
const key = navKey(moduleId);
|
|
73
|
-
const forModule = rows.filter((r) => r.moduleId === key);
|
|
74
|
-
// Geen rij voor deze module → default-allow.
|
|
75
|
-
if (forModule.length === 0)
|
|
76
|
-
return "edit";
|
|
77
|
-
if (featureId) {
|
|
78
|
-
const feature = forModule.find((r) => r.featureId === featureId);
|
|
79
|
-
if (feature)
|
|
80
|
-
return feature.permission;
|
|
81
|
-
}
|
|
82
|
-
const moduleLevel = forModule.find((r) => !r.featureId);
|
|
83
|
-
if (moduleLevel)
|
|
84
|
-
return moduleLevel.permission;
|
|
85
|
-
// Alleen feature-rijen bekend: hoogste feature-recht bepaalt moduletoegang.
|
|
86
|
-
if (forModule.some((r) => r.permission === "edit"))
|
|
87
|
-
return "edit";
|
|
88
|
-
if (forModule.some((r) => r.permission === "view"))
|
|
89
|
-
return "view";
|
|
90
|
-
return "none";
|
|
91
|
-
}
|
|
92
50
|
/**
|
|
93
|
-
* Rechtenniveau voor één module (optioneel één feature). Tijdens laden
|
|
94
|
-
*
|
|
51
|
+
* Rechtenniveau voor één module (optioneel één feature). Tijdens laden, bij
|
|
52
|
+
* een fout en voor admin/superadmin: `edit`. Zichtbaarheid, geen beveiliging.
|
|
95
53
|
*/
|
|
96
54
|
export function useModulePermission(moduleId, featureId) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
55
|
+
const snap = usePermissionSnapshot();
|
|
56
|
+
const unknown = !isKnown(snap);
|
|
57
|
+
const level = unknown || rolesAreAdmin(snap.roles) ? "edit" : resolveLevel(snap.rows, moduleId, featureId);
|
|
99
58
|
return {
|
|
100
59
|
level,
|
|
101
60
|
canView: level === "view" || level === "edit",
|
|
102
61
|
canEdit: level === "edit",
|
|
103
|
-
isLoading:
|
|
62
|
+
isLoading: unknown,
|
|
104
63
|
};
|
|
105
64
|
}
|
|
106
65
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
66
|
+
* Sinds 1.0.23. `allowed` = niveau ≥ `level` (standaard `view`). Laden → true.
|
|
67
|
+
* Zichtbaarheid, geen beveiliging.
|
|
68
|
+
*/
|
|
69
|
+
export function useCan(moduleId, featureId, level = "view") {
|
|
70
|
+
const p = useModulePermission(moduleId, featureId);
|
|
71
|
+
return { allowed: RANK[p.level] >= RANK[level], level: p.level, isLoading: p.isLoading };
|
|
72
|
+
}
|
|
73
|
+
/** Sinds 1.0.23. Rendert children als `useCan` toestaat. Zichtbaarheid, geen beveiliging. */
|
|
74
|
+
export function Can({ module, feature, level = "view", fallback = null, children }) {
|
|
75
|
+
const { allowed } = useCan(module, feature ?? null, level);
|
|
76
|
+
return createElement(Fragment, null, allowed ? children : fallback);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* ModuleListTransform die zijbalkitems met `none` weglaat. Laden/fout/admin →
|
|
80
|
+
* ongewijzigd. Zichtbaarheid, geen beveiliging.
|
|
115
81
|
*/
|
|
116
82
|
export function useModulePermissionTransform() {
|
|
117
|
-
const
|
|
83
|
+
const snap = usePermissionSnapshot();
|
|
118
84
|
return useCallback((modules) => {
|
|
119
|
-
if (
|
|
85
|
+
if (!isKnown(snap) || rolesAreAdmin(snap.roles))
|
|
120
86
|
return modules;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
87
|
+
return filterModules(modules, snap.rows);
|
|
88
|
+
}, [snap]);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Sinds 1.0.23. Zijbalkfilter met laadstatus: `loading` = eerste laden (toon
|
|
92
|
+
* skeleton); na fout of 3 s time-out de volledige lijst.
|
|
93
|
+
* Zichtbaarheid, geen beveiliging.
|
|
94
|
+
*/
|
|
95
|
+
export function usePermissionFilteredModules(modules, enabled = true) {
|
|
96
|
+
const snap = usePermissionSnapshot();
|
|
97
|
+
if (!enabled)
|
|
98
|
+
return { modules, loading: false };
|
|
99
|
+
if (snap.status === "loading") {
|
|
100
|
+
return { modules, loading: !rolesAreAdmin(snap.roles) };
|
|
101
|
+
}
|
|
102
|
+
if (rolesAreAdmin(snap.roles) || snap.status !== "ready")
|
|
103
|
+
return { modules, loading: false };
|
|
104
|
+
return { modules: filterModules(modules, snap.rows), loading: false };
|
|
137
105
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface SystemRouteOptions {
|
|
2
|
+
/** Pad als er geen geldig `?redirect` is (na inloggen, handover, reset). */
|
|
3
|
+
landing: string;
|
|
4
|
+
/** Pad van de inlogpagina. Standaard "/login". */
|
|
5
|
+
loginPath?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LoginSearch {
|
|
8
|
+
redirect?: string;
|
|
9
|
+
}
|
|
10
|
+
/** `/login`: ingelogd → `?redirect` (alleen same-site) wint van `landing`. */
|
|
11
|
+
export declare function loginRoute({ landing }: SystemRouteOptions): {
|
|
12
|
+
validateSearch: (search: Record<string, unknown>) => LoginSearch;
|
|
13
|
+
beforeLoad: ({ context, search }: {
|
|
14
|
+
context: unknown;
|
|
15
|
+
search: LoginSearch;
|
|
16
|
+
}) => void;
|
|
17
|
+
head: () => {
|
|
18
|
+
meta: {
|
|
19
|
+
title: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
component: () => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
};
|
|
24
|
+
/** `/reset-wachtwoord`: de core-pagina, daarna naar `landing`. */
|
|
25
|
+
export declare function resetPasswordRoute({ landing, loginPath }: SystemRouteOptions): {
|
|
26
|
+
head: () => {
|
|
27
|
+
meta: {
|
|
28
|
+
title: string;
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
31
|
+
component: () => import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
};
|
|
33
|
+
/** `/`: redirect in beforeLoad, dus er wordt niets gerenderd (geen flits). */
|
|
34
|
+
export declare function landingRoute({ landing }: Pick<SystemRouteOptions, "landing">): {
|
|
35
|
+
beforeLoad: () => never;
|
|
36
|
+
component: () => null;
|
|
37
|
+
};
|
|
38
|
+
/** `/auth/handover`: client-only, want de sessie staat alleen in localStorage. */
|
|
39
|
+
export declare function handoverRoute({ landing, loginPath, exchangePath, }: SystemRouteOptions & {
|
|
40
|
+
exchangePath?: string;
|
|
41
|
+
}): {
|
|
42
|
+
ssr: false;
|
|
43
|
+
head: () => {
|
|
44
|
+
meta: {
|
|
45
|
+
title: string;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
48
|
+
component: () => import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/routes/index.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,kBAAkB;6BAE7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,WAAW;sCAE5B;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE;;;;;;;EAU9E;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,SAAoB,EAAE,EAAE,kBAAkB;;;;;;;EAOvF;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC;sBAEzD,KAAK;;EAOxB;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,SAAoB,EACpB,YAAY,GACb,EAAE,kBAAkB,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;EAQhD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Route-opties voor de systeemroutes. Shells houden per route een dun,
|
|
4
|
+
* gegenereerd omhulsel:
|
|
5
|
+
*
|
|
6
|
+
* export const Route = createFileRoute("/login")(loginRoute({ landing: "/voorraad" }));
|
|
7
|
+
*
|
|
8
|
+
* Core levert de volledige opties, zodat een wijziging aan een systeempagina
|
|
9
|
+
* met één core-update in alle shells landt.
|
|
10
|
+
*/
|
|
11
|
+
import { redirect } from "@tanstack/react-router";
|
|
12
|
+
import { LoginPage } from "../components/layout/LoginPage.js";
|
|
13
|
+
import { ResetWachtwoordPage } from "../components/layout/ResetWachtwoordPage.js";
|
|
14
|
+
import { HandoverPage } from "../components/layout/HandoverPage.js";
|
|
15
|
+
import { safeRedirectPath } from "../auth/redirect.js";
|
|
16
|
+
function isAuthenticated(context) {
|
|
17
|
+
return Boolean(context?.auth?.isAuthenticated);
|
|
18
|
+
}
|
|
19
|
+
/** `/login`: ingelogd → `?redirect` (alleen same-site) wint van `landing`. */
|
|
20
|
+
export function loginRoute({ landing }) {
|
|
21
|
+
return {
|
|
22
|
+
validateSearch: (search) => typeof search.redirect === "string" ? { redirect: search.redirect } : {},
|
|
23
|
+
beforeLoad: ({ context, search }) => {
|
|
24
|
+
if (isAuthenticated(context)) {
|
|
25
|
+
throw redirect({ href: safeRedirectPath(search?.redirect, landing) });
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
head: () => ({ meta: [{ title: "Inloggen" }] }),
|
|
29
|
+
component: function LoginRouteComponent() {
|
|
30
|
+
return _jsx(LoginPage, { redirectTo: landing });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** `/reset-wachtwoord`: de core-pagina, daarna naar `landing`. */
|
|
35
|
+
export function resetPasswordRoute({ landing, loginPath = "/login" }) {
|
|
36
|
+
return {
|
|
37
|
+
head: () => ({ meta: [{ title: "Nieuw wachtwoord instellen" }] }),
|
|
38
|
+
component: function ResetPasswordRouteComponent() {
|
|
39
|
+
return _jsx(ResetWachtwoordPage, { redirectTo: landing, loginPath: loginPath });
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** `/`: redirect in beforeLoad, dus er wordt niets gerenderd (geen flits). */
|
|
44
|
+
export function landingRoute({ landing }) {
|
|
45
|
+
return {
|
|
46
|
+
beforeLoad: () => {
|
|
47
|
+
throw redirect({ href: landing });
|
|
48
|
+
},
|
|
49
|
+
component: function LandingRouteComponent() {
|
|
50
|
+
return null;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** `/auth/handover`: client-only, want de sessie staat alleen in localStorage. */
|
|
55
|
+
export function handoverRoute({ landing, loginPath = "/login", exchangePath, }) {
|
|
56
|
+
return {
|
|
57
|
+
ssr: false,
|
|
58
|
+
head: () => ({ meta: [{ title: "Aanmelden" }] }),
|
|
59
|
+
component: function HandoverRouteComponent() {
|
|
60
|
+
return _jsx(HandoverPage, { redirectTo: landing, loginPath: loginPath, exchangePath: exchangePath });
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { exchangeHandoverCode } from "./provider-token.js";
|
|
2
|
+
/** Maximale grootte van de request-body in bytes. */
|
|
3
|
+
export declare const HANDOVER_MAX_BODY_BYTES = 2048;
|
|
4
|
+
/** Maximale lengte van een handover-code. */
|
|
5
|
+
export declare const HANDOVER_MAX_CODE_LENGTH = 512;
|
|
6
|
+
export interface HandoverExchangeHandlerOptions {
|
|
7
|
+
/** Alleen voor tests: vervangt de echte inwisseling bij central-auth. */
|
|
8
|
+
exchange?: typeof exchangeHandoverCode;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Sinds 1.0.24. Server-route-handler voor de handover (vervangt de
|
|
12
|
+
* serverfunctie uit 1.0.19–1.0.23, die servercode in de clientbundel trok).
|
|
13
|
+
* Shells koppelen hem in `src/routes/api/public/auth/handover.ts`:
|
|
14
|
+
*
|
|
15
|
+
* server: { handlers: { POST: handoverExchangeHandler() } }
|
|
16
|
+
*
|
|
17
|
+
* Alleen POST met `application/json`, body ≤ 2 KB, `{ code }` niet-leeg en
|
|
18
|
+
* begrensd. Fail-closed: generieke 400/401 zonder detail, geen CORS-headers,
|
|
19
|
+
* code en tokens worden nooit gelogd. Uitsluitend via "./server".
|
|
20
|
+
*/
|
|
21
|
+
export declare function handoverExchangeHandler(options?: HandoverExchangeHandlerOptions): ({ request }: {
|
|
22
|
+
request: Request;
|
|
23
|
+
}) => Promise<Response>;
|
|
24
|
+
//# sourceMappingURL=handover-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handover-handler.d.ts","sourceRoot":"","sources":["../../src/core/server/handover-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,6CAA6C;AAC7C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAK5C,MAAM,WAAW,8BAA8B;IAC7C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,oBAAoB,CAAC;CACxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,8BAAmC,IAEpE,aAAa;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,KAAG,OAAO,CAAC,QAAQ,CAAC,CAuCpE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { exchangeHandoverCode } from "./provider-token.js";
|
|
2
|
+
/** Maximale grootte van de request-body in bytes. */
|
|
3
|
+
export const HANDOVER_MAX_BODY_BYTES = 2048;
|
|
4
|
+
/** Maximale lengte van een handover-code. */
|
|
5
|
+
export const HANDOVER_MAX_CODE_LENGTH = 512;
|
|
6
|
+
const JSON_HEADERS = { "content-type": "application/json", "cache-control": "no-store" };
|
|
7
|
+
const fail = (status) => new Response(JSON.stringify({ error: "handover_failed" }), { status, headers: JSON_HEADERS });
|
|
8
|
+
/**
|
|
9
|
+
* Sinds 1.0.24. Server-route-handler voor de handover (vervangt de
|
|
10
|
+
* serverfunctie uit 1.0.19–1.0.23, die servercode in de clientbundel trok).
|
|
11
|
+
* Shells koppelen hem in `src/routes/api/public/auth/handover.ts`:
|
|
12
|
+
*
|
|
13
|
+
* server: { handlers: { POST: handoverExchangeHandler() } }
|
|
14
|
+
*
|
|
15
|
+
* Alleen POST met `application/json`, body ≤ 2 KB, `{ code }` niet-leeg en
|
|
16
|
+
* begrensd. Fail-closed: generieke 400/401 zonder detail, geen CORS-headers,
|
|
17
|
+
* code en tokens worden nooit gelogd. Uitsluitend via "./server".
|
|
18
|
+
*/
|
|
19
|
+
export function handoverExchangeHandler(options = {}) {
|
|
20
|
+
const exchange = options.exchange ?? exchangeHandoverCode;
|
|
21
|
+
return async ({ request }) => {
|
|
22
|
+
if (request.method !== "POST") {
|
|
23
|
+
return new Response(null, { status: 405, headers: { allow: "POST" } });
|
|
24
|
+
}
|
|
25
|
+
const type = (request.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
26
|
+
if (type !== "application/json")
|
|
27
|
+
return fail(400);
|
|
28
|
+
const declared = Number(request.headers.get("content-length") ?? "0");
|
|
29
|
+
if (declared > HANDOVER_MAX_BODY_BYTES)
|
|
30
|
+
return fail(400);
|
|
31
|
+
let raw;
|
|
32
|
+
try {
|
|
33
|
+
const buf = await request.arrayBuffer();
|
|
34
|
+
if (buf.byteLength > HANDOVER_MAX_BODY_BYTES)
|
|
35
|
+
return fail(400);
|
|
36
|
+
raw = new TextDecoder().decode(buf);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return fail(400);
|
|
40
|
+
}
|
|
41
|
+
let code;
|
|
42
|
+
try {
|
|
43
|
+
code = JSON.parse(raw)?.code;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return fail(400);
|
|
47
|
+
}
|
|
48
|
+
if (typeof code !== "string" || code.trim() === "" || code.length > HANDOVER_MAX_CODE_LENGTH) {
|
|
49
|
+
return fail(400);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const session = await exchange(code);
|
|
53
|
+
return new Response(JSON.stringify({ accessToken: session.accessToken, refreshToken: session.refreshToken }), { status: 200, headers: JSON_HEADERS });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return fail(401);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export type { ProviderAccount } from "./provider-token.js";
|
|
|
3
3
|
export { exchangeHandoverCode, HandoverExchangeError } from "./provider-token.js";
|
|
4
4
|
export type { HandoverSession } from "./provider-token.js";
|
|
5
5
|
export { CentralAuthConfigError, ProviderNotConnectedError, ProviderUpstreamError, CentralAuthUnavailableError, AmbiguousAccountError, } from "./provider-token.js";
|
|
6
|
-
export { requireModulePermission } from "./permissions.server.js";
|
|
6
|
+
export { requireModulePermission, isSuperadminServer } from "./permissions.server.js";
|
|
7
|
+
export { getRoleFlags } from "../auth/roles.js";
|
|
8
|
+
export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
|
|
9
|
+
export type { HandoverExchangeHandlerOptions } from "./handover-handler.js";
|
|
7
10
|
export type { PermissionLevel } from "./permissions.server.js";
|
|
8
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -3,4 +3,6 @@ export { exchangeHandoverCode, HandoverExchangeError } from "./provider-token.js
|
|
|
3
3
|
export { CentralAuthConfigError, ProviderNotConnectedError, ProviderUpstreamError, CentralAuthUnavailableError, AmbiguousAccountError, } from "./provider-token.js";
|
|
4
4
|
// Server-only rechtencheck. Uitsluitend via het "./server" subpath, nooit via
|
|
5
5
|
// de hoofdbarrel.
|
|
6
|
-
export { requireModulePermission } from "./permissions.server.js";
|
|
6
|
+
export { requireModulePermission, isSuperadminServer } from "./permissions.server.js";
|
|
7
|
+
export { getRoleFlags } from "../auth/roles.js";
|
|
8
|
+
export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { getRoleFlags } from "../auth/roles.js";
|
|
2
3
|
export type PermissionLevel = "none" | "view" | "edit";
|
|
3
4
|
/**
|
|
4
|
-
* Server-side rechtencheck via RPC `effective_module_permission(
|
|
5
|
+
* Server-side rechtencheck via RPC `effective_module_permission(_user_id,
|
|
6
|
+
* _module_id, _feature_id)` (3 argumenten).
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
+
* Sinds 1.0.23: `_user_id` komt uitsluitend uit de geverifieerde sessie van het
|
|
9
|
+
* verzoek; moduleniveau = `_feature_id ''`. **Vóór 1.0.23 liet deze functie
|
|
10
|
+
* alles door** (de RPC werd nooit gevonden → default-allow).
|
|
8
11
|
*
|
|
9
|
-
* - Ontbreekt de RPC (
|
|
10
|
-
*
|
|
11
|
-
* - Onvoldoende rechten → gooit een `Response` met status 403.
|
|
12
|
-
*
|
|
13
|
-
* Core raakt géén enkele tabel uit het rechtenschema; alleen deze RPC.
|
|
12
|
+
* - Ontbreekt de RPC (add-on niet geïnstalleerd) → toegestaan (default-allow).
|
|
13
|
+
* - Geen geldige sessie → 401. Onvoldoende rechten → 403.
|
|
14
14
|
*/
|
|
15
15
|
export declare function requireModulePermission(supabase: SupabaseClient<any, any, any>, moduleId: string, featureId?: string | null, level?: PermissionLevel): Promise<PermissionLevel>;
|
|
16
|
+
export { getRoleFlags };
|
|
17
|
+
/**
|
|
18
|
+
* Sinds 1.0.23. Superadmin? Claims eerst, terugval tabel `user_roles` (RLS:
|
|
19
|
+
* eigen rijen). Zichtbaarheid, geen beveiliging.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isSuperadminServer(supabase: SupabaseClient<any, any, any>, userId: string, claims?: Record<string, unknown> | null): Promise<boolean>;
|
|
16
22
|
//# sourceMappingURL=permissions.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.server.d.ts","sourceRoot":"","sources":["../../src/core/server/permissions.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.server.d.ts","sourceRoot":"","sources":["../../src/core/server/permissions.server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAuCvD;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAM,GAAG,IAAW,EAC/B,KAAK,GAAE,eAAwB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAyB1B;AAED,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EACvC,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACtC,OAAO,CAAC,OAAO,CAAC,CAKlB"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getRequest } from "@tanstack/react-start/server";
|
|
2
|
+
import { getRoleFlags } from "../auth/roles.js";
|
|
1
3
|
const RANK = { none: 0, view: 1, edit: 2 };
|
|
2
4
|
function isMissingRpc(error) {
|
|
3
5
|
const e = (error ?? {});
|
|
@@ -10,22 +12,44 @@ function isMissingRpc(error) {
|
|
|
10
12
|
message.includes("does not exist") ||
|
|
11
13
|
message.includes("could not find the function"));
|
|
12
14
|
}
|
|
15
|
+
/** Bearer-token van het huidige verzoek, of null. */
|
|
16
|
+
function requestToken() {
|
|
17
|
+
try {
|
|
18
|
+
const header = getRequest()?.headers?.get("authorization") ?? "";
|
|
19
|
+
return header.startsWith("Bearer ") ? header.slice(7) || null : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
13
25
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
* Geverifieerde user-id: uitsluitend uit het token van het verzoek, gevalideerd
|
|
27
|
+
* bij Supabase Auth (`getUser(token)`). Geen aanroeper-input.
|
|
28
|
+
*/
|
|
29
|
+
async function verifiedUserId(supabase) {
|
|
30
|
+
const token = requestToken();
|
|
31
|
+
const { data, error } = token ? await supabase.auth.getUser(token) : await supabase.auth.getUser();
|
|
32
|
+
if (error || !data?.user?.id)
|
|
33
|
+
throw new Response("Unauthorized", { status: 401 });
|
|
34
|
+
return data.user.id;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Server-side rechtencheck via RPC `effective_module_permission(_user_id,
|
|
38
|
+
* _module_id, _feature_id)` (3 argumenten).
|
|
18
39
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
40
|
+
* Sinds 1.0.23: `_user_id` komt uitsluitend uit de geverifieerde sessie van het
|
|
41
|
+
* verzoek; moduleniveau = `_feature_id ''`. **Vóór 1.0.23 liet deze functie
|
|
42
|
+
* alles door** (de RPC werd nooit gevonden → default-allow).
|
|
22
43
|
*
|
|
23
|
-
*
|
|
44
|
+
* - Ontbreekt de RPC (add-on niet geïnstalleerd) → toegestaan (default-allow).
|
|
45
|
+
* - Geen geldige sessie → 401. Onvoldoende rechten → 403.
|
|
24
46
|
*/
|
|
25
47
|
export async function requireModulePermission(supabase, moduleId, featureId = null, level = "view") {
|
|
48
|
+
const userId = await verifiedUserId(supabase);
|
|
26
49
|
const { data, error } = await supabase.rpc("effective_module_permission", {
|
|
50
|
+
_user_id: userId,
|
|
27
51
|
_module_id: moduleId,
|
|
28
|
-
_feature_id: featureId,
|
|
52
|
+
_feature_id: featureId ?? "",
|
|
29
53
|
});
|
|
30
54
|
if (error) {
|
|
31
55
|
if (isMissingRpc(error))
|
|
@@ -39,3 +63,16 @@ export async function requireModulePermission(supabase, moduleId, featureId = nu
|
|
|
39
63
|
}
|
|
40
64
|
return effective;
|
|
41
65
|
}
|
|
66
|
+
export { getRoleFlags };
|
|
67
|
+
/**
|
|
68
|
+
* Sinds 1.0.23. Superadmin? Claims eerst, terugval tabel `user_roles` (RLS:
|
|
69
|
+
* eigen rijen). Zichtbaarheid, geen beveiliging.
|
|
70
|
+
*/
|
|
71
|
+
export async function isSuperadminServer(supabase, userId, claims) {
|
|
72
|
+
if (getRoleFlags(claims).isSuperadmin)
|
|
73
|
+
return true;
|
|
74
|
+
const { data, error } = await supabase.from("user_roles").select("role").eq("user_id", userId);
|
|
75
|
+
if (error || !Array.isArray(data))
|
|
76
|
+
return false;
|
|
77
|
+
return data.some((r) => r.role === "superadmin");
|
|
78
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FlowModule } from "../types/module.js";
|
|
2
|
+
/**
|
|
3
|
+
* Sinds 1.0.23. Context vanuit AuthenticatedLayout.
|
|
4
|
+
* `modules` = de ruwe lijst van de shell (vóór transforms);
|
|
5
|
+
* `visibleModules` = wat de zijbalk toont (na rechten + extensies).
|
|
6
|
+
* Zichtbaarheid, geen beveiliging.
|
|
7
|
+
*/
|
|
8
|
+
export interface ShellContextValue {
|
|
9
|
+
shellId?: string;
|
|
10
|
+
modules: FlowModule[];
|
|
11
|
+
visibleModules: FlowModule[];
|
|
12
|
+
/** True tijdens het eerste laden van de rechten. */
|
|
13
|
+
permissionsLoading: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const ShellContext: import("react").Context<ShellContextValue | null>;
|
|
16
|
+
/** Sinds 1.0.23. `null` buiten AuthenticatedLayout. Zichtbaarheid, geen beveiliging. */
|
|
17
|
+
export declare function useShell(): ShellContextValue | null;
|
|
18
|
+
/**
|
|
19
|
+
* Sinds 1.0.23. Vorm uit contract §6.1. `null` = geen provider; een lege of
|
|
20
|
+
* ontbrekende lijst mag nooit iets deactiveren. Zichtbaarheid, geen beveiliging.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useShellModules(): {
|
|
23
|
+
modules: FlowModule[];
|
|
24
|
+
shellId?: string;
|
|
25
|
+
} | null;
|
|
26
|
+
//# sourceMappingURL=ShellContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellContext.d.ts","sourceRoot":"","sources":["../../src/core/shell/ShellContext.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,oDAAoD;IACpD,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,YAAY,mDAAgD,CAAC;AAE1E,wFAAwF;AACxF,wBAAgB,QAAQ,IAAI,iBAAiB,GAAG,IAAI,CAEnD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGpF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
export const ShellContext = createContext(null);
|
|
3
|
+
/** Sinds 1.0.23. `null` buiten AuthenticatedLayout. Zichtbaarheid, geen beveiliging. */
|
|
4
|
+
export function useShell() {
|
|
5
|
+
return useContext(ShellContext);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sinds 1.0.23. Vorm uit contract §6.1. `null` = geen provider; een lege of
|
|
9
|
+
* ontbrekende lijst mag nooit iets deactiveren. Zichtbaarheid, geen beveiliging.
|
|
10
|
+
*/
|
|
11
|
+
export function useShellModules() {
|
|
12
|
+
const ctx = useContext(ShellContext);
|
|
13
|
+
return ctx ? { modules: ctx.modules, shellId: ctx.shellId } : null;
|
|
14
|
+
}
|