@businessdash/sdk 0.9.70 → 0.9.80

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,157 @@
1
+ /**
2
+ * `@businessdash/sdk/consent` — tracking consent, per org.
3
+ *
4
+ * ## The rule that shapes everything here
5
+ *
6
+ * Consent is given to a **controller**, and each org is a separate controller.
7
+ * It does not travel between them in either direction:
8
+ *
9
+ * - A visitor who refuses at org 2 has said nothing to org 1. Org 1 keeps
10
+ * collecting.
11
+ * - A visitor who consents at org 1 has NOT consented at org 2. Org 2 may not
12
+ * start collecting on the strength of it.
13
+ *
14
+ * The second half is the one that gets skipped, and it is the one with legal
15
+ * teeth: acting on another controller's consent is processing without a lawful
16
+ * basis.
17
+ *
18
+ * That is why the visitor key lives in a **first-party cookie on the org's own
19
+ * domain**, and why every call carries the org's own API key. Two sites cannot
20
+ * see each other's cookies, so the isolation is enforced by the browser rather
21
+ * than by us remembering to scope a query.
22
+ *
23
+ * ## Gate analytics on it
24
+ *
25
+ * ```ts
26
+ * const consent = await loadConsent({ client });
27
+ * if (consent.allows('analytics')) {
28
+ * initBiabAnalytics({ siteId, baseUrl, apiKey });
29
+ * }
30
+ * if (consent.mustAsk) showYourBanner(consent);
31
+ * ```
32
+ *
33
+ * `initBiabAnalytics` is deliberately NOT made to call this itself. A gate that
34
+ * fires implicitly is one nobody can see in review, and the decision of what
35
+ * happens before consent — render nothing, render a placeholder, collect
36
+ * essential-only — belongs to the site, not to us.
37
+ *
38
+ * @module
39
+ */
40
+ const VISITOR_COOKIE = "biab_visitor";
41
+ const VISITOR_HEADER = "x-biab-visitor";
42
+ /**
43
+ * The visitor's id for THIS site, minted on first need.
44
+ *
45
+ * A first-party cookie on the org's own domain, which is what makes the
46
+ * per-org isolation structural: the same person visiting two orgs gets two
47
+ * unrelated keys, because two domains cannot read each other's cookies. There
48
+ * is no cross-site identifier here and deliberately no way to add one.
49
+ *
50
+ * Returns null on the server, where there is no visitor to identify.
51
+ */
52
+ export function visitorKey() {
53
+ if (typeof document === "undefined")
54
+ return null;
55
+ const found = document.cookie
56
+ .split(";")
57
+ .map((part) => part.trim())
58
+ .find((part) => part.startsWith(`${VISITOR_COOKIE}=`));
59
+ if (found)
60
+ return decodeURIComponent(found.slice(VISITOR_COOKIE.length + 1));
61
+ const key = typeof crypto !== "undefined" && "randomUUID" in crypto
62
+ ? crypto.randomUUID()
63
+ : `v${Date.now()}${Math.floor(Math.random() * 1e9)}`;
64
+ // SameSite=Lax and no Secure flag would leak on http; Secure is set unless
65
+ // we are plainly on localhost, where it would stop the cookie working at all.
66
+ const secure = location.protocol === "https:" ? "; Secure" : "";
67
+ // biome-ignore lint/suspicious/noDocumentCookie: the suggested CookieStore
68
+ // API has no Safari or Firefox support. A consent banner has to work in
69
+ // every browser a visitor might arrive in — that is the entire point of it —
70
+ // so the universally supported API is the correct one here.
71
+ document.cookie = `${VISITOR_COOKIE}=${encodeURIComponent(key)}; Path=/; Max-Age=31536000; SameSite=Lax${secure}`;
72
+ return key;
73
+ }
74
+ /** True when the browser is signalling a global opt-out (DNT or GPC). */
75
+ export function browserOptOut() {
76
+ if (typeof navigator === "undefined")
77
+ return false;
78
+ const nav = navigator;
79
+ if (nav.globalPrivacyControl === true)
80
+ return true;
81
+ const dnt = nav.doNotTrack ??
82
+ (typeof window === "undefined"
83
+ ? undefined
84
+ : window.doNotTrack);
85
+ return dnt === "1" || dnt === "yes";
86
+ }
87
+ function toState(raw) {
88
+ const categories = (raw.categories ?? []);
89
+ const allowed = (raw.allowed ?? []);
90
+ return {
91
+ categories,
92
+ noticeText: raw.noticeText ?? null,
93
+ noticeVersion: raw.noticeVersion ?? 1,
94
+ allowed,
95
+ mustAsk: raw.mustAsk ?? true,
96
+ reason: (raw.reason ?? "default_deny"),
97
+ allows: (category) => allowed.includes(category),
98
+ };
99
+ }
100
+ /**
101
+ * What this visitor currently allows, at this org.
102
+ *
103
+ * On any failure this returns a **deny-everything** state rather than throwing.
104
+ * A consent gate that fails open collects data nobody agreed to; one that fails
105
+ * closed loses some analytics. Only one of those is a compliance incident.
106
+ */
107
+ export async function loadConsent(options) {
108
+ const key = visitorKey();
109
+ try {
110
+ const raw = (await options.client.request({
111
+ path: "consent",
112
+ ...(key ? { headers: { [VISITOR_HEADER]: key } } : {}),
113
+ responseSchema: { parse: (v) => v },
114
+ }));
115
+ return toState(raw);
116
+ }
117
+ catch {
118
+ return {
119
+ categories: [],
120
+ noticeText: null,
121
+ noticeVersion: 1,
122
+ allowed: [],
123
+ mustAsk: false,
124
+ reason: "default_deny",
125
+ allows: () => false,
126
+ };
127
+ }
128
+ }
129
+ /**
130
+ * Record the visitor's answer.
131
+ *
132
+ * `granted: []` is a real answer — "asked, refused everything" — and is stored
133
+ * as one, so the banner does not reappear on the next page.
134
+ */
135
+ export async function recordConsent(options) {
136
+ const key = visitorKey();
137
+ if (!key)
138
+ return { ok: false };
139
+ try {
140
+ await options.client.request({
141
+ method: "POST",
142
+ path: "consent",
143
+ body: {
144
+ granted: options.granted,
145
+ noticeVersion: options.noticeVersion,
146
+ source: options.source ?? "banner",
147
+ },
148
+ headers: { [VISITOR_HEADER]: key },
149
+ responseSchema: { parse: (v) => v },
150
+ });
151
+ return { ok: true };
152
+ }
153
+ catch {
154
+ return { ok: false };
155
+ }
156
+ }
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent-core/index.ts"],"names":[],"mappings":"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"}
@@ -3286,6 +3286,217 @@ export declare const customerPortalProfileSchema: z.ZodNullable<z.ZodObject<{
3286
3286
  profile: Record<string, unknown> | null;
3287
3287
  lastSeenAt: string | null;
3288
3288
  }>>;
3289
+ /**
3290
+ * One quiz outcome for the signed-in customer. `scoreWithheld` marks an
3291
+ * interview-mode quiz: the attempt exists, the verdict is staff-only —
3292
+ * render "completed" and nothing else.
3293
+ */
3294
+ export declare const customerPortalQuizResultSchema: z.ZodObject<{
3295
+ invitationId: z.ZodString;
3296
+ quizName: z.ZodString;
3297
+ competency: z.ZodNullable<z.ZodString>;
3298
+ status: z.ZodString;
3299
+ completedAt: z.ZodNullable<z.ZodString>;
3300
+ scoreWithheld: z.ZodBoolean;
3301
+ result: z.ZodNullable<z.ZodObject<{
3302
+ status: z.ZodString;
3303
+ scorePercent: z.ZodNullable<z.ZodNumber>;
3304
+ provisionalScorePercent: z.ZodNullable<z.ZodNumber>;
3305
+ passed: z.ZodNullable<z.ZodBoolean>;
3306
+ pointsEarned: z.ZodNullable<z.ZodNumber>;
3307
+ pointsPossible: z.ZodNullable<z.ZodNumber>;
3308
+ passingScore: z.ZodNumber;
3309
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
3310
+ submittedAt: z.ZodNullable<z.ZodString>;
3311
+ durationSeconds: z.ZodNullable<z.ZodNumber>;
3312
+ }, "strip", z.ZodTypeAny, {
3313
+ status: string;
3314
+ scorePercent: number | null;
3315
+ provisionalScorePercent: number | null;
3316
+ passed: boolean | null;
3317
+ pointsEarned: number | null;
3318
+ pointsPossible: number | null;
3319
+ passingScore: number;
3320
+ results: Record<string, unknown>[];
3321
+ submittedAt: string | null;
3322
+ durationSeconds: number | null;
3323
+ }, {
3324
+ status: string;
3325
+ scorePercent: number | null;
3326
+ provisionalScorePercent: number | null;
3327
+ passed: boolean | null;
3328
+ pointsEarned: number | null;
3329
+ pointsPossible: number | null;
3330
+ passingScore: number;
3331
+ results: Record<string, unknown>[];
3332
+ submittedAt: string | null;
3333
+ durationSeconds: number | null;
3334
+ }>>;
3335
+ }, "strip", z.ZodTypeAny, {
3336
+ status: string;
3337
+ result: {
3338
+ status: string;
3339
+ scorePercent: number | null;
3340
+ provisionalScorePercent: number | null;
3341
+ passed: boolean | null;
3342
+ pointsEarned: number | null;
3343
+ pointsPossible: number | null;
3344
+ passingScore: number;
3345
+ results: Record<string, unknown>[];
3346
+ submittedAt: string | null;
3347
+ durationSeconds: number | null;
3348
+ } | null;
3349
+ invitationId: string;
3350
+ quizName: string;
3351
+ competency: string | null;
3352
+ completedAt: string | null;
3353
+ scoreWithheld: boolean;
3354
+ }, {
3355
+ status: string;
3356
+ result: {
3357
+ status: string;
3358
+ scorePercent: number | null;
3359
+ provisionalScorePercent: number | null;
3360
+ passed: boolean | null;
3361
+ pointsEarned: number | null;
3362
+ pointsPossible: number | null;
3363
+ passingScore: number;
3364
+ results: Record<string, unknown>[];
3365
+ submittedAt: string | null;
3366
+ durationSeconds: number | null;
3367
+ } | null;
3368
+ invitationId: string;
3369
+ quizName: string;
3370
+ competency: string | null;
3371
+ completedAt: string | null;
3372
+ scoreWithheld: boolean;
3373
+ }>;
3374
+ export type CustomerPortalQuizResult = z.infer<typeof customerPortalQuizResultSchema>;
3375
+ export declare const customerPortalQuizResultsResponseSchema: z.ZodObject<{
3376
+ items: z.ZodArray<z.ZodObject<{
3377
+ invitationId: z.ZodString;
3378
+ quizName: z.ZodString;
3379
+ competency: z.ZodNullable<z.ZodString>;
3380
+ status: z.ZodString;
3381
+ completedAt: z.ZodNullable<z.ZodString>;
3382
+ scoreWithheld: z.ZodBoolean;
3383
+ result: z.ZodNullable<z.ZodObject<{
3384
+ status: z.ZodString;
3385
+ scorePercent: z.ZodNullable<z.ZodNumber>;
3386
+ provisionalScorePercent: z.ZodNullable<z.ZodNumber>;
3387
+ passed: z.ZodNullable<z.ZodBoolean>;
3388
+ pointsEarned: z.ZodNullable<z.ZodNumber>;
3389
+ pointsPossible: z.ZodNullable<z.ZodNumber>;
3390
+ passingScore: z.ZodNumber;
3391
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
3392
+ submittedAt: z.ZodNullable<z.ZodString>;
3393
+ durationSeconds: z.ZodNullable<z.ZodNumber>;
3394
+ }, "strip", z.ZodTypeAny, {
3395
+ status: string;
3396
+ scorePercent: number | null;
3397
+ provisionalScorePercent: number | null;
3398
+ passed: boolean | null;
3399
+ pointsEarned: number | null;
3400
+ pointsPossible: number | null;
3401
+ passingScore: number;
3402
+ results: Record<string, unknown>[];
3403
+ submittedAt: string | null;
3404
+ durationSeconds: number | null;
3405
+ }, {
3406
+ status: string;
3407
+ scorePercent: number | null;
3408
+ provisionalScorePercent: number | null;
3409
+ passed: boolean | null;
3410
+ pointsEarned: number | null;
3411
+ pointsPossible: number | null;
3412
+ passingScore: number;
3413
+ results: Record<string, unknown>[];
3414
+ submittedAt: string | null;
3415
+ durationSeconds: number | null;
3416
+ }>>;
3417
+ }, "strip", z.ZodTypeAny, {
3418
+ status: string;
3419
+ result: {
3420
+ status: string;
3421
+ scorePercent: number | null;
3422
+ provisionalScorePercent: number | null;
3423
+ passed: boolean | null;
3424
+ pointsEarned: number | null;
3425
+ pointsPossible: number | null;
3426
+ passingScore: number;
3427
+ results: Record<string, unknown>[];
3428
+ submittedAt: string | null;
3429
+ durationSeconds: number | null;
3430
+ } | null;
3431
+ invitationId: string;
3432
+ quizName: string;
3433
+ competency: string | null;
3434
+ completedAt: string | null;
3435
+ scoreWithheld: boolean;
3436
+ }, {
3437
+ status: string;
3438
+ result: {
3439
+ status: string;
3440
+ scorePercent: number | null;
3441
+ provisionalScorePercent: number | null;
3442
+ passed: boolean | null;
3443
+ pointsEarned: number | null;
3444
+ pointsPossible: number | null;
3445
+ passingScore: number;
3446
+ results: Record<string, unknown>[];
3447
+ submittedAt: string | null;
3448
+ durationSeconds: number | null;
3449
+ } | null;
3450
+ invitationId: string;
3451
+ quizName: string;
3452
+ competency: string | null;
3453
+ completedAt: string | null;
3454
+ scoreWithheld: boolean;
3455
+ }>, "many">;
3456
+ }, "strip", z.ZodTypeAny, {
3457
+ items: {
3458
+ status: string;
3459
+ result: {
3460
+ status: string;
3461
+ scorePercent: number | null;
3462
+ provisionalScorePercent: number | null;
3463
+ passed: boolean | null;
3464
+ pointsEarned: number | null;
3465
+ pointsPossible: number | null;
3466
+ passingScore: number;
3467
+ results: Record<string, unknown>[];
3468
+ submittedAt: string | null;
3469
+ durationSeconds: number | null;
3470
+ } | null;
3471
+ invitationId: string;
3472
+ quizName: string;
3473
+ competency: string | null;
3474
+ completedAt: string | null;
3475
+ scoreWithheld: boolean;
3476
+ }[];
3477
+ }, {
3478
+ items: {
3479
+ status: string;
3480
+ result: {
3481
+ status: string;
3482
+ scorePercent: number | null;
3483
+ provisionalScorePercent: number | null;
3484
+ passed: boolean | null;
3485
+ pointsEarned: number | null;
3486
+ pointsPossible: number | null;
3487
+ passingScore: number;
3488
+ results: Record<string, unknown>[];
3489
+ submittedAt: string | null;
3490
+ durationSeconds: number | null;
3491
+ } | null;
3492
+ invitationId: string;
3493
+ quizName: string;
3494
+ competency: string | null;
3495
+ completedAt: string | null;
3496
+ scoreWithheld: boolean;
3497
+ }[];
3498
+ }>;
3499
+ export type CustomerPortalQuizResultsResponse = z.infer<typeof customerPortalQuizResultsResponseSchema>;
3289
3500
  export declare const customerPortalUpdateProfileInputSchema: z.ZodObject<{
3290
3501
  displayName: z.ZodOptional<z.ZodString>;
3291
3502
  profile: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -7331,21 +7542,21 @@ export declare const customerReviewSummarySchema: z.ZodObject<{
7331
7542
  }, "strip", z.ZodTypeAny, {
7332
7543
  status: OpenEnum<"pending" | "published" | "hidden">;
7333
7544
  id: string;
7545
+ submittedAt: string;
7334
7546
  title: string | null;
7335
7547
  publishedAt: string | null;
7336
7548
  rating: number;
7337
7549
  body: string;
7338
7550
  jobId: string | null;
7339
- submittedAt: string;
7340
7551
  }, {
7341
7552
  status: OpenEnum<"pending" | "published" | "hidden">;
7342
7553
  id: string;
7554
+ submittedAt: string;
7343
7555
  title: string | null;
7344
7556
  publishedAt: string | null;
7345
7557
  rating: number;
7346
7558
  body: string;
7347
7559
  jobId: string | null;
7348
- submittedAt: string;
7349
7560
  }>;
7350
7561
  export declare const customerReviewListResponseSchema: z.ZodObject<{
7351
7562
  items: z.ZodArray<z.ZodObject<{
@@ -7361,43 +7572,43 @@ export declare const customerReviewListResponseSchema: z.ZodObject<{
7361
7572
  }, "strip", z.ZodTypeAny, {
7362
7573
  status: OpenEnum<"pending" | "published" | "hidden">;
7363
7574
  id: string;
7575
+ submittedAt: string;
7364
7576
  title: string | null;
7365
7577
  publishedAt: string | null;
7366
7578
  rating: number;
7367
7579
  body: string;
7368
7580
  jobId: string | null;
7369
- submittedAt: string;
7370
7581
  }, {
7371
7582
  status: OpenEnum<"pending" | "published" | "hidden">;
7372
7583
  id: string;
7584
+ submittedAt: string;
7373
7585
  title: string | null;
7374
7586
  publishedAt: string | null;
7375
7587
  rating: number;
7376
7588
  body: string;
7377
7589
  jobId: string | null;
7378
- submittedAt: string;
7379
7590
  }>, "many">;
7380
7591
  }, "strip", z.ZodTypeAny, {
7381
7592
  items: {
7382
7593
  status: OpenEnum<"pending" | "published" | "hidden">;
7383
7594
  id: string;
7595
+ submittedAt: string;
7384
7596
  title: string | null;
7385
7597
  publishedAt: string | null;
7386
7598
  rating: number;
7387
7599
  body: string;
7388
7600
  jobId: string | null;
7389
- submittedAt: string;
7390
7601
  }[];
7391
7602
  }, {
7392
7603
  items: {
7393
7604
  status: OpenEnum<"pending" | "published" | "hidden">;
7394
7605
  id: string;
7606
+ submittedAt: string;
7395
7607
  title: string | null;
7396
7608
  publishedAt: string | null;
7397
7609
  rating: number;
7398
7610
  body: string;
7399
7611
  jobId: string | null;
7400
- submittedAt: string;
7401
7612
  }[];
7402
7613
  }>;
7403
7614
  export declare const customerReviewSubmitInputSchema: z.ZodObject<{
@@ -7589,16 +7800,16 @@ export declare const customerJobEtaResponseSchema: z.ZodObject<{
7589
7800
  completedAt: z.ZodNullable<z.ZodString>;
7590
7801
  }, "strip", z.ZodTypeAny, {
7591
7802
  startedAt: string | null;
7803
+ completedAt: string | null;
7592
7804
  scheduledStartAt: string | null;
7593
7805
  enRouteAt: string | null;
7594
7806
  arrivedAt: string | null;
7595
- completedAt: string | null;
7596
7807
  }, {
7597
7808
  startedAt: string | null;
7809
+ completedAt: string | null;
7598
7810
  scheduledStartAt: string | null;
7599
7811
  enRouteAt: string | null;
7600
7812
  arrivedAt: string | null;
7601
- completedAt: string | null;
7602
7813
  }>;
7603
7814
  assignments: z.ZodArray<z.ZodObject<{
7604
7815
  displayName: z.ZodString;
@@ -7662,10 +7873,10 @@ export declare const customerJobEtaResponseSchema: z.ZodObject<{
7662
7873
  dispatchStatus: OpenEnum<"completed" | "en_route" | "unscheduled" | "scheduled" | "arrived" | "started">;
7663
7874
  timeline: {
7664
7875
  startedAt: string | null;
7876
+ completedAt: string | null;
7665
7877
  scheduledStartAt: string | null;
7666
7878
  enRouteAt: string | null;
7667
7879
  arrivedAt: string | null;
7668
- completedAt: string | null;
7669
7880
  };
7670
7881
  assignments: {
7671
7882
  role: string | null;
@@ -7688,10 +7899,10 @@ export declare const customerJobEtaResponseSchema: z.ZodObject<{
7688
7899
  dispatchStatus: OpenEnum<"completed" | "en_route" | "unscheduled" | "scheduled" | "arrived" | "started">;
7689
7900
  timeline: {
7690
7901
  startedAt: string | null;
7902
+ completedAt: string | null;
7691
7903
  scheduledStartAt: string | null;
7692
7904
  enRouteAt: string | null;
7693
7905
  arrivedAt: string | null;
7694
- completedAt: string | null;
7695
7906
  };
7696
7907
  assignments: {
7697
7908
  role: string | null;