@cosmicdrift/kumiko-samples 0.349.0 → 0.351.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 +1 -1
- package/packages/bundled-features/package.json +1 -1
- package/packages/bundled-features/src/audit/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/email-templates.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/reset-token.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/unlock-token.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/verification-token.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/constants.ts +3 -17
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +2 -9
- package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +3 -11
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +1 -1
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +1 -9
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +15 -5
- package/packages/bundled-features/src/channel-email/pii-guard.ts +6 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +1 -0
- package/packages/bundled-features/src/channel-email/types.ts +3 -0
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/jobs.ts +3 -1
- package/packages/bundled-features/src/delivery/types.ts +3 -0
- package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/lookup.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +1 -1
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/metrics/compute.ts +1 -1
- package/packages/bundled-features/src/metrics/feature.ts +1 -1
- package/packages/bundled-features/src/metrics/types.ts +1 -1
- package/packages/bundled-features/src/metrics/window.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/expiry.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +1 -1
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +68 -15
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/session-callbacks.ts +1 -1
- package/packages/bundled-features/src/shared/grace-period.ts +3 -10
- package/packages/bundled-features/src/shared/row-bound-grant.ts +1 -1
- package/packages/bundled-features/src/shared/signed-token.ts +1 -1
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +1 -1
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/by-tenant.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/collection-shared.ts +1 -1
- package/packages/bundled-features/src/template-resolver/table.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/invited-membership.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/db/queries/export-jobs.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +3 -9
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +29 -5
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +8 -8
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +1 -1
- package/samples/apps/marketing-demo/package.json +1 -0
- package/samples/apps/marketing-demo/src/app/seed.ts +1 -1
- package/samples/apps/use-all-bundled/package.json +1 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +1 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
9
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
10
|
-
import { Temporal } from "temporal
|
|
10
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
11
11
|
import * as z from "zod";
|
|
12
12
|
import { encryptForDirectWrite, verifyPassword } from "../../shared/index.js";
|
|
13
13
|
import { UserQueries } from "../../user/index.js";
|
|
@@ -2,7 +2,7 @@ import { updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
|
-
import { Temporal } from "temporal
|
|
5
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { API_TOKEN_ENTITY_NAME, PatErrors } from "../constants.js";
|
|
8
8
|
import { PAT_REVOKED_AGGREGATE_TYPE, PAT_REVOKED_EVENT_QN } from "../pat-revoked-event.js";
|
|
@@ -3,7 +3,7 @@ import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
|
3
3
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
5
5
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
6
|
-
import { Temporal } from "temporal
|
|
6
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
7
7
|
import {
|
|
8
8
|
PAT_REVOKED_AGGREGATE_TYPE,
|
|
9
9
|
PAT_REVOKED_EVENT_QN,
|
|
@@ -117,21 +117,35 @@ function renderBrandingHeader(
|
|
|
117
117
|
return `<div style="margin:0 0 24px;padding:0 0 16px;border-bottom:3px solid ${escapeHtmlAttr(primaryColor)}">${logo}</div>`;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
type ResolvedBrandingFooter = {
|
|
121
|
+
readonly footerText: string | undefined;
|
|
122
|
+
readonly links: readonly { readonly label: string; readonly url: string }[];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
function resolveBrandingFooter(
|
|
126
|
+
branding: MailBranding,
|
|
122
127
|
locale: string | undefined,
|
|
123
|
-
):
|
|
124
|
-
if (!branding) return "";
|
|
128
|
+
): ResolvedBrandingFooter {
|
|
125
129
|
const pick = (value: LocalizedText | undefined): string | undefined =>
|
|
126
130
|
resolveLocalized(value, locale, branding.defaultLocale);
|
|
127
131
|
const links = (branding.footerLinks ?? []).flatMap((link) => {
|
|
128
132
|
const url = pick(link.url);
|
|
129
133
|
if (url === undefined || !isHttpUrl(url)) return [];
|
|
130
|
-
return [
|
|
131
|
-
`<a href="${escapeHtmlAttr(url)}" style="color:#999">${escapeHtml(pick(link.label) ?? "")}</a>`,
|
|
132
|
-
];
|
|
134
|
+
return [{ label: pick(link.label) ?? "", url }];
|
|
133
135
|
});
|
|
134
|
-
|
|
136
|
+
return { footerText: pick(branding.footerText), links };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function renderBrandingFooter(
|
|
140
|
+
branding: MailBranding | undefined,
|
|
141
|
+
locale: string | undefined,
|
|
142
|
+
): string {
|
|
143
|
+
if (!branding) return "";
|
|
144
|
+
const { footerText, links: resolvedLinks } = resolveBrandingFooter(branding, locale);
|
|
145
|
+
const links = resolvedLinks.map(
|
|
146
|
+
(link) =>
|
|
147
|
+
`<a href="${escapeHtmlAttr(link.url)}" style="color:#999">${escapeHtml(link.label)}</a>`,
|
|
148
|
+
);
|
|
135
149
|
const footerPartsHtml = [footerText ? escapeHtml(footerText) : "", links.join(" · ")].filter(
|
|
136
150
|
(part) => part !== "",
|
|
137
151
|
);
|
|
@@ -140,6 +154,18 @@ function renderBrandingFooter(
|
|
|
140
154
|
return `<p style="margin:16px 0 0;color:#999;font-size:12px">${footerHtml}</p>`;
|
|
141
155
|
}
|
|
142
156
|
|
|
157
|
+
function brandingFooterLines(
|
|
158
|
+
branding: MailBranding | undefined,
|
|
159
|
+
locale: string | undefined,
|
|
160
|
+
): readonly string[] {
|
|
161
|
+
if (!branding) return [];
|
|
162
|
+
const { footerText, links } = resolveBrandingFooter(branding, locale);
|
|
163
|
+
return [
|
|
164
|
+
...(footerText ? [footerText] : []),
|
|
165
|
+
...links.map((link) => (link.label ? `${link.label}: ${link.url}` : link.url)),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
|
|
143
169
|
type Section =
|
|
144
170
|
| { readonly text: string }
|
|
145
171
|
| { readonly heading: string }
|
|
@@ -156,6 +182,29 @@ type EmailTemplateData = {
|
|
|
156
182
|
readonly body?: string;
|
|
157
183
|
};
|
|
158
184
|
|
|
185
|
+
function templateContent(variables: Readonly<Record<string, unknown>>): {
|
|
186
|
+
readonly header: string | undefined;
|
|
187
|
+
readonly sections: readonly Section[] | undefined;
|
|
188
|
+
readonly footer: string | undefined;
|
|
189
|
+
} {
|
|
190
|
+
const data = variables as EmailTemplateData; // @cast-boundary render-helper
|
|
191
|
+
// Without structured fields, title + body become the header and a single text section.
|
|
192
|
+
return {
|
|
193
|
+
header: data.header ?? data.title,
|
|
194
|
+
sections: data.sections ?? (data.body ? [{ text: data.body }] : undefined),
|
|
195
|
+
footer: data.footer,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Markdown stays as written: its source is already readable plain text.
|
|
200
|
+
function sectionText(section: Section): string {
|
|
201
|
+
if ("text" in section) return section.text;
|
|
202
|
+
if ("heading" in section) return section.heading;
|
|
203
|
+
if ("markdown" in section) return section.markdown;
|
|
204
|
+
if ("button" in section) return `${section.button.label}: ${section.button.url}`;
|
|
205
|
+
return "";
|
|
206
|
+
}
|
|
207
|
+
|
|
159
208
|
function renderSection(section: Section, primaryColor: string): string {
|
|
160
209
|
if ("text" in section) {
|
|
161
210
|
return `<p style="margin:0 0 16px;color:#333;font-size:14px;line-height:1.5">${escapeHtml(section.text)}</p>`;
|
|
@@ -183,11 +232,7 @@ export function createSimpleRenderer(branding?: MailBranding): NotificationRende
|
|
|
183
232
|
name: "simple",
|
|
184
233
|
|
|
185
234
|
async render(input) {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
// Fallback: if no structured fields, use title + body as header + single text section
|
|
189
|
-
const header = data.header ?? data.title;
|
|
190
|
-
const sections = data.sections ?? (data.body ? [{ text: data.body }] : undefined);
|
|
235
|
+
const { header, sections, footer } = templateContent(input.variables);
|
|
191
236
|
|
|
192
237
|
const parts: string[] = [];
|
|
193
238
|
parts.push('<!DOCTYPE html><html><body style="margin:0;padding:0;font-family:sans-serif">');
|
|
@@ -206,9 +251,9 @@ export function createSimpleRenderer(branding?: MailBranding): NotificationRende
|
|
|
206
251
|
}
|
|
207
252
|
}
|
|
208
253
|
|
|
209
|
-
if (
|
|
254
|
+
if (footer) {
|
|
210
255
|
parts.push(
|
|
211
|
-
`<p style="margin:24px 0 0;color:#999;font-size:12px;border-top:1px solid #eee;padding-top:16px">${escapeHtml(
|
|
256
|
+
`<p style="margin:24px 0 0;color:#999;font-size:12px;border-top:1px solid #eee;padding-top:16px">${escapeHtml(footer)}</p>`,
|
|
212
257
|
);
|
|
213
258
|
}
|
|
214
259
|
|
|
@@ -216,6 +261,14 @@ export function createSimpleRenderer(branding?: MailBranding): NotificationRende
|
|
|
216
261
|
parts.push("</div></body></html>");
|
|
217
262
|
return parts.join("");
|
|
218
263
|
},
|
|
264
|
+
|
|
265
|
+
async renderText(input) {
|
|
266
|
+
const { header, sections, footer } = templateContent(input.variables);
|
|
267
|
+
const brandingFooter = brandingFooterLines(branding, input.locale).join("\n");
|
|
268
|
+
return [header, ...(sections ?? []).map(sectionText), footer, brandingFooter]
|
|
269
|
+
.filter((block): block is string => block !== undefined && block !== "")
|
|
270
|
+
.join("\n\n");
|
|
271
|
+
},
|
|
219
272
|
};
|
|
220
273
|
}
|
|
221
274
|
|
|
@@ -4,7 +4,7 @@ import { access, defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumik
|
|
|
4
4
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
5
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
6
6
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
7
|
-
import { Temporal } from "temporal
|
|
7
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
8
8
|
import * as z from "zod";
|
|
9
9
|
import { userSessionTable } from "../schema/user-session.js";
|
|
10
10
|
import {
|
|
@@ -2,7 +2,7 @@ import { updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
|
-
import { Temporal } from "temporal
|
|
5
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { SessionErrors } from "../constants.js";
|
|
8
8
|
import { userSessionTable } from "../schema/user-session.js";
|
|
@@ -2,7 +2,7 @@ import { fetchOne, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
|
-
import { Temporal } from "temporal
|
|
5
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { SessionErrors, USER_SESSION_ENTITY_NAME } from "../constants.js";
|
|
8
8
|
import { userSessionTable } from "../schema/user-session.js";
|
|
@@ -15,7 +15,7 @@ import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine
|
|
|
15
15
|
import { buildSessionRoles, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
16
16
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
17
17
|
import { generateId, parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
18
|
-
import { Temporal } from "temporal
|
|
18
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
19
19
|
import { encryptForDirectWrite } from "../shared/index.js";
|
|
20
20
|
import { tenantMembershipsTable } from "../tenant/index.js";
|
|
21
21
|
import { isPrincipalBlocked, type UserStatus, userTable } from "../user/index.js";
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
/** Shared grace-period check for user-data-rights + tenant-lifecycle cancel flows. */
|
|
3
3
|
|
|
4
|
-
import { Temporal
|
|
4
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* True when `gracePeriodEnd` is still in the future.
|
|
8
|
-
* Uses the
|
|
8
|
+
* Uses the module's Temporal so callers don't depend on `globalThis.Temporal`.
|
|
9
9
|
*/
|
|
10
10
|
export function isWithinGracePeriod(gracePeriodEnd: Temporal.Instant | null): boolean {
|
|
11
|
-
// @cast-boundary temporal-polyfill-vs-ambient: same TC39 Temporal.Instant
|
|
12
|
-
// at runtime — row types resolve against ambient Temporal; polyfill Instant
|
|
13
|
-
// is a separate nominal type across the two .d.ts sources.
|
|
14
11
|
return (
|
|
15
|
-
gracePeriodEnd != null &&
|
|
16
|
-
TemporalPolyfill.Instant.compare(
|
|
17
|
-
gracePeriodEnd as unknown as InstanceType<typeof TemporalPolyfill.Instant>,
|
|
18
|
-
TemporalPolyfill.Now.instant(),
|
|
19
|
-
) > 0
|
|
12
|
+
gracePeriodEnd != null && Temporal.Instant.compare(gracePeriodEnd, Temporal.Now.instant()) > 0
|
|
20
13
|
);
|
|
21
14
|
}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
// (single-use-token-store) instead.
|
|
32
32
|
|
|
33
33
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
34
|
-
import type { Temporal } from "temporal
|
|
34
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
35
35
|
import { peekTokenSubject, signToken, verifyToken } from "./signed-token.js";
|
|
36
36
|
|
|
37
37
|
export type RowBoundGrantResult =
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// signal through a short-circuit.
|
|
14
14
|
|
|
15
15
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
16
|
-
import { Temporal } from "temporal
|
|
16
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
17
17
|
|
|
18
18
|
export type VerifyResult =
|
|
19
19
|
| { readonly ok: true; readonly userId: string; readonly expiresAtMs: number }
|
|
@@ -23,11 +23,11 @@ import {
|
|
|
23
23
|
SubscriptionStatuses,
|
|
24
24
|
} from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
25
25
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
26
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
26
27
|
import type {
|
|
27
28
|
Payment as MolliePayment,
|
|
28
29
|
Subscription as MollieSubscription,
|
|
29
30
|
} from "@mollie/api-client";
|
|
30
|
-
import { Temporal } from "temporal-polyfill";
|
|
31
31
|
import { MOLLIE_PROVIDER_NAME } from "./constants.js";
|
|
32
32
|
import type { MolliePriceConfig } from "./plugin-methods.js";
|
|
33
33
|
|
|
@@ -47,8 +47,8 @@ import {
|
|
|
47
47
|
import { ExtraRouteRejection } from "@cosmicdrift/kumiko-framework/api";
|
|
48
48
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
49
49
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
50
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
50
51
|
import Stripe from "stripe";
|
|
51
|
-
import { Temporal } from "temporal-polyfill";
|
|
52
52
|
import { STRIPE_PROVIDER_NAME, StripeEventTypes } from "./constants.js";
|
|
53
53
|
import type { StripeWebhookRuntime } from "./runtime.js";
|
|
54
54
|
|
|
@@ -6,7 +6,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
6
6
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
7
7
|
import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
|
-
import type { Temporal } from "temporal
|
|
9
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
10
10
|
import type { ContentFormat, TemplateKind } from "./constants.js";
|
|
11
11
|
import { FALLBACK_LOCALE, SYSTEM_TENANT_ID } from "./constants.js";
|
|
12
12
|
import { type TemplateResourceRow, templateResourcesTable } from "./table.js";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
crossTenantOverrideDenied,
|
|
5
5
|
defineQueryHandler,
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
|
-
import type { Temporal } from "temporal
|
|
7
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
8
8
|
import * as z from "zod";
|
|
9
9
|
import { TEXT_BLOCK_KIND } from "../constants.js";
|
|
10
10
|
import { type TemplateResourceRow, templateResourcesTable } from "../table.js";
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
TenantId,
|
|
8
8
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
9
9
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
10
|
-
import type { Temporal } from "temporal
|
|
10
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
11
11
|
import { templateResourceEntity, templateResourcesTable } from "../table.js";
|
|
12
12
|
import { userContentEntriesTable, userContentEntryEntity } from "../user-content-table.js";
|
|
13
13
|
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createSelectField,
|
|
6
6
|
createTextField,
|
|
7
7
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
-
import type { Temporal } from "temporal
|
|
8
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
9
9
|
import {
|
|
10
10
|
CONTENT_FORMATS,
|
|
11
11
|
TEMPLATE_KINDS,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
7
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
8
8
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
9
|
-
import { Temporal } from "temporal
|
|
9
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
10
10
|
import * as z from "zod";
|
|
11
11
|
import { userSessionTable } from "../../sessions/index.js";
|
|
12
12
|
import { decryptStoredPii, mapWithConcurrency } from "../../shared/index.js";
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { createSystemUser, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
import { ConflictError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
9
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
10
|
-
import { Temporal } from "temporal
|
|
10
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
11
11
|
import { TenantErrors } from "./constants.js";
|
|
12
12
|
import { findForbiddenMembershipRole, reservedMembershipRoleError } from "./membership-roles.js";
|
|
13
13
|
import { tenantMembershipEntity, tenantMembershipsTable } from "./membership-table.js";
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// put in a URL, logged, or emailed. The run's own photos may show plates —
|
|
13
13
|
// the grant must stay exactly where the anonymous flow minted it.
|
|
14
14
|
|
|
15
|
-
import type { Temporal } from "temporal
|
|
15
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
16
16
|
import { signRowBoundGrant } from "../shared/index.js";
|
|
17
17
|
|
|
18
18
|
const HANDOVER_PURPOSE_PREFIX = "tenant-handover";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
-
import type { Temporal } from "temporal
|
|
4
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
5
|
import { exportJobsTable } from "../../schema/export-job.js";
|
|
6
6
|
|
|
7
7
|
export type ExportJobCleanupCandidate = {
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
|
|
4
|
-
// `Temporal` TYPE that gracePeriodEndCutoff's parameter type resolves
|
|
5
|
-
// against (same #1438 dual-package-hazard pattern as event-store.ts).
|
|
6
|
-
import { Temporal as TemporalPolyfill } from "temporal-polyfill";
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
7
4
|
import { userTable } from "../../../user/index.js";
|
|
8
5
|
|
|
9
6
|
export async function selectUsersDueForForgetCleanup(
|
|
10
7
|
db: DbConnection,
|
|
11
8
|
status: string,
|
|
12
|
-
gracePeriodEndCutoff:
|
|
9
|
+
gracePeriodEndCutoff: Temporal.Instant | string,
|
|
13
10
|
): Promise<readonly { id: string }[]> {
|
|
14
|
-
// @cast-boundary temporal-polyfill-vs-ambient: same TC39 Temporal.Instant
|
|
15
|
-
// at runtime, distinct nominal types across the two .d.ts sources (see
|
|
16
|
-
// event-store.ts). Only ever consumed via the `lte` comparison below.
|
|
17
11
|
const cutoff =
|
|
18
12
|
typeof gracePeriodEndCutoff === "string"
|
|
19
|
-
?
|
|
13
|
+
? Temporal.Instant.from(gracePeriodEndCutoff)
|
|
20
14
|
: gracePeriodEndCutoff;
|
|
21
15
|
return selectMany<{ id: string }>(db, userTable, {
|
|
22
16
|
status,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// The purpose string and the anchoring are unchanged from the hand-rolled
|
|
8
8
|
// version this replaced, so tokens stay byte-compatible.
|
|
9
9
|
|
|
10
|
-
import type { Temporal } from "temporal
|
|
10
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
11
11
|
import {
|
|
12
12
|
type RowBoundGrantResult,
|
|
13
13
|
redeemRowBoundGrant,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { mailT, registerMailTranslations } from "@cosmicdrift/kumiko-framework/i18n";
|
|
7
7
|
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
8
|
-
import { Temporal } from "temporal
|
|
8
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
9
9
|
|
|
10
10
|
export type GdprMailLocale = string;
|
|
11
11
|
|
|
@@ -44,6 +44,7 @@ registerMailTranslations("en", GDPR_MAIL_EN);
|
|
|
44
44
|
export type RenderedEmail = {
|
|
45
45
|
readonly subject: string;
|
|
46
46
|
readonly html: string;
|
|
47
|
+
readonly text: string;
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
export type RenderExportReadyEmailArgs = {
|
|
@@ -89,7 +90,13 @@ export function renderExportReadyEmail(args: RenderExportReadyEmailArgs): Render
|
|
|
89
90
|
<p style="margin: 0 0 24px;">${renderButton({ url: args.downloadUrl, label: t(locale, "gdpr.mail.exportReady.button") })}</p>
|
|
90
91
|
<p style="margin: 0 0 8px; font-size: 13px; color: #555;">${escapeHtml(t(locale, "gdpr.mail.exportReady.expiry", { when: formatTimestamp(args.expiresAt) }))}</p>
|
|
91
92
|
${renderFallbackUrl({ url: args.downloadUrl, label: t(locale, "gdpr.mail.fallbackUrl") })}`;
|
|
92
|
-
|
|
93
|
+
const text = plainTextBody([
|
|
94
|
+
t(locale, "gdpr.mail.greeting"),
|
|
95
|
+
t(locale, "gdpr.mail.exportReady.intro", { app }),
|
|
96
|
+
`${t(locale, "gdpr.mail.exportReady.button")}: ${args.downloadUrl}`,
|
|
97
|
+
t(locale, "gdpr.mail.exportReady.expiry", { when: formatTimestamp(args.expiresAt) }),
|
|
98
|
+
]);
|
|
99
|
+
return { subject, html: renderShell({ title: subject, bodyHtml: wrapCell(body), locale }), text };
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
export function renderExportFailedEmail(args: RenderExportFailedEmailArgs): RenderedEmail {
|
|
@@ -99,7 +106,11 @@ export function renderExportFailedEmail(args: RenderExportFailedEmailArgs): Rend
|
|
|
99
106
|
const body = `
|
|
100
107
|
<p style="margin: 0 0 16px; font-size: 16px;">${escapeHtml(t(locale, "gdpr.mail.greeting"))}</p>
|
|
101
108
|
<p style="margin: 0; font-size: 14px; line-height: 1.5;">${escapeHtml(t(locale, "gdpr.mail.exportFailed.intro", { app }))}</p>`;
|
|
102
|
-
|
|
109
|
+
const text = plainTextBody([
|
|
110
|
+
t(locale, "gdpr.mail.greeting"),
|
|
111
|
+
t(locale, "gdpr.mail.exportFailed.intro", { app }),
|
|
112
|
+
]);
|
|
113
|
+
return { subject, html: renderShell({ title: subject, bodyHtml: wrapCell(body), locale }), text };
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
export function renderDeletionRequestedEmail(
|
|
@@ -113,7 +124,12 @@ export function renderDeletionRequestedEmail(
|
|
|
113
124
|
<p style="margin: 0 0 16px; font-size: 16px;">${escapeHtml(t(locale, "gdpr.mail.greeting"))}</p>
|
|
114
125
|
<p style="margin: 0 0 16px; font-size: 14px; line-height: 1.5;">${escapeHtml(t(locale, "gdpr.mail.deletionRequested.intro", { app, when }))}</p>
|
|
115
126
|
<p style="margin: 0; font-size: 13px; color: #555;">${escapeHtml(t(locale, "gdpr.mail.deletionRequested.cancel"))}</p>`;
|
|
116
|
-
|
|
127
|
+
const text = plainTextBody([
|
|
128
|
+
t(locale, "gdpr.mail.greeting"),
|
|
129
|
+
t(locale, "gdpr.mail.deletionRequested.intro", { app, when }),
|
|
130
|
+
t(locale, "gdpr.mail.deletionRequested.cancel"),
|
|
131
|
+
]);
|
|
132
|
+
return { subject, html: renderShell({ title: subject, bodyHtml: wrapCell(body), locale }), text };
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
export function renderDeletionExecutedEmail(args: RenderDeletionExecutedEmailArgs): RenderedEmail {
|
|
@@ -124,7 +140,15 @@ export function renderDeletionExecutedEmail(args: RenderDeletionExecutedEmailArg
|
|
|
124
140
|
const body = `
|
|
125
141
|
<p style="margin: 0 0 16px; font-size: 16px;">${escapeHtml(t(locale, "gdpr.mail.greeting"))}</p>
|
|
126
142
|
<p style="margin: 0; font-size: 14px; line-height: 1.5;">${escapeHtml(t(locale, "gdpr.mail.deletionExecuted.intro", { app, when }))}</p>`;
|
|
127
|
-
|
|
143
|
+
const text = plainTextBody([
|
|
144
|
+
t(locale, "gdpr.mail.greeting"),
|
|
145
|
+
t(locale, "gdpr.mail.deletionExecuted.intro", { app, when }),
|
|
146
|
+
]);
|
|
147
|
+
return { subject, html: renderShell({ title: subject, bodyHtml: wrapCell(body), locale }), text };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function plainTextBody(paragraphs: readonly string[]): string {
|
|
151
|
+
return paragraphs.join("\n\n");
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
function wrapCell(bodyHtml: string): string {
|
package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import { Temporal } from "temporal
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { downloadAttemptsTable } from "../schema/download-attempt.js";
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
4
|
-
import { Temporal } from "temporal
|
|
4
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
|
|
7
7
|
// DSGVO Art. 15 Selbstauskunft — User reads HIS OWN audit-log.
|
|
@@ -57,13 +57,13 @@ export function makeDefaultExportReadyEmail(
|
|
|
57
57
|
): SendExportReadyEmailFn {
|
|
58
58
|
return async (args) => {
|
|
59
59
|
const transport = await resolveTransport(args.tenantId);
|
|
60
|
-
const { subject, html } = renderExportReadyEmail({
|
|
60
|
+
const { subject, html, text } = renderExportReadyEmail({
|
|
61
61
|
downloadUrl: args.downloadUrl,
|
|
62
62
|
expiresAt: args.expiresAt,
|
|
63
63
|
locale: localeFor(args.userLocale, defaults),
|
|
64
64
|
appName: defaults.appName,
|
|
65
65
|
});
|
|
66
|
-
await transport.send({ to: args.userEmail, subject, html });
|
|
66
|
+
await transport.send({ to: args.userEmail, subject, html, text });
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -73,11 +73,11 @@ export function makeDefaultExportFailedEmail(
|
|
|
73
73
|
): SendExportFailedEmailFn {
|
|
74
74
|
return async (args) => {
|
|
75
75
|
const transport = await resolveTransport(args.tenantId);
|
|
76
|
-
const { subject, html } = renderExportFailedEmail({
|
|
76
|
+
const { subject, html, text } = renderExportFailedEmail({
|
|
77
77
|
locale: localeFor(args.userLocale, defaults),
|
|
78
78
|
appName: defaults.appName,
|
|
79
79
|
});
|
|
80
|
-
await transport.send({ to: args.userEmail, subject, html });
|
|
80
|
+
await transport.send({ to: args.userEmail, subject, html, text });
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -87,12 +87,12 @@ export function makeDefaultDeletionRequestedEmail(
|
|
|
87
87
|
): SendDeletionRequestedEmailFn {
|
|
88
88
|
return async (args) => {
|
|
89
89
|
const transport = await resolveTransport(args.tenantId);
|
|
90
|
-
const { subject, html } = renderDeletionRequestedEmail({
|
|
90
|
+
const { subject, html, text } = renderDeletionRequestedEmail({
|
|
91
91
|
gracePeriodEnd: args.gracePeriodEnd,
|
|
92
92
|
locale: localeFor(args.userLocale, defaults),
|
|
93
93
|
appName: defaults.appName,
|
|
94
94
|
});
|
|
95
|
-
await transport.send({ to: args.userEmail, subject, html });
|
|
95
|
+
await transport.send({ to: args.userEmail, subject, html, text });
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -110,11 +110,11 @@ export function makeDefaultDeletionExecutedEmail(
|
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
const transport = await resolveTransport(tenantId);
|
|
113
|
-
const { subject, html } = renderDeletionExecutedEmail({
|
|
113
|
+
const { subject, html, text } = renderDeletionExecutedEmail({
|
|
114
114
|
executedAt: args.executedAt,
|
|
115
115
|
locale: localeFor(args.userLocale, defaults),
|
|
116
116
|
appName: defaults.appName,
|
|
117
117
|
});
|
|
118
|
-
await transport.send({ to: args.userEmail, subject, html });
|
|
118
|
+
await transport.send({ to: args.userEmail, subject, html, text });
|
|
119
119
|
};
|
|
120
120
|
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type WriteFailure,
|
|
14
14
|
writeFailure,
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/errors";
|
|
16
|
-
import { Temporal } from "temporal
|
|
16
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
17
17
|
import { AuthErrors, AuthHandlers } from "../../auth-email-password/index.js";
|
|
18
18
|
import { TenantHandlers, tenantTable } from "../../tenant/index.js";
|
|
19
19
|
import { DEFAULT_OWN_TENANT_INVITE_ROLE, WAITLIST_STATUS, WaitlistErrors } from "../constants.js";
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type WriteResult,
|
|
7
7
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
|
-
import { Temporal } from "temporal
|
|
9
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
10
10
|
import { WAITLIST_NOTIFICATION_TYPES, WAITLIST_STATUS } from "../constants.js";
|
|
11
11
|
import { normalizeEmail, platformActor, waitlistDb, waitlistExecutor } from "../lib.js";
|
|
12
12
|
import {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@cosmicdrift/kumiko-framework": "workspace:*",
|
|
16
16
|
"@cosmicdrift/kumiko-headless": "workspace:*",
|
|
17
17
|
"@cosmicdrift/kumiko-renderer-web": "workspace:*",
|
|
18
|
+
"@cosmicdrift/kumiko-types": "workspace:*",
|
|
18
19
|
"react": "^19.2.6",
|
|
19
20
|
"react-dom": "^19.2.6",
|
|
20
21
|
"@cosmicdrift/kumiko-locale-de": "workspace:*"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { SeedFn } from "@cosmicdrift/kumiko-dev-server";
|
|
12
12
|
import { TestUsers } from "@cosmicdrift/kumiko-framework/stack";
|
|
13
|
-
import { Temporal } from "temporal
|
|
13
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
14
14
|
import { countAssetsForTenant, countTicketsForTenant } from "../db/queries/seed-counts";
|
|
15
15
|
import { ASSET_STATUSES } from "../features/assets/schema";
|
|
16
16
|
import { TICKET_SEVERITIES, TICKET_STATUSES } from "../features/helpdesk/schema";
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"@cosmicdrift/kumiko-headless": "workspace:*",
|
|
23
23
|
"@cosmicdrift/kumiko-renderer-web": "workspace:*",
|
|
24
24
|
"@cosmicdrift/kumiko-server-runtime": "workspace:*",
|
|
25
|
+
"@cosmicdrift/kumiko-types": "workspace:*",
|
|
25
26
|
"react": "^19.2.6",
|
|
26
27
|
"react-dom": "^19.2.6",
|
|
27
28
|
"zod": "^4.4.3",
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
35
35
|
import { TestUsers, unsafeCreateEntityTable } from "@cosmicdrift/kumiko-framework/stack";
|
|
36
36
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
37
|
-
import { Temporal } from "temporal
|
|
37
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
38
38
|
import { ADMIN_EMAIL, BETA_TENANT_ID, DEMO_NOTE_ID, DEV_TENANT_ID } from "./auth-constants";
|
|
39
39
|
import { noteEntity } from "./notes-feature";
|
|
40
40
|
|