@devwithbobby/loops 0.1.17 → 0.1.19
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 +46 -23
- package/dist/client/index.d.ts +105 -85
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +26 -7
- package/dist/component/_generated/api.d.ts +44 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/{src → dist}/component/_generated/api.js +10 -3
- package/dist/component/_generated/component.d.ts +259 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +9 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +10 -0
- package/{src → dist}/component/_generated/server.d.ts +10 -38
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/{src → dist}/component/_generated/server.js +9 -22
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/convex.config.js +0 -22
- package/dist/component/helpers.d.ts +1 -1
- package/dist/component/helpers.d.ts.map +1 -1
- package/dist/component/helpers.js +1 -2
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +0 -1
- package/dist/component/lib.d.ts +7 -0
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +62 -20
- package/dist/component/schema.d.ts +2 -2
- package/dist/component/tables/contacts.d.ts.map +1 -1
- package/dist/component/tables/emailOperations.d.ts +4 -4
- package/dist/component/tables/emailOperations.d.ts.map +1 -1
- package/dist/test.d.ts +83 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +16 -0
- package/dist/utils.d.ts +6 -6
- package/package.json +15 -9
- package/src/client/index.ts +31 -14
- package/src/component/_generated/api.ts +60 -0
- package/src/component/_generated/component.ts +323 -0
- package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +1 -1
- package/src/component/_generated/server.ts +161 -0
- package/src/component/convex.config.ts +0 -27
- package/src/component/helpers.ts +2 -2
- package/src/component/http.ts +0 -4
- package/src/component/lib.ts +69 -20
- package/src/test.ts +27 -0
- package/dist/client/types.d.ts +0 -24
- package/dist/client/types.d.ts.map +0 -1
- package/dist/client/types.js +0 -0
- package/src/component/_generated/api.d.ts +0 -47
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ A Convex component for integrating with [Loops.so](https://loops.so) email marke
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
9
|
+
- **Contact Management** - Create, update, find, list, and delete contacts
|
|
10
|
+
- **Transactional Emails** - Send one-off emails with templates
|
|
11
|
+
- **Events** - Trigger email workflows based on events
|
|
12
|
+
- **Loops** - Trigger automated email sequences
|
|
13
|
+
- **Monitoring** - Track all email operations with spam detection
|
|
14
|
+
- **Rate Limiting** - Built-in rate limiting queries for abuse prevention
|
|
15
|
+
- **Type-Safe** - Full TypeScript support with Zod validation
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -40,17 +40,17 @@ export default app;
|
|
|
40
40
|
|
|
41
41
|
### 2. Set Up Environment Variables
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
**IMPORTANT: Set your Loops API key before using the component.**
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
npx convex env set LOOPS_API_KEY "your-loops-api-key-here"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
**Or via Convex Dashboard:**
|
|
50
|
-
1. Go to Settings
|
|
50
|
+
1. Go to Settings -> Environment Variables
|
|
51
51
|
2. Add `LOOPS_API_KEY` with your Loops.so API key
|
|
52
52
|
|
|
53
|
-
Get your API key from [Loops.so Dashboard](https://app.loops.so/settings
|
|
53
|
+
Get your API key from [Loops.so Dashboard](https://app.loops.so/settings?page=api).
|
|
54
54
|
|
|
55
55
|
### 3. Use the Component
|
|
56
56
|
|
|
@@ -135,6 +135,27 @@ await loops.updateContact(ctx, "user@example.com", {
|
|
|
135
135
|
const contact = await loops.findContact(ctx, "user@example.com");
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
+
#### List Contacts
|
|
139
|
+
|
|
140
|
+
List contacts with pagination and optional filtering.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Simple list with default limit (100)
|
|
144
|
+
const result = await loops.listContacts(ctx);
|
|
145
|
+
|
|
146
|
+
// List with filters and pagination
|
|
147
|
+
const result = await loops.listContacts(ctx, {
|
|
148
|
+
userGroup: "premium",
|
|
149
|
+
subscribed: true,
|
|
150
|
+
limit: 20,
|
|
151
|
+
offset: 0
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
console.log(result.contacts); // Array of contacts
|
|
155
|
+
console.log(result.total); // Total count matching filters
|
|
156
|
+
console.log(result.hasMore); // Boolean indicating if more pages exist
|
|
157
|
+
```
|
|
158
|
+
|
|
138
159
|
#### Delete Contact
|
|
139
160
|
|
|
140
161
|
```typescript
|
|
@@ -335,11 +356,12 @@ export const {
|
|
|
335
356
|
sendEvent,
|
|
336
357
|
triggerLoop,
|
|
337
358
|
countContacts,
|
|
359
|
+
listContacts,
|
|
338
360
|
// ... all other functions
|
|
339
361
|
} = loops.api();
|
|
340
362
|
```
|
|
341
363
|
|
|
342
|
-
|
|
364
|
+
**Security Warning:** The `api()` helper exports functions without authentication. Always wrap these functions with auth checks in production:
|
|
343
365
|
|
|
344
366
|
```typescript
|
|
345
367
|
export const addContact = action({
|
|
@@ -392,12 +414,12 @@ npx convex env set LOOPS_API_KEY "your-api-key"
|
|
|
392
414
|
|
|
393
415
|
**Via Dashboard:**
|
|
394
416
|
1. Go to your Convex Dashboard
|
|
395
|
-
2. Navigate to Settings
|
|
417
|
+
2. Navigate to Settings -> Environment Variables
|
|
396
418
|
3. Add `LOOPS_API_KEY` with your Loops.so API key value
|
|
397
419
|
|
|
398
|
-
Get your API key from [Loops.so Dashboard](https://app.loops.so/settings
|
|
420
|
+
Get your API key from [Loops.so Dashboard](https://app.loops.so/settings?page=api).
|
|
399
421
|
|
|
400
|
-
|
|
422
|
+
**Never** pass the API key directly in code or via function options in production. Always use environment variables.
|
|
401
423
|
|
|
402
424
|
## Monitoring & Rate Limiting
|
|
403
425
|
|
|
@@ -458,15 +480,16 @@ example/ # Example app
|
|
|
458
480
|
|
|
459
481
|
This component implements the following Loops.so API endpoints:
|
|
460
482
|
|
|
461
|
-
-
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
-
|
|
468
|
-
-
|
|
469
|
-
-
|
|
483
|
+
- Create/Update Contact
|
|
484
|
+
- Delete Contact
|
|
485
|
+
- Find Contact
|
|
486
|
+
- Batch Create Contacts
|
|
487
|
+
- Unsubscribe/Resubscribe Contact
|
|
488
|
+
- Count Contacts (custom implementation)
|
|
489
|
+
- List Contacts (custom implementation)
|
|
490
|
+
- Send Transactional Email
|
|
491
|
+
- Send Event
|
|
492
|
+
- Trigger Loop
|
|
470
493
|
|
|
471
494
|
## Contributing
|
|
472
495
|
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { RunActionCtx, RunQueryCtx
|
|
3
|
-
export type LoopsComponent =
|
|
1
|
+
import type { ComponentApi } from "../component/_generated/component.js";
|
|
2
|
+
import type { RunActionCtx, RunQueryCtx } from "../types";
|
|
3
|
+
export type LoopsComponent = ComponentApi;
|
|
4
4
|
export interface ContactData {
|
|
5
5
|
email: string;
|
|
6
6
|
firstName?: string;
|
|
@@ -25,16 +25,21 @@ export declare class Loops {
|
|
|
25
25
|
apiKey?: string;
|
|
26
26
|
};
|
|
27
27
|
private readonly lib;
|
|
28
|
+
private _apiKey?;
|
|
28
29
|
constructor(component: LoopsComponent, options?: {
|
|
29
30
|
apiKey?: string;
|
|
30
31
|
});
|
|
31
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Get the API key, checking environment at call time (not constructor time).
|
|
34
|
+
* This allows the Loops client to be instantiated at module load time.
|
|
35
|
+
*/
|
|
36
|
+
private get apiKey();
|
|
32
37
|
/**
|
|
33
38
|
* Add or update a contact in Loops
|
|
34
39
|
*/
|
|
35
40
|
addContact(ctx: RunActionCtx, contact: ContactData): Promise<{
|
|
41
|
+
id?: string;
|
|
36
42
|
success: boolean;
|
|
37
|
-
id?: string | undefined;
|
|
38
43
|
}>;
|
|
39
44
|
/**
|
|
40
45
|
* Update an existing contact in Loops
|
|
@@ -48,8 +53,8 @@ export declare class Loops {
|
|
|
48
53
|
* Send a transactional email using a transactional ID
|
|
49
54
|
*/
|
|
50
55
|
sendTransactional(ctx: RunActionCtx, options: TransactionalEmailOptions): Promise<{
|
|
56
|
+
messageId?: string;
|
|
51
57
|
success: boolean;
|
|
52
|
-
messageId?: string | undefined;
|
|
53
58
|
}>;
|
|
54
59
|
/**
|
|
55
60
|
* Send an event to Loops to trigger email workflows
|
|
@@ -62,32 +67,32 @@ export declare class Loops {
|
|
|
62
67
|
* Retrieves contact information from Loops
|
|
63
68
|
*/
|
|
64
69
|
findContact(ctx: RunActionCtx, email: string): Promise<{
|
|
65
|
-
success: boolean;
|
|
66
70
|
contact?: {
|
|
67
|
-
|
|
68
|
-
email?: string | null
|
|
69
|
-
firstName?: string | null
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
71
|
+
createdAt?: string | null;
|
|
72
|
+
email?: string | null;
|
|
73
|
+
firstName?: string | null;
|
|
74
|
+
id?: string | null;
|
|
75
|
+
lastName?: string | null;
|
|
76
|
+
source?: string | null;
|
|
77
|
+
subscribed?: boolean | null;
|
|
78
|
+
userGroup?: string | null;
|
|
79
|
+
userId?: string | null;
|
|
80
|
+
};
|
|
81
|
+
success: boolean;
|
|
77
82
|
}>;
|
|
78
83
|
/**
|
|
79
84
|
* Batch create contacts
|
|
80
85
|
* Create multiple contacts in a single API call
|
|
81
86
|
*/
|
|
82
87
|
batchCreateContacts(ctx: RunActionCtx, contacts: ContactData[]): Promise<{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
results?: {
|
|
88
|
+
created?: number;
|
|
89
|
+
failed?: number;
|
|
90
|
+
results?: Array<{
|
|
87
91
|
email: string;
|
|
92
|
+
error?: string;
|
|
88
93
|
success: boolean;
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
}>;
|
|
95
|
+
success: boolean;
|
|
91
96
|
}>;
|
|
92
97
|
/**
|
|
93
98
|
* Unsubscribe a contact
|
|
@@ -125,23 +130,23 @@ export declare class Loops {
|
|
|
125
130
|
limit?: number;
|
|
126
131
|
offset?: number;
|
|
127
132
|
}): Promise<{
|
|
128
|
-
contacts: {
|
|
133
|
+
contacts: Array<{
|
|
129
134
|
_id: string;
|
|
135
|
+
createdAt: number;
|
|
130
136
|
email: string;
|
|
137
|
+
firstName?: string;
|
|
138
|
+
lastName?: string;
|
|
139
|
+
loopsContactId?: string;
|
|
140
|
+
source?: string;
|
|
131
141
|
subscribed: boolean;
|
|
132
|
-
createdAt: number;
|
|
133
142
|
updatedAt: number;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
userGroup?: string | undefined;
|
|
139
|
-
loopsContactId?: string | undefined;
|
|
140
|
-
}[];
|
|
141
|
-
total: number;
|
|
143
|
+
userGroup?: string;
|
|
144
|
+
userId?: string;
|
|
145
|
+
}>;
|
|
146
|
+
hasMore: boolean;
|
|
142
147
|
limit: number;
|
|
143
148
|
offset: number;
|
|
144
|
-
|
|
149
|
+
total: number;
|
|
145
150
|
}>;
|
|
146
151
|
/**
|
|
147
152
|
* Detect spam patterns: emails sent to the same recipient too frequently
|
|
@@ -150,9 +155,8 @@ export declare class Loops {
|
|
|
150
155
|
timeWindowMs?: number;
|
|
151
156
|
maxEmailsPerRecipient?: number;
|
|
152
157
|
}): Promise<{
|
|
153
|
-
[x: string]: any;
|
|
154
|
-
email: string;
|
|
155
158
|
count: number;
|
|
159
|
+
email: string;
|
|
156
160
|
timeWindowMs: number;
|
|
157
161
|
}[]>;
|
|
158
162
|
/**
|
|
@@ -172,15 +176,12 @@ export declare class Loops {
|
|
|
172
176
|
getEmailStats(ctx: RunQueryCtx, options?: {
|
|
173
177
|
timeWindowMs?: number;
|
|
174
178
|
}): Promise<{
|
|
175
|
-
[x: string]: any;
|
|
176
|
-
totalOperations: number;
|
|
177
|
-
successfulOperations: number;
|
|
178
179
|
failedOperations: number;
|
|
179
|
-
operationsByType:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
uniqueRecipients: number;
|
|
180
|
+
operationsByType: Record<string, number>;
|
|
181
|
+
successfulOperations: number;
|
|
182
|
+
totalOperations: number;
|
|
183
183
|
uniqueActors: number;
|
|
184
|
+
uniqueRecipients: number;
|
|
184
185
|
}>;
|
|
185
186
|
/**
|
|
186
187
|
* Detect rapid-fire email sending patterns
|
|
@@ -189,12 +190,12 @@ export declare class Loops {
|
|
|
189
190
|
timeWindowMs?: number;
|
|
190
191
|
minEmailsInWindow?: number;
|
|
191
192
|
}): Promise<{
|
|
193
|
+
actorId?: string;
|
|
192
194
|
count: number;
|
|
193
|
-
|
|
195
|
+
email?: string;
|
|
194
196
|
firstTimestamp: number;
|
|
195
197
|
lastTimestamp: number;
|
|
196
|
-
|
|
197
|
-
actorId?: string | undefined;
|
|
198
|
+
timeWindowMs: number;
|
|
198
199
|
}[]>;
|
|
199
200
|
/**
|
|
200
201
|
* Check if an email can be sent to a recipient based on rate limits
|
|
@@ -204,12 +205,11 @@ export declare class Loops {
|
|
|
204
205
|
timeWindowMs: number;
|
|
205
206
|
maxEmails: number;
|
|
206
207
|
}): Promise<{
|
|
207
|
-
[x: string]: any;
|
|
208
208
|
allowed: boolean;
|
|
209
209
|
count: number;
|
|
210
210
|
limit: number;
|
|
211
|
+
retryAfter?: number;
|
|
211
212
|
timeWindowMs: number;
|
|
212
|
-
retryAfter?: number | undefined;
|
|
213
213
|
}>;
|
|
214
214
|
/**
|
|
215
215
|
* Check if an actor/user can send more emails based on rate limits
|
|
@@ -222,8 +222,8 @@ export declare class Loops {
|
|
|
222
222
|
allowed: boolean;
|
|
223
223
|
count: number;
|
|
224
224
|
limit: number;
|
|
225
|
+
retryAfter?: number;
|
|
225
226
|
timeWindowMs: number;
|
|
226
|
-
retryAfter?: number | undefined;
|
|
227
227
|
}>;
|
|
228
228
|
/**
|
|
229
229
|
* Check global email sending rate limit
|
|
@@ -260,7 +260,7 @@ export declare class Loops {
|
|
|
260
260
|
eventName?: string;
|
|
261
261
|
}): Promise<{
|
|
262
262
|
success: boolean;
|
|
263
|
-
warning?: string
|
|
263
|
+
warning?: string;
|
|
264
264
|
}>;
|
|
265
265
|
/**
|
|
266
266
|
* For easy re-exporting.
|
|
@@ -279,8 +279,8 @@ export declare class Loops {
|
|
|
279
279
|
userGroup?: string | undefined;
|
|
280
280
|
email: string;
|
|
281
281
|
}, Promise<{
|
|
282
|
+
id?: string;
|
|
282
283
|
success: boolean;
|
|
283
|
-
id?: string | undefined;
|
|
284
284
|
}>>;
|
|
285
285
|
updateContact: import("convex/server").RegisteredAction<"public", {
|
|
286
286
|
firstName?: string | undefined;
|
|
@@ -299,8 +299,8 @@ export declare class Loops {
|
|
|
299
299
|
email: string;
|
|
300
300
|
transactionalId: string;
|
|
301
301
|
}, Promise<{
|
|
302
|
+
messageId?: string;
|
|
302
303
|
success: boolean;
|
|
303
|
-
messageId?: string | undefined;
|
|
304
304
|
}>>;
|
|
305
305
|
sendEvent: import("convex/server").RegisteredAction<"public", {
|
|
306
306
|
eventProperties?: any;
|
|
@@ -320,23 +320,23 @@ export declare class Loops {
|
|
|
320
320
|
loopId: string;
|
|
321
321
|
}, Promise<{
|
|
322
322
|
success: boolean;
|
|
323
|
-
warning?: string
|
|
323
|
+
warning?: string;
|
|
324
324
|
}>>;
|
|
325
325
|
findContact: import("convex/server").RegisteredAction<"public", {
|
|
326
326
|
email: string;
|
|
327
327
|
}, Promise<{
|
|
328
|
-
success: boolean;
|
|
329
328
|
contact?: {
|
|
330
|
-
|
|
331
|
-
email?: string | null
|
|
332
|
-
firstName?: string | null
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
329
|
+
createdAt?: string | null;
|
|
330
|
+
email?: string | null;
|
|
331
|
+
firstName?: string | null;
|
|
332
|
+
id?: string | null;
|
|
333
|
+
lastName?: string | null;
|
|
334
|
+
source?: string | null;
|
|
335
|
+
subscribed?: boolean | null;
|
|
336
|
+
userGroup?: string | null;
|
|
337
|
+
userId?: string | null;
|
|
338
|
+
};
|
|
339
|
+
success: boolean;
|
|
340
340
|
}>>;
|
|
341
341
|
batchCreateContacts: import("convex/server").RegisteredAction<"public", {
|
|
342
342
|
contacts: {
|
|
@@ -349,14 +349,14 @@ export declare class Loops {
|
|
|
349
349
|
email: string;
|
|
350
350
|
}[];
|
|
351
351
|
}, Promise<{
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
results?: {
|
|
352
|
+
created?: number;
|
|
353
|
+
failed?: number;
|
|
354
|
+
results?: Array<{
|
|
356
355
|
email: string;
|
|
356
|
+
error?: string;
|
|
357
357
|
success: boolean;
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
}>;
|
|
359
|
+
success: boolean;
|
|
360
360
|
}>>;
|
|
361
361
|
unsubscribeContact: import("convex/server").RegisteredAction<"public", {
|
|
362
362
|
email: string;
|
|
@@ -373,13 +373,37 @@ export declare class Loops {
|
|
|
373
373
|
subscribed?: boolean | undefined;
|
|
374
374
|
userGroup?: string | undefined;
|
|
375
375
|
}, Promise<number>>;
|
|
376
|
+
listContacts: import("convex/server").RegisteredQuery<"public", {
|
|
377
|
+
source?: string | undefined;
|
|
378
|
+
subscribed?: boolean | undefined;
|
|
379
|
+
userGroup?: string | undefined;
|
|
380
|
+
limit?: number | undefined;
|
|
381
|
+
offset?: number | undefined;
|
|
382
|
+
}, Promise<{
|
|
383
|
+
contacts: Array<{
|
|
384
|
+
_id: string;
|
|
385
|
+
createdAt: number;
|
|
386
|
+
email: string;
|
|
387
|
+
firstName?: string;
|
|
388
|
+
lastName?: string;
|
|
389
|
+
loopsContactId?: string;
|
|
390
|
+
source?: string;
|
|
391
|
+
subscribed: boolean;
|
|
392
|
+
updatedAt: number;
|
|
393
|
+
userGroup?: string;
|
|
394
|
+
userId?: string;
|
|
395
|
+
}>;
|
|
396
|
+
hasMore: boolean;
|
|
397
|
+
limit: number;
|
|
398
|
+
offset: number;
|
|
399
|
+
total: number;
|
|
400
|
+
}>>;
|
|
376
401
|
detectRecipientSpam: import("convex/server").RegisteredQuery<"public", {
|
|
377
402
|
timeWindowMs?: number | undefined;
|
|
378
403
|
maxEmailsPerRecipient?: number | undefined;
|
|
379
404
|
}, Promise<{
|
|
380
|
-
[x: string]: any;
|
|
381
|
-
email: string;
|
|
382
405
|
count: number;
|
|
406
|
+
email: string;
|
|
383
407
|
timeWindowMs: number;
|
|
384
408
|
}[]>>;
|
|
385
409
|
detectActorSpam: import("convex/server").RegisteredQuery<"public", {
|
|
@@ -393,38 +417,34 @@ export declare class Loops {
|
|
|
393
417
|
getEmailStats: import("convex/server").RegisteredQuery<"public", {
|
|
394
418
|
timeWindowMs?: number | undefined;
|
|
395
419
|
}, Promise<{
|
|
396
|
-
[x: string]: any;
|
|
397
|
-
totalOperations: number;
|
|
398
|
-
successfulOperations: number;
|
|
399
420
|
failedOperations: number;
|
|
400
|
-
operationsByType:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
uniqueRecipients: number;
|
|
421
|
+
operationsByType: Record<string, number>;
|
|
422
|
+
successfulOperations: number;
|
|
423
|
+
totalOperations: number;
|
|
404
424
|
uniqueActors: number;
|
|
425
|
+
uniqueRecipients: number;
|
|
405
426
|
}>>;
|
|
406
427
|
detectRapidFirePatterns: import("convex/server").RegisteredQuery<"public", {
|
|
407
428
|
timeWindowMs?: number | undefined;
|
|
408
429
|
minEmailsInWindow?: number | undefined;
|
|
409
430
|
}, Promise<{
|
|
431
|
+
actorId?: string;
|
|
410
432
|
count: number;
|
|
411
|
-
|
|
433
|
+
email?: string;
|
|
412
434
|
firstTimestamp: number;
|
|
413
435
|
lastTimestamp: number;
|
|
414
|
-
|
|
415
|
-
actorId?: string | undefined;
|
|
436
|
+
timeWindowMs: number;
|
|
416
437
|
}[]>>;
|
|
417
438
|
checkRecipientRateLimit: import("convex/server").RegisteredQuery<"public", {
|
|
418
439
|
email: string;
|
|
419
440
|
timeWindowMs: number;
|
|
420
441
|
maxEmails: number;
|
|
421
442
|
}, Promise<{
|
|
422
|
-
[x: string]: any;
|
|
423
443
|
allowed: boolean;
|
|
424
444
|
count: number;
|
|
425
445
|
limit: number;
|
|
446
|
+
retryAfter?: number;
|
|
426
447
|
timeWindowMs: number;
|
|
427
|
-
retryAfter?: number | undefined;
|
|
428
448
|
}>>;
|
|
429
449
|
checkActorRateLimit: import("convex/server").RegisteredQuery<"public", {
|
|
430
450
|
actorId: string;
|
|
@@ -434,8 +454,8 @@ export declare class Loops {
|
|
|
434
454
|
allowed: boolean;
|
|
435
455
|
count: number;
|
|
436
456
|
limit: number;
|
|
457
|
+
retryAfter?: number;
|
|
437
458
|
timeWindowMs: number;
|
|
438
|
-
retryAfter?: number | undefined;
|
|
439
459
|
}>>;
|
|
440
460
|
checkGlobalRateLimit: import("convex/server").RegisteredQuery<"public", {
|
|
441
461
|
timeWindowMs: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
package/dist/client/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { v } from "convex/values";
|
|
|
3
3
|
export class Loops {
|
|
4
4
|
options;
|
|
5
5
|
lib;
|
|
6
|
+
_apiKey;
|
|
6
7
|
constructor(component, options) {
|
|
7
8
|
if (!component) {
|
|
8
9
|
throw new Error("Loops component reference is required. " +
|
|
@@ -17,18 +18,24 @@ export class Loops {
|
|
|
17
18
|
}
|
|
18
19
|
this.lib = component.lib;
|
|
19
20
|
this.options = options;
|
|
20
|
-
|
|
21
|
-
if (!apiKey) {
|
|
22
|
-
throw new Error("Loops API key is required. Set LOOPS_API_KEY in your Convex environment variables.");
|
|
23
|
-
}
|
|
21
|
+
this._apiKey = options?.apiKey;
|
|
24
22
|
if (options?.apiKey) {
|
|
25
23
|
console.warn("API key passed directly via options. " +
|
|
26
24
|
"For security, use LOOPS_API_KEY environment variable instead. " +
|
|
27
|
-
"See
|
|
25
|
+
"See README.md for details.");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the API key, checking environment at call time (not constructor time).
|
|
30
|
+
* This allows the Loops client to be instantiated at module load time.
|
|
31
|
+
*/
|
|
32
|
+
get apiKey() {
|
|
33
|
+
const key = this._apiKey ?? process.env.LOOPS_API_KEY;
|
|
34
|
+
if (!key) {
|
|
35
|
+
throw new Error("Loops API key is required. Set LOOPS_API_KEY in your Convex environment variables.");
|
|
28
36
|
}
|
|
29
|
-
|
|
37
|
+
return key;
|
|
30
38
|
}
|
|
31
|
-
apiKey;
|
|
32
39
|
/**
|
|
33
40
|
* Add or update a contact in Loops
|
|
34
41
|
*/
|
|
@@ -333,6 +340,18 @@ export class Loops {
|
|
|
333
340
|
return await this.countContacts(ctx, args);
|
|
334
341
|
},
|
|
335
342
|
}),
|
|
343
|
+
listContacts: queryGeneric({
|
|
344
|
+
args: {
|
|
345
|
+
userGroup: v.optional(v.string()),
|
|
346
|
+
source: v.optional(v.string()),
|
|
347
|
+
subscribed: v.optional(v.boolean()),
|
|
348
|
+
limit: v.optional(v.number()),
|
|
349
|
+
offset: v.optional(v.number()),
|
|
350
|
+
},
|
|
351
|
+
handler: async (ctx, args) => {
|
|
352
|
+
return await this.listContacts(ctx, args);
|
|
353
|
+
},
|
|
354
|
+
}),
|
|
336
355
|
detectRecipientSpam: queryGeneric({
|
|
337
356
|
args: {
|
|
338
357
|
timeWindowMs: v.optional(v.number()),
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated `api` utility.
|
|
3
|
+
*
|
|
4
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
5
|
+
*
|
|
6
|
+
* To regenerate, run `npx convex dev`.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type * as helpers from "../helpers.js";
|
|
10
|
+
import type * as http from "../http.js";
|
|
11
|
+
import type * as lib from "../lib.js";
|
|
12
|
+
import type * as tables_contacts from "../tables/contacts.js";
|
|
13
|
+
import type * as tables_emailOperations from "../tables/emailOperations.js";
|
|
14
|
+
import type * as validators from "../validators.js";
|
|
15
|
+
import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
|
|
16
|
+
declare const fullApi: ApiFromModules<{
|
|
17
|
+
helpers: typeof helpers;
|
|
18
|
+
http: typeof http;
|
|
19
|
+
lib: typeof lib;
|
|
20
|
+
"tables/contacts": typeof tables_contacts;
|
|
21
|
+
"tables/emailOperations": typeof tables_emailOperations;
|
|
22
|
+
validators: typeof validators;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* ```js
|
|
29
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const api: FilterApi<typeof fullApi, FunctionReference<any, "public">>;
|
|
33
|
+
/**
|
|
34
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
35
|
+
*
|
|
36
|
+
* Usage:
|
|
37
|
+
* ```js
|
|
38
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, "internal">>;
|
|
42
|
+
export declare const components: {};
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,OAAO,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,KAAK,GAAG,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,KAAK,eAAe,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,KAAK,sBAAsB,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,QAAA,MAAM,OAAO,EAAE,cAAc,CAAC;IAC5B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,GAAG,EAAE,OAAO,GAAG,CAAC;IAChB,iBAAiB,EAAE,OAAO,eAAe,CAAC;IAC1C,wBAAwB,EAAE,OAAO,sBAAsB,CAAC;IACxD,UAAU,EAAE,OAAO,UAAU,CAAC;CAC/B,CAAiB,CAAC;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,EAAE,SAAS,CACzB,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CACjB,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,CAC9B,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,CACnB,CAAC;AAElB,eAAO,MAAM,UAAU,EAAqC,EAAE,CAAC"}
|
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
* To regenerate, run `npx convex dev`.
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
10
|
import { anyApi, componentsGeneric } from "convex/server";
|
|
12
|
-
|
|
11
|
+
const fullApi = anyApi;
|
|
13
12
|
/**
|
|
14
|
-
* A utility for referencing Convex functions in your app's API.
|
|
13
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
15
14
|
*
|
|
16
15
|
* Usage:
|
|
17
16
|
* ```js
|
|
@@ -19,5 +18,13 @@ import { anyApi, componentsGeneric } from "convex/server";
|
|
|
19
18
|
* ```
|
|
20
19
|
*/
|
|
21
20
|
export const api = anyApi;
|
|
21
|
+
/**
|
|
22
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* ```js
|
|
26
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
22
29
|
export const internal = anyApi;
|
|
23
30
|
export const components = componentsGeneric();
|