@cavuno/board 4.15.0 → 4.18.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/CHANGELOG.md +27 -0
- package/dist/{_spec-DdWB8z1Z.d.mts → _spec-Dk0TIRUc.d.mts} +19 -7
- package/dist/{_spec-DdWB8z1Z.d.ts → _spec-Dk0TIRUc.d.ts} +19 -7
- package/dist/analytics.d.mts +58 -0
- package/dist/analytics.d.ts +58 -0
- package/dist/analytics.js +222 -0
- package/dist/analytics.mjs +199 -0
- package/dist/bin.mjs +99 -26
- package/dist/{blog-B7xorPBw.d.ts → blog-1THfucKR.d.ts} +1 -1
- package/dist/{blog-DopIQsKV.d.mts → blog-C04NWTh_.d.mts} +1 -1
- package/dist/{board-Bpnqn4Rg.d.ts → board-CqeoQAa1.d.ts} +2 -2
- package/dist/{board-CkgTW4cC.d.mts → board-DIO9aFXx.d.mts} +2 -2
- package/dist/{breadcrumbs-DelEqbSz.d.ts → breadcrumbs-CCT1oC2-.d.ts} +2 -2
- package/dist/{breadcrumbs-CAppxMoS.d.mts → breadcrumbs-mE9Zsxlq.d.mts} +2 -2
- package/dist/browser/cavuno-board.global.min.js +1 -1
- package/dist/{chunk-W73BOGOQ.mjs → chunk-2WBQVVOB.mjs} +1 -0
- package/dist/doctor.js +93 -21
- package/dist/doctor.mjs +93 -21
- package/dist/filters.d.mts +2 -2
- package/dist/filters.d.ts +2 -2
- package/dist/format.d.mts +4 -4
- package/dist/format.d.ts +4 -4
- package/dist/index.d.mts +12 -12
- package/dist/index.d.ts +12 -12
- package/dist/index.js +6 -2
- package/dist/index.mjs +6 -3
- package/dist/{jobs-Dq0TTcRf.d.mts → jobs-CVmAWGtJ.d.mts} +1 -1
- package/dist/{jobs-CExc4QdI.d.ts → jobs-Dvv0vX8p.d.ts} +1 -1
- package/dist/{search-D6hau3d4.d.mts → search-BJ5myYaf.d.mts} +1 -1
- package/dist/{search-CZOec95V.d.ts → search-CcstbHOe.d.ts} +1 -1
- package/dist/{seo-C1hFa0-v.d.ts → seo-CWO6N-iE.d.ts} +1 -1
- package/dist/{seo-CJSwd_ss.d.mts → seo-Ds6tLMqU.d.mts} +1 -1
- package/dist/seo-files.d.mts +2 -2
- package/dist/seo-files.d.ts +2 -2
- package/dist/seo.d.mts +6 -6
- package/dist/seo.d.ts +6 -6
- package/dist/server.d.mts +6 -6
- package/dist/server.d.ts +6 -6
- package/dist/server.mjs +1 -1
- package/dist/sitemap.d.mts +6 -6
- package/dist/sitemap.d.ts +6 -6
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/package.json +11 -1
- package/skills/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
This changelog records changes that affect Board API compatibility, exported
|
|
4
4
|
types, runtime behavior, or supported integration patterns.
|
|
5
5
|
|
|
6
|
+
## 4.18.0 — 2026-09-02
|
|
7
|
+
|
|
8
|
+
- **`board.auth.requestMagicLink({ intent: 'sign_in' })`**: new optional
|
|
9
|
+
`intent` on `POST /auth/magic-link`. Pass `'sign_in'` from a sign-in form so
|
|
10
|
+
an unknown email is refused with 404 `board_auth_account_not_found` instead
|
|
11
|
+
of silently minting a candidate sign-up token. Omit it to keep the previous
|
|
12
|
+
sign-in-or-sign-up behaviour. Any other value is a 400.
|
|
13
|
+
- **`BOARD_API_ERROR_CODES`**: adds `board_auth_account_not_found`.
|
|
14
|
+
|
|
15
|
+
## 4.17.0 — 2026-08-31
|
|
16
|
+
|
|
17
|
+
- **`EmployerPipelineStage.systemStage`**: a closed set of pipeline literals —
|
|
18
|
+
`shortlisted`, `contacted`, `replied`, `review`, `offer`, `hired`,
|
|
19
|
+
`rejected`, or `null` — instead of an open string. `applied` remains a
|
|
20
|
+
list-query alias for `review`, not a `systemStage` value.
|
|
21
|
+
|
|
22
|
+
## 4.16.0 — 2026-08-31
|
|
23
|
+
|
|
24
|
+
- **`@cavuno/board/analytics`**: new subpath with `install({ publishableKey })`
|
|
25
|
+
and `track(action, payload?)`. Uses the board publishable key (`pk_…`) only.
|
|
26
|
+
Default collect URL is Cavuno central collect; pass a first-party
|
|
27
|
+
`/.well-known/cavuno/collect` URL when the board host serves those handlers.
|
|
28
|
+
- **Stable analytics tenant**: events are keyed by the immutable public board
|
|
29
|
+
id (`boards_<accountId>`), not the mutable board slug.
|
|
30
|
+
- **Doctor**: `npx @cavuno/board doctor` includes an analytics-surface scan that
|
|
31
|
+
flags legacy third-party tracker credentials in app source.
|
|
32
|
+
|
|
6
33
|
## 4.15.0 — 2026-08-30
|
|
7
34
|
|
|
8
35
|
- **LinkedIn conversion IDs on `board.context().analytics`**: five nullable
|
|
@@ -249,6 +249,11 @@ interface components {
|
|
|
249
249
|
email: string;
|
|
250
250
|
/** @description Optional same-origin path to carry through the email link. */
|
|
251
251
|
returnTo?: string;
|
|
252
|
+
/**
|
|
253
|
+
* @description Pass `sign_in` from a sign-in form: an unknown email returns 404 `board_auth_account_not_found` and nothing is minted or sent; existing users are unchanged. Omit it for today’s behaviour (existing users get a sign-in token; unknown emails get a candidate sign-up token when candidate registration is enabled).
|
|
254
|
+
* @enum {string}
|
|
255
|
+
*/
|
|
256
|
+
intent?: "sign_in";
|
|
252
257
|
};
|
|
253
258
|
BoardAuthResetPasswordBody: {
|
|
254
259
|
token: string;
|
|
@@ -1054,7 +1059,7 @@ interface components {
|
|
|
1054
1059
|
inOfficePeriod?: "per_week" | "per_month" | "per_year";
|
|
1055
1060
|
/** @description How often the candidate must be in-office over `inOfficePeriod`. */
|
|
1056
1061
|
inOfficeFrequency?: number;
|
|
1057
|
-
/** @description Physical office locations associated with the job. Each entry is
|
|
1062
|
+
/** @description Physical office locations associated with the job. Prefer `{query: "City, Country"}` for free-form input; `{city, country, region?, locality?}` is also accepted when you already have structured fields. Each entry is resolved server-side; a country mismatch returns `400 jobs_unresolvable_location`. */
|
|
1058
1063
|
officeLocations?: components["schemas"]["JobOfficeLocationInput"][];
|
|
1059
1064
|
/** @description The job title. */
|
|
1060
1065
|
title: string;
|
|
@@ -1211,8 +1216,11 @@ interface components {
|
|
|
1211
1216
|
object: "employer_pipeline_stage";
|
|
1212
1217
|
jobId: string;
|
|
1213
1218
|
label: string;
|
|
1214
|
-
/**
|
|
1215
|
-
|
|
1219
|
+
/**
|
|
1220
|
+
* @description The stable system meaning, or `null` for a custom employer stage.
|
|
1221
|
+
* @enum {string|null}
|
|
1222
|
+
*/
|
|
1223
|
+
systemStage: "shortlisted" | "contacted" | "replied" | "review" | "offer" | "hired" | "rejected" | null;
|
|
1216
1224
|
isProtected: boolean;
|
|
1217
1225
|
hidden: boolean;
|
|
1218
1226
|
position: number;
|
|
@@ -1299,7 +1307,7 @@ interface components {
|
|
|
1299
1307
|
inOfficePeriod?: "per_week" | "per_month" | "per_year";
|
|
1300
1308
|
/** @description How often the candidate must be in-office over `inOfficePeriod`. */
|
|
1301
1309
|
inOfficeFrequency?: number;
|
|
1302
|
-
/** @description Physical office locations associated with the job. Each entry is
|
|
1310
|
+
/** @description Physical office locations associated with the job. Prefer `{query: "City, Country"}` for free-form input; `{city, country, region?, locality?}` is also accepted when you already have structured fields. Each entry is resolved server-side; a country mismatch returns `400 jobs_unresolvable_location`. */
|
|
1303
1311
|
officeLocations?: components["schemas"]["JobOfficeLocationInput"][];
|
|
1304
1312
|
/** @description The job title. */
|
|
1305
1313
|
title?: string;
|
|
@@ -1355,6 +1363,9 @@ interface components {
|
|
|
1355
1363
|
displayName: string | null;
|
|
1356
1364
|
};
|
|
1357
1365
|
JobOfficeLocationInput: {
|
|
1366
|
+
/** @description Free-form location string (e.g. `"Berlin, Germany"`, `"Utrecht, Netherlands"`, `"Mountain View, California, USA"`). Resolved server-side; rejected when no high-confidence match is found. */
|
|
1367
|
+
query: string;
|
|
1368
|
+
} | {
|
|
1358
1369
|
/** @description Neighborhood or sub-locality. */
|
|
1359
1370
|
locality?: string;
|
|
1360
1371
|
/** @description City. */
|
|
@@ -1363,9 +1374,6 @@ interface components {
|
|
|
1363
1374
|
region?: string;
|
|
1364
1375
|
/** @description ISO 3166-1 alpha-2 country code OR recognized country name/alias. Aliases are normalized to canonical alpha-2 server-side (e.g. `US`, `USA`, `United States` → `US`; `UK`, `GB`, `United Kingdom` → `GB`). */
|
|
1365
1376
|
country: string;
|
|
1366
|
-
} | {
|
|
1367
|
-
/** @description Free-form location string (e.g. `"Berlin, Germany"`, `"Mountain View, California, USA"`). Mapbox parses + ranks candidates server-side; rejects on low confidence. */
|
|
1368
|
-
query: string;
|
|
1369
1377
|
};
|
|
1370
1378
|
JobPostingBillingOptions: {
|
|
1371
1379
|
/** @enum {string} */
|
|
@@ -2597,9 +2605,13 @@ interface components {
|
|
|
2597
2605
|
StartConversationBody: {
|
|
2598
2606
|
candidateBoardUserId: string;
|
|
2599
2607
|
body: string;
|
|
2608
|
+
/** @description Optional job id. When set, this send is from that job pipeline and may auto-advance Shortlisted to Contacted. */
|
|
2609
|
+
job?: string;
|
|
2600
2610
|
} | {
|
|
2601
2611
|
candidateHandle: string;
|
|
2602
2612
|
body: string;
|
|
2613
|
+
/** @description Optional job id. When set, this send is from that job pipeline and may auto-advance Shortlisted to Contacted. */
|
|
2614
|
+
job?: string;
|
|
2603
2615
|
};
|
|
2604
2616
|
SuggestResult: {
|
|
2605
2617
|
/** @enum {string} */
|
|
@@ -249,6 +249,11 @@ interface components {
|
|
|
249
249
|
email: string;
|
|
250
250
|
/** @description Optional same-origin path to carry through the email link. */
|
|
251
251
|
returnTo?: string;
|
|
252
|
+
/**
|
|
253
|
+
* @description Pass `sign_in` from a sign-in form: an unknown email returns 404 `board_auth_account_not_found` and nothing is minted or sent; existing users are unchanged. Omit it for today’s behaviour (existing users get a sign-in token; unknown emails get a candidate sign-up token when candidate registration is enabled).
|
|
254
|
+
* @enum {string}
|
|
255
|
+
*/
|
|
256
|
+
intent?: "sign_in";
|
|
252
257
|
};
|
|
253
258
|
BoardAuthResetPasswordBody: {
|
|
254
259
|
token: string;
|
|
@@ -1054,7 +1059,7 @@ interface components {
|
|
|
1054
1059
|
inOfficePeriod?: "per_week" | "per_month" | "per_year";
|
|
1055
1060
|
/** @description How often the candidate must be in-office over `inOfficePeriod`. */
|
|
1056
1061
|
inOfficeFrequency?: number;
|
|
1057
|
-
/** @description Physical office locations associated with the job. Each entry is
|
|
1062
|
+
/** @description Physical office locations associated with the job. Prefer `{query: "City, Country"}` for free-form input; `{city, country, region?, locality?}` is also accepted when you already have structured fields. Each entry is resolved server-side; a country mismatch returns `400 jobs_unresolvable_location`. */
|
|
1058
1063
|
officeLocations?: components["schemas"]["JobOfficeLocationInput"][];
|
|
1059
1064
|
/** @description The job title. */
|
|
1060
1065
|
title: string;
|
|
@@ -1211,8 +1216,11 @@ interface components {
|
|
|
1211
1216
|
object: "employer_pipeline_stage";
|
|
1212
1217
|
jobId: string;
|
|
1213
1218
|
label: string;
|
|
1214
|
-
/**
|
|
1215
|
-
|
|
1219
|
+
/**
|
|
1220
|
+
* @description The stable system meaning, or `null` for a custom employer stage.
|
|
1221
|
+
* @enum {string|null}
|
|
1222
|
+
*/
|
|
1223
|
+
systemStage: "shortlisted" | "contacted" | "replied" | "review" | "offer" | "hired" | "rejected" | null;
|
|
1216
1224
|
isProtected: boolean;
|
|
1217
1225
|
hidden: boolean;
|
|
1218
1226
|
position: number;
|
|
@@ -1299,7 +1307,7 @@ interface components {
|
|
|
1299
1307
|
inOfficePeriod?: "per_week" | "per_month" | "per_year";
|
|
1300
1308
|
/** @description How often the candidate must be in-office over `inOfficePeriod`. */
|
|
1301
1309
|
inOfficeFrequency?: number;
|
|
1302
|
-
/** @description Physical office locations associated with the job. Each entry is
|
|
1310
|
+
/** @description Physical office locations associated with the job. Prefer `{query: "City, Country"}` for free-form input; `{city, country, region?, locality?}` is also accepted when you already have structured fields. Each entry is resolved server-side; a country mismatch returns `400 jobs_unresolvable_location`. */
|
|
1303
1311
|
officeLocations?: components["schemas"]["JobOfficeLocationInput"][];
|
|
1304
1312
|
/** @description The job title. */
|
|
1305
1313
|
title?: string;
|
|
@@ -1355,6 +1363,9 @@ interface components {
|
|
|
1355
1363
|
displayName: string | null;
|
|
1356
1364
|
};
|
|
1357
1365
|
JobOfficeLocationInput: {
|
|
1366
|
+
/** @description Free-form location string (e.g. `"Berlin, Germany"`, `"Utrecht, Netherlands"`, `"Mountain View, California, USA"`). Resolved server-side; rejected when no high-confidence match is found. */
|
|
1367
|
+
query: string;
|
|
1368
|
+
} | {
|
|
1358
1369
|
/** @description Neighborhood or sub-locality. */
|
|
1359
1370
|
locality?: string;
|
|
1360
1371
|
/** @description City. */
|
|
@@ -1363,9 +1374,6 @@ interface components {
|
|
|
1363
1374
|
region?: string;
|
|
1364
1375
|
/** @description ISO 3166-1 alpha-2 country code OR recognized country name/alias. Aliases are normalized to canonical alpha-2 server-side (e.g. `US`, `USA`, `United States` → `US`; `UK`, `GB`, `United Kingdom` → `GB`). */
|
|
1365
1376
|
country: string;
|
|
1366
|
-
} | {
|
|
1367
|
-
/** @description Free-form location string (e.g. `"Berlin, Germany"`, `"Mountain View, California, USA"`). Mapbox parses + ranks candidates server-side; rejects on low confidence. */
|
|
1368
|
-
query: string;
|
|
1369
1377
|
};
|
|
1370
1378
|
JobPostingBillingOptions: {
|
|
1371
1379
|
/** @enum {string} */
|
|
@@ -2597,9 +2605,13 @@ interface components {
|
|
|
2597
2605
|
StartConversationBody: {
|
|
2598
2606
|
candidateBoardUserId: string;
|
|
2599
2607
|
body: string;
|
|
2608
|
+
/** @description Optional job id. When set, this send is from that job pipeline and may auto-advance Shortlisted to Contacted. */
|
|
2609
|
+
job?: string;
|
|
2600
2610
|
} | {
|
|
2601
2611
|
candidateHandle: string;
|
|
2602
2612
|
body: string;
|
|
2613
|
+
/** @description Optional job id. When set, this send is from that job pipeline and may auto-advance Shortlisted to Contacted. */
|
|
2614
|
+
job?: string;
|
|
2603
2615
|
};
|
|
2604
2616
|
SuggestResult: {
|
|
2605
2617
|
/** @enum {string} */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
declare const DEFAULT_COLLECT_URL = "https://cavuno.com/api/analytics/collect";
|
|
2
|
+
declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js";
|
|
3
|
+
/** Placeholder tenant; collect rewrites from the publishable key. */
|
|
4
|
+
declare const PENDING_TENANT_ID = "boards_pending";
|
|
5
|
+
declare const WELL_KNOWN_ANALYTICS_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
|
|
6
|
+
declare const WELL_KNOWN_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
|
|
7
|
+
declare const WELL_KNOWN_COLLECT_PATH = "/.well-known/cavuno/collect";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Optional first-party well-known handlers for board origin Workers.
|
|
11
|
+
* Central collect remains the default when these paths are not mounted.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
declare const WELL_KNOWN_COLLECT_EVENTS_PATH = "/.well-known/cavuno/collect/v0/events";
|
|
15
|
+
/**
|
|
16
|
+
* Handle `/.well-known/cavuno/{analytics.js,collect}` when mounted on a Worker.
|
|
17
|
+
* Returns null for every other path so the host can fall through.
|
|
18
|
+
*/
|
|
19
|
+
declare function matchAnalyticsWellKnown(request: Request): Promise<Response | null>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `@cavuno/board/analytics` — client write surface for board analytics.
|
|
23
|
+
*
|
|
24
|
+
* Emits pageviews (via hosted script) and custom events to Cavuno collect.
|
|
25
|
+
* Uses the board publishable key only; no second analytics credential.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
type AnalyticsInstallOptions = {
|
|
29
|
+
publishableKey: string;
|
|
30
|
+
/** Default: Cavuno central collect (self-host safe) */
|
|
31
|
+
collectUrl?: string;
|
|
32
|
+
/** Default: Cavuno-hosted metrics script */
|
|
33
|
+
scriptUrl?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Load the Cavuno-hosted metrics script and remember collect credentials.
|
|
37
|
+
* Safe to call once per page; subsequent calls update collect targets only.
|
|
38
|
+
*/
|
|
39
|
+
declare function install(options: AnalyticsInstallOptions): void;
|
|
40
|
+
/**
|
|
41
|
+
* Emit a custom analytics event to Cavuno collect.
|
|
42
|
+
* Prefer `install` first so pageviews and vitals are also recorded.
|
|
43
|
+
*/
|
|
44
|
+
declare function track(action: string, payload?: Record<string, unknown>): void;
|
|
45
|
+
declare const analytics: {
|
|
46
|
+
install: typeof install;
|
|
47
|
+
track: typeof track;
|
|
48
|
+
matchAnalyticsWellKnown: typeof matchAnalyticsWellKnown;
|
|
49
|
+
DEFAULT_COLLECT_URL: string;
|
|
50
|
+
DEFAULT_SCRIPT_URL: string;
|
|
51
|
+
PENDING_TENANT_ID: string;
|
|
52
|
+
WELL_KNOWN_ANALYTICS_SCRIPT_PATH: string;
|
|
53
|
+
WELL_KNOWN_SCRIPT_PATH: string;
|
|
54
|
+
WELL_KNOWN_COLLECT_PATH: string;
|
|
55
|
+
WELL_KNOWN_COLLECT_EVENTS_PATH: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { type AnalyticsInstallOptions, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, track };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
declare const DEFAULT_COLLECT_URL = "https://cavuno.com/api/analytics/collect";
|
|
2
|
+
declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js";
|
|
3
|
+
/** Placeholder tenant; collect rewrites from the publishable key. */
|
|
4
|
+
declare const PENDING_TENANT_ID = "boards_pending";
|
|
5
|
+
declare const WELL_KNOWN_ANALYTICS_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
|
|
6
|
+
declare const WELL_KNOWN_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
|
|
7
|
+
declare const WELL_KNOWN_COLLECT_PATH = "/.well-known/cavuno/collect";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Optional first-party well-known handlers for board origin Workers.
|
|
11
|
+
* Central collect remains the default when these paths are not mounted.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
declare const WELL_KNOWN_COLLECT_EVENTS_PATH = "/.well-known/cavuno/collect/v0/events";
|
|
15
|
+
/**
|
|
16
|
+
* Handle `/.well-known/cavuno/{analytics.js,collect}` when mounted on a Worker.
|
|
17
|
+
* Returns null for every other path so the host can fall through.
|
|
18
|
+
*/
|
|
19
|
+
declare function matchAnalyticsWellKnown(request: Request): Promise<Response | null>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `@cavuno/board/analytics` — client write surface for board analytics.
|
|
23
|
+
*
|
|
24
|
+
* Emits pageviews (via hosted script) and custom events to Cavuno collect.
|
|
25
|
+
* Uses the board publishable key only; no second analytics credential.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
type AnalyticsInstallOptions = {
|
|
29
|
+
publishableKey: string;
|
|
30
|
+
/** Default: Cavuno central collect (self-host safe) */
|
|
31
|
+
collectUrl?: string;
|
|
32
|
+
/** Default: Cavuno-hosted metrics script */
|
|
33
|
+
scriptUrl?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Load the Cavuno-hosted metrics script and remember collect credentials.
|
|
37
|
+
* Safe to call once per page; subsequent calls update collect targets only.
|
|
38
|
+
*/
|
|
39
|
+
declare function install(options: AnalyticsInstallOptions): void;
|
|
40
|
+
/**
|
|
41
|
+
* Emit a custom analytics event to Cavuno collect.
|
|
42
|
+
* Prefer `install` first so pageviews and vitals are also recorded.
|
|
43
|
+
*/
|
|
44
|
+
declare function track(action: string, payload?: Record<string, unknown>): void;
|
|
45
|
+
declare const analytics: {
|
|
46
|
+
install: typeof install;
|
|
47
|
+
track: typeof track;
|
|
48
|
+
matchAnalyticsWellKnown: typeof matchAnalyticsWellKnown;
|
|
49
|
+
DEFAULT_COLLECT_URL: string;
|
|
50
|
+
DEFAULT_SCRIPT_URL: string;
|
|
51
|
+
PENDING_TENANT_ID: string;
|
|
52
|
+
WELL_KNOWN_ANALYTICS_SCRIPT_PATH: string;
|
|
53
|
+
WELL_KNOWN_SCRIPT_PATH: string;
|
|
54
|
+
WELL_KNOWN_COLLECT_PATH: string;
|
|
55
|
+
WELL_KNOWN_COLLECT_EVENTS_PATH: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { type AnalyticsInstallOptions, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, track };
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/analytics/index.ts
|
|
21
|
+
var analytics_exports = {};
|
|
22
|
+
__export(analytics_exports, {
|
|
23
|
+
DEFAULT_COLLECT_URL: () => DEFAULT_COLLECT_URL,
|
|
24
|
+
DEFAULT_SCRIPT_URL: () => DEFAULT_SCRIPT_URL,
|
|
25
|
+
PENDING_TENANT_ID: () => PENDING_TENANT_ID,
|
|
26
|
+
WELL_KNOWN_ANALYTICS_SCRIPT_PATH: () => WELL_KNOWN_ANALYTICS_SCRIPT_PATH,
|
|
27
|
+
WELL_KNOWN_COLLECT_EVENTS_PATH: () => WELL_KNOWN_COLLECT_EVENTS_PATH,
|
|
28
|
+
WELL_KNOWN_COLLECT_PATH: () => WELL_KNOWN_COLLECT_PATH,
|
|
29
|
+
WELL_KNOWN_SCRIPT_PATH: () => WELL_KNOWN_SCRIPT_PATH,
|
|
30
|
+
analytics: () => analytics,
|
|
31
|
+
install: () => install,
|
|
32
|
+
matchAnalyticsWellKnown: () => matchAnalyticsWellKnown,
|
|
33
|
+
track: () => track
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(analytics_exports);
|
|
36
|
+
|
|
37
|
+
// src/analytics/defaults.ts
|
|
38
|
+
var DEFAULT_COLLECT_URL = "https://cavuno.com/api/analytics/collect";
|
|
39
|
+
var DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js";
|
|
40
|
+
var PENDING_TENANT_ID = "boards_pending";
|
|
41
|
+
var WELL_KNOWN_ANALYTICS_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
|
|
42
|
+
var WELL_KNOWN_SCRIPT_PATH = WELL_KNOWN_ANALYTICS_SCRIPT_PATH;
|
|
43
|
+
var WELL_KNOWN_COLLECT_PATH = "/.well-known/cavuno/collect";
|
|
44
|
+
|
|
45
|
+
// src/analytics/well-known.ts
|
|
46
|
+
var WELL_KNOWN_COLLECT_EVENTS_PATH = "/.well-known/cavuno/collect/v0/events";
|
|
47
|
+
var CORS_HEADERS = {
|
|
48
|
+
"Access-Control-Allow-Origin": "*",
|
|
49
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
50
|
+
"Access-Control-Allow-Headers": "Authorization, Content-Type, Accept",
|
|
51
|
+
"Access-Control-Max-Age": "86400",
|
|
52
|
+
"Cross-Origin-Resource-Policy": "cross-origin"
|
|
53
|
+
};
|
|
54
|
+
function stripTrailingSlash(pathname) {
|
|
55
|
+
return pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
56
|
+
}
|
|
57
|
+
function withCors(response) {
|
|
58
|
+
const headers = new Headers(response.headers);
|
|
59
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
60
|
+
headers.set(key, value);
|
|
61
|
+
}
|
|
62
|
+
return new Response(response.body, {
|
|
63
|
+
status: response.status,
|
|
64
|
+
statusText: response.statusText,
|
|
65
|
+
headers
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function corsPreflight() {
|
|
69
|
+
return withCors(new Response(null, { status: 204 }));
|
|
70
|
+
}
|
|
71
|
+
async function proxyCollect(request, targetUrl) {
|
|
72
|
+
if (request.method === "OPTIONS") {
|
|
73
|
+
return corsPreflight();
|
|
74
|
+
}
|
|
75
|
+
if (request.method !== "POST") {
|
|
76
|
+
return withCors(new Response(null, { status: 405 }));
|
|
77
|
+
}
|
|
78
|
+
const headers = new Headers();
|
|
79
|
+
const authorization = request.headers.get("Authorization");
|
|
80
|
+
const contentType = request.headers.get("Content-Type");
|
|
81
|
+
const accept = request.headers.get("Accept");
|
|
82
|
+
if (authorization) headers.set("Authorization", authorization);
|
|
83
|
+
if (contentType) headers.set("Content-Type", contentType);
|
|
84
|
+
if (accept) headers.set("Accept", accept);
|
|
85
|
+
const body = await request.arrayBuffer();
|
|
86
|
+
const upstream = await fetch(targetUrl, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers,
|
|
89
|
+
body
|
|
90
|
+
});
|
|
91
|
+
return withCors(
|
|
92
|
+
new Response(upstream.body, {
|
|
93
|
+
status: upstream.status,
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": upstream.headers.get("Content-Type") ?? "application/json"
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
async function matchAnalyticsWellKnown(request) {
|
|
101
|
+
const path = stripTrailingSlash(new URL(request.url).pathname);
|
|
102
|
+
if (path === WELL_KNOWN_ANALYTICS_SCRIPT_PATH) {
|
|
103
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
104
|
+
return new Response(null, { status: 405 });
|
|
105
|
+
}
|
|
106
|
+
return Response.redirect(DEFAULT_SCRIPT_URL, 302);
|
|
107
|
+
}
|
|
108
|
+
if (path === WELL_KNOWN_COLLECT_PATH) {
|
|
109
|
+
return proxyCollect(request, DEFAULT_COLLECT_URL);
|
|
110
|
+
}
|
|
111
|
+
if (path === WELL_KNOWN_COLLECT_EVENTS_PATH) {
|
|
112
|
+
return proxyCollect(request, `${DEFAULT_COLLECT_URL}/v0/events`);
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/analytics/index.ts
|
|
118
|
+
var GLOBAL_NAME = "CavunoAnalytics";
|
|
119
|
+
var installed = null;
|
|
120
|
+
function getRoot() {
|
|
121
|
+
return globalThis;
|
|
122
|
+
}
|
|
123
|
+
function normalizeCollectUrl(url) {
|
|
124
|
+
return url.replace(/\/+$/, "");
|
|
125
|
+
}
|
|
126
|
+
function resolveDefaultCollectUrl() {
|
|
127
|
+
return DEFAULT_COLLECT_URL;
|
|
128
|
+
}
|
|
129
|
+
function resolveDefaultScriptUrl() {
|
|
130
|
+
return DEFAULT_SCRIPT_URL;
|
|
131
|
+
}
|
|
132
|
+
function postCollectJson(state, action, payload) {
|
|
133
|
+
const body = JSON.stringify({
|
|
134
|
+
publishableKey: state.publishableKey,
|
|
135
|
+
action,
|
|
136
|
+
payload: payload ?? {}
|
|
137
|
+
});
|
|
138
|
+
void fetch(state.collectUrl, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
Authorization: `Bearer ${state.publishableKey}`,
|
|
142
|
+
"Content-Type": "application/json",
|
|
143
|
+
Accept: "application/json"
|
|
144
|
+
},
|
|
145
|
+
body,
|
|
146
|
+
keepalive: true
|
|
147
|
+
}).catch(() => {
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function exposeGlobal(state) {
|
|
151
|
+
const root = getRoot();
|
|
152
|
+
root[GLOBAL_NAME] = {
|
|
153
|
+
trackEvent(action, payload) {
|
|
154
|
+
postCollectJson(state, action, payload);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function injectScript(state, scriptUrl) {
|
|
159
|
+
const doc = getRoot().document;
|
|
160
|
+
if (!doc?.createElement) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (doc.querySelector?.(`script[data-cavuno-analytics="1"]`)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const el = doc.createElement("script");
|
|
167
|
+
el.defer = true;
|
|
168
|
+
el.src = scriptUrl;
|
|
169
|
+
el.setAttribute("data-cavuno-analytics", "1");
|
|
170
|
+
el.setAttribute("data-token", state.publishableKey);
|
|
171
|
+
el.setAttribute("data-host", state.collectUrl);
|
|
172
|
+
el.setAttribute("data-tenant-id", PENDING_TENANT_ID);
|
|
173
|
+
el.setAttribute("data-web-vitals", "true");
|
|
174
|
+
const head = doc.head;
|
|
175
|
+
if (head?.appendChild) {
|
|
176
|
+
head.appendChild(el);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const first = doc.getElementsByTagName("script")[0];
|
|
180
|
+
first?.parentNode?.insertBefore(el, first);
|
|
181
|
+
}
|
|
182
|
+
function install(options) {
|
|
183
|
+
const publishableKey = options.publishableKey.trim();
|
|
184
|
+
if (!publishableKey.startsWith("pk_")) {
|
|
185
|
+
throw new Error("analytics.install requires a publishable key (pk_\u2026)");
|
|
186
|
+
}
|
|
187
|
+
const collectUrl = normalizeCollectUrl(
|
|
188
|
+
(options.collectUrl ?? resolveDefaultCollectUrl()).trim() || resolveDefaultCollectUrl()
|
|
189
|
+
);
|
|
190
|
+
const scriptUrl = (options.scriptUrl ?? resolveDefaultScriptUrl()).trim() || resolveDefaultScriptUrl();
|
|
191
|
+
installed = { publishableKey, collectUrl };
|
|
192
|
+
exposeGlobal(installed);
|
|
193
|
+
injectScript(installed, scriptUrl);
|
|
194
|
+
}
|
|
195
|
+
function track(action, payload) {
|
|
196
|
+
const trimmed = action.trim();
|
|
197
|
+
if (!trimmed) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const root = getRoot();
|
|
201
|
+
const globalTrack = root[GLOBAL_NAME]?.trackEvent;
|
|
202
|
+
if (typeof globalTrack === "function" && installed) {
|
|
203
|
+
globalTrack(trimmed, payload);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!installed) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
postCollectJson(installed, trimmed, payload);
|
|
210
|
+
}
|
|
211
|
+
var analytics = {
|
|
212
|
+
install,
|
|
213
|
+
track,
|
|
214
|
+
matchAnalyticsWellKnown,
|
|
215
|
+
DEFAULT_COLLECT_URL,
|
|
216
|
+
DEFAULT_SCRIPT_URL,
|
|
217
|
+
PENDING_TENANT_ID,
|
|
218
|
+
WELL_KNOWN_ANALYTICS_SCRIPT_PATH,
|
|
219
|
+
WELL_KNOWN_SCRIPT_PATH,
|
|
220
|
+
WELL_KNOWN_COLLECT_PATH,
|
|
221
|
+
WELL_KNOWN_COLLECT_EVENTS_PATH
|
|
222
|
+
};
|