@iann29/rastro 0.1.0-alpha.2 → 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.
Files changed (124) hide show
  1. package/README.md +325 -63
  2. package/agent/integration.md +651 -0
  3. package/agent/manifest.json +183 -0
  4. package/agent/manifest.schema.json +405 -0
  5. package/dist/client/federation.d.ts +205 -0
  6. package/dist/client/federation.d.ts.map +1 -0
  7. package/dist/client/federation.js +179 -0
  8. package/dist/client/federation.js.map +1 -0
  9. package/dist/client/index.d.ts +1600 -8
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +210 -2
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +9 -5
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts +161 -1
  17. package/dist/component/_generated/component.d.ts.map +1 -1
  18. package/dist/component/_generated/server.d.ts +4 -0
  19. package/dist/component/_generated/server.d.ts.map +1 -1
  20. package/dist/component/_generated/server.js.map +1 -1
  21. package/dist/component/cardinality.d.ts +12 -0
  22. package/dist/component/cardinality.d.ts.map +1 -0
  23. package/dist/component/cardinality.js +94 -0
  24. package/dist/component/cardinality.js.map +1 -0
  25. package/dist/component/constants.d.ts +10 -0
  26. package/dist/component/constants.d.ts.map +1 -1
  27. package/dist/component/constants.js +10 -0
  28. package/dist/component/constants.js.map +1 -1
  29. package/dist/component/convex.config.d.ts +7 -2
  30. package/dist/component/convex.config.d.ts.map +1 -1
  31. package/dist/component/convex.config.js +9 -4
  32. package/dist/component/convex.config.js.map +1 -1
  33. package/dist/component/coverage.d.ts +18 -0
  34. package/dist/component/coverage.d.ts.map +1 -0
  35. package/dist/component/coverage.js +19 -0
  36. package/dist/component/coverage.js.map +1 -0
  37. package/dist/component/diagnostics.d.ts +9 -0
  38. package/dist/component/diagnostics.d.ts.map +1 -0
  39. package/dist/component/diagnostics.js +47 -0
  40. package/dist/component/diagnostics.js.map +1 -0
  41. package/dist/component/errors.d.ts +1 -1
  42. package/dist/component/errors.d.ts.map +1 -1
  43. package/dist/component/errors.js.map +1 -1
  44. package/dist/component/eventStore.d.ts +11 -9
  45. package/dist/component/eventStore.d.ts.map +1 -1
  46. package/dist/component/eventStore.js +55 -227
  47. package/dist/component/eventStore.js.map +1 -1
  48. package/dist/component/geo.d.ts +71 -0
  49. package/dist/component/geo.d.ts.map +1 -0
  50. package/dist/component/geo.js +610 -0
  51. package/dist/component/geo.js.map +1 -0
  52. package/dist/component/http.d.ts.map +1 -1
  53. package/dist/component/http.js +168 -17
  54. package/dist/component/http.js.map +1 -1
  55. package/dist/component/ingest.d.ts.map +1 -1
  56. package/dist/component/ingest.js +272 -32
  57. package/dist/component/ingest.js.map +1 -1
  58. package/dist/component/live.d.ts.map +1 -1
  59. package/dist/component/live.js +6 -3
  60. package/dist/component/live.js.map +1 -1
  61. package/dist/component/reports.d.ts +117 -8
  62. package/dist/component/reports.d.ts.map +1 -1
  63. package/dist/component/reports.js +641 -79
  64. package/dist/component/reports.js.map +1 -1
  65. package/dist/component/retention.d.ts +75 -1
  66. package/dist/component/retention.d.ts.map +1 -1
  67. package/dist/component/retention.js +517 -54
  68. package/dist/component/retention.js.map +1 -1
  69. package/dist/component/sanitize.d.ts +4 -1
  70. package/dist/component/sanitize.d.ts.map +1 -1
  71. package/dist/component/sanitize.js +9 -4
  72. package/dist/component/sanitize.js.map +1 -1
  73. package/dist/component/schema.d.ts +202 -53
  74. package/dist/component/schema.js +113 -16
  75. package/dist/component/schema.js.map +1 -1
  76. package/dist/component/sites.d.ts.map +1 -1
  77. package/dist/component/sites.js +5 -1
  78. package/dist/component/sites.js.map +1 -1
  79. package/dist/component/validators.d.ts +29 -51
  80. package/dist/component/validators.d.ts.map +1 -1
  81. package/dist/component/validators.js +11 -12
  82. package/dist/component/validators.js.map +1 -1
  83. package/dist/tracker/generated.d.ts +4 -4
  84. package/dist/tracker/generated.d.ts.map +1 -1
  85. package/dist/tracker/generated.js +4 -4
  86. package/dist/tracker/generated.js.map +1 -1
  87. package/dist/tracker/tracker.js +7 -6
  88. package/dist/tracker/tracker.js.map +1 -1
  89. package/dist/tracker.min.js +1 -1
  90. package/docs/benchmarks/2026-08-20-realistic.md +5 -5
  91. package/docs/benchmarks/2026-08-21-formal-certification.md +333 -0
  92. package/docs/federation-setup.md +395 -0
  93. package/docs/federation.md +258 -0
  94. package/docs/upgrading.md +130 -0
  95. package/llms.txt +65 -0
  96. package/package.json +29 -7
  97. package/scripts/benchmark-ingest.mjs +81 -32
  98. package/scripts/generate-federation-keys.mjs +20 -0
  99. package/src/component/_generated/api.ts +9 -5
  100. package/src/component/_generated/component.ts +224 -1
  101. package/src/component/_generated/server.ts +4 -0
  102. package/src/component/cardinality.ts +116 -0
  103. package/src/component/constants.ts +10 -0
  104. package/src/component/convex.config.ts +11 -5
  105. package/src/component/coverage.ts +25 -0
  106. package/src/component/diagnostics.ts +64 -0
  107. package/src/component/errors.ts +2 -1
  108. package/src/component/eventStore.ts +78 -280
  109. package/src/component/geo.ts +779 -0
  110. package/src/component/http.ts +259 -22
  111. package/src/component/ingest.ts +436 -27
  112. package/src/component/live.ts +8 -3
  113. package/src/component/reports.ts +895 -87
  114. package/src/component/retention.ts +624 -88
  115. package/src/component/sanitize.ts +15 -4
  116. package/src/component/schema.ts +125 -16
  117. package/src/component/sites.ts +5 -1
  118. package/src/component/validators.ts +12 -13
  119. package/src/test.ts +0 -2
  120. package/dist/component/migrations.d.ts +0 -17
  121. package/dist/component/migrations.d.ts.map +0 -1
  122. package/dist/component/migrations.js +0 -44
  123. package/dist/component/migrations.js.map +0 -1
  124. package/src/component/migrations.ts +0 -52
@@ -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 ensureBatchBytes(value: unknown): number {
253
- const bytes = new TextEncoder().encode(JSON.stringify(value)).byteLength;
254
- if (bytes > MAX_BATCH_BYTES) throw new Error("batch exceeds 64 KiB");
255
- return bytes;
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 {
@@ -4,11 +4,11 @@ import {
4
4
  affiliateFieldsValidator,
5
5
  aggregateFieldsValidator,
6
6
  batchedEventValidator,
7
- storedEventFieldsValidator,
8
7
  funnelFieldsValidator,
9
8
  goalFieldsValidator,
10
9
  sessionFieldsValidator,
11
10
  siteFieldsValidator,
11
+ visitorSketchValidator,
12
12
  } from "./validators.js";
13
13
 
14
14
  export default defineSchema({
@@ -17,7 +17,11 @@ export default defineSchema({
17
17
  .index("by_ownerId_and_name", ["ownerId", "name"])
18
18
  .index("by_networkId", ["networkId"]),
19
19
 
20
- sessions: defineTable(sessionFieldsValidator)
20
+ sessions: defineTable(
21
+ sessionFieldsValidator.extend({
22
+ geoLookupAttemptedAt: v.optional(v.number()),
23
+ }),
24
+ )
21
25
  .index("by_siteId_and_sessionId", ["siteId", "sessionId"])
22
26
  .index("by_siteId_and_lastSeenAt", ["siteId", "lastSeenAt"])
23
27
  .index("by_siteId_and_startedAt", ["siteId", "startedAt"])
@@ -59,19 +63,11 @@ export default defineSchema({
59
63
  scheduledAt: v.number(),
60
64
  }).index("by_siteId", ["siteId"]),
61
65
 
62
- events: defineTable(storedEventFieldsValidator)
63
- .index("by_siteId_and_eventId", ["siteId", "eventId"])
64
- .index("by_siteId_and_timestamp", ["siteId", "timestamp"])
65
- .index("by_siteId_and_sessionId_and_timestamp", [
66
- "siteId",
67
- "sessionId",
68
- "timestamp",
69
- ])
70
- .index("by_siteId_and_visitorId_and_timestamp", [
71
- "siteId",
72
- "visitorId",
73
- "timestamp",
74
- ]),
66
+ geoLookupBudgets: defineTable({
67
+ siteId: v.id("sites"),
68
+ dayStart: v.number(),
69
+ count: v.number(),
70
+ }).index("by_siteId", ["siteId"]),
75
71
 
76
72
  eventBatches: defineTable({
77
73
  siteId: v.id("sites"),
@@ -106,6 +102,115 @@ export default defineSchema({
106
102
  "bucketStart",
107
103
  ]),
108
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
+
109
214
  goals: defineTable(goalFieldsValidator)
110
215
  .index("by_siteId", ["siteId"])
111
216
  .index("by_siteId_and_key", ["siteId", "key"]),
@@ -122,7 +227,11 @@ export default defineSchema({
122
227
  valueCents: v.number(),
123
228
  })
124
229
  .index("by_siteId_and_goalId_and_eventId", ["siteId", "goalId", "eventId"])
125
- .index("by_siteId_and_goalId_and_timestamp", ["siteId", "goalId", "timestamp"])
230
+ .index("by_siteId_and_goalId_and_timestamp", [
231
+ "siteId",
232
+ "goalId",
233
+ "timestamp",
234
+ ])
126
235
  .index("by_siteId_and_timestamp", ["siteId", "timestamp"]),
127
236
 
128
237
  funnels: defineTable(funnelFieldsValidator)
@@ -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
- return await ctx.db.insert("sites", {
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
 
@@ -50,7 +50,10 @@ export const ingestContextValidator = v.object({
50
50
  });
51
51
 
52
52
  export const batchedEventValidator = trackerEventValidator.extend(
53
- ingestContextValidator.fields,
53
+ {
54
+ ...ingestContextValidator.fields,
55
+ aggregateCountry: v.optional(v.string()),
56
+ },
54
57
  );
55
58
 
56
59
  export const siteFieldsValidator = v.object({
@@ -123,18 +126,6 @@ export const eventFieldsValidator = v.object({
123
126
  device: v.string(),
124
127
  });
125
128
 
126
- export const storedEventFieldsValidator = eventFieldsValidator
127
- .omit("country", "city", "latitude", "longitude", "browser", "os", "device")
128
- .extend({
129
- country: v.optional(v.string()),
130
- city: v.optional(v.string()),
131
- latitude: v.optional(v.number()),
132
- longitude: v.optional(v.number()),
133
- browser: v.optional(v.string()),
134
- os: v.optional(v.string()),
135
- device: v.optional(v.string()),
136
- });
137
-
138
129
  export const dimensionTypeValidator = v.union(
139
130
  v.literal("source"),
140
131
  v.literal("page"),
@@ -153,6 +144,12 @@ export const dimensionSlotValidator = v.object({
153
144
  revenueCents: v.number(),
154
145
  });
155
146
 
147
+ export const visitorSketchValidator = v.object({
148
+ version: v.literal(1),
149
+ precision: v.number(),
150
+ registers: v.bytes(),
151
+ });
152
+
156
153
  export const aggregateFieldsValidator = v.object({
157
154
  siteId: v.id("sites"),
158
155
  granularity: v.union(v.literal("hour"), v.literal("day")),
@@ -165,6 +162,8 @@ export const aggregateFieldsValidator = v.object({
165
162
  outboundClicks: v.number(),
166
163
  sessions: v.number(),
167
164
  visitors: v.number(),
165
+ visitorSketch: v.optional(visitorSketchValidator),
166
+ visitorSketchComplete: v.optional(v.boolean()),
168
167
  conversions: v.number(),
169
168
  revenueCents: v.number(),
170
169
  dimensions: v.array(dimensionSlotValidator),
package/src/test.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  /// <reference types="vite/client" />
2
- import migrationsComponent from "@convex-dev/migrations/test";
3
2
  import type { TestConvex } from "convex-test";
4
3
  import type { GenericSchema, SchemaDefinition } from "convex/server";
5
4
  import schema from "./component/schema.js";
@@ -16,6 +15,5 @@ export function register(
16
15
  name: string = "rastroAnalytics",
17
16
  ) {
18
17
  t.registerComponent(name, schema, modules);
19
- migrationsComponent.register(t, `${name}/migrations`);
20
18
  }
21
19
  export default { register, schema, modules };
@@ -1,17 +0,0 @@
1
- export declare const migrateLegacyEvents: import("convex/server").RegisteredMutation<"internal", {
2
- fn?: string | undefined;
3
- cursor?: string | null | undefined;
4
- batchSize?: number | undefined;
5
- dryRun?: boolean | undefined;
6
- next?: string[] | undefined;
7
- reset?: boolean | undefined;
8
- oneBatchOnly?: boolean | undefined;
9
- }, Promise<any>>;
10
- export declare const legacyEventsStatus: import("convex/server").RegisteredQuery<"internal", {}, Promise<{
11
- complete: boolean;
12
- sourceEmpty: boolean;
13
- migrationState: "canceled" | "failed" | "inProgress" | "success" | "unknown";
14
- processed: number;
15
- sampleRemainingId: import("convex/values").GenericId<"events"> | null;
16
- }>>;
17
- //# sourceMappingURL=migrations.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/component/migrations.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,mBAAmB;;;;;;;;gBAI9B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;GAiC7B,CAAC"}
@@ -1,44 +0,0 @@
1
- import { Migrations } from "@convex-dev/migrations";
2
- import { v } from "convex/values";
3
- import { components } from "./_generated/api.js";
4
- import { internalMutation, internalQuery } from "./_generated/server.js";
5
- import { migrateLegacyEventToBatch } from "./eventStore.js";
6
- import schema from "./schema.js";
7
- const migrations = new Migrations(components.migrations, {
8
- internalMutation,
9
- schema,
10
- });
11
- export const migrateLegacyEvents = migrations.define({
12
- table: "events",
13
- batchSize: 16,
14
- migrateOne: migrateLegacyEventToBatch,
15
- });
16
- export const legacyEventsStatus = internalQuery({
17
- args: {},
18
- returns: v.object({
19
- complete: v.boolean(),
20
- sourceEmpty: v.boolean(),
21
- migrationState: v.union(v.literal("inProgress"), v.literal("success"), v.literal("failed"), v.literal("canceled"), v.literal("unknown")),
22
- processed: v.number(),
23
- sampleRemainingId: v.union(v.id("events"), v.null()),
24
- }),
25
- handler: async (ctx) => {
26
- const [remaining, statuses] = await Promise.all([
27
- ctx.db.query("events").first(),
28
- migrations.getStatus(ctx, {
29
- migrations: ["migrations:migrateLegacyEvents"],
30
- }),
31
- ]);
32
- const migrationStatus = statuses[0];
33
- const migrationState = migrationStatus?.state ?? "unknown";
34
- const sourceEmpty = remaining === null;
35
- return {
36
- complete: sourceEmpty && migrationState === "success",
37
- sourceEmpty,
38
- migrationState,
39
- processed: migrationStatus?.processed ?? 0,
40
- sampleRemainingId: remaining?._id ?? null,
41
- };
42
- },
43
- });
44
- //# sourceMappingURL=migrations.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/component/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,UAAU,EAAE;IACvD,gBAAgB;IAChB,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC,MAAM,CAAC;IACnD,KAAK,EAAE,QAAQ;IACf,SAAS,EAAE,EAAE;IACb,UAAU,EAAE,yBAAyB;CACtC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC9C,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;QACrB,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;QACxB,cAAc,EAAE,CAAC,CAAC,KAAK,CACrB,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EACvB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EACnB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EACrB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CACrB;QACD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;KACrD,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE;YAC9B,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;gBACxB,UAAU,EAAE,CAAC,gCAAgC,CAAC;aAC/C,CAAC;SACH,CAAC,CAAC;QACH,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,cAAc,GAAG,eAAe,EAAE,KAAK,IAAI,SAAS,CAAC;QAC3D,MAAM,WAAW,GAAG,SAAS,KAAK,IAAI,CAAC;QACvC,OAAO;YACL,QAAQ,EAAE,WAAW,IAAI,cAAc,KAAK,SAAS;YACrD,WAAW;YACX,cAAc;YACd,SAAS,EAAE,eAAe,EAAE,SAAS,IAAI,CAAC;YAC1C,iBAAiB,EAAE,SAAS,EAAE,GAAG,IAAI,IAAI;SAC1C,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -1,52 +0,0 @@
1
- import { Migrations } from "@convex-dev/migrations";
2
- import { v } from "convex/values";
3
- import { components } from "./_generated/api.js";
4
- import { internalMutation, internalQuery } from "./_generated/server.js";
5
- import { migrateLegacyEventToBatch } from "./eventStore.js";
6
- import schema from "./schema.js";
7
-
8
- const migrations = new Migrations(components.migrations, {
9
- internalMutation,
10
- schema,
11
- });
12
-
13
- export const migrateLegacyEvents = migrations.define({
14
- table: "events",
15
- batchSize: 16,
16
- migrateOne: migrateLegacyEventToBatch,
17
- });
18
-
19
- export const legacyEventsStatus = internalQuery({
20
- args: {},
21
- returns: v.object({
22
- complete: v.boolean(),
23
- sourceEmpty: v.boolean(),
24
- migrationState: v.union(
25
- v.literal("inProgress"),
26
- v.literal("success"),
27
- v.literal("failed"),
28
- v.literal("canceled"),
29
- v.literal("unknown"),
30
- ),
31
- processed: v.number(),
32
- sampleRemainingId: v.union(v.id("events"), v.null()),
33
- }),
34
- handler: async (ctx) => {
35
- const [remaining, statuses] = await Promise.all([
36
- ctx.db.query("events").first(),
37
- migrations.getStatus(ctx, {
38
- migrations: ["migrations:migrateLegacyEvents"],
39
- }),
40
- ]);
41
- const migrationStatus = statuses[0];
42
- const migrationState = migrationStatus?.state ?? "unknown";
43
- const sourceEmpty = remaining === null;
44
- return {
45
- complete: sourceEmpty && migrationState === "success",
46
- sourceEmpty,
47
- migrationState,
48
- processed: migrationStatus?.processed ?? 0,
49
- sampleRemainingId: remaining?._id ?? null,
50
- };
51
- },
52
- });