@splendidlabz/third-party 1.1.1 → 1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @splendidlabz/third-party
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Many changes! Woo
8
+
9
+ ### Patch Changes
10
+
11
+ - 5114274: Sendy: Include prettyName option
12
+ - Updated dependencies [2c149fc]
13
+ - Updated dependencies
14
+ - @splendidlabz/utils@1.7.0
15
+
16
+ ## 1.2.0
17
+
18
+ ### Minor Changes
19
+
20
+ - Many many upgrades which I didn't document properly 😅. The details are in Github history, but too many details to talk about now.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies
25
+ - @splendidlabz/utils@1.6.0
26
+
3
27
  ## 1.1.1
4
28
 
5
29
  ### Patch Changes
@@ -52,7 +52,6 @@ export async function createSubscriber(
52
52
 
53
53
  if (response) return response.body
54
54
  if (error) {
55
- console.log(error)
56
55
  // Button down throws this error if the email is invalid. Though this only occurs after using fake emails for many times. Shouldn't be a problem to ignore.
57
56
  if (error.code === 'email_invalid') return
58
57
  return reject(error)
package/lib/logger.js ADDED
@@ -0,0 +1,49 @@
1
+ import pino from 'pino'
2
+
3
+ // Potentially can put into logtail for production
4
+ // Potentially can use pino-http for logging requests
5
+
6
+ const isDev = import.meta.env.DEV
7
+
8
+ export function createLogger({ verbose = false, level = 'info' } = {}) {
9
+ return pino({
10
+ level,
11
+ transport: isDev
12
+ ? {
13
+ target: 'pino-pretty', // Nice formatting for dev
14
+ options: { colorize: true },
15
+ }
16
+ : undefined, // Raw JSON for production
17
+ hooks: {
18
+ logMethod(inputArgs, method, level) {
19
+ if (!verbose) return method.apply(this, inputArgs)
20
+ if (inputArgs.length >= 2) {
21
+ const arg1 = inputArgs.shift()
22
+ const arg2 = inputArgs.shift()
23
+ return method.apply(this, [arg2, arg1, ...inputArgs])
24
+ }
25
+ return method.apply(this, inputArgs)
26
+ },
27
+ },
28
+ })
29
+ }
30
+
31
+ // For fast usage, but will be verbose by default
32
+ export const logger = pino({
33
+ transport: isDev
34
+ ? {
35
+ target: 'pino-pretty', // Nice formatting for dev
36
+ options: { colorize: true },
37
+ }
38
+ : undefined, // Raw JSON for production
39
+ hooks: {
40
+ logMethod(inputArgs, method, level) {
41
+ if (inputArgs.length >= 2) {
42
+ const arg1 = inputArgs.shift()
43
+ const arg2 = inputArgs.shift()
44
+ return method.apply(this, [arg2, arg1, ...inputArgs])
45
+ }
46
+ return method.apply(this, inputArgs)
47
+ },
48
+ },
49
+ })
package/lib/logger.md ADDED
@@ -0,0 +1,45 @@
1
+ # Logger
2
+
3
+ ## Installation
4
+
5
+ ```
6
+ npm install @splendidlabz/third-party
7
+ ```
8
+
9
+ ## Basic Usage
10
+
11
+ Import `logger` from the package to log messages. This will be verbose by default.
12
+
13
+ ```js
14
+ import { logger } from '@splendidlabz/third-party/logger'
15
+ logger.info('Message', { data: 'data' })
16
+ ```
17
+
18
+ ## Advanced Usage
19
+
20
+ You can create a logger with a custom verbosity and log level.
21
+
22
+ ```js
23
+ import { createLogger } from '@splendidlabz/third-party/logger'
24
+
25
+ export const logger = createLogger({
26
+ level: 'info',
27
+ verbose: false,
28
+ })
29
+ ```
30
+
31
+ ## Log Levels
32
+
33
+ Pino default log levels are:
34
+
35
+ | Level | Value |
36
+ | ------ | -------- |
37
+ | trace | 10 |
38
+ | debug | 20 |
39
+ | info | 30 |
40
+ | warn | 40 |
41
+ | error | 50 |
42
+ | fatal | 60 |
43
+ | silent | Infinity |
44
+
45
+ Default log level is `info`
@@ -1,13 +1,12 @@
1
1
  import postmark from 'postmark'
2
2
 
3
- const POSTMARK_KEY = process.env.POSTMARK_KEY
4
3
  const DEFAULT_OPTIONS = {
5
4
  MessageStream: 'outbound', // outbound | broadcast
6
5
  }
7
6
 
8
- const client = new postmark.ServerClient(POSTMARK_KEY)
9
-
10
- export default function Postmark(options) {
7
+ export default function Postmark(API_KEY, options) {
8
+ const POSTMARK_KEY = API_KEY || process.env.POSTMARK_KEY
9
+ const client = new postmark.ServerClient(POSTMARK_KEY)
11
10
  const baseOpts = Object.assign({}, DEFAULT_OPTIONS, options)
12
11
 
13
12
  return {
package/lib/sendy.js ADDED
@@ -0,0 +1,215 @@
1
+ import { omitEmpty } from '@splendidlabz/utils'
2
+ import zlFetch, { toQueryString } from 'zl-fetch'
3
+
4
+ /**
5
+ * Creates a Sendy API client instance
6
+ * @param {string} baseURL - The base URL of your Sendy installation (e.g., 'https://your-domain.com/sendy')
7
+ * @param {Object} config - Configuration object
8
+ * @param {string} config.apiKey - Your Sendy API key (available in Settings)
9
+ * @return {Object} Sendy client with available methods
10
+ * @property {Function} subscribe - Subscribe a user to a mailing list
11
+ */
12
+ export function createSendy({
13
+ baseURL,
14
+ listId,
15
+ brandId,
16
+ apiKey,
17
+ getReferrer = null,
18
+ getIpAddress = null,
19
+ prettyName = true,
20
+ } = {}) {
21
+ const api_key = apiKey
22
+ const list = listId
23
+ const brand = brandId
24
+
25
+ return {
26
+ async subscriberStatus({ email, listId }) {
27
+ const response = await zlFetch.post(
28
+ `${baseURL}/api/subscribers/subscription-status.php`,
29
+ {
30
+ body: toQueryString({
31
+ boolean: true,
32
+ api_key,
33
+ list_id: listId || list,
34
+ email,
35
+ }),
36
+ },
37
+ )
38
+
39
+ const successMessages = [
40
+ 'Subscribed',
41
+ 'Unsubscribed',
42
+ 'Unconfirmed',
43
+ 'Bounced',
44
+ 'Soft bounced',
45
+ 'Complained',
46
+ ]
47
+
48
+ if (successMessages.includes(response.body)) return response.body
49
+ else throw new Error(response.body)
50
+ },
51
+
52
+ async subscriberCount({ listId } = {}) {
53
+ const response = await zlFetch.post(
54
+ `${baseURL}/api/subscribers/subscribers-count.php`,
55
+ {
56
+ body: toQueryString({
57
+ boolean: true,
58
+ api_key,
59
+ list_id: listId || list,
60
+ }),
61
+ },
62
+ )
63
+
64
+ const result = response.body
65
+ const count = Number(result)
66
+ if (isNaN(count)) throw new Error(result)
67
+ return count
68
+ },
69
+
70
+ async subscribe({
71
+ context,
72
+ email,
73
+ name,
74
+ listId,
75
+ gdpr = false,
76
+ silent = false,
77
+ ...rest
78
+ }) {
79
+ const ip = getIpAddress && context && getIpAddress(context)
80
+ const referrer = getReferrer && context && getReferrer(context)
81
+
82
+ // Potentially add country and city based on ip address...
83
+ // Via ipapi.co - Free for 30,000 requests per month
84
+
85
+ if (name && prettyName) {
86
+ name = name
87
+ .toLowerCase()
88
+ .split(' ')
89
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
90
+ .join(' ')
91
+ }
92
+
93
+ const body = omitEmpty({
94
+ boolean: true,
95
+ api_key,
96
+ list: listId || list,
97
+ email,
98
+ ipaddress: ip,
99
+ referrer,
100
+ gdpr,
101
+ silent,
102
+ ...rest,
103
+ })
104
+
105
+ const response = await zlFetch.post(`${baseURL}/subscribe`, {
106
+ body: toQueryString(body),
107
+ })
108
+
109
+ const result = response.body
110
+ if (result === '1') return true
111
+
112
+ // Details will be updated when users are subscribed. So this strictly doesn't count as an error. Better to use subscriberStatus to check for status.
113
+ if (result === 'Already subscribed.') return true
114
+ else throw new Error(result)
115
+ },
116
+
117
+ async unsubscribe({ email, listId, ...rest }) {
118
+ const response = await zlFetch.post(`${baseURL}/unsubscribe`, {
119
+ body: toQueryString({
120
+ boolean: true,
121
+ api_key,
122
+ email,
123
+ list: listId || list,
124
+ ...rest,
125
+ }),
126
+ })
127
+
128
+ const result = response.body
129
+ if (result === '1') return true
130
+ else throw new Error(result)
131
+ },
132
+
133
+ async delete({ email, listId }) {
134
+ const response = await zlFetch.post(
135
+ `${baseURL}/api/subscribers/delete.php`,
136
+ {
137
+ body: toQueryString({
138
+ boolean: true,
139
+ api_key,
140
+ email,
141
+ list_id: listId || list,
142
+ }),
143
+ },
144
+ )
145
+
146
+ const result = response.body
147
+ if (result === '1') return true
148
+ // No harm in returning true for this
149
+ if (result === 'Subscriber does not exist') return true
150
+ else throw new Error(result)
151
+ },
152
+
153
+ async createCampaign({
154
+ listIds = [],
155
+ excludeListIds = [],
156
+ segmentIds = [],
157
+ excludeSegmentIds = [],
158
+ brandId = brand,
159
+
160
+ send = false,
161
+ subject,
162
+ fromName,
163
+ fromEmail,
164
+ replyTo,
165
+ plainText,
166
+ htmlText,
167
+
168
+ queryString = '',
169
+ trackOpens = 1,
170
+ trackClicks = 1,
171
+ scheduleDateTime,
172
+ scheduleTimezone,
173
+ }) {
174
+ const send_campaign = send ? 1 : 0
175
+ const response = await zlFetch.post(
176
+ `${baseURL}/api/campaigns/create.php`,
177
+ {
178
+ body: toQueryString(
179
+ omitEmpty({
180
+ boolean: true,
181
+ api_key,
182
+ brand_id: brandId,
183
+ list_ids: listIds.join(','),
184
+ segment_ids: segmentIds.join(','),
185
+ exclude_list_ids: excludeListIds.join(','),
186
+ exclude_segment_ids: excludeSegmentIds.join(','),
187
+ send_campaign,
188
+ from_name: fromName,
189
+ from_email: fromEmail,
190
+ reply_to: replyTo || fromEmail,
191
+ subject,
192
+ plain_text: plainText,
193
+ html_text: htmlText,
194
+ query_string: queryString,
195
+ track_opens: trackOpens,
196
+ track_clicks: trackClicks,
197
+ schedule_date_time: scheduleDateTime,
198
+ schedule_timezone: scheduleTimezone,
199
+ }),
200
+ ),
201
+ },
202
+ )
203
+
204
+ const successMessages = [
205
+ 'Campaign created',
206
+ 'Campaign created and now sending',
207
+ 'Campaign scheduled',
208
+ ]
209
+
210
+ const result = response.body
211
+ if (successMessages.includes(result)) return result
212
+ else throw new Error(result)
213
+ },
214
+ }
215
+ }
package/package.json CHANGED
@@ -1,16 +1,13 @@
1
1
  {
2
2
  "name": "@splendidlabz/third-party",
3
3
  "prettier": "@splendidlabz/prettier-config",
4
- "version": "1.1.1",
4
+ "version": "1.3.0",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "sideEffects": false,
9
9
  "exports": {
10
- "./google": "./google.js",
11
- "./postmark": "./postmark.js",
12
- "./buttondown": "./buttondown.js",
13
- "./stripe": "./stripe.js"
10
+ "./*": "./lib/*.js"
14
11
  },
15
12
  "scripts": {
16
13
  "lint": "eslint . --fix",
@@ -19,15 +16,17 @@
19
16
  },
20
17
  "author": "Zell Liew <zellwk@gmail.com>",
21
18
  "dependencies": {
22
- "@splendidlabz/utils": "1.5.1",
19
+ "@splendidlabz/utils": "1.7.0",
23
20
  "googleapis": "^148.0.0",
24
21
  "http-errors": "^2.0.0",
22
+ "pino": "^9.7.0",
23
+ "pino-pretty": "^13.0.0",
25
24
  "postmark": "^4.0.5",
26
25
  "vitest": "^3.1.4",
27
26
  "zl-fetch": "^6.0.6"
28
27
  },
29
28
  "devDependencies": {
30
- "@splendidlabz/eslint-config": "2.0.0",
31
- "@splendidlabz/prettier-config": "1.1.0"
29
+ "@splendidlabz/eslint-config": "2.1.0",
30
+ "@splendidlabz/prettier-config": "1.2.0"
32
31
  }
33
32
  }
File without changes
File without changes
File without changes