@q32/core 0.2.1 → 0.3.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.
- package/README.md +2 -0
- package/dist/conversion-outbox.d.ts +41 -0
- package/dist/conversion-outbox.d.ts.map +1 -0
- package/dist/conversion-outbox.js +54 -0
- package/dist/conversion-outbox.js.map +1 -0
- package/dist/email.d.ts +10 -0
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +20 -0
- package/dist/email.js.map +1 -1
- package/dist/google-ads.d.ts +36 -0
- package/dist/google-ads.d.ts.map +1 -0
- package/dist/google-ads.js +159 -0
- package/dist/google-ads.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ses.d.ts +5 -0
- package/dist/ses.d.ts.map +1 -1
- package/dist/ses.js +23 -0
- package/dist/ses.js.map +1 -1
- package/docs/conversion-outbox.md +39 -0
- package/package.json +9 -1
- package/src/conversion-outbox.ts +90 -0
- package/src/email.ts +33 -0
- package/src/google-ads.ts +176 -0
- package/src/index.ts +2 -0
- package/src/ses.ts +31 -0
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ The first release focuses on common infrastructure repeated across Q32 apps:
|
|
|
8
8
|
- API operation registries
|
|
9
9
|
- API/MCP discovery surfaces: OpenAPI docs, API catalogs, Agent Skills indexes, MCP manifests, and bearer challenges
|
|
10
10
|
- framework-neutral auth/session/MCP auth services with thin Hono and React Router adapters
|
|
11
|
+
- durable purchase-conversion outbox contracts and Google Ads click-conversion uploads
|
|
11
12
|
- billing plan/status primitives
|
|
12
13
|
- Cloudflare binding guards
|
|
13
14
|
- environment parsing
|
|
@@ -67,6 +68,7 @@ import {
|
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
See [docs/jobs.md](docs/jobs.md) for the shared jobs model.
|
|
71
|
+
See [docs/conversion-outbox.md](docs/conversion-outbox.md) for the confirmed-purchase conversion contract.
|
|
70
72
|
|
|
71
73
|
The root `@q32/core` export is kept dependency-light. Optional integrations that
|
|
72
74
|
need peer packages should be imported from their subpaths, for example
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type ConversionProvider = "google_ads" | "meta" | "microsoft_ads";
|
|
2
|
+
export type ConversionDeliveryStatus = "pending" | "sending" | "sent" | "retry" | "dead" | "skipped";
|
|
3
|
+
export type AdClickIds = {
|
|
4
|
+
gclid?: string | null;
|
|
5
|
+
gbraid?: string | null;
|
|
6
|
+
wbraid?: string | null;
|
|
7
|
+
fbclid?: string | null;
|
|
8
|
+
msclkid?: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type PurchaseConversionEvent = {
|
|
11
|
+
eventId: string;
|
|
12
|
+
orderId: string;
|
|
13
|
+
productSlug: string;
|
|
14
|
+
conversionAt: string;
|
|
15
|
+
value: number;
|
|
16
|
+
currency: string;
|
|
17
|
+
clickIds: AdClickIds;
|
|
18
|
+
emailSha256?: string | null;
|
|
19
|
+
sourceUrl?: string | null;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
export type ConversionDelivery = {
|
|
23
|
+
deliveryKey: string;
|
|
24
|
+
eventId: string;
|
|
25
|
+
provider: ConversionProvider;
|
|
26
|
+
status: ConversionDeliveryStatus;
|
|
27
|
+
attemptCount: number;
|
|
28
|
+
nextAttemptAt: string | null;
|
|
29
|
+
sentAt: string | null;
|
|
30
|
+
lastErrorCode: string | null;
|
|
31
|
+
lastErrorMessage: string | null;
|
|
32
|
+
};
|
|
33
|
+
export declare function createPurchaseConversionEvent(input: PurchaseConversionEvent): PurchaseConversionEvent;
|
|
34
|
+
export declare function conversionDeliveryKey(eventId: string, provider: ConversionProvider): string;
|
|
35
|
+
export declare function createConversionDelivery(eventId: string, provider: ConversionProvider): ConversionDelivery;
|
|
36
|
+
export declare function nextConversionRetryAt(attemptCount: number, options?: {
|
|
37
|
+
now?: number;
|
|
38
|
+
baseDelayMs?: number;
|
|
39
|
+
maxDelayMs?: number;
|
|
40
|
+
}): string;
|
|
41
|
+
//# sourceMappingURL=conversion-outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversion-outbox.d.ts","sourceRoot":"","sources":["../src/conversion-outbox.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,CAAC;AACzE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAErG,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,UAAU,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,wBAAwB,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,uBAAuB,GAAG,uBAAuB,CAcrG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAG3F;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,kBAAkB,CAY1G;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,MAAM,CAMR"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function createPurchaseConversionEvent(input) {
|
|
2
|
+
if (!input.eventId.trim())
|
|
3
|
+
throw new Error("Purchase conversion eventId is required");
|
|
4
|
+
if (!input.orderId.trim())
|
|
5
|
+
throw new Error("Purchase conversion orderId is required");
|
|
6
|
+
if (!input.productSlug.trim())
|
|
7
|
+
throw new Error("Purchase conversion productSlug is required");
|
|
8
|
+
if (!Number.isFinite(input.value) || input.value < 0)
|
|
9
|
+
throw new Error("Purchase conversion value must be non-negative");
|
|
10
|
+
if (!/^[A-Z]{3}$/.test(input.currency))
|
|
11
|
+
throw new Error("Purchase conversion currency must be a three-letter uppercase code");
|
|
12
|
+
if (!Number.isFinite(Date.parse(input.conversionAt)))
|
|
13
|
+
throw new Error("Purchase conversion conversionAt must be an ISO date-time");
|
|
14
|
+
return {
|
|
15
|
+
...input,
|
|
16
|
+
eventId: input.eventId.trim(),
|
|
17
|
+
orderId: input.orderId.trim(),
|
|
18
|
+
productSlug: input.productSlug.trim(),
|
|
19
|
+
clickIds: compactClickIds(input.clickIds),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function conversionDeliveryKey(eventId, provider) {
|
|
23
|
+
if (!eventId.trim())
|
|
24
|
+
throw new Error("Conversion delivery eventId is required");
|
|
25
|
+
return `${eventId.trim()}:${provider}`;
|
|
26
|
+
}
|
|
27
|
+
export function createConversionDelivery(eventId, provider) {
|
|
28
|
+
return {
|
|
29
|
+
deliveryKey: conversionDeliveryKey(eventId, provider),
|
|
30
|
+
eventId: eventId.trim(),
|
|
31
|
+
provider,
|
|
32
|
+
status: "pending",
|
|
33
|
+
attemptCount: 0,
|
|
34
|
+
nextAttemptAt: null,
|
|
35
|
+
sentAt: null,
|
|
36
|
+
lastErrorCode: null,
|
|
37
|
+
lastErrorMessage: null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function nextConversionRetryAt(attemptCount, options = {}) {
|
|
41
|
+
if (!Number.isInteger(attemptCount) || attemptCount < 1)
|
|
42
|
+
throw new Error("Conversion attemptCount must be a positive integer");
|
|
43
|
+
const baseDelayMs = options.baseDelayMs ?? 30_000;
|
|
44
|
+
const maxDelayMs = options.maxDelayMs ?? 24 * 60 * 60 * 1000;
|
|
45
|
+
const delay = Math.min(maxDelayMs, baseDelayMs * 2 ** Math.min(attemptCount - 1, 20));
|
|
46
|
+
return new Date((options.now ?? Date.now()) + delay).toISOString();
|
|
47
|
+
}
|
|
48
|
+
function compactClickIds(input) {
|
|
49
|
+
return Object.fromEntries(Object.entries(input).flatMap(([key, value]) => {
|
|
50
|
+
const normalized = value?.trim();
|
|
51
|
+
return normalized ? [[key, normalized]] : [];
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=conversion-outbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversion-outbox.js","sourceRoot":"","sources":["../src/conversion-outbox.ts"],"names":[],"mappings":"AAoCA,MAAM,UAAU,6BAA6B,CAAC,KAA8B;IAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACtF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACtF,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC9F,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACxH,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC9H,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IACnI,OAAO;QACL,GAAG,KAAK;QACR,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAC7B,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAC7B,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE;QACrC,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,QAA4B;IACjF,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAChF,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,QAA4B;IACpF,OAAO;QACL,WAAW,EAAE,qBAAqB,CAAC,OAAO,EAAE,QAAQ,CAAC;QACrD,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;QACvB,QAAQ;QACR,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,CAAC;QACf,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;QACnB,gBAAgB,EAAE,IAAI;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,YAAoB,EACpB,UAAuE,EAAE;IAEzE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC/H,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC;IAClD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACtF,OAAO,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,eAAe,CAAC,KAAiB;IACxC,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC7C,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;QACjC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
package/dist/email.d.ts
CHANGED
|
@@ -37,5 +37,15 @@ export type RawMimeMessageInput = Omit<SendEmailInput, "tags"> & {
|
|
|
37
37
|
headers?: Record<string, string>;
|
|
38
38
|
boundaryPrefix?: string;
|
|
39
39
|
};
|
|
40
|
+
export type ResentMimeMessageInput = {
|
|
41
|
+
rawMessage: Uint8Array | string;
|
|
42
|
+
resentFrom: EmailAddress;
|
|
43
|
+
resentTo: EmailAddress[];
|
|
44
|
+
resentSender?: EmailAddress;
|
|
45
|
+
resentDate?: Date;
|
|
46
|
+
resentMessageId?: string;
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
};
|
|
40
49
|
export declare function buildRawMimeMessage(input: RawMimeMessageInput): string;
|
|
50
|
+
export declare function buildResentMimeMessage(input: ResentMimeMessageInput): string;
|
|
41
51
|
//# sourceMappingURL=email.d.ts.map
|
package/dist/email.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,YAAY,EAAE,CAAC;IACnB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACvD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAIhE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK/E;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAiDtE"}
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,YAAY,EAAE,CAAC;IACnB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACvD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAIhE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK/E;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAChC,UAAU,EAAE,YAAY,CAAC;IACzB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAiDtE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CAgB5E"}
|
package/dist/email.js
CHANGED
|
@@ -75,6 +75,22 @@ export function buildRawMimeMessage(input) {
|
|
|
75
75
|
parts.push(`--${mixedBoundary}--`, "");
|
|
76
76
|
return parts.join("\r\n");
|
|
77
77
|
}
|
|
78
|
+
export function buildResentMimeMessage(input) {
|
|
79
|
+
const headers = [
|
|
80
|
+
`Resent-From: ${formatEmailAddress(input.resentFrom)}`,
|
|
81
|
+
...(input.resentSender ? [`Resent-Sender: ${formatEmailAddress(input.resentSender)}`] : []),
|
|
82
|
+
`Resent-To: ${input.resentTo.map(formatEmailAddress).join(", ")}`,
|
|
83
|
+
`Resent-Date: ${(input.resentDate ?? new Date()).toUTCString()}`,
|
|
84
|
+
`Resent-Message-ID: ${sanitizeEmailHeaderValue(input.resentMessageId ?? `<${crypto.randomUUID()}@resent.q32.email>`)}`,
|
|
85
|
+
];
|
|
86
|
+
for (const [name, value] of Object.entries(input.headers ?? {})) {
|
|
87
|
+
const headerName = sanitizeHeaderName(name);
|
|
88
|
+
const headerValue = sanitizeEmailHeaderValue(value);
|
|
89
|
+
if (headerName && headerValue)
|
|
90
|
+
headers.push(`${headerName}: ${headerValue}`);
|
|
91
|
+
}
|
|
92
|
+
return `${headers.join("\r\n")}\r\n${normalizeRawMessage(input.rawMessage)}`;
|
|
93
|
+
}
|
|
78
94
|
function sanitizeHeaderName(value) {
|
|
79
95
|
const name = value.trim();
|
|
80
96
|
return /^[A-Za-z0-9-]+$/.test(name) ? name : "";
|
|
@@ -90,6 +106,10 @@ function attachmentDataToBase64(value) {
|
|
|
90
106
|
binary += String.fromCharCode(byte);
|
|
91
107
|
return btoa(binary);
|
|
92
108
|
}
|
|
109
|
+
function normalizeRawMessage(value) {
|
|
110
|
+
const text = typeof value === "string" ? value : new TextDecoder("utf-8", { fatal: false }).decode(value);
|
|
111
|
+
return text.replace(/^\uFEFF/, "").replace(/\r?\n/g, "\r\n");
|
|
112
|
+
}
|
|
93
113
|
function base64Utf8(value) {
|
|
94
114
|
const bytes = new TextEncoder().encode(value);
|
|
95
115
|
let binary = "";
|
package/dist/email.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,IAAI,CAAC,OAAO,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,OAAO,IAAI,OAAO,MAAM,OAAO,CAAC,KAAK,GAAG,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACvG,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,cAAsB;IAC1E,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,cAAc,IAAI,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,wBAAwB,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,aAAa,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,OAAO;QACL,kBAAkB,EAAE,IAAI,wBAAwB,CAAC,GAAG,CAAC,GAAG;QACxD,uBAAuB,EAAE,4BAA4B;KACtD,CAAC;AACJ,CAAC;
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,IAAI,CAAC,OAAO,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,OAAO,IAAI,OAAO,MAAM,OAAO,CAAC,KAAK,GAAG,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACvG,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,cAAsB;IAC1E,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,cAAc,IAAI,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,wBAAwB,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,aAAa,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,OAAO;QACL,kBAAkB,EAAE,IAAI,wBAAwB,CAAC,GAAG,CAAC,GAAG;QACxD,uBAAuB,EAAE,4BAA4B;KACtD,CAAC;AACJ,CAAC;AAiBD,MAAM,UAAU,mBAAmB,CAAC,KAA0B;IAC5D,MAAM,aAAa,GAAG,GAAG,KAAK,CAAC,cAAc,IAAI,KAAK,UAAU,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;IACtF,MAAM,YAAY,GAAG,GAAG,KAAK,CAAC,cAAc,IAAI,KAAK,SAAS,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;IACpF,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG;QACd,SAAS,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;QACzC,OAAO,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACpD,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,YAAY,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;QAC7C,mBAAmB;QACnB,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACpG,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,UAAU,IAAI,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,WAAW,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,EAAE,4CAA4C,aAAa,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7F,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC,CAAC;IAEjC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,kDAAkD,YAAY,GAAG,EAAE,EAAE,CAAC,CAAC;QAClF,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,IAAI,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;SAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9F,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,mCAAmC,EAAE,EAAE,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,iBAAiB,wBAAwB,CAAC,UAAU,CAAC,WAAW,CAAC,UAAU,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/H,KAAK,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,6CAA6C,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjG,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,sBAAsB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,IAAI,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAA6B;IAClE,MAAM,OAAO,GAAG;QACd,gBAAgB,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE;QACtD,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,kBAAkB,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,cAAc,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACjE,gBAAgB,CAAC,KAAK,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE;QAChE,sBAAsB,wBAAwB,CAAC,KAAK,CAAC,eAAe,IAAI,IAAI,MAAM,CAAC,UAAU,EAAE,oBAAoB,CAAC,EAAE;KACvH,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,UAAU,IAAI,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,WAAW,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,IAAI,wBAAwB,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC;AAC5E,CAAC;AAED,SAAS,sBAAsB,CAAC,KAA0B;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,mBAAmB,CAAC,KAA0B;IACrD,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1G,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FetchLike } from "./http.js";
|
|
2
|
+
import type { PurchaseConversionEvent } from "./conversion-outbox.js";
|
|
3
|
+
export type GoogleAdsConversionConfig = {
|
|
4
|
+
developerToken: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
refreshToken: string;
|
|
8
|
+
customerId: string;
|
|
9
|
+
conversionActionId: string;
|
|
10
|
+
loginCustomerId?: string;
|
|
11
|
+
apiVersion?: string;
|
|
12
|
+
};
|
|
13
|
+
export type GoogleAdsUploadResult = {
|
|
14
|
+
status: "sent";
|
|
15
|
+
jobId: string | null;
|
|
16
|
+
requestId: string | null;
|
|
17
|
+
} | {
|
|
18
|
+
status: "skipped";
|
|
19
|
+
reason: "missing_google_click_id";
|
|
20
|
+
};
|
|
21
|
+
export declare class GoogleAdsConversionError extends Error {
|
|
22
|
+
readonly code: string;
|
|
23
|
+
readonly retryable: boolean;
|
|
24
|
+
readonly status: number | null;
|
|
25
|
+
constructor(message: string, code: string, retryable: boolean, status: number | null);
|
|
26
|
+
}
|
|
27
|
+
export declare class GoogleAdsConversionClient {
|
|
28
|
+
private readonly config;
|
|
29
|
+
private readonly fetchImpl;
|
|
30
|
+
private accessToken;
|
|
31
|
+
constructor(config: GoogleAdsConversionConfig, fetchImpl?: FetchLike);
|
|
32
|
+
uploadPurchase(event: PurchaseConversionEvent): Promise<GoogleAdsUploadResult>;
|
|
33
|
+
private getAccessToken;
|
|
34
|
+
}
|
|
35
|
+
export declare function googleAdsDateTime(value: string): string;
|
|
36
|
+
//# sourceMappingURL=google-ads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-ads.d.ts","sourceRoot":"","sources":["../src/google-ads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAKtE,MAAM,MAAM,yBAAyB,GAAG;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClE;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAA;CAAE,CAAC;AAE7D,qBAAa,wBAAyB,SAAQ,KAAK;IAG/C,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;gBAH9B,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,MAAM,GAAG,IAAI;CAKjC;AAED,qBAAa,yBAAyB;IAIlC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,WAAW,CAAqD;gBAGrD,MAAM,EAAE,yBAAyB,EACjC,SAAS,GAAE,SAAwB;IAGhD,cAAc,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAuCtE,cAAc;CAsB7B;AAYD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { defaultFetch } from "./http.js";
|
|
2
|
+
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
3
|
+
const GOOGLE_ADS_ORIGIN = "https://googleads.googleapis.com";
|
|
4
|
+
export class GoogleAdsConversionError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
retryable;
|
|
7
|
+
status;
|
|
8
|
+
constructor(message, code, retryable, status) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.retryable = retryable;
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.name = "GoogleAdsConversionError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class GoogleAdsConversionClient {
|
|
17
|
+
config;
|
|
18
|
+
fetchImpl;
|
|
19
|
+
accessToken = null;
|
|
20
|
+
constructor(config, fetchImpl = defaultFetch) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.fetchImpl = fetchImpl;
|
|
23
|
+
}
|
|
24
|
+
async uploadPurchase(event) {
|
|
25
|
+
const clickId = googleClickId(event);
|
|
26
|
+
if (!clickId)
|
|
27
|
+
return { status: "skipped", reason: "missing_google_click_id" };
|
|
28
|
+
const accessToken = await this.getAccessToken();
|
|
29
|
+
const customerId = digitsOnly(this.config.customerId, "customerId");
|
|
30
|
+
const actionId = digitsOnly(this.config.conversionActionId, "conversionActionId");
|
|
31
|
+
const apiVersion = this.config.apiVersion ?? "v24";
|
|
32
|
+
const conversion = {
|
|
33
|
+
conversionAction: `customers/${customerId}/conversionActions/${actionId}`,
|
|
34
|
+
conversionDateTime: googleAdsDateTime(event.conversionAt),
|
|
35
|
+
conversionValue: event.value,
|
|
36
|
+
currencyCode: event.currency,
|
|
37
|
+
orderId: event.orderId,
|
|
38
|
+
conversionEnvironment: "WEB",
|
|
39
|
+
[clickId.kind]: clickId.value,
|
|
40
|
+
};
|
|
41
|
+
const headers = {
|
|
42
|
+
authorization: `Bearer ${accessToken}`,
|
|
43
|
+
"content-type": "application/json",
|
|
44
|
+
"developer-token": this.config.developerToken,
|
|
45
|
+
};
|
|
46
|
+
if (this.config.loginCustomerId) {
|
|
47
|
+
headers["login-customer-id"] = digitsOnly(this.config.loginCustomerId, "loginCustomerId");
|
|
48
|
+
}
|
|
49
|
+
const response = await this.fetchImpl(`${GOOGLE_ADS_ORIGIN}/${apiVersion}/customers/${customerId}:uploadClickConversions`, { method: "POST", headers, body: JSON.stringify({ conversions: [conversion], partialFailure: true }) });
|
|
50
|
+
const payload = await readJson(response);
|
|
51
|
+
if (!response.ok)
|
|
52
|
+
throw googleAdsHttpError(response.status, payload);
|
|
53
|
+
const partialFailure = recordValue(payload, "partialFailureError");
|
|
54
|
+
if (partialFailure)
|
|
55
|
+
throw googleAdsPartialFailure(partialFailure);
|
|
56
|
+
return {
|
|
57
|
+
status: "sent",
|
|
58
|
+
jobId: stringValue(payload, "jobId"),
|
|
59
|
+
requestId: response.headers.get("request-id"),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async getAccessToken() {
|
|
63
|
+
if (this.accessToken && this.accessToken.expiresAt > Date.now() + 60_000)
|
|
64
|
+
return this.accessToken.value;
|
|
65
|
+
const response = await this.fetchImpl(GOOGLE_TOKEN_URL, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
68
|
+
body: new URLSearchParams({
|
|
69
|
+
client_id: this.config.clientId,
|
|
70
|
+
client_secret: this.config.clientSecret,
|
|
71
|
+
refresh_token: this.config.refreshToken,
|
|
72
|
+
grant_type: "refresh_token",
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
const payload = await readJson(response);
|
|
76
|
+
const token = stringValue(payload, "access_token");
|
|
77
|
+
if (!response.ok || !token) {
|
|
78
|
+
const code = stringValue(payload, "error") ?? "oauth_refresh_failed";
|
|
79
|
+
throw new GoogleAdsConversionError(`Google OAuth refresh failed: ${code}`, code, response.status >= 500, response.status);
|
|
80
|
+
}
|
|
81
|
+
const expiresIn = numberValue(payload, "expires_in") ?? 3600;
|
|
82
|
+
this.accessToken = { value: token, expiresAt: Date.now() + expiresIn * 1000 };
|
|
83
|
+
return token;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function googleClickId(event) {
|
|
87
|
+
const candidates = [
|
|
88
|
+
["gclid", event.clickIds.gclid],
|
|
89
|
+
["wbraid", event.clickIds.wbraid],
|
|
90
|
+
["gbraid", event.clickIds.gbraid],
|
|
91
|
+
];
|
|
92
|
+
for (const [kind, value] of candidates)
|
|
93
|
+
if (value?.trim())
|
|
94
|
+
return { kind, value: value.trim() };
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
export function googleAdsDateTime(value) {
|
|
98
|
+
const date = new Date(value);
|
|
99
|
+
if (!Number.isFinite(date.getTime()))
|
|
100
|
+
throw new Error("Google Ads conversion time must be a valid date-time");
|
|
101
|
+
return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "+00:00");
|
|
102
|
+
}
|
|
103
|
+
function digitsOnly(value, field) {
|
|
104
|
+
const normalized = value.replaceAll("-", "").trim();
|
|
105
|
+
if (!/^\d+$/.test(normalized))
|
|
106
|
+
throw new Error(`Google Ads ${field} must contain only digits`);
|
|
107
|
+
return normalized;
|
|
108
|
+
}
|
|
109
|
+
async function readJson(response) {
|
|
110
|
+
return (await response.json().catch(() => ({})));
|
|
111
|
+
}
|
|
112
|
+
function googleAdsHttpError(status, payload) {
|
|
113
|
+
const error = recordValue(payload, "error");
|
|
114
|
+
const code = stringValue(error ?? payload, "status") ?? `http_${status}`;
|
|
115
|
+
const message = stringValue(error ?? payload, "message") ?? `Google Ads upload failed with HTTP ${status}`;
|
|
116
|
+
return new GoogleAdsConversionError(message, code, status === 429 || status >= 500, status);
|
|
117
|
+
}
|
|
118
|
+
function googleAdsPartialFailure(error) {
|
|
119
|
+
const code = firstGoogleAdsErrorCode(error) ?? "partial_failure";
|
|
120
|
+
const message = stringValue(error, "message") ?? "Google Ads rejected the conversion";
|
|
121
|
+
const retryable = ["INTERNAL", "RESOURCE_EXHAUSTED", "UNAVAILABLE", "TOO_RECENT_CONVERSION_ACTION"].includes(code);
|
|
122
|
+
return new GoogleAdsConversionError(message, code, retryable, 200);
|
|
123
|
+
}
|
|
124
|
+
function firstGoogleAdsErrorCode(error) {
|
|
125
|
+
const details = error.details;
|
|
126
|
+
if (!Array.isArray(details))
|
|
127
|
+
return null;
|
|
128
|
+
for (const detail of details) {
|
|
129
|
+
if (!detail || typeof detail !== "object")
|
|
130
|
+
continue;
|
|
131
|
+
const errors = detail.errors;
|
|
132
|
+
if (!Array.isArray(errors))
|
|
133
|
+
continue;
|
|
134
|
+
for (const item of errors) {
|
|
135
|
+
if (!item || typeof item !== "object")
|
|
136
|
+
continue;
|
|
137
|
+
const errorCode = recordValue(item, "errorCode");
|
|
138
|
+
if (!errorCode)
|
|
139
|
+
continue;
|
|
140
|
+
for (const value of Object.values(errorCode))
|
|
141
|
+
if (typeof value === "string")
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
function recordValue(input, key) {
|
|
148
|
+
const value = input[key];
|
|
149
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
150
|
+
}
|
|
151
|
+
function stringValue(input, key) {
|
|
152
|
+
const value = input[key];
|
|
153
|
+
return typeof value === "string" ? value : typeof value === "number" ? String(value) : null;
|
|
154
|
+
}
|
|
155
|
+
function numberValue(input, key) {
|
|
156
|
+
const value = input[key];
|
|
157
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=google-ads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-ads.js","sourceRoot":"","sources":["../src/google-ads.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,gBAAgB,GAAG,qCAAqC,CAAC;AAC/D,MAAM,iBAAiB,GAAG,kCAAkC,CAAC;AAiB7D,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAGtC;IACA;IACA;IAJX,YACE,OAAe,EACN,IAAY,EACZ,SAAkB,EAClB,MAAqB;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,SAAI,GAAJ,IAAI,CAAQ;QACZ,cAAS,GAAT,SAAS,CAAS;QAClB,WAAM,GAAN,MAAM,CAAe;QAG9B,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED,MAAM,OAAO,yBAAyB;IAIjB;IACA;IAJX,WAAW,GAAgD,IAAI,CAAC;IAExE,YACmB,MAAiC,EACjC,YAAuB,YAAY;QADnC,WAAM,GAAN,MAAM,CAA2B;QACjC,cAAS,GAAT,SAAS,CAA0B;IACnD,CAAC;IAEJ,KAAK,CAAC,cAAc,CAAC,KAA8B;QACjD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;QAC9E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;QAClF,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC;QACnD,MAAM,UAAU,GAAG;YACjB,gBAAgB,EAAE,aAAa,UAAU,sBAAsB,QAAQ,EAAE;YACzE,kBAAkB,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC;YACzD,eAAe,EAAE,KAAK,CAAC,KAAK;YAC5B,YAAY,EAAE,KAAK,CAAC,QAAQ;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,qBAAqB,EAAE,KAAK;YAC5B,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,KAAK;SAC9B,CAAC;QACF,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,WAAW,EAAE;YACtC,cAAc,EAAE,kBAAkB;YAClC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;SAC9C,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAChC,OAAO,CAAC,mBAAmB,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CACnC,GAAG,iBAAiB,IAAI,UAAU,cAAc,UAAU,yBAAyB,EACnF,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,CACvG,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrE,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;QACnE,IAAI,cAAc;YAAE,MAAM,uBAAuB,CAAC,cAAc,CAAC,CAAC;QAClE,OAAO;YACL,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC;YACpC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;SAC9C,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QACxG,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE;YACtD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,eAAe,CAAC;gBACxB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC/B,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBACvC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBACvC,UAAU,EAAE,eAAe;aAC5B,CAAC;SACH,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,sBAAsB,CAAC;YACrE,MAAM,IAAI,wBAAwB,CAAC,gCAAgC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5H,CAAC;QACD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC;QAC7D,IAAI,CAAC,WAAW,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,EAAE,CAAC;QAC9E,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,SAAS,aAAa,CAAC,KAA8B;IACnD,MAAM,UAAU,GAAG;QACjB,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC/B,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACjC,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;KACzB,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,UAAU;QAAE,IAAI,KAAK,EAAE,IAAI,EAAE;YAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;IAChG,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC9G,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,UAAU,CAAC,KAAa,EAAE,KAAa;IAC9C,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,2BAA2B,CAAC,CAAC;IAC/F,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,QAAkB;IACxC,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA4B,CAAC;AAC9E,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc,EAAE,OAAgC;IAC1E,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,IAAI,OAAO,EAAE,QAAQ,CAAC,IAAI,QAAQ,MAAM,EAAE,CAAC;IACzE,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,OAAO,EAAE,SAAS,CAAC,IAAI,sCAAsC,MAAM,EAAE,CAAC;IAC3G,OAAO,IAAI,wBAAwB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,GAAG,IAAI,MAAM,IAAI,GAAG,EAAE,MAAM,CAAC,CAAC;AAC9F,CAAC;AAED,SAAS,uBAAuB,CAAC,KAA8B;IAC7D,MAAM,IAAI,GAAG,uBAAuB,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC;IACjE,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,oCAAoC,CAAC;IACtF,MAAM,SAAS,GAAG,CAAC,UAAU,EAAE,oBAAoB,EAAE,aAAa,EAAE,8BAA8B,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnH,OAAO,IAAI,wBAAwB,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,uBAAuB,CAAC,KAA8B;IAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QACpD,MAAM,MAAM,GAAI,MAAkC,CAAC,MAAM,CAAC;QAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,SAAS;QACrC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,SAAS;YAChD,MAAM,SAAS,GAAG,WAAW,CAAC,IAA+B,EAAE,WAAW,CAAC,CAAC;YAC5E,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;gBAAE,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,OAAO,KAAK,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,KAA8B,EAAE,GAAW;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAiC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjH,CAAC;AAED,SAAS,WAAW,CAAC,KAA8B,EAAE,GAAW;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9F,CAAC;AAED,SAAS,WAAW,CAAC,KAA8B,EAAE,GAAW;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5E,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ export * from "./auth.js";
|
|
|
4
4
|
export * from "./aws.js";
|
|
5
5
|
export * from "./billing.js";
|
|
6
6
|
export * from "./cloudflare.js";
|
|
7
|
+
export * from "./conversion-outbox.js";
|
|
7
8
|
export * from "./crypto.js";
|
|
8
9
|
export * from "./d1.js";
|
|
9
10
|
export * from "./encoding.js";
|
|
10
11
|
export * from "./email.js";
|
|
11
12
|
export * from "./env.js";
|
|
12
13
|
export * from "./hono.js";
|
|
14
|
+
export * from "./google-ads.js";
|
|
13
15
|
export * from "./http.js";
|
|
14
16
|
export * from "./ids.js";
|
|
15
17
|
export * from "./jobs.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,12 +4,14 @@ export * from "./auth.js";
|
|
|
4
4
|
export * from "./aws.js";
|
|
5
5
|
export * from "./billing.js";
|
|
6
6
|
export * from "./cloudflare.js";
|
|
7
|
+
export * from "./conversion-outbox.js";
|
|
7
8
|
export * from "./crypto.js";
|
|
8
9
|
export * from "./d1.js";
|
|
9
10
|
export * from "./encoding.js";
|
|
10
11
|
export * from "./email.js";
|
|
11
12
|
export * from "./env.js";
|
|
12
13
|
export * from "./hono.js";
|
|
14
|
+
export * from "./google-ads.js";
|
|
13
15
|
export * from "./http.js";
|
|
14
16
|
export * from "./ids.js";
|
|
15
17
|
export * from "./jobs.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
package/dist/ses.d.ts
CHANGED
|
@@ -48,6 +48,11 @@ export declare function createSesMailerFromEnv(env: SesMailerEnv, options?: {
|
|
|
48
48
|
export declare function sendSesEmail(config: AwsConfig, input: SendEmailInput & {
|
|
49
49
|
headers?: Record<string, string>;
|
|
50
50
|
}, options?: SesSendOptions): Promise<SesSendResult>;
|
|
51
|
+
export declare function sendSesRawEmail(config: AwsConfig, input: {
|
|
52
|
+
fromEmailAddress?: string;
|
|
53
|
+
destination: string[];
|
|
54
|
+
rawMime: string;
|
|
55
|
+
}, options?: SesSendOptions): Promise<SesSendResult>;
|
|
51
56
|
export declare function sesQuery(config: AwsConfig, params: Record<string, string>, options?: SesSendOptions): Promise<string>;
|
|
52
57
|
export declare function inboundSesMxHost(region?: string): string;
|
|
53
58
|
//# sourceMappingURL=ses.d.ts.map
|
package/dist/ses.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ses.d.ts","sourceRoot":"","sources":["../src/ses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,EAA2C,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7G,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IACzD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF,qBAAa,aAAc,YAAW,SAAS;IAC7C,QAAQ,CAAC,OAAO,SAAS;IAEnB,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC;CAGrC;AAED,qBAAa,eAAgB,YAAW,SAAS;IAI7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,QAAQ,CAAC,OAAO,QAAQ;gBAGL,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE;QACzB,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;QACzB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;KACxB;IAGH,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;CAOnD;AAED,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,YAAY,EACjB,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACnB,GACL,SAAS,CAcX;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,cAAc,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC5D,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAaxB;AAED,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAc,GAAG,MAAM,CAE7D"}
|
|
1
|
+
{"version":3,"file":"ses.d.ts","sourceRoot":"","sources":["../src/ses.ts"],"names":[],"mappings":"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"}
|
package/dist/ses.js
CHANGED
|
@@ -53,6 +53,29 @@ export async function sendSesEmail(config, input, options = {}) {
|
|
|
53
53
|
const messageId = stringValue(payload.MessageId) ?? stringValue(payload.messageId) ?? "";
|
|
54
54
|
return { provider: "ses", messageId };
|
|
55
55
|
}
|
|
56
|
+
export async function sendSesRawEmail(config, input, options = {}) {
|
|
57
|
+
const response = await awsFetch(config, `https://email.${config.region}.amazonaws.com/v2/email/outbound-emails`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
...(input.fromEmailAddress ? { FromEmailAddress: input.fromEmailAddress } : {}),
|
|
62
|
+
Destination: {
|
|
63
|
+
ToAddresses: input.destination,
|
|
64
|
+
},
|
|
65
|
+
Content: {
|
|
66
|
+
Raw: {
|
|
67
|
+
Data: textToBase64(input.rawMime),
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
}, { service: "ses", fetcher: options.fetcher });
|
|
72
|
+
const text = await response.text();
|
|
73
|
+
if (!response.ok)
|
|
74
|
+
throw new Error(`SES raw send failed: ${response.status} ${text.slice(0, 300)}`);
|
|
75
|
+
const payload = parseJsonObject(text);
|
|
76
|
+
const messageId = stringValue(payload.MessageId) ?? stringValue(payload.messageId) ?? "";
|
|
77
|
+
return { provider: "ses", messageId };
|
|
78
|
+
}
|
|
56
79
|
export async function sesQuery(config, params, options = {}) {
|
|
57
80
|
const body = new URLSearchParams({ Version: "2010-12-01", ...params }).toString();
|
|
58
81
|
const response = await awsFetch(config, `https://email.${config.region}.amazonaws.com/`, {
|
package/dist/ses.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ses.js","sourceRoot":"","sources":["../src/ses.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ses.js","sourceRoot":"","sources":["../src/ses.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Purchase conversion outbox
|
|
2
|
+
|
|
3
|
+
Commit the product purchase and its conversion event before calling an ad provider. Create one delivery per configured provider, acknowledge the verified payment webhook, and retry deliveries independently.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createConversionDelivery, createPurchaseConversionEvent } from "@q32/core/conversion-outbox";
|
|
7
|
+
import { GoogleAdsConversionClient } from "@q32/core/google-ads";
|
|
8
|
+
|
|
9
|
+
const purchase = createPurchaseConversionEvent({
|
|
10
|
+
eventId: `purchase:${order.id}`,
|
|
11
|
+
orderId: order.id,
|
|
12
|
+
productSlug: "example",
|
|
13
|
+
conversionAt: order.paidAt,
|
|
14
|
+
value: order.valueCents / 100,
|
|
15
|
+
currency: order.currency.toUpperCase(),
|
|
16
|
+
clickIds: { gclid: attribution.gclid, gbraid: attribution.gbraid, wbraid: attribution.wbraid },
|
|
17
|
+
});
|
|
18
|
+
const googleDelivery = createConversionDelivery(purchase.eventId, "google_ads");
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Persist both records atomically with the purchase. The package intentionally does not prescribe D1 or Postgres table names.
|
|
22
|
+
|
|
23
|
+
Upload with a client shared by the Worker isolate:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const google = new GoogleAdsConversionClient({
|
|
27
|
+
developerToken: env.GOOGLE_ADS_DEVELOPER_TOKEN,
|
|
28
|
+
clientId: env.GOOGLE_ADS_CLIENT_ID,
|
|
29
|
+
clientSecret: env.GOOGLE_ADS_CLIENT_SECRET,
|
|
30
|
+
refreshToken: env.GOOGLE_ADS_REFRESH_TOKEN,
|
|
31
|
+
customerId: env.GOOGLE_ADS_CUSTOMER_ID,
|
|
32
|
+
loginCustomerId: env.GOOGLE_ADS_LOGIN_CUSTOMER_ID,
|
|
33
|
+
conversionActionId: env.GOOGLE_ADS_PURCHASE_CONVERSION_ACTION_ID,
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`uploadPurchase` returns `skipped` when no Google click identifier is available. It throws `GoogleAdsConversionError` for provider failures; `retryable` distinguishes transient failures from permanent rejection. Store bounded error details and schedule retry with `nextConversionRetryAt`.
|
|
38
|
+
|
|
39
|
+
Google requires an `UPLOAD_CLICKS` conversion action, `partialFailure: true`, and a unique order ID per conversion action. Keep browser purchase events when useful for diagnostics, but do not make the durable upload depend on a success-page visit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q32/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared TypeScript primitives for Q32 Cloudflare Worker projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/cloudflare.d.ts",
|
|
45
45
|
"default": "./dist/cloudflare.js"
|
|
46
46
|
},
|
|
47
|
+
"./conversion-outbox": {
|
|
48
|
+
"types": "./dist/conversion-outbox.d.ts",
|
|
49
|
+
"default": "./dist/conversion-outbox.js"
|
|
50
|
+
},
|
|
47
51
|
"./crypto": {
|
|
48
52
|
"types": "./dist/crypto.d.ts",
|
|
49
53
|
"default": "./dist/crypto.js"
|
|
@@ -72,6 +76,10 @@
|
|
|
72
76
|
"types": "./dist/hono.d.ts",
|
|
73
77
|
"default": "./dist/hono.js"
|
|
74
78
|
},
|
|
79
|
+
"./google-ads": {
|
|
80
|
+
"types": "./dist/google-ads.d.ts",
|
|
81
|
+
"default": "./dist/google-ads.js"
|
|
82
|
+
},
|
|
75
83
|
"./http": {
|
|
76
84
|
"types": "./dist/http.d.ts",
|
|
77
85
|
"default": "./dist/http.js"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type ConversionProvider = "google_ads" | "meta" | "microsoft_ads";
|
|
2
|
+
export type ConversionDeliveryStatus = "pending" | "sending" | "sent" | "retry" | "dead" | "skipped";
|
|
3
|
+
|
|
4
|
+
export type AdClickIds = {
|
|
5
|
+
gclid?: string | null;
|
|
6
|
+
gbraid?: string | null;
|
|
7
|
+
wbraid?: string | null;
|
|
8
|
+
fbclid?: string | null;
|
|
9
|
+
msclkid?: string | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PurchaseConversionEvent = {
|
|
13
|
+
eventId: string;
|
|
14
|
+
orderId: string;
|
|
15
|
+
productSlug: string;
|
|
16
|
+
conversionAt: string;
|
|
17
|
+
value: number;
|
|
18
|
+
currency: string;
|
|
19
|
+
clickIds: AdClickIds;
|
|
20
|
+
emailSha256?: string | null;
|
|
21
|
+
sourceUrl?: string | null;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ConversionDelivery = {
|
|
26
|
+
deliveryKey: string;
|
|
27
|
+
eventId: string;
|
|
28
|
+
provider: ConversionProvider;
|
|
29
|
+
status: ConversionDeliveryStatus;
|
|
30
|
+
attemptCount: number;
|
|
31
|
+
nextAttemptAt: string | null;
|
|
32
|
+
sentAt: string | null;
|
|
33
|
+
lastErrorCode: string | null;
|
|
34
|
+
lastErrorMessage: string | null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function createPurchaseConversionEvent(input: PurchaseConversionEvent): PurchaseConversionEvent {
|
|
38
|
+
if (!input.eventId.trim()) throw new Error("Purchase conversion eventId is required");
|
|
39
|
+
if (!input.orderId.trim()) throw new Error("Purchase conversion orderId is required");
|
|
40
|
+
if (!input.productSlug.trim()) throw new Error("Purchase conversion productSlug is required");
|
|
41
|
+
if (!Number.isFinite(input.value) || input.value < 0) throw new Error("Purchase conversion value must be non-negative");
|
|
42
|
+
if (!/^[A-Z]{3}$/.test(input.currency)) throw new Error("Purchase conversion currency must be a three-letter uppercase code");
|
|
43
|
+
if (!Number.isFinite(Date.parse(input.conversionAt))) throw new Error("Purchase conversion conversionAt must be an ISO date-time");
|
|
44
|
+
return {
|
|
45
|
+
...input,
|
|
46
|
+
eventId: input.eventId.trim(),
|
|
47
|
+
orderId: input.orderId.trim(),
|
|
48
|
+
productSlug: input.productSlug.trim(),
|
|
49
|
+
clickIds: compactClickIds(input.clickIds),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function conversionDeliveryKey(eventId: string, provider: ConversionProvider): string {
|
|
54
|
+
if (!eventId.trim()) throw new Error("Conversion delivery eventId is required");
|
|
55
|
+
return `${eventId.trim()}:${provider}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createConversionDelivery(eventId: string, provider: ConversionProvider): ConversionDelivery {
|
|
59
|
+
return {
|
|
60
|
+
deliveryKey: conversionDeliveryKey(eventId, provider),
|
|
61
|
+
eventId: eventId.trim(),
|
|
62
|
+
provider,
|
|
63
|
+
status: "pending",
|
|
64
|
+
attemptCount: 0,
|
|
65
|
+
nextAttemptAt: null,
|
|
66
|
+
sentAt: null,
|
|
67
|
+
lastErrorCode: null,
|
|
68
|
+
lastErrorMessage: null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function nextConversionRetryAt(
|
|
73
|
+
attemptCount: number,
|
|
74
|
+
options: { now?: number; baseDelayMs?: number; maxDelayMs?: number } = {},
|
|
75
|
+
): string {
|
|
76
|
+
if (!Number.isInteger(attemptCount) || attemptCount < 1) throw new Error("Conversion attemptCount must be a positive integer");
|
|
77
|
+
const baseDelayMs = options.baseDelayMs ?? 30_000;
|
|
78
|
+
const maxDelayMs = options.maxDelayMs ?? 24 * 60 * 60 * 1000;
|
|
79
|
+
const delay = Math.min(maxDelayMs, baseDelayMs * 2 ** Math.min(attemptCount - 1, 20));
|
|
80
|
+
return new Date((options.now ?? Date.now()) + delay).toISOString();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function compactClickIds(input: AdClickIds): AdClickIds {
|
|
84
|
+
return Object.fromEntries(
|
|
85
|
+
Object.entries(input).flatMap(([key, value]) => {
|
|
86
|
+
const normalized = value?.trim();
|
|
87
|
+
return normalized ? [[key, normalized]] : [];
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
}
|
package/src/email.ts
CHANGED
|
@@ -69,6 +69,16 @@ export type RawMimeMessageInput = Omit<SendEmailInput, "tags"> & {
|
|
|
69
69
|
boundaryPrefix?: string;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
export type ResentMimeMessageInput = {
|
|
73
|
+
rawMessage: Uint8Array | string;
|
|
74
|
+
resentFrom: EmailAddress;
|
|
75
|
+
resentTo: EmailAddress[];
|
|
76
|
+
resentSender?: EmailAddress;
|
|
77
|
+
resentDate?: Date;
|
|
78
|
+
resentMessageId?: string;
|
|
79
|
+
headers?: Record<string, string>;
|
|
80
|
+
};
|
|
81
|
+
|
|
72
82
|
export function buildRawMimeMessage(input: RawMimeMessageInput): string {
|
|
73
83
|
const mixedBoundary = `${input.boundaryPrefix ?? "q32"}-mixed-${crypto.randomUUID()}`;
|
|
74
84
|
const bodyBoundary = `${input.boundaryPrefix ?? "q32"}-body-${crypto.randomUUID()}`;
|
|
@@ -120,6 +130,24 @@ export function buildRawMimeMessage(input: RawMimeMessageInput): string {
|
|
|
120
130
|
return parts.join("\r\n");
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
export function buildResentMimeMessage(input: ResentMimeMessageInput): string {
|
|
134
|
+
const headers = [
|
|
135
|
+
`Resent-From: ${formatEmailAddress(input.resentFrom)}`,
|
|
136
|
+
...(input.resentSender ? [`Resent-Sender: ${formatEmailAddress(input.resentSender)}`] : []),
|
|
137
|
+
`Resent-To: ${input.resentTo.map(formatEmailAddress).join(", ")}`,
|
|
138
|
+
`Resent-Date: ${(input.resentDate ?? new Date()).toUTCString()}`,
|
|
139
|
+
`Resent-Message-ID: ${sanitizeEmailHeaderValue(input.resentMessageId ?? `<${crypto.randomUUID()}@resent.q32.email>`)}`,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
for (const [name, value] of Object.entries(input.headers ?? {})) {
|
|
143
|
+
const headerName = sanitizeHeaderName(name);
|
|
144
|
+
const headerValue = sanitizeEmailHeaderValue(value);
|
|
145
|
+
if (headerName && headerValue) headers.push(`${headerName}: ${headerValue}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return `${headers.join("\r\n")}\r\n${normalizeRawMessage(input.rawMessage)}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
function sanitizeHeaderName(value: string): string {
|
|
124
152
|
const name = value.trim();
|
|
125
153
|
return /^[A-Za-z0-9-]+$/.test(name) ? name : "";
|
|
@@ -136,6 +164,11 @@ function attachmentDataToBase64(value: Uint8Array | string): string {
|
|
|
136
164
|
return btoa(binary);
|
|
137
165
|
}
|
|
138
166
|
|
|
167
|
+
function normalizeRawMessage(value: Uint8Array | string): string {
|
|
168
|
+
const text = typeof value === "string" ? value : new TextDecoder("utf-8", { fatal: false }).decode(value);
|
|
169
|
+
return text.replace(/^\uFEFF/, "").replace(/\r?\n/g, "\r\n");
|
|
170
|
+
}
|
|
171
|
+
|
|
139
172
|
function base64Utf8(value: string): string {
|
|
140
173
|
const bytes = new TextEncoder().encode(value);
|
|
141
174
|
let binary = "";
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { FetchLike } from "./http.js";
|
|
2
|
+
import { defaultFetch } from "./http.js";
|
|
3
|
+
import type { PurchaseConversionEvent } from "./conversion-outbox.js";
|
|
4
|
+
|
|
5
|
+
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
6
|
+
const GOOGLE_ADS_ORIGIN = "https://googleads.googleapis.com";
|
|
7
|
+
|
|
8
|
+
export type GoogleAdsConversionConfig = {
|
|
9
|
+
developerToken: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
customerId: string;
|
|
14
|
+
conversionActionId: string;
|
|
15
|
+
loginCustomerId?: string;
|
|
16
|
+
apiVersion?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type GoogleAdsUploadResult =
|
|
20
|
+
| { status: "sent"; jobId: string | null; requestId: string | null }
|
|
21
|
+
| { status: "skipped"; reason: "missing_google_click_id" };
|
|
22
|
+
|
|
23
|
+
export class GoogleAdsConversionError extends Error {
|
|
24
|
+
constructor(
|
|
25
|
+
message: string,
|
|
26
|
+
readonly code: string,
|
|
27
|
+
readonly retryable: boolean,
|
|
28
|
+
readonly status: number | null,
|
|
29
|
+
) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = "GoogleAdsConversionError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class GoogleAdsConversionClient {
|
|
36
|
+
private accessToken: { value: string; expiresAt: number } | null = null;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
private readonly config: GoogleAdsConversionConfig,
|
|
40
|
+
private readonly fetchImpl: FetchLike = defaultFetch,
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
async uploadPurchase(event: PurchaseConversionEvent): Promise<GoogleAdsUploadResult> {
|
|
44
|
+
const clickId = googleClickId(event);
|
|
45
|
+
if (!clickId) return { status: "skipped", reason: "missing_google_click_id" };
|
|
46
|
+
const accessToken = await this.getAccessToken();
|
|
47
|
+
const customerId = digitsOnly(this.config.customerId, "customerId");
|
|
48
|
+
const actionId = digitsOnly(this.config.conversionActionId, "conversionActionId");
|
|
49
|
+
const apiVersion = this.config.apiVersion ?? "v24";
|
|
50
|
+
const conversion = {
|
|
51
|
+
conversionAction: `customers/${customerId}/conversionActions/${actionId}`,
|
|
52
|
+
conversionDateTime: googleAdsDateTime(event.conversionAt),
|
|
53
|
+
conversionValue: event.value,
|
|
54
|
+
currencyCode: event.currency,
|
|
55
|
+
orderId: event.orderId,
|
|
56
|
+
conversionEnvironment: "WEB",
|
|
57
|
+
[clickId.kind]: clickId.value,
|
|
58
|
+
};
|
|
59
|
+
const headers: Record<string, string> = {
|
|
60
|
+
authorization: `Bearer ${accessToken}`,
|
|
61
|
+
"content-type": "application/json",
|
|
62
|
+
"developer-token": this.config.developerToken,
|
|
63
|
+
};
|
|
64
|
+
if (this.config.loginCustomerId) {
|
|
65
|
+
headers["login-customer-id"] = digitsOnly(this.config.loginCustomerId, "loginCustomerId");
|
|
66
|
+
}
|
|
67
|
+
const response = await this.fetchImpl(
|
|
68
|
+
`${GOOGLE_ADS_ORIGIN}/${apiVersion}/customers/${customerId}:uploadClickConversions`,
|
|
69
|
+
{ method: "POST", headers, body: JSON.stringify({ conversions: [conversion], partialFailure: true }) },
|
|
70
|
+
);
|
|
71
|
+
const payload = await readJson(response);
|
|
72
|
+
if (!response.ok) throw googleAdsHttpError(response.status, payload);
|
|
73
|
+
const partialFailure = recordValue(payload, "partialFailureError");
|
|
74
|
+
if (partialFailure) throw googleAdsPartialFailure(partialFailure);
|
|
75
|
+
return {
|
|
76
|
+
status: "sent",
|
|
77
|
+
jobId: stringValue(payload, "jobId"),
|
|
78
|
+
requestId: response.headers.get("request-id"),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async getAccessToken(): Promise<string> {
|
|
83
|
+
if (this.accessToken && this.accessToken.expiresAt > Date.now() + 60_000) return this.accessToken.value;
|
|
84
|
+
const response = await this.fetchImpl(GOOGLE_TOKEN_URL, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
87
|
+
body: new URLSearchParams({
|
|
88
|
+
client_id: this.config.clientId,
|
|
89
|
+
client_secret: this.config.clientSecret,
|
|
90
|
+
refresh_token: this.config.refreshToken,
|
|
91
|
+
grant_type: "refresh_token",
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
const payload = await readJson(response);
|
|
95
|
+
const token = stringValue(payload, "access_token");
|
|
96
|
+
if (!response.ok || !token) {
|
|
97
|
+
const code = stringValue(payload, "error") ?? "oauth_refresh_failed";
|
|
98
|
+
throw new GoogleAdsConversionError(`Google OAuth refresh failed: ${code}`, code, response.status >= 500, response.status);
|
|
99
|
+
}
|
|
100
|
+
const expiresIn = numberValue(payload, "expires_in") ?? 3600;
|
|
101
|
+
this.accessToken = { value: token, expiresAt: Date.now() + expiresIn * 1000 };
|
|
102
|
+
return token;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function googleClickId(event: PurchaseConversionEvent): { kind: "gclid" | "wbraid" | "gbraid"; value: string } | null {
|
|
107
|
+
const candidates = [
|
|
108
|
+
["gclid", event.clickIds.gclid],
|
|
109
|
+
["wbraid", event.clickIds.wbraid],
|
|
110
|
+
["gbraid", event.clickIds.gbraid],
|
|
111
|
+
] as const;
|
|
112
|
+
for (const [kind, value] of candidates) if (value?.trim()) return { kind, value: value.trim() };
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function googleAdsDateTime(value: string): string {
|
|
117
|
+
const date = new Date(value);
|
|
118
|
+
if (!Number.isFinite(date.getTime())) throw new Error("Google Ads conversion time must be a valid date-time");
|
|
119
|
+
return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "+00:00");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function digitsOnly(value: string, field: string): string {
|
|
123
|
+
const normalized = value.replaceAll("-", "").trim();
|
|
124
|
+
if (!/^\d+$/.test(normalized)) throw new Error(`Google Ads ${field} must contain only digits`);
|
|
125
|
+
return normalized;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function readJson(response: Response): Promise<Record<string, unknown>> {
|
|
129
|
+
return (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function googleAdsHttpError(status: number, payload: Record<string, unknown>): GoogleAdsConversionError {
|
|
133
|
+
const error = recordValue(payload, "error");
|
|
134
|
+
const code = stringValue(error ?? payload, "status") ?? `http_${status}`;
|
|
135
|
+
const message = stringValue(error ?? payload, "message") ?? `Google Ads upload failed with HTTP ${status}`;
|
|
136
|
+
return new GoogleAdsConversionError(message, code, status === 429 || status >= 500, status);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function googleAdsPartialFailure(error: Record<string, unknown>): GoogleAdsConversionError {
|
|
140
|
+
const code = firstGoogleAdsErrorCode(error) ?? "partial_failure";
|
|
141
|
+
const message = stringValue(error, "message") ?? "Google Ads rejected the conversion";
|
|
142
|
+
const retryable = ["INTERNAL", "RESOURCE_EXHAUSTED", "UNAVAILABLE", "TOO_RECENT_CONVERSION_ACTION"].includes(code);
|
|
143
|
+
return new GoogleAdsConversionError(message, code, retryable, 200);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function firstGoogleAdsErrorCode(error: Record<string, unknown>): string | null {
|
|
147
|
+
const details = error.details;
|
|
148
|
+
if (!Array.isArray(details)) return null;
|
|
149
|
+
for (const detail of details) {
|
|
150
|
+
if (!detail || typeof detail !== "object") continue;
|
|
151
|
+
const errors = (detail as Record<string, unknown>).errors;
|
|
152
|
+
if (!Array.isArray(errors)) continue;
|
|
153
|
+
for (const item of errors) {
|
|
154
|
+
if (!item || typeof item !== "object") continue;
|
|
155
|
+
const errorCode = recordValue(item as Record<string, unknown>, "errorCode");
|
|
156
|
+
if (!errorCode) continue;
|
|
157
|
+
for (const value of Object.values(errorCode)) if (typeof value === "string") return value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function recordValue(input: Record<string, unknown>, key: string): Record<string, unknown> | null {
|
|
164
|
+
const value = input[key];
|
|
165
|
+
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function stringValue(input: Record<string, unknown>, key: string): string | null {
|
|
169
|
+
const value = input[key];
|
|
170
|
+
return typeof value === "string" ? value : typeof value === "number" ? String(value) : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function numberValue(input: Record<string, unknown>, key: string): number | null {
|
|
174
|
+
const value = input[key];
|
|
175
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
176
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,12 +4,14 @@ export * from "./auth.js";
|
|
|
4
4
|
export * from "./aws.js";
|
|
5
5
|
export * from "./billing.js";
|
|
6
6
|
export * from "./cloudflare.js";
|
|
7
|
+
export * from "./conversion-outbox.js";
|
|
7
8
|
export * from "./crypto.js";
|
|
8
9
|
export * from "./d1.js";
|
|
9
10
|
export * from "./encoding.js";
|
|
10
11
|
export * from "./email.js";
|
|
11
12
|
export * from "./env.js";
|
|
12
13
|
export * from "./hono.js";
|
|
14
|
+
export * from "./google-ads.js";
|
|
13
15
|
export * from "./http.js";
|
|
14
16
|
export * from "./ids.js";
|
|
15
17
|
export * from "./jobs.js";
|
package/src/ses.ts
CHANGED
|
@@ -102,6 +102,37 @@ export async function sendSesEmail(
|
|
|
102
102
|
return { provider: "ses", messageId };
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
export async function sendSesRawEmail(
|
|
106
|
+
config: AwsConfig,
|
|
107
|
+
input: {
|
|
108
|
+
fromEmailAddress?: string;
|
|
109
|
+
destination: string[];
|
|
110
|
+
rawMime: string;
|
|
111
|
+
},
|
|
112
|
+
options: SesSendOptions = {},
|
|
113
|
+
): Promise<SesSendResult> {
|
|
114
|
+
const response = await awsFetch(config, `https://email.${config.region}.amazonaws.com/v2/email/outbound-emails`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { "content-type": "application/json" },
|
|
117
|
+
body: JSON.stringify({
|
|
118
|
+
...(input.fromEmailAddress ? { FromEmailAddress: input.fromEmailAddress } : {}),
|
|
119
|
+
Destination: {
|
|
120
|
+
ToAddresses: input.destination,
|
|
121
|
+
},
|
|
122
|
+
Content: {
|
|
123
|
+
Raw: {
|
|
124
|
+
Data: textToBase64(input.rawMime),
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
}, { service: "ses", fetcher: options.fetcher });
|
|
129
|
+
const text = await response.text();
|
|
130
|
+
if (!response.ok) throw new Error(`SES raw send failed: ${response.status} ${text.slice(0, 300)}`);
|
|
131
|
+
const payload = parseJsonObject(text);
|
|
132
|
+
const messageId = stringValue(payload.MessageId) ?? stringValue(payload.messageId) ?? "";
|
|
133
|
+
return { provider: "ses", messageId };
|
|
134
|
+
}
|
|
135
|
+
|
|
105
136
|
export async function sesQuery(
|
|
106
137
|
config: AwsConfig,
|
|
107
138
|
params: Record<string, string>,
|