@whitewall/blip-sdk 0.0.136 → 0.0.138
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/dist/cjs/client.js +1 -1
- package/dist/cjs/client.js.map +1 -1
- package/dist/esm/client.js +1 -1
- package/dist/esm/client.js.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +117 -0
- package/src/index.ts +6 -0
- package/src/namespaces/account.ts +729 -0
- package/src/namespaces/activecampaign.ts +285 -0
- package/src/namespaces/analytics.ts +230 -0
- package/src/namespaces/billing.ts +17 -0
- package/src/namespaces/builder.ts +52 -0
- package/src/namespaces/configurations.ts +19 -0
- package/src/namespaces/context.ts +67 -0
- package/src/namespaces/desk.ts +679 -0
- package/src/namespaces/media.ts +39 -0
- package/src/namespaces/namespace.ts +125 -0
- package/src/namespaces/plugins.ts +223 -0
- package/src/namespaces/portal.ts +402 -0
- package/src/namespaces/scheduler.ts +88 -0
- package/src/namespaces/whatsapp.ts +383 -0
- package/src/sender/bliperror.ts +42 -0
- package/src/sender/enveloperesolver.ts +148 -0
- package/src/sender/gateway/customgatewaysender.ts +43 -0
- package/src/sender/http/httpsender.ts +94 -0
- package/src/sender/index.ts +7 -0
- package/src/sender/plugin/communication.ts +72 -0
- package/src/sender/plugin/pluginsender.ts +75 -0
- package/src/sender/security.ts +33 -0
- package/src/sender/sender.ts +145 -0
- package/src/sender/sessionnegotiator.ts +175 -0
- package/src/sender/tcp/tcpsender.ts +252 -0
- package/src/sender/throttler.ts +36 -0
- package/src/sender/websocket/websocketsender.ts +175 -0
- package/src/types/account.ts +84 -0
- package/src/types/analytics.ts +18 -0
- package/src/types/billing.ts +15 -0
- package/src/types/command.ts +47 -0
- package/src/types/commons.ts +16 -0
- package/src/types/desk.ts +51 -0
- package/src/types/envelope.ts +9 -0
- package/src/types/flow.ts +327 -0
- package/src/types/index.ts +13 -0
- package/src/types/message.ts +116 -0
- package/src/types/node.ts +86 -0
- package/src/types/notification.ts +18 -0
- package/src/types/plugins.ts +51 -0
- package/src/types/portal.ts +39 -0
- package/src/types/reason.ts +22 -0
- package/src/types/session.ts +22 -0
- package/src/types/whatsapp.ts +84 -0
- package/src/utils/odata.ts +114 -0
- package/src/utils/random.ts +3 -0
- package/src/utils/uri.ts +46 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Authentication } from '../sender/security.ts'
|
|
2
|
+
import type { Envelope } from './envelope.ts'
|
|
3
|
+
import type { Reason } from './reason.ts'
|
|
4
|
+
|
|
5
|
+
export type Session = Envelope & {
|
|
6
|
+
state: 'new' | 'negotiating' | 'authenticating' | 'established' | 'finishing' | 'finished' | 'failed'
|
|
7
|
+
|
|
8
|
+
encryptionOptions?: Array<string>
|
|
9
|
+
compressionOptions?: Array<string>
|
|
10
|
+
schemeOptions?: Array<string>
|
|
11
|
+
|
|
12
|
+
encryption?: string
|
|
13
|
+
compression?: string
|
|
14
|
+
scheme?: string
|
|
15
|
+
|
|
16
|
+
authentication?: Omit<Authentication, 'scheme'>
|
|
17
|
+
reason?: Reason
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const isSession = (value: Envelope): value is Session => {
|
|
21
|
+
return 'state' in value
|
|
22
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type WhatsappFlow = {
|
|
2
|
+
id: string
|
|
3
|
+
name: string
|
|
4
|
+
status: 'DRAFT' | 'PUBLISHED' | 'DEPRECATED'
|
|
5
|
+
categories: Array<string>
|
|
6
|
+
validation_errors: Array<{
|
|
7
|
+
error: string
|
|
8
|
+
error_type: string
|
|
9
|
+
message: string
|
|
10
|
+
}>
|
|
11
|
+
preview: {
|
|
12
|
+
preview_url: string
|
|
13
|
+
expires_at: string
|
|
14
|
+
}
|
|
15
|
+
endpoint_uri?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Components ref.: https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/components
|
|
19
|
+
|
|
20
|
+
export type HandleFormats = 'IMAGE' | 'VIDEO' | 'DOCUMENT'
|
|
21
|
+
|
|
22
|
+
type ExampleType<T extends 'TEXT' | 'LOCATION' | HandleFormats> = T extends 'TEXT'
|
|
23
|
+
? { header_text?: Array<string> }
|
|
24
|
+
: T extends HandleFormats
|
|
25
|
+
? { header_handle?: Array<string> }
|
|
26
|
+
: never
|
|
27
|
+
|
|
28
|
+
export type HeaderComponent<T extends 'TEXT' | 'LOCATION' | HandleFormats> = {
|
|
29
|
+
type: 'HEADER'
|
|
30
|
+
format: T
|
|
31
|
+
example: ExampleType<T>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type BodyComponent = {
|
|
35
|
+
type: 'BODY'
|
|
36
|
+
text: string
|
|
37
|
+
example?: {
|
|
38
|
+
body_text: [Array<string>]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type FooterComponent = {
|
|
43
|
+
type: 'FOOTER'
|
|
44
|
+
text: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type ButtonComponent = {
|
|
48
|
+
type: 'BUTTONS'
|
|
49
|
+
buttons: Array<{
|
|
50
|
+
type: string
|
|
51
|
+
// biome-ignore lint/suspicious/noExplicitAny: Too hard to type this properly
|
|
52
|
+
[key: string]: any
|
|
53
|
+
}>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type CarouselComponent = {
|
|
57
|
+
type: 'CAROUSEL'
|
|
58
|
+
cards: Array<{
|
|
59
|
+
components: Array<UnknownComponent>
|
|
60
|
+
}>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type UnknownComponent =
|
|
64
|
+
| HeaderComponent<'TEXT' | 'LOCATION' | HandleFormats>
|
|
65
|
+
| BodyComponent
|
|
66
|
+
| FooterComponent
|
|
67
|
+
| ButtonComponent
|
|
68
|
+
| CarouselComponent
|
|
69
|
+
|
|
70
|
+
export type MessageTemplate = {
|
|
71
|
+
id: string
|
|
72
|
+
name: string
|
|
73
|
+
category: 'AUTHENTICATION' | 'MARKETING' | 'UTILITY'
|
|
74
|
+
status: 'APPROVED' | 'REJECTED' | 'PENDING'
|
|
75
|
+
rejected_reason: string
|
|
76
|
+
language: string
|
|
77
|
+
last_update_time: string
|
|
78
|
+
components: Array<UnknownComponent>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type MessageTemplateVariable = {
|
|
82
|
+
type: 'BODY' | 'MEDIA' | 'BUTTON'
|
|
83
|
+
example?: string
|
|
84
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
type Primitive = string | number | Date | boolean
|
|
2
|
+
|
|
3
|
+
export class ODataFilter<T> {
|
|
4
|
+
private filterParts: Array<string> = []
|
|
5
|
+
|
|
6
|
+
private getFormattedValue(value: Primitive | unknown): string {
|
|
7
|
+
if (typeof value === 'string') {
|
|
8
|
+
return `'${value.replace(/'/g, '%27%27')}'`
|
|
9
|
+
} else if (value instanceof Date) {
|
|
10
|
+
return `datetimeoffset'${value.toISOString()}'`
|
|
11
|
+
} else if (typeof value === 'undefined' || value === null) {
|
|
12
|
+
return 'null'
|
|
13
|
+
} else {
|
|
14
|
+
return value.toString()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private addCondition(condition: string) {
|
|
19
|
+
this.filterParts.push(condition)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
eq<K extends keyof T>(field: K, value: T[K]): ODataFilter<T> {
|
|
23
|
+
this.addCondition(`${String(field)} eq ${this.getFormattedValue(value)}`)
|
|
24
|
+
return this
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ne<K extends keyof T>(field: K, value: T[K]): ODataFilter<T> {
|
|
28
|
+
this.addCondition(`${String(field)} ne ${this.getFormattedValue(value)}`)
|
|
29
|
+
return this
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
gt<K extends keyof T>(field: K, value: T[K] | Date): ODataFilter<T> {
|
|
33
|
+
this.addCondition(`${String(field)} gt ${this.getFormattedValue(value)}`)
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ge<K extends keyof T>(field: K, value: T[K] | Date): ODataFilter<T> {
|
|
38
|
+
this.addCondition(`${String(field)} ge ${this.getFormattedValue(value)}`)
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
lt<K extends keyof T>(field: K, value: T[K] | Date): ODataFilter<T> {
|
|
43
|
+
this.addCondition(`${String(field)} lt ${this.getFormattedValue(value)}`)
|
|
44
|
+
return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
le<K extends keyof T>(field: K, value: T[K] | Date): ODataFilter<T> {
|
|
48
|
+
this.addCondition(`${String(field)} le ${this.getFormattedValue(value)}`)
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
in<K extends keyof T>(field: K, values: Array<T[K]>): ODataFilter<T> {
|
|
53
|
+
const formattedValues = values.map((v) => this.getFormattedValue(v)).join(',')
|
|
54
|
+
this.addCondition(`${String(field)} in (${formattedValues})`)
|
|
55
|
+
return this
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
startsWith<K extends keyof T>(field: K, value: string): ODataFilter<T> {
|
|
59
|
+
this.addCondition(`startswith(${String(field)}, ${this.getFormattedValue(value)})`)
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
endsWith<K extends keyof T>(field: K, value: string): ODataFilter<T> {
|
|
64
|
+
this.addCondition(`endswith(${String(field)}, ${this.getFormattedValue(value)})`)
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
contains<K extends keyof T>(field: K, value: string): ODataFilter<T> {
|
|
69
|
+
this.addCondition(`contains(${String(field)}, ${this.getFormattedValue(value)})`)
|
|
70
|
+
return this
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
includes<K extends keyof T>(field: K, value: string): ODataFilter<T> {
|
|
74
|
+
this.addCondition(`substringof(${this.getFormattedValue(value)}, ${String(field)}) eq true`)
|
|
75
|
+
return this
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
notIncludes<K extends keyof T>(field: K, value: string): ODataFilter<T> {
|
|
79
|
+
this.addCondition(`substringof(${this.getFormattedValue(value)}, ${String(field)}) eq false`)
|
|
80
|
+
return this
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
and(filterBuilder: (filter: ODataFilter<T>) => ODataFilter<T>): ODataFilter<T> {
|
|
84
|
+
const newFilter = filterBuilder(new ODataFilter<T>())
|
|
85
|
+
if (newFilter.filterParts.length > 0) {
|
|
86
|
+
if (this.filterParts.length > 0) {
|
|
87
|
+
this.addCondition(`${this.toString()} and (${newFilter})`)
|
|
88
|
+
} else {
|
|
89
|
+
this.addCondition(`(${newFilter})`)
|
|
90
|
+
}
|
|
91
|
+
this.filterParts = [this.filterParts.pop()!] // keep only the latest compound condition
|
|
92
|
+
}
|
|
93
|
+
return this
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
or(filterBuilder: (filter: ODataFilter<T>) => ODataFilter<T>): ODataFilter<T> {
|
|
97
|
+
const newFilter = filterBuilder(new ODataFilter<T>())
|
|
98
|
+
if (newFilter.filterParts.length > 0) {
|
|
99
|
+
if (this.filterParts.length > 0) {
|
|
100
|
+
this.addCondition(`${this.toString()} or (${newFilter})`)
|
|
101
|
+
} else {
|
|
102
|
+
this.addCondition(`(${newFilter})`)
|
|
103
|
+
}
|
|
104
|
+
this.filterParts = [this.filterParts.pop()!] // keep only the latest compound condition
|
|
105
|
+
}
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
toString(): string {
|
|
110
|
+
return this.filterParts.join(' and ')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const filter = <T>(): ODataFilter<T> => new ODataFilter<T>()
|
package/src/utils/uri.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface URI {
|
|
2
|
+
path: string
|
|
3
|
+
// native URLSearchParams is not worth here
|
|
4
|
+
// we can't use URLSearchParams.toString() because it encodes the spaces as `+` instead of `%20`
|
|
5
|
+
// causing issues in some cases like odata queries
|
|
6
|
+
// also not encoding the keys is good for dev readability (lime doesn't care much about it)
|
|
7
|
+
query: Map<string, string>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const uri = (parts: TemplateStringsArray, ...vars: Array<unknown>): URI => {
|
|
11
|
+
let queryObject: Record<string, unknown> | undefined
|
|
12
|
+
const last = vars[vars.length - 1]
|
|
13
|
+
if (last && last.constructor === Object) {
|
|
14
|
+
queryObject = last as Record<string, unknown>
|
|
15
|
+
vars.splice(vars.length - 1, 1)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const stringify = (v: unknown): string =>
|
|
19
|
+
v === undefined || v === null ? '' : v instanceof Date ? v.toISOString() : String(v)
|
|
20
|
+
|
|
21
|
+
const raw = parts.map((part, i) => part + encodeURIComponent(stringify(vars[i]))).join('')
|
|
22
|
+
|
|
23
|
+
const [path, rawQuery = ''] = raw.split('?', 2)
|
|
24
|
+
const query = new Map(new URLSearchParams(rawQuery))
|
|
25
|
+
|
|
26
|
+
if (queryObject) {
|
|
27
|
+
for (const [key, value] of Object.entries(queryObject)) {
|
|
28
|
+
if (value !== undefined && value !== null) {
|
|
29
|
+
query.set(key, stringify(value))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { path, query }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const uriToString = (uri: URI): string => {
|
|
38
|
+
if (uri.query.size === 0) {
|
|
39
|
+
return uri.path
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const query = Array.from(uri.query)
|
|
43
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
44
|
+
.join('&')
|
|
45
|
+
return `${uri.path}?${query}`
|
|
46
|
+
}
|