@devwithbobby/loops 0.1.18 → 0.2.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 (52) hide show
  1. package/dist/client/index.d.ts +133 -103
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +55 -10
  4. package/dist/component/_generated/api.d.ts +228 -0
  5. package/dist/component/_generated/api.d.ts.map +1 -0
  6. package/{src → dist}/component/_generated/api.js +10 -3
  7. package/dist/component/_generated/component.d.ts +266 -0
  8. package/dist/component/_generated/component.d.ts.map +1 -0
  9. package/dist/component/_generated/component.js +9 -0
  10. package/dist/component/_generated/dataModel.d.ts +46 -0
  11. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  12. package/dist/component/_generated/dataModel.js +10 -0
  13. package/{src → dist}/component/_generated/server.d.ts +10 -38
  14. package/dist/component/_generated/server.d.ts.map +1 -0
  15. package/{src → dist}/component/_generated/server.js +9 -22
  16. package/dist/component/aggregates.d.ts +42 -0
  17. package/dist/component/aggregates.d.ts.map +1 -0
  18. package/dist/component/aggregates.js +54 -0
  19. package/dist/component/convex.config.d.ts.map +1 -1
  20. package/dist/component/convex.config.js +2 -22
  21. package/dist/component/helpers.d.ts +1 -1
  22. package/dist/component/helpers.d.ts.map +1 -1
  23. package/dist/component/helpers.js +1 -2
  24. package/dist/component/http.js +1 -1
  25. package/dist/component/lib.d.ts +66 -17
  26. package/dist/component/lib.d.ts.map +1 -1
  27. package/dist/component/lib.js +194 -73
  28. package/dist/component/schema.d.ts +2 -2
  29. package/dist/component/tables/contacts.d.ts.map +1 -1
  30. package/dist/component/tables/emailOperations.d.ts +4 -4
  31. package/dist/component/tables/emailOperations.d.ts.map +1 -1
  32. package/dist/test.d.ts +83 -0
  33. package/dist/test.d.ts.map +1 -0
  34. package/dist/test.js +16 -0
  35. package/dist/types.d.ts +249 -62
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js +4 -2
  38. package/dist/utils.d.ts +6 -6
  39. package/package.json +25 -13
  40. package/src/client/index.ts +69 -18
  41. package/src/component/_generated/api.ts +249 -0
  42. package/src/component/_generated/component.ts +328 -0
  43. package/src/component/_generated/server.ts +161 -0
  44. package/src/component/aggregates.ts +89 -0
  45. package/src/component/convex.config.ts +2 -26
  46. package/src/component/helpers.ts +2 -2
  47. package/src/component/http.ts +1 -1
  48. package/src/component/lib.ts +226 -89
  49. package/src/test.ts +27 -0
  50. package/src/types.ts +20 -122
  51. package/src/component/_generated/api.d.ts +0 -47
  52. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -1,4 +1,3 @@
1
- /* eslint-disable */
2
1
  /**
3
2
  * Generated utilities for implementing server-side Convex query and mutation functions.
4
3
  *
@@ -7,27 +6,8 @@
7
6
  * To regenerate, run `npx convex dev`.
8
7
  * @module
9
8
  */
10
-
11
- import {
12
- ActionBuilder,
13
- AnyComponents,
14
- HttpActionBuilder,
15
- MutationBuilder,
16
- QueryBuilder,
17
- GenericActionCtx,
18
- GenericMutationCtx,
19
- GenericQueryCtx,
20
- GenericDatabaseReader,
21
- GenericDatabaseWriter,
22
- FunctionReference,
23
- } from "convex/server";
9
+ import type { ActionBuilder, HttpActionBuilder, MutationBuilder, QueryBuilder, GenericActionCtx, GenericMutationCtx, GenericQueryCtx, GenericDatabaseReader, GenericDatabaseWriter } from "convex/server";
24
10
  import type { DataModel } from "./dataModel.js";
25
-
26
- type GenericCtx =
27
- | GenericActionCtx<DataModel>
28
- | GenericMutationCtx<DataModel>
29
- | GenericQueryCtx<DataModel>;
30
-
31
11
  /**
32
12
  * Define a query in this Convex app's public API.
33
13
  *
@@ -37,7 +17,6 @@ type GenericCtx =
37
17
  * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
38
18
  */
39
19
  export declare const query: QueryBuilder<DataModel, "public">;
40
-
41
20
  /**
42
21
  * Define a query that is only accessible from other Convex functions (but not from the client).
43
22
  *
@@ -47,7 +26,6 @@ export declare const query: QueryBuilder<DataModel, "public">;
47
26
  * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
48
27
  */
49
28
  export declare const internalQuery: QueryBuilder<DataModel, "internal">;
50
-
51
29
  /**
52
30
  * Define a mutation in this Convex app's public API.
53
31
  *
@@ -57,7 +35,6 @@ export declare const internalQuery: QueryBuilder<DataModel, "internal">;
57
35
  * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
58
36
  */
59
37
  export declare const mutation: MutationBuilder<DataModel, "public">;
60
-
61
38
  /**
62
39
  * Define a mutation that is only accessible from other Convex functions (but not from the client).
63
40
  *
@@ -67,7 +44,6 @@ export declare const mutation: MutationBuilder<DataModel, "public">;
67
44
  * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
68
45
  */
69
46
  export declare const internalMutation: MutationBuilder<DataModel, "internal">;
70
-
71
47
  /**
72
48
  * Define an action in this Convex app's public API.
73
49
  *
@@ -80,7 +56,6 @@ export declare const internalMutation: MutationBuilder<DataModel, "internal">;
80
56
  * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
81
57
  */
82
58
  export declare const action: ActionBuilder<DataModel, "public">;
83
-
84
59
  /**
85
60
  * Define an action that is only accessible from other Convex functions (but not from the client).
86
61
  *
@@ -88,38 +63,36 @@ export declare const action: ActionBuilder<DataModel, "public">;
88
63
  * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
89
64
  */
90
65
  export declare const internalAction: ActionBuilder<DataModel, "internal">;
91
-
92
66
  /**
93
67
  * Define an HTTP action.
94
68
  *
95
- * This function will be used to respond to HTTP requests received by a Convex
96
- * deployment if the requests matches the path and method where this action
97
- * is routed. Be sure to route your action in `convex/http.js`.
69
+ * The wrapped function will be used to respond to HTTP requests received
70
+ * by a Convex deployment if the requests matches the path and method where
71
+ * this action is routed. Be sure to route your httpAction in `convex/http.js`.
98
72
  *
99
- * @param func - The function. It receives an {@link ActionCtx} as its first argument.
73
+ * @param func - The function. It receives an {@link ActionCtx} as its first argument
74
+ * and a Fetch API `Request` object as its second.
100
75
  * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
101
76
  */
102
77
  export declare const httpAction: HttpActionBuilder;
103
-
104
78
  /**
105
79
  * A set of services for use within Convex query functions.
106
80
  *
107
81
  * The query context is passed as the first argument to any Convex query
108
82
  * function run on the server.
109
83
  *
110
- * This differs from the {@link MutationCtx} because all of the services are
111
- * read-only.
84
+ * If you're using code generation, use the `QueryCtx` type in `convex/_generated/server.d.ts` instead.
112
85
  */
113
86
  export type QueryCtx = GenericQueryCtx<DataModel>;
114
-
115
87
  /**
116
88
  * A set of services for use within Convex mutation functions.
117
89
  *
118
90
  * The mutation context is passed as the first argument to any Convex mutation
119
91
  * function run on the server.
92
+ *
93
+ * If you're using code generation, use the `MutationCtx` type in `convex/_generated/server.d.ts` instead.
120
94
  */
121
95
  export type MutationCtx = GenericMutationCtx<DataModel>;
122
-
123
96
  /**
124
97
  * A set of services for use within Convex action functions.
125
98
  *
@@ -127,7 +100,6 @@ export type MutationCtx = GenericMutationCtx<DataModel>;
127
100
  * function run on the server.
128
101
  */
129
102
  export type ActionCtx = GenericActionCtx<DataModel>;
130
-
131
103
  /**
132
104
  * An interface to read from the database within Convex query functions.
133
105
  *
@@ -136,7 +108,6 @@ export type ActionCtx = GenericActionCtx<DataModel>;
136
108
  * building a query.
137
109
  */
138
110
  export type DatabaseReader = GenericDatabaseReader<DataModel>;
139
-
140
111
  /**
141
112
  * An interface to read from and write to the database within Convex mutation
142
113
  * functions.
@@ -147,3 +118,4 @@ export type DatabaseReader = GenericDatabaseReader<DataModel>;
147
118
  * for the guarantees Convex provides your functions.
148
119
  */
149
120
  export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
121
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;AAO/D;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC"}
@@ -7,18 +7,7 @@
7
7
  * To regenerate, run `npx convex dev`.
8
8
  * @module
9
9
  */
10
-
11
- import {
12
- actionGeneric,
13
- httpActionGeneric,
14
- queryGeneric,
15
- mutationGeneric,
16
- internalActionGeneric,
17
- internalMutationGeneric,
18
- internalQueryGeneric,
19
- componentsGeneric,
20
- } from "convex/server";
21
-
10
+ import { actionGeneric, httpActionGeneric, queryGeneric, mutationGeneric, internalActionGeneric, internalMutationGeneric, internalQueryGeneric, } from "convex/server";
22
11
  /**
23
12
  * Define a query in this Convex app's public API.
24
13
  *
@@ -28,7 +17,6 @@ import {
28
17
  * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
29
18
  */
30
19
  export const query = queryGeneric;
31
-
32
20
  /**
33
21
  * Define a query that is only accessible from other Convex functions (but not from the client).
34
22
  *
@@ -38,7 +26,6 @@ export const query = queryGeneric;
38
26
  * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
39
27
  */
40
28
  export const internalQuery = internalQueryGeneric;
41
-
42
29
  /**
43
30
  * Define a mutation in this Convex app's public API.
44
31
  *
@@ -48,7 +35,6 @@ export const internalQuery = internalQueryGeneric;
48
35
  * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
49
36
  */
50
37
  export const mutation = mutationGeneric;
51
-
52
38
  /**
53
39
  * Define a mutation that is only accessible from other Convex functions (but not from the client).
54
40
  *
@@ -58,7 +44,6 @@ export const mutation = mutationGeneric;
58
44
  * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
59
45
  */
60
46
  export const internalMutation = internalMutationGeneric;
61
-
62
47
  /**
63
48
  * Define an action in this Convex app's public API.
64
49
  *
@@ -71,7 +56,6 @@ export const internalMutation = internalMutationGeneric;
71
56
  * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
72
57
  */
73
58
  export const action = actionGeneric;
74
-
75
59
  /**
76
60
  * Define an action that is only accessible from other Convex functions (but not from the client).
77
61
  *
@@ -79,12 +63,15 @@ export const action = actionGeneric;
79
63
  * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
80
64
  */
81
65
  export const internalAction = internalActionGeneric;
82
-
83
66
  /**
84
- * Define a Convex HTTP action.
67
+ * Define an HTTP action.
85
68
  *
86
- * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
87
- * as its second.
88
- * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
69
+ * The wrapped function will be used to respond to HTTP requests received
70
+ * by a Convex deployment if the requests matches the path and method where
71
+ * this action is routed. Be sure to route your httpAction in `convex/http.js`.
72
+ *
73
+ * @param func - The function. It receives an {@link ActionCtx} as its first argument
74
+ * and a Fetch API `Request` object as its second.
75
+ * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
89
76
  */
90
77
  export const httpAction = httpActionGeneric;
@@ -0,0 +1,42 @@
1
+ import { TableAggregate } from "@convex-dev/aggregate";
2
+ import type { GenericMutationCtx, GenericQueryCtx } from "convex/server";
3
+ import type { DataModel, Doc } from "./_generated/dataModel";
4
+ /**
5
+ * Aggregate for counting contacts.
6
+ * Uses userGroup as namespace for efficient filtered counting.
7
+ * Key is null since we only need counts, not ordering.
8
+ */
9
+ export declare const contactAggregate: TableAggregate<{
10
+ Namespace: string | undefined;
11
+ Key: null;
12
+ DataModel: DataModel;
13
+ TableName: "contacts";
14
+ }>;
15
+ type MutationCtx = GenericMutationCtx<DataModel>;
16
+ type QueryCtx = GenericQueryCtx<DataModel>;
17
+ /**
18
+ * Insert a contact into the aggregate
19
+ */
20
+ export declare function aggregateInsert(ctx: MutationCtx, doc: Doc<"contacts">): Promise<void>;
21
+ /**
22
+ * Delete a contact from the aggregate
23
+ */
24
+ export declare function aggregateDelete(ctx: MutationCtx, doc: Doc<"contacts">): Promise<void>;
25
+ /**
26
+ * Replace a contact in the aggregate (when userGroup changes)
27
+ */
28
+ export declare function aggregateReplace(ctx: MutationCtx, oldDoc: Doc<"contacts">, newDoc: Doc<"contacts">): Promise<void>;
29
+ /**
30
+ * Count contacts by userGroup namespace
31
+ */
32
+ export declare function aggregateCountByUserGroup(ctx: QueryCtx, userGroup: string | undefined): Promise<number>;
33
+ /**
34
+ * Count all contacts across all userGroups
35
+ */
36
+ export declare function aggregateCountTotal(ctx: QueryCtx): Promise<number>;
37
+ /**
38
+ * Clear and reinitialize the aggregate (for backfill)
39
+ */
40
+ export declare function aggregateClear(ctx: MutationCtx, namespace?: string): Promise<void>;
41
+ export {};
42
+ //# sourceMappingURL=aggregates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregates.d.ts","sourceRoot":"","sources":["../../src/component/aggregates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAO7D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;eACjB,MAAM,GAAG,SAAS;SACxB,IAAI;eACE,SAAS;eACT,UAAU;EAIpB,CAAC;AAEH,KAAK,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AACjD,KAAK,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAE3C;;GAEG;AACH,wBAAsB,eAAe,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;GAEG;AACH,wBAAsB,eAAe,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,EACvB,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,GACrB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxE;AAED;;GAEG;AACH,wBAAsB,cAAc,CACnC,GAAG,EAAE,WAAW,EAChB,SAAS,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -0,0 +1,54 @@
1
+ import { TableAggregate } from "@convex-dev/aggregate";
2
+ import { components } from "./_generated/api";
3
+ // Cast components to the expected type for the aggregate library
4
+ // biome-ignore lint/suspicious/noExplicitAny: Component API type mismatch with aggregate library
5
+ const contactAggregateComponent = components.contactAggregate;
6
+ /**
7
+ * Aggregate for counting contacts.
8
+ * Uses userGroup as namespace for efficient filtered counting.
9
+ * Key is null since we only need counts, not ordering.
10
+ */
11
+ export const contactAggregate = new TableAggregate(contactAggregateComponent, {
12
+ namespace: (doc) => doc.userGroup,
13
+ sortKey: () => null,
14
+ });
15
+ /**
16
+ * Insert a contact into the aggregate
17
+ */
18
+ export async function aggregateInsert(ctx, doc) {
19
+ await contactAggregate.insertIfDoesNotExist(ctx, doc);
20
+ }
21
+ /**
22
+ * Delete a contact from the aggregate
23
+ */
24
+ export async function aggregateDelete(ctx, doc) {
25
+ await contactAggregate.deleteIfExists(ctx, doc);
26
+ }
27
+ /**
28
+ * Replace a contact in the aggregate (when userGroup changes)
29
+ */
30
+ export async function aggregateReplace(ctx, oldDoc, newDoc) {
31
+ await contactAggregate.replaceOrInsert(ctx, oldDoc, newDoc);
32
+ }
33
+ /**
34
+ * Count contacts by userGroup namespace
35
+ */
36
+ export async function aggregateCountByUserGroup(ctx, userGroup) {
37
+ return await contactAggregate.count(ctx, { namespace: userGroup });
38
+ }
39
+ /**
40
+ * Count all contacts across all userGroups
41
+ */
42
+ export async function aggregateCountTotal(ctx) {
43
+ let total = 0;
44
+ for await (const namespace of contactAggregate.iterNamespaces(ctx)) {
45
+ total += await contactAggregate.count(ctx, { namespace });
46
+ }
47
+ return total;
48
+ }
49
+ /**
50
+ * Clear and reinitialize the aggregate (for backfill)
51
+ */
52
+ export async function aggregateClear(ctx, namespace) {
53
+ await contactAggregate.clear(ctx, { namespace });
54
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,SAAS,kDAA2B,CAAC;AA4B3C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,SAAS,kDAA2B,CAAC;AAI3C,eAAe,SAAS,CAAC"}
@@ -1,25 +1,5 @@
1
+ import aggregate from "@convex-dev/aggregate/convex.config";
1
2
  import { defineComponent } from "convex/server";
2
- import { api } from "./_generated/api";
3
3
  const component = defineComponent("loops");
4
- component.export(api, {
5
- addContact: api.lib.addContact,
6
- updateContact: api.lib.updateContact,
7
- findContact: api.lib.findContact,
8
- batchCreateContacts: api.lib.batchCreateContacts,
9
- unsubscribeContact: api.lib.unsubscribeContact,
10
- resubscribeContact: api.lib.resubscribeContact,
11
- countContacts: api.lib.countContacts,
12
- listContacts: api.lib.listContacts,
13
- sendTransactional: api.lib.sendTransactional,
14
- sendEvent: api.lib.sendEvent,
15
- triggerLoop: api.lib.triggerLoop,
16
- deleteContact: api.lib.deleteContact,
17
- detectRecipientSpam: api.lib.detectRecipientSpam,
18
- detectActorSpam: api.lib.detectActorSpam,
19
- getEmailStats: api.lib.getEmailStats,
20
- detectRapidFirePatterns: api.lib.detectRapidFirePatterns,
21
- checkRecipientRateLimit: api.lib.checkRecipientRateLimit,
22
- checkActorRateLimit: api.lib.checkActorRateLimit,
23
- checkGlobalRateLimit: api.lib.checkGlobalRateLimit,
24
- });
4
+ component.use(aggregate, { name: "contactAggregate" });
25
5
  export default component;
@@ -1,4 +1,4 @@
1
- import { type HeadersInitParam } from "../types";
1
+ import type { HeadersInitParam } from "../types";
2
2
  export declare const LOOPS_API_BASE_URL = "https://app.loops.so/api/v1";
3
3
  export declare const sanitizeLoopsError: (status: number, _errorText: string) => Error;
4
4
  export type LoopsRequestInit = Omit<RequestInit, "body"> & {
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/component/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAWjD,eAAO,MAAM,kBAAkB,gCAAgC,CAAC;AAEhE,eAAO,MAAM,kBAAkB,GAC9B,QAAQ,MAAM,EACd,YAAY,MAAM,KAChB,KAcF,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,UAAU,GACtB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,OAAM,gBAAqB,sBAe3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,gBAAgB,YAQxD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,OAAO,EAAE,OAAO,YAAY,aAM9D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,YAAY,aAKhD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,CAAC,EAAE,SAAS,OAAO,KAAG,OAAO,CAAC,CAAC,CAMjE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,GAAG,IAAI,wBAWpD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,GAAG,IAAI,EAAE,UAAU,MAAM,WAMrE,CAAC;AAEF,eAAO,MAAM,kBAAkB,cAQ9B,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,aAS1C,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/component/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAWjD,eAAO,MAAM,kBAAkB,gCAAgC,CAAC;AAEhE,eAAO,MAAM,kBAAkB,WACtB,MAAM,cACF,MAAM,KAChB,KAcF,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,UAAU,WACd,MAAM,QACR,MAAM,SACN,gBAAgB,sBAetB,CAAC;AAEF,eAAO,MAAM,gBAAgB,WAAY,gBAAgB,YAQxD,CAAC;AAEF,eAAO,MAAM,YAAY,SAAU,OAAO,SAAS,YAAY,aAM9D,CAAC;AAEF,eAAO,MAAM,aAAa,UAAW,YAAY,aAKhD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,CAAC,WAAW,OAAO,KAAG,OAAO,CAAC,CAAC,CAMjE,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAAW,MAAM,GAAG,IAAI,wBAWpD,CAAC;AAEF,eAAO,MAAM,eAAe,UAAW,MAAM,GAAG,IAAI,YAAY,MAAM,WAMrE,CAAC;AAEF,eAAO,MAAM,kBAAkB,cAQ9B,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,OAAO,aAS1C,CAAC"}
@@ -1,4 +1,3 @@
1
- import {} from "../types";
2
1
  const allowedOrigin = process.env.CONVEX_URL ??
3
2
  process.env.NEXT_PUBLIC_CONVEX_URL ??
4
3
  process.env.CONVEX_SITE_URL ??
@@ -58,7 +57,7 @@ export const readJsonBody = async (request) => {
58
57
  try {
59
58
  return (await request.json());
60
59
  }
61
- catch (error) {
60
+ catch (_error) {
62
61
  throw new Error("Invalid JSON body");
63
62
  }
64
63
  };
@@ -82,7 +82,7 @@ http.route({
82
82
  source: url.searchParams.get("source") ?? undefined,
83
83
  subscribed: booleanFromQuery(url.searchParams.get("subscribed")),
84
84
  limit: numberFromQuery(url.searchParams.get("limit"), 100),
85
- offset: numberFromQuery(url.searchParams.get("offset"), 0),
85
+ cursor: url.searchParams.get("cursor") ?? null,
86
86
  });
87
87
  return jsonResponse(data);
88
88
  }
@@ -36,9 +36,15 @@ export declare const logEmailOperation: import("convex/server").RegisteredMutati
36
36
  * Count contacts in the database
37
37
  * Can filter by audience criteria (userGroup, source, subscribed status)
38
38
  *
39
- * Note: When multiple filters are provided, only one index can be used.
40
- * Additional filters are applied in-memory, which is efficient for small result sets.
41
- * For large contact lists with multiple filters, consider using a composite index.
39
+ * For userGroup-only filtering, uses efficient O(log n) aggregate counting.
40
+ * For other filters (source, subscribed), uses indexed queries with a read limit.
41
+ *
42
+ * IMPORTANT: Before using this with existing data, run the backfillContactAggregate
43
+ * mutation to populate the aggregate with existing contacts.
44
+ *
45
+ * NOTE: When filtering by source or subscribed, counts are capped at MAX_COUNT_LIMIT
46
+ * to avoid query read limit errors. For exact counts with large datasets, use
47
+ * userGroup-only filtering which uses efficient aggregate counting.
42
48
  */
43
49
  export declare const countContacts: import("convex/server").RegisteredQuery<"public", {
44
50
  userGroup?: string | undefined;
@@ -46,19 +52,22 @@ export declare const countContacts: import("convex/server").RegisteredQuery<"pub
46
52
  subscribed?: boolean | undefined;
47
53
  }, Promise<number>>;
48
54
  /**
49
- * List contacts from the database with pagination
55
+ * List contacts from the database with cursor-based pagination
50
56
  * Can filter by audience criteria (userGroup, source, subscribed status)
51
57
  * Returns actual contact data, not just a count
52
58
  *
59
+ * Uses cursor-based pagination for efficient querying - only reads documents
60
+ * from the cursor position forward, not all preceding documents.
61
+ *
53
62
  * Note: When multiple filters are provided, only one index can be used.
54
- * Additional filters are applied in-memory before pagination.
63
+ * Additional filters are applied in-memory after fetching.
55
64
  */
56
65
  export declare const listContacts: import("convex/server").RegisteredQuery<"public", {
57
66
  limit: number;
58
- offset: number;
59
67
  userGroup?: string | undefined;
60
68
  source?: string | undefined;
61
69
  subscribed?: boolean | undefined;
70
+ cursor?: string | null | undefined;
62
71
  }, Promise<{
63
72
  contacts: {
64
73
  _id: string;
@@ -73,10 +82,8 @@ export declare const listContacts: import("convex/server").RegisteredQuery<"publ
73
82
  userGroup?: string | undefined;
74
83
  loopsContactId?: string | undefined;
75
84
  }[];
76
- total: number;
77
- limit: number;
78
- offset: number;
79
- hasMore: boolean;
85
+ continueCursor: string | null;
86
+ isDone: boolean;
80
87
  }>>;
81
88
  /**
82
89
  * Add or update a contact in Loops
@@ -242,7 +249,10 @@ export declare const resubscribeContact: import("convex/server").RegisteredActio
242
249
  }>>;
243
250
  /**
244
251
  * Check for spam patterns: too many emails to the same recipient in a time window
245
- * Returns email addresses that received too many emails
252
+ * Returns email addresses that received too many emails.
253
+ *
254
+ * NOTE: Analysis is limited to the most recent MAX_SPAM_DETECTION_LIMIT operations
255
+ * in the time window to avoid query read limit errors.
246
256
  */
247
257
  export declare const detectRecipientSpam: import("convex/server").RegisteredQuery<"public", {
248
258
  timeWindowMs: number;
@@ -255,7 +265,10 @@ export declare const detectRecipientSpam: import("convex/server").RegisteredQuer
255
265
  }[]>>;
256
266
  /**
257
267
  * Check for spam patterns: too many emails from the same actor/user
258
- * Returns actor IDs that sent too many emails
268
+ * Returns actor IDs that sent too many emails.
269
+ *
270
+ * NOTE: Analysis is limited to the most recent MAX_SPAM_DETECTION_LIMIT operations
271
+ * in the time window to avoid query read limit errors.
259
272
  */
260
273
  export declare const detectActorSpam: import("convex/server").RegisteredQuery<"public", {
261
274
  timeWindowMs: number;
@@ -266,7 +279,11 @@ export declare const detectActorSpam: import("convex/server").RegisteredQuery<"p
266
279
  timeWindowMs: number;
267
280
  }[]>>;
268
281
  /**
269
- * Get recent email operation statistics for monitoring
282
+ * Get recent email operation statistics for monitoring.
283
+ *
284
+ * NOTE: Statistics are calculated from the most recent MAX_SPAM_DETECTION_LIMIT
285
+ * operations in the time window to avoid query read limit errors. For high-volume
286
+ * applications, consider using scheduled jobs with pagination for exact statistics.
270
287
  */
271
288
  export declare const getEmailStats: import("convex/server").RegisteredQuery<"public", {
272
289
  timeWindowMs: number;
@@ -281,7 +298,10 @@ export declare const getEmailStats: import("convex/server").RegisteredQuery<"pub
281
298
  }>>;
282
299
  /**
283
300
  * Detect rapid-fire email sending patterns (multiple emails sent in quick succession)
284
- * Returns suspicious patterns indicating potential spam
301
+ * Returns suspicious patterns indicating potential spam.
302
+ *
303
+ * NOTE: Analysis is limited to the most recent MAX_SPAM_DETECTION_LIMIT operations
304
+ * in the time window to avoid query read limit errors.
285
305
  */
286
306
  export declare const detectRapidFirePatterns: import("convex/server").RegisteredQuery<"public", {
287
307
  timeWindowMs: number;
@@ -296,7 +316,10 @@ export declare const detectRapidFirePatterns: import("convex/server").Registered
296
316
  }[]>>;
297
317
  /**
298
318
  * Rate limiting: Check if an email can be sent to a recipient
299
- * Based on recent email operations in the database
319
+ * Based on recent email operations in the database.
320
+ *
321
+ * Uses efficient .take() query - only reads the minimum number of documents
322
+ * needed to determine if the rate limit is exceeded.
300
323
  */
301
324
  export declare const checkRecipientRateLimit: import("convex/server").RegisteredQuery<"public", {
302
325
  email: string;
@@ -312,7 +335,10 @@ export declare const checkRecipientRateLimit: import("convex/server").Registered
312
335
  }>>;
313
336
  /**
314
337
  * Rate limiting: Check if an actor/user can send more emails
315
- * Based on recent email operations in the database
338
+ * Based on recent email operations in the database.
339
+ *
340
+ * Uses efficient .take() query - only reads the minimum number of documents
341
+ * needed to determine if the rate limit is exceeded.
316
342
  */
317
343
  export declare const checkActorRateLimit: import("convex/server").RegisteredQuery<"public", {
318
344
  actorId: string;
@@ -327,7 +353,10 @@ export declare const checkActorRateLimit: import("convex/server").RegisteredQuer
327
353
  }>>;
328
354
  /**
329
355
  * Rate limiting: Check global email sending rate
330
- * Checks total email operations across all senders
356
+ * Checks total email operations across all senders.
357
+ *
358
+ * Uses efficient .take() query - only reads the minimum number of documents
359
+ * needed to determine if the rate limit is exceeded.
331
360
  */
332
361
  export declare const checkGlobalRateLimit: import("convex/server").RegisteredQuery<"public", {
333
362
  timeWindowMs: number;
@@ -338,4 +367,24 @@ export declare const checkGlobalRateLimit: import("convex/server").RegisteredQue
338
367
  limit: number;
339
368
  timeWindowMs: number;
340
369
  }>>;
370
+ /**
371
+ * Backfill the contact aggregate with existing contacts.
372
+ * Run this mutation after upgrading to a version with aggregate support.
373
+ *
374
+ * This processes contacts in batches to avoid timeout issues with large datasets.
375
+ * Call repeatedly with the returned cursor until isDone is true.
376
+ *
377
+ * Usage:
378
+ * 1. First call with clear: true to reset the aggregate
379
+ * 2. Subsequent calls with the returned cursor until isDone is true
380
+ */
381
+ export declare const backfillContactAggregate: import("convex/server").RegisteredMutation<"public", {
382
+ batchSize: number;
383
+ cursor?: string | null | undefined;
384
+ clear?: boolean | undefined;
385
+ }, Promise<{
386
+ processed: number;
387
+ cursor: string | null;
388
+ isDone: boolean;
389
+ }>>;
341
390
  //# sourceMappingURL=lib.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;iBA6CvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;iBAexB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;iBAkC5B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;mBA0DxB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;GA8FvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;GA+GrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;GAgDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;GAiD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;GAyCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;GA0BxB,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW;;;;;;;;;GA4DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;GA4DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;GA8D9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;GA2B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;GA2B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;KA8C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;KA4C1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;GAkDxB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;KAsGlC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;GA+ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;GA6C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;GA8B/B,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;iBA2DvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;iBAiBxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;iBAkC5B,CAAC;AAUH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;mBA8DxB,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;GAgGvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;GA+GrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;GAgDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;GAiD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;GAyCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;GA0BxB,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW;;;;;;;;;GA4DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;GA4DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;GA8D9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;GA2B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;GA2B7B,CAAC;AASH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;KA8C9B,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;KA4C1B,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;GAkDxB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;KAsGlC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;GAiDlC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;GA+C9B,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;GAiC/B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;GAsCnC,CAAC"}