@iann29/rastro 0.1.0-alpha.3 → 0.1.0-alpha.4
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 +264 -51
- package/agent/integration.md +651 -0
- package/agent/manifest.json +183 -0
- package/agent/manifest.schema.json +405 -0
- package/dist/client/federation.d.ts +205 -0
- package/dist/client/federation.d.ts.map +1 -0
- package/dist/client/federation.js +179 -0
- package/dist/client/federation.js.map +1 -0
- package/dist/client/index.d.ts +1600 -8
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +210 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +4 -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 +151 -1
- package/dist/component/_generated/component.d.ts.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 +10 -0
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +10 -0
- package/dist/component/constants.js.map +1 -1
- package/dist/component/coverage.d.ts +18 -0
- package/dist/component/coverage.d.ts.map +1 -0
- package/dist/component/coverage.js +19 -0
- package/dist/component/coverage.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/http.d.ts.map +1 -1
- package/dist/component/http.js +46 -8
- package/dist/component/http.js.map +1 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +238 -18
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +6 -3
- package/dist/component/live.js.map +1 -1
- package/dist/component/reports.d.ts +111 -2
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +639 -78
- 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 +517 -54
- package/dist/component/retention.js.map +1 -1
- package/dist/component/sanitize.d.ts +4 -1
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +9 -4
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +187 -1
- package/dist/component/schema.js +100 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/sites.d.ts.map +1 -1
- package/dist/component/sites.js +5 -1
- package/dist/component/sites.js.map +1 -1
- package/dist/component/validators.d.ts +26 -1
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +7 -0
- package/dist/component/validators.js.map +1 -1
- package/docs/benchmarks/2026-08-20-realistic.md +4 -4
- package/docs/benchmarks/2026-08-21-formal-certification.md +333 -0
- package/docs/federation-setup.md +395 -0
- package/docs/federation.md +258 -0
- package/docs/upgrading.md +130 -0
- package/llms.txt +65 -0
- package/package.json +32 -6
- package/scripts/generate-federation-keys.mjs +20 -0
- package/src/component/_generated/api.ts +4 -0
- package/src/component/_generated/component.ts +211 -1
- package/src/component/cardinality.ts +116 -0
- package/src/component/constants.ts +10 -0
- package/src/component/coverage.ts +25 -0
- package/src/component/errors.ts +2 -1
- package/src/component/http.ts +76 -6
- package/src/component/ingest.ts +387 -12
- package/src/component/live.ts +8 -3
- package/src/component/reports.ts +891 -86
- package/src/component/retention.ts +624 -88
- package/src/component/sanitize.ts +15 -4
- package/src/component/schema.ts +110 -0
- package/src/component/sites.ts +5 -1
- package/src/component/validators.ts +8 -0
|
@@ -249,10 +249,21 @@ export function isValidMoney(value: number): boolean {
|
|
|
249
249
|
return Number.isSafeInteger(value) && value >= 0 && value <= 1_000_000_000;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
export function
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
export function measureBatchBytes(
|
|
253
|
+
siteId: string,
|
|
254
|
+
events: TrackerEvent[],
|
|
255
|
+
): { batchBytes: number; eventByteCounts: number[] } {
|
|
256
|
+
const encoder = new TextEncoder();
|
|
257
|
+
const eventByteCounts = events.map((event) =>
|
|
258
|
+
encoder.encode(JSON.stringify(event)).byteLength
|
|
259
|
+
);
|
|
260
|
+
// The empty envelope already includes both array brackets; only commas remain.
|
|
261
|
+
const batchBytes =
|
|
262
|
+
encoder.encode(JSON.stringify({ siteId, events: [] })).byteLength +
|
|
263
|
+
eventByteCounts.reduce((total, byteCount) => total + byteCount, 0) +
|
|
264
|
+
Math.max(0, events.length - 1);
|
|
265
|
+
if (batchBytes > MAX_BATCH_BYTES) throw new Error("batch exceeds 64 KiB");
|
|
266
|
+
return { batchBytes, eventByteCounts };
|
|
256
267
|
}
|
|
257
268
|
|
|
258
269
|
export function sourceFromReferrer(referrer: string | undefined): string {
|
package/src/component/schema.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
goalFieldsValidator,
|
|
9
9
|
sessionFieldsValidator,
|
|
10
10
|
siteFieldsValidator,
|
|
11
|
+
visitorSketchValidator,
|
|
11
12
|
} from "./validators.js";
|
|
12
13
|
|
|
13
14
|
export default defineSchema({
|
|
@@ -101,6 +102,115 @@ export default defineSchema({
|
|
|
101
102
|
"bucketStart",
|
|
102
103
|
]),
|
|
103
104
|
|
|
105
|
+
goalDailyRollups: defineTable({
|
|
106
|
+
siteId: v.id("sites"),
|
|
107
|
+
goalId: v.id("goals"),
|
|
108
|
+
bucketStart: v.number(),
|
|
109
|
+
shard: v.number(),
|
|
110
|
+
generation: v.literal(1),
|
|
111
|
+
goalKey: v.string(),
|
|
112
|
+
completions: v.number(),
|
|
113
|
+
valueCents: v.number(),
|
|
114
|
+
})
|
|
115
|
+
.index("by_siteId_and_goalId_and_bucketStart_and_shard", [
|
|
116
|
+
"siteId",
|
|
117
|
+
"goalId",
|
|
118
|
+
"bucketStart",
|
|
119
|
+
"shard",
|
|
120
|
+
])
|
|
121
|
+
.index("by_siteId_and_goalId_and_bucketStart", [
|
|
122
|
+
"siteId",
|
|
123
|
+
"goalId",
|
|
124
|
+
"bucketStart",
|
|
125
|
+
])
|
|
126
|
+
.index("by_siteId_and_bucketStart", ["siteId", "bucketStart"]),
|
|
127
|
+
|
|
128
|
+
funnelDailyRollups: defineTable({
|
|
129
|
+
siteId: v.id("sites"),
|
|
130
|
+
funnelId: v.id("funnels"),
|
|
131
|
+
bucketStart: v.number(),
|
|
132
|
+
shard: v.number(),
|
|
133
|
+
generation: v.literal(1),
|
|
134
|
+
steps: v.array(v.object({
|
|
135
|
+
step: v.number(),
|
|
136
|
+
entrants: v.number(),
|
|
137
|
+
completions: v.number(),
|
|
138
|
+
})),
|
|
139
|
+
})
|
|
140
|
+
.index("by_siteId_and_funnelId_and_bucketStart_and_shard", [
|
|
141
|
+
"siteId",
|
|
142
|
+
"funnelId",
|
|
143
|
+
"bucketStart",
|
|
144
|
+
"shard",
|
|
145
|
+
])
|
|
146
|
+
.index("by_siteId_and_funnelId_and_bucketStart", [
|
|
147
|
+
"siteId",
|
|
148
|
+
"funnelId",
|
|
149
|
+
"bucketStart",
|
|
150
|
+
])
|
|
151
|
+
.index("by_siteId_and_bucketStart", ["siteId", "bucketStart"]),
|
|
152
|
+
|
|
153
|
+
affiliateDailyRollups: defineTable({
|
|
154
|
+
siteId: v.id("sites"),
|
|
155
|
+
affiliateId: v.id("affiliates"),
|
|
156
|
+
bucketStart: v.number(),
|
|
157
|
+
shard: v.number(),
|
|
158
|
+
generation: v.literal(1),
|
|
159
|
+
affiliateSlug: v.string(),
|
|
160
|
+
visits: v.number(),
|
|
161
|
+
visitorSketch: visitorSketchValidator,
|
|
162
|
+
conversions: v.number(),
|
|
163
|
+
revenueCents: v.number(),
|
|
164
|
+
commissionCents: v.number(),
|
|
165
|
+
})
|
|
166
|
+
.index("by_siteId_and_affiliateId_and_bucketStart_and_shard", [
|
|
167
|
+
"siteId",
|
|
168
|
+
"affiliateId",
|
|
169
|
+
"bucketStart",
|
|
170
|
+
"shard",
|
|
171
|
+
])
|
|
172
|
+
.index("by_siteId_and_affiliateId_and_bucketStart", [
|
|
173
|
+
"siteId",
|
|
174
|
+
"affiliateId",
|
|
175
|
+
"bucketStart",
|
|
176
|
+
])
|
|
177
|
+
.index("by_siteId_and_bucketStart", ["siteId", "bucketStart"]),
|
|
178
|
+
|
|
179
|
+
analyticsCoverage: defineTable({
|
|
180
|
+
siteId: v.id("sites"),
|
|
181
|
+
generation: v.literal(1),
|
|
182
|
+
rollupCompleteFrom: v.number(),
|
|
183
|
+
retained: v.array(v.object({ dataset: v.string(), before: v.number() })),
|
|
184
|
+
updatedAt: v.number(),
|
|
185
|
+
}).index("by_siteId", ["siteId"]),
|
|
186
|
+
|
|
187
|
+
retentionPolicies: defineTable({
|
|
188
|
+
siteId: v.id("sites"),
|
|
189
|
+
eventsDays: v.number(),
|
|
190
|
+
sessionsDays: v.number(),
|
|
191
|
+
conversionsDays: v.number(),
|
|
192
|
+
aggregatesDays: v.number(),
|
|
193
|
+
reportsDays: v.number(),
|
|
194
|
+
updatedAt: v.number(),
|
|
195
|
+
}).index("by_siteId", ["siteId"]),
|
|
196
|
+
|
|
197
|
+
retentionStates: defineTable({
|
|
198
|
+
siteId: v.id("sites"),
|
|
199
|
+
kind: v.string(),
|
|
200
|
+
before: v.number(),
|
|
201
|
+
status: v.union(
|
|
202
|
+
v.literal("pending"),
|
|
203
|
+
v.literal("running"),
|
|
204
|
+
v.literal("complete"),
|
|
205
|
+
),
|
|
206
|
+
generation: v.number(),
|
|
207
|
+
attempt: v.number(),
|
|
208
|
+
leaseExpiresAt: v.optional(v.number()),
|
|
209
|
+
scheduledAt: v.optional(v.number()),
|
|
210
|
+
deleted: v.number(),
|
|
211
|
+
updatedAt: v.number(),
|
|
212
|
+
}).index("by_siteId_and_kind", ["siteId", "kind"]),
|
|
213
|
+
|
|
104
214
|
goals: defineTable(goalFieldsValidator)
|
|
105
215
|
.index("by_siteId", ["siteId"])
|
|
106
216
|
.index("by_siteId_and_key", ["siteId", "key"]),
|
package/src/component/sites.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
2
|
import { internalQuery, mutation, query } from "./_generated/server.js";
|
|
3
3
|
import { MAX_SITES_PER_OWNER } from "./constants.js";
|
|
4
|
+
import { ensureAnalyticsControl } from "./coverage.js";
|
|
4
5
|
import { fail } from "./errors.js";
|
|
5
6
|
import {
|
|
6
7
|
cleanString,
|
|
@@ -66,6 +67,7 @@ export const create = mutation({
|
|
|
66
67
|
) {
|
|
67
68
|
fail("CONFLICT", "site name is already associated with different settings");
|
|
68
69
|
}
|
|
70
|
+
await ensureAnalyticsControl(ctx, existing._id, Date.now());
|
|
69
71
|
return existing._id;
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -80,7 +82,7 @@ export const create = mutation({
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
const now = Date.now();
|
|
83
|
-
|
|
85
|
+
const siteId = await ctx.db.insert("sites", {
|
|
84
86
|
ownerId,
|
|
85
87
|
name,
|
|
86
88
|
domains,
|
|
@@ -91,6 +93,8 @@ export const create = mutation({
|
|
|
91
93
|
createdAt: now,
|
|
92
94
|
updatedAt: now,
|
|
93
95
|
});
|
|
96
|
+
await ensureAnalyticsControl(ctx, siteId, now);
|
|
97
|
+
return siteId;
|
|
94
98
|
},
|
|
95
99
|
});
|
|
96
100
|
|
|
@@ -144,6 +144,12 @@ export const dimensionSlotValidator = v.object({
|
|
|
144
144
|
revenueCents: v.number(),
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
export const visitorSketchValidator = v.object({
|
|
148
|
+
version: v.literal(1),
|
|
149
|
+
precision: v.number(),
|
|
150
|
+
registers: v.bytes(),
|
|
151
|
+
});
|
|
152
|
+
|
|
147
153
|
export const aggregateFieldsValidator = v.object({
|
|
148
154
|
siteId: v.id("sites"),
|
|
149
155
|
granularity: v.union(v.literal("hour"), v.literal("day")),
|
|
@@ -156,6 +162,8 @@ export const aggregateFieldsValidator = v.object({
|
|
|
156
162
|
outboundClicks: v.number(),
|
|
157
163
|
sessions: v.number(),
|
|
158
164
|
visitors: v.number(),
|
|
165
|
+
visitorSketch: v.optional(visitorSketchValidator),
|
|
166
|
+
visitorSketchComplete: v.optional(v.boolean()),
|
|
159
167
|
conversions: v.number(),
|
|
160
168
|
revenueCents: v.number(),
|
|
161
169
|
dimensions: v.array(dimensionSlotValidator),
|