@meetreeve/consent
v0.1.0
Published
Framework-agnostic Reeve.Compliance consent-capture client — useConsent() + <ConsentGate> for versioned ToS/Privacy/... acceptance. Companion to reeve-services' consent-capture substrate (DEV-2614) and the require_current_consent enforcement dependency (D
Readme
@meetreeve/consent
Framework-agnostic Reeve.Compliance consent-capture client — useConsent() +
<ConsentGate> for versioned ToS/Privacy/... acceptance. Companion to
reeve-services' consent-capture substrate (DEV-2614) and its
require_current_consent enforcement dependency (DEV-3714).
Why this exists
The backend already exposes GET /api/compliance/v1/consent/status
(needs_acceptance per doc_type), POST /consent, and GET /legal/current —
but nothing on the frontend read them. This package is the consumer half:
one hook, one gate component, reused by every app that needs to block a
signed-in user until they've accepted the current version of something.
The substrate's /api/compliance/v1/* is HMAC-guarded (X-Reeve-Signature,
shared secret) — a browser can never hold that secret directly. Every
consumer wires a small same-origin BFF route that resolves the caller's
identity server-side and signs the forwarded request. See Studio's
src/app/api/compliance/[...path]/route.ts for the reference BFF this
package was built against.
Entrypoints
| Import | What |
|---|---|
| @meetreeve/consent/core | Framework-free fetch client (fetchConsentStatus, postConsent, fetchCurrentLegal) |
| @meetreeve/consent/react | useConsent() hook + <ConsentGate> component |
/react
import { ConsentGate } from "@meetreeve/consent/react";
import { useAuth } from "@/lib/auth-context";
function StudioShell({ children }) {
const { user } = useAuth();
return (
<ConsentGate
baseUrl="/api/compliance"
hostApp="reeve"
subjectId={user?.user_id ?? null}
docTypes={["tos", "privacy"]}
>
{children}
</ConsentGate>
);
}subjectId={null} (no signed-in user) renders children unconditionally —
matches Studio's action-level (not page-level) auth model: anonymous viewers
still see the shell; there's nothing to gate for someone who hasn't signed in.
Pass renderGate for custom gate UI instead of the built-in minimal default:
<ConsentGate
{...config}
renderGate={({ pendingDocTypes, accept, loading }) => (
<MyOwnModal docTypes={pendingDocTypes} onAccept={accept} loading={loading} />
)}
>
{children}
</ConsentGate>/core
import { fetchConsentStatus, postConsent } from "@meetreeve/consent/core";
const status = await fetchConsentStatus(
{ baseUrl: "/api/compliance" },
{ hostApp: "reeve", subjectId: user.sub, docTypes: ["tos"] },
);