@cosmicdrift/kumiko-bundled-features 0.350.0 → 0.352.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/dist/audit/handlers/list.query.js +1 -1
- package/dist/auth-email-password/email-templates.js +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.js +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.js +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
- package/dist/auth-email-password/reset-token.d.ts +1 -1
- package/dist/auth-email-password/unlock-token.d.ts +1 -1
- package/dist/auth-email-password/verification-token.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.js +1 -1
- package/dist/auth-mfa/hmac-token-codec.d.ts +1 -1
- package/dist/auth-mfa/hmac-token-codec.js +1 -1
- package/dist/auth-mfa/mfa-challenge-token.d.ts +1 -1
- package/dist/auth-mfa/mfa-challenge-token.js +1 -1
- package/dist/auth-mfa/mfa-preauth-setup-token.d.ts +1 -1
- package/dist/auth-mfa/mfa-preauth-setup-token.js +1 -1
- package/dist/auth-mfa/mfa-setup-token.d.ts +1 -1
- package/dist/auth-mfa/mfa-setup-token.js +1 -1
- package/dist/billing-foundation/constants.d.ts +2 -2
- package/dist/billing-foundation/constants.js +3 -12
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +1 -1
- package/dist/billing-foundation/consumer-protection/termination-mail.js +1 -1
- package/dist/billing-foundation/feature.js +2 -9
- package/dist/billing-foundation/handlers/sync-subscription.write.js +3 -6
- package/dist/cap-counter/book-cap-usage.d.ts +5 -1
- package/dist/cap-counter/book-cap-usage.js +66 -45
- package/dist/cap-counter/cap-reservation.d.ts +12 -0
- package/dist/cap-counter/cap-reservation.js +135 -0
- package/dist/cap-counter/constants.d.ts +3 -0
- package/dist/cap-counter/constants.js +6 -0
- package/dist/cap-counter/enforce-cap.d.ts +1 -1
- package/dist/cap-counter/enforce-cap.js +1 -8
- package/dist/cap-counter/feature.js +7 -2
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +5 -0
- package/dist/cap-counter/handlers/increment-rolling.write.js +4 -0
- package/dist/cap-counter/index.d.ts +4 -2
- package/dist/cap-counter/index.js +3 -1
- package/dist/cap-counter/payload-field.d.ts +1 -0
- package/dist/cap-counter/payload-field.js +11 -0
- package/dist/cap-counter/reservation-chain.d.ts +2 -0
- package/dist/cap-counter/reservation-chain.js +44 -0
- package/dist/cap-counter/stock-cap-guard.d.ts +3 -0
- package/dist/cap-counter/tables.d.ts +3 -0
- package/dist/cap-counter/tables.js +36 -0
- package/dist/cap-counter/value-cap-guard.d.ts +18 -0
- package/dist/cap-counter/value-cap-guard.js +33 -0
- package/dist/cap-counter/with-cap-enforcement.d.ts +5 -7
- package/dist/cap-counter/with-cap-enforcement.js +35 -51
- package/dist/channel-discord/feature.js +1 -0
- package/dist/channel-slack/feature.js +1 -0
- package/dist/channel-teams/feature.js +1 -0
- package/dist/compliance-profiles/schema/profile-selection.d.ts +2 -0
- package/dist/delivery/attempt-log-retention.d.ts +9 -0
- package/dist/delivery/attempt-log-retention.js +23 -0
- package/dist/delivery/attempt-log.d.ts +1 -1
- package/dist/delivery/attempt-log.js +21 -2
- package/dist/delivery/constants.d.ts +1 -1
- package/dist/delivery/constants.js +1 -1
- package/dist/delivery/events.d.ts +1 -0
- package/dist/delivery/events.js +2 -0
- package/dist/delivery/feature.d.ts +1 -0
- package/dist/delivery/feature.js +39 -3
- package/dist/delivery/handlers/log.query.d.ts +1 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/index.d.ts +2 -1
- package/dist/delivery/index.js +2 -1
- package/dist/delivery/mask-recipient-address.d.ts +1 -0
- package/dist/delivery/mask-recipient-address.js +41 -0
- package/dist/delivery/public-names.d.ts +2 -0
- package/dist/delivery/public-names.js +3 -0
- package/dist/delivery/types.d.ts +1 -0
- package/dist/delivery/types.js +6 -2
- package/dist/feature-toggles/handlers/set.write.js +1 -1
- package/dist/form-draft/db/queries/cleanup.d.ts +1 -1
- package/dist/form-draft/db/queries/cleanup.js +1 -1
- package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
- package/dist/form-draft/lookup.d.ts +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +1 -1
- package/dist/inbound-mail-foundation/oauth-state.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-state.js +1 -1
- package/dist/inbound-mail-foundation/watch-supervisor.js +1 -1
- package/dist/inbound-provider-imap/feature.js +1 -1
- package/dist/inbound-provider-imap/imap-client.js +1 -1
- package/dist/jobs/job-instant.d.ts +1 -1
- package/dist/jobs/job-instant.js +1 -1
- package/dist/ledger/entity.d.ts +3 -0
- package/dist/metrics/compute.d.ts +1 -1
- package/dist/metrics/feature.js +1 -1
- package/dist/metrics/types.d.ts +1 -1
- package/dist/metrics/window.d.ts +1 -1
- package/dist/metrics/window.js +1 -1
- package/dist/personal-access-tokens/expiry.js +1 -1
- package/dist/personal-access-tokens/handlers/create.write.js +1 -1
- package/dist/personal-access-tokens/handlers/revoke.write.js +1 -1
- package/dist/personal-access-tokens/revoke-for-user.js +1 -1
- package/dist/renderer-foundation/types.d.ts +1 -0
- package/dist/renderer-simple/feature.js +4 -6
- package/dist/renderer-simple/index.d.ts +1 -0
- package/dist/renderer-simple/mail-blocks.d.ts +21 -0
- package/dist/renderer-simple/mail-blocks.js +107 -0
- package/dist/renderer-simple/simple-renderer.js +21 -3
- package/dist/sessions/handlers/revoke-all-for-user.write.js +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.js +1 -1
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/session-callbacks.js +1 -1
- package/dist/shared/grace-period.d.ts +2 -1
- package/dist/shared/grace-period.js +3 -7
- package/dist/shared/row-bound-grant.d.ts +1 -1
- package/dist/shared/signed-token.d.ts +1 -1
- package/dist/shared/signed-token.js +1 -1
- package/dist/subscription-mollie/verify-webhook.js +1 -1
- package/dist/subscription-stripe/verify-webhook.js +1 -1
- package/dist/template-resolver/api.d.ts +1 -1
- package/dist/template-resolver/handlers/by-slug.query.d.ts +1 -1
- package/dist/template-resolver/handlers/by-tenant.query.d.ts +2 -2
- package/dist/template-resolver/handlers/collection-item.query.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-list.query.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-shared.d.ts +2 -2
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -1
- package/dist/template-resolver/handlers/list.query.d.ts +1 -1
- package/dist/template-resolver/table.d.ts +9 -1
- package/dist/template-resolver/user-content-table.d.ts +4 -0
- package/dist/tenant/handlers/team-list.query.d.ts +1 -1
- package/dist/tenant/handlers/team-list.query.js +1 -1
- package/dist/tenant/invitation-table.d.ts +2 -0
- package/dist/tenant/invited-membership.d.ts +1 -1
- package/dist/tenant/invited-membership.js +1 -1
- package/dist/tenant/schema/tenant.d.ts +2 -0
- package/dist/tenant-handover/grant.d.ts +1 -1
- package/dist/tier-engine/index.d.ts +1 -0
- package/dist/tier-engine/index.js +1 -0
- package/dist/tier-engine/tier-option-gate.d.ts +32 -0
- package/dist/tier-engine/tier-option-gate.js +62 -0
- package/dist/user/schema/user.d.ts +2 -0
- package/dist/user-data-rights/db/queries/export-jobs.d.ts +1 -1
- package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
- package/dist/user-data-rights/db/queries/forget-cleanup.js +2 -8
- package/dist/user-data-rights/deletion-token.d.ts +1 -1
- package/dist/user-data-rights/email-templates.js +1 -1
- package/dist/user-data-rights/handlers/list-download-attempts.query.js +1 -1
- package/dist/user-data-rights/handlers/my-audit-log.query.js +1 -1
- package/dist/user-data-rights/schema/export-job.d.ts +2 -0
- package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +25 -6
- package/dist/waitlist/entity.d.ts +2 -0
- package/dist/waitlist/handlers/invite.write.js +1 -1
- package/dist/waitlist/handlers/submit.write.js +1 -1
- package/package.json +10 -10
- package/src/cap-counter/changes.json +28 -0
- package/src/delivery/changes.json +12 -0
- package/src/derivatives-sharp/changes.json +6 -0
- package/src/legal-pages/changes.json +6 -0
- package/src/renderer-simple/changes.json +6 -0
- package/src/tier-engine/changes.json +6 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
export type DeliveryFeatureOptions = {
|
|
3
3
|
readonly access?: readonly string[];
|
|
4
|
+
readonly attemptLogRetentionDays?: number | false;
|
|
4
5
|
};
|
|
5
6
|
export declare function createDeliveryFeature(options?: DeliveryFeatureOptions): FeatureDefinition;
|
package/dist/delivery/feature.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { upsertByPk } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
1
|
+
import { updateMany, upsertByPk } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { access, defineFeature, i18nKey, } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { ConsumerLagError } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
|
+
import { resolveAttemptLogRetentionDays, runAttemptLogRetention } from "./attempt-log-retention.js";
|
|
5
|
+
import { DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT, DELIVERY_ATTEMPT_EVENT, DELIVERY_CHANNEL_CELL_COMPONENT, DELIVERY_CHANNEL_EXTENSION, DELIVERY_ERROR_CELL_COMPONENT, DELIVERY_LOG_SCREEN_ID, DELIVERY_STATUS_CELL_COMPONENT, DELIVERY_TIME_CELL_COMPONENT, DELIVERY_TYPE_CELL_COMPONENT, DeliveryJobNames, DeliveryQueries, } from "./constants.js";
|
|
6
|
+
import { deliveryAttemptAddressErasedSchema, deliveryAttemptSchema } from "./events.js";
|
|
5
7
|
import { logQuery } from "./handlers/log.query.js";
|
|
6
8
|
import { preferencesQuery } from "./handlers/preferences.query.js";
|
|
7
9
|
import { resubscribeAddressWrite } from "./handlers/resubscribe-address.write.js";
|
|
@@ -12,8 +14,10 @@ import { unsubscribeUserWrite } from "./handlers/unsubscribe-user.write.js";
|
|
|
12
14
|
import { DELIVERY_I18N } from "./i18n.js";
|
|
13
15
|
import { deliveryRenderJob, deliverySendJob } from "./jobs.js";
|
|
14
16
|
import { deliveryAttemptsTable, deliveryAttemptsTableMeta, notificationAddressOptOutEntity, notificationAddressOptOutsTable, notificationPreferenceEntity, notificationPreferencesTable, } from "./tables.js";
|
|
17
|
+
const ATTEMPT_LOG_RETENTION_ESCAPE_HATCH_REASON = "prunes delivery attempt events and log rows of every tenant";
|
|
15
18
|
export function createDeliveryFeature(options) {
|
|
16
19
|
const resolvedAccess = options?.access ?? access.admin;
|
|
20
|
+
const attemptLogRetentionDays = resolveAttemptLogRetentionDays(options?.attemptLogRetentionDays);
|
|
17
21
|
return defineFeature("delivery", (r) => {
|
|
18
22
|
r.describe("The notification dispatch core: call `ctx.notify(notificationType, { to, route, data, priority, idempotencyKey })` from any handler to fan out a notification across all registered channels (email, in-app, push). It stores per-user channel preferences in the `notification-preference` entity, opt-outs for no-account recipient addresses in `notification-address-opt-out` (keyed by a blind-index hash, never the plaintext address), logs every attempt to `store_delivery_attempts`, and enforces idempotency and rate-limiting \u2014 add `channel-email`, `channel-in-app`, or `channel-push` on top to actually send anything. Unsubscribe/resubscribe links are served by `createUnsubscribeRoutes({ secret })` mounted via the app's `extraRoutes` at `/api/delivery/unsubscribe` and `/api/delivery/resubscribe`: `GET /unsubscribe?token=` renders a confirmation page (no write), `POST /unsubscribe` performs the opt-out (RFC 8058 one-click, token from the form body or query), `POST /resubscribe` undoes it (JSON `{token}` or form body only, never the query — reachable by a mail-client link prefetcher) — sign links with `signUnsubscribeToken` / `signAddressUnsubscribeToken` using the same secret. `channel-email` sets `List-Unsubscribe` / `List-Unsubscribe-Post` automatically when a message's `data.unsubscribeUrl` points at the unsubscribe route.");
|
|
19
23
|
r.uiHints({
|
|
@@ -54,6 +58,11 @@ export function createDeliveryFeature(options) {
|
|
|
54
58
|
recipientAddress: { personal: { of: "recipientId", whenAbsent: "tenant" } },
|
|
55
59
|
},
|
|
56
60
|
});
|
|
61
|
+
// Written when the recipient is forgotten: clears the address on the projection row and, being
|
|
62
|
+
// an event, survives a projection rebuild (a plain UPDATE would be undone by the replay).
|
|
63
|
+
r.defineEvent("attempt-address-erased", deliveryAttemptAddressErasedSchema, {
|
|
64
|
+
piiFields: "none",
|
|
65
|
+
});
|
|
57
66
|
// Inline projection that materialises every delivery attempt into
|
|
58
67
|
// deliveryAttemptsTable. Runs in the SAME transaction as the low-level
|
|
59
68
|
// append(), so callers see their write immediately — no dispatcher
|
|
@@ -89,6 +98,9 @@ export function createDeliveryFeature(options) {
|
|
|
89
98
|
confirmed: p.confirmed ?? null,
|
|
90
99
|
});
|
|
91
100
|
},
|
|
101
|
+
[DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT]: async (event, tx) => {
|
|
102
|
+
await updateMany(tx, deliveryAttemptsTable, { recipientAddress: null }, { id: event.aggregateId });
|
|
103
|
+
},
|
|
92
104
|
},
|
|
93
105
|
});
|
|
94
106
|
// Extension point: delivery-channels (email/in-app/push). Renderer-
|
|
@@ -106,6 +118,30 @@ export function createDeliveryFeature(options) {
|
|
|
106
118
|
// from the delivery-service, hence manual trigger.
|
|
107
119
|
r.job(DeliveryJobNames.render, { trigger: { manual: true }, retries: 3, backoff: "exponential" }, deliveryRenderJob);
|
|
108
120
|
r.job(DeliveryJobNames.send, { trigger: { manual: true }, retries: 3, backoff: "exponential" }, deliverySendJob);
|
|
121
|
+
if (attemptLogRetentionDays !== undefined) {
|
|
122
|
+
r.job({
|
|
123
|
+
name: DeliveryJobNames.attemptLogRetention,
|
|
124
|
+
trigger: { cron: "15 3 * * *" },
|
|
125
|
+
concurrency: "skip",
|
|
126
|
+
escapeHatch: { reason: ATTEMPT_LOG_RETENTION_ESCAPE_HATCH_REASON },
|
|
127
|
+
handler: async (_payload, ctx) => {
|
|
128
|
+
const db = ctx.db.unsafeRaw(); // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
129
|
+
try {
|
|
130
|
+
const pruned = await runAttemptLogRetention(db, {
|
|
131
|
+
olderThanDays: attemptLogRetentionDays,
|
|
132
|
+
});
|
|
133
|
+
ctx.log.info("delivery attempt log pruned", pruned);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (!(error instanceof ConsumerLagError))
|
|
137
|
+
throw error;
|
|
138
|
+
ctx.log.warn("delivery attempt log retention postponed: an event consumer lags", {
|
|
139
|
+
consumer: error.laggingConsumer,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
109
145
|
const handlers = {
|
|
110
146
|
setPreference: r.writeHandler(setPreferenceWrite),
|
|
111
147
|
unsubscribeAddress: r.writeHandler(unsubscribeAddressWrite),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type DeliveryErrorCode, type NotifyPriority } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
import { Temporal } from "temporal-polyfill";
|
|
3
2
|
import * as z from "zod";
|
|
4
3
|
import type { DeliveryStatusValue } from "../constants.js";
|
|
5
4
|
export declare const logQuery: import("@cosmicdrift/kumiko-framework/engine").PagedQueryHandlerDefinition<"log", z.ZodObject<{
|
|
@@ -22,5 +21,5 @@ export declare const logQuery: import("@cosmicdrift/kumiko-framework/engine").Pa
|
|
|
22
21
|
error: DeliveryErrorCode | null;
|
|
23
22
|
priority: NotifyPriority;
|
|
24
23
|
confirmed: boolean | null;
|
|
25
|
-
createdAt: Temporal.Instant;
|
|
24
|
+
createdAt: globalThis.Temporal.Instant;
|
|
26
25
|
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
@@ -2,7 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { decodeKeysetCursor, encodeKeysetCursor, } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { access, definePagedQueryHandler, isDeliveryErrorCode, isUuid, MAX_LIST_LIMIT, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
-
import { Temporal } from "temporal
|
|
5
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { decryptStoredPii } from "../../shared/index.js";
|
|
8
8
|
import { resolveUserDisplayNames, USER_FEATURE } from "../../user/index.js";
|
package/dist/delivery/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { hashUnsubscribeAddress } from "./address-opt-out.js";
|
|
2
|
+
export { type AttemptLogRetentionOptions, DEFAULT_ATTEMPT_LOG_RETENTION_DAYS, runAttemptLogRetention, } from "./attempt-log-retention.js";
|
|
2
3
|
export { type ChatWebhookChannelOptions, chatMessageText, createChatWebhookChannel, resolveChatWebhookTarget, toChannelResult, } from "./chat-webhook-channel.js";
|
|
3
4
|
export { type ChatSendFailureCode, type ChatSendResult, type ChatWebhookRequest, type ChatWebhookResponse, type ChatWebhookTarget, chatConnectionNameSchema, chatWebhookUrlSchema, checkChatWebhookTarget, DEFAULT_CHAT_TIMEOUT_MS, postChatWebhook, truncateChars, } from "./chat-webhook-sender.js";
|
|
4
5
|
export type { DeliveryStatusValue } from "./constants.js";
|
|
5
|
-
export { DELIVERY_CHANNEL_EXTENSION, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobs, DeliveryQueries, DeliveryStatus, } from "./constants.js";
|
|
6
|
+
export { DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT, DELIVERY_CHANNEL_EXTENSION, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobs, DeliveryQueries, DeliveryStatus, } from "./constants.js";
|
|
6
7
|
export { collectChannels, createDeliveryService, type DeliveryServiceOptions, type KillSwitchResolver, type RateLimitConfig, } from "./delivery-service.js";
|
|
7
8
|
export { createDeliveryFeature, type DeliveryFeatureOptions } from "./feature.js";
|
|
8
9
|
export { createDeliveryNotifyFactory } from "./notify-factory.js";
|
package/dist/delivery/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { hashUnsubscribeAddress } from "./address-opt-out.js";
|
|
2
|
+
export { DEFAULT_ATTEMPT_LOG_RETENTION_DAYS, runAttemptLogRetention, } from "./attempt-log-retention.js";
|
|
2
3
|
export { chatMessageText, createChatWebhookChannel, resolveChatWebhookTarget, toChannelResult, } from "./chat-webhook-channel.js";
|
|
3
4
|
export { chatConnectionNameSchema, chatWebhookUrlSchema, checkChatWebhookTarget, DEFAULT_CHAT_TIMEOUT_MS, postChatWebhook, truncateChars, } from "./chat-webhook-sender.js";
|
|
4
|
-
export { DELIVERY_CHANNEL_EXTENSION, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobs, DeliveryQueries, DeliveryStatus, } from "./constants.js";
|
|
5
|
+
export { DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT, DELIVERY_CHANNEL_EXTENSION, DELIVERY_FEATURE, DELIVERY_LOG_SCREEN_ID, DELIVERY_UNSUBSCRIBE_PATH, DeliveryErrors, DeliveryHandlers, DeliveryJobs, DeliveryQueries, DeliveryStatus, } from "./constants.js";
|
|
5
6
|
export { collectChannels, createDeliveryService, } from "./delivery-service.js";
|
|
6
7
|
export { createDeliveryFeature } from "./feature.js";
|
|
7
8
|
export { createDeliveryNotifyFactory } from "./notify-factory.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function maskRecipientAddress(address: string | null): string | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isPiiCiphertext } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
|
+
const MASK = "***";
|
|
3
|
+
const VISIBLE_TAIL_LENGTH = 4;
|
|
4
|
+
const MIN_LENGTH_FOR_VISIBLE_TAIL = 8;
|
|
5
|
+
function maskedUrl(address) {
|
|
6
|
+
if (!URL.canParse(address))
|
|
7
|
+
return undefined;
|
|
8
|
+
const url = new URL(address);
|
|
9
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
10
|
+
return undefined;
|
|
11
|
+
return `${url.origin}/${MASK}`;
|
|
12
|
+
}
|
|
13
|
+
function maskedEmail(address) {
|
|
14
|
+
const parts = address.split("@");
|
|
15
|
+
const [local, domain] = parts;
|
|
16
|
+
if (parts.length !== 2 || !local || domain === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
return `${local.charAt(0)}${MASK}@${domain}`;
|
|
19
|
+
}
|
|
20
|
+
// The attempt log is an audit trail, not a contact list: it keeps enough of the address to
|
|
21
|
+
// recognise where a message went, never the full value. URLs go first because webhook URLs can
|
|
22
|
+
// contain an "@" and their path carries the secret.
|
|
23
|
+
export function maskRecipientAddress(address) {
|
|
24
|
+
if (address === null)
|
|
25
|
+
return null;
|
|
26
|
+
return maskPlainAddress(address);
|
|
27
|
+
}
|
|
28
|
+
function maskPlainAddress(address) {
|
|
29
|
+
// isPiiCiphertext is a type predicate; its false branch would narrow `string` to `never`.
|
|
30
|
+
if (isPiiCiphertext(address))
|
|
31
|
+
return MASK;
|
|
32
|
+
return maskStructuredOrTail(address);
|
|
33
|
+
}
|
|
34
|
+
function maskStructuredOrTail(address) {
|
|
35
|
+
const structured = maskedUrl(address) ?? maskedEmail(address);
|
|
36
|
+
if (structured !== undefined)
|
|
37
|
+
return structured;
|
|
38
|
+
if (address.length < MIN_LENGTH_FOR_VISIBLE_TAIL)
|
|
39
|
+
return MASK;
|
|
40
|
+
return `${MASK}${address.slice(-VISIBLE_TAIL_LENGTH)}`;
|
|
41
|
+
}
|
|
@@ -34,4 +34,6 @@ export declare const DELIVERY_ATTEMPT_EVENT: "delivery:event:attempt";
|
|
|
34
34
|
export declare const DeliveryJobNames: {
|
|
35
35
|
readonly render: "render";
|
|
36
36
|
readonly send: "send";
|
|
37
|
+
readonly attemptLogRetention: "attempt-log-retention";
|
|
37
38
|
};
|
|
39
|
+
export declare const DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT: "delivery:event:attempt-address-erased";
|
|
@@ -48,4 +48,7 @@ export const DELIVERY_ATTEMPT_EVENT = "delivery:event:attempt";
|
|
|
48
48
|
export const DeliveryJobNames = {
|
|
49
49
|
render: "render",
|
|
50
50
|
send: "send",
|
|
51
|
+
attemptLogRetention: "attempt-log-retention",
|
|
51
52
|
};
|
|
53
|
+
// Clears recipientAddress on an attempt row after its recipient was forgotten.
|
|
54
|
+
export const DELIVERY_ATTEMPT_ADDRESS_ERASED_EVENT = "delivery:event:attempt-address-erased";
|
package/dist/delivery/types.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export type DeliveryChannel = {
|
|
|
34
34
|
readonly name: string;
|
|
35
35
|
readonly mode: DeliveryChannelMode;
|
|
36
36
|
resolve?(userId: string, ctx: ChannelContext): Promise<string | null>;
|
|
37
|
+
readonly addressKind?: "connection-name";
|
|
37
38
|
render?(message: ChannelMessage, ctx: ChannelContext): Promise<RenderedMessage>;
|
|
38
39
|
send(address: string, message: ChannelMessage, ctx: ChannelContext, rendered?: RenderedMessage): Promise<ChannelResult>;
|
|
39
40
|
};
|
package/dist/delivery/types.js
CHANGED
|
@@ -4,13 +4,17 @@ import { DELIVERY_CHANNEL_EXTENSION } from "./constants.js";
|
|
|
4
4
|
// queued — sent asynchronously via the delivery.send job; channels with a
|
|
5
5
|
// render() additionally run through delivery.render first.
|
|
6
6
|
export const DELIVERY_CHANNEL_MODES = ["inline", "queued"];
|
|
7
|
+
function hasOptionalHooks(o) {
|
|
8
|
+
return ((!("render" in o) || o.render === undefined || typeof o.render === "function") &&
|
|
9
|
+
(!("resolve" in o) || o.resolve === undefined || typeof o.resolve === "function") &&
|
|
10
|
+
(!("addressKind" in o) || o.addressKind === undefined || o.addressKind === "connection-name"));
|
|
11
|
+
}
|
|
7
12
|
export function isDeliveryChannelPlugin(o) {
|
|
8
13
|
return (typeof o === "object" &&
|
|
9
14
|
o !== null &&
|
|
10
15
|
"mode" in o &&
|
|
11
16
|
DELIVERY_CHANNEL_MODES.some((mode) => mode === o.mode) &&
|
|
12
|
-
(
|
|
13
|
-
(!("resolve" in o) || o.resolve === undefined || typeof o.resolve === "function") &&
|
|
17
|
+
hasOptionalHooks(o) &&
|
|
14
18
|
"send" in o &&
|
|
15
19
|
typeof o.send === "function");
|
|
16
20
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { insertOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, VersionConflictError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
-
import { Temporal } from "temporal
|
|
4
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
import { FEATURE_TOGGLE_AGGREGATE_TYPE, FEATURE_TOGGLE_CROSS_TENANT_REASON, FEATURE_TOGGLE_SET_EVENT_NAME, FeatureToggleErrors, } from "../constants.js";
|
|
7
7
|
import { globalFeatureStateTable } from "../global-feature-state-table.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import { Temporal } from "temporal
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import type { FormDraftBlob } from "../../schemas.js";
|
|
5
5
|
export type StaleDraftRow = {
|
|
6
6
|
readonly id: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import { Temporal } from "temporal
|
|
2
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
3
|
// COALESCE(modified_at, inserted_at) — a draft's "last touched" timestamp is
|
|
4
4
|
// modified_at once the upsert path (save.write.ts) has updated it at least
|
|
5
5
|
// once; a draft saved exactly once (create, never updated) has no
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import type { Temporal } from "temporal
|
|
3
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
export declare function filterOwnedStorageKeys(db: DbRunner, tenantId: TenantId, ownerId: string, candidateKeys: readonly string[], draftInsertedAt: Temporal.Instant, isCreateMode: boolean): Promise<readonly string[]>;
|
|
5
5
|
export type OwnedFileRef = {
|
|
6
6
|
readonly id: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import type { Temporal } from "temporal
|
|
3
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import type { FormDraftBlob } from "./schemas.js";
|
|
5
5
|
export type FormDraftRow = {
|
|
6
6
|
readonly id: string;
|
|
@@ -23,7 +23,7 @@ import { countWhere } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
23
23
|
import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
24
24
|
import { acquireNamespacedAdvisoryLock } from "@cosmicdrift/kumiko-framework/db";
|
|
25
25
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
26
|
-
import { Temporal } from "temporal
|
|
26
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
27
27
|
import * as z from "zod";
|
|
28
28
|
import { inboundMessageAggregateId, mailThreadAggregateId } from "../aggregate-id.js";
|
|
29
29
|
import { INBOUND_MESSAGE_PII_FIELDS, inboundMessageEntity, MAIL_THREAD_PII_FIELDS, mailThreadEntity, seenMessageTable, } from "../entities.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
3
|
-
import { Temporal } from "temporal
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import { type InboundMailContext, type InboundMailProviderPlugin, type MailAccountRecord, type OAuthTokenSet } from "./types.js";
|
|
5
5
|
/** Shared by the OAuth callback and the refresh path so both write the slot identically. */
|
|
6
6
|
export declare function inboundRefreshTokenSecretOptions(accountId: string): {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// so the read always reflects the latest committed write.
|
|
18
18
|
import { transaction } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
19
19
|
import { acquireNamespacedAdvisoryLock } from "@cosmicdrift/kumiko-framework/db";
|
|
20
|
-
import { Temporal } from "temporal
|
|
20
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
21
21
|
import { InboundMailAuthMethods, inboundCredentialSecretKey } from "./constants.js";
|
|
22
22
|
import { InboundAuthError, } from "./types.js";
|
|
23
23
|
// 'inoa' as ASCII — disjoint from the other fixed advisory-lock namespaces.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Muster: auth-email-password/signed-token.ts (HMAC-SHA256, purpose im
|
|
11
11
|
// HMAC-Input, timing-safe compare, Expiry im Klartext-Segment).
|
|
12
12
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
13
|
-
import { Temporal } from "temporal
|
|
13
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
14
14
|
const STATE_PURPOSE = "inbound-mail-oauth-connect";
|
|
15
15
|
function sign(input, secret) {
|
|
16
16
|
return createHmac("sha256", secret).update(input).digest("base64url");
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
// exceeded) tears down the local watcher instead of racing a new holder.
|
|
46
46
|
import { fetchOne, insertOne, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
47
47
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
48
|
-
import { Temporal } from "temporal
|
|
48
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
49
49
|
import { InboundMailAccountStatuses, InboundMailFoundationHandlers } from "./constants.js";
|
|
50
50
|
import { MAIL_ACCOUNT_PII_FIELDS, syncCursorTable } from "./entities.js";
|
|
51
51
|
import { createOAuthAccessTokenManager, usesFoundationManagedOAuth, } from "./oauth-access-token.js";
|
|
@@ -26,7 +26,7 @@ import { INBOUND_MAIL_PROVIDER_EXTENSION, InboundAuthError, inboundCredentialSec
|
|
|
26
26
|
import { requireSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
27
27
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
28
28
|
import { instantToLegacyDate } from "@cosmicdrift/kumiko-framework/time";
|
|
29
|
-
import { Temporal } from "temporal
|
|
29
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
30
30
|
import { parseImapCredentialDocument } from "./credential-document.js";
|
|
31
31
|
import { assertUidValidity, coerceDate, createImapClient, IMAP_MAILBOX, imapMailHostGuard, mapImapError, parseImapCursor, toRawInboundMessage, } from "./imap-client.js";
|
|
32
32
|
const FEATURE_NAME = "inbound-provider-imap";
|
|
@@ -5,9 +5,9 @@ import { BlockedHostError, readAllowedPrivateMailHostsFromEnv, resolveMailConnec
|
|
|
5
5
|
import { InboundAuthError, InboundCursorInvalidError, InboundTransientError, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
6
6
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
7
7
|
import { legacyDateToInstant } from "@cosmicdrift/kumiko-framework/time";
|
|
8
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
8
9
|
import { ImapFlow } from "imapflow";
|
|
9
10
|
import { simpleParser } from "mailparser";
|
|
10
|
-
import { Temporal } from "temporal-polyfill";
|
|
11
11
|
export const IMAP_MAILBOX = "INBOX";
|
|
12
12
|
const SNIPPET_MAX = 300;
|
|
13
13
|
const log = createFallbackLogger("inbound-provider-imap");
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Temporal } from "temporal
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
2
|
export declare function parseJobInstant(iso: string): Temporal.Instant;
|
package/dist/jobs/job-instant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Temporal } from "temporal
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
2
|
// kumiko-framework#1525: the five value-position Temporal.Instant.from
|
|
3
3
|
// call sites in feature.ts live inside defineApply callbacks with no
|
|
4
4
|
// exported seam, so this thin wrapper gives the ambient-global-independence
|
package/dist/ledger/entity.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export declare const accountEntity: import("@cosmicdrift/kumiko-types/fields").E
|
|
|
40
40
|
} | undefined;
|
|
41
41
|
readonly optionsQuery?: string;
|
|
42
42
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
43
|
+
readonly optionsAvailabilityQuery?: string;
|
|
43
44
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
44
45
|
required: true;
|
|
45
46
|
};
|
|
@@ -160,6 +161,7 @@ export declare const transactionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
160
161
|
} | undefined;
|
|
161
162
|
readonly optionsQuery?: string;
|
|
162
163
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
164
|
+
readonly optionsAvailabilityQuery?: string;
|
|
163
165
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
164
166
|
required: true;
|
|
165
167
|
};
|
|
@@ -288,6 +290,7 @@ export declare const scheduleEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
288
290
|
} | undefined;
|
|
289
291
|
readonly optionsQuery?: string;
|
|
290
292
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
293
|
+
readonly optionsAvailabilityQuery?: string;
|
|
291
294
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
292
295
|
required: true;
|
|
293
296
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
3
|
import type { TenantDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
3
|
-
import type { Temporal } from "temporal-polyfill";
|
|
4
4
|
import { type MetricRange } from "./constants.js";
|
|
5
5
|
import type { MetricDefinition, MetricResult } from "./types.js";
|
|
6
6
|
export type MetricRunParams = {
|
package/dist/metrics/feature.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access, defineFeature, defineQueryHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { InternalError, ValidationError } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
-
import { Temporal } from "temporal
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { runMetric } from "./compute.js";
|
|
6
6
|
import { METRIC_RANGES, METRICS_FEATURE, METRICS_SYSTEM_FEATURE, } from "./constants.js";
|
package/dist/metrics/types.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import type { EntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
|
|
|
2
2
|
import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import type { AggregateMeasure, AggregateTimeBucket } from "@cosmicdrift/kumiko-types/aggregate-types";
|
|
4
4
|
import type { SchemaTable } from "@cosmicdrift/kumiko-types/schema-table-types";
|
|
5
|
+
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
6
|
import type { WhereObject } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
6
|
-
import type { Temporal } from "temporal-polyfill";
|
|
7
7
|
import type { MetricRange, MetricScope } from "./constants.js";
|
|
8
8
|
/** Maps a payload value (e.g. a folder id) to the values `column` may have (e.g. the folder and its subfolders' loan ids). Runs with the tenant handler context, so reads stay tenant-scoped; the result only narrows. An empty result yields no rows. */
|
|
9
9
|
export type MetricFilterResolver = {
|
package/dist/metrics/window.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AggregateTimeBucket } from "@cosmicdrift/kumiko-types/aggregate-types";
|
|
2
|
-
import { Temporal } from "temporal
|
|
2
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
3
|
import type { MetricRange } from "./constants.js";
|
|
4
4
|
import type { MetricDefinition } from "./types.js";
|
|
5
5
|
export type MetricWindow = {
|
package/dist/metrics/window.js
CHANGED
|
@@ -2,7 +2,7 @@ import { insertOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { createSystemUser, 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 { encryptForDirectWrite, verifyPassword } from "../../shared/index.js";
|
|
8
8
|
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";
|
|
@@ -2,7 +2,7 @@ import { transaction, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
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 { PAT_REVOKED_AGGREGATE_TYPE, PAT_REVOKED_EVENT_QN, patRevokedSchema, } from "./pat-revoked-event.js";
|
|
7
7
|
import { apiTokenTable } from "./schema/api-token.js";
|
|
8
8
|
// A pool has begin(); a tx/savepoint handle does not and is already atomic.
|
|
@@ -16,12 +16,10 @@ export async function adaptToFoundation(req, ctx, renderer = simpleRenderer) {
|
|
|
16
16
|
throw new RendererError(`renderer-simple supports only kind="notification", got "${req.kind}"`, "invalid_payload");
|
|
17
17
|
}
|
|
18
18
|
const variables = await resolveNotificationVariables(req, ctx);
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
return { kind: "notification", html };
|
|
19
|
+
const input = { template: req.payload.template ?? "", variables, locale: req.payload.locale };
|
|
20
|
+
const html = await renderer.render(input);
|
|
21
|
+
const text = renderer.renderText ? await renderer.renderText(input) : undefined;
|
|
22
|
+
return text === undefined ? { kind: "notification", html } : { kind: "notification", html, text };
|
|
25
23
|
}
|
|
26
24
|
export function createRendererSimpleFeature(options) {
|
|
27
25
|
const renderer = options?.mailBranding
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { createRendererSimpleFeature, type RendererSimpleOptions } from "./feature.js";
|
|
2
|
+
export type { MailBadge, MailChip, MailStage, MailStageState, MailTone } from "./mail-blocks.js";
|
|
2
3
|
export { createSimpleRenderer, type LocalizedText, type MailBranding, simpleRenderer, } from "./simple-renderer.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type MailTone = "neutral" | "info" | "success" | "warning" | "danger";
|
|
2
|
+
export type MailStageState = "done" | "current" | "upcoming";
|
|
3
|
+
export type MailBadge = {
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly tone?: MailTone;
|
|
6
|
+
};
|
|
7
|
+
export type MailStage = {
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly state: MailStageState;
|
|
10
|
+
};
|
|
11
|
+
export type MailChip = {
|
|
12
|
+
readonly label: string;
|
|
13
|
+
readonly tone?: MailTone;
|
|
14
|
+
};
|
|
15
|
+
export declare function parseBadge(value: unknown): MailBadge | undefined;
|
|
16
|
+
export declare function renderBadge(badge: MailBadge): string;
|
|
17
|
+
export declare function renderChips(chips: unknown): string;
|
|
18
|
+
export declare function renderStages(stages: unknown, primaryColor: string): string;
|
|
19
|
+
export declare function badgeText(badge: MailBadge): string;
|
|
20
|
+
export declare function stagesText(stages: unknown): string;
|
|
21
|
+
export declare function chipsText(chips: unknown): string;
|