@iann29/rastro 0.4.0 → 0.6.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 (112) hide show
  1. package/README.md +146 -24
  2. package/agent/integration.md +66 -29
  3. package/agent/manifest.json +19 -8
  4. package/agent/manifest.schema.json +28 -9
  5. package/dist/client/federation.d.ts +38 -8
  6. package/dist/client/federation.d.ts.map +1 -1
  7. package/dist/client/federation.js +18 -1
  8. package/dist/client/federation.js.map +1 -1
  9. package/dist/client/identity.d.ts +11 -0
  10. package/dist/client/identity.d.ts.map +1 -0
  11. package/dist/client/identity.js +123 -0
  12. package/dist/client/identity.js.map +1 -0
  13. package/dist/client/index.d.ts +501 -11
  14. package/dist/client/index.d.ts.map +1 -1
  15. package/dist/client/index.js +220 -4
  16. package/dist/client/index.js.map +1 -1
  17. package/dist/component/_generated/api.d.ts +8 -0
  18. package/dist/component/_generated/api.d.ts.map +1 -1
  19. package/dist/component/_generated/api.js.map +1 -1
  20. package/dist/component/_generated/component.d.ts +158 -1
  21. package/dist/component/_generated/component.d.ts.map +1 -1
  22. package/dist/component/constants.d.ts +2 -0
  23. package/dist/component/constants.d.ts.map +1 -1
  24. package/dist/component/constants.js +7 -0
  25. package/dist/component/constants.js.map +1 -1
  26. package/dist/component/coverage.d.ts +1 -0
  27. package/dist/component/coverage.d.ts.map +1 -1
  28. package/dist/component/coverage.js +6 -1
  29. package/dist/component/coverage.js.map +1 -1
  30. package/dist/component/eventStore.d.ts +2 -0
  31. package/dist/component/eventStore.d.ts.map +1 -1
  32. package/dist/component/http.d.ts.map +1 -1
  33. package/dist/component/http.js +51 -1
  34. package/dist/component/http.js.map +1 -1
  35. package/dist/component/ingest.d.ts +2 -0
  36. package/dist/component/ingest.d.ts.map +1 -1
  37. package/dist/component/ingest.js +72 -22
  38. package/dist/component/ingest.js.map +1 -1
  39. package/dist/component/origin.d.ts +70 -0
  40. package/dist/component/origin.d.ts.map +1 -0
  41. package/dist/component/origin.js +230 -0
  42. package/dist/component/origin.js.map +1 -0
  43. package/dist/component/people.d.ts +79 -0
  44. package/dist/component/people.d.ts.map +1 -0
  45. package/dist/component/people.js +249 -0
  46. package/dist/component/people.js.map +1 -0
  47. package/dist/component/platforms.d.ts +33 -0
  48. package/dist/component/platforms.d.ts.map +1 -0
  49. package/dist/component/platforms.js +328 -0
  50. package/dist/component/platforms.js.map +1 -0
  51. package/dist/component/reports.d.ts +35 -74
  52. package/dist/component/reports.d.ts.map +1 -1
  53. package/dist/component/reports.js +59 -14
  54. package/dist/component/reports.js.map +1 -1
  55. package/dist/component/sanitize.d.ts +5 -0
  56. package/dist/component/sanitize.d.ts.map +1 -1
  57. package/dist/component/sanitize.js +33 -1
  58. package/dist/component/sanitize.js.map +1 -1
  59. package/dist/component/schema.d.ts +101 -7
  60. package/dist/component/schema.js +45 -1
  61. package/dist/component/schema.js.map +1 -1
  62. package/dist/component/trackedLinks.d.ts +91 -0
  63. package/dist/component/trackedLinks.d.ts.map +1 -0
  64. package/dist/component/trackedLinks.js +314 -0
  65. package/dist/component/trackedLinks.js.map +1 -0
  66. package/dist/component/useragent.d.ts +6 -0
  67. package/dist/component/useragent.d.ts.map +1 -1
  68. package/dist/component/useragent.js +9 -0
  69. package/dist/component/useragent.js.map +1 -1
  70. package/dist/component/validators.d.ts +101 -11
  71. package/dist/component/validators.d.ts.map +1 -1
  72. package/dist/component/validators.js +62 -1
  73. package/dist/component/validators.js.map +1 -1
  74. package/dist/component/visitors.d.ts +38 -2
  75. package/dist/component/visitors.d.ts.map +1 -1
  76. package/dist/component/visitors.js +162 -42
  77. package/dist/component/visitors.js.map +1 -1
  78. package/dist/react/index.d.ts +9 -5
  79. package/dist/react/index.d.ts.map +1 -1
  80. package/dist/react/index.js +36 -5
  81. package/dist/react/index.js.map +1 -1
  82. package/dist/tracker/generated.d.ts +6 -6
  83. package/dist/tracker/generated.d.ts.map +1 -1
  84. package/dist/tracker/generated.js +6 -6
  85. package/dist/tracker/generated.js.map +1 -1
  86. package/dist/tracker/tracker.d.ts +3 -1
  87. package/dist/tracker/tracker.d.ts.map +1 -1
  88. package/dist/tracker/tracker.js +56 -5
  89. package/dist/tracker/tracker.js.map +1 -1
  90. package/dist/tracker.min.js +1 -1
  91. package/docs/federation.md +24 -0
  92. package/docs/identity.md +307 -0
  93. package/docs/upgrading.md +190 -19
  94. package/llms.txt +11 -2
  95. package/package.json +5 -3
  96. package/src/component/_generated/api.ts +8 -0
  97. package/src/component/_generated/component.ts +251 -1
  98. package/src/component/constants.ts +7 -0
  99. package/src/component/coverage.ts +7 -1
  100. package/src/component/http.ts +56 -1
  101. package/src/component/ingest.ts +100 -28
  102. package/src/component/origin.ts +273 -0
  103. package/src/component/people.ts +321 -0
  104. package/src/component/platforms.ts +359 -0
  105. package/src/component/reports.ts +84 -13
  106. package/src/component/sanitize.ts +39 -1
  107. package/src/component/schema.ts +53 -0
  108. package/src/component/trackedLinks.ts +384 -0
  109. package/src/component/useragent.ts +11 -0
  110. package/src/component/validators.ts +120 -0
  111. package/src/component/visitors.ts +232 -55
  112. package/src/tracker/generated.ts +6 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../../src/component/origin.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhF;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA8DF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAkBzD;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,SAAS,CAGlE;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG5D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB,CAUA"}
@@ -0,0 +1,230 @@
1
+ import { CLICK_IDS, PLATFORMS } from "./platforms.js";
2
+ import { channelValidator } from "./validators.js";
3
+ const MAX_CLICK_IDS = 8;
4
+ const DIRECT = {
5
+ platform: "none",
6
+ channel: "direct",
7
+ evidence: "none",
8
+ };
9
+ const UNKNOWN_REFERRAL = { platform: "unknown", channel: "referral" };
10
+ // Surfaces that add `fbclid` to their outbound links: a referrer or in-app
11
+ // browser naming one of them says which Meta app the click came from.
12
+ const META_FAMILY = new Set(["facebook", "instagram", "messenger", "threads"]);
13
+ const CHANNELS = new Set(channelValidator.members.map((member) => member.value));
14
+ const BY_ID = new Map(PLATFORMS.map((row) => [row.id, row]));
15
+ const BY_HOST = new Map();
16
+ const BY_ALIAS = new Map();
17
+ for (const row of PLATFORMS) {
18
+ const paid = { row, channel: paidChannel(row.channel) ?? row.channel };
19
+ for (const host of [...(row.domains ?? []), ...(row.packages ?? [])]) {
20
+ BY_HOST.set(host, { row, channel: row.channel });
21
+ }
22
+ for (const host of row.paidDomains ?? [])
23
+ BY_HOST.set(host, paid);
24
+ for (const alias of [row.id, ...(row.aliases ?? [])]) {
25
+ BY_ALIAS.set(alias, { row, channel: row.channel });
26
+ }
27
+ for (const alias of row.paidAliases ?? [])
28
+ BY_ALIAS.set(alias, paid);
29
+ }
30
+ const CLICK_ID_RANK = new Map(CLICK_IDS.map(([key], rank) => [key, rank]));
31
+ // utm_medium spellings besides the channel ids, which are accepted as they
32
+ // are (tracked links send them). "paid" and "organic" follow the platform:
33
+ // search or social, whichever it is. A Map, so no utm_medium can reach
34
+ // Object.prototype.
35
+ const MEDIUMS = new Map([
36
+ ["cpc", "paid"],
37
+ ["ppc", "paid"],
38
+ ["paid", "paid"],
39
+ ["paidsearch", "paid_search"],
40
+ ["sem", "paid_search"],
41
+ ["paidsocial", "paid_social"],
42
+ ["social", "organic_social"],
43
+ ["social-media", "organic_social"],
44
+ ["social_media", "organic_social"],
45
+ ["e-mail", "email"],
46
+ ["newsletter", "email"],
47
+ ["mail", "email"],
48
+ ["affiliates", "affiliate"],
49
+ ["partner", "affiliate"],
50
+ ["banner", "display"],
51
+ ["video", "display"],
52
+ ["cpm", "display"],
53
+ ["whatsapp", "messaging"],
54
+ ["sms", "messaging"],
55
+ ["print", "offline"],
56
+ ["qr", "offline"],
57
+ ["organic", "organic"],
58
+ ]);
59
+ /**
60
+ * Classifies a session's first touch. One rule decides and the next only
61
+ * runs when it did not: utm_source, then click IDs, then the referrer host,
62
+ * then the in-app browser; an affiliate `ref` then sets the channel, keeping
63
+ * the platform and evidence an earlier rule found. Nothing at all is direct,
64
+ * the only way to be direct. Matching is exact, never by substring.
65
+ */
66
+ export function classifyOrigin(input) {
67
+ const referred = referrerPlatform(input.referrer);
68
+ const inApp = input.inApp === undefined ? undefined : BY_ID.get(input.inApp);
69
+ const origin = fromUtm(input.utmSource, input.utmMedium) ??
70
+ fromClickIds(input.clickIds, referred?.platform, inApp?.id) ??
71
+ (referred && { ...referred, evidence: "referrer" }) ??
72
+ (inApp && {
73
+ platform: inApp.id,
74
+ channel: inApp.channel,
75
+ evidence: "inApp",
76
+ });
77
+ if (input.affiliate) {
78
+ return origin
79
+ ? { ...origin, channel: "affiliate" }
80
+ : { platform: "none", channel: "affiliate", evidence: "affiliate" };
81
+ }
82
+ return origin ?? DIRECT;
83
+ }
84
+ /** The known click-ID keys of a list, deduplicated and bounded. */
85
+ export function knownClickIds(keys) {
86
+ const known = [...new Set(keys.filter((key) => CLICK_ID_RANK.has(key)))];
87
+ return known.length > 0 ? known.slice(0, MAX_CLICK_IDS) : undefined;
88
+ }
89
+ export function isPlatform(id) {
90
+ return BY_ID.has(id);
91
+ }
92
+ /**
93
+ * The platform behind a stored `source` — a `utm_source` alias, a referrer
94
+ * host, or an Android package, as sessions and the overview keep them.
95
+ * `unknown` when the table has no row for it, `none` when the session had no
96
+ * source at all. Matching is `classifyOrigin`'s, never by substring, so a
97
+ * dashboard can group raw hosts the same way the classifier does.
98
+ */
99
+ export function platformForSource(source) {
100
+ const value = source.trim().toLowerCase();
101
+ if (!value || value === "direct" || value === "internal")
102
+ return "none";
103
+ return (BY_ALIAS.get(value) ?? hostHit(value))?.row.id ?? "unknown";
104
+ }
105
+ /**
106
+ * Where a platform's traffic lands when nothing says paid or organic:
107
+ * `direct` for `none`, `referral` for an id outside the table.
108
+ */
109
+ export function channelForPlatform(platform) {
110
+ if (platform === "none")
111
+ return DIRECT.channel;
112
+ return BY_ID.get(platform)?.channel ?? UNKNOWN_REFERRAL.channel;
113
+ }
114
+ /**
115
+ * The value the `platform` aggregate dimension carries. A session that
116
+ * arrived by its platform's default channel keeps the bare id; one that
117
+ * deviated carries `platform@channel` (`google@paid_search` for a `gclid`,
118
+ * `unknown@email` for a newsletter nobody's table knows), so the aggregates
119
+ * hold the pair without a dimension of their own. `none` is always `none`:
120
+ * a session with no platform is the direct row, whatever its channel.
121
+ */
122
+ export function platformDimensionValue(origin) {
123
+ if (origin.platform === "none")
124
+ return "none";
125
+ return origin.channel === channelForPlatform(origin.platform)
126
+ ? origin.platform
127
+ : `${origin.platform}@${origin.channel}`;
128
+ }
129
+ /**
130
+ * The inverse: the platform and the channel behind a `platform` dimension
131
+ * value. A bare id — every session recorded before the pair was encoded
132
+ * included — reads as that platform's default channel, which is what it was
133
+ * classified with.
134
+ */
135
+ export function splitPlatformDimension(value) {
136
+ const at = value.indexOf("@");
137
+ const platform = at < 0 ? value : value.slice(0, at);
138
+ const channel = at < 0 ? "" : value.slice(at + 1);
139
+ return {
140
+ platform,
141
+ channel: CHANNELS.has(channel)
142
+ ? channel
143
+ : channelForPlatform(platform),
144
+ };
145
+ }
146
+ function fromUtm(utmSource, utmMedium) {
147
+ const source = utmSource?.trim().toLowerCase();
148
+ if (!source)
149
+ return undefined;
150
+ // A host-shaped value (utm_source=chatgpt.com) matches like a referrer.
151
+ const hit = BY_ALIAS.get(source) ?? (source.includes(".") ? hostHit(source) : null);
152
+ const declared = hit?.channel ?? UNKNOWN_REFERRAL.channel;
153
+ const mediumKey = utmMedium?.trim().toLowerCase() ?? "";
154
+ const medium = CHANNELS.has(mediumKey)
155
+ ? mediumKey
156
+ : MEDIUMS.get(mediumKey);
157
+ const channel = medium === "paid"
158
+ ? (paidChannel(hit?.row.channel) ?? declared)
159
+ : medium === "organic"
160
+ ? (hit?.row.channel ?? declared)
161
+ : (medium ?? declared);
162
+ return {
163
+ platform: hit?.row.id ?? UNKNOWN_REFERRAL.platform,
164
+ channel,
165
+ evidence: "utm",
166
+ };
167
+ }
168
+ function fromClickIds(keys, referred, inApp) {
169
+ let best;
170
+ for (const key of keys ?? []) {
171
+ const rank = CLICK_ID_RANK.get(key);
172
+ if (rank !== undefined && (!best || rank < CLICK_ID_RANK.get(best[0]))) {
173
+ best = CLICK_IDS[rank];
174
+ }
175
+ }
176
+ if (!best)
177
+ return undefined;
178
+ const [, platform, channel] = best;
179
+ // The click ID names who generated the link. A Meta id defers to the Meta
180
+ // app the referrer or the in-app browser names, channel included (a
181
+ // Messenger link is messaging); an e-mail id takes the referrer's platform.
182
+ if (platform === "meta") {
183
+ const app = [referred, inApp].find((id) => id && META_FAMILY.has(id));
184
+ const row = app === undefined ? undefined : BY_ID.get(app);
185
+ return row
186
+ ? { platform: row.id, channel: row.channel, evidence: "clickId" }
187
+ : { platform, channel, evidence: "clickId" };
188
+ }
189
+ const resolved = platform === "unknown" && referred && referred !== "unknown"
190
+ ? referred
191
+ : platform;
192
+ return { platform: resolved, channel, evidence: "clickId" };
193
+ }
194
+ function referrerPlatform(referrer) {
195
+ if (!referrer)
196
+ return undefined;
197
+ let host;
198
+ try {
199
+ host = new URL(referrer).hostname.toLowerCase().replace(/\.$/, "");
200
+ }
201
+ catch {
202
+ return undefined;
203
+ }
204
+ if (!host)
205
+ return undefined;
206
+ const hit = hostHit(host);
207
+ return hit
208
+ ? { platform: hit.row.id, channel: hit.channel }
209
+ : UNKNOWN_REFERRAL;
210
+ }
211
+ /** The longest listed suffix of a host (a whole label at a time), or null. */
212
+ function hostHit(host) {
213
+ for (let suffix = host; suffix;) {
214
+ const hit = BY_HOST.get(suffix);
215
+ if (hit)
216
+ return hit;
217
+ const dot = suffix.indexOf(".");
218
+ suffix = dot < 0 ? "" : suffix.slice(dot + 1);
219
+ }
220
+ const row = PLATFORMS.find((candidate) => candidate.hostPattern?.test(host));
221
+ return row ? { row, channel: row.channel } : null;
222
+ }
223
+ function paidChannel(channel) {
224
+ if (channel === "organic_search")
225
+ return "paid_search";
226
+ if (channel === "organic_social")
227
+ return "paid_social";
228
+ return undefined;
229
+ }
230
+ //# sourceMappingURL=origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/component/origin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { type MutationCtx } from "./_generated/server.js";
2
+ import type { Doc, Id } from "./_generated/dataModel.js";
3
+ import { type PersonAttributes } from "./validators.js";
4
+ export declare const personDocumentValidator: import("convex/values").VObject<{
5
+ _creationTime: number;
6
+ _id: import("convex/values").GenericId<"visitorProfiles">;
7
+ attributes?: Record<string, string | number | boolean> | undefined;
8
+ email?: string | undefined;
9
+ identifiedAt: number;
10
+ name?: string | undefined;
11
+ siteId: import("convex/values").GenericId<"sites">;
12
+ updatedAt: number;
13
+ visitorId: string;
14
+ }, {
15
+ visitorId: import("convex/values").VString<string, "required">;
16
+ name: import("convex/values").VString<string | undefined, "optional">;
17
+ email: import("convex/values").VString<string | undefined, "optional">;
18
+ attributes: import("convex/values").VRecord<Record<string, string | number | boolean> | undefined, import("convex/values").VString<string, "required">, import("convex/values").VUnion<string | number | boolean, [import("convex/values").VString<string, "required">, import("convex/values").VFloat64<number, "required">, import("convex/values").VBoolean<boolean, "required">], "required", never>, "optional", string>;
19
+ identifiedAt: import("convex/values").VFloat64<number, "required">;
20
+ updatedAt: import("convex/values").VFloat64<number, "required">;
21
+ _id: import("convex/values").VId<import("convex/values").GenericId<"visitorProfiles">, "required">;
22
+ _creationTime: import("convex/values").VFloat64<number, "required">;
23
+ siteId: import("convex/values").VId<import("convex/values").GenericId<"sites">, "required">;
24
+ }, "required", "_creationTime" | "_id" | "attributes" | "email" | "identifiedAt" | "name" | "siteId" | "updatedAt" | "visitorId" | `attributes.${string}`>;
25
+ export declare function profileSearchFields(name?: string, email?: string): {
26
+ searchName: string;
27
+ searchEmail: string;
28
+ };
29
+ export declare function mergePersonAttributes(previous?: PersonAttributes, patch?: Record<string, string | number | boolean | null>): PersonAttributes;
30
+ /** Index each current attribute once; ordinary reads never scan all profiles. */
31
+ export declare function syncPersonAttributes(ctx: MutationCtx, siteId: Id<"sites">, visitorId: string, attributes: PersonAttributes): Promise<void>;
32
+ export declare function publicPerson(row: Doc<"visitorProfiles">): {
33
+ _creationTime: number;
34
+ _id: import("convex/values").GenericId<"visitorProfiles">;
35
+ attributes?: Record<string, string | number | boolean> | undefined;
36
+ email?: string | undefined;
37
+ identifiedAt: number;
38
+ name?: string | undefined;
39
+ siteId: import("convex/values").GenericId<"sites">;
40
+ updatedAt: number;
41
+ visitorId: string;
42
+ };
43
+ /** An optional, bounded upgrade for profiles created before the directory. */
44
+ export declare const backfillSearch: import("convex/server").RegisteredMutation<"public", {
45
+ siteId: import("convex/values").GenericId<"sites">;
46
+ }, Promise<{
47
+ updated: number;
48
+ isDone: boolean;
49
+ }>>;
50
+ export declare const list: import("convex/server").RegisteredQuery<"public", {
51
+ filter?: {
52
+ field: "email" | "name" | "visitorId";
53
+ prefix: string;
54
+ } | {
55
+ field: "attribute";
56
+ key: string;
57
+ value: string | number | boolean;
58
+ } | undefined;
59
+ paginationOpts: {
60
+ cursor: string | null;
61
+ numItems: number;
62
+ };
63
+ siteId: import("convex/values").GenericId<"sites">;
64
+ }, Promise<{
65
+ page: {
66
+ _creationTime: number;
67
+ _id: import("convex/values").GenericId<"visitorProfiles">;
68
+ attributes?: Record<string, string | number | boolean> | undefined;
69
+ email?: string | undefined;
70
+ identifiedAt: number;
71
+ name?: string | undefined;
72
+ siteId: import("convex/values").GenericId<"sites">;
73
+ updatedAt: number;
74
+ visitorId: string;
75
+ }[];
76
+ isDone: boolean;
77
+ continueCursor: string;
78
+ }>>;
79
+ //# sourceMappingURL=people.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"people.d.ts","sourceRoot":"","sources":["../../src/component/people.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAEzB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;0JAIlC,CAAC;AAEH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAE7D,UAAU;IACV,WAAW;EAEd;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,gBAAqB,EAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,GACvD,gBAAgB,CAqBlB;AAwBD,iFAAiF;AACjF,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,gBAAgB,iBA2B7B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,iBAAiB,CAAC;;;;;;;;;;EAGvD;AAED,8EAA8E;AAC9E,eAAO,MAAM,cAAc;;;;;GA2BzB,CAAC;AAgCH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiJf,CAAC"}
@@ -0,0 +1,249 @@
1
+ import { v } from "convex/values";
2
+ import { query, mutation } from "./_generated/server.js";
3
+ import { fail } from "./errors.js";
4
+ import { personFilterValidator, visitorProfileValidator, } from "./validators.js";
5
+ export const personDocumentValidator = visitorProfileValidator.extend({
6
+ _id: v.id("visitorProfiles"),
7
+ _creationTime: v.number(),
8
+ siteId: v.id("sites"),
9
+ });
10
+ export function profileSearchFields(name, email) {
11
+ return {
12
+ searchName: normalizeSearch(name ?? ""),
13
+ searchEmail: normalizeSearch(email ?? ""),
14
+ };
15
+ }
16
+ function normalizeSearch(value) {
17
+ return value.normalize("NFKC").trim().toLowerCase();
18
+ }
19
+ export function mergePersonAttributes(previous = {}, patch) {
20
+ const attributes = { ...previous };
21
+ for (const [key, value] of Object.entries(patch ?? {})) {
22
+ validateAttributeKey(key);
23
+ if (value === null)
24
+ delete attributes[key];
25
+ else {
26
+ validateAttributeValue(value);
27
+ attributes[key] = value;
28
+ }
29
+ }
30
+ if (Object.keys(attributes).length > 32 ||
31
+ new TextEncoder().encode(JSON.stringify(attributes)).length > 4096)
32
+ fail("INVALID_ARGUMENT", "attributes allow at most 32 keys and 4096 UTF-8 bytes");
33
+ return Object.fromEntries(Object.entries(attributes).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
34
+ }
35
+ function validateAttributeKey(key) {
36
+ if (!/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(key) ||
37
+ ["__proto__", "constructor", "prototype"].includes(key))
38
+ fail("INVALID_ARGUMENT", "attribute keys must start with a letter and contain up to 64 ASCII letters, digits, dots, underscores or hyphens");
39
+ }
40
+ function validateAttributeValue(value) {
41
+ if ((typeof value === "string" && value.length > 512) ||
42
+ (typeof value === "number" && !Number.isFinite(value)))
43
+ fail("INVALID_ARGUMENT", "attribute values require finite numbers or strings up to 512 characters");
44
+ }
45
+ /** Index each current attribute once; ordinary reads never scan all profiles. */
46
+ export async function syncPersonAttributes(ctx, siteId, visitorId, attributes) {
47
+ const rows = await ctx.db
48
+ .query("visitorAttributes")
49
+ .withIndex("by_siteId_and_visitorId", (q) => q.eq("siteId", siteId).eq("visitorId", visitorId))
50
+ .take(33);
51
+ const present = new Set();
52
+ for (const row of rows) {
53
+ present.add(row.key);
54
+ if (!Object.hasOwn(attributes, row.key))
55
+ await ctx.db.delete("visitorAttributes", row._id);
56
+ else if (attributes[row.key] !== row.value)
57
+ await ctx.db.patch("visitorAttributes", row._id, {
58
+ value: attributes[row.key],
59
+ });
60
+ }
61
+ for (const [key, value] of Object.entries(attributes)) {
62
+ if (!present.has(key))
63
+ await ctx.db.insert("visitorAttributes", {
64
+ siteId,
65
+ visitorId,
66
+ key,
67
+ value,
68
+ });
69
+ }
70
+ }
71
+ export function publicPerson(row) {
72
+ const { searchName: _name, searchEmail: _email, ...person } = row;
73
+ return person;
74
+ }
75
+ /** An optional, bounded upgrade for profiles created before the directory. */
76
+ export const backfillSearch = mutation({
77
+ args: { siteId: v.id("sites") },
78
+ returns: v.object({ updated: v.number(), isDone: v.boolean() }),
79
+ handler: async (ctx, { siteId }) => {
80
+ if (!(await ctx.db.get("sites", siteId)))
81
+ fail("NOT_FOUND", "site not found");
82
+ const rows = await ctx.db
83
+ .query("visitorProfiles")
84
+ .withIndex("by_siteId_and_searchName_and_visitorId", (q) => q.eq("siteId", siteId).eq("searchName", undefined))
85
+ .take(65);
86
+ for (const row of rows.slice(0, 64)) {
87
+ await ctx.db.patch("visitorProfiles", row._id, profileSearchFields(row.name, row.email));
88
+ await syncPersonAttributes(ctx, siteId, row.visitorId, row.attributes ?? {});
89
+ }
90
+ return { updated: Math.min(64, rows.length), isDone: rows.length <= 64 };
91
+ },
92
+ });
93
+ /** Exclusive upper bound for a Unicode prefix, including supplementary characters. */
94
+ function afterPrefix(prefix) {
95
+ const points = Array.from(prefix);
96
+ for (let i = points.length - 1; i >= 0; i--) {
97
+ const code = points[i].codePointAt(0);
98
+ if (code < 0x10ffff)
99
+ return (points.slice(0, i).join("") +
100
+ String.fromCodePoint(code + 1 === 0xd800 ? 0xe000 : code + 1));
101
+ }
102
+ return undefined;
103
+ }
104
+ function normalizedFilter(filter) {
105
+ if (!filter)
106
+ return undefined;
107
+ if (filter.field === "attribute") {
108
+ validateAttributeKey(filter.key);
109
+ validateAttributeValue(filter.value);
110
+ return { field: "attribute", key: filter.key, value: filter.value };
111
+ }
112
+ if (filter.prefix.length > 320)
113
+ fail("INVALID_ARGUMENT", "search prefix exceeds 320 characters");
114
+ const prefix = filter.field === "visitorId"
115
+ ? filter.prefix.trim()
116
+ : normalizeSearch(filter.prefix);
117
+ return prefix ? { field: filter.field, prefix } : undefined;
118
+ }
119
+ export const list = query({
120
+ args: {
121
+ siteId: v.id("sites"),
122
+ filter: v.optional(personFilterValidator),
123
+ paginationOpts: v.object({
124
+ cursor: v.union(v.string(), v.null()),
125
+ numItems: v.number(),
126
+ }),
127
+ },
128
+ returns: v.object({
129
+ page: v.array(personDocumentValidator),
130
+ isDone: v.boolean(),
131
+ continueCursor: v.string(),
132
+ }),
133
+ handler: async (ctx, args) => {
134
+ const filter = normalizedFilter(args.filter);
135
+ const scope = JSON.stringify([args.siteId, filter ?? null]);
136
+ const limit = args.paginationOpts.numItems;
137
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50)
138
+ fail("INVALID_ARGUMENT", "numItems must be an integer between 1 and 50");
139
+ let after;
140
+ if (args.paginationOpts.cursor !== null) {
141
+ try {
142
+ // NFKC may expand one character into several, in both scope and value.
143
+ if (args.paginationOpts.cursor.length > 16_384)
144
+ throw new Error();
145
+ const cursor = JSON.parse(args.paginationOpts.cursor);
146
+ if (cursor.v !== 1 ||
147
+ cursor.scope !== scope ||
148
+ typeof cursor.value !== "string" ||
149
+ typeof cursor.visitorId !== "string")
150
+ throw new Error();
151
+ after = { value: cursor.value, visitorId: cursor.visitorId };
152
+ if (filter &&
153
+ filter.field !== "attribute" &&
154
+ !after.value.startsWith(filter.prefix))
155
+ throw new Error();
156
+ }
157
+ catch {
158
+ fail("INVALID_ARGUMENT", "cursor does not belong to this people query");
159
+ }
160
+ }
161
+ let rows;
162
+ const take = limit + 1;
163
+ if (filter?.field === "attribute") {
164
+ const matches = await ctx.db
165
+ .query("visitorAttributes")
166
+ .withIndex("by_siteId_and_key_and_value_and_visitorId", (q) => q
167
+ .eq("siteId", args.siteId)
168
+ .eq("key", filter.key)
169
+ .eq("value", filter.value)
170
+ .gt("visitorId", after?.visitorId ?? ""))
171
+ .take(take);
172
+ rows = await Promise.all(matches.map(async (match) => {
173
+ const row = await ctx.db
174
+ .query("visitorProfiles")
175
+ .withIndex("by_siteId_and_visitorId", (q) => q.eq("siteId", args.siteId).eq("visitorId", match.visitorId))
176
+ .unique();
177
+ if (!row)
178
+ fail("NOT_FOUND", "person attribute index has no profile");
179
+ return row;
180
+ }));
181
+ }
182
+ else if (filter && filter.field !== "visitorId") {
183
+ const pending = await ctx.db
184
+ .query("visitorProfiles")
185
+ .withIndex("by_siteId_and_searchName_and_visitorId", (q) => q.eq("siteId", args.siteId).eq("searchName", undefined))
186
+ .first();
187
+ if (pending)
188
+ fail("CONFLICT", "Run backfillVisitorProfiles for this site before searching existing profiles");
189
+ const field = filter.field === "name" ? "searchName" : "searchEmail";
190
+ const index = filter.field === "name"
191
+ ? "by_siteId_and_searchName_and_visitorId"
192
+ : "by_siteId_and_searchEmail_and_visitorId";
193
+ rows = after
194
+ ? await ctx.db
195
+ .query("visitorProfiles")
196
+ .withIndex(index, (q) => q
197
+ .eq("siteId", args.siteId)
198
+ .eq(field, after.value)
199
+ .gt("visitorId", after.visitorId))
200
+ .take(take)
201
+ : [];
202
+ if (rows.length < take) {
203
+ const upper = afterPrefix(filter.prefix);
204
+ const rest = await ctx.db
205
+ .query("visitorProfiles")
206
+ .withIndex(index, (q) => {
207
+ const lower = after
208
+ ? q.eq("siteId", args.siteId).gt(field, after.value)
209
+ : q.eq("siteId", args.siteId).gte(field, filter.prefix);
210
+ return upper === undefined ? lower : lower.lt(field, upper);
211
+ })
212
+ .take(take - rows.length);
213
+ rows.push(...rest);
214
+ }
215
+ }
216
+ else {
217
+ const prefix = filter?.prefix ?? "";
218
+ const upper = afterPrefix(prefix);
219
+ rows = await ctx.db
220
+ .query("visitorProfiles")
221
+ .withIndex("by_siteId_and_visitorId", (q) => {
222
+ const lower = after
223
+ ? q.eq("siteId", args.siteId).gt("visitorId", after.visitorId)
224
+ : q.eq("siteId", args.siteId).gte("visitorId", prefix);
225
+ return upper === undefined ? lower : lower.lt("visitorId", upper);
226
+ })
227
+ .take(take);
228
+ }
229
+ const page = rows.slice(0, limit);
230
+ const last = page.at(-1);
231
+ return {
232
+ page: page.map(publicPerson),
233
+ isDone: rows.length <= limit,
234
+ continueCursor: JSON.stringify({
235
+ v: 1,
236
+ scope,
237
+ visitorId: last?.visitorId ?? after?.visitorId ?? "",
238
+ value: last
239
+ ? filter?.field === "name"
240
+ ? last.searchName
241
+ : filter?.field === "email"
242
+ ? last.searchEmail
243
+ : last.visitorId
244
+ : (after?.value ?? ""),
245
+ }),
246
+ };
247
+ },
248
+ });
249
+ //# sourceMappingURL=people.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"people.js","sourceRoot":"","sources":["../../src/component/people.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Channel } from "./validators.js";
2
+ /**
3
+ * The closed table origin.ts classifies against. Bump the version whenever a
4
+ * row changes, and add a corpus case (origin.test.ts) for the change.
5
+ */
6
+ export declare const PLATFORM_TABLE_VERSION = 1;
7
+ export type PlatformRow = {
8
+ id: string;
9
+ /** Where the platform's traffic lands when nothing says paid or organic. */
10
+ channel: Channel;
11
+ /** Registrable-domain suffixes: a host equal to one or ending in "." + it. */
12
+ domains?: readonly string[];
13
+ /** Hosts matched in full, for a family with many country domains. */
14
+ hostPattern?: RegExp;
15
+ /** Hosts that only ever send the platform's ads (the paid channel). */
16
+ paidDomains?: readonly string[];
17
+ /** Android packages, as an `android-app://<package>` referrer names them. */
18
+ packages?: readonly string[];
19
+ /** The platform's in-app browser marker in the User-Agent. */
20
+ agent?: RegExp;
21
+ /** utm_source spellings, lowercase; the id itself always matches. */
22
+ aliases?: readonly string[];
23
+ /** utm_source spellings that themselves declare the paid channel. */
24
+ paidAliases?: readonly string[];
25
+ };
26
+ export declare const PLATFORMS: readonly PlatformRow[];
27
+ /**
28
+ * Click-ID keys the tracker may report by name, in priority order: the ad
29
+ * networks' own ids first, then the organic share ids, `fbclid` last because
30
+ * Meta adds it to organic clicks too. `unknown` platforms take the referrer's.
31
+ */
32
+ export declare const CLICK_IDS: ReadonlyArray<readonly [key: string, platform: string, channel: Channel]>;
33
+ //# sourceMappingURL=platforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platforms.d.ts","sourceRoot":"","sources":["../../src/component/platforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,qEAAqE;IACrE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC;AAOF,eAAO,MAAM,SAAS,EAAE,SAAS,WAAW,EAyS3C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,aAAa,CACnC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAoB3D,CAAC"}