@polymorfa/nextjs 0.1.0-dev.20260922094209
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/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/development-store.d.ts +35 -0
- package/dist/development-store.d.ts.map +1 -0
- package/dist/development-store.js +186 -0
- package/dist/express.d.ts +21 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +92 -0
- package/dist/handler.d.ts +229 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +434 -0
- package/dist/hono.d.ts +15 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +11 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/media-route.d.ts +76 -0
- package/dist/media-route.d.ts.map +1 -0
- package/dist/media-route.js +151 -0
- package/dist/messaging-token-mint.d.ts +40 -0
- package/dist/messaging-token-mint.d.ts.map +1 -0
- package/dist/messaging-token-mint.js +38 -0
- package/dist/template-builder-route.d.ts +123 -0
- package/dist/template-builder-route.d.ts.map +1 -0
- package/dist/template-builder-route.js +187 -0
- package/dist/token-route.d.ts +19 -0
- package/dist/token-route.d.ts.map +1 -0
- package/dist/token-route.js +60 -0
- package/dist/webhook.d.ts +10 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +16 -0
- package/package.json +54 -0
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PolymorfaHandler } from "./handler.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serves a Polymorfa handler from Hono (or any framework whose context
|
|
4
|
+
* exposes the Fetch `Request` as `c.req.raw`):
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* app.all("/api/polymorfa/*", toHono(handler));
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function toHono(handler: PolymorfaHandler): (context: {
|
|
11
|
+
readonly req: {
|
|
12
|
+
readonly raw: Request;
|
|
13
|
+
};
|
|
14
|
+
}) => Promise<Response>;
|
|
15
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,gBAAgB,GACxB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAE7E"}
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serves a Polymorfa handler from Hono (or any framework whose context
|
|
3
|
+
* exposes the Fetch `Request` as `c.req.raw`):
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* app.all("/api/polymorfa/*", toHono(handler));
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export function toHono(handler) {
|
|
10
|
+
return (context) => handler.handle(context.req.raw);
|
|
11
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createClientTokenRoute, type ClientTokenClaims, type ClientTokenMint, type ClientTokenRouteOptions, type ClientTokenSubject, } from "./token-route.js";
|
|
2
|
+
export { createMessagingClientTokenMint, type MessagingClientTokenMintOptions, type MessagingClientTokenMintRequest, type CustomerClientTokenAction, type MessagingClientTokenMintResource, } from "./messaging-token-mint.js";
|
|
3
|
+
export { readVerifiedWebhook, type WebhookConstructor, type WebhookRequestOptions, } from "./webhook.js";
|
|
4
|
+
export { createTemplateBuilderRoute, type TemplateBuilderRouteOptions, type TemplateRouteButton, type TemplateRouteDefinition, type TemplateRouteDraft, type TemplateRouteHeader, type TemplateRouteResource, type TemplateRouteSubject, } from "./template-builder-route.js";
|
|
5
|
+
export { createMediaDownloadRoute, INLINE_MEDIA_TYPES, mediaTypeEssence, safeMediaHeaders, type MediaDownloadGrant, type MediaDownloadRouteOptions, type MediaDownloadRouteResource, } from "./media-route.js";
|
|
6
|
+
export { conversationOf, createPolymorfaHandler, type ClientTokenMintInput, type HistoryContact, type HistoryConversation, type HistoryMessage, type HistoryPage, type PolymorfaGrant, type PolymorfaGrantInput, type PolymorfaHandler, type PolymorfaHandlerClient, type PolymorfaHandlerOptions, type PolymorfaPermission, type PolymorfaRouteContext, type PolymorfaRouteHandler, type QuickLinkInput, type RelayEvent, } from "./handler.js";
|
|
7
|
+
export { createDevelopmentInboxStore, type DevelopmentInboxStore, type DevelopmentInboxStoreOptions, } from "./development-store.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,8BAA8B,EAC9B,KAAK,+BAA+B,EACpC,KAAK,+BAA+B,EACpC,KAAK,yBAAyB,EAC9B,KAAK,gCAAgC,GACtC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,GAClC,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createClientTokenRoute, } from "./token-route.js";
|
|
2
|
+
export { createMessagingClientTokenMint, } from "./messaging-token-mint.js";
|
|
3
|
+
export { readVerifiedWebhook, } from "./webhook.js";
|
|
4
|
+
export { createTemplateBuilderRoute, } from "./template-builder-route.js";
|
|
5
|
+
export { createMediaDownloadRoute, INLINE_MEDIA_TYPES, mediaTypeEssence, safeMediaHeaders, } from "./media-route.js";
|
|
6
|
+
export { conversationOf, createPolymorfaHandler, } from "./handler.js";
|
|
7
|
+
export { createDevelopmentInboxStore, } from "./development-store.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Structural subset of `MessagingClient["media"]` used by the route. */
|
|
2
|
+
export interface MediaDownloadRouteResource {
|
|
3
|
+
downloadStream(mediaId: string, options?: {
|
|
4
|
+
readonly signal?: AbortSignal;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
readonly body: ReadableStream<Uint8Array>;
|
|
7
|
+
readonly contentType?: string;
|
|
8
|
+
readonly contentLength?: number;
|
|
9
|
+
readonly filename?: string;
|
|
10
|
+
}>;
|
|
11
|
+
downloadUrl(mediaId: string, options?: {
|
|
12
|
+
readonly signal?: AbortSignal;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
readonly streamed: false;
|
|
15
|
+
readonly url: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly streamed: true;
|
|
18
|
+
readonly url: undefined;
|
|
19
|
+
}>;
|
|
20
|
+
downloadFromWhatsApp?(input: {
|
|
21
|
+
readonly type: string;
|
|
22
|
+
readonly media?: string;
|
|
23
|
+
}, options?: {
|
|
24
|
+
readonly signal?: AbortSignal;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
readonly body: ReadableStream<Uint8Array>;
|
|
27
|
+
readonly mimetype: string;
|
|
28
|
+
readonly fileName?: string;
|
|
29
|
+
readonly fileLength?: number;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/** What the caller may download, decided by `authorize`. */
|
|
33
|
+
export type MediaDownloadGrant = {
|
|
34
|
+
/** Polymorfa media ID, for `redirect` and `proxy` modes. */
|
|
35
|
+
readonly mediaId: string;
|
|
36
|
+
/** Overrides the upstream filename in `Content-Disposition`. */
|
|
37
|
+
readonly filename?: string;
|
|
38
|
+
} | {
|
|
39
|
+
/**
|
|
40
|
+
* Stored message webhook fields, for `whatsapp` mode. `media` contains
|
|
41
|
+
* a decryption key: load it from your own storage, never from the
|
|
42
|
+
* request.
|
|
43
|
+
*/
|
|
44
|
+
readonly message: {
|
|
45
|
+
readonly type: string;
|
|
46
|
+
readonly media: string;
|
|
47
|
+
};
|
|
48
|
+
readonly filename?: string;
|
|
49
|
+
};
|
|
50
|
+
export interface MediaDownloadRouteOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Required. Authenticate the request and return what it may download, or
|
|
53
|
+
* `null` to deny. Resolve the media from your own records rather than
|
|
54
|
+
* trusting an ID supplied by the browser. Throwing also denies.
|
|
55
|
+
*/
|
|
56
|
+
readonly authorize: (request: Request) => Promise<MediaDownloadGrant | null> | MediaDownloadGrant | null;
|
|
57
|
+
/** A server-side `MessagingClient`. */
|
|
58
|
+
readonly client: {
|
|
59
|
+
readonly media: MediaDownloadRouteResource;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* `redirect` answers 302 to a short-lived signed storage URL (and proxies
|
|
63
|
+
* when the API streams instead). `proxy` streams bytes through this route.
|
|
64
|
+
* `whatsapp` downloads and decrypts from the WhatsApp CDN server-side.
|
|
65
|
+
*/
|
|
66
|
+
readonly mode: "redirect" | "proxy" | "whatsapp";
|
|
67
|
+
}
|
|
68
|
+
/** Types rendered inline. Everything else is served as an attachment. */
|
|
69
|
+
export declare const INLINE_MEDIA_TYPES: ReadonlySet<string>;
|
|
70
|
+
/** Creates a Next.js App Router-compatible GET route without importing Next.js. */
|
|
71
|
+
export declare function createMediaDownloadRoute(options: MediaDownloadRouteOptions): (request: Request) => Promise<Response>;
|
|
72
|
+
/** Returns the lowercase MIME essence, or undefined when malformed. */
|
|
73
|
+
export declare function mediaTypeEssence(value: string | undefined): string | undefined;
|
|
74
|
+
/** Builds the safe response headers used by proxied media. */
|
|
75
|
+
export declare function safeMediaHeaders(contentType: string | undefined, filename: string | undefined, contentLength?: number): Record<string, string>;
|
|
76
|
+
//# sourceMappingURL=media-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-route.d.ts","sourceRoot":"","sources":["../src/media-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** Types rendered inline. Everything else is served as an attachment. */
|
|
2
|
+
export const INLINE_MEDIA_TYPES = new Set([
|
|
3
|
+
"image/jpeg",
|
|
4
|
+
"image/png",
|
|
5
|
+
"image/gif",
|
|
6
|
+
"image/webp",
|
|
7
|
+
"image/avif",
|
|
8
|
+
"audio/mpeg",
|
|
9
|
+
"audio/ogg",
|
|
10
|
+
"audio/aac",
|
|
11
|
+
"audio/mp4",
|
|
12
|
+
"audio/wav",
|
|
13
|
+
"audio/webm",
|
|
14
|
+
"video/mp4",
|
|
15
|
+
"video/webm",
|
|
16
|
+
"video/ogg",
|
|
17
|
+
"video/3gpp",
|
|
18
|
+
"video/quicktime",
|
|
19
|
+
]);
|
|
20
|
+
const baseHeaders = {
|
|
21
|
+
"Cache-Control": "private, no-store",
|
|
22
|
+
Vary: "Cookie, Authorization",
|
|
23
|
+
"X-Content-Type-Options": "nosniff",
|
|
24
|
+
"Referrer-Policy": "no-referrer",
|
|
25
|
+
};
|
|
26
|
+
/** Creates a Next.js App Router-compatible GET route without importing Next.js. */
|
|
27
|
+
export function createMediaDownloadRoute(options) {
|
|
28
|
+
if (typeof options.authorize !== "function") {
|
|
29
|
+
throw new TypeError("authorize must be a function.");
|
|
30
|
+
}
|
|
31
|
+
if (typeof options.client?.media?.downloadStream !== "function" ||
|
|
32
|
+
typeof options.client.media.downloadUrl !== "function") {
|
|
33
|
+
throw new TypeError("client must be a Polymorfa MessagingClient.");
|
|
34
|
+
}
|
|
35
|
+
if (!["redirect", "proxy", "whatsapp"].includes(options.mode)) {
|
|
36
|
+
throw new TypeError("mode must be redirect, proxy, or whatsapp.");
|
|
37
|
+
}
|
|
38
|
+
if (options.mode === "whatsapp" &&
|
|
39
|
+
typeof options.client.media.downloadFromWhatsApp !== "function") {
|
|
40
|
+
throw new TypeError("client.media.downloadFromWhatsApp is required.");
|
|
41
|
+
}
|
|
42
|
+
const media = options.client.media;
|
|
43
|
+
return async (request) => {
|
|
44
|
+
if (request.method !== "GET") {
|
|
45
|
+
return jsonError(405, "method_not_allowed", "Use GET.", {
|
|
46
|
+
Allow: "GET",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
let grant;
|
|
50
|
+
try {
|
|
51
|
+
grant = await options.authorize(request);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
grant = null;
|
|
55
|
+
}
|
|
56
|
+
if (!isGrant(grant, options.mode)) {
|
|
57
|
+
return jsonError(403, "forbidden", "You cannot download this media.");
|
|
58
|
+
}
|
|
59
|
+
const signal = request.signal;
|
|
60
|
+
try {
|
|
61
|
+
if (options.mode === "whatsapp" && "message" in grant) {
|
|
62
|
+
const download = await media.downloadFromWhatsApp(grant.message, {
|
|
63
|
+
signal,
|
|
64
|
+
});
|
|
65
|
+
return proxied(download.body, download.mimetype, grant.filename ?? download.fileName, undefined);
|
|
66
|
+
}
|
|
67
|
+
if (!("mediaId" in grant)) {
|
|
68
|
+
return jsonError(403, "forbidden", "You cannot download this media.");
|
|
69
|
+
}
|
|
70
|
+
if (options.mode === "redirect") {
|
|
71
|
+
const target = await media.downloadUrl(grant.mediaId, { signal });
|
|
72
|
+
if (!target.streamed) {
|
|
73
|
+
return new Response(null, {
|
|
74
|
+
status: 302,
|
|
75
|
+
headers: { ...baseHeaders, Location: target.url },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const download = await media.downloadStream(grant.mediaId, { signal });
|
|
80
|
+
return proxied(download.body, download.contentType, grant.filename ?? download.filename, download.contentLength);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const status = error?.status;
|
|
84
|
+
if (status === 404) {
|
|
85
|
+
return jsonError(404, "not_found", "Media not found.");
|
|
86
|
+
}
|
|
87
|
+
return jsonError(502, "media_unavailable", "The media could not be downloaded.");
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function isGrant(grant, mode) {
|
|
92
|
+
if (typeof grant !== "object" || grant === null)
|
|
93
|
+
return false;
|
|
94
|
+
if (mode === "whatsapp") {
|
|
95
|
+
return ("message" in grant &&
|
|
96
|
+
typeof grant.message?.media === "string" &&
|
|
97
|
+
typeof grant.message.type === "string");
|
|
98
|
+
}
|
|
99
|
+
return ("mediaId" in grant &&
|
|
100
|
+
typeof grant.mediaId === "string" &&
|
|
101
|
+
grant.mediaId.length > 0);
|
|
102
|
+
}
|
|
103
|
+
/** Returns the lowercase MIME essence, or undefined when malformed. */
|
|
104
|
+
export function mediaTypeEssence(value) {
|
|
105
|
+
const essence = value?.split(";")[0]?.trim().toLowerCase();
|
|
106
|
+
return essence !== undefined &&
|
|
107
|
+
/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(essence)
|
|
108
|
+
? essence
|
|
109
|
+
: undefined;
|
|
110
|
+
}
|
|
111
|
+
/** Builds the safe response headers used by proxied media. */
|
|
112
|
+
export function safeMediaHeaders(contentType, filename, contentLength) {
|
|
113
|
+
const essence = mediaTypeEssence(contentType);
|
|
114
|
+
const inline = essence !== undefined && INLINE_MEDIA_TYPES.has(essence);
|
|
115
|
+
const headers = {
|
|
116
|
+
...baseHeaders,
|
|
117
|
+
"Content-Type": inline ? essence : "application/octet-stream",
|
|
118
|
+
"Content-Security-Policy": "sandbox",
|
|
119
|
+
"Content-Disposition": contentDisposition(inline ? "inline" : "attachment", filename),
|
|
120
|
+
};
|
|
121
|
+
if (contentLength !== undefined && Number.isSafeInteger(contentLength)) {
|
|
122
|
+
headers["Content-Length"] = String(contentLength);
|
|
123
|
+
}
|
|
124
|
+
return headers;
|
|
125
|
+
}
|
|
126
|
+
function proxied(body, contentType, filename, contentLength) {
|
|
127
|
+
return new Response(body, {
|
|
128
|
+
status: 200,
|
|
129
|
+
headers: safeMediaHeaders(contentType, filename, contentLength),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function contentDisposition(type, filename) {
|
|
133
|
+
if (filename === undefined)
|
|
134
|
+
return type;
|
|
135
|
+
const clean = Array.from(filename)
|
|
136
|
+
.filter((character) => {
|
|
137
|
+
const code = character.charCodeAt(0);
|
|
138
|
+
return code >= 0x20 && code !== 0x7f;
|
|
139
|
+
})
|
|
140
|
+
.join("")
|
|
141
|
+
.replace(/[\\/]/g, "_")
|
|
142
|
+
.slice(0, 255);
|
|
143
|
+
if (clean.length === 0)
|
|
144
|
+
return type;
|
|
145
|
+
const ascii = clean.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_");
|
|
146
|
+
const encoded = encodeURIComponent(clean).replace(/['()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
147
|
+
return `${type}; filename="${ascii}"; filename*=UTF-8''${encoded}`;
|
|
148
|
+
}
|
|
149
|
+
function jsonError(status, code, message, headers = {}) {
|
|
150
|
+
return Response.json({ error: { code, message } }, { status, headers: { ...baseHeaders, ...headers } });
|
|
151
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ClientTokenMint, ClientTokenSubject } from "./token-route.js";
|
|
2
|
+
/** Actions a Customer-scoped client token can carry in `allow`. */
|
|
3
|
+
export type CustomerClientTokenAction = "send_message" | "send_reaction" | "send_typing" | "send_seen" | "read_presence" | "subscribe_presence" | "read_contact";
|
|
4
|
+
/**
|
|
5
|
+
* What `resolve` returns: a token for one `session`, or (beta) for the
|
|
6
|
+
* numbers one `customer` owns, optionally narrowed by `allow`.
|
|
7
|
+
*/
|
|
8
|
+
export type MessagingClientTokenMintRequest = {
|
|
9
|
+
readonly session: string;
|
|
10
|
+
readonly ephemeralId: string;
|
|
11
|
+
readonly ttlSeconds?: number;
|
|
12
|
+
readonly customer?: never;
|
|
13
|
+
readonly allow?: never;
|
|
14
|
+
} | {
|
|
15
|
+
/** Polymorfa Customer ID. Never pass your own external ID. */
|
|
16
|
+
readonly customer: string;
|
|
17
|
+
readonly allow?: readonly CustomerClientTokenAction[];
|
|
18
|
+
readonly ephemeralId: string;
|
|
19
|
+
readonly ttlSeconds?: number;
|
|
20
|
+
readonly session?: never;
|
|
21
|
+
};
|
|
22
|
+
export interface MessagingClientTokenMintResource {
|
|
23
|
+
mint(input: MessagingClientTokenMintRequest, options: {
|
|
24
|
+
readonly signal: AbortSignal;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
readonly data: {
|
|
27
|
+
readonly success: true;
|
|
28
|
+
readonly data: {
|
|
29
|
+
readonly token: string;
|
|
30
|
+
readonly expiresAt: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface MessagingClientTokenMintOptions {
|
|
36
|
+
readonly clientTokens: MessagingClientTokenMintResource;
|
|
37
|
+
readonly resolve: (subject: ClientTokenSubject, request: Request) => MessagingClientTokenMintRequest | Promise<MessagingClientTokenMintRequest>;
|
|
38
|
+
}
|
|
39
|
+
export declare function createMessagingClientTokenMint(options: MessagingClientTokenMintOptions): ClientTokenMint;
|
|
40
|
+
//# sourceMappingURL=messaging-token-mint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-token-mint.d.ts","sourceRoot":"","sources":["../src/messaging-token-mint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EACf,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAE1B,mEAAmE;AACnE,MAAM,MAAM,yBAAyB,GACjC,cAAc,GACd,eAAe,GACf,aAAa,GACb,WAAW,GACX,eAAe,GACf,oBAAoB,GACpB,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,+BAA+B,GACvC;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB,GACD;IACE,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;CAC1B,CAAC;AAEN,MAAM,WAAW,gCAAgC;IAC/C,IAAI,CACF,KAAK,EAAE,+BAA+B,EACtC,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,GACxC,OAAO,CAAC;QACT,QAAQ,CAAC,IAAI,EAAE;YACb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;YACvB,QAAQ,CAAC,IAAI,EAAE;gBACb,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;aAC5B,CAAC;SACH,CAAC;KACH,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,YAAY,EAAE,gCAAgC,CAAC;IACxD,QAAQ,CAAC,OAAO,EAAE,CAChB,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,OAAO,KAEhB,+BAA+B,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;CAC9E;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,eAAe,CAmCjB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function createMessagingClientTokenMint(options) {
|
|
2
|
+
if (typeof options.clientTokens?.mint !== "function" ||
|
|
3
|
+
typeof options.resolve !== "function") {
|
|
4
|
+
throw new TypeError("clientTokens.mint and resolve must be functions.");
|
|
5
|
+
}
|
|
6
|
+
return async (subject, request) => {
|
|
7
|
+
const input = await options.resolve(subject, request);
|
|
8
|
+
validateInput(input);
|
|
9
|
+
const response = await options.clientTokens.mint(input, {
|
|
10
|
+
signal: request.signal,
|
|
11
|
+
});
|
|
12
|
+
const token = response.data?.data?.token;
|
|
13
|
+
const expiresAt = Date.parse(response.data?.data?.expiresAt ?? "");
|
|
14
|
+
if (typeof token !== "string" ||
|
|
15
|
+
!token.startsWith("pmfa_ct_") ||
|
|
16
|
+
token.length <= "pmfa_ct_".length ||
|
|
17
|
+
!Number.isFinite(expiresAt)) {
|
|
18
|
+
throw new TypeError("The server SDK returned an invalid client token response.");
|
|
19
|
+
}
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
value: token,
|
|
22
|
+
audience: "browser",
|
|
23
|
+
expiresAt,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function validateInput(input) {
|
|
28
|
+
const hasSession = typeof input.session === "string" && input.session.length > 0;
|
|
29
|
+
const hasCustomer = typeof input.customer === "string" && input.customer.length > 0;
|
|
30
|
+
if (hasSession === hasCustomer ||
|
|
31
|
+
typeof input.ephemeralId !== "string" ||
|
|
32
|
+
input.ephemeralId.length === 0) {
|
|
33
|
+
throw new TypeError("Exactly one of session or customer, and an ephemeralId, are required to mint a client token.");
|
|
34
|
+
}
|
|
35
|
+
if (input.allow !== undefined && !hasCustomer) {
|
|
36
|
+
throw new TypeError("allow is only supported for Customer-scoped client tokens.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export interface TemplateRouteSubject {
|
|
2
|
+
readonly userId: string;
|
|
3
|
+
readonly projectId?: string;
|
|
4
|
+
readonly organizationId?: string;
|
|
5
|
+
}
|
|
6
|
+
export type TemplateRouteHeader = {
|
|
7
|
+
readonly format: "none";
|
|
8
|
+
} | {
|
|
9
|
+
readonly format: "text";
|
|
10
|
+
readonly text: string;
|
|
11
|
+
} | {
|
|
12
|
+
readonly format: "image" | "video" | "document";
|
|
13
|
+
readonly example?: string;
|
|
14
|
+
readonly filename?: string;
|
|
15
|
+
} | {
|
|
16
|
+
readonly format: "location";
|
|
17
|
+
readonly example?: {
|
|
18
|
+
readonly latitude: number;
|
|
19
|
+
readonly longitude: number;
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
readonly address?: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export type TemplateRouteButton = {
|
|
25
|
+
readonly type: "quick_reply";
|
|
26
|
+
readonly text: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly type: "url";
|
|
29
|
+
readonly text: string;
|
|
30
|
+
readonly url: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly type: "phone";
|
|
33
|
+
readonly text: string;
|
|
34
|
+
readonly phone: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: "copy_code";
|
|
37
|
+
readonly text?: string;
|
|
38
|
+
readonly example?: string;
|
|
39
|
+
};
|
|
40
|
+
export interface TemplateRouteDefinition {
|
|
41
|
+
readonly version: 1;
|
|
42
|
+
readonly kind: "standard" | "carousel" | "authentication" | "limited_time_offer";
|
|
43
|
+
readonly category: "MARKETING" | "UTILITY" | "AUTHENTICATION";
|
|
44
|
+
readonly language: string;
|
|
45
|
+
readonly header?: TemplateRouteHeader;
|
|
46
|
+
readonly body: string;
|
|
47
|
+
readonly footer?: string;
|
|
48
|
+
readonly buttons?: readonly TemplateRouteButton[];
|
|
49
|
+
readonly carousel?: {
|
|
50
|
+
readonly cards: readonly {
|
|
51
|
+
readonly header: Extract<TemplateRouteHeader, {
|
|
52
|
+
readonly format: "image" | "video" | "document";
|
|
53
|
+
}>;
|
|
54
|
+
readonly body: string;
|
|
55
|
+
readonly buttons?: readonly TemplateRouteButton[];
|
|
56
|
+
}[];
|
|
57
|
+
};
|
|
58
|
+
readonly authentication?: {
|
|
59
|
+
readonly otpType: "copy_code" | "one_tap";
|
|
60
|
+
readonly codeExample?: string;
|
|
61
|
+
readonly addSecurityRecommendation?: boolean;
|
|
62
|
+
readonly codeExpirationMinutes?: number;
|
|
63
|
+
};
|
|
64
|
+
readonly limitedTimeOffer?: {
|
|
65
|
+
readonly text: string;
|
|
66
|
+
readonly hasExpiration: boolean;
|
|
67
|
+
};
|
|
68
|
+
readonly variables: readonly {
|
|
69
|
+
readonly name: string;
|
|
70
|
+
readonly type: "text" | "number" | "currency" | "date_time";
|
|
71
|
+
readonly example: string;
|
|
72
|
+
}[];
|
|
73
|
+
}
|
|
74
|
+
export interface TemplateRouteDraft {
|
|
75
|
+
readonly name: string;
|
|
76
|
+
readonly definition: TemplateRouteDefinition;
|
|
77
|
+
readonly sampleValues?: Readonly<Record<string, string>>;
|
|
78
|
+
}
|
|
79
|
+
interface SdkResponse<T> {
|
|
80
|
+
readonly data: {
|
|
81
|
+
readonly success: boolean;
|
|
82
|
+
readonly data: T;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export interface TemplateRouteResource {
|
|
86
|
+
/** Enables the `list` action. */
|
|
87
|
+
list?(projectSlug: string, options: {
|
|
88
|
+
readonly signal: AbortSignal;
|
|
89
|
+
}): Promise<SdkResponse<unknown>>;
|
|
90
|
+
create(projectSlug: string, body: TemplateRouteDraft, options: {
|
|
91
|
+
readonly signal: AbortSignal;
|
|
92
|
+
}): Promise<SdkResponse<unknown>>;
|
|
93
|
+
retrieve(projectSlug: string, templateId: string, options: {
|
|
94
|
+
readonly signal: AbortSignal;
|
|
95
|
+
}): Promise<SdkResponse<unknown>>;
|
|
96
|
+
update(projectSlug: string, templateId: string, body: TemplateRouteDraft, options: {
|
|
97
|
+
readonly signal: AbortSignal;
|
|
98
|
+
}): Promise<SdkResponse<unknown>>;
|
|
99
|
+
delete(projectSlug: string, templateId: string, options: {
|
|
100
|
+
readonly signal: AbortSignal;
|
|
101
|
+
}): Promise<unknown>;
|
|
102
|
+
preview(projectSlug: string, templateId: string, body: {
|
|
103
|
+
readonly values?: Readonly<Record<string, string>>;
|
|
104
|
+
readonly surface?: "cloud" | "whatsmeow" | "sandbox";
|
|
105
|
+
}, options: {
|
|
106
|
+
readonly signal: AbortSignal;
|
|
107
|
+
}): Promise<SdkResponse<unknown>>;
|
|
108
|
+
submit(projectSlug: string, templateId: string, body: {
|
|
109
|
+
readonly session: string;
|
|
110
|
+
}, options: {
|
|
111
|
+
readonly signal: AbortSignal;
|
|
112
|
+
}): Promise<SdkResponse<unknown>>;
|
|
113
|
+
}
|
|
114
|
+
export interface TemplateBuilderRouteOptions {
|
|
115
|
+
readonly templates: TemplateRouteResource;
|
|
116
|
+
readonly authorize: (request: Request) => TemplateRouteSubject | null | Promise<TemplateRouteSubject | null>;
|
|
117
|
+
readonly resolveProjectSlug: (subject: TemplateRouteSubject, request: Request) => string | Promise<string>;
|
|
118
|
+
readonly resolveSubmissionSession: (subject: TemplateRouteSubject, request: Request) => string | Promise<string>;
|
|
119
|
+
}
|
|
120
|
+
/** Creates a Next.js App Router-compatible, application-mediated template route. */
|
|
121
|
+
export declare function createTemplateBuilderRoute(options: TemplateBuilderRouteOptions): (request: Request) => Promise<Response>;
|
|
122
|
+
export {};
|
|
123
|
+
//# sourceMappingURL=template-builder-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-builder-route.d.ts","sourceRoot":"","sources":["../src/template-builder-route.ts"],"names":[],"mappings":"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"}
|