@iann29/rastro 0.1.0-alpha.1 → 0.1.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +693 -69
- package/agent/integration.md +801 -0
- package/agent/manifest.json +205 -0
- package/agent/manifest.schema.json +444 -0
- package/dist/client/federation.d.ts +381 -0
- package/dist/client/federation.d.ts.map +1 -0
- package/dist/client/federation.js +274 -0
- package/dist/client/federation.js.map +1 -0
- package/dist/client/index.d.ts +2673 -15
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +565 -33
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +22 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +315 -4
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +4 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/affiliates.d.ts.map +1 -1
- package/dist/component/affiliates.js +6 -2
- package/dist/component/affiliates.js.map +1 -1
- package/dist/component/cardinality.d.ts +12 -0
- package/dist/component/cardinality.d.ts.map +1 -0
- package/dist/component/cardinality.js +94 -0
- package/dist/component/cardinality.js.map +1 -0
- package/dist/component/constants.d.ts +28 -1
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +44 -1
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +6 -1
- package/dist/component/convex.config.js +9 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/coverage.d.ts +27 -0
- package/dist/component/coverage.d.ts.map +1 -0
- package/dist/component/coverage.js +56 -0
- package/dist/component/coverage.js.map +1 -0
- package/dist/component/diagnostics.d.ts +9 -0
- package/dist/component/diagnostics.d.ts.map +1 -0
- package/dist/component/diagnostics.js +47 -0
- package/dist/component/diagnostics.js.map +1 -0
- package/dist/component/errors.d.ts +1 -1
- package/dist/component/errors.d.ts.map +1 -1
- package/dist/component/errors.js.map +1 -1
- package/dist/component/eventStore.d.ts +21 -9
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/eventStore.js +142 -152
- package/dist/component/eventStore.js.map +1 -1
- package/dist/component/funnels.d.ts.map +1 -1
- package/dist/component/funnels.js +5 -3
- package/dist/component/funnels.js.map +1 -1
- package/dist/component/geo.d.ts +73 -0
- package/dist/component/geo.d.ts.map +1 -0
- package/dist/component/geo.js +648 -0
- package/dist/component/geo.js.map +1 -0
- package/dist/component/goals.d.ts.map +1 -1
- package/dist/component/goals.js +8 -5
- package/dist/component/goals.js.map +1 -1
- package/dist/component/guards.d.ts.map +1 -1
- package/dist/component/guards.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +281 -62
- package/dist/component/http.js.map +1 -1
- package/dist/component/identity.d.ts +13 -0
- package/dist/component/identity.d.ts.map +1 -0
- package/dist/component/identity.js +58 -0
- package/dist/component/identity.js.map +1 -0
- package/dist/component/ingest.d.ts +3 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +498 -95
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +7 -6
- package/dist/component/live.js.map +1 -1
- package/dist/component/localTime.d.ts +25 -0
- package/dist/component/localTime.d.ts.map +1 -0
- package/dist/component/localTime.js +126 -0
- package/dist/component/localTime.js.map +1 -0
- package/dist/component/reports.d.ts +266 -10
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +1243 -137
- package/dist/component/reports.js.map +1 -1
- package/dist/component/retention.d.ts +75 -1
- package/dist/component/retention.d.ts.map +1 -1
- package/dist/component/retention.js +561 -38
- package/dist/component/retention.js.map +1 -1
- package/dist/component/rollupStore.d.ts +320 -0
- package/dist/component/rollupStore.d.ts.map +1 -0
- package/dist/component/rollupStore.js +596 -0
- package/dist/component/rollupStore.js.map +1 -0
- package/dist/component/rollups.d.ts +20 -0
- package/dist/component/rollups.d.ts.map +1 -0
- package/dist/component/rollups.js +73 -0
- package/dist/component/rollups.js.map +1 -0
- package/dist/component/sanitize.d.ts +24 -1
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +96 -16
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +687 -65
- package/dist/component/schema.js +187 -20
- package/dist/component/schema.js.map +1 -1
- package/dist/component/sites.d.ts +12 -0
- package/dist/component/sites.d.ts.map +1 -1
- package/dist/component/sites.js +41 -7
- package/dist/component/sites.js.map +1 -1
- package/dist/component/useragent.d.ts +9 -0
- package/dist/component/useragent.d.ts.map +1 -0
- package/dist/component/useragent.js +152 -0
- package/dist/component/useragent.js.map +1 -0
- package/dist/component/validators.d.ts +124 -69
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +35 -14
- package/dist/component/validators.js.map +1 -1
- package/dist/component/visitors.d.ts +19 -0
- package/dist/component/visitors.d.ts.map +1 -0
- package/dist/component/visitors.js +86 -0
- package/dist/component/visitors.js.map +1 -0
- package/dist/component/vitals.d.ts +41 -0
- package/dist/component/vitals.d.ts.map +1 -0
- package/dist/component/vitals.js +115 -0
- package/dist/component/vitals.js.map +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/tracker/generated.d.ts +11 -4
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +11 -4
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -1
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +57 -20
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker/vitals.d.ts +10 -0
- package/dist/tracker/vitals.d.ts.map +1 -0
- package/dist/tracker/vitals.js +140 -0
- package/dist/tracker/vitals.js.map +1 -0
- package/dist/tracker.min.js +1 -1
- package/dist/vitals.min.js +1 -0
- package/docs/benchmarks/2026-08-20-realistic.md +76 -76
- package/docs/benchmarks/2026-08-21-formal-certification.md +353 -0
- package/docs/benchmarks/2026-08-30-alpha6-recertification.md +206 -0
- package/docs/federation-setup.md +464 -0
- package/docs/federation.md +352 -0
- package/docs/upgrading.md +344 -0
- package/llms.txt +72 -0
- package/package.json +55 -11
- package/scripts/benchmark-ingest.mjs +175 -73
- package/scripts/generate-federation-keys.mjs +20 -0
- package/src/component/_generated/api.ts +22 -0
- package/src/component/_generated/component.ts +381 -4
- package/src/component/_generated/server.ts +4 -0
- package/src/component/affiliates.ts +20 -5
- package/src/component/cardinality.ts +117 -0
- package/src/component/constants.ts +44 -1
- package/src/component/convex.config.ts +11 -1
- package/src/component/coverage.ts +71 -0
- package/src/component/diagnostics.ts +65 -0
- package/src/component/errors.ts +2 -1
- package/src/component/eventStore.ts +217 -193
- package/src/component/funnels.ts +19 -16
- package/src/component/geo.ts +835 -0
- package/src/component/goals.ts +29 -21
- package/src/component/guards.ts +3 -1
- package/src/component/http.ts +404 -70
- package/src/component/identity.ts +74 -0
- package/src/component/ingest.ts +894 -188
- package/src/component/live.ts +13 -7
- package/src/component/localTime.ts +167 -0
- package/src/component/reports.ts +1872 -197
- package/src/component/retention.ts +788 -96
- package/src/component/rollupStore.ts +799 -0
- package/src/component/rollups.ts +82 -0
- package/src/component/sanitize.ts +144 -29
- package/src/component/schema.ts +217 -21
- package/src/component/sites.ts +59 -12
- package/src/component/useragent.ts +171 -0
- package/src/component/validators.ts +49 -14
- package/src/component/visitors.ts +116 -0
- package/src/component/vitals.ts +146 -0
package/src/component/live.ts
CHANGED
|
@@ -23,16 +23,23 @@ export const expire = internalMutation({
|
|
|
23
23
|
if (liveSession.expiryChainDeadline !== args.scheduledDeadline) {
|
|
24
24
|
return { expired: false, rescheduled: false, stale: true };
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
if (liveSession.expiresAt > now) {
|
|
27
28
|
const state = await ctx.db
|
|
28
29
|
.query("liveSweepStates")
|
|
29
|
-
.withIndex("by_siteId", (range) =>
|
|
30
|
+
.withIndex("by_siteId", (range) =>
|
|
31
|
+
range.eq("siteId", liveSession.siteId),
|
|
32
|
+
)
|
|
30
33
|
.unique();
|
|
31
34
|
const scheduledAt = Math.min(
|
|
32
35
|
liveSession.expiresAt,
|
|
33
|
-
|
|
36
|
+
now + LIVE_SWEEP_INTERVAL_MS,
|
|
34
37
|
);
|
|
35
|
-
if (
|
|
38
|
+
if (
|
|
39
|
+
!state ||
|
|
40
|
+
state.scheduledAt <= now ||
|
|
41
|
+
state.scheduledAt > scheduledAt
|
|
42
|
+
) {
|
|
36
43
|
if (state) {
|
|
37
44
|
await ctx.db.patch("liveSweepStates", state._id, {
|
|
38
45
|
scheduledAt,
|
|
@@ -98,9 +105,8 @@ export const sweep = internalMutation({
|
|
|
98
105
|
return { expired: expired.length, rescheduled: false, stale: false };
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
const scheduledAt =
|
|
102
|
-
? now
|
|
103
|
-
: now + LIVE_SWEEP_INTERVAL_MS;
|
|
108
|
+
const scheduledAt =
|
|
109
|
+
expired.length === SWEEP_BATCH_SIZE ? now : now + LIVE_SWEEP_INTERVAL_MS;
|
|
104
110
|
await ctx.db.patch("liveSweepStates", state._id, { scheduledAt });
|
|
105
111
|
await ctx.scheduler.runAt(scheduledAt, internal.live.sweep, {
|
|
106
112
|
siteId: args.siteId,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { DAY_MS, HOUR_MS } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Local calendar days for a site's timezone. Aggregates are bucketed by UTC
|
|
5
|
+
* hour and UTC day; a site with a timezone also gets a `localDay` bucket per
|
|
6
|
+
* calendar day in that zone (see foldAggregates), so "yesterday" in São Paulo
|
|
7
|
+
* ends at midnight in São Paulo, not at 21:00. Every function here uses the
|
|
8
|
+
* runtime's Intl data — the same data `validateTimezone` relies on.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const formatters = new Map<string, Intl.DateTimeFormat>();
|
|
12
|
+
|
|
13
|
+
function formatter(timeZone: string): Intl.DateTimeFormat {
|
|
14
|
+
let cached = formatters.get(timeZone);
|
|
15
|
+
if (!cached) {
|
|
16
|
+
cached = new Intl.DateTimeFormat("en-US", {
|
|
17
|
+
timeZone,
|
|
18
|
+
hourCycle: "h23",
|
|
19
|
+
year: "numeric",
|
|
20
|
+
month: "2-digit",
|
|
21
|
+
day: "2-digit",
|
|
22
|
+
hour: "2-digit",
|
|
23
|
+
minute: "2-digit",
|
|
24
|
+
second: "2-digit",
|
|
25
|
+
});
|
|
26
|
+
formatters.set(timeZone, cached);
|
|
27
|
+
}
|
|
28
|
+
return cached;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type LocalParts = {
|
|
32
|
+
year: number;
|
|
33
|
+
month: number;
|
|
34
|
+
day: number;
|
|
35
|
+
hour: number;
|
|
36
|
+
minute: number;
|
|
37
|
+
second: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function localParts(timestamp: number, timeZone: string): LocalParts {
|
|
41
|
+
const parts: Partial<LocalParts> = {};
|
|
42
|
+
for (const part of formatter(timeZone).formatToParts(timestamp)) {
|
|
43
|
+
if (part.type === "literal") continue;
|
|
44
|
+
parts[part.type as keyof LocalParts] = Number(part.value);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
year: parts.year ?? 1970,
|
|
48
|
+
month: parts.month ?? 1,
|
|
49
|
+
day: parts.day ?? 1,
|
|
50
|
+
// Some engines print 24 for midnight even with hourCycle h23.
|
|
51
|
+
hour: (parts.hour ?? 0) % 24,
|
|
52
|
+
minute: parts.minute ?? 0,
|
|
53
|
+
second: parts.second ?? 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The zone's offset from UTC at `timestamp`, in milliseconds (east is positive). */
|
|
58
|
+
export function localOffsetMs(timestamp: number, timeZone: string): number {
|
|
59
|
+
const parts = localParts(timestamp, timeZone);
|
|
60
|
+
const asUtc = Date.UTC(
|
|
61
|
+
parts.year,
|
|
62
|
+
parts.month - 1,
|
|
63
|
+
parts.day,
|
|
64
|
+
parts.hour,
|
|
65
|
+
parts.minute,
|
|
66
|
+
parts.second,
|
|
67
|
+
);
|
|
68
|
+
return asUtc - (timestamp - (((timestamp % 1000) + 1000) % 1000));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function localDateKey(timestamp: number, timeZone: string): number {
|
|
72
|
+
const parts = localParts(timestamp, timeZone);
|
|
73
|
+
return parts.year * 10_000 + parts.month * 100 + parts.day;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The first instant of the local calendar day that contains `timestamp`, as
|
|
78
|
+
* a UTC millisecond timestamp. Where a transition removes midnight (Chile
|
|
79
|
+
* springs forward at 00:00), the day starts at the first instant that exists.
|
|
80
|
+
*/
|
|
81
|
+
export function localDayStart(timestamp: number, timeZone: string): number {
|
|
82
|
+
const parts = localParts(timestamp, timeZone);
|
|
83
|
+
const target = parts.year * 10_000 + parts.month * 100 + parts.day;
|
|
84
|
+
const guess = Date.UTC(parts.year, parts.month - 1, parts.day);
|
|
85
|
+
// The offset in force at UTC midnight is usually the one at local midnight
|
|
86
|
+
// too; around a transition it is not, so a second and third candidate use
|
|
87
|
+
// the offset observed at the previous guess, and the earliest instant that
|
|
88
|
+
// still falls on the target date wins.
|
|
89
|
+
const first = guess - localOffsetMs(guess, timeZone);
|
|
90
|
+
const second = guess - localOffsetMs(first, timeZone);
|
|
91
|
+
const third = guess - localOffsetMs(second, timeZone);
|
|
92
|
+
const candidates = [first, second, third]
|
|
93
|
+
.filter((candidate) => localDateKey(candidate, timeZone) === target)
|
|
94
|
+
.sort((left, right) => left - right);
|
|
95
|
+
return candidates[0] ?? first;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The start of the local day after the one starting at `dayStart`. */
|
|
99
|
+
export function nextLocalDayStart(dayStart: number, timeZone: string): number {
|
|
100
|
+
return localDayStart(dayStart + DAY_MS + 12 * HOUR_MS, timeZone);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The start of the local day before the one starting at `dayStart`. */
|
|
104
|
+
export function previousLocalDayStart(
|
|
105
|
+
dayStart: number,
|
|
106
|
+
timeZone: string,
|
|
107
|
+
): number {
|
|
108
|
+
return localDayStart(dayStart - 12 * HOUR_MS, timeZone);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Every local day start from `from` through `to`, when `from` is one. */
|
|
112
|
+
export function localDayStarts(
|
|
113
|
+
from: number,
|
|
114
|
+
to: number,
|
|
115
|
+
timeZone: string,
|
|
116
|
+
): number[] {
|
|
117
|
+
const starts: number[] = [];
|
|
118
|
+
let cursor = from;
|
|
119
|
+
while (cursor <= to) {
|
|
120
|
+
starts.push(cursor);
|
|
121
|
+
cursor = nextLocalDayStart(cursor, timeZone);
|
|
122
|
+
}
|
|
123
|
+
return starts;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Whether `[from, to]` covers whole local days in the zone, inclusively. */
|
|
127
|
+
export function isLocalDayAligned(
|
|
128
|
+
from: number,
|
|
129
|
+
to: number,
|
|
130
|
+
timeZone: string,
|
|
131
|
+
): boolean {
|
|
132
|
+
return (
|
|
133
|
+
localDayStart(from, timeZone) === from &&
|
|
134
|
+
localDayStart(to + 1, timeZone) === to + 1
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* A site's timezone when it needs its own day buckets: undefined for no
|
|
140
|
+
* timezone and for zones that never leave UTC, whose local days are the UTC
|
|
141
|
+
* days already kept.
|
|
142
|
+
*/
|
|
143
|
+
export function localDayTimezone(
|
|
144
|
+
timezone: string | undefined | null,
|
|
145
|
+
): string | undefined {
|
|
146
|
+
if (!timezone) return undefined;
|
|
147
|
+
try {
|
|
148
|
+
const year = new Date().getUTCFullYear();
|
|
149
|
+
const january = Date.UTC(year, 0, 1);
|
|
150
|
+
const july = Date.UTC(year, 6, 1);
|
|
151
|
+
return localOffsetMs(january, timezone) === 0 &&
|
|
152
|
+
localOffsetMs(july, timezone) === 0
|
|
153
|
+
? undefined
|
|
154
|
+
: timezone;
|
|
155
|
+
} catch {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** One timezone shared by every site, or undefined when they differ. */
|
|
161
|
+
export function sharedLocalTimezone(
|
|
162
|
+
timezones: Array<string | undefined>,
|
|
163
|
+
): string | undefined {
|
|
164
|
+
const distinct = new Set(timezones);
|
|
165
|
+
if (distinct.size !== 1) return undefined;
|
|
166
|
+
return timezones[0];
|
|
167
|
+
}
|