@apex-inc/sdk 0.5.0 → 0.7.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.
- package/README.md +4 -4
- package/dist/esm/index.js +150 -7
- package/dist/esm/management.js +18 -18
- package/dist/index.d.ts +62 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -7
- package/dist/index.js.map +1 -1
- package/dist/management.d.ts +9 -9
- package/dist/management.d.ts.map +1 -1
- package/dist/management.js +18 -18
- package/dist/management.js.map +1 -1
- package/package.json +1 -1
- package/skills/apex-communications/SKILL.md +4 -4
- package/skills/apex-experimentation/SKILL.md +20 -3
- package/skills/apex-growth-tracking/SKILL.md +6 -6
- package/skills/apex-integration-cookbook/SKILL.md +4 -4
- package/skills/apex-partner-network/SKILL.md +4 -4
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Works in Node.js 18+ and browser environments. Zero runtime dependencies.
|
|
|
6
6
|
|
|
7
7
|
## Access
|
|
8
8
|
|
|
9
|
-
Apex is invite-only during the private-beta period. If you received an invite, your onboarding email contains your `
|
|
9
|
+
Apex is invite-only during the private-beta period. If you received an invite, your onboarding email contains your `workspaceKey` and `apiUrl`. Full API documentation is available in-app once your account is active.
|
|
10
10
|
|
|
11
11
|
Not in the beta yet? Request access at [apex.inc](https://apex.inc) (coming soon).
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ npm install @apex-inc/sdk
|
|
|
22
22
|
import { init, track, identify } from "@apex-inc/sdk";
|
|
23
23
|
|
|
24
24
|
init({
|
|
25
|
-
|
|
25
|
+
workspaceKey: "your-workspace-key",
|
|
26
26
|
apiUrl: "https://your-apex-instance.com",
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -40,7 +40,7 @@ expansion, and retention rollups automatically:
|
|
|
40
40
|
import { init, identify, group, feature, track } from "@apex-inc/sdk";
|
|
41
41
|
import { EVENTS, type ActivatedEvent } from "@apex-inc/sdk/plg-events";
|
|
42
42
|
|
|
43
|
-
init({
|
|
43
|
+
init({ workspaceKey: "your-workspace-key", apiUrl: "https://app.apex.inc" });
|
|
44
44
|
|
|
45
45
|
// Identify the authenticated user.
|
|
46
46
|
identify("chris@acme.com", { visitorId: "apex_vid_from_cookie" });
|
|
@@ -93,7 +93,7 @@ stitching just works.
|
|
|
93
93
|
## Multi-workspace orgs
|
|
94
94
|
|
|
95
95
|
If your organization runs more than one workspace (e.g. a SaaS product
|
|
96
|
-
plus a partner marketplace), each workspace has its own `
|
|
96
|
+
plus a partner marketplace), each workspace has its own `workspaceKey`.
|
|
97
97
|
Initialize one SDK instance per workspace; event identity stitches at
|
|
98
98
|
the org level via `OrgPerson` so a single human tracked in both shows
|
|
99
99
|
up as one person in cross-workspace cohorts and reports.
|
package/dist/esm/index.js
CHANGED
|
@@ -14,7 +14,7 @@ async function sendBatch(events) {
|
|
|
14
14
|
const apiUrl = config.apiUrl ?? DEFAULT_API_URL;
|
|
15
15
|
const url = `${apiUrl.replace(/\/$/, "")}/api/events`;
|
|
16
16
|
const body = JSON.stringify({
|
|
17
|
-
|
|
17
|
+
workspaceKey: config.workspaceKey,
|
|
18
18
|
userId,
|
|
19
19
|
events,
|
|
20
20
|
// MMP-070 — propagate the SDK-level environment override (when
|
|
@@ -68,11 +68,16 @@ function enqueue(event) {
|
|
|
68
68
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
69
69
|
/**
|
|
70
70
|
* Initialize the Apex SDK. Must be called before track() or identify().
|
|
71
|
+
* Accepts `workspaceKey` (canonical) or the deprecated `projectKey`.
|
|
71
72
|
*/
|
|
72
73
|
export function init(options) {
|
|
73
|
-
|
|
74
|
+
const workspaceKey = options.workspaceKey ?? options.projectKey;
|
|
75
|
+
if (!workspaceKey) {
|
|
76
|
+
throw new Error("apex.init: `workspaceKey` is required");
|
|
77
|
+
}
|
|
78
|
+
config = { ...options, workspaceKey };
|
|
74
79
|
queue = [];
|
|
75
|
-
log("Initialized with
|
|
80
|
+
log("Initialized with workspace:", workspaceKey);
|
|
76
81
|
startFlushTimer();
|
|
77
82
|
}
|
|
78
83
|
/**
|
|
@@ -133,7 +138,7 @@ async function stitchIdentity(email, browserVisitorId, metadata, stableUserId) {
|
|
|
133
138
|
return;
|
|
134
139
|
const apiUrl = config.apiUrl ?? DEFAULT_API_URL;
|
|
135
140
|
const url = `${apiUrl.replace(/\/$/, "")}/api/identity/stitch`;
|
|
136
|
-
const effectiveVisitorId = browserVisitorId || `sdk-${config.
|
|
141
|
+
const effectiveVisitorId = browserVisitorId || `sdk-${config.workspaceKey}-${email}`;
|
|
137
142
|
try {
|
|
138
143
|
const fetchFn = typeof fetch !== "undefined" ? fetch : await loadNodeFetch();
|
|
139
144
|
await fetchFn(url, {
|
|
@@ -142,7 +147,7 @@ async function stitchIdentity(email, browserVisitorId, metadata, stableUserId) {
|
|
|
142
147
|
body: JSON.stringify({
|
|
143
148
|
visitorId: effectiveVisitorId,
|
|
144
149
|
email,
|
|
145
|
-
|
|
150
|
+
workspaceKey: config.workspaceKey,
|
|
146
151
|
...(stableUserId ? { userId: stableUserId } : {}),
|
|
147
152
|
metadata: metadata ?? {},
|
|
148
153
|
}),
|
|
@@ -270,6 +275,144 @@ export function shutdown() {
|
|
|
270
275
|
config = null;
|
|
271
276
|
log("Shut down");
|
|
272
277
|
}
|
|
278
|
+
function experimentMurmurhash3(key) {
|
|
279
|
+
let h = 0x811c9dc5;
|
|
280
|
+
for (let i = 0; i < key.length; i++) {
|
|
281
|
+
h ^= key.charCodeAt(i);
|
|
282
|
+
h = Math.imul(h, 0x01000193);
|
|
283
|
+
}
|
|
284
|
+
return (h >>> 0) % 100;
|
|
285
|
+
}
|
|
286
|
+
function getExperimentVisitorId() {
|
|
287
|
+
if (!isBrowser)
|
|
288
|
+
return "";
|
|
289
|
+
const key = "apex_vid";
|
|
290
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${key}=([^;]*)`));
|
|
291
|
+
if (match)
|
|
292
|
+
return decodeURIComponent(match[1]);
|
|
293
|
+
const id = typeof crypto !== "undefined" && crypto.randomUUID
|
|
294
|
+
? crypto.randomUUID()
|
|
295
|
+
: `vid_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
|
|
296
|
+
const d = new Date();
|
|
297
|
+
d.setTime(d.getTime() + 365 * 86400000);
|
|
298
|
+
document.cookie = `${key}=${encodeURIComponent(id)};expires=${d.toUTCString()};path=/;SameSite=Lax`;
|
|
299
|
+
return id;
|
|
300
|
+
}
|
|
301
|
+
function getExperimentPreview() {
|
|
302
|
+
if (!isBrowser)
|
|
303
|
+
return null;
|
|
304
|
+
const params = new URLSearchParams(window.location.search);
|
|
305
|
+
const variant = params.get("_apex_preview");
|
|
306
|
+
const expId = params.get("_apex_exp");
|
|
307
|
+
if (variant)
|
|
308
|
+
return { variant, expId };
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
// Deduped per (experiment, variant) for the page session so repeated
|
|
312
|
+
// `getVariant` calls don't double-count exposures.
|
|
313
|
+
const firedExperimentExposures = new Set();
|
|
314
|
+
function fireExperimentExposure(experimentId, variant, apiBase, workspaceKey) {
|
|
315
|
+
if (!isBrowser)
|
|
316
|
+
return;
|
|
317
|
+
const dedupeKey = `${experimentId}:${variant}`;
|
|
318
|
+
if (firedExperimentExposures.has(dedupeKey))
|
|
319
|
+
return;
|
|
320
|
+
firedExperimentExposures.add(dedupeKey);
|
|
321
|
+
const visitorId = getExperimentVisitorId();
|
|
322
|
+
try {
|
|
323
|
+
void fetch(`${apiBase}/api/events`, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: {
|
|
326
|
+
"Content-Type": "application/json",
|
|
327
|
+
...(workspaceKey ? { "x-apex-workspace": workspaceKey } : {}),
|
|
328
|
+
},
|
|
329
|
+
credentials: "include",
|
|
330
|
+
keepalive: true,
|
|
331
|
+
body: JSON.stringify({
|
|
332
|
+
type: "experiment_exposure",
|
|
333
|
+
visitorId,
|
|
334
|
+
url: window.location.href,
|
|
335
|
+
timestamp: new Date().toISOString(),
|
|
336
|
+
experimentId,
|
|
337
|
+
variant,
|
|
338
|
+
data: { experiment_id: experimentId, variant_key: variant, surface: "web" },
|
|
339
|
+
}),
|
|
340
|
+
}).catch(() => { });
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
/* exposure tracking is best-effort */
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Resolve the experiment arm for the current visitor — the React-free
|
|
348
|
+
* equivalent of `useApexVariant`.
|
|
349
|
+
*
|
|
350
|
+
* Resolution order:
|
|
351
|
+
* 1. Preview mode (`?_apex_preview=variant_b&_apex_exp=<id>`) — author
|
|
352
|
+
* preview, never counted as an exposure.
|
|
353
|
+
* 2. Cached assignment in `localStorage` (`apex_var_<id>`).
|
|
354
|
+
* 3. Server assignment (`GET /api/experiments/{id}/assign?source=sdk`).
|
|
355
|
+
* 4. Deterministic murmur-hash of `visitorId + experimentId` (fallback).
|
|
356
|
+
*
|
|
357
|
+
* Returns `"control"` during SSR / non-browser contexts. Fires the
|
|
358
|
+
* canonical `experiment_exposure` event (deduped) for cases 2–4.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* import { getVariant } from "@apex-inc/sdk";
|
|
362
|
+
* const arm = await getVariant("exp_123");
|
|
363
|
+
* if (arm === "variant_b") renderVariant(); else renderControl();
|
|
364
|
+
*/
|
|
365
|
+
export async function getVariant(experimentId, opts = {}) {
|
|
366
|
+
if (!isBrowser)
|
|
367
|
+
return "control";
|
|
368
|
+
const apiBase = (opts.apiUrl ?? config?.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
369
|
+
const workspaceKey = opts.workspaceKey ?? config?.workspaceKey;
|
|
370
|
+
// 1. Preview mode — author previewing, not a real exposure.
|
|
371
|
+
const preview = getExperimentPreview();
|
|
372
|
+
if (preview && (!preview.expId || preview.expId === experimentId)) {
|
|
373
|
+
return preview.variant;
|
|
374
|
+
}
|
|
375
|
+
const cacheKey = `apex_var_${experimentId}`;
|
|
376
|
+
// 2. Cached assignment — still count the (deduped) exposure.
|
|
377
|
+
const cached = localStorage.getItem(cacheKey);
|
|
378
|
+
if (cached === "control" || cached === "variant_b") {
|
|
379
|
+
fireExperimentExposure(experimentId, cached, apiBase, workspaceKey);
|
|
380
|
+
return cached;
|
|
381
|
+
}
|
|
382
|
+
const visitorId = getExperimentVisitorId();
|
|
383
|
+
if (!visitorId)
|
|
384
|
+
return "control";
|
|
385
|
+
// 3. Server assignment.
|
|
386
|
+
try {
|
|
387
|
+
const res = await fetch(`${apiBase}/api/experiments/${experimentId}/assign?source=sdk`, {
|
|
388
|
+
credentials: "include",
|
|
389
|
+
headers: workspaceKey ? { "x-apex-workspace": workspaceKey } : {},
|
|
390
|
+
});
|
|
391
|
+
if (!res.ok)
|
|
392
|
+
throw new Error(`${res.status}`);
|
|
393
|
+
const data = (await res.json());
|
|
394
|
+
localStorage.setItem(cacheKey, data.variant);
|
|
395
|
+
fireExperimentExposure(experimentId, data.variant, apiBase, workspaceKey);
|
|
396
|
+
return data.variant;
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// 4. Deterministic hash fallback.
|
|
400
|
+
const bucket = experimentMurmurhash3(visitorId + experimentId);
|
|
401
|
+
const fallback = bucket < 50 ? "control" : "variant_b";
|
|
402
|
+
localStorage.setItem(cacheKey, fallback);
|
|
403
|
+
fireExperimentExposure(experimentId, fallback, apiBase, workspaceKey);
|
|
404
|
+
return fallback;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Clear a cached experiment assignment (e.g. when an experiment is
|
|
409
|
+
* archived or completed). No-op outside the browser.
|
|
410
|
+
*/
|
|
411
|
+
export function clearVariant(experimentId) {
|
|
412
|
+
if (isBrowser) {
|
|
413
|
+
localStorage.removeItem(`apex_var_${experimentId}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
273
416
|
export { createManagementClient, } from "./management";
|
|
274
417
|
/**
|
|
275
418
|
* Generate a stable idempotency key. Prefers `crypto.randomUUID()`
|
|
@@ -325,8 +468,8 @@ export async function sendServerEvents(config, events, options = {}) {
|
|
|
325
468
|
"x-api-key": config.apiKey,
|
|
326
469
|
"idempotency-key": idempotencyKey,
|
|
327
470
|
};
|
|
328
|
-
if (config.
|
|
329
|
-
headers["x-apex-
|
|
471
|
+
if (config.workspaceKey)
|
|
472
|
+
headers["x-apex-workspace"] = config.workspaceKey;
|
|
330
473
|
const fetchFn = typeof fetch !== "undefined" ? fetch : await loadNodeFetch();
|
|
331
474
|
const res = await fetchFn(url, {
|
|
332
475
|
method: "POST",
|
package/dist/esm/management.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Programmatic access to Apex APIs using an API key (same as MCP server).
|
|
3
|
-
* Intended for Node.js backends; sends x-api-key and x-apex-
|
|
3
|
+
* Intended for Node.js backends; sends x-api-key and x-apex-workspace on each request.
|
|
4
4
|
*/
|
|
5
5
|
function baseUrl(cfg) {
|
|
6
6
|
return (cfg.apiUrl ?? "http://localhost:3000").replace(/\/$/, "");
|
|
@@ -9,7 +9,7 @@ function keyHeaders(cfg) {
|
|
|
9
9
|
return {
|
|
10
10
|
"Content-Type": "application/json",
|
|
11
11
|
"x-api-key": cfg.apiKey,
|
|
12
|
-
"x-apex-
|
|
12
|
+
"x-apex-workspace": cfg.workspaceKey,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
@@ -21,7 +21,7 @@ export function createManagementClient(cfg) {
|
|
|
21
21
|
const headers = keyHeaders(cfg);
|
|
22
22
|
return {
|
|
23
23
|
async listExperiments() {
|
|
24
|
-
const res = await fetch(`${root}/api/experiments`, { headers });
|
|
24
|
+
const res = await fetch(`${root}/api/experiments?unified=true`, { headers });
|
|
25
25
|
if (!res.ok) {
|
|
26
26
|
const text = await res.text().catch(() => "");
|
|
27
27
|
throw new Error(`Apex listExperiments failed (${res.status}): ${text}`);
|
|
@@ -31,7 +31,7 @@ export function createManagementClient(cfg) {
|
|
|
31
31
|
async assignVariant(experimentId, visitorId) {
|
|
32
32
|
const cookie = [
|
|
33
33
|
`apex_vid=${encodeURIComponent(visitorId)}`,
|
|
34
|
-
`apex_project=${encodeURIComponent(cfg.
|
|
34
|
+
`apex_project=${encodeURIComponent(cfg.workspaceKey)}`,
|
|
35
35
|
].join("; ");
|
|
36
36
|
const res = await fetch(`${root}/api/experiments/${encodeURIComponent(experimentId)}/assign`, {
|
|
37
37
|
headers: { ...headers, Cookie: cookie },
|
|
@@ -192,7 +192,7 @@ export function createManagementClient(cfg) {
|
|
|
192
192
|
},
|
|
193
193
|
// ─── Partner Network — Programs (MMP-161) ─────────────────────────
|
|
194
194
|
//
|
|
195
|
-
// Every method below runs under the merchant's x-apex-
|
|
195
|
+
// Every method below runs under the merchant's x-apex-workspace scope.
|
|
196
196
|
// Partner-facing flows (self-signup, dashboard, Stripe Connect OAuth)
|
|
197
197
|
// are cookie-authed in the portal and not wrapped in this SDK.
|
|
198
198
|
async listPrograms() {
|
|
@@ -334,14 +334,14 @@ export function createManagementClient(cfg) {
|
|
|
334
334
|
// API-key caller as a trusted server.
|
|
335
335
|
preferences: {
|
|
336
336
|
async get(endUserId) {
|
|
337
|
-
const res = await fetch(`${root}/api/communications/preferences/${encodeURIComponent(endUserId)}?
|
|
337
|
+
const res = await fetch(`${root}/api/communications/preferences/${encodeURIComponent(endUserId)}?workspaceKey=${encodeURIComponent(cfg.workspaceKey)}`, { headers });
|
|
338
338
|
if (!res.ok) {
|
|
339
339
|
throw new Error(`Apex preferences.get failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
340
340
|
}
|
|
341
341
|
return res.json();
|
|
342
342
|
},
|
|
343
343
|
async update(endUserId, patch) {
|
|
344
|
-
const res = await fetch(`${root}/api/communications/preferences/${encodeURIComponent(endUserId)}?
|
|
344
|
+
const res = await fetch(`${root}/api/communications/preferences/${encodeURIComponent(endUserId)}?workspaceKey=${encodeURIComponent(cfg.workspaceKey)}`, {
|
|
345
345
|
method: "PATCH",
|
|
346
346
|
headers,
|
|
347
347
|
body: JSON.stringify(patch),
|
|
@@ -403,7 +403,7 @@ export function createManagementClient(cfg) {
|
|
|
403
403
|
* `get()` returns the current hub signal + per-step overrides
|
|
404
404
|
* (read-only). `complete(stepId)` / `skip(stepId, reason)` mirror
|
|
405
405
|
* the dashboard's write contract at
|
|
406
|
-
* POST /api/workspaces/<
|
|
406
|
+
* POST /api/workspaces/<workspaceKey>/setup-state/<stepId>.
|
|
407
407
|
*
|
|
408
408
|
* Allowed step IDs are enforced server-side via the SetupRegistry
|
|
409
409
|
* allowlist. Unknown IDs return 400.
|
|
@@ -411,7 +411,7 @@ export function createManagementClient(cfg) {
|
|
|
411
411
|
workspace: {
|
|
412
412
|
setupState: {
|
|
413
413
|
async get() {
|
|
414
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
414
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/setup-state`, { headers });
|
|
415
415
|
if (!res.ok) {
|
|
416
416
|
throw new Error(`Apex workspace.setupState.get failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
417
417
|
}
|
|
@@ -422,13 +422,13 @@ export function createManagementClient(cfg) {
|
|
|
422
422
|
};
|
|
423
423
|
},
|
|
424
424
|
async complete(stepId) {
|
|
425
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
425
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/setup-state/${encodeURIComponent(stepId)}/complete`, { method: "POST", headers });
|
|
426
426
|
if (!res.ok) {
|
|
427
427
|
throw new Error(`Apex workspace.setupState.complete failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
428
428
|
}
|
|
429
429
|
},
|
|
430
430
|
async skip(stepId, reason) {
|
|
431
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
431
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/setup-state/${encodeURIComponent(stepId)}/skip`, {
|
|
432
432
|
method: "POST",
|
|
433
433
|
headers,
|
|
434
434
|
body: JSON.stringify({ reason: reason ?? null }),
|
|
@@ -442,13 +442,13 @@ export function createManagementClient(cfg) {
|
|
|
442
442
|
/**
|
|
443
443
|
* Data Sources registry — the producers that send events into this
|
|
444
444
|
* workspace (Website, iOS / Android apps, Backend). Mirrors
|
|
445
|
-
* /api/workspaces/<
|
|
445
|
+
* /api/workspaces/<workspaceKey>/data-sources. `dataSourceId` is
|
|
446
446
|
* reporting-only; these methods manage the registry, not access control.
|
|
447
447
|
*/
|
|
448
448
|
dataSources: {
|
|
449
449
|
async list(opts) {
|
|
450
450
|
const qs = opts?.includeArchived ? "?includeArchived=true" : "";
|
|
451
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
451
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources${qs}`, { headers });
|
|
452
452
|
if (!res.ok) {
|
|
453
453
|
throw new Error(`Apex dataSources.list failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
454
454
|
}
|
|
@@ -456,7 +456,7 @@ export function createManagementClient(cfg) {
|
|
|
456
456
|
return json.data ?? [];
|
|
457
457
|
},
|
|
458
458
|
async get(id) {
|
|
459
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
459
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources/${encodeURIComponent(id)}`, { headers });
|
|
460
460
|
if (!res.ok) {
|
|
461
461
|
throw new Error(`Apex dataSources.get failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
462
462
|
}
|
|
@@ -464,7 +464,7 @@ export function createManagementClient(cfg) {
|
|
|
464
464
|
return json.data;
|
|
465
465
|
},
|
|
466
466
|
async create(params) {
|
|
467
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
467
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources`, { method: "POST", headers, body: JSON.stringify(params) });
|
|
468
468
|
if (!res.ok) {
|
|
469
469
|
throw new Error(`Apex dataSources.create failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
470
470
|
}
|
|
@@ -472,7 +472,7 @@ export function createManagementClient(cfg) {
|
|
|
472
472
|
return json.data;
|
|
473
473
|
},
|
|
474
474
|
async update(id, patch) {
|
|
475
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
475
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources/${encodeURIComponent(id)}`, { method: "PATCH", headers, body: JSON.stringify(patch) });
|
|
476
476
|
if (!res.ok) {
|
|
477
477
|
throw new Error(`Apex dataSources.update failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
478
478
|
}
|
|
@@ -480,13 +480,13 @@ export function createManagementClient(cfg) {
|
|
|
480
480
|
return json.data;
|
|
481
481
|
},
|
|
482
482
|
async archive(id) {
|
|
483
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
483
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources/${encodeURIComponent(id)}`, { method: "DELETE", headers });
|
|
484
484
|
if (!res.ok) {
|
|
485
485
|
throw new Error(`Apex dataSources.archive failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
486
486
|
}
|
|
487
487
|
},
|
|
488
488
|
async health(id) {
|
|
489
|
-
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.
|
|
489
|
+
const res = await fetch(`${root}/api/workspaces/${encodeURIComponent(cfg.workspaceKey)}/data-sources/${encodeURIComponent(id)}/health`, { headers });
|
|
490
490
|
if (!res.ok) {
|
|
491
491
|
throw new Error(`Apex dataSources.health failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
492
492
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export interface ApexConfig {
|
|
2
|
-
|
|
2
|
+
workspaceKey: string;
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use `workspaceKey`. Permanent back-compat alias from
|
|
5
|
+
* the project→workspace rename (2026-06); existing installs passing
|
|
6
|
+
* `projectKey` keep working forever.
|
|
7
|
+
*/
|
|
8
|
+
projectKey?: string;
|
|
3
9
|
apiUrl?: string;
|
|
4
10
|
/** Flush interval in ms (default: 5000) */
|
|
5
11
|
flushInterval?: number;
|
|
@@ -15,15 +21,17 @@ export interface ApexConfig {
|
|
|
15
21
|
* for ~95% of merchants. Set this when:
|
|
16
22
|
*
|
|
17
23
|
* - Your staging deploy lives on a custom domain that looks like
|
|
18
|
-
* production (e.g. `app-staging.acme.com` resolves to
|
|
19
|
-
*
|
|
24
|
+
* production (e.g. `app-staging.acme.com` resolves to your
|
|
25
|
+
* staging stack but you want it sliced as preview).
|
|
20
26
|
* - You want to force `localhost` in a hosted test harness.
|
|
21
|
-
* - You're running CI smoke tests that should
|
|
27
|
+
* - You're running CI smoke tests that should stay out of your
|
|
28
|
+
* production dashboard slice.
|
|
22
29
|
*
|
|
23
30
|
* Accepted values: `"production"`, `"preview"`, `"localhost"`.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* The environment drives dashboard slicing and the per-environment
|
|
32
|
+
* breakdown on the Usage page. Every ingested event counts toward
|
|
33
|
+
* the Events meter regardless of environment; use `testMode` (or a
|
|
34
|
+
* sandbox workspace) for traffic that should never bill.
|
|
27
35
|
*/
|
|
28
36
|
environment?: "production" | "preview" | "localhost";
|
|
29
37
|
}
|
|
@@ -58,8 +66,12 @@ export interface FeaturePayload {
|
|
|
58
66
|
}
|
|
59
67
|
/**
|
|
60
68
|
* Initialize the Apex SDK. Must be called before track() or identify().
|
|
69
|
+
* Accepts `workspaceKey` (canonical) or the deprecated `projectKey`.
|
|
61
70
|
*/
|
|
62
|
-
export declare function init(options: ApexConfig
|
|
71
|
+
export declare function init(options: ApexConfig | (Omit<ApexConfig, "workspaceKey"> & {
|
|
72
|
+
workspaceKey?: string;
|
|
73
|
+
projectKey: string;
|
|
74
|
+
})): void;
|
|
63
75
|
/**
|
|
64
76
|
* Track a custom event.
|
|
65
77
|
*/
|
|
@@ -205,6 +217,44 @@ export declare const trackCart: {
|
|
|
205
217
|
* Shut down the SDK — flush remaining events and stop the timer.
|
|
206
218
|
*/
|
|
207
219
|
export declare function shutdown(): void;
|
|
220
|
+
/** Experiment arm returned by `getVariant`. */
|
|
221
|
+
export type Variant = "control" | "variant_b";
|
|
222
|
+
/** Per-call overrides for `getVariant`. Falls back to `init()` config. */
|
|
223
|
+
export interface GetVariantOptions {
|
|
224
|
+
/**
|
|
225
|
+
* Apex API base URL (no trailing slash). Defaults to the SDK's
|
|
226
|
+
* configured `apiUrl` (from `init()`), then to the current origin in
|
|
227
|
+
* the browser. Set for cross-origin customer apps.
|
|
228
|
+
*/
|
|
229
|
+
apiUrl?: string;
|
|
230
|
+
/** Workspace key — sent as the `x-apex-workspace` header when present. */
|
|
231
|
+
workspaceKey?: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Resolve the experiment arm for the current visitor — the React-free
|
|
235
|
+
* equivalent of `useApexVariant`.
|
|
236
|
+
*
|
|
237
|
+
* Resolution order:
|
|
238
|
+
* 1. Preview mode (`?_apex_preview=variant_b&_apex_exp=<id>`) — author
|
|
239
|
+
* preview, never counted as an exposure.
|
|
240
|
+
* 2. Cached assignment in `localStorage` (`apex_var_<id>`).
|
|
241
|
+
* 3. Server assignment (`GET /api/experiments/{id}/assign?source=sdk`).
|
|
242
|
+
* 4. Deterministic murmur-hash of `visitorId + experimentId` (fallback).
|
|
243
|
+
*
|
|
244
|
+
* Returns `"control"` during SSR / non-browser contexts. Fires the
|
|
245
|
+
* canonical `experiment_exposure` event (deduped) for cases 2–4.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* import { getVariant } from "@apex-inc/sdk";
|
|
249
|
+
* const arm = await getVariant("exp_123");
|
|
250
|
+
* if (arm === "variant_b") renderVariant(); else renderControl();
|
|
251
|
+
*/
|
|
252
|
+
export declare function getVariant(experimentId: string, opts?: GetVariantOptions): Promise<Variant>;
|
|
253
|
+
/**
|
|
254
|
+
* Clear a cached experiment assignment (e.g. when an experiment is
|
|
255
|
+
* archived or completed). No-op outside the browser.
|
|
256
|
+
*/
|
|
257
|
+
export declare function clearVariant(experimentId: string): void;
|
|
208
258
|
export { createManagementClient, type ManagementConfig, type AssignVariantResult, type CommChannel, type EndUserCommPreferences, type InboxNotification, type ApexDataSourceKind, type ApexDataSourceStatus, type ApexDataSource, type ApexDataSourceHealth, type ApexHostSeen, type ApexAnalyticsSources, } from "./management";
|
|
209
259
|
/**
|
|
210
260
|
* Configuration for server-side event delivery.
|
|
@@ -218,10 +268,10 @@ export interface ServerEventsConfig {
|
|
|
218
268
|
*/
|
|
219
269
|
apiUrl?: string;
|
|
220
270
|
/**
|
|
221
|
-
* When your org has multiple
|
|
222
|
-
* Optional — Apex auto-picks if the org has exactly one
|
|
271
|
+
* When your org has multiple workspaces, set this to disambiguate.
|
|
272
|
+
* Optional — Apex auto-picks if the org has exactly one workspace.
|
|
223
273
|
*/
|
|
224
|
-
|
|
274
|
+
workspaceKey?: string;
|
|
225
275
|
}
|
|
226
276
|
/**
|
|
227
277
|
* A single server-side event in the Apex Spec shape.
|
|
@@ -256,7 +306,7 @@ export interface ServerEventsResponse {
|
|
|
256
306
|
deduped?: boolean;
|
|
257
307
|
};
|
|
258
308
|
metadata?: {
|
|
259
|
-
|
|
309
|
+
workspaceKey?: string;
|
|
260
310
|
idempotencyKey?: string;
|
|
261
311
|
batchSize?: number;
|
|
262
312
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|