@orakl/shared 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +662 -0
  2. package/dist/analytics.d.ts +21 -0
  3. package/dist/analytics.js +21 -0
  4. package/dist/avatar-helpers.d.ts +46 -0
  5. package/dist/avatar-helpers.js +69 -0
  6. package/dist/avatars.d.ts +6 -0
  7. package/dist/avatars.js +138 -0
  8. package/dist/badges.d.ts +126 -0
  9. package/dist/badges.js +667 -0
  10. package/dist/constants/auth.constants.d.ts +26 -0
  11. package/dist/constants/auth.constants.js +94 -0
  12. package/dist/constants/feature-flags.constants.d.ts +138 -0
  13. package/dist/constants/feature-flags.constants.js +119 -0
  14. package/dist/constants/font.constants.d.ts +9 -0
  15. package/dist/constants/font.constants.js +14 -0
  16. package/dist/constants/lobby.constants.d.ts +2 -0
  17. package/dist/constants/lobby.constants.js +438 -0
  18. package/dist/constants/patterns.constants.d.ts +4 -0
  19. package/dist/constants/patterns.constants.js +4 -0
  20. package/dist/constants/theme.constants.d.ts +24 -0
  21. package/dist/constants/theme.constants.js +69 -0
  22. package/dist/env.d.ts +3 -0
  23. package/dist/env.js +5 -0
  24. package/dist/font.d.ts +4 -0
  25. package/dist/font.js +8 -0
  26. package/dist/history-range.d.ts +13 -0
  27. package/dist/history-range.js +29 -0
  28. package/dist/index.d.ts +23 -0
  29. package/dist/index.js +26 -0
  30. package/dist/lobby.d.ts +16 -0
  31. package/dist/lobby.js +50 -0
  32. package/dist/manage-questions.d.ts +52 -0
  33. package/dist/manage-questions.js +26 -0
  34. package/dist/math.d.ts +27 -0
  35. package/dist/math.js +30 -0
  36. package/dist/murmurhash.d.ts +9 -0
  37. package/dist/murmurhash.js +52 -0
  38. package/dist/pricing.d.ts +32 -0
  39. package/dist/pricing.js +32 -0
  40. package/dist/profile-tabs.d.ts +8 -0
  41. package/dist/profile-tabs.js +18 -0
  42. package/dist/quiz-routes.d.ts +28 -0
  43. package/dist/quiz-routes.js +65 -0
  44. package/dist/sanitize.d.ts +38 -0
  45. package/dist/sanitize.js +109 -0
  46. package/dist/schemas/question.schema.d.ts +1 -0
  47. package/dist/schemas/question.schema.js +1 -0
  48. package/dist/solo-entitlements.d.ts +28 -0
  49. package/dist/solo-entitlements.js +56 -0
  50. package/dist/theme.d.ts +39 -0
  51. package/dist/theme.js +139 -0
  52. package/dist/types/auth.d.ts +143 -0
  53. package/dist/types/auth.js +28 -0
  54. package/dist/utils.d.ts +50 -0
  55. package/dist/utils.js +133 -0
  56. package/package.json +45 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Analytics posture (map #840, decision #12): one self-hosted Umami script,
3
+ * served only when the visitor has not turned analytics off and their browser
4
+ * sends no privacy signal. The server decides — an opted-out page carries no
5
+ * measurement script at all.
6
+ */
7
+ /** First-party opt-out cookie; value `off` means no script. */
8
+ export declare const ANALYTICS_COOKIE = "orakl-analytics";
9
+ export declare const ANALYTICS_OFF = "off";
10
+ /** Placeholder in `app.html` that the HTML hook replaces. */
11
+ export declare const ANALYTICS_MARKER = "<meta name=\"orakl-analytics-slot\" />";
12
+ export declare const UMAMI_SCRIPT_TAG = "<script async defer src=\"https://visitors.nhg.app/vues\" data-website-id=\"3d102361-df16-457f-b384-3e22a7dc56fb\"></script>";
13
+ export interface AnalyticsState {
14
+ /** True when the script is served. */
15
+ enabled: boolean;
16
+ /** True when the browser sent Global Privacy Control or Do Not Track. */
17
+ browserSignal: boolean;
18
+ }
19
+ export declare function analyticsState(request: Request, cookie: string | undefined): AnalyticsState;
20
+ /** Replace the marker with the script tag, or with nothing. */
21
+ export declare function renderAnalytics(html: string, state: AnalyticsState): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Analytics posture (map #840, decision #12): one self-hosted Umami script,
3
+ * served only when the visitor has not turned analytics off and their browser
4
+ * sends no privacy signal. The server decides — an opted-out page carries no
5
+ * measurement script at all.
6
+ */
7
+ /** First-party opt-out cookie; value `off` means no script. */
8
+ export const ANALYTICS_COOKIE = "orakl-analytics";
9
+ export const ANALYTICS_OFF = "off";
10
+ /** Placeholder in `app.html` that the HTML hook replaces. */
11
+ export const ANALYTICS_MARKER = '<meta name="orakl-analytics-slot" />';
12
+ export const UMAMI_SCRIPT_TAG = '<script async defer src="https://visitors.nhg.app/vues" data-website-id="3d102361-df16-457f-b384-3e22a7dc56fb"></script>';
13
+ export function analyticsState(request, cookie) {
14
+ const browserSignal = request.headers.get("Sec-GPC") === "1" ||
15
+ request.headers.get("DNT") === "1";
16
+ return { enabled: !browserSignal && cookie !== ANALYTICS_OFF, browserSignal };
17
+ }
18
+ /** Replace the marker with the script tag, or with nothing. */
19
+ export function renderAnalytics(html, state) {
20
+ return html.replace(ANALYTICS_MARKER, state.enabled ? UMAMI_SCRIPT_TAG : "");
21
+ }
@@ -0,0 +1,46 @@
1
+ export interface DbAvatar {
2
+ id: string;
3
+ title: string;
4
+ description: string;
5
+ category: string;
6
+ src: string;
7
+ }
8
+ export interface AvatarGroup {
9
+ category: string;
10
+ items: {
11
+ id: string;
12
+ title: string;
13
+ description: string;
14
+ src: string;
15
+ }[];
16
+ }
17
+ /** Build categorised avatar groups from DB avatar list. */
18
+ export declare function buildAvatarGroups(dbAvatars: DbAvatar[]): AvatarGroup[];
19
+ /** Resolve avatar src from DB list, falling back to static bundled avatars. */
20
+ export declare function getAvatarSrc(avatarId: string, dbAvatars: DbAvatar[]): string;
21
+ /** Default avatar description when none is provided. */
22
+ export declare const DEFAULT_AVATAR_DESC = "There's no description for this avatar yet.";
23
+ export interface PoolAvatar {
24
+ id: string;
25
+ title: string;
26
+ description: string;
27
+ src: string;
28
+ }
29
+ /** Merge DB avatars over the static bundled list, DB rows winning by id. */
30
+ export declare function avatarPool(dbAvatars: DbAvatar[], staticAvatarList?: {
31
+ id: string;
32
+ name: string;
33
+ src: string;
34
+ }[]): PoolAvatar[];
35
+ /** Pick a uniformly random avatar from a pool. */
36
+ export declare function randomAvatar<T>(pool: T[], rng?: () => number): T;
37
+ /** Resolve a shown description, falling back to the default when empty. */
38
+ export declare function avatarDescription(desc: string | undefined): string;
39
+ /** Candidates for rallying: grouped (rendered) avatars when loaded, else the full pool. */
40
+ export declare function rallyCandidates(groups: AvatarGroup[], dbAvatars: DbAvatar[], staticAvatarList?: {
41
+ id: string;
42
+ name: string;
43
+ src: string;
44
+ }[]): PoolAvatar[];
45
+ /** Whether setup should auto-pick an avatar: no selection yet, but a pool exists. */
46
+ export declare function shouldAutoRally(selectedId: string, poolLength: number): boolean;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Avatar selection helpers for the join-quiz flow.
3
+ * Pure functions — no Alpine state dependencies.
4
+ */
5
+ import { avatars as staticAvatars } from "./avatars.js";
6
+ /** Build categorised avatar groups from DB avatar list. */
7
+ export function buildAvatarGroups(dbAvatars) {
8
+ const grouped = new Map();
9
+ for (const av of dbAvatars) {
10
+ const cat = av.category || "";
11
+ if (!grouped.has(cat))
12
+ grouped.set(cat, []);
13
+ grouped.get(cat)?.push(av);
14
+ }
15
+ const cats = [...grouped.keys()].filter(Boolean).sort();
16
+ return [
17
+ ...cats.map((c) => ({ category: c, items: grouped.get(c) ?? [] })),
18
+ ...(grouped.has("")
19
+ ? [{ category: "", items: grouped.get("") ?? [] }]
20
+ : []),
21
+ ];
22
+ }
23
+ /** Resolve avatar src from DB list, falling back to static bundled avatars. */
24
+ export function getAvatarSrc(avatarId, dbAvatars) {
25
+ const dbMatch = dbAvatars.find((a) => a.id === avatarId);
26
+ if (dbMatch)
27
+ return dbMatch.src;
28
+ return staticAvatars.find((a) => a.id === avatarId)?.src ?? "";
29
+ }
30
+ /** Default avatar description when none is provided. */
31
+ export const DEFAULT_AVATAR_DESC = "There's no description for this avatar yet.";
32
+ /** Merge DB avatars over the static bundled list, DB rows winning by id. */
33
+ export function avatarPool(dbAvatars, staticAvatarList = staticAvatars) {
34
+ const dbIds = new Set(dbAvatars.map((a) => a.id));
35
+ const fromStatic = staticAvatarList
36
+ .filter((a) => !dbIds.has(a.id))
37
+ .map((a) => ({ id: a.id, title: a.name, description: "", src: a.src }));
38
+ const fromDb = dbAvatars.map((a) => ({
39
+ id: a.id,
40
+ title: a.title,
41
+ description: a.description,
42
+ src: a.src,
43
+ }));
44
+ return [...fromDb, ...fromStatic];
45
+ }
46
+ /** Pick a uniformly random avatar from a pool. */
47
+ export function randomAvatar(pool, rng = Math.random) {
48
+ return pool[Math.floor(rng() * pool.length)];
49
+ }
50
+ /** Resolve a shown description, falling back to the default when empty. */
51
+ export function avatarDescription(desc) {
52
+ return desc || DEFAULT_AVATAR_DESC;
53
+ }
54
+ /** Candidates for rallying: grouped (rendered) avatars when loaded, else the full pool. */
55
+ export function rallyCandidates(groups, dbAvatars, staticAvatarList = staticAvatars) {
56
+ const flattened = groups.flatMap((g) => g.items.map((av) => ({
57
+ id: av.id,
58
+ title: av.title,
59
+ description: av.description,
60
+ src: av.src,
61
+ })));
62
+ return flattened.length > 0
63
+ ? flattened
64
+ : avatarPool(dbAvatars, staticAvatarList);
65
+ }
66
+ /** Whether setup should auto-pick an avatar: no selection yet, but a pool exists. */
67
+ export function shouldAutoRally(selectedId, poolLength) {
68
+ return !selectedId && poolLength > 0;
69
+ }
@@ -0,0 +1,6 @@
1
+ export interface Avatar {
2
+ id: string;
3
+ name: string;
4
+ src: string;
5
+ }
6
+ export declare const avatars: Avatar[];