@devwithbobby/loops 0.1.10 → 0.1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,SAAS,KAA2B,CAAC;AAuB3C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,SAAS,KAA2B,CAAC;AAwB3C,eAAe,SAAS,CAAC"}
@@ -9,6 +9,7 @@ component.export(api, {
9
9
  unsubscribeContact: api.lib.unsubscribeContact,
10
10
  resubscribeContact: api.lib.resubscribeContact,
11
11
  countContacts: api.lib.countContacts,
12
+ listContacts: api.lib.listContacts,
12
13
  sendTransactional: api.lib.sendTransactional,
13
14
  sendEvent: api.lib.sendEvent,
14
15
  sendCampaign: api.lib.sendCampaign,
@@ -15,6 +15,12 @@ export declare const logEmailOperation: any;
15
15
  * Can filter by audience criteria (userGroup, source, subscribed status)
16
16
  */
17
17
  export declare const countContacts: any;
18
+ /**
19
+ * List contacts from the database with pagination
20
+ * Can filter by audience criteria (userGroup, source, subscribed status)
21
+ * Returns actual contact data, not just a count
22
+ */
23
+ export declare const listContacts: any;
18
24
  /**
19
25
  * Add or update a contact in Loops
20
26
  * This function tries to create a contact, and if the email already exists (409),
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,YAAY,KA6CvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAexB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAgC5B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,KAwCxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU,KAiHrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAoDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAqD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,KAgCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KA8BxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,YAAY,KAiHvB,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,KAyDtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,KA2DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAyD9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAwC9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe,KAwC1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAgDxB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAsGlC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KA6ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KA6C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,KA8B/B,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,YAAY,KA6CvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAexB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAgC5B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,KAwCxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,YAAY,KA8EvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU,KAiHrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAoDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAqD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,KAgCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KA8BxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,YAAY,KAiHvB,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,KAyDtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,KA2DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAyD9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAwC9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe,KAwC1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAgDxB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAsGlC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KA6ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KA6C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,KA8B/B,CAAC"}
@@ -175,6 +175,86 @@ export const countContacts = zq({
175
175
  return contacts.length;
176
176
  },
177
177
  });
178
+ /**
179
+ * List contacts from the database with pagination
180
+ * Can filter by audience criteria (userGroup, source, subscribed status)
181
+ * Returns actual contact data, not just a count
182
+ */
183
+ export const listContacts = zq({
184
+ args: z.object({
185
+ userGroup: z.string().optional(),
186
+ source: z.string().optional(),
187
+ subscribed: z.boolean().optional(),
188
+ limit: z.number().min(1).max(1000).default(100),
189
+ offset: z.number().min(0).default(0),
190
+ }),
191
+ returns: z.object({
192
+ contacts: z.array(z.object({
193
+ _id: z.string(),
194
+ email: z.string(),
195
+ firstName: z.string().optional(),
196
+ lastName: z.string().optional(),
197
+ userId: z.string().optional(),
198
+ source: z.string().optional(),
199
+ subscribed: z.boolean(),
200
+ userGroup: z.string().optional(),
201
+ loopsContactId: z.string().optional(),
202
+ createdAt: z.number(),
203
+ updatedAt: z.number(),
204
+ })),
205
+ total: z.number(),
206
+ limit: z.number(),
207
+ offset: z.number(),
208
+ hasMore: z.boolean(),
209
+ }),
210
+ handler: async (ctx, args) => {
211
+ let allContacts;
212
+ // Get all contacts matching the filters
213
+ if (args.userGroup !== undefined) {
214
+ allContacts = await ctx.db
215
+ .query("contacts")
216
+ .withIndex("userGroup", (q) => q.eq("userGroup", args.userGroup))
217
+ .collect();
218
+ }
219
+ else if (args.source !== undefined) {
220
+ allContacts = await ctx.db
221
+ .query("contacts")
222
+ .withIndex("source", (q) => q.eq("source", args.source))
223
+ .collect();
224
+ }
225
+ else if (args.subscribed !== undefined) {
226
+ allContacts = await ctx.db
227
+ .query("contacts")
228
+ .withIndex("subscribed", (q) => q.eq("subscribed", args.subscribed))
229
+ .collect();
230
+ }
231
+ else {
232
+ allContacts = await ctx.db.query("contacts").collect();
233
+ }
234
+ // Apply additional filters (for cases where we need to filter by multiple criteria)
235
+ if (args.userGroup !== undefined && allContacts) {
236
+ allContacts = allContacts.filter((c) => c.userGroup === args.userGroup);
237
+ }
238
+ if (args.source !== undefined && allContacts) {
239
+ allContacts = allContacts.filter((c) => c.source === args.source);
240
+ }
241
+ if (args.subscribed !== undefined && allContacts) {
242
+ allContacts = allContacts.filter((c) => c.subscribed === args.subscribed);
243
+ }
244
+ // Sort by createdAt (newest first)
245
+ allContacts.sort((a, b) => b.createdAt - a.createdAt);
246
+ const total = allContacts.length;
247
+ const paginatedContacts = allContacts.slice(args.offset, args.offset + args.limit);
248
+ const hasMore = args.offset + args.limit < total;
249
+ return {
250
+ contacts: paginatedContacts,
251
+ total,
252
+ limit: args.limit,
253
+ offset: args.offset,
254
+ hasMore,
255
+ };
256
+ },
257
+ });
178
258
  /**
179
259
  * Add or update a contact in Loops
180
260
  * This function tries to create a contact, and if the email already exists (409),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devwithbobby/loops",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Convex component for integrating with Loops.so email marketing platform",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -10,6 +10,7 @@ const component = defineComponent("loops");
10
10
  unsubscribeContact: api.lib.unsubscribeContact,
11
11
  resubscribeContact: api.lib.resubscribeContact,
12
12
  countContacts: api.lib.countContacts,
13
+ listContacts: api.lib.listContacts,
13
14
  sendTransactional: api.lib.sendTransactional,
14
15
  sendEvent: api.lib.sendEvent,
15
16
  sendCampaign: api.lib.sendCampaign,
@@ -177,6 +177,91 @@ export const countContacts = zq({
177
177
  },
178
178
  });
179
179
 
180
+ /**
181
+ * List contacts from the database with pagination
182
+ * Can filter by audience criteria (userGroup, source, subscribed status)
183
+ * Returns actual contact data, not just a count
184
+ */
185
+ export const listContacts = zq({
186
+ args: z.object({
187
+ userGroup: z.string().optional(),
188
+ source: z.string().optional(),
189
+ subscribed: z.boolean().optional(),
190
+ limit: z.number().min(1).max(1000).default(100),
191
+ offset: z.number().min(0).default(0),
192
+ }),
193
+ returns: z.object({
194
+ contacts: z.array(
195
+ z.object({
196
+ _id: z.string(),
197
+ email: z.string(),
198
+ firstName: z.string().optional(),
199
+ lastName: z.string().optional(),
200
+ userId: z.string().optional(),
201
+ source: z.string().optional(),
202
+ subscribed: z.boolean(),
203
+ userGroup: z.string().optional(),
204
+ loopsContactId: z.string().optional(),
205
+ createdAt: z.number(),
206
+ updatedAt: z.number(),
207
+ }),
208
+ ),
209
+ total: z.number(),
210
+ limit: z.number(),
211
+ offset: z.number(),
212
+ hasMore: z.boolean(),
213
+ }),
214
+ handler: async (ctx, args) => {
215
+ let allContacts;
216
+
217
+ // Get all contacts matching the filters
218
+ if (args.userGroup !== undefined) {
219
+ allContacts = await ctx.db
220
+ .query("contacts")
221
+ .withIndex("userGroup", (q) => q.eq("userGroup", args.userGroup))
222
+ .collect();
223
+ } else if (args.source !== undefined) {
224
+ allContacts = await ctx.db
225
+ .query("contacts")
226
+ .withIndex("source", (q) => q.eq("source", args.source))
227
+ .collect();
228
+ } else if (args.subscribed !== undefined) {
229
+ allContacts = await ctx.db
230
+ .query("contacts")
231
+ .withIndex("subscribed", (q) => q.eq("subscribed", args.subscribed))
232
+ .collect();
233
+ } else {
234
+ allContacts = await ctx.db.query("contacts").collect();
235
+ }
236
+
237
+ // Apply additional filters (for cases where we need to filter by multiple criteria)
238
+ if (args.userGroup !== undefined && allContacts) {
239
+ allContacts = allContacts.filter((c) => c.userGroup === args.userGroup);
240
+ }
241
+ if (args.source !== undefined && allContacts) {
242
+ allContacts = allContacts.filter((c) => c.source === args.source);
243
+ }
244
+ if (args.subscribed !== undefined && allContacts) {
245
+ allContacts = allContacts.filter((c) => c.subscribed === args.subscribed);
246
+ }
247
+
248
+ // Sort by createdAt (newest first)
249
+ allContacts.sort((a, b) => b.createdAt - a.createdAt);
250
+
251
+ const total = allContacts.length;
252
+ const paginatedContacts = allContacts.slice(args.offset, args.offset + args.limit);
253
+ const hasMore = args.offset + args.limit < total;
254
+
255
+ return {
256
+ contacts: paginatedContacts,
257
+ total,
258
+ limit: args.limit,
259
+ offset: args.offset,
260
+ hasMore,
261
+ };
262
+ },
263
+ });
264
+
180
265
  /**
181
266
  * Add or update a contact in Loops
182
267
  * This function tries to create a contact, and if the email already exists (409),