@agent-native/core 0.140.0 → 0.141.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +0 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +11 -6
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +8 -18
- package/corpus/templates/analytics/server/plugins/db.ts +174 -0
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +38 -38
- package/corpus/templates/calendar/changelog/2026-08-05-out-of-office-blocks-now-open-from-their-scheduled-time-rang.md +6 -0
- package/corpus/templates/calendar/server/lib/booking-emails.ts +70 -154
- package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +28 -64
- package/corpus/templates/clips/actions/get-recording-player-data.ts +1 -0
- package/corpus/templates/clips/actions/invite-member.ts +16 -39
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +2 -3
- package/corpus/templates/clips/app/components/player/reaction-emojis.ts +10 -0
- package/corpus/templates/clips/app/components/player/reactions-tray.tsx +2 -2
- package/corpus/templates/clips/app/components/player/video-player.tsx +20 -6
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +33 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +5 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +27 -1
- package/corpus/templates/clips/changelog/2026-08-05-comment-and-video-reaction-pickers-now-include-eyes-and-mind.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-05-fresh-recordings-now-open-with-the-complete-video.md +6 -0
- package/corpus/templates/clips/server/lib/transactional-email-templates.ts +0 -24
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/content/server/lib/comment-notifications.ts +26 -57
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +1 -44
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1 -265
- package/corpus/templates/design/actions/create-design-system.ts +10 -9
- package/corpus/templates/design/actions/get-design-system.ts +0 -53
- package/corpus/templates/design/actions/import-design-tokens.ts +71 -5
- package/corpus/templates/design/actions/index-design-tokens.ts +80 -61
- package/corpus/templates/design/actions/update-design-system.ts +10 -7
- package/corpus/templates/design/app/components/design/EditPanel.tsx +2 -45
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +0 -12
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +6 -10
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +3 -19
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +10 -36
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +0 -15
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +1 -19
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +0 -6
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +23 -53
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +4 -142
- package/corpus/templates/design/app/components/design/types.ts +0 -6
- package/corpus/templates/design/app/i18n-data.ts +0 -20
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -78
- package/corpus/templates/design/app/pages/design-editor/html-layer-positioning.ts +1 -6
- package/corpus/templates/design/e2e/helpers.ts +0 -17
- package/corpus/templates/design/shared/resolve-tweaks.ts +18 -10
- package/corpus/templates/forms/server/lib/response-email.ts +0 -2
- package/corpus/templates/plan/actions/request-plan-access.ts +12 -33
- package/corpus/templates/plan/server/lib/comment-notifications.ts +27 -53
- package/corpus/templates/slides/server/lib/comment-notifications.ts +21 -44
- package/dist/a2a/handlers.js +9 -10
- package/dist/brand-kit/index.d.ts +0 -1
- package/dist/brand-kit/index.js +0 -1
- package/dist/brand-kit/types.d.ts +0 -25
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.js +8 -8
- package/dist/db/migrations.js +39 -19
- package/dist/localization/default-messages.d.ts +0 -45
- package/dist/localization/default-messages.js +0 -45
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/handlers.js +1 -8
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.js +0 -22
- package/dist/server/auth.js +3 -1
- package/dist/server/better-auth-instance.js +2 -17
- package/dist/server/email.d.ts +0 -9
- package/dist/server/email.js +3 -44
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.js +0 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +3 -2
- package/docs/content/authentication.mdx +25 -19
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +3 -2
- package/corpus/templates/analytics/server/lib/emails.ts +0 -51
- package/corpus/templates/analytics/server/plugins/transactional-emails.ts +0 -5
- package/corpus/templates/calendar/server/lib/emails.ts +0 -167
- package/corpus/templates/calendar/server/plugins/transactional-emails.ts +0 -5
- package/corpus/templates/clips/server/lib/emails.ts +0 -256
- package/corpus/templates/clips/server/plugins/transactional-email-catalog.ts +0 -5
- package/corpus/templates/content/server/lib/emails.ts +0 -70
- package/corpus/templates/content/server/plugins/transactional-emails.ts +0 -5
- package/corpus/templates/design/app/components/design/edit-panel/design-system-swatches.ts +0 -215
- package/corpus/templates/design/changelog/2026-08-04-design-system-tokens-show-by-name-in-the-editor.md +0 -6
- package/corpus/templates/design/server/lib/design-system-tokens.ts +0 -35
- package/corpus/templates/design/shared/authored-color-styles.ts +0 -401
- package/corpus/templates/dispatch/app/routes/transactional-email.$appId.$id.tsx +0 -4
- package/corpus/templates/dispatch/app/routes/transactional-email.tsx +0 -4
- package/corpus/templates/forms/server/lib/emails.ts +0 -52
- package/corpus/templates/forms/server/plugins/transactional-emails.ts +0 -5
- package/corpus/templates/plan/server/lib/emails.ts +0 -67
- package/corpus/templates/plan/server/plugins/transactional-emails.ts +0 -5
- package/corpus/templates/slides/server/lib/emails.ts +0 -40
- package/corpus/templates/slides/server/plugins/transactional-emails.ts +0 -5
- package/dist/brand-kit/tokens.d.ts +0 -92
- package/dist/brand-kit/tokens.js +0 -280
- package/dist/email-catalog/actions/list-email-activity.d.ts +0 -6
- package/dist/email-catalog/actions/list-email-activity.js +0 -15
- package/dist/email-catalog/actions/list-email-engagement.d.ts +0 -6
- package/dist/email-catalog/actions/list-email-engagement.js +0 -15
- package/dist/email-catalog/actions/list-email-log.d.ts +0 -8
- package/dist/email-catalog/actions/list-email-log.js +0 -22
- package/dist/email-catalog/actions/list-transactional-emails.d.ts +0 -23
- package/dist/email-catalog/actions/list-transactional-emails.js +0 -61
- package/dist/email-catalog/actions/render-transactional-email-preview.d.ts +0 -10
- package/dist/email-catalog/actions/render-transactional-email-preview.js +0 -24
- package/dist/email-catalog/authorize.d.ts +0 -2
- package/dist/email-catalog/authorize.js +0 -10
- package/dist/email-catalog/log.d.ts +0 -57
- package/dist/email-catalog/log.js +0 -117
- package/dist/email-catalog/provider-metrics.d.ts +0 -34
- package/dist/email-catalog/provider-metrics.js +0 -165
- package/dist/email-catalog/registry.d.ts +0 -79
- package/dist/email-catalog/registry.js +0 -71
- package/dist/email-catalog/schema.d.ts +0 -212
- package/dist/email-catalog/schema.js +0 -49
- package/dist/email-catalog/system-emails.d.ts +0 -14
- package/dist/email-catalog/system-emails.js +0 -68
package/dist/brand-kit/tokens.js
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@agent-native/core/brand-kit/tokens`
|
|
3
|
-
*
|
|
4
|
-
* Pure helpers for the named-token vocabulary a Brand Kit stores alongside its
|
|
5
|
-
* seven color roles. Kept on its own subpath (no `design-token-utils` re-export)
|
|
6
|
-
* so browser code can import the CSS-safety predicates without pulling the
|
|
7
|
-
* server-side extraction stack into the bundle.
|
|
8
|
-
*/
|
|
9
|
-
const BRAND_KIT_TOKEN_TYPES = [
|
|
10
|
-
"color",
|
|
11
|
-
"typography",
|
|
12
|
-
"spacing",
|
|
13
|
-
"radius",
|
|
14
|
-
"shadow",
|
|
15
|
-
"other",
|
|
16
|
-
];
|
|
17
|
-
/**
|
|
18
|
-
* Upper bound on a single kit's stored vocabulary. Large systems legitimately
|
|
19
|
-
* define hundreds of tokens; this only stops an unbounded scrape landing in SQL.
|
|
20
|
-
*/
|
|
21
|
-
export const MAX_BRAND_KIT_TOKENS = 500;
|
|
22
|
-
const CSS_CUSTOM_PROPERTY_NAME = /^--[-_a-zA-Z0-9]+$/;
|
|
23
|
-
const COLOR_FUNCTION_OR_HEX = /^(#[0-9a-fA-F]{3,8}|rgba?\(|hsla?\(|oklch\(|color\()/i;
|
|
24
|
-
const NAMED_CSS_COLORS = new Set([
|
|
25
|
-
"red",
|
|
26
|
-
"blue",
|
|
27
|
-
"green",
|
|
28
|
-
"yellow",
|
|
29
|
-
"orange",
|
|
30
|
-
"purple",
|
|
31
|
-
"pink",
|
|
32
|
-
"cyan",
|
|
33
|
-
"magenta",
|
|
34
|
-
"teal",
|
|
35
|
-
"navy",
|
|
36
|
-
"maroon",
|
|
37
|
-
"coral",
|
|
38
|
-
"salmon",
|
|
39
|
-
"gold",
|
|
40
|
-
"silver",
|
|
41
|
-
"gray",
|
|
42
|
-
"grey",
|
|
43
|
-
"indigo",
|
|
44
|
-
"violet",
|
|
45
|
-
"lime",
|
|
46
|
-
"olive",
|
|
47
|
-
"aqua",
|
|
48
|
-
"fuchsia",
|
|
49
|
-
"crimson",
|
|
50
|
-
"turquoise",
|
|
51
|
-
"ivory",
|
|
52
|
-
"beige",
|
|
53
|
-
"lavender",
|
|
54
|
-
"tan",
|
|
55
|
-
"khaki",
|
|
56
|
-
"plum",
|
|
57
|
-
"orchid",
|
|
58
|
-
"sienna",
|
|
59
|
-
]);
|
|
60
|
-
export function isSafeCssVarName(value) {
|
|
61
|
-
return CSS_CUSTOM_PROPERTY_NAME.test(value);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Token values are spliced raw into `:root { … }` declarations, so anything that
|
|
65
|
-
* could terminate a declaration or break out of a `<style>` element is unsafe.
|
|
66
|
-
*/
|
|
67
|
-
export function isSafeCssTokenValue(value) {
|
|
68
|
-
return (value.length > 0 &&
|
|
69
|
-
value.length <= 300 &&
|
|
70
|
-
!/[;{}<>]/.test(value) &&
|
|
71
|
-
!/\/\*/.test(value) &&
|
|
72
|
-
!/[\u0000-\u001f\u007f]/.test(value));
|
|
73
|
-
}
|
|
74
|
-
export function isColorTokenValue(value) {
|
|
75
|
-
const normalized = value.trim().toLowerCase();
|
|
76
|
-
return (COLOR_FUNCTION_OR_HEX.test(normalized) || NAMED_CSS_COLORS.has(normalized));
|
|
77
|
-
}
|
|
78
|
-
const COLOR_NAME_HINT = /color|bg|background|text|border|accent|primary|secondary|surface|muted|foreground|fill|stroke/i;
|
|
79
|
-
const DIMENSION_VALUE = /^-?\d*\.?\d+(px|rem|em|ex|ch|%|vh|vw|vmin|vmax|pt|pc|cm|mm|in|s|ms|deg|fr)?$/i;
|
|
80
|
-
/**
|
|
81
|
-
* Bucket a token, trusting the value's shape over the name's keywords.
|
|
82
|
-
*
|
|
83
|
-
* Name-first classification called Primer's `--text-body-size-medium: 1rem` a
|
|
84
|
-
* color because the name contains "text", which then rendered a swatch with
|
|
85
|
-
* `background: 1rem`. A dimension is never a color however the token is named.
|
|
86
|
-
*/
|
|
87
|
-
export function classifyBrandKitToken(name, value) {
|
|
88
|
-
const n = name.toLowerCase();
|
|
89
|
-
if (isColorTokenValue(value))
|
|
90
|
-
return "color";
|
|
91
|
-
if (/radius|rounded/i.test(n))
|
|
92
|
-
return "radius";
|
|
93
|
-
// `line-?height` and `letter` run before the spacing check so
|
|
94
|
-
// `letter-spacing` is type metrics, not layout spacing.
|
|
95
|
-
if (/font|size|leading|line-?height|letter|tracking|weight|heading|body|type/i.test(n)) {
|
|
96
|
-
return "typography";
|
|
97
|
-
}
|
|
98
|
-
if (/spacing|gap|padding|margin|space/i.test(n))
|
|
99
|
-
return "spacing";
|
|
100
|
-
if (/shadow|blur|drop/i.test(n))
|
|
101
|
-
return "shadow";
|
|
102
|
-
if (COLOR_NAME_HINT.test(n) && !DIMENSION_VALUE.test(value.trim())) {
|
|
103
|
-
return "color";
|
|
104
|
-
}
|
|
105
|
-
return "other";
|
|
106
|
-
}
|
|
107
|
-
/** Title-case display name derived from a CSS var, e.g. `--primary-color`. */
|
|
108
|
-
export function friendlyTokenName(cssVar) {
|
|
109
|
-
return cssVar
|
|
110
|
-
.replace(/^--/, "")
|
|
111
|
-
.replace(/[-_]/g, " ")
|
|
112
|
-
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
113
|
-
}
|
|
114
|
-
function readString(source, key) {
|
|
115
|
-
const value = source[key];
|
|
116
|
-
return typeof value === "string" ? value.trim() : "";
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Coerce untrusted token JSON into storable tokens, reporting every entry it
|
|
120
|
-
* could not accept. Later duplicates of a `cssVar` win, matching the overlay
|
|
121
|
-
* order importers use.
|
|
122
|
-
*/
|
|
123
|
-
export function normalizeBrandKitTokens(input) {
|
|
124
|
-
if (input === undefined || input === null) {
|
|
125
|
-
return { tokens: [], rejected: [] };
|
|
126
|
-
}
|
|
127
|
-
if (!Array.isArray(input)) {
|
|
128
|
-
return { tokens: [], rejected: [{ reason: "malformed", label: "tokens" }] };
|
|
129
|
-
}
|
|
130
|
-
const byCssVar = new Map();
|
|
131
|
-
const rejected = [];
|
|
132
|
-
for (const entry of input) {
|
|
133
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
134
|
-
rejected.push({ reason: "malformed", label: String(entry) });
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const record = entry;
|
|
138
|
-
const cssVar = readString(record, "cssVar");
|
|
139
|
-
const value = readString(record, "value");
|
|
140
|
-
const name = readString(record, "name") || friendlyTokenName(cssVar);
|
|
141
|
-
if (!cssVar || !isSafeCssVarName(cssVar)) {
|
|
142
|
-
rejected.push({ reason: "unsafe-css-var", label: cssVar || name });
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (!isSafeCssTokenValue(value)) {
|
|
146
|
-
rejected.push({ reason: "unsafe-value", label: cssVar });
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const declaredType = readString(record, "type");
|
|
150
|
-
const group = readString(record, "group");
|
|
151
|
-
const source = readString(record, "source");
|
|
152
|
-
byCssVar.set(cssVar, {
|
|
153
|
-
name,
|
|
154
|
-
cssVar,
|
|
155
|
-
value,
|
|
156
|
-
type: BRAND_KIT_TOKEN_TYPES.includes(declaredType)
|
|
157
|
-
? declaredType
|
|
158
|
-
: classifyBrandKitToken(cssVar, value),
|
|
159
|
-
...(group ? { group } : {}),
|
|
160
|
-
...(source ? { source } : {}),
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
const tokens = [...byCssVar.values()];
|
|
164
|
-
if (tokens.length > MAX_BRAND_KIT_TOKENS) {
|
|
165
|
-
for (const token of tokens.slice(MAX_BRAND_KIT_TOKENS)) {
|
|
166
|
-
rejected.push({ reason: "over-limit", label: token.cssVar });
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return { tokens: tokens.slice(0, MAX_BRAND_KIT_TOKENS), rejected };
|
|
170
|
-
}
|
|
171
|
-
/** Human-readable summary of why tokens were refused, for thrown errors. */
|
|
172
|
-
export function describeBrandKitTokenRejections(rejected) {
|
|
173
|
-
return rejected
|
|
174
|
-
.slice(0, 10)
|
|
175
|
-
.map((entry) => `${entry.label} (${entry.reason})`)
|
|
176
|
-
.join(", ");
|
|
177
|
-
}
|
|
178
|
-
/** The CSS variable each colour role is published under. */
|
|
179
|
-
const BRAND_KIT_COLOR_ROLE_VARS = {
|
|
180
|
-
primary: "--color-primary",
|
|
181
|
-
secondary: "--color-secondary",
|
|
182
|
-
accent: "--color-accent",
|
|
183
|
-
background: "--color-background",
|
|
184
|
-
surface: "--color-surface",
|
|
185
|
-
text: "--color-text",
|
|
186
|
-
textMuted: "--color-text-muted",
|
|
187
|
-
};
|
|
188
|
-
/**
|
|
189
|
-
* The seven colour roles plus radius/spacing, as tokens. Derived from a kit's
|
|
190
|
-
* summary fields rather than named by the source system, so they carry no
|
|
191
|
-
* `group`.
|
|
192
|
-
*/
|
|
193
|
-
export function brandKitRoleTokens(data, source = "Brand Kit") {
|
|
194
|
-
if (!data)
|
|
195
|
-
return [];
|
|
196
|
-
const out = [];
|
|
197
|
-
const push = (cssVar, value) => {
|
|
198
|
-
if (typeof value !== "string" || !value.trim())
|
|
199
|
-
return;
|
|
200
|
-
if (!isSafeCssVarName(cssVar) || !isSafeCssTokenValue(value.trim()))
|
|
201
|
-
return;
|
|
202
|
-
out.push({
|
|
203
|
-
name: friendlyTokenName(cssVar),
|
|
204
|
-
cssVar,
|
|
205
|
-
value: value.trim(),
|
|
206
|
-
type: classifyBrandKitToken(cssVar, value.trim()),
|
|
207
|
-
source,
|
|
208
|
-
});
|
|
209
|
-
};
|
|
210
|
-
const colors = (data.colors ?? {});
|
|
211
|
-
const spacing = (data.spacing ?? {});
|
|
212
|
-
for (const [role, cssVar] of Object.entries(BRAND_KIT_COLOR_ROLE_VARS)) {
|
|
213
|
-
push(cssVar, colors[role]);
|
|
214
|
-
}
|
|
215
|
-
push("--radius", data.borders?.radius);
|
|
216
|
-
push("--spacing-element-gap", spacing.elementGap);
|
|
217
|
-
push("--spacing-page-padding", spacing.pagePadding);
|
|
218
|
-
return out;
|
|
219
|
-
}
|
|
220
|
-
const CSS_CUSTOM_PROPERTY_DECLARATION = /(--[-_a-zA-Z0-9]+)\s*:\s*([^;{}]+)[;}]/g;
|
|
221
|
-
/**
|
|
222
|
-
* Read a kit's named tokens out of a raw CSS block. A kit's `customCSS` already
|
|
223
|
-
* spells out the source system's real token names. Declaration order wins on
|
|
224
|
-
* duplicates, matching the cascade.
|
|
225
|
-
*/
|
|
226
|
-
export function parseBrandKitTokensFromCss(css, source) {
|
|
227
|
-
const byCssVar = new Map();
|
|
228
|
-
for (const match of css.matchAll(CSS_CUSTOM_PROPERTY_DECLARATION)) {
|
|
229
|
-
const cssVar = match[1];
|
|
230
|
-
const value = match[2].trim();
|
|
231
|
-
if (!isSafeCssVarName(cssVar) || !isSafeCssTokenValue(value))
|
|
232
|
-
continue;
|
|
233
|
-
byCssVar.set(cssVar, {
|
|
234
|
-
name: cssVar.replace(/^--/, ""),
|
|
235
|
-
cssVar,
|
|
236
|
-
value,
|
|
237
|
-
type: classifyBrandKitToken(cssVar, value),
|
|
238
|
-
...(source ? { source } : {}),
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
return [...byCssVar.values()].slice(0, MAX_BRAND_KIT_TOKENS);
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* The kit's named vocabulary: its stored `tokens` when it has them, else the
|
|
245
|
-
* names its `customCSS` declares. Kits predating `tokens` still carry real names
|
|
246
|
-
* in CSS, so reading only `tokens` would report them as having none.
|
|
247
|
-
*/
|
|
248
|
-
export function resolveBrandKitTokens(data, cssSource) {
|
|
249
|
-
if (!data)
|
|
250
|
-
return [];
|
|
251
|
-
const stored = normalizeBrandKitTokens(data.tokens).tokens;
|
|
252
|
-
if (stored.length > 0)
|
|
253
|
-
return stored;
|
|
254
|
-
return typeof data.customCSS === "string"
|
|
255
|
-
? parseBrandKitTokensFromCss(data.customCSS, cssSource)
|
|
256
|
-
: [];
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Group tokens for display, preserving the source's collection paths. Groups sort
|
|
260
|
-
* by token type so colors lead, then alphabetically within a type.
|
|
261
|
-
*/
|
|
262
|
-
export function groupBrandKitTokens(tokens) {
|
|
263
|
-
const groups = new Map();
|
|
264
|
-
for (const token of tokens) {
|
|
265
|
-
const label = token.group?.trim() || token.type;
|
|
266
|
-
const key = `${token.type}::${label}`;
|
|
267
|
-
const existing = groups.get(key);
|
|
268
|
-
if (existing) {
|
|
269
|
-
existing.tokens.push(token);
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
groups.set(key, { label, type: token.type, tokens: [token] });
|
|
273
|
-
}
|
|
274
|
-
return [...groups.values()].sort((a, b) => {
|
|
275
|
-
const byType = BRAND_KIT_TOKEN_TYPES.indexOf(a.type) -
|
|
276
|
-
BRAND_KIT_TOKEN_TYPES.indexOf(b.type);
|
|
277
|
-
return byType !== 0 ? byType : a.label.localeCompare(b.label);
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
//# sourceMappingURL=tokens.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
declare const _default: import("../../action.js").ActionDefinition<{
|
|
2
|
-
templateId: string;
|
|
3
|
-
limit?: unknown;
|
|
4
|
-
}, import("../provider-metrics.js").ProviderMetricsResult<import("../provider-metrics.js").EmailActivityEntry[]>>;
|
|
5
|
-
export default _default;
|
|
6
|
-
//# sourceMappingURL=list-email-activity.d.ts.map
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineAction } from "../../action.js";
|
|
3
|
-
import { authorizeTransactionalEmailRead } from "../authorize.js";
|
|
4
|
-
import { fetchEmailActivity } from "../provider-metrics.js";
|
|
5
|
-
export default defineAction({
|
|
6
|
-
description: "List recent provider activity for one registered transactional email in this app. Organization admin only.",
|
|
7
|
-
schema: z.object({
|
|
8
|
-
templateId: z.string().min(1),
|
|
9
|
-
limit: z.coerce.number().int().min(1).max(1000).default(50),
|
|
10
|
-
}),
|
|
11
|
-
http: { method: "GET" },
|
|
12
|
-
authorize: ({ templateId }) => authorizeTransactionalEmailRead([templateId]),
|
|
13
|
-
run: async ({ templateId, limit }) => fetchEmailActivity({ templateId, limit }),
|
|
14
|
-
});
|
|
15
|
-
//# sourceMappingURL=list-email-activity.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
declare const _default: import("../../action.js").ActionDefinition<{
|
|
2
|
-
templateIds: string[];
|
|
3
|
-
windowDays?: unknown;
|
|
4
|
-
}, import("../provider-metrics.js").ProviderMetricsResult<import("../provider-metrics.js").EmailEngagement[]>>;
|
|
5
|
-
export default _default;
|
|
6
|
-
//# sourceMappingURL=list-email-engagement.d.ts.map
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineAction } from "../../action.js";
|
|
3
|
-
import { authorizeTransactionalEmailRead } from "../authorize.js";
|
|
4
|
-
import { fetchEmailEngagement } from "../provider-metrics.js";
|
|
5
|
-
export default defineAction({
|
|
6
|
-
description: "Read provider engagement metrics for registered transactional emails in this app. Organization admin only.",
|
|
7
|
-
schema: z.object({
|
|
8
|
-
templateIds: z.array(z.string().min(1)).max(100),
|
|
9
|
-
windowDays: z.coerce.number().int().min(1).max(365).default(30),
|
|
10
|
-
}),
|
|
11
|
-
http: { method: "POST" },
|
|
12
|
-
authorize: ({ templateIds }) => authorizeTransactionalEmailRead(templateIds),
|
|
13
|
-
run: async ({ templateIds, windowDays }) => fetchEmailEngagement(templateIds, windowDays),
|
|
14
|
-
});
|
|
15
|
-
//# sourceMappingURL=list-email-engagement.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineAction } from "../../action.js";
|
|
3
|
-
import { getAppSlug } from "../../server/app-name.js";
|
|
4
|
-
import { authorizeTransactionalEmailRead } from "../authorize.js";
|
|
5
|
-
import { listEmailLog } from "../log.js";
|
|
6
|
-
export default defineAction({
|
|
7
|
-
description: "List recent transactional email sends from this app, newest first, optionally filtered to one registered email id.",
|
|
8
|
-
schema: z.object({
|
|
9
|
-
templateId: z.string().optional(),
|
|
10
|
-
limit: z.coerce.number().int().min(1).max(500).default(100),
|
|
11
|
-
}),
|
|
12
|
-
http: { method: "GET" },
|
|
13
|
-
authorize: ({ templateId }) => authorizeTransactionalEmailRead(templateId ? [templateId] : []),
|
|
14
|
-
run: async ({ templateId, limit }) => ({
|
|
15
|
-
entries: await listEmailLog({
|
|
16
|
-
app: getAppSlug() ?? "unknown",
|
|
17
|
-
templateId,
|
|
18
|
-
limit,
|
|
19
|
-
}),
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
//# sourceMappingURL=list-email-log.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
declare const _default: import("../../action.js").ActionDefinition<{
|
|
2
|
-
windowDays?: unknown;
|
|
3
|
-
}, {
|
|
4
|
-
app: string;
|
|
5
|
-
windowDays: number;
|
|
6
|
-
statsAvailable: boolean;
|
|
7
|
-
statsError: string;
|
|
8
|
-
emails: {
|
|
9
|
-
id: string;
|
|
10
|
-
app: string;
|
|
11
|
-
name: string;
|
|
12
|
-
trigger: string;
|
|
13
|
-
recipient: string;
|
|
14
|
-
recipientLabel: string;
|
|
15
|
-
sender: string;
|
|
16
|
-
senderLabel: string;
|
|
17
|
-
sent: number;
|
|
18
|
-
failed: number;
|
|
19
|
-
lastSentAt: number;
|
|
20
|
-
}[];
|
|
21
|
-
}>;
|
|
22
|
-
export default _default;
|
|
23
|
-
//# sourceMappingURL=list-transactional-emails.d.ts.map
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineAction } from "../../action.js";
|
|
3
|
-
import { getAppSlug } from "../../server/app-name.js";
|
|
4
|
-
import { getEmailSendStats } from "../log.js";
|
|
5
|
-
import { listTransactionalEmails } from "../registry.js";
|
|
6
|
-
import { registerCoreSystemEmails } from "../system-emails.js";
|
|
7
|
-
const DEFAULT_WINDOW_DAYS = 30;
|
|
8
|
-
export default defineAction({
|
|
9
|
-
description: "List the transactional emails this app can send, with the trigger, recipient and sender logic for each, plus local send counts and last-sent. Engagement metrics such as open rate are not included here — they live at the email provider.",
|
|
10
|
-
schema: z.object({
|
|
11
|
-
windowDays: z.coerce
|
|
12
|
-
.number()
|
|
13
|
-
.int()
|
|
14
|
-
.min(1)
|
|
15
|
-
.max(365)
|
|
16
|
-
.default(DEFAULT_WINDOW_DAYS)
|
|
17
|
-
.describe("How many days of send history to summarize."),
|
|
18
|
-
}),
|
|
19
|
-
http: { method: "GET" },
|
|
20
|
-
run: async ({ windowDays }) => {
|
|
21
|
-
registerCoreSystemEmails();
|
|
22
|
-
const since = Date.now() - windowDays * 24 * 60 * 60 * 1000;
|
|
23
|
-
const app = getAppSlug() ?? "unknown";
|
|
24
|
-
const definitions = listTransactionalEmails();
|
|
25
|
-
// A failed stats read must not masquerade as "no email ever sent" — the
|
|
26
|
-
// catalog is still worth returning, but the caller has to be able to tell
|
|
27
|
-
// that the numbers are missing rather than zero.
|
|
28
|
-
let statsById = null;
|
|
29
|
-
let statsError = null;
|
|
30
|
-
try {
|
|
31
|
-
const stats = await getEmailSendStats(since, app);
|
|
32
|
-
statsById = new Map(stats.map((row) => [row.templateId, row]));
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
statsError = error instanceof Error ? error.message : String(error);
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
app,
|
|
39
|
-
windowDays,
|
|
40
|
-
statsAvailable: statsError === null,
|
|
41
|
-
statsError,
|
|
42
|
-
emails: definitions.map((definition) => {
|
|
43
|
-
const stats = statsById?.get(definition.id);
|
|
44
|
-
return {
|
|
45
|
-
id: definition.id,
|
|
46
|
-
app: definition.app,
|
|
47
|
-
name: definition.name,
|
|
48
|
-
trigger: definition.trigger,
|
|
49
|
-
recipient: definition.recipient,
|
|
50
|
-
recipientLabel: definition.recipientLabel,
|
|
51
|
-
sender: definition.sender,
|
|
52
|
-
senderLabel: definition.senderLabel,
|
|
53
|
-
sent: stats?.sent ?? (statsById ? 0 : null),
|
|
54
|
-
failed: stats?.failed ?? (statsById ? 0 : null),
|
|
55
|
-
lastSentAt: stats?.lastSentAt ?? null,
|
|
56
|
-
};
|
|
57
|
-
}),
|
|
58
|
-
};
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
//# sourceMappingURL=list-transactional-emails.js.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineAction } from "../../action.js";
|
|
3
|
-
import { renderTransactionalEmailPreview } from "../registry.js";
|
|
4
|
-
import { registerCoreSystemEmails } from "../system-emails.js";
|
|
5
|
-
export default defineAction({
|
|
6
|
-
description: "Render one of this app's transactional emails with representative dummy data, returning the subject, HTML and plain-text bodies for preview.",
|
|
7
|
-
schema: z.object({
|
|
8
|
-
id: z
|
|
9
|
-
.string()
|
|
10
|
-
.describe("Registered email id, e.g. calendar.booking-confirmed."),
|
|
11
|
-
}),
|
|
12
|
-
http: { method: "GET" },
|
|
13
|
-
run: async ({ id }) => {
|
|
14
|
-
registerCoreSystemEmails();
|
|
15
|
-
const rendered = renderTransactionalEmailPreview(id);
|
|
16
|
-
return {
|
|
17
|
-
id,
|
|
18
|
-
subject: rendered.subject,
|
|
19
|
-
html: rendered.html,
|
|
20
|
-
text: rendered.text,
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
//# sourceMappingURL=render-transactional-email-preview.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { currentRequestUserIsOrgAdmin } from "../server/org-admin.js";
|
|
2
|
-
import { getTransactionalEmail } from "./registry.js";
|
|
3
|
-
import { registerCoreSystemEmails } from "./system-emails.js";
|
|
4
|
-
export async function authorizeTransactionalEmailRead(templateIds = []) {
|
|
5
|
-
if (!(await currentRequestUserIsOrgAdmin()))
|
|
6
|
-
return false;
|
|
7
|
-
registerCoreSystemEmails();
|
|
8
|
-
return templateIds.every((id) => Boolean(getTransactionalEmail(id)));
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=authorize.js.map
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable send log for transactional emails.
|
|
3
|
-
*
|
|
4
|
-
* Written by `sendEmail` on every attempt, successful or not. Read by Dispatch
|
|
5
|
-
* to report per-email send counts and last-sent without depending on the
|
|
6
|
-
* provider's activity retention window.
|
|
7
|
-
*/
|
|
8
|
-
export interface RecordEmailSendArgs {
|
|
9
|
-
templateId?: string;
|
|
10
|
-
app?: string;
|
|
11
|
-
recipient: string;
|
|
12
|
-
sender: string;
|
|
13
|
-
subject: string;
|
|
14
|
-
status: "sent" | "failed";
|
|
15
|
-
error?: string;
|
|
16
|
-
provider: string;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Append one send record.
|
|
20
|
-
*
|
|
21
|
-
* Callers treat logging as best-effort: a logging failure must not turn a
|
|
22
|
-
* delivered email into a thrown send. The failure is surfaced on the console
|
|
23
|
-
* rather than swallowed, so a persistently broken log is visible instead of
|
|
24
|
-
* quietly producing an empty activity view.
|
|
25
|
-
*/
|
|
26
|
-
export declare function recordEmailSend(args: RecordEmailSendArgs): Promise<void>;
|
|
27
|
-
export interface EmailSendStats {
|
|
28
|
-
templateId: string;
|
|
29
|
-
sent: number;
|
|
30
|
-
failed: number;
|
|
31
|
-
lastSentAt: number | null;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Per-template send counts and last-sent, for sends at or after `since`.
|
|
35
|
-
* Templates with no rows are absent from the result — callers distinguish
|
|
36
|
-
* "never sent" from "sent zero times in window" by that absence.
|
|
37
|
-
*/
|
|
38
|
-
export declare function getEmailSendStats(since: number, app: string): Promise<EmailSendStats[]>;
|
|
39
|
-
export interface EmailLogEntry {
|
|
40
|
-
id: string;
|
|
41
|
-
templateId: string | null;
|
|
42
|
-
app: string | null;
|
|
43
|
-
recipient: string;
|
|
44
|
-
sender: string;
|
|
45
|
-
subject: string;
|
|
46
|
-
status: string;
|
|
47
|
-
error: string | null;
|
|
48
|
-
provider: string;
|
|
49
|
-
createdAt: number;
|
|
50
|
-
}
|
|
51
|
-
/** Most recent sends for one app, newest first, optionally filtered to one template. */
|
|
52
|
-
export declare function listEmailLog(options: {
|
|
53
|
-
app: string;
|
|
54
|
-
templateId?: string;
|
|
55
|
-
limit?: number;
|
|
56
|
-
}): Promise<EmailLogEntry[]>;
|
|
57
|
-
//# sourceMappingURL=log.d.ts.map
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable send log for transactional emails.
|
|
3
|
-
*
|
|
4
|
-
* Written by `sendEmail` on every attempt, successful or not. Read by Dispatch
|
|
5
|
-
* to report per-email send counts and last-sent without depending on the
|
|
6
|
-
* provider's activity retention window.
|
|
7
|
-
*/
|
|
8
|
-
import { randomUUID } from "node:crypto";
|
|
9
|
-
import { getDbExec, isPostgres } from "../db/client.js";
|
|
10
|
-
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
11
|
-
let _initPromise;
|
|
12
|
-
async function ensureTable() {
|
|
13
|
-
if (!_initPromise) {
|
|
14
|
-
_initPromise = (async () => {
|
|
15
|
-
const { EMAIL_LOG_CREATE_SQL, EMAIL_LOG_TEMPLATE_INDEX_SQL } = await import("./schema.js");
|
|
16
|
-
// Generic INTEGER maps to BIGINT on Postgres, which millisecond
|
|
17
|
-
// timestamps need.
|
|
18
|
-
const createSql = isPostgres()
|
|
19
|
-
? EMAIL_LOG_CREATE_SQL.replace(/\bINTEGER\b/g, "BIGINT")
|
|
20
|
-
: EMAIL_LOG_CREATE_SQL;
|
|
21
|
-
await ensureTableExists("email_log", createSql);
|
|
22
|
-
await ensureIndexExists("email_log_template_created_idx", EMAIL_LOG_TEMPLATE_INDEX_SQL);
|
|
23
|
-
})().catch((error) => {
|
|
24
|
-
// Don't memoize a failed bootstrap — the next send should retry rather
|
|
25
|
-
// than log nothing forever.
|
|
26
|
-
_initPromise = undefined;
|
|
27
|
-
throw error;
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return _initPromise;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Append one send record.
|
|
34
|
-
*
|
|
35
|
-
* Callers treat logging as best-effort: a logging failure must not turn a
|
|
36
|
-
* delivered email into a thrown send. The failure is surfaced on the console
|
|
37
|
-
* rather than swallowed, so a persistently broken log is visible instead of
|
|
38
|
-
* quietly producing an empty activity view.
|
|
39
|
-
*/
|
|
40
|
-
export async function recordEmailSend(args) {
|
|
41
|
-
try {
|
|
42
|
-
await ensureTable();
|
|
43
|
-
await getDbExec().execute({
|
|
44
|
-
sql: `INSERT INTO email_log
|
|
45
|
-
(id, template_id, app, recipient, sender, subject, status, error, provider, created_at)
|
|
46
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
47
|
-
args: [
|
|
48
|
-
randomUUID(),
|
|
49
|
-
args.templateId ?? null,
|
|
50
|
-
args.app ?? null,
|
|
51
|
-
args.recipient,
|
|
52
|
-
args.sender,
|
|
53
|
-
args.subject,
|
|
54
|
-
args.status,
|
|
55
|
-
args.error ?? null,
|
|
56
|
-
args.provider,
|
|
57
|
-
Date.now(),
|
|
58
|
-
],
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
console.error("[agent-native:email] failed to record send", error);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Per-template send counts and last-sent, for sends at or after `since`.
|
|
67
|
-
* Templates with no rows are absent from the result — callers distinguish
|
|
68
|
-
* "never sent" from "sent zero times in window" by that absence.
|
|
69
|
-
*/
|
|
70
|
-
export async function getEmailSendStats(since, app) {
|
|
71
|
-
await ensureTable();
|
|
72
|
-
const { rows } = await getDbExec().execute({
|
|
73
|
-
sql: `SELECT template_id,
|
|
74
|
-
SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) AS sent,
|
|
75
|
-
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed,
|
|
76
|
-
MAX(CASE WHEN status = 'sent' THEN created_at END) AS last_sent_at
|
|
77
|
-
FROM email_log
|
|
78
|
-
WHERE app = ? AND template_id IS NOT NULL AND created_at >= ?
|
|
79
|
-
GROUP BY template_id`,
|
|
80
|
-
args: [app, since],
|
|
81
|
-
});
|
|
82
|
-
return rows.map((row) => ({
|
|
83
|
-
templateId: String(row.template_id),
|
|
84
|
-
sent: Number(row.sent ?? 0),
|
|
85
|
-
failed: Number(row.failed ?? 0),
|
|
86
|
-
lastSentAt: row.last_sent_at == null ? null : Number(row.last_sent_at),
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
/** Most recent sends for one app, newest first, optionally filtered to one template. */
|
|
90
|
-
export async function listEmailLog(options) {
|
|
91
|
-
await ensureTable();
|
|
92
|
-
const limit = Math.min(Math.max(options.limit ?? 100, 1), 500);
|
|
93
|
-
const where = options.templateId
|
|
94
|
-
? `WHERE app = ? AND template_id = ?`
|
|
95
|
-
: `WHERE app = ?`;
|
|
96
|
-
const args = options.templateId
|
|
97
|
-
? [options.app, options.templateId, limit]
|
|
98
|
-
: [options.app, limit];
|
|
99
|
-
const { rows } = await getDbExec().execute({
|
|
100
|
-
sql: `SELECT id, template_id, app, recipient, sender, subject, status, error, provider, created_at
|
|
101
|
-
FROM email_log ${where} ORDER BY created_at DESC LIMIT ?`,
|
|
102
|
-
args,
|
|
103
|
-
});
|
|
104
|
-
return rows.map((row) => ({
|
|
105
|
-
id: String(row.id),
|
|
106
|
-
templateId: row.template_id == null ? null : String(row.template_id),
|
|
107
|
-
app: row.app == null ? null : String(row.app),
|
|
108
|
-
recipient: String(row.recipient),
|
|
109
|
-
sender: String(row.sender),
|
|
110
|
-
subject: String(row.subject),
|
|
111
|
-
status: String(row.status),
|
|
112
|
-
error: row.error == null ? null : String(row.error),
|
|
113
|
-
provider: String(row.provider),
|
|
114
|
-
createdAt: Number(row.created_at),
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
//# sourceMappingURL=log.js.map
|