@antelopejs/dms-mailing 0.7.1 → 0.8.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 +21 -4
- package/dist/crons/jobs.d.ts +1 -1
- package/dist/data/body.js +2 -1
- package/dist/data/body.js.map +1 -1
- package/dist/data/templates-table.js +4 -3
- package/dist/data/templates-table.js.map +1 -1
- package/dist/db/models/template-versions.model.js.map +1 -1
- package/dist/db/models/templates.model.js +2 -1
- package/dist/db/models/templates.model.js.map +1 -1
- package/dist/engine/changes.js +2 -1
- package/dist/engine/changes.js.map +1 -1
- package/dist/engine/conditions.js +2 -3
- package/dist/engine/conditions.js.map +1 -1
- package/dist/engine/fallback-html.js +8 -8
- package/dist/engine/fallback-html.js.map +1 -1
- package/dist/engine/formats.js +6 -6
- package/dist/engine/formats.js.map +1 -1
- package/dist/engine/paths.js +2 -3
- package/dist/engine/paths.js.map +1 -1
- package/dist/engine/system-variables.js +1 -1
- package/dist/engine/system-variables.js.map +1 -1
- package/dist/engine/tokens.js.map +1 -1
- package/dist/pages/sends.js.map +1 -1
- package/dist/pages/toolbar.js.map +1 -1
- package/dist/routes/events.js +2 -2
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/metrics.js +6 -5
- package/dist/routes/metrics.js.map +1 -1
- package/dist/routes/sends.js +1 -1
- package/dist/routes/sends.js.map +1 -1
- package/dist/routes/settings.js +6 -6
- package/dist/routes/settings.js.map +1 -1
- package/dist/routes/templates.js +4 -4
- package/dist/routes/templates.js.map +1 -1
- package/dist/services/attention.js +2 -1
- package/dist/services/attention.js.map +1 -1
- package/dist/services/categories.js +6 -6
- package/dist/services/categories.js.map +1 -1
- package/dist/services/checked-send.js +16 -9
- package/dist/services/checked-send.js.map +1 -1
- package/dist/services/declarations.js.map +1 -1
- package/dist/services/events.js +11 -7
- package/dist/services/events.js.map +1 -1
- package/dist/services/localized-text.d.ts +2 -1
- package/dist/services/localized-text.js +2 -1
- package/dist/services/localized-text.js.map +1 -1
- package/dist/services/metrics.js.map +1 -1
- package/dist/services/render.js.map +1 -1
- package/dist/services/send-import.js +7 -3
- package/dist/services/send-import.js.map +1 -1
- package/dist/services/send.js +4 -3
- package/dist/services/send.js.map +1 -1
- package/dist/services/starters.js +5 -5
- package/dist/services/starters.js.map +1 -1
- package/dist/services/template-stats.js +5 -2
- package/dist/services/template-stats.js.map +1 -1
- package/dist/services/templates.js +6 -2
- package/dist/services/templates.js.map +1 -1
- package/dist/validation/declarations.schema.d.ts +16 -16
- package/dist/validation/declarations.schema.js +2 -2
- package/dist/validation/declarations.schema.js.map +1 -1
- package/dist/validation/guards.d.ts +4 -0
- package/dist/validation/guards.js +19 -0
- package/dist/validation/guards.js.map +1 -0
- package/dist/validation/send-event-type.schema.d.ts +2 -0
- package/dist/validation/send-event-type.schema.js +11 -0
- package/dist/validation/send-event-type.schema.js.map +1 -0
- package/dist/validation/settings.schema.js +1 -1
- package/dist/validation/settings.schema.js.map +1 -1
- package/dist/validation/templates.schema.d.ts +19 -18
- package/dist/validation/templates.schema.js.map +1 -1
- package/frontend-vue/app/build/components/TemplateDrawerFooter.vue +45 -0
- package/frontend-vue/app/build/components/TemplateDrawerPerformance.vue +102 -0
- package/frontend-vue/app/build/components/TemplateDrawerVariables.vue +110 -0
- package/frontend-vue/app/build/composables/useDrawerStats.ts +78 -0
- package/frontend-vue/app/components/DuplicateModal.vue +3 -11
- package/frontend-vue/app/components/FixAddressModal.vue +2 -1
- package/frontend-vue/app/components/NewTemplateModal.vue +3 -11
- package/frontend-vue/app/components/RealSendModal.vue +6 -17
- package/frontend-vue/app/components/RenderingModal.vue +2 -1
- package/frontend-vue/app/components/SendDrawer.vue +5 -4
- package/frontend-vue/app/components/StarterGallery.vue +3 -11
- package/frontend-vue/app/components/TemplateDrawer.vue +40 -269
- package/frontend-vue/app/components/TestSendModal.vue +7 -18
- package/frontend-vue/app/components/VersionHistory.vue +2 -7
- package/frontend-vue/app/components/WebhookStatus.vue +2 -1
- package/frontend-vue/app/components/editor/EditorWorkspace.vue +2 -7
- package/frontend-vue/build/backend-boundary.ts +122 -0
- package/frontend-vue/dms.frontend.build.ts +7 -0
- package/frontend-vue/eslint.config.mjs +13 -0
- package/frontend-vue/package.json +4 -2
- package/frontend-vue/pnpm-lock.yaml +210 -61
- package/frontend-vue/tests/backend-boundary.test.ts +203 -0
- package/frontend-vue/tests/dnd.test.ts +1 -2
- package/frontend-vue/tests/editor-block-list.test.ts +136 -53
- package/frontend-vue/tests/helpers/auth-fetch.ts +123 -0
- package/frontend-vue/tests/helpers/nuxt-globals.ts +17 -1
- package/frontend-vue/tests/new-template-modal.test.ts +41 -6
- package/frontend-vue/tests/page-reads.test.ts +26 -6
- package/frontend-vue/tests/send-actions.test.ts +0 -1
- package/frontend-vue/tests/send-drawer.test.ts +2 -6
- package/frontend-vue/tests/send-modals.test.ts +8 -5
- package/frontend-vue/tests/sortable-list.test.ts +54 -62
- package/frontend-vue/tests/suppression.test.ts +1 -2
- package/frontend-vue/tests/template-drawer.test.ts +27 -18
- package/frontend-vue/tests/template-gallery.test.ts +7 -5
- package/frontend-vue/tests/template-preview-frame.test.ts +2 -5
- package/package.json +3 -8
- package/skills/dms-mailing/SKILL.md +3 -3
- package/skills/dms-mailing/cases.json +49 -0
- package/src/crons/jobs.ts +1 -1
- package/src/data/body.ts +2 -1
- package/src/data/templates-table.ts +12 -7
- package/src/db/models/template-versions.model.ts +5 -1
- package/src/db/models/templates.model.ts +6 -3
- package/src/engine/changes.ts +3 -2
- package/src/engine/conditions.ts +2 -4
- package/src/engine/fallback-html.ts +13 -13
- package/src/engine/formats.ts +6 -6
- package/src/engine/paths.ts +4 -3
- package/src/engine/system-variables.ts +4 -3
- package/src/engine/tokens.ts +1 -2
- package/src/pages/sends.ts +5 -3
- package/src/pages/templates.ts +1 -1
- package/src/pages/toolbar.ts +4 -2
- package/src/routes/events.ts +3 -6
- package/src/routes/metrics.ts +7 -5
- package/src/routes/sends.ts +10 -18
- package/src/routes/settings.ts +8 -9
- package/src/routes/suppressions.ts +1 -1
- package/src/routes/templates.ts +5 -5
- package/src/services/attention.ts +4 -3
- package/src/services/categories.ts +6 -6
- package/src/services/checked-send.ts +17 -19
- package/src/services/declarations.ts +3 -5
- package/src/services/events.ts +16 -13
- package/src/services/localized-text.ts +4 -2
- package/src/services/metrics.ts +2 -2
- package/src/services/notify.ts +1 -1
- package/src/services/render.ts +5 -1
- package/src/services/send-import.ts +16 -11
- package/src/services/send.ts +7 -7
- package/src/services/starters.ts +6 -6
- package/src/services/template-stats.ts +11 -4
- package/src/services/templates.ts +10 -8
- package/src/validation/declarations.schema.ts +2 -2
- package/src/validation/guards.ts +25 -0
- package/src/validation/send-event-type.schema.ts +10 -0
- package/src/validation/settings.schema.ts +7 -2
- package/src/validation/templates.schema.ts +2 -1
- package/frontend-vue/tests/i18n-editor.test.ts +0 -50
- package/frontend-vue/tests/i18n.test.ts +0 -138
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { readFileSync, readdirSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
import en from '../i18n/locales/mailing-en-GB.json'
|
|
5
|
-
import fr from '../i18n/locales/mailing-fr-FR.json'
|
|
6
|
-
|
|
7
|
-
// vitest runs with the layer as its root; the backend sits one level above it.
|
|
8
|
-
const BACKEND_SOURCE_DIR = join(process.cwd(), '..', 'src')
|
|
9
|
-
|
|
10
|
-
// `$dms_mailing.a.b` as the backend writes it. A literal that stops on a dot is
|
|
11
|
-
// the prefix of a key completed by a template expression (`${status}`); those
|
|
12
|
-
// are resolved at runtime and cannot be checked here.
|
|
13
|
-
const KEY_LITERAL = /\$dms_mailing\.[\w.]+/g
|
|
14
|
-
const DYNAMIC_KEY_SUFFIX = '.'
|
|
15
|
-
|
|
16
|
-
function keysOf(value: unknown, prefix = ''): string[] {
|
|
17
|
-
if (typeof value !== 'object' || value === null) return [prefix]
|
|
18
|
-
return Object.entries(value).flatMap(([key, child]) =>
|
|
19
|
-
keysOf(child, prefix ? `${prefix}.${key}` : key),
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function typescriptFiles(directory: string): string[] {
|
|
24
|
-
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
25
|
-
const path = join(directory, entry.name)
|
|
26
|
-
if (entry.isDirectory()) return typescriptFiles(path)
|
|
27
|
-
return entry.isFile() && path.endsWith('.ts') ? [path] : []
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function backendKeys(): string[] {
|
|
32
|
-
const found = new Set<string>()
|
|
33
|
-
for (const file of typescriptFiles(BACKEND_SOURCE_DIR)) {
|
|
34
|
-
for (const literal of readFileSync(file, 'utf8').match(KEY_LITERAL) ?? []) {
|
|
35
|
-
const key = literal.slice(1)
|
|
36
|
-
if (!key.endsWith(DYNAMIC_KEY_SUFFIX)) found.add(key)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return [...found].sort()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function messagesOf(value: unknown, prefix = ''): [string, string][] {
|
|
43
|
-
if (typeof value === 'string') return [[prefix, value]]
|
|
44
|
-
if (typeof value !== 'object' || value === null) return []
|
|
45
|
-
return Object.entries(value).flatMap(([key, child]) =>
|
|
46
|
-
messagesOf(child, prefix ? `${prefix}.${key}` : key),
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// `{name}` placeholders and `{'…'}` literals, the only braces a message holds.
|
|
51
|
-
const BRACED = /\{'[^']*'\}|\{\w+\}/g
|
|
52
|
-
const PLACEHOLDER = /\{(\w+)\}/g
|
|
53
|
-
const PLURAL_SEPARATOR = '|'
|
|
54
|
-
// vue-i18n's default rule picks, for three forms, the first at 0 and the
|
|
55
|
-
// second at 1: French says « 0 envoi », so every French plural needs all three.
|
|
56
|
-
const FRENCH_PLURAL_FORMS = 3
|
|
57
|
-
|
|
58
|
-
function placeholdersOf(message: string): string[] {
|
|
59
|
-
return [
|
|
60
|
-
...new Set([...message.matchAll(PLACEHOLDER)].map((match) => match[1])),
|
|
61
|
-
].sort()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function resolve(root: unknown, key: string): unknown {
|
|
65
|
-
return key
|
|
66
|
-
.split('.')
|
|
67
|
-
.reduce<unknown>(
|
|
68
|
-
(node, part) =>
|
|
69
|
-
typeof node === 'object' && node !== null
|
|
70
|
-
? (node as Record<string, unknown>)[part]
|
|
71
|
-
: undefined,
|
|
72
|
-
root,
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
describe('locales', () => {
|
|
77
|
-
it('carry the same key set', () => {
|
|
78
|
-
expect(keysOf(fr).sort()).toEqual(keysOf(en).sort())
|
|
79
|
-
})
|
|
80
|
-
it('cover every namespace the pages use', () => {
|
|
81
|
-
for (const namespace of [
|
|
82
|
-
'overview',
|
|
83
|
-
'templates',
|
|
84
|
-
'editor',
|
|
85
|
-
'sends',
|
|
86
|
-
'settings',
|
|
87
|
-
'metrics',
|
|
88
|
-
'funnel',
|
|
89
|
-
'errors',
|
|
90
|
-
'blocks',
|
|
91
|
-
]) {
|
|
92
|
-
expect(en.dms_mailing).toHaveProperty(namespace)
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
it('resolve every static key the backend emits', () => {
|
|
96
|
-
const keys = backendKeys()
|
|
97
|
-
expect(keys.length).toBeGreaterThan(0)
|
|
98
|
-
expect(keys.filter((key) => typeof resolve(en, key) !== 'string')).toEqual(
|
|
99
|
-
[],
|
|
100
|
-
)
|
|
101
|
-
expect(keys.filter((key) => typeof resolve(fr, key) !== 'string')).toEqual(
|
|
102
|
-
[],
|
|
103
|
-
)
|
|
104
|
-
})
|
|
105
|
-
it('compile: no stray brace or unescaped @ outside placeholders', () => {
|
|
106
|
-
const broken = [...messagesOf(en), ...messagesOf(fr)].filter(
|
|
107
|
-
([, message]) => /[{}@]/.test(message.replace(BRACED, '')),
|
|
108
|
-
)
|
|
109
|
-
expect(broken).toEqual([])
|
|
110
|
-
})
|
|
111
|
-
it('give each message the same placeholders in both languages', () => {
|
|
112
|
-
const french = new Map(messagesOf(fr))
|
|
113
|
-
const mismatched = messagesOf(en)
|
|
114
|
-
.filter(
|
|
115
|
-
([key, message]) =>
|
|
116
|
-
placeholdersOf(message).join() !==
|
|
117
|
-
placeholdersOf(french.get(key) ?? '').join(),
|
|
118
|
-
)
|
|
119
|
-
.map(([key]) => key)
|
|
120
|
-
expect(mismatched).toEqual([])
|
|
121
|
-
})
|
|
122
|
-
it('say « modèle », never « template », in French', () => {
|
|
123
|
-
const anglicisms = messagesOf(fr)
|
|
124
|
-
.filter(([, message]) => /\btemplates?\b/i.test(message))
|
|
125
|
-
.map(([key]) => key)
|
|
126
|
-
expect(anglicisms).toEqual([])
|
|
127
|
-
})
|
|
128
|
-
it('write every French plural with three forms, 0 in the singular', () => {
|
|
129
|
-
const incomplete = messagesOf(fr)
|
|
130
|
-
.filter(
|
|
131
|
-
([, message]) =>
|
|
132
|
-
message.includes(PLURAL_SEPARATOR) &&
|
|
133
|
-
message.split(PLURAL_SEPARATOR).length !== FRENCH_PLURAL_FORMS,
|
|
134
|
-
)
|
|
135
|
-
.map(([key]) => key)
|
|
136
|
-
expect(incomplete).toEqual([])
|
|
137
|
-
})
|
|
138
|
-
})
|