@apex-inc/react 0.3.0

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.
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ /**
3
+ * `useApexPreferences` — headless data hook backing the
4
+ * `<ApexPreferenceCenter>` component.
5
+ *
6
+ * Customers with their own design system import this hook directly
7
+ * and render their own controls. The default `<ApexPreferenceCenter>`
8
+ * built on top of this hook ships as the "drop a tag, get a working
9
+ * surface" path; the hook is the "I'll style it myself" path.
10
+ *
11
+ * The hook never throws — all errors land on `state.error` so the
12
+ * caller can render an inline message instead of an error boundary
13
+ * fallback. This matches the dashboard's own preference page UX.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.useApexPreferences = useApexPreferences;
17
+ const react_1 = require("react");
18
+ const DEFAULT_API = "https://app.apex.inc";
19
+ function decodeHmacEuid(token) {
20
+ // The Apex token shape is `<base64url-payload>.<base64url-sig>`.
21
+ // We only read the payload; the server re-verifies the signature
22
+ // on every call, so we never trust the decoded values on the
23
+ // client beyond using them as fetch parameters.
24
+ const parts = token.split(".");
25
+ if (parts.length !== 2)
26
+ return {};
27
+ try {
28
+ const padded = parts[0]
29
+ .replace(/-/g, "+")
30
+ .replace(/_/g, "/")
31
+ .padEnd(parts[0].length + ((4 - (parts[0].length % 4)) % 4), "=");
32
+ const json = atob(padded);
33
+ const obj = JSON.parse(json);
34
+ return { euid: obj.euid, pk: obj.pk };
35
+ }
36
+ catch {
37
+ return {};
38
+ }
39
+ }
40
+ function useApexPreferences(options) {
41
+ const apiBaseUrl = (options.apiBaseUrl ?? DEFAULT_API).replace(/\/$/, "");
42
+ // Resolve identity once. With token auth we crack open the payload
43
+ // to read the workspaceKey + endUserId; with cookie auth we require
44
+ // both as explicit props.
45
+ const tokenAuth = "token" in options.endUserAuth ? options.endUserAuth : null;
46
+ const decoded = tokenAuth ? decodeHmacEuid(tokenAuth.token) : null;
47
+ const endUserId = options.endUserId ?? decoded?.euid;
48
+ const workspaceKey = options.workspaceKey ?? decoded?.pk;
49
+ const [state, setState] = (0, react_1.useState)({
50
+ prefs: null,
51
+ loading: true,
52
+ saving: false,
53
+ saved: false,
54
+ error: null,
55
+ });
56
+ const authHeaders = (0, react_1.useCallback)(() => {
57
+ const headers = {
58
+ "Content-Type": "application/json",
59
+ };
60
+ if (tokenAuth) {
61
+ headers["Authorization"] = `Bearer ${tokenAuth.token}`;
62
+ }
63
+ if (workspaceKey) {
64
+ headers["x-workspace-key"] = workspaceKey;
65
+ }
66
+ return headers;
67
+ }, [tokenAuth, workspaceKey]);
68
+ const fetchOpts = (0, react_1.useCallback)(() => {
69
+ const init = { headers: authHeaders() };
70
+ if (!tokenAuth) {
71
+ // Cookie-auth callers need credentials forwarded to pick up the
72
+ // Apex dashboard's `apex_session` cookie.
73
+ init.credentials = "include";
74
+ }
75
+ return init;
76
+ }, [authHeaders, tokenAuth]);
77
+ const refresh = (0, react_1.useCallback)(async () => {
78
+ if (!endUserId) {
79
+ setState((s) => ({
80
+ ...s,
81
+ loading: false,
82
+ error: "endUserId not available — pass it explicitly or use a token-based auth.",
83
+ }));
84
+ return;
85
+ }
86
+ setState((s) => ({ ...s, loading: true, error: null }));
87
+ try {
88
+ const url = new URL(`/api/communications/preferences/${encodeURIComponent(endUserId)}`, apiBaseUrl);
89
+ if (workspaceKey)
90
+ url.searchParams.set("workspaceKey", workspaceKey);
91
+ const res = await fetch(url.toString(), fetchOpts());
92
+ if (!res.ok) {
93
+ throw new Error(`HTTP ${res.status}`);
94
+ }
95
+ const prefs = (await res.json());
96
+ setState((s) => ({ ...s, prefs, loading: false }));
97
+ }
98
+ catch (err) {
99
+ setState((s) => ({
100
+ ...s,
101
+ loading: false,
102
+ error: err instanceof Error
103
+ ? err.message
104
+ : "Failed to load preferences",
105
+ }));
106
+ }
107
+ }, [apiBaseUrl, endUserId, fetchOpts, workspaceKey]);
108
+ (0, react_1.useEffect)(() => {
109
+ void refresh();
110
+ }, [refresh]);
111
+ const save = (0, react_1.useCallback)(async (patch) => {
112
+ if (!endUserId)
113
+ return;
114
+ setState((s) => ({ ...s, saving: true, saved: false, error: null }));
115
+ try {
116
+ const url = new URL(`/api/communications/preferences/${encodeURIComponent(endUserId)}`, apiBaseUrl);
117
+ if (workspaceKey)
118
+ url.searchParams.set("workspaceKey", workspaceKey);
119
+ const res = await fetch(url.toString(), {
120
+ method: "PATCH",
121
+ ...fetchOpts(),
122
+ body: JSON.stringify(patch),
123
+ });
124
+ if (!res.ok) {
125
+ throw new Error(`HTTP ${res.status}`);
126
+ }
127
+ const saved = (await res.json());
128
+ setState((s) => ({ ...s, prefs: saved, saving: false, saved: true }));
129
+ // Clear the "saved!" badge after 2s — matches the dashboard.
130
+ setTimeout(() => {
131
+ setState((s) => ({ ...s, saved: false }));
132
+ }, 2000);
133
+ }
134
+ catch (err) {
135
+ setState((s) => ({
136
+ ...s,
137
+ saving: false,
138
+ error: err instanceof Error
139
+ ? err.message
140
+ : "Failed to save preferences",
141
+ }));
142
+ }
143
+ }, [apiBaseUrl, endUserId, fetchOpts, workspaceKey]);
144
+ const setGlobalOptOut = (0, react_1.useCallback)(async (value) => {
145
+ if (!state.prefs)
146
+ return;
147
+ const next = { ...state.prefs, globalOptOut: value };
148
+ setState((s) => ({ ...s, prefs: next }));
149
+ await save({ globalOptOut: value });
150
+ }, [save, state.prefs]);
151
+ const setChannel = (0, react_1.useCallback)(async (channel, enabled) => {
152
+ if (!state.prefs)
153
+ return;
154
+ // Mirror the legacy `in_app_push` alias when toggling `inbox`.
155
+ const channelPatch = {
156
+ [channel]: enabled,
157
+ };
158
+ if (channel === "inbox")
159
+ channelPatch.in_app_push = enabled;
160
+ const next = {
161
+ ...state.prefs,
162
+ channelPreferences: {
163
+ ...state.prefs.channelPreferences,
164
+ ...channelPatch,
165
+ },
166
+ };
167
+ setState((s) => ({ ...s, prefs: next }));
168
+ await save({ channelPreferences: channelPatch });
169
+ }, [save, state.prefs]);
170
+ const setCommunicationOverride = (0, react_1.useCallback)(async (communicationId, optedOut) => {
171
+ if (!state.prefs)
172
+ return;
173
+ const next = {
174
+ ...state.prefs,
175
+ communicationOverrides: {
176
+ ...state.prefs.communicationOverrides,
177
+ [communicationId]: { optedOut },
178
+ },
179
+ };
180
+ setState((s) => ({ ...s, prefs: next }));
181
+ await save({
182
+ communicationOverrides: { [communicationId]: { optedOut } },
183
+ });
184
+ }, [save, state.prefs]);
185
+ return {
186
+ ...state,
187
+ refresh,
188
+ setGlobalOptOut,
189
+ setChannel,
190
+ setCommunicationOverride,
191
+ };
192
+ }
193
+ //# sourceMappingURL=useApexPreferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useApexPreferences.js","sourceRoot":"","sources":["../src/useApexPreferences.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@apex-inc/react",
3
+ "version": "0.3.0",
4
+ "description": "React components and hooks for Apex — ApexPreferenceCenter for communication preferences plus useApexVariant for code-level A/B experiments.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": ["dist", "skills"],
17
+ "scripts": {
18
+ "build": "tsc && tsc -p tsconfig.esm.json",
19
+ "dev": "tsc --watch"
20
+ },
21
+ "keywords": [
22
+ "apex",
23
+ "react",
24
+ "preferences",
25
+ "notifications",
26
+ "preference-center"
27
+ ],
28
+ "license": "MIT",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "peerDependencies": {
33
+ "react": ">=17.0.0",
34
+ "react-dom": ">=17.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "^18.0.0",
38
+ "@types/react-dom": "^18.0.0",
39
+ "react": "^18.0.0",
40
+ "react-dom": "^18.0.0",
41
+ "typescript": "^5.7.0"
42
+ }
43
+ }