@cosmicdrift/kumiko-bundled-features 0.337.1 → 0.339.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/agent-tools/tool-catalog.js +37 -17
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +3 -2
- package/dist/auth-email-password/handlers/invite-accept.write.js +3 -4
- package/dist/auth-email-password/handlers/invite-info.query.js +4 -1
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +3 -4
- package/dist/auth-email-password/handlers/signup-confirm.write.js +2 -1
- package/dist/auth-email-password/handlers/signup-request.write.js +2 -2
- package/dist/auth-email-password/web/auth-gate.d.ts +12 -2
- package/dist/auth-email-password/web/auth-gate.js +25 -1
- package/dist/auth-email-password/web/auth-redirect.d.ts +5 -0
- package/dist/auth-email-password/web/auth-redirect.js +58 -0
- package/dist/auth-email-password/web/client-plugin.d.ts +8 -1
- package/dist/auth-email-password/web/client-plugin.js +2 -0
- package/dist/auth-email-password/web/index.d.ts +2 -1
- package/dist/auth-email-password/web/index.js +1 -0
- package/dist/auth-email-password/web/session.d.ts +3 -1
- package/dist/auth-email-password/web/session.js +8 -2
- package/dist/auth-mfa/feature.js +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +2 -1
- package/dist/auth-mfa/handlers/verify.write.js +1 -1
- package/dist/auth-mfa/mfa-code-verifier.js +1 -1
- package/dist/auth-mfa/mfa-status-checker.js +1 -1
- package/dist/billing-foundation/aggregate-id.d.ts +2 -0
- package/dist/billing-foundation/aggregate-id.js +8 -0
- package/dist/billing-foundation/checkout-core.d.ts +3 -0
- package/dist/billing-foundation/checkout-core.js +3 -0
- package/dist/billing-foundation/constants.d.ts +38 -0
- package/dist/billing-foundation/constants.js +43 -0
- package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +21 -0
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +125 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +32 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +96 -0
- package/dist/billing-foundation/consumer-protection/consent-locale.d.ts +5 -0
- package/dist/billing-foundation/consumer-protection/consent-locale.js +17 -0
- package/dist/billing-foundation/consumer-protection/consent-text.d.ts +11 -0
- package/dist/billing-foundation/consumer-protection/consent-text.js +28 -0
- package/dist/billing-foundation/consumer-protection/issue-confirmation.d.ts +15 -0
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +149 -0
- package/dist/billing-foundation/consumer-protection/register-consent.d.ts +3 -0
- package/dist/billing-foundation/consumer-protection/register-consent.js +63 -0
- package/dist/billing-foundation/consumer-protection/register-termination.d.ts +3 -0
- package/dist/billing-foundation/consumer-protection/register-termination.js +40 -0
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +45 -0
- package/dist/billing-foundation/consumer-protection/termination-mail.js +76 -0
- package/dist/billing-foundation/consumer-protection/termination-notify.d.ts +18 -0
- package/dist/billing-foundation/consumer-protection/termination-notify.js +21 -0
- package/dist/billing-foundation/consumer-protection/termination-pages.d.ts +8 -0
- package/dist/billing-foundation/consumer-protection/termination-pages.js +268 -0
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +73 -0
- package/dist/billing-foundation/consumer-protection/termination-record.js +230 -0
- package/dist/billing-foundation/consumer-protection/termination-request.d.ts +45 -0
- package/dist/billing-foundation/consumer-protection/termination-request.js +231 -0
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +62 -0
- package/dist/billing-foundation/consumer-protection/termination-texts.js +168 -0
- package/dist/billing-foundation/events.d.ts +105 -2
- package/dist/billing-foundation/events.js +88 -2
- package/dist/billing-foundation/feature.js +6 -2
- package/dist/billing-foundation/handlers/billing-plans.query.js +19 -1
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +33 -2
- package/dist/billing-foundation/handlers/process-event.write.d.ts +2 -2
- package/dist/billing-foundation/handlers/process-event.write.js +43 -15
- package/dist/billing-foundation/handlers/process-payment-event.write.d.ts +2 -1
- package/dist/billing-foundation/handlers/process-payment-event.write.js +6 -2
- package/dist/billing-foundation/handlers/start-plan-checkout.write.js +17 -1
- package/dist/billing-foundation/handlers/sync-subscription.write.js +0 -1
- package/dist/billing-foundation/i18n.js +61 -0
- package/dist/billing-foundation/index.d.ts +3 -2
- package/dist/billing-foundation/index.js +2 -1
- package/dist/billing-foundation/tenant-destroy-hook.js +4 -4
- package/dist/billing-foundation/types.d.ts +54 -9
- package/dist/billing-foundation/validate-options.js +51 -10
- package/dist/billing-foundation/web/billing-plans-panel.js +40 -3
- package/dist/billing-foundation/web/cancel-contract-dialog.d.ts +7 -0
- package/dist/billing-foundation/web/cancel-contract-dialog.js +57 -0
- package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +28 -0
- package/dist/billing-foundation/web/checkout-consent-dialog.js +38 -0
- package/dist/billing-foundation/webhook-handler.js +5 -2
- package/dist/cap-counter/with-cap-enforcement.js +7 -3
- package/dist/cap-overview/access.d.ts +2 -1
- package/dist/cap-overview/access.js +26 -5
- package/dist/cap-overview/feature.d.ts +4 -0
- package/dist/cap-overview/feature.js +8 -6
- package/dist/cap-overview/handlers/caps-usage.query.d.ts +1 -1
- package/dist/cap-overview/handlers/caps-usage.query.js +6 -4
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +12 -7
- package/dist/cap-overview/index.d.ts +2 -2
- package/dist/cap-overview/index.js +2 -2
- package/dist/cap-overview/screens.d.ts +1 -1
- package/dist/cap-overview/screens.js +17 -16
- package/dist/channel-discord/discord-channel.d.ts +6 -0
- package/dist/channel-discord/discord-channel.js +19 -0
- package/dist/channel-discord/feature.d.ts +3 -0
- package/dist/channel-discord/feature.js +30 -0
- package/dist/channel-discord/index.d.ts +2 -0
- package/dist/channel-discord/index.js +2 -0
- package/dist/channel-slack/feature.d.ts +3 -0
- package/dist/channel-slack/feature.js +30 -0
- package/dist/channel-slack/index.d.ts +2 -0
- package/dist/channel-slack/index.js +2 -0
- package/dist/channel-slack/slack-channel.d.ts +5 -0
- package/dist/channel-slack/slack-channel.js +16 -0
- package/dist/channel-teams/feature.d.ts +3 -0
- package/dist/channel-teams/feature.js +34 -0
- package/dist/channel-teams/index.d.ts +2 -0
- package/dist/channel-teams/index.js +2 -0
- package/dist/channel-teams/teams-channel.d.ts +5 -0
- package/dist/channel-teams/teams-channel.js +38 -0
- package/dist/channel-telegram/feature.d.ts +3 -0
- package/dist/channel-telegram/feature.js +28 -0
- package/dist/channel-telegram/index.d.ts +2 -0
- package/dist/channel-telegram/index.js +2 -0
- package/dist/channel-telegram/telegram-channel.d.ts +8 -0
- package/dist/channel-telegram/telegram-channel.js +54 -0
- package/dist/config/handlers/set.write.js +18 -2
- package/dist/crypto-shredding/feature.js +3 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.js +30 -19
- package/dist/custom-fields/feature.js +9 -6
- package/dist/custom-fields/handlers/clear-custom-field.write.js +2 -1
- package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -1
- package/dist/custom-fields/handlers/set-custom-field.write.js +2 -1
- package/dist/custom-fields/wire-user-data-rights.js +1 -1
- package/dist/data-retention/feature.js +1 -1
- package/dist/delivery/channel-context.d.ts +2 -1
- package/dist/delivery/channel-context.js +2 -2
- package/dist/delivery/chat-webhook-channel.d.ts +19 -0
- package/dist/delivery/chat-webhook-channel.js +48 -0
- package/dist/delivery/chat-webhook-sender.d.ts +21 -0
- package/dist/delivery/chat-webhook-sender.js +102 -0
- package/dist/delivery/delivery-service.js +12 -7
- package/dist/delivery/index.d.ts +2 -0
- package/dist/delivery/index.js +2 -0
- package/dist/delivery/jobs.js +1 -1
- package/dist/delivery/testing.d.ts +2 -0
- package/dist/delivery/types.d.ts +3 -1
- package/dist/delivery/types.js +1 -2
- package/dist/document-ingest-foundation/feature.js +3 -2
- package/dist/file-derivatives/handlers/public-variant-by-file-ref.query.js +1 -1
- package/dist/files-tenant-data/index.js +1 -1
- package/dist/form-draft/feature.js +1 -1
- package/dist/form-draft/handlers/discard.write.js +2 -1
- package/dist/form-draft/handlers/save.write.js +2 -1
- package/dist/inbound-mail-foundation/connect-routes.d.ts +7 -0
- package/dist/inbound-mail-foundation/connect-routes.js +13 -8
- package/dist/inbound-mail-foundation/feature.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +2 -1
- package/dist/inbound-mail-foundation/index.d.ts +1 -0
- package/dist/inbound-mail-foundation/index.js +1 -0
- package/dist/inbound-mail-foundation/oauth-access-token.d.ts +28 -0
- package/dist/inbound-mail-foundation/oauth-access-token.js +131 -0
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +1 -1
- package/dist/inbound-mail-foundation/types.d.ts +18 -1
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +5 -0
- package/dist/inbound-mail-foundation/watch-supervisor.js +42 -14
- package/dist/managed-pages/handlers/set.write.js +2 -1
- package/dist/notes-history/executor.d.ts +2 -0
- package/dist/notes-history/executor.js +9 -1
- package/dist/notes-history/feature.d.ts +2 -3
- package/dist/notes-history/feature.js +2 -1
- package/dist/notes-history/handlers/add-note.write.d.ts +2 -1
- package/dist/notes-history/handlers/add-note.write.js +21 -4
- package/dist/notes-history-user-data/hooks.js +26 -8
- package/dist/renderer-simple/feature.d.ts +8 -2
- package/dist/renderer-simple/feature.js +8 -5
- package/dist/renderer-simple/index.d.ts +2 -2
- package/dist/renderer-simple/index.js +1 -1
- package/dist/renderer-simple/simple-renderer.d.ts +15 -0
- package/dist/renderer-simple/simple-renderer.js +74 -27
- package/dist/secrets/feature.js +1 -1
- package/dist/sessions/feature.js +1 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -1
- package/dist/subscription-mollie/plugin-methods.js +1 -0
- package/dist/subscription-mollie/verify-webhook.js +5 -3
- package/dist/subscription-stripe/plugin-methods.d.ts +3 -1
- package/dist/subscription-stripe/plugin-methods.js +78 -13
- package/dist/subscription-stripe/verify-webhook.js +7 -3
- package/dist/tags/executor.d.ts +2 -0
- package/dist/tags/executor.js +9 -1
- package/dist/tags/feature.d.ts +4 -3
- package/dist/tags/feature.js +4 -2
- package/dist/tags/handlers/assign-tag.write.d.ts +2 -1
- package/dist/tags/handlers/assign-tag.write.js +4 -4
- package/dist/tags/handlers/remove-tag.write.d.ts +2 -1
- package/dist/tags/handlers/remove-tag.write.js +3 -3
- package/dist/tenant-handover/handlers/claim.write.js +2 -2
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +4 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.js +2 -1
- package/dist/tier-engine/feature.js +1 -1
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +2 -1
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +2 -1
- package/dist/user/handlers/detail.query.js +1 -0
- package/dist/user/handlers/list.query.js +1 -0
- package/dist/user-data-rights/feature.js +2 -2
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +1 -1
- package/dist/user-data-rights/handlers/download-attempt-list.query.js +1 -0
- package/dist/user-data-rights/handlers/download-by-job.query.js +2 -1
- package/dist/user-data-rights/handlers/download-by-token.query.js +2 -1
- package/dist/user-data-rights/handlers/export-status.query.js +2 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.js +3 -4
- package/dist/user-data-rights/handlers/my-audit-log.query.js +2 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +2 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +1 -1
- package/dist/user-data-rights/handlers/request-export.write.js +2 -1
- package/dist/user-data-rights/handlers/restrict-account.write.js +2 -2
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +2 -1
- package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
- package/dist/user-data-rights/run-forget-cleanup.js +30 -15
- package/dist/workflow-runner/event-subscriber.js +6 -2
- package/dist/workflow-runner/event-trigger.js +5 -0
- package/dist/workflow-runner/handlers/resume-run.write.js +147 -59
- package/dist/workflow-runner/pending-projection.js +1 -0
- package/dist/workflow-runner/runner.d.ts +7 -1
- package/dist/workflow-runner/runner.js +1 -1
- package/dist/workflow-runner/tables.js +8 -1
- package/package.json +41 -9
- package/src/agent-tools/changes.json +14 -0
- package/src/auth-email-password/changes.json +7 -0
- package/src/billing-foundation/changes.json +83 -0
- package/src/cap-counter/changes.json +7 -0
- package/src/cap-overview/changes.json +14 -0
- package/src/config/changes.json +7 -0
- package/src/crypto-shredding/changes.json +13 -0
- package/src/custom-fields/changes.json +7 -0
- package/src/delivery/changes.json +14 -0
- package/src/inbound-mail-foundation/changes.json +7 -0
- package/src/notes-history/changes.json +7 -0
- package/src/renderer-simple/changes.json +9 -1
- package/src/subscription-mollie/changes.json +13 -0
- package/src/subscription-stripe/changes.json +13 -0
- package/src/tags/changes.json +7 -0
- package/src/user-data-rights/changes.json +6 -0
- package/src/workflow-runner/changes.json +7 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { buildEntityTable, decodeCursor, encodeCursor } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import { definePagedQueryHandler, MAX_LIST_LIMIT } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { definePagedQueryHandler, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { InternalError, ValidationError } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
import { subscriptionsProjectionTable } from "../../billing-foundation/index.js";
|
|
@@ -21,11 +21,16 @@ function billingLabel(subscription, tierSource) {
|
|
|
21
21
|
}
|
|
22
22
|
const FILTER_OP = z.enum(["eq", "ne", "lt", "gt", "in"]);
|
|
23
23
|
const SORTABLE_FIELDS = ["name", "tier", "billing"];
|
|
24
|
-
function
|
|
25
|
-
return JSON.stringify([
|
|
24
|
+
function tenantCapKey(tenantId, capId) {
|
|
25
|
+
return JSON.stringify([tenantId, capId]);
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// One config accessor per tenant: limits may read tenant-scoped config keys, so
|
|
28
|
+
// a limit shared across tenants of the same tier would show the wrong value.
|
|
29
|
+
async function resolveLimitsByTenantAndCap(caps, tenants, configFor) {
|
|
30
|
+
const entries = await Promise.all(tenants.flatMap(({ tenantId, tier }) => {
|
|
31
|
+
const context = { config: configFor?.(tenantId) };
|
|
32
|
+
return caps.map(async (cap) => [tenantCapKey(tenantId, cap.id), await cap.limit(tier, context)]);
|
|
33
|
+
}));
|
|
29
34
|
return new Map(entries);
|
|
30
35
|
}
|
|
31
36
|
function isSortableField(field) {
|
|
@@ -183,7 +188,7 @@ export function createTenantCapsListQuery(caps, listCaps) {
|
|
|
183
188
|
const perTenantEntries = await Promise.all(pageTenantIds.map(async (tenantId) => [tenantId, await cap.usage(db, tenantId)]));
|
|
184
189
|
usageByCap.set(cap.id, new Map(perTenantEntries));
|
|
185
190
|
}));
|
|
186
|
-
const
|
|
191
|
+
const limitByTenantAndCap = await resolveLimitsByTenantAndCap(listedCaps, page, ctx.configFor);
|
|
187
192
|
const rows = page.map((row) => {
|
|
188
193
|
const capFields = {};
|
|
189
194
|
for (const cap of listedCaps) {
|
|
@@ -191,7 +196,7 @@ export function createTenantCapsListQuery(caps, listCaps) {
|
|
|
191
196
|
// only an explicit `null` value means "not measured".
|
|
192
197
|
const rawUsed = usageByCap.get(cap.id)?.get(row.tenantId);
|
|
193
198
|
const used = rawUsed === undefined ? 0 : rawUsed;
|
|
194
|
-
const limit =
|
|
199
|
+
const limit = limitByTenantAndCap.get(tenantCapKey(row.tenantId, cap.id)) ?? null;
|
|
195
200
|
capFields[capFieldName(cap.id)] =
|
|
196
201
|
used === null
|
|
197
202
|
? { used: null, limit, fraction: 0 }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { DEFAULT_CAP_USAGE_ROLES } from "./access.js";
|
|
2
2
|
export { CAP_CARDS_PANEL_COMPONENT, CAP_OVERVIEW_FEATURE, CAP_USAGE_CELL_COMPONENT, CapOverviewQueries, capFieldName, MY_CAPS_SCREEN_ID, PLATFORM_TENANT_CAPS_SCREEN_ID, TENANT_CAP_LIST_SCREEN_ID, } from "./constants.js";
|
|
3
3
|
export { type CreateCapOverviewOptions, createCapOverviewFeature } from "./feature.js";
|
|
4
|
-
export {
|
|
4
|
+
export { createMyCapsScreen, createTenantCapListScreen, platformTenantCapsScreen, } from "./screens.js";
|
|
5
5
|
export type { CapSpec, CapUsage, CapUsageTone, CapUsageWithMeta } from "./types.js";
|
|
6
6
|
export { computeFraction, computeTone } from "./usage-math.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Public API of the cap-overview bundled-feature.
|
|
2
|
-
export {
|
|
2
|
+
export { DEFAULT_CAP_USAGE_ROLES } from "./access.js";
|
|
3
3
|
export { CAP_CARDS_PANEL_COMPONENT, CAP_OVERVIEW_FEATURE, CAP_USAGE_CELL_COMPONENT, CapOverviewQueries, capFieldName, MY_CAPS_SCREEN_ID, PLATFORM_TENANT_CAPS_SCREEN_ID, TENANT_CAP_LIST_SCREEN_ID, } from "./constants.js";
|
|
4
4
|
export { createCapOverviewFeature } from "./feature.js";
|
|
5
|
-
export {
|
|
5
|
+
export { createMyCapsScreen, createTenantCapListScreen, platformTenantCapsScreen, } from "./screens.js";
|
|
6
6
|
export { computeFraction, computeTone } from "./usage-math.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScreenDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import type { CapSpec } from "./types.js";
|
|
3
3
|
export declare function createTenantCapListScreen(caps: readonly CapSpec[], listCaps: readonly string[], tiers?: readonly string[]): ScreenDefinition;
|
|
4
|
-
export declare
|
|
4
|
+
export declare function createMyCapsScreen(usageRoles: readonly string[]): ScreenDefinition;
|
|
5
5
|
export declare const platformTenantCapsScreen: ScreenDefinition;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MY_CAPS_ACCESS_ROLES } from "./access.js";
|
|
2
1
|
import { CAP_CARDS_PANEL_COMPONENT, CAP_USAGE_CELL_COMPONENT, CapOverviewQueries, capFieldName, MY_CAPS_SCREEN_ID, PLATFORM_TENANT_CAPS_SCREEN_ID, TENANT_CAP_LIST_SCREEN_ID, } from "./constants.js";
|
|
3
2
|
// `tiers` has no room for a separate per-tier display label (it's just
|
|
4
3
|
// `readonly string[]`) — tier-engine itself has no enumerated tier list to
|
|
@@ -55,21 +54,23 @@ export function createTenantCapListScreen(caps, listCaps, tiers) {
|
|
|
55
54
|
dormant: true,
|
|
56
55
|
};
|
|
57
56
|
}
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
export function createMyCapsScreen(usageRoles) {
|
|
58
|
+
return {
|
|
59
|
+
id: MY_CAPS_SCREEN_ID,
|
|
60
|
+
type: "dashboard",
|
|
61
|
+
panels: [
|
|
62
|
+
{
|
|
63
|
+
kind: "custom",
|
|
64
|
+
id: "cap-cards",
|
|
65
|
+
component: { react: { __component: CAP_CARDS_PANEL_COMPONENT } },
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
access: { roles: usageRoles },
|
|
69
|
+
// No nav wiring yet (see feature.ts header) — no nav area to resolve in
|
|
70
|
+
// isolation.
|
|
71
|
+
dormant: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
73
74
|
export const platformTenantCapsScreen = {
|
|
74
75
|
id: PLATFORM_TENANT_CAPS_SCREEN_ID,
|
|
75
76
|
type: "dashboard",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ChatWebhookChannelOptions, type DeliveryChannel } from "../delivery/index.js";
|
|
2
|
+
export type DiscordChannelOptions = ChatWebhookChannelOptions;
|
|
3
|
+
export declare const DISCORD_DEFAULT_ALLOWED_HOSTS: readonly ["discord.com", "discordapp.com"];
|
|
4
|
+
export declare const DISCORD_WEBHOOK_PATH_PREFIX = "/api/webhooks/";
|
|
5
|
+
export declare const DISCORD_CONTENT_MAX_CHARS = 2000;
|
|
6
|
+
export declare function createDiscordChannel(options: DiscordChannelOptions, keyFor: (connection: string) => string): DeliveryChannel;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { chatMessageText, createChatWebhookChannel, truncateChars, } from "../delivery/index.js";
|
|
2
|
+
export const DISCORD_DEFAULT_ALLOWED_HOSTS = ["discord.com", "discordapp.com"];
|
|
3
|
+
export const DISCORD_WEBHOOK_PATH_PREFIX = "/api/webhooks/";
|
|
4
|
+
export const DISCORD_CONTENT_MAX_CHARS = 2000;
|
|
5
|
+
export function createDiscordChannel(options, keyFor) {
|
|
6
|
+
return createChatWebhookChannel({
|
|
7
|
+
name: "discord",
|
|
8
|
+
featureName: "channel-discord",
|
|
9
|
+
defaultAllowedHosts: DISCORD_DEFAULT_ALLOWED_HOSTS,
|
|
10
|
+
requiredPathPrefix: DISCORD_WEBHOOK_PATH_PREFIX,
|
|
11
|
+
keyFor,
|
|
12
|
+
// parse: [] keeps @everyone / @here / role and user mentions in the text inert.
|
|
13
|
+
buildBody: (message) => ({
|
|
14
|
+
content: truncateChars(chatMessageText(message), DISCORD_CONTENT_MAX_CHARS),
|
|
15
|
+
allowed_mentions: { parse: [] },
|
|
16
|
+
}),
|
|
17
|
+
options,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { chatConnectionNameSchema, DELIVERY_CHANNEL_EXTENSION, } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
2
|
+
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { createDiscordChannel } from "./discord-channel.js";
|
|
4
|
+
export function createChannelDiscordFeature(options = {}) {
|
|
5
|
+
return defineFeature("channel-discord", (r) => {
|
|
6
|
+
r.describe('Posts delivery notifications to a Discord webhook, registered as the `discord` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { discord: "<connection name>" } })`. The webhook URL is a tenant secret under `channel-discord:webhooks.<connection name>`; only the connection name is logged. Mentions are disabled and content is cut to 2000 characters. Requires `delivery` and `secrets`. Host allowlist (discord.com, discordapp.com, path /api/webhooks/), https-only and the request timeout are app options of `createChannelDiscordFeature(opts)`, never tenant config.');
|
|
7
|
+
r.uiHints({
|
|
8
|
+
displayLabel: "Discord Channel",
|
|
9
|
+
category: "notifications",
|
|
10
|
+
recommended: false,
|
|
11
|
+
});
|
|
12
|
+
r.requires("delivery");
|
|
13
|
+
r.requires("secrets");
|
|
14
|
+
const webhooks = r.secretNamespace("webhooks", {
|
|
15
|
+
label: { en: "Discord webhooks", de: "Discord-Webhooks", es: "Webhooks de Discord" },
|
|
16
|
+
hint: {
|
|
17
|
+
en: 'One secret per connection name; the value is the Discord webhook URL. Address it with route: { discord: "<connection name>" }.',
|
|
18
|
+
de: 'Ein Secret pro Verbindungsname; der Wert ist die Discord-Webhook-URL. Adressierung mit route: { discord: "<Verbindungsname>" }.',
|
|
19
|
+
es: 'Un secreto por nombre de conexión; el valor es la URL del webhook de Discord. Se usa con route: { discord: "<nombre de conexión>" }.',
|
|
20
|
+
},
|
|
21
|
+
scope: "tenant",
|
|
22
|
+
nameSchema: chatConnectionNameSchema,
|
|
23
|
+
});
|
|
24
|
+
const channel = createDiscordChannel(options, webhooks.keyFor);
|
|
25
|
+
r.useExtension(DELIVERY_CHANNEL_EXTENSION, "discord", {
|
|
26
|
+
mode: channel.mode,
|
|
27
|
+
send: channel.send,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { chatConnectionNameSchema, DELIVERY_CHANNEL_EXTENSION, } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
2
|
+
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { createSlackChannel } from "./slack-channel.js";
|
|
4
|
+
export function createChannelSlackFeature(options = {}) {
|
|
5
|
+
return defineFeature("channel-slack", (r) => {
|
|
6
|
+
r.describe('Posts delivery notifications to a Slack incoming webhook, registered as the `slack` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { slack: "<connection name>" } })`. The webhook URL is a tenant secret under `channel-slack:webhooks.<connection name>`; only the connection name is logged. Requires `delivery` and `secrets`. Host allowlist (hooks.slack.com), https-only and the request timeout are app options of `createChannelSlackFeature(opts)`, never tenant config.');
|
|
7
|
+
r.uiHints({
|
|
8
|
+
displayLabel: "Slack Channel",
|
|
9
|
+
category: "notifications",
|
|
10
|
+
recommended: false,
|
|
11
|
+
});
|
|
12
|
+
r.requires("delivery");
|
|
13
|
+
r.requires("secrets");
|
|
14
|
+
const webhooks = r.secretNamespace("webhooks", {
|
|
15
|
+
label: { en: "Slack webhooks", de: "Slack-Webhooks", es: "Webhooks de Slack" },
|
|
16
|
+
hint: {
|
|
17
|
+
en: 'One secret per connection name; the value is the Slack webhook URL. Address it with route: { slack: "<connection name>" }.',
|
|
18
|
+
de: 'Ein Secret pro Verbindungsname; der Wert ist die Slack-Webhook-URL. Adressierung mit route: { slack: "<Verbindungsname>" }.',
|
|
19
|
+
es: 'Un secreto por nombre de conexión; el valor es la URL del webhook de Slack. Se usa con route: { slack: "<nombre de conexión>" }.',
|
|
20
|
+
},
|
|
21
|
+
scope: "tenant",
|
|
22
|
+
nameSchema: chatConnectionNameSchema,
|
|
23
|
+
});
|
|
24
|
+
const channel = createSlackChannel(options, webhooks.keyFor);
|
|
25
|
+
r.useExtension(DELIVERY_CHANNEL_EXTENSION, "slack", {
|
|
26
|
+
mode: channel.mode,
|
|
27
|
+
send: channel.send,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ChatWebhookChannelOptions, type DeliveryChannel } from "../delivery/index.js";
|
|
2
|
+
export type SlackChannelOptions = ChatWebhookChannelOptions;
|
|
3
|
+
export declare const SLACK_DEFAULT_ALLOWED_HOSTS: readonly ["hooks.slack.com"];
|
|
4
|
+
export declare function escapeSlackText(text: string): string;
|
|
5
|
+
export declare function createSlackChannel(options: SlackChannelOptions, keyFor: (connection: string) => string): DeliveryChannel;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { chatMessageText, createChatWebhookChannel, } from "../delivery/index.js";
|
|
2
|
+
export const SLACK_DEFAULT_ALLOWED_HOSTS = ["hooks.slack.com"];
|
|
3
|
+
// Slack mrkdwn treats & < > as control characters (links, @-mentions via <!channel>).
|
|
4
|
+
export function escapeSlackText(text) {
|
|
5
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
6
|
+
}
|
|
7
|
+
export function createSlackChannel(options, keyFor) {
|
|
8
|
+
return createChatWebhookChannel({
|
|
9
|
+
name: "slack",
|
|
10
|
+
featureName: "channel-slack",
|
|
11
|
+
defaultAllowedHosts: SLACK_DEFAULT_ALLOWED_HOSTS,
|
|
12
|
+
keyFor,
|
|
13
|
+
buildBody: (message) => ({ text: escapeSlackText(chatMessageText(message)) }),
|
|
14
|
+
options,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { chatConnectionNameSchema, DELIVERY_CHANNEL_EXTENSION, } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
2
|
+
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { createTeamsChannel } from "./teams-channel.js";
|
|
4
|
+
export function createChannelTeamsFeature(options = {}) {
|
|
5
|
+
return defineFeature("channel-teams", (r) => {
|
|
6
|
+
r.describe('Posts delivery notifications to a Microsoft Teams webhook (Workflows or connector) as an Adaptive Card, registered as the `teams` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { teams: "<connection name>" } })`. The webhook URL is a tenant secret under `channel-teams:webhooks.<connection name>`; only the connection name is logged. Requires `delivery` and `secrets`. Host allowlist (.webhook.office.com, .logic.azure.com, .powerplatform.com), https-only and the request timeout are app options of `createChannelTeamsFeature(opts)`, never tenant config.');
|
|
7
|
+
r.uiHints({
|
|
8
|
+
displayLabel: "Microsoft Teams Channel",
|
|
9
|
+
category: "notifications",
|
|
10
|
+
recommended: false,
|
|
11
|
+
});
|
|
12
|
+
r.requires("delivery");
|
|
13
|
+
r.requires("secrets");
|
|
14
|
+
const webhooks = r.secretNamespace("webhooks", {
|
|
15
|
+
label: {
|
|
16
|
+
en: "Microsoft Teams webhooks",
|
|
17
|
+
de: "Microsoft Teams-Webhooks",
|
|
18
|
+
es: "Webhooks de Microsoft Teams",
|
|
19
|
+
},
|
|
20
|
+
hint: {
|
|
21
|
+
en: 'One secret per connection name; the value is the Microsoft Teams webhook URL. Address it with route: { teams: "<connection name>" }.',
|
|
22
|
+
de: 'Ein Secret pro Verbindungsname; der Wert ist die Microsoft Teams-Webhook-URL. Adressierung mit route: { teams: "<Verbindungsname>" }.',
|
|
23
|
+
es: 'Un secreto por nombre de conexión; el valor es la URL del webhook de Microsoft Teams. Se usa con route: { teams: "<nombre de conexión>" }.',
|
|
24
|
+
},
|
|
25
|
+
scope: "tenant",
|
|
26
|
+
nameSchema: chatConnectionNameSchema,
|
|
27
|
+
});
|
|
28
|
+
const channel = createTeamsChannel(options, webhooks.keyFor);
|
|
29
|
+
r.useExtension(DELIVERY_CHANNEL_EXTENSION, "teams", {
|
|
30
|
+
mode: channel.mode,
|
|
31
|
+
send: channel.send,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ChatWebhookChannelOptions, type DeliveryChannel } from "../delivery/index.js";
|
|
2
|
+
export type TeamsChannelOptions = ChatWebhookChannelOptions;
|
|
3
|
+
export declare const TEAMS_DEFAULT_ALLOWED_HOSTS: readonly [".webhook.office.com", ".logic.azure.com", ".powerplatform.com"];
|
|
4
|
+
export declare function buildTeamsAdaptiveCard(title: string, body: string | undefined): unknown;
|
|
5
|
+
export declare function createTeamsChannel(options: TeamsChannelOptions, keyFor: (connection: string) => string): DeliveryChannel;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createChatWebhookChannel, } from "../delivery/index.js";
|
|
2
|
+
export const TEAMS_DEFAULT_ALLOWED_HOSTS = [
|
|
3
|
+
".webhook.office.com",
|
|
4
|
+
".logic.azure.com",
|
|
5
|
+
".powerplatform.com",
|
|
6
|
+
];
|
|
7
|
+
// Text only ever goes into TextBlocks (no markup interpretation of titles/bodies
|
|
8
|
+
// beyond Adaptive Card's own text rendering).
|
|
9
|
+
export function buildTeamsAdaptiveCard(title, body) {
|
|
10
|
+
return {
|
|
11
|
+
type: "message",
|
|
12
|
+
attachments: [
|
|
13
|
+
{
|
|
14
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
15
|
+
contentUrl: null,
|
|
16
|
+
content: {
|
|
17
|
+
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
18
|
+
type: "AdaptiveCard",
|
|
19
|
+
version: "1.4",
|
|
20
|
+
body: [
|
|
21
|
+
{ type: "TextBlock", text: title, weight: "Bolder", wrap: true },
|
|
22
|
+
...(body ? [{ type: "TextBlock", text: body, wrap: true }] : []),
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function createTeamsChannel(options, keyFor) {
|
|
30
|
+
return createChatWebhookChannel({
|
|
31
|
+
name: "teams",
|
|
32
|
+
featureName: "channel-teams",
|
|
33
|
+
defaultAllowedHosts: TEAMS_DEFAULT_ALLOWED_HOSTS,
|
|
34
|
+
keyFor,
|
|
35
|
+
buildBody: (message) => buildTeamsAdaptiveCard(message.title, message.body),
|
|
36
|
+
options,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DELIVERY_CHANNEL_EXTENSION } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
2
|
+
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { createTelegramChannel } from "./telegram-channel.js";
|
|
4
|
+
export function createChannelTelegramFeature(options = {}) {
|
|
5
|
+
return defineFeature("channel-telegram", (r) => {
|
|
6
|
+
r.describe('Sends delivery notifications through the Telegram Bot API (`sendMessage`, plain text), registered as the `telegram` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { telegram: "<chat id or @channel>" } })`. The bot token is the tenant secret `channel-telegram:secret:bot-token`; the token never reaches logs or delivery attempts. Requires `delivery` and `secrets`. The API base URL and request timeout are app options of `createChannelTelegramFeature(opts)`, never tenant config.');
|
|
7
|
+
r.uiHints({
|
|
8
|
+
displayLabel: "Telegram Channel",
|
|
9
|
+
category: "notifications",
|
|
10
|
+
recommended: false,
|
|
11
|
+
});
|
|
12
|
+
r.requires("delivery");
|
|
13
|
+
r.requires("secrets");
|
|
14
|
+
const botToken = r.secret("botToken", {
|
|
15
|
+
label: { en: "Telegram bot token" },
|
|
16
|
+
hint: {
|
|
17
|
+
en: 'Token from @BotFather. Used for every Telegram chat the tenant addresses via route: { telegram: "<chat id>" }.',
|
|
18
|
+
},
|
|
19
|
+
redact: () => "••••••••",
|
|
20
|
+
scope: "tenant",
|
|
21
|
+
});
|
|
22
|
+
const channel = createTelegramChannel(options, botToken.name);
|
|
23
|
+
r.useExtension(DELIVERY_CHANNEL_EXTENSION, "telegram", {
|
|
24
|
+
mode: channel.mode,
|
|
25
|
+
send: channel.send,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DeliveryChannel } from "../delivery/index.js";
|
|
2
|
+
export type TelegramChannelOptions = {
|
|
3
|
+
readonly apiBaseUrl?: string;
|
|
4
|
+
readonly timeoutMs?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const TELEGRAM_DEFAULT_API_BASE_URL = "https://api.telegram.org";
|
|
7
|
+
export declare const TELEGRAM_TEXT_MAX_CHARS = 4096;
|
|
8
|
+
export declare function createTelegramChannel(options: TelegramChannelOptions, botTokenKey: string): DeliveryChannel;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-types/identifiers";
|
|
2
|
+
import { chatMessageText, DEFAULT_CHAT_TIMEOUT_MS, postChatWebhook, toChannelResult, truncateChars, } from "../delivery/index.js";
|
|
3
|
+
export const TELEGRAM_DEFAULT_API_BASE_URL = "https://api.telegram.org";
|
|
4
|
+
export const TELEGRAM_TEXT_MAX_CHARS = 4096;
|
|
5
|
+
// Numeric chat/group id (groups and channels are negative) or a public @channelname.
|
|
6
|
+
const TELEGRAM_CHAT_ID_PATTERN = /^(-?\d{1,20}|@[A-Za-z][A-Za-z0-9_]{3,31})$/;
|
|
7
|
+
// `<bot id>:<secret>`; the token is spliced into the URL path, so only the real shape passes.
|
|
8
|
+
const TELEGRAM_BOT_TOKEN_PATTERN = /^\d{1,20}:[A-Za-z0-9_-]{10,}$/;
|
|
9
|
+
export function createTelegramChannel(options, botTokenKey) {
|
|
10
|
+
const apiBaseUrl = options.apiBaseUrl ?? TELEGRAM_DEFAULT_API_BASE_URL;
|
|
11
|
+
return {
|
|
12
|
+
name: "telegram",
|
|
13
|
+
mode: "queued",
|
|
14
|
+
async send(address, message, ctx) {
|
|
15
|
+
if (!TELEGRAM_CHAT_ID_PATTERN.test(address)) {
|
|
16
|
+
return toChannelResult(address, { ok: false, code: "invalid_address" });
|
|
17
|
+
}
|
|
18
|
+
let token;
|
|
19
|
+
try {
|
|
20
|
+
const revealed = await ctx.secrets?.get(ctx.tenantId, botTokenKey, {
|
|
21
|
+
userId: SYSTEM_USER_ID,
|
|
22
|
+
handlerName: "channel-telegram:send",
|
|
23
|
+
});
|
|
24
|
+
token = revealed?.reveal();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
token = undefined;
|
|
28
|
+
}
|
|
29
|
+
if (!token || !TELEGRAM_BOT_TOKEN_PATTERN.test(token)) {
|
|
30
|
+
return toChannelResult(address, { ok: false, code: "missing_credentials" });
|
|
31
|
+
}
|
|
32
|
+
// The base URL is the allowlist: only its own host is reachable.
|
|
33
|
+
let baseHost;
|
|
34
|
+
try {
|
|
35
|
+
baseHost = new URL(apiBaseUrl).hostname;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return toChannelResult(address, { ok: false, code: "host_not_allowed" });
|
|
39
|
+
}
|
|
40
|
+
const result = await postChatWebhook({
|
|
41
|
+
url: `${apiBaseUrl.replace(/\/+$/, "")}/bot${token}/sendMessage`,
|
|
42
|
+
allowedHosts: [baseHost],
|
|
43
|
+
requireHttps: apiBaseUrl.startsWith("https:"),
|
|
44
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_CHAT_TIMEOUT_MS,
|
|
45
|
+
// No parse_mode: Telegram renders the text literally.
|
|
46
|
+
body: {
|
|
47
|
+
chat_id: address,
|
|
48
|
+
text: truncateChars(chatMessageText(message), TELEGRAM_TEXT_MAX_CHARS),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return toChannelResult(address, result);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -2,7 +2,7 @@ import { requestContext } from "@cosmicdrift/kumiko-framework/api";
|
|
|
2
2
|
import { configuredPiiSubjectKms, encryptPiiValueForSubject, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
3
|
import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
4
4
|
import { ConfigScopes, defineWriteHandler, isEncryptedAtRest, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
-
import { InternalError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import { InternalError, isKumikoError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { requireConfigEncryption, requireSystemDb } from "../feature.js";
|
|
8
8
|
import { MASKED } from "../read-redaction.js";
|
|
@@ -14,7 +14,7 @@ const executor = createEventStoreExecutor(configValuesTable, configValueEntity,
|
|
|
14
14
|
});
|
|
15
15
|
export const setWrite = defineWriteHandler({
|
|
16
16
|
name: "set",
|
|
17
|
-
description: "Stores a config value for one key at the requested scope (user, tenant or system) after type, bounds and
|
|
17
|
+
description: "Stores a config value for one key at the requested scope (user, tenant or system) after type, bounds, pattern and the key's own validator, encrypting it when the key declares that; use it to change a setting.",
|
|
18
18
|
schema: z.object({
|
|
19
19
|
key: z.string(),
|
|
20
20
|
value: z.union([z.string(), z.number(), z.boolean()]),
|
|
@@ -67,6 +67,22 @@ export const setWrite = defineWriteHandler({
|
|
|
67
67
|
const patternError = validatePattern(event.payload.value, keyDef);
|
|
68
68
|
if (patternError)
|
|
69
69
|
return writeFailure(patternError);
|
|
70
|
+
if (keyDef.validate) {
|
|
71
|
+
try {
|
|
72
|
+
await keyDef.validate(event.payload.value, {
|
|
73
|
+
key: event.payload.key,
|
|
74
|
+
scope,
|
|
75
|
+
tenantId,
|
|
76
|
+
userId,
|
|
77
|
+
db,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (isKumikoError(error))
|
|
82
|
+
return writeFailure(error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
70
86
|
// The batch idempotency cache stores this result and only masks entity
|
|
71
87
|
// writeOnly fields, so an at-rest-encrypted value must not be echoed.
|
|
72
88
|
const echoedValue = isEncryptedAtRest(keyDef) ? MASKED : event.payload.value;
|
|
@@ -8,8 +8,9 @@ export function createCryptoShreddingFeature() {
|
|
|
8
8
|
displayLabel: "Crypto-Shredding",
|
|
9
9
|
category: "compliance",
|
|
10
10
|
});
|
|
11
|
-
// `reason` is operator free text and may name the subject
|
|
12
|
-
// the subject key
|
|
11
|
+
// `reason` is operator free text and may name the subject, yet stays plaintext: the forget
|
|
12
|
+
// erases the subject key before the audit append, so a forget of the actor's own tenant
|
|
13
|
+
// would fail to seal the reason under that tenant's key.
|
|
13
14
|
r.defineEvent("subject-forgotten", subjectForgottenSchema, { piiFields: "none" });
|
|
14
15
|
r.defineEvent("forget-denied", subjectForgetDeniedSchema, { piiFields: "none" });
|
|
15
16
|
r.writeHandler(forgetSubjectWrite);
|
|
@@ -21,7 +21,7 @@ export declare const subjectForgottenSchema: z.ZodObject<{
|
|
|
21
21
|
forgottenBy: z.ZodString;
|
|
22
22
|
}, z.core.$strip>;
|
|
23
23
|
export declare const subjectForgetDeniedSchema: z.ZodObject<{
|
|
24
|
-
subjectKeyDigest: z.ZodString
|
|
24
|
+
subjectKeyDigest: z.ZodOptional<z.ZodString>;
|
|
25
25
|
subjectKind: z.ZodEnum<{
|
|
26
26
|
record: "record";
|
|
27
27
|
tenant: "tenant";
|