@meith/settings 0.29.0 → 0.30.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/package.json +3 -3
- package/src/branding.ts +14 -0
- package/src/definitions.ts +21 -5
- package/src/index.ts +4 -0
- package/src/mail.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/settings",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"zod": "^4.4.3",
|
|
23
|
-
"@meith/core": "0.
|
|
24
|
-
"@meith/i18n": "0.
|
|
23
|
+
"@meith/core": "0.30.0",
|
|
24
|
+
"@meith/i18n": "0.30.0"
|
|
25
25
|
}
|
|
26
26
|
}
|
package/src/branding.ts
CHANGED
|
@@ -4,6 +4,8 @@ export const LOGO_SCHEMES: readonly LogoScheme[] = ['light', 'dark']
|
|
|
4
4
|
|
|
5
5
|
const LOGO_KEY = /^board\/logo-(light|dark)-[a-f0-9-]{36}\.(png|jpg|webp|svg)$/
|
|
6
6
|
|
|
7
|
+
const FAVICON_KEY = /^board\/favicon-[a-f0-9-]{36}\.(png|jpg|webp|svg)$/
|
|
8
|
+
|
|
7
9
|
export function isLogoScheme(value: unknown): value is LogoScheme {
|
|
8
10
|
return value === 'light' || value === 'dark'
|
|
9
11
|
}
|
|
@@ -12,10 +14,22 @@ export function isLogoKey(value: string): boolean {
|
|
|
12
14
|
return LOGO_KEY.test(value)
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
export function isFaviconKey(value: string): boolean {
|
|
18
|
+
return FAVICON_KEY.test(value)
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export function logoFormat(key: string): string | null {
|
|
16
22
|
return LOGO_KEY.exec(key)?.[2] ?? null
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
export function faviconFormat(key: string): string | null {
|
|
26
|
+
return FAVICON_KEY.exec(key)?.[1] ?? null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function faviconPath(key: string): string {
|
|
30
|
+
return `/brand/favicon?v=${key.slice(-16, -4)}`
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
export function logoPath(scheme: LogoScheme, key: string): string {
|
|
20
34
|
return `/logo/${scheme}?v=${key.slice(-16, -4)}`
|
|
21
35
|
}
|
package/src/definitions.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { z } from 'zod'
|
|
|
3
3
|
import { normaliseLocale, SOURCE_LOCALE } from '@meith/i18n'
|
|
4
4
|
|
|
5
5
|
import { DEFAULT_PRIVACY_POLICY, DEFAULT_TERMS_OF_SERVICE } from './legal'
|
|
6
|
+
import { mailEndpointProblem } from './mail'
|
|
6
7
|
import { isUsableFeedUrl, isUsableIssuer, isUsableOrigin } from './origin'
|
|
7
8
|
|
|
8
9
|
export type SettingGroup =
|
|
@@ -301,6 +302,19 @@ export const SETTING_DEFINITIONS = [
|
|
|
301
302
|
invalidates: ['settings', 'layout'],
|
|
302
303
|
ui: { managed: true },
|
|
303
304
|
}),
|
|
305
|
+
define({
|
|
306
|
+
key: 'board.favicon',
|
|
307
|
+
group: 'board',
|
|
308
|
+
label: 'Favicon',
|
|
309
|
+
description:
|
|
310
|
+
'The icon browser tabs, bookmarks and an installed shortcut show for this board. ' +
|
|
311
|
+
'Uploaded, not typed. Left empty, an icon is generated from the board name and its ' +
|
|
312
|
+
'theme colours.',
|
|
313
|
+
schema: z.string().max(300),
|
|
314
|
+
default: '',
|
|
315
|
+
invalidates: ['settings', 'layout'],
|
|
316
|
+
ui: { managed: true },
|
|
317
|
+
}),
|
|
304
318
|
define({
|
|
305
319
|
key: 'board.logo_alt',
|
|
306
320
|
group: 'board',
|
|
@@ -417,14 +431,16 @@ export const SETTING_DEFINITIONS = [
|
|
|
417
431
|
'Only for the provider-API transport. The board posts Resend’s exact ' +
|
|
418
432
|
'field names with a Bearer token, so this works for Resend and for ' +
|
|
419
433
|
'anything that copies it — and not for Postmark or Mailgun, whose SMTP ' +
|
|
420
|
-
'hosts are the way in.'
|
|
434
|
+
'hosts are the way in. It must be an https:// address with no embedded ' +
|
|
435
|
+
'credentials, and the board refuses to reach one that resolves to a ' +
|
|
436
|
+
'private or internal address.',
|
|
421
437
|
schema: z
|
|
422
438
|
.string()
|
|
423
439
|
.trim()
|
|
424
|
-
.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
),
|
|
440
|
+
.superRefine((value, ctx) => {
|
|
441
|
+
const problem = mailEndpointProblem(value)
|
|
442
|
+
if (problem !== null) ctx.addIssue({ code: z.ZodIssueCode.custom, message: problem })
|
|
443
|
+
}),
|
|
428
444
|
default: '',
|
|
429
445
|
}),
|
|
430
446
|
define({
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,9 @@ export {
|
|
|
7
7
|
resolveBoardUrl,
|
|
8
8
|
} from './board-url'
|
|
9
9
|
export {
|
|
10
|
+
faviconFormat,
|
|
11
|
+
faviconPath,
|
|
12
|
+
isFaviconKey,
|
|
10
13
|
isLogoKey,
|
|
11
14
|
isLogoScheme,
|
|
12
15
|
LOGO_SCHEMES,
|
|
@@ -48,6 +51,7 @@ export {
|
|
|
48
51
|
mailConfigFromEnvironment,
|
|
49
52
|
mailConfigFromSettings,
|
|
50
53
|
mailConfigProblems,
|
|
54
|
+
mailEndpointProblem,
|
|
51
55
|
NO_MAIL,
|
|
52
56
|
resolveMailConfig,
|
|
53
57
|
type SmtpMailConfig,
|
package/src/mail.ts
CHANGED
|
@@ -175,6 +175,24 @@ function hostOf(endpoint: string): string {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
export function mailEndpointProblem(value: string): string | null {
|
|
179
|
+
const trimmed = value.trim()
|
|
180
|
+
if (trimmed === '') return null
|
|
181
|
+
|
|
182
|
+
let url: URL
|
|
183
|
+
try {
|
|
184
|
+
url = new URL(trimmed)
|
|
185
|
+
} catch {
|
|
186
|
+
return 'That is not a URL.'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (url.protocol !== 'https:') return 'The endpoint must be an https:// URL.'
|
|
190
|
+
if (url.username !== '' || url.password !== '') {
|
|
191
|
+
return 'The endpoint must not carry a username or password.'
|
|
192
|
+
}
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
export interface MailPreset {
|
|
179
197
|
readonly id: string
|
|
180
198
|
readonly label: string
|