@kilpi/react-client
Version:
Kilpi React Client Components · Kilpi is the authorization framework for full-stack TypeScript applications, designed for flexible, powerful, agnostic, intuitive and developer friendly authorization.
190 lines (184 loc) • 5.14 kB
JavaScript
// src/plugins/ReactClientPlugin.ts
import { createKilpiClientPlugin } from "@kilpi/client";
// src/components/AuthorizeClient.tsx
function create_AuthorizeClient(client) {
void client;
return function AuthorizeClient({
policy,
children,
Unauthorized,
Idle,
Pending,
Error,
isDisabled
}) {
const query = policy.useAuthorize({ isDisabled });
switch (query.status) {
// Handle pending, idle and error states with custom functions
case "pending": {
return typeof Pending === "function" ? Pending(query) : Pending ?? null;
}
case "idle": {
if (Idle === void 0) {
return typeof Pending === "function" ? Pending(query) : Pending ?? null;
}
return typeof Idle === "function" ? Idle(query) : Idle ?? null;
}
case "error": {
return typeof Error === "function" ? Error(query) : Error ?? null;
}
// Success: Decide component to render based on granted
case "success": {
if (query.granted) {
return typeof children === "function" ? children(query) : children ?? null;
}
return typeof Unauthorized === "function" ? Unauthorized(query) : Unauthorized ?? null;
}
}
};
}
// src/hooks/useAuthorize.ts
import "@kilpi/client";
import { useEffect as useEffect2, useState } from "react";
// src/hooks/useUnmountAbortSignalRef.ts
import { useEffect, useRef } from "react";
function useUnmountAbortSignalRef() {
const controllerRef = useRef(new AbortController());
const signalRef = useRef(controllerRef.current.signal);
useEffect(() => {
return () => {
controllerRef.current.abort();
};
}, []);
return signalRef;
}
// src/hooks/useAuthorize.ts
function create_useAuthorize(Client, policy) {
void Client;
function getStatusWithFlags(status) {
return {
status,
isIdle: status === "idle",
isError: status === "error",
isSuccess: status === "success",
isPending: status === "pending"
};
}
return function useAuthorize(options = {}) {
const isDisabled = options.isDisabled || false;
const [status, setStatus] = useState("idle");
const [error, setError] = useState(null);
const [decision, setDecision] = useState(null);
const signalRef = useUnmountAbortSignalRef();
useEffect2(() => {
return Client.$hooks.onCacheInvalidate(({ matches }) => {
if (matches(policy)) {
setStatus("idle");
setError(null);
setDecision(null);
}
});
}, []);
useEffect2(() => {
if (isDisabled) {
setStatus("idle");
setError(null);
setDecision(null);
return;
}
if (status !== "idle") {
return;
}
setStatus("pending");
policy.authorize({ signal: signalRef.current }).then((value) => {
if (signalRef.current.aborted) return;
setDecision(value);
setStatus("success");
}).catch((err) => {
if (signalRef.current.aborted) return;
setError(err);
setStatus("error");
});
}, [isDisabled, status, signalRef]);
switch (status) {
case "idle":
return {
...getStatusWithFlags(status),
decision: null,
granted: false,
error: null,
isDisabled
};
case "pending":
return {
...getStatusWithFlags(status),
decision: null,
granted: false,
error: null,
isDisabled
};
case "error":
return {
...getStatusWithFlags(status),
decision: null,
granted: false,
error,
isDisabled
};
case "success":
if (decision?.granted) {
return {
...getStatusWithFlags(status),
decision,
granted: true,
error: null,
isDisabled
};
} else {
return {
...getStatusWithFlags(status),
// `decision` should never be null here, but we need to satisfy TypeScript
decision: decision || { granted: false },
granted: false,
error: null,
isDisabled
};
}
}
};
}
// src/plugins/ReactClientPlugin.ts
function ReactClientPlugin() {
return createKilpiClientPlugin((Client) => {
return {
/**
* Extend the client to allow creating components.
*/
extendClient() {
return {
$createReactClientComponents() {
const AuthorizeClient = create_AuthorizeClient(Client);
return {
AuthorizeClient
};
}
};
},
/**
* Extend policies to add the `useAuthorize` hook. Ensure matches extension type
* with the satisfies clause.
*/
extendPolicy(policy) {
return {
useAuthorize(options) {
const useAuthorize = create_useAuthorize(Client, policy);
return useAuthorize(options);
}
};
}
};
});
}
export {
ReactClientPlugin
};