@antelopejs/dms-mailing 0.7.1 → 0.9.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 +46 -31
- package/dist/automation/email-event-trigger.d.ts +2 -2
- package/dist/automation/send-template-action.d.ts +11 -3
- package/dist/automation/send-template-action.js +1 -1
- package/dist/automation/send-template-action.js.map +1 -1
- package/dist/crons/jobs.d.ts +1 -1
- package/dist/data/body.d.ts +2 -1
- package/dist/data/body.js +4 -2
- package/dist/data/body.js.map +1 -1
- package/dist/data/read-only-routes.d.ts +4 -4
- package/dist/data/sends-table.d.ts +4 -4
- package/dist/data/suppressions-table.d.ts +4 -4
- package/dist/data/templates-table.d.ts +35 -12
- package/dist/data/templates-table.js +30 -24
- package/dist/data/templates-table.js.map +1 -1
- package/dist/data-types/categories-type.d.ts +1 -2
- package/dist/data-types/categories-type.js +3 -5
- package/dist/data-types/categories-type.js.map +1 -1
- package/dist/data-types/category-type.d.ts +1 -2
- package/dist/data-types/category-type.js +3 -5
- package/dist/data-types/category-type.js.map +1 -1
- package/dist/db/models/send-events.model.d.ts +7 -5
- package/dist/db/models/sends.model.d.ts +7 -5
- package/dist/db/models/sends.model.js.map +1 -1
- package/dist/db/models/settings.model.d.ts +7 -5
- package/dist/db/models/suppressions.model.d.ts +7 -5
- package/dist/db/models/template-versions.model.d.ts +7 -5
- package/dist/db/models/template-versions.model.js +4 -3
- package/dist/db/models/template-versions.model.js.map +1 -1
- package/dist/db/models/templates.model.d.ts +7 -5
- package/dist/db/models/templates.model.js +2 -1
- package/dist/db/models/templates.model.js.map +1 -1
- package/dist/db/models/webhook-status.model.d.ts +7 -5
- package/dist/db/tables/templates.table.js.map +1 -1
- package/dist/engine/changes.js +8 -18
- package/dist/engine/changes.js.map +1 -1
- package/dist/engine/conditions.d.ts +2 -1
- package/dist/engine/conditions.js +5 -5
- package/dist/engine/conditions.js.map +1 -1
- package/dist/engine/coverage.d.ts +2 -1
- package/dist/engine/coverage.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.d.ts +4 -6
- package/dist/engine/formats.js +22 -8
- package/dist/engine/formats.js.map +1 -1
- package/dist/engine/paths.d.ts +4 -2
- package/dist/engine/paths.js +16 -5
- package/dist/engine/paths.js.map +1 -1
- package/dist/engine/plain-text.js +2 -4
- package/dist/engine/plain-text.js.map +1 -1
- package/dist/engine/resolve.d.ts +2 -1
- package/dist/engine/resolve.js +1 -1
- package/dist/engine/resolve.js.map +1 -1
- package/dist/engine/system-variables.d.ts +2 -1
- package/dist/engine/system-variables.js +1 -1
- package/dist/engine/system-variables.js.map +1 -1
- package/dist/engine/tokens.d.ts +2 -1
- package/dist/engine/tokens.js.map +1 -1
- package/dist/hooks/tenant-export.js +10 -9
- package/dist/hooks/tenant-export.js.map +1 -1
- package/dist/pages/sends.js.map +1 -1
- package/dist/pages/templates.js +3 -1
- package/dist/pages/templates.js.map +1 -1
- package/dist/pages/toolbar.js.map +1 -1
- package/dist/routes/events.js +6 -4
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/metrics.js +31 -31
- package/dist/routes/metrics.js.map +1 -1
- package/dist/routes/period.js +5 -5
- package/dist/routes/period.js.map +1 -1
- package/dist/routes/sends.js +4 -5
- package/dist/routes/sends.js.map +1 -1
- package/dist/routes/settings.d.ts +1 -1
- package/dist/routes/settings.js +16 -14
- package/dist/routes/settings.js.map +1 -1
- package/dist/routes/suppressions.js +9 -6
- package/dist/routes/suppressions.js.map +1 -1
- package/dist/routes/template-content.d.ts +27 -0
- package/dist/routes/template-content.js +3 -0
- package/dist/routes/template-content.js.map +1 -0
- package/dist/routes/templates.d.ts +7 -30
- package/dist/routes/templates.js +7 -6
- package/dist/routes/templates.js.map +1 -1
- package/dist/routes/zod-json.d.ts +3 -0
- package/dist/routes/zod-json.js +21 -0
- package/dist/routes/zod-json.js.map +1 -0
- package/dist/services/attention.js +16 -8
- package/dist/services/attention.js.map +1 -1
- package/dist/services/catalog.js +4 -2
- package/dist/services/catalog.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/declaration-error.d.ts +1 -1
- package/dist/services/declaration-error.js.map +1 -1
- package/dist/services/declarations.js +4 -1
- package/dist/services/declarations.js.map +1 -1
- package/dist/services/events.js +13 -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 +2 -5
- 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 +7 -7
- package/dist/services/send.js.map +1 -1
- package/dist/services/settings.d.ts +4 -2
- package/dist/services/settings.js +5 -4
- package/dist/services/settings.js.map +1 -1
- package/dist/services/starters.d.ts +3 -2
- 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.d.ts +2 -1
- package/dist/services/templates.js +8 -4
- package/dist/services/templates.js.map +1 -1
- package/dist/services/webhook-status.d.ts +2 -2
- package/dist/services/webhook-status.js +6 -4
- package/dist/services/webhook-status.js.map +1 -1
- package/dist/types/json.d.ts +1 -0
- package/dist/types/json.js +3 -0
- package/dist/types/json.js.map +1 -0
- package/dist/types/settings.d.ts +2 -2
- package/dist/validation/declarations.schema.d.ts +44 -44
- package/dist/validation/declarations.schema.js +6 -5
- package/dist/validation/declarations.schema.js.map +1 -1
- package/dist/validation/guards.d.ts +3 -0
- package/dist/validation/guards.js +15 -0
- package/dist/validation/guards.js.map +1 -0
- package/dist/validation/json.d.ts +5 -0
- package/dist/validation/json.js +54 -0
- package/dist/validation/json.js.map +1 -0
- package/dist/validation/json.schema.d.ts +2 -0
- package/dist/validation/json.schema.js +9 -0
- package/dist/validation/json.schema.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 +44 -42
- package/dist/validation/templates.schema.js +4 -3
- 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 +250 -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 +16 -20
- package/skills/dms-mailing/SKILL.md +1 -1
- package/skills/dms-mailing/cases.json +49 -0
- package/src/automation/email-event-trigger.ts +2 -2
- package/src/automation/send-template-action.ts +33 -14
- package/src/crons/jobs.ts +1 -1
- package/src/data/body.ts +7 -3
- package/src/data/templates-table.ts +55 -56
- package/src/data-types/categories-type.ts +2 -2
- package/src/data-types/category-type.ts +2 -2
- package/src/db/models/sends.model.ts +2 -2
- package/src/db/models/template-versions.model.ts +10 -4
- package/src/db/models/templates.model.ts +6 -3
- package/src/db/tables/templates.table.ts +1 -1
- package/src/engine/changes.ts +11 -28
- package/src/engine/conditions.ts +14 -8
- package/src/engine/coverage.ts +2 -1
- package/src/engine/fallback-html.ts +13 -13
- package/src/engine/formats.ts +35 -21
- package/src/engine/paths.ts +41 -7
- package/src/engine/plain-text.ts +2 -4
- package/src/engine/resolve.ts +12 -4
- package/src/engine/system-variables.ts +6 -4
- package/src/engine/tokens.ts +3 -3
- package/src/hooks/tenant-export.ts +11 -13
- package/src/pages/sends.ts +5 -3
- package/src/pages/templates.ts +4 -2
- package/src/pages/toolbar.ts +4 -2
- package/src/routes/events.ts +10 -10
- package/src/routes/index.ts +1 -0
- package/src/routes/metrics.ts +35 -33
- package/src/routes/period.ts +5 -5
- package/src/routes/sends.ts +23 -26
- package/src/routes/settings.ts +21 -17
- package/src/routes/suppressions.ts +12 -8
- package/src/routes/template-content.ts +45 -0
- package/src/routes/templates.ts +16 -49
- package/src/routes/zod-json.ts +28 -0
- package/src/services/attention.ts +20 -18
- package/src/services/catalog.ts +4 -8
- package/src/services/categories.ts +6 -6
- package/src/services/checked-send.ts +17 -19
- package/src/services/declaration-error.ts +1 -1
- package/src/services/declarations.ts +7 -6
- package/src/services/events.ts +18 -13
- package/src/services/localized-text.ts +4 -2
- package/src/services/metrics.ts +5 -7
- 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 +10 -13
- package/src/services/settings.ts +17 -16
- package/src/services/starters.ts +12 -8
- package/src/services/template-stats.ts +11 -4
- package/src/services/templates.ts +24 -15
- package/src/services/webhook-status.ts +13 -8
- package/src/types/json.ts +1 -0
- package/src/types/settings.ts +2 -2
- package/src/validation/declarations.schema.ts +6 -5
- package/src/validation/guards.ts +17 -0
- package/src/validation/json.schema.ts +7 -0
- package/src/validation/json.ts +66 -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 +9 -4
- package/frontend-vue/tests/i18n-editor.test.ts +0 -50
- package/frontend-vue/tests/i18n.test.ts +0 -138
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useDrawerStats } from '../build/composables/useDrawerStats'
|
|
3
|
+
import TemplateDrawerFooter from '../build/components/TemplateDrawerFooter.vue'
|
|
4
|
+
import TemplateDrawerPerformance from '../build/components/TemplateDrawerPerformance.vue'
|
|
5
|
+
import TemplateDrawerVariables from '../build/components/TemplateDrawerVariables.vue'
|
|
3
6
|
import { useElementSize } from '@vueuse/core'
|
|
4
7
|
import {
|
|
5
8
|
useMailingApi,
|
|
@@ -10,11 +13,9 @@ import {
|
|
|
10
13
|
useTemplatePerformanceRead,
|
|
11
14
|
} from '../composables/useMailingReads'
|
|
12
15
|
import { useTemplateDrawer } from '../composables/useTemplateDrawer'
|
|
13
|
-
import { apiErrorMessage } from '../utils/api-error'
|
|
14
16
|
import { formatCardDate } from '../utils/gallery'
|
|
15
17
|
import { localeCoverage } from '../utils/locales'
|
|
16
18
|
import { sendSnippet } from '../utils/send-snippet'
|
|
17
|
-
import { variableTypeLabelKey } from '../utils/variable-types'
|
|
18
19
|
import { validateName } from '../utils/slug'
|
|
19
20
|
import {
|
|
20
21
|
validTransitions,
|
|
@@ -66,14 +67,8 @@ interface LifecycleSpec {
|
|
|
66
67
|
confirmColor?: ConfirmTone
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
/** The send log reads these: `?template=` narrows it, `?view=` opens a tab. */
|
|
70
|
-
const SENDS_TEMPLATE_KEY = 'template'
|
|
71
|
-
const SENDS_VIEW_KEY = 'view'
|
|
72
|
-
const PROBLEMS_VIEW = 'problems'
|
|
73
70
|
const PREVIEW_WIDTH = 600
|
|
74
71
|
const PREVIEW_FALLBACK_SCALE = 0.72
|
|
75
|
-
const OPEN_RATE_DIGITS = 1
|
|
76
|
-
const PERCENT = 100
|
|
77
72
|
const DEFAULT_VARIABLE_TYPE: VariableType = 'string'
|
|
78
73
|
|
|
79
74
|
const STATUS_TONES: Record<TemplateStatus, string> = {
|
|
@@ -88,9 +83,9 @@ defineEmits<{ success: [changed?: boolean] }>()
|
|
|
88
83
|
|
|
89
84
|
const api = useMailingApi()
|
|
90
85
|
const toast = useToast()
|
|
86
|
+
const { presentError } = useApiErrorPresenter()
|
|
91
87
|
const { confirm } = useConfirm()
|
|
92
88
|
const { t, locale: uiLocale } = useI18n()
|
|
93
|
-
const { processApiMessage } = useTranslation()
|
|
94
89
|
const { uniqueLocales } = useUniqueLocales()
|
|
95
90
|
const overlays = useTemplateDrawer()
|
|
96
91
|
|
|
@@ -142,10 +137,7 @@ async function load(): Promise<void> {
|
|
|
142
137
|
performance.value = stats
|
|
143
138
|
locale.value ||= content.fallbackLocale
|
|
144
139
|
} catch (error) {
|
|
145
|
-
|
|
146
|
-
color: 'error',
|
|
147
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
148
|
-
})
|
|
140
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
149
141
|
} finally {
|
|
150
142
|
loading.value = false
|
|
151
143
|
}
|
|
@@ -261,60 +253,12 @@ const versionLabel = computed(() =>
|
|
|
261
253
|
: t('dms_mailing.templates.drawer.never_published'),
|
|
262
254
|
)
|
|
263
255
|
|
|
264
|
-
const formatNumber = (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const formatPercent = (value: number) =>
|
|
271
|
-
new Intl.NumberFormat(uiLocale.value, {
|
|
272
|
-
style: 'percent',
|
|
273
|
-
maximumFractionDigits: OPEN_RATE_DIGITS,
|
|
274
|
-
}).format(value / PERCENT)
|
|
275
|
-
|
|
276
|
-
/** The send log narrowed to this template, on one of its tabs if given. */
|
|
277
|
-
function sendLogRoute(view?: string) {
|
|
278
|
-
return {
|
|
279
|
-
path: SENDS_PATH,
|
|
280
|
-
query: {
|
|
281
|
-
[SENDS_TEMPLATE_KEY]: template.value?.slug ?? '',
|
|
282
|
-
...(view ? { [SENDS_VIEW_KEY]: view } : {}),
|
|
283
|
-
},
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function sendLogUrl(view?: string): string {
|
|
288
|
-
const { path, query } = sendLogRoute(view)
|
|
289
|
-
return `${path}?${new URLSearchParams(query).toString()}`
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const hasSends = computed(() => Boolean(performance.value?.sends))
|
|
293
|
-
|
|
294
|
-
const stats = computed(() => [
|
|
295
|
-
{
|
|
296
|
-
id: 'sends',
|
|
297
|
-
eyebrow: t('dms_mailing.templates.drawer.stats_sends'),
|
|
298
|
-
value: formatNumber(performance.value?.sends ?? 0),
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
id: 'open',
|
|
302
|
-
eyebrow: t('dms_mailing.templates.drawer.stats_open'),
|
|
303
|
-
value: performance.value?.sends
|
|
304
|
-
? formatPercent(performance.value.openRate)
|
|
305
|
-
: '—',
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
id: 'problems',
|
|
309
|
-
eyebrow: t('dms_mailing.templates.drawer.stats_problems'),
|
|
310
|
-
value: formatNumber(performance.value?.problems ?? 0),
|
|
311
|
-
detail: performance.value?.problems
|
|
312
|
-
? t('dms_mailing.templates.drawer.problems_detail')
|
|
313
|
-
: undefined,
|
|
314
|
-
detailTone: 'error' as const,
|
|
315
|
-
to: performance.value?.problems ? sendLogUrl(PROBLEMS_VIEW) : undefined,
|
|
316
|
-
},
|
|
317
|
-
])
|
|
256
|
+
const { formatNumber, sendLogRoute, hasSends, stats } = useDrawerStats(
|
|
257
|
+
template,
|
|
258
|
+
performance,
|
|
259
|
+
t,
|
|
260
|
+
uiLocale,
|
|
261
|
+
)
|
|
318
262
|
|
|
319
263
|
const snippet = computed(() =>
|
|
320
264
|
sendSnippet({
|
|
@@ -363,10 +307,7 @@ async function saveDetails(changes: Partial<TemplateDetails>): Promise<void> {
|
|
|
363
307
|
})
|
|
364
308
|
reloadInPlace()
|
|
365
309
|
} catch (error) {
|
|
366
|
-
|
|
367
|
-
color: 'error',
|
|
368
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
369
|
-
})
|
|
310
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
370
311
|
}
|
|
371
312
|
}
|
|
372
313
|
|
|
@@ -424,10 +365,7 @@ async function runLifecycle(action: LifecycleAction): Promise<void> {
|
|
|
424
365
|
})
|
|
425
366
|
settle()
|
|
426
367
|
} catch (error) {
|
|
427
|
-
|
|
428
|
-
color: 'error',
|
|
429
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
430
|
-
})
|
|
368
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
431
369
|
}
|
|
432
370
|
}
|
|
433
371
|
|
|
@@ -472,10 +410,7 @@ async function declareVariable(path: string): Promise<void> {
|
|
|
472
410
|
detail.value.variables = response.variables
|
|
473
411
|
detail.value.revision = response.revision
|
|
474
412
|
} catch (error) {
|
|
475
|
-
|
|
476
|
-
color: 'error',
|
|
477
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
478
|
-
})
|
|
413
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
479
414
|
reloadInPlace()
|
|
480
415
|
}
|
|
481
416
|
}
|
|
@@ -499,10 +434,7 @@ async function restoreVersion(version: number): Promise<void> {
|
|
|
499
434
|
],
|
|
500
435
|
})
|
|
501
436
|
} catch (error) {
|
|
502
|
-
|
|
503
|
-
color: 'error',
|
|
504
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
505
|
-
})
|
|
437
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
506
438
|
}
|
|
507
439
|
reloadInPlace()
|
|
508
440
|
}
|
|
@@ -701,172 +633,26 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
701
633
|
</div>
|
|
702
634
|
|
|
703
635
|
<div v-show="tab === 'variables'" class="flex flex-col gap-4 py-4">
|
|
704
|
-
<
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
:key="variable.path"
|
|
714
|
-
class="border-default flex min-h-9 items-center gap-2 border-b last:border-b-0"
|
|
715
|
-
>
|
|
716
|
-
<code
|
|
717
|
-
class="truncate font-mono text-[12px]"
|
|
718
|
-
:class="
|
|
719
|
-
variable.usage === 'unused' ? 'text-dimmed' : 'text-toned'
|
|
720
|
-
"
|
|
721
|
-
>
|
|
722
|
-
{{ variable.path }}
|
|
723
|
-
</code>
|
|
724
|
-
<span
|
|
725
|
-
v-if="variable.required && variable.usage !== 'detected'"
|
|
726
|
-
class="text-error"
|
|
727
|
-
:title="t('dms_mailing.templates.drawer.required')"
|
|
728
|
-
>
|
|
729
|
-
*
|
|
730
|
-
</span>
|
|
731
|
-
<DmsStatusPill
|
|
732
|
-
v-if="variable.usage === 'detected'"
|
|
733
|
-
tone="warning"
|
|
734
|
-
size="sm"
|
|
735
|
-
dot="none"
|
|
736
|
-
:label="t('dms_mailing.templates.drawer.not_declared')"
|
|
737
|
-
/>
|
|
738
|
-
<DmsStatusPill
|
|
739
|
-
v-if="variable.usage === 'unused'"
|
|
740
|
-
tone="neutral"
|
|
741
|
-
size="sm"
|
|
742
|
-
dot="none"
|
|
743
|
-
:label="t('dms_mailing.templates.drawer.unused')"
|
|
744
|
-
/>
|
|
745
|
-
<UButton
|
|
746
|
-
v-if="variable.usage === 'detected' && !isLive"
|
|
747
|
-
class="ml-auto"
|
|
748
|
-
color="neutral"
|
|
749
|
-
variant="ghost"
|
|
750
|
-
size="xs"
|
|
751
|
-
:label="t('dms_mailing.templates.drawer.declare')"
|
|
752
|
-
@click="declareVariable(variable.path)"
|
|
753
|
-
/>
|
|
754
|
-
<span
|
|
755
|
-
v-else-if="variable.usage !== 'detected'"
|
|
756
|
-
class="text-dimmed ml-auto font-mono text-[11px]"
|
|
757
|
-
>
|
|
758
|
-
{{ t(variableTypeLabelKey(variable.type)) }}
|
|
759
|
-
</span>
|
|
760
|
-
</div>
|
|
761
|
-
</div>
|
|
762
|
-
<DmsBanner
|
|
763
|
-
v-if="hasDraftDeclarations"
|
|
764
|
-
tone="info"
|
|
765
|
-
size="sm"
|
|
766
|
-
icon="i-ph-brackets-curly"
|
|
767
|
-
:title="t('dms_mailing.templates.drawer.draft_declarations')"
|
|
768
|
-
:description="
|
|
769
|
-
t('dms_mailing.templates.drawer.draft_declarations_description')
|
|
770
|
-
"
|
|
771
|
-
>
|
|
772
|
-
<template #actions>
|
|
773
|
-
<UButton
|
|
774
|
-
color="neutral"
|
|
775
|
-
variant="outline"
|
|
776
|
-
size="xs"
|
|
777
|
-
:label="t('dms_mailing.templates.actions.open_editor')"
|
|
778
|
-
@click="openEditor()"
|
|
779
|
-
/>
|
|
780
|
-
</template>
|
|
781
|
-
</DmsBanner>
|
|
782
|
-
<MailingSystemVariables :variables="systemVars" />
|
|
783
|
-
<div class="flex flex-col gap-1.5">
|
|
784
|
-
<div class="flex items-center justify-between">
|
|
785
|
-
<DmsEyebrow
|
|
786
|
-
:label="t('dms_mailing.templates.drawer.call_from_code')"
|
|
787
|
-
/>
|
|
788
|
-
<DmsCopyButton :value="snippet" />
|
|
789
|
-
</div>
|
|
790
|
-
<pre
|
|
791
|
-
class="bg-elevated text-toned overflow-x-auto rounded-md p-3 font-mono text-[11.5px] leading-relaxed"
|
|
792
|
-
>{{ snippet }}</pre>
|
|
793
|
-
</div>
|
|
636
|
+
<TemplateDrawerVariables
|
|
637
|
+
:variables="variables"
|
|
638
|
+
:is-live="isLive"
|
|
639
|
+
:has-draft-declarations="hasDraftDeclarations"
|
|
640
|
+
:system-vars="systemVars"
|
|
641
|
+
:snippet="snippet"
|
|
642
|
+
:declare-variable="declareVariable"
|
|
643
|
+
:open-editor="openEditor"
|
|
644
|
+
/>
|
|
794
645
|
</div>
|
|
795
646
|
|
|
796
647
|
<div v-show="tab === 'performance'" class="flex flex-col gap-4 py-4">
|
|
797
|
-
<
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
class="p-0"
|
|
806
|
-
:label="t('dms_mailing.templates.drawer.open_send_log')"
|
|
807
|
-
@click="openSendLog"
|
|
808
|
-
/>
|
|
809
|
-
</div>
|
|
810
|
-
<DmsStatGroup
|
|
811
|
-
layout="joined"
|
|
812
|
-
:columns="3"
|
|
813
|
-
:items="stats"
|
|
814
|
-
:loading="loading"
|
|
815
|
-
/>
|
|
816
|
-
</div>
|
|
817
|
-
<div class="flex flex-col gap-1">
|
|
818
|
-
<DmsEyebrow :label="t('dms_mailing.templates.drawer.called_from')" />
|
|
819
|
-
<p
|
|
820
|
-
v-if="!hasSends || !performance?.sources.length"
|
|
821
|
-
class="text-muted text-[12.5px]"
|
|
822
|
-
>
|
|
823
|
-
{{ t('dms_mailing.templates.drawer.no_sources') }}
|
|
824
|
-
</p>
|
|
825
|
-
<DmsListRow
|
|
826
|
-
v-for="source in performance?.sources ?? []"
|
|
827
|
-
:key="source.source"
|
|
828
|
-
icon="i-ph-code"
|
|
829
|
-
size="sm"
|
|
830
|
-
mono
|
|
831
|
-
:title="
|
|
832
|
-
source.source || t('dms_mailing.templates.drawer.unknown_source')
|
|
833
|
-
"
|
|
834
|
-
:trailing="
|
|
835
|
-
t(
|
|
836
|
-
'dms_mailing.templates.drawer.sends_count',
|
|
837
|
-
{ count: formatNumber(source.count) },
|
|
838
|
-
source.count,
|
|
839
|
-
)
|
|
840
|
-
"
|
|
841
|
-
/>
|
|
842
|
-
</div>
|
|
843
|
-
<div class="flex flex-col gap-1">
|
|
844
|
-
<DmsEyebrow :label="t('dms_mailing.templates.drawer.fallbacks')" />
|
|
845
|
-
<p v-if="!hasSends" class="text-muted text-[12.5px]">
|
|
846
|
-
{{ t('dms_mailing.templates.drawer.no_sources') }}
|
|
847
|
-
</p>
|
|
848
|
-
<p
|
|
849
|
-
v-else-if="!performance?.fallbacks.length"
|
|
850
|
-
class="text-muted text-[12.5px]"
|
|
851
|
-
>
|
|
852
|
-
{{ t('dms_mailing.templates.drawer.no_fallbacks') }}
|
|
853
|
-
</p>
|
|
854
|
-
<DmsListRow
|
|
855
|
-
v-for="fallback in performance?.fallbacks ?? []"
|
|
856
|
-
:key="`${fallback.requested}-${fallback.used}`"
|
|
857
|
-
icon="i-ph-translate"
|
|
858
|
-
tone="warning"
|
|
859
|
-
size="sm"
|
|
860
|
-
:title="`${fallback.requested.toUpperCase()} → ${fallback.used.toUpperCase()}`"
|
|
861
|
-
:trailing="
|
|
862
|
-
t(
|
|
863
|
-
'dms_mailing.templates.drawer.sends_count',
|
|
864
|
-
{ count: formatNumber(fallback.count) },
|
|
865
|
-
fallback.count,
|
|
866
|
-
)
|
|
867
|
-
"
|
|
868
|
-
/>
|
|
869
|
-
</div>
|
|
648
|
+
<TemplateDrawerPerformance
|
|
649
|
+
:stats="stats"
|
|
650
|
+
:loading="loading"
|
|
651
|
+
:has-sends="hasSends"
|
|
652
|
+
:performance="performance"
|
|
653
|
+
:open-send-log="openSendLog"
|
|
654
|
+
:format-number="formatNumber"
|
|
655
|
+
/>
|
|
870
656
|
</div>
|
|
871
657
|
|
|
872
658
|
<div v-if="tab === 'history'" class="flex flex-col gap-2 py-4">
|
|
@@ -882,28 +668,13 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
882
668
|
/>
|
|
883
669
|
</div>
|
|
884
670
|
|
|
885
|
-
<
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
/>
|
|
893
|
-
<UButton
|
|
894
|
-
class="ml-auto"
|
|
895
|
-
icon="i-ph-copy"
|
|
896
|
-
color="neutral"
|
|
897
|
-
variant="outline"
|
|
898
|
-
:label="t('dms_mailing.templates.actions.duplicate')"
|
|
899
|
-
@click="overlays.openDuplicate(templateId, { rowData: template })"
|
|
900
|
-
/>
|
|
901
|
-
<UButton
|
|
902
|
-
icon="i-ph-pencil-simple"
|
|
903
|
-
:label="t('dms_mailing.templates.actions.open_editor')"
|
|
904
|
-
@click="openEditor(locale)"
|
|
905
|
-
/>
|
|
906
|
-
</DmsContainerFooter>
|
|
671
|
+
<TemplateDrawerFooter
|
|
672
|
+
:template-id="templateId"
|
|
673
|
+
:locale="locale"
|
|
674
|
+
:template="template"
|
|
675
|
+
:overlays="overlays"
|
|
676
|
+
:open-editor="openEditor"
|
|
677
|
+
/>
|
|
907
678
|
</template>
|
|
908
679
|
</div>
|
|
909
680
|
</template>
|
|
@@ -4,7 +4,6 @@ import FormRows from '../build/components/FormRows.vue'
|
|
|
4
4
|
import { useMailingApi } from '../composables/useMailingApi'
|
|
5
5
|
import { useTemplateContentRead } from '../composables/useMailingReads'
|
|
6
6
|
import type { SendResult, TestSendInput } from '../composables/useMailingApi'
|
|
7
|
-
import { apiErrorMessage } from '../utils/api-error'
|
|
8
7
|
import { sendLogRoute } from '../utils/send-link'
|
|
9
8
|
import { reasonKey, type SuppressionSummary } from '../utils/suppression'
|
|
10
9
|
import {
|
|
@@ -49,8 +48,8 @@ const emit = defineEmits<{ success: [sent?: boolean] }>()
|
|
|
49
48
|
|
|
50
49
|
const api = useMailingApi()
|
|
51
50
|
const toast = useToast()
|
|
51
|
+
const { presentError } = useApiErrorPresenter()
|
|
52
52
|
const { t, locale: uiLocale } = useI18n()
|
|
53
|
-
const { processApiMessage } = useTranslation()
|
|
54
53
|
const { user } = useCurrentUser()
|
|
55
54
|
const { uniqueLocales } = useUniqueLocales()
|
|
56
55
|
|
|
@@ -70,10 +69,7 @@ onMounted(async () => {
|
|
|
70
69
|
detail.value = await readContent()
|
|
71
70
|
locale.value ||= detail.value.fallbackLocale
|
|
72
71
|
} catch (error) {
|
|
73
|
-
|
|
74
|
-
color: 'error',
|
|
75
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
76
|
-
})
|
|
72
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
77
73
|
}
|
|
78
74
|
})
|
|
79
75
|
|
|
@@ -154,14 +150,10 @@ function contentOverride(): Pick<TestSendInput, 'content' | 'version'> {
|
|
|
154
150
|
return {}
|
|
155
151
|
}
|
|
156
152
|
|
|
157
|
-
function reportFailures(results: SendResult[]
|
|
158
|
-
results.forEach((result
|
|
153
|
+
function reportFailures(results: SendResult[]): void {
|
|
154
|
+
results.forEach((result) => {
|
|
159
155
|
if (!result.error) return
|
|
160
|
-
|
|
161
|
-
color: 'error',
|
|
162
|
-
title: to[index] ?? result.sendId,
|
|
163
|
-
description: processApiMessage(result.error),
|
|
164
|
-
})
|
|
156
|
+
presentError(result.error, { fallbackKey: 'error.500.description' })
|
|
165
157
|
})
|
|
166
158
|
const sent = results.filter((result) => !result.error).length
|
|
167
159
|
if (sent) {
|
|
@@ -213,14 +205,11 @@ async function submit(): Promise<void> {
|
|
|
213
205
|
...contentOverride(),
|
|
214
206
|
data: DATA_SETS[dataChoice.value](),
|
|
215
207
|
})
|
|
216
|
-
reportFailures(response.results
|
|
208
|
+
reportFailures(response.results)
|
|
217
209
|
warnOwnSuppression(response.ownAddressSuppressed)
|
|
218
210
|
close(true)
|
|
219
211
|
} catch (error) {
|
|
220
|
-
|
|
221
|
-
color: 'error',
|
|
222
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
223
|
-
})
|
|
212
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
224
213
|
} finally {
|
|
225
214
|
sending.value = false
|
|
226
215
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { Component } from 'vue'
|
|
3
3
|
import { useTemplateVersionsRead } from '../composables/useMailingReads'
|
|
4
4
|
import type { TemplateStatus, TemplateVersionSummary } from '../types/mailing'
|
|
5
|
-
import { apiErrorMessage } from '../utils/api-error'
|
|
6
5
|
import {
|
|
7
6
|
changeSummary,
|
|
8
7
|
formatVersionDate,
|
|
@@ -32,10 +31,9 @@ const props = withDefaults(defineProps<VersionHistoryProps>(), {
|
|
|
32
31
|
})
|
|
33
32
|
|
|
34
33
|
const modal = useModal()
|
|
35
|
-
const
|
|
34
|
+
const { presentError } = useApiErrorPresenter()
|
|
36
35
|
const { confirm } = useConfirm()
|
|
37
36
|
const { t, locale: uiLocale } = useI18n()
|
|
38
|
-
const { processApiMessage } = useTranslation()
|
|
39
37
|
const previewComponent = resolveComponent(PREVIEW_COMPONENT) as Component
|
|
40
38
|
|
|
41
39
|
const readVersions = useTemplateVersionsRead(() => props.templateId)
|
|
@@ -47,10 +45,7 @@ async function load(): Promise<void> {
|
|
|
47
45
|
try {
|
|
48
46
|
versions.value = (await readVersions()).versions
|
|
49
47
|
} catch (error) {
|
|
50
|
-
|
|
51
|
-
color: 'error',
|
|
52
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
53
|
-
})
|
|
48
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
54
49
|
} finally {
|
|
55
50
|
loading.value = false
|
|
56
51
|
}
|
|
@@ -35,6 +35,7 @@ const api = useMailingApi()
|
|
|
35
35
|
const toast = useToast()
|
|
36
36
|
const { t, locale } = useI18n()
|
|
37
37
|
const { dateTimeFormat } = useRegionalFormat()
|
|
38
|
+
const { presentError } = useApiErrorPresenter()
|
|
38
39
|
|
|
39
40
|
const read = useWebhookStatus()
|
|
40
41
|
/** The status read, `null` when the read failed. */
|
|
@@ -84,7 +85,7 @@ async function sendTest(): Promise<void> {
|
|
|
84
85
|
: undefined,
|
|
85
86
|
})
|
|
86
87
|
} catch (error) {
|
|
87
|
-
|
|
88
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
88
89
|
} finally {
|
|
89
90
|
isTesting.value = false
|
|
90
91
|
}
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
useEditorShortcuts,
|
|
12
12
|
} from '../../composables/useEditorShortcuts'
|
|
13
13
|
import { createEditorState } from '../../composables/useTemplateEditor'
|
|
14
|
-
import { apiErrorMessage } from '../../utils/api-error'
|
|
15
14
|
|
|
16
15
|
interface Props {
|
|
17
16
|
loaded: TemplateContentResponse
|
|
@@ -23,8 +22,7 @@ const props = defineProps<Props>()
|
|
|
23
22
|
const locale = defineModel<string | null>('locale', { default: null })
|
|
24
23
|
const emit = defineEmits<{ renamed: [string] }>()
|
|
25
24
|
|
|
26
|
-
const
|
|
27
|
-
const { processApiMessage } = useTranslation()
|
|
25
|
+
const { presentError } = useApiErrorPresenter()
|
|
28
26
|
const { uniqueLocales } = useUniqueLocales()
|
|
29
27
|
const api = useMailingApi()
|
|
30
28
|
|
|
@@ -44,10 +42,7 @@ const editor = createEditorState(
|
|
|
44
42
|
)
|
|
45
43
|
|
|
46
44
|
function reportFailure(error: unknown): void {
|
|
47
|
-
|
|
48
|
-
color: 'error',
|
|
49
|
-
title: processApiMessage(apiErrorMessage(error)),
|
|
50
|
-
})
|
|
45
|
+
presentError(error, { fallbackKey: 'error.500.description' })
|
|
51
46
|
}
|
|
52
47
|
|
|
53
48
|
const reads = useEditorReads(props.templateId)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
2
|
+
import { dirname, isAbsolute, join, relative, sep } from 'node:path'
|
|
3
|
+
import type { DmsVitePluginEntry } from '#dms/frontend-build'
|
|
4
|
+
|
|
5
|
+
type VitePlugin = Extract<DmsVitePluginEntry, { name: string }>
|
|
6
|
+
|
|
7
|
+
const BACKEND_PACKAGE = '@antelopejs/dms-mailing'
|
|
8
|
+
const BACKEND_SOURCE = 'src'
|
|
9
|
+
const QUERY_OR_HASH = /[?#].*$/
|
|
10
|
+
|
|
11
|
+
const backendRoots = new Map<string, string | null>()
|
|
12
|
+
|
|
13
|
+
/** Whether a parsed manifest names its package. */
|
|
14
|
+
function isNamedManifest(value: unknown): value is { name: string } {
|
|
15
|
+
return (
|
|
16
|
+
typeof value === 'object' &&
|
|
17
|
+
value !== null &&
|
|
18
|
+
'name' in value &&
|
|
19
|
+
typeof value.name === 'string'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The package name a `package.json` declares, or `undefined`. */
|
|
24
|
+
function packageName(manifest: string): string | undefined {
|
|
25
|
+
try {
|
|
26
|
+
const parsed: unknown = JSON.parse(readFileSync(manifest, 'utf8'))
|
|
27
|
+
return isNamedManifest(parsed) ? parsed.name : undefined
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The directory of the backend package that holds `directory`, or null. */
|
|
34
|
+
function backendRoot(directory: string): string | null {
|
|
35
|
+
const known = backendRoots.get(directory)
|
|
36
|
+
if (known !== undefined) {
|
|
37
|
+
return known
|
|
38
|
+
}
|
|
39
|
+
const manifest = join(directory, 'package.json')
|
|
40
|
+
const parent = dirname(directory)
|
|
41
|
+
let root: string | null = null
|
|
42
|
+
if (existsSync(manifest)) {
|
|
43
|
+
root = packageName(manifest) === BACKEND_PACKAGE ? directory : null
|
|
44
|
+
} else if (parent !== directory) {
|
|
45
|
+
root = backendRoot(parent)
|
|
46
|
+
}
|
|
47
|
+
backendRoots.set(directory, root)
|
|
48
|
+
return root
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The real path of a resolved module id when it lies under the `src`
|
|
53
|
+
* directory of the backend package, or `undefined`. The package that owns the
|
|
54
|
+
* file decides, not a path, so the answer is the same in the repository and in
|
|
55
|
+
* the build workspace. Virtual ids and ids that are not files are ignored.
|
|
56
|
+
*/
|
|
57
|
+
function backendSourceFile(id: string): string | undefined {
|
|
58
|
+
const path = id.replace(QUERY_OR_HASH, '')
|
|
59
|
+
if (!isAbsolute(path)) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
let real: string
|
|
63
|
+
try {
|
|
64
|
+
real = realpathSync(path)
|
|
65
|
+
} catch {
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
const root = backendRoot(dirname(real))
|
|
69
|
+
if (root === null) {
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
return relative(root, real).split(sep)[0] === BACKEND_SOURCE
|
|
73
|
+
? real
|
|
74
|
+
: undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Fails the build and the dev server when a module of the frontend resolves to
|
|
79
|
+
* a file under the `src` of the backend package: the browser reaches the
|
|
80
|
+
* backend only through its HTTP interface. It checks what the whole resolution
|
|
81
|
+
* chain returns, an id marked external included, so an alias is seen through
|
|
82
|
+
* and a symlink the resolver returns is followed to its real path, and it runs
|
|
83
|
+
* before the other plugins (`enforce: "pre"`). Vite builds workers with their
|
|
84
|
+
* own plugin list, so the plugin adds itself there too.
|
|
85
|
+
*
|
|
86
|
+
* antelopejs-sync writes this file from `@antelopejs/tooling-configs` and
|
|
87
|
+
* restores it on every run: change the plugin in the pack, not here.
|
|
88
|
+
*/
|
|
89
|
+
export function backendBoundary(): VitePlugin {
|
|
90
|
+
return {
|
|
91
|
+
...boundaryCheck(),
|
|
92
|
+
config: () => ({ worker: { plugins: () => [boundaryCheck()] } }),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The check alone, as a worker build runs it. */
|
|
97
|
+
function boundaryCheck(): VitePlugin {
|
|
98
|
+
return {
|
|
99
|
+
name: `${BACKEND_PACKAGE}:backend-boundary`,
|
|
100
|
+
enforce: 'pre',
|
|
101
|
+
async resolveId(source, importer, options) {
|
|
102
|
+
const resolved = await this.resolve(source, importer, {
|
|
103
|
+
...options,
|
|
104
|
+
skipSelf: true,
|
|
105
|
+
})
|
|
106
|
+
if (resolved === null) {
|
|
107
|
+
return resolved
|
|
108
|
+
}
|
|
109
|
+
const file = backendSourceFile(resolved.id)
|
|
110
|
+
if (file === undefined) {
|
|
111
|
+
return resolved
|
|
112
|
+
}
|
|
113
|
+
const from =
|
|
114
|
+
importer === undefined
|
|
115
|
+
? 'an entry point'
|
|
116
|
+
: importer.replace(QUERY_OR_HASH, '')
|
|
117
|
+
return this.error(
|
|
118
|
+
`Backend boundary: ${from} imports ${file}, which is under the src of ${BACKEND_PACKAGE}. The frontend reaches the backend only through its HTTP interface.`,
|
|
119
|
+
)
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineDmsFrontendBuild } from '#dms/frontend-build'
|
|
2
|
+
import { backendBoundary } from './build/backend-boundary.ts'
|
|
3
|
+
|
|
4
|
+
/** The build of the frontend module: the backend boundary in every build. */
|
|
5
|
+
export default defineDmsFrontendBuild((build) => {
|
|
6
|
+
build.registerVitePlugins(() => [backendBoundary()])
|
|
7
|
+
})
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import antiSlop from '@antelopejs/tooling-configs/oxc/anti-slop'
|
|
2
|
+
import guardrails from '@antelopejs/tooling-configs/oxc/guardrails'
|
|
1
3
|
import vue from 'eslint-plugin-vue'
|
|
2
4
|
import ts from 'typescript-eslint'
|
|
3
5
|
|
|
@@ -10,4 +12,15 @@ export default [
|
|
|
10
12
|
rules: { 'vue/multi-word-component-names': 'off' },
|
|
11
13
|
},
|
|
12
14
|
{ files: ['**/*.ts'], languageOptions: { parser: ts.parser } },
|
|
15
|
+
{
|
|
16
|
+
files: ['tests/**/*.ts'],
|
|
17
|
+
plugins: { antelopejs: guardrails, 'anti-slop': antiSlop },
|
|
18
|
+
rules: {
|
|
19
|
+
'antelopejs/no-fs-read-in-tests': 'error',
|
|
20
|
+
'antelopejs/no-constant-value-assertion': 'error',
|
|
21
|
+
'antelopejs/no-typeof-function-only-test': 'error',
|
|
22
|
+
'antelopejs/no-self-import-in-tests': 'error',
|
|
23
|
+
'anti-slop/no-module-mocking': 'error',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
13
26
|
]
|