@cosmicdrift/kumiko-bundled-features 0.333.0 → 0.335.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/admin-shell/overview-panels.js +2 -2
- package/dist/agent-tools/agent-doc-lint.d.ts +1 -0
- package/dist/agent-tools/agent-doc-lint.js +33 -3
- package/dist/agent-tools/agent-manifest.js +47 -6
- package/dist/agent-tools/filter-ops.d.ts +2 -0
- package/dist/agent-tools/filter-ops.js +4 -0
- package/dist/agent-tools/tool-catalog.js +1 -4
- package/dist/agent-tools/tool-dispatch.js +1 -4
- package/dist/audit/constants.d.ts +1 -1
- package/dist/audit/constants.js +1 -1
- package/dist/audit/handlers/list.query.js +4 -3
- package/dist/auth-email-password/handlers/change-password.write.js +2 -0
- package/dist/auth-email-password/handlers/invite-create.write.js +12 -2
- package/dist/auth-email-password/handlers/login.write.js +2 -0
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/i18n.js +2 -0
- package/dist/auth-email-password/web/auth-client.d.ts +1 -0
- package/dist/auth-email-password/web/auth-client.js +1 -1
- package/dist/auth-email-password/web/auth-gate.js +6 -8
- package/dist/auth-email-password/web/index.d.ts +1 -1
- package/dist/auth-email-password/web/index.js +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -12
- package/dist/auth-email-password/web/login-screen.js +1 -1
- package/dist/auth-email-password/web/session-bootstrap-error.d.ts +5 -1
- package/dist/auth-email-password/web/session-bootstrap-error.js +2 -2
- package/dist/auth-email-password/web/session.d.ts +4 -0
- package/dist/auth-email-password/web/session.js +15 -0
- package/dist/auth-mfa/feature.js +3 -2
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +2 -2
- package/dist/auth-mfa/handlers/verify.write.js +2 -0
- package/dist/auth-mfa/hmac-token-codec.js +3 -0
- package/dist/auth-mfa/mfa-challenge-token.js +4 -0
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -0
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -0
- package/dist/auth-mfa/web/mfa-client.d.ts +1 -1
- package/dist/auth-mfa/web/mfa-client.js +4 -10
- package/dist/billing-foundation/billing-info-query.js +1 -1
- package/dist/billing-foundation/checkout-core.d.ts +5 -5
- package/dist/billing-foundation/checkout-core.js +10 -6
- package/dist/billing-foundation/entities.d.ts +8 -0
- package/dist/billing-foundation/handlers/switch-plan.write.js +5 -8
- package/dist/billing-foundation/projection.d.ts +8 -0
- package/dist/cap-counter/book-cap-usage.js +15 -5
- package/dist/cap-counter/enforce-cap.js +9 -3
- package/dist/cap-counter/entity.d.ts +1 -0
- package/dist/cap-counter/feature.js +3 -2
- package/dist/cap-counter/i18n.d.ts +1 -0
- package/dist/cap-counter/i18n.js +6 -5
- package/dist/cap-overview/handlers/caps-usage.query.js +9 -16
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +41 -15
- package/dist/cap-overview/i18n.js +5 -0
- package/dist/cap-overview/web/cap-cards-panel.js +4 -1
- package/dist/cap-overview/web/cap-usage-bar.js +1 -1
- package/dist/channel-email/email-channel.js +12 -1
- package/dist/channel-in-app/tables.js +2 -6
- package/dist/compliance-profiles/handlers/sub-processors.query.js +1 -1
- package/dist/compliance-profiles/schema/profile-selection.d.ts +2 -2
- package/dist/config/db/queries/resolver.js +3 -1
- package/dist/config/handlers/set.write.js +8 -4
- package/dist/config/table.d.ts +6 -0
- package/dist/config/web/i18n.js +13 -4
- package/dist/config/write-helpers.d.ts +1 -1
- package/dist/config/write-helpers.js +11 -5
- package/dist/crypto-shredding/handlers/forget-subject.write.js +15 -8
- package/dist/custom-fields/constants.js +1 -4
- package/dist/custom-fields/entity.d.ts +4 -0
- package/dist/data-retention/resolve-for-tenant.d.ts +2 -3
- package/dist/data-retention/run-retention-cleanup.js +100 -44
- package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -0
- package/dist/delivery/tables.d.ts +6 -0
- package/dist/delivery/types.d.ts +2 -1
- package/dist/delivery/types.js +7 -0
- package/dist/delivery/unsubscribe.js +5 -11
- package/dist/derivatives-sharp/render.js +27 -9
- package/dist/document-ingest-foundation/entity.d.ts +4 -0
- package/dist/document-ingest-foundation/feature.js +5 -5
- package/dist/document-ingest-foundation/providers.d.ts +1 -0
- package/dist/document-ingest-foundation/providers.js +6 -0
- package/dist/document-ingest-foundation/write-document-extract.d.ts +2 -2
- package/dist/document-ingest-foundation/write-document-extract.js +10 -0
- package/dist/feature-toggles/global-feature-state-table.js +2 -7
- package/dist/feature-toggles/toggle-sync-signal.d.ts +2 -1
- package/dist/feature-toggles/toggle-sync-signal.js +3 -2
- package/dist/file-provider-s3/feature.js +1 -1
- package/dist/files-tenant-data/index.js +3 -2
- package/dist/folders/entity.d.ts +8 -0
- package/dist/folders/handlers/clear-folder.write.js +4 -11
- package/dist/folders/handlers/set-folder.write.js +4 -10
- package/dist/form-draft/entity.d.ts +2 -0
- package/dist/form-draft/feature.js +3 -2
- package/dist/form-draft/handlers/discard.write.js +7 -8
- package/dist/form-draft/handlers/get.query.js +7 -1
- package/dist/foundation-shared/host-allowlist.d.ts +4 -0
- package/dist/foundation-shared/host-allowlist.js +14 -0
- package/dist/foundation-shared/index.d.ts +1 -0
- package/dist/foundation-shared/index.js +1 -0
- package/dist/foundation-shared/mail-host-policy.d.ts +0 -2
- package/dist/foundation-shared/mail-host-policy.js +3 -13
- package/dist/inbound-mail-foundation/connect-routes.js +7 -5
- package/dist/inbound-mail-foundation/entities.d.ts +25 -0
- package/dist/inbound-mail-foundation/projection.d.ts +20 -0
- package/dist/inbound-mail-foundation/watch-supervisor.js +47 -43
- package/dist/jobs/handlers/list.query.d.ts +7 -2
- package/dist/jobs/handlers/list.query.js +9 -2
- package/dist/jobs/handlers/retry.write.js +3 -2
- package/dist/jobs/handlers/tenant-failures.query.js +3 -9
- package/dist/jobs/job-run-logger.js +18 -2
- package/dist/jobs/job-run-table.d.ts +6 -0
- package/dist/jobs/tenant-job-failure-table.js +2 -6
- package/dist/ledger/entity.d.ts +16 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +2 -2
- package/dist/ledger/handlers/reverse-transaction.write.js +2 -2
- package/dist/ledger/subject-reference.d.ts +4 -0
- package/dist/ledger/subject-reference.js +7 -0
- package/dist/legal-pages/feature.js +8 -1
- package/dist/managed-pages/handlers/branding.query.js +1 -1
- package/dist/managed-pages/handlers/by-slug.query.js +1 -1
- package/dist/managed-pages/handlers/by-tenant-published.query.js +1 -1
- package/dist/managed-pages/table.d.ts +12 -0
- package/dist/metrics/compute.js +7 -1
- package/dist/metrics/window.js +5 -0
- package/dist/notes-history/entity.d.ts +12 -0
- package/dist/notes-history/handlers/add-note.write.js +4 -9
- package/dist/notes-history-user-data/hooks.js +8 -1
- package/dist/personal-access-tokens/constants.d.ts +1 -0
- package/dist/personal-access-tokens/constants.js +1 -0
- package/dist/personal-access-tokens/feature.js +2 -2
- package/dist/personal-access-tokens/handlers/create.write.js +2 -2
- package/dist/personal-access-tokens/handlers/revoke.write.js +2 -2
- package/dist/personal-access-tokens/i18n.js +12 -13
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -0
- package/dist/personal-access-tokens/schema/api-token.js +2 -1
- package/dist/personal-access-tokens/screens.js +5 -1
- package/dist/rate-limiting/feature.js +2 -0
- package/dist/rate-limiting/handlers/bucket-access.js +15 -7
- package/dist/rate-limiting/i18n.d.ts +10 -0
- package/dist/rate-limiting/i18n.js +10 -0
- package/dist/secrets/feature.js +3 -2
- package/dist/secrets/table.d.ts +1 -0
- package/dist/seo/handlers/seo-config.query.js +1 -1
- package/dist/sessions/constants.d.ts +1 -0
- package/dist/sessions/constants.js +1 -0
- package/dist/sessions/feature.js +5 -3
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -0
- package/dist/sessions/handlers/revoke-all-for-user.write.js +12 -2
- package/dist/sessions/handlers/revoke.write.js +2 -2
- package/dist/sessions/schema/user-session.d.ts +4 -0
- package/dist/sessions/schema/user-session.js +2 -1
- package/dist/sessions/screens.js +5 -1
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/lockout-counter.js +1 -1
- package/dist/shared/parent-visibility.d.ts +5 -0
- package/dist/shared/parent-visibility.js +17 -1
- package/dist/shared/row-bound-grant.js +20 -9
- package/dist/shared/single-use-token-store.js +5 -13
- package/dist/step-dispatcher/webhook-runner.d.ts +0 -2
- package/dist/step-dispatcher/webhook-runner.js +18 -17
- package/dist/subscription-mollie/plugin-methods.d.ts +3 -2
- package/dist/subscription-mollie/plugin-methods.js +7 -1
- package/dist/subscription-mollie/verify-webhook.js +6 -0
- package/dist/subscription-stripe/feature.js +3 -3
- package/dist/subscription-stripe/plugin-methods.js +25 -6
- package/dist/subscription-stripe/verify-webhook.js +13 -6
- package/dist/tags/entity.d.ts +9 -0
- package/dist/tags/handlers/assign-tag.write.js +4 -10
- package/dist/tags/handlers/remove-tag.write.js +4 -11
- package/dist/template-resolver/api.d.ts +2 -1
- package/dist/template-resolver/api.js +1 -1
- package/dist/template-resolver/handlers/by-slug.query.d.ts +1 -1
- package/dist/template-resolver/handlers/by-slug.query.js +2 -2
- package/dist/template-resolver/handlers/by-tenant.query.d.ts +3 -2
- package/dist/template-resolver/handlers/by-tenant.query.js +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 +3 -2
- package/dist/template-resolver/handlers/collection-shared.js +1 -1
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -1
- package/dist/template-resolver/handlers/find-by-id.query.js +1 -1
- package/dist/template-resolver/handlers/list.query.d.ts +1 -1
- package/dist/template-resolver/handlers/list.query.js +1 -1
- package/dist/template-resolver/seeding.js +41 -39
- package/dist/template-resolver/table.d.ts +26 -13
- package/dist/template-resolver/testing.js +2 -2
- package/dist/template-resolver/user-content-table.d.ts +16 -9
- package/dist/template-resolver/web/client-plugin.d.ts +1 -1
- package/dist/template-resolver-user-data/hooks.js +1 -1
- package/dist/tenant/cancel-pending-invitation.d.ts +8 -0
- package/dist/tenant/cancel-pending-invitation.js +22 -0
- package/dist/tenant/feature.js +4 -2
- package/dist/tenant/handlers/cancel-invitation.write.js +6 -21
- package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/member-directory.query.js +17 -4
- package/dist/tenant/handlers/remove-member.write.js +9 -14
- package/dist/tenant/handlers/team-list.query.js +2 -2
- package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/tenant-directory.query.js +13 -3
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update-member-roles.write.js +28 -12
- package/dist/tenant/i18n.js +4 -0
- package/dist/tenant/index.d.ts +1 -1
- package/dist/tenant/index.js +1 -1
- package/dist/tenant/invitation-table.d.ts +8 -2
- package/dist/tenant/last-tenant-admin.d.ts +1 -1
- package/dist/tenant/last-tenant-admin.js +5 -3
- package/dist/tenant/membership-table.d.ts +4 -0
- package/dist/tenant/schema/tenant.d.ts +8 -2
- package/dist/tenant/seeding.d.ts +4 -0
- package/dist/tenant/seeding.js +9 -2
- package/dist/tenant-lifecycle/boot-checks.d.ts +1 -0
- package/dist/tenant-lifecycle/boot-checks.js +27 -1
- package/dist/tenant-lifecycle/feature.js +2 -1
- package/dist/tenant-lifecycle/stages.js +2 -2
- package/dist/tenant-settings/feature.js +9 -6
- package/dist/tier-engine/entity.d.ts +2 -0
- package/dist/tier-engine/feature.js +3 -2
- package/dist/tier-engine/tier-resolver.d.ts +2 -0
- package/dist/user/command-schemas.d.ts +2 -2
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/create.write.js +8 -2
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/handlers/update.write.js +8 -2
- package/dist/user/schema/user.d.ts +14 -2
- package/dist/user-data-rights/db/queries/export-jobs.d.ts +2 -0
- package/dist/user-data-rights/feature.js +22 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +4 -7
- package/dist/user-data-rights/handlers/deletion-grace-period.d.ts +3 -2
- package/dist/user-data-rights/handlers/deletion-grace-period.js +30 -8
- package/dist/user-data-rights/handlers/download-by-token.query.js +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +3 -6
- package/dist/user-data-rights/lib/update-user-lifecycle.js +7 -14
- package/dist/user-data-rights/run-export-jobs.d.ts +1 -0
- package/dist/user-data-rights/run-export-jobs.js +74 -41
- package/dist/user-data-rights/run-forget-cleanup.js +2 -2
- package/dist/user-data-rights/run-user-export.js +8 -4
- package/dist/user-data-rights/schema/download-attempt.d.ts +14 -0
- package/dist/user-data-rights/schema/download-token.d.ts +8 -0
- package/dist/user-data-rights/schema/export-job.d.ts +8 -2
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +4 -1
- package/dist/user-profile/feature.js +6 -1
- package/dist/user-profile/handlers/change-email.write.js +2 -0
- package/dist/workflow-runner/db/queries/due-runs.js +6 -9
- package/dist/workflow-runner/event-subscriber.js +29 -9
- package/dist/workflow-runner/handlers/resume-run.write.js +8 -5
- package/dist/workflow-runner/pending-projection.js +8 -1
- package/package.json +11 -9
- package/src/agent-tools/changes.json +12 -0
- package/src/auth-email-password/changes.json +18 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +7 -0
- package/src/cap-counter/changes.json +7 -0
- package/src/cap-overview/changes.json +12 -0
- package/src/channel-email/changes.json +6 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/data-retention/changes.json +12 -0
- package/src/delivery/changes.json +7 -0
- package/src/derivatives-sharp/changes.json +11 -0
- package/src/document-ingest-foundation/changes.json +12 -0
- package/src/inbound-mail-foundation/changes.json +12 -0
- package/src/jobs/changes.json +13 -0
- package/src/legal-pages/changes.json +8 -1
- package/src/managed-pages/changes.json +5 -0
- package/src/metrics/changes.json +6 -0
- package/src/rate-limiting/changes.json +5 -0
- package/src/secrets/changes.json +7 -0
- package/src/sessions/changes.json +5 -0
- package/src/shared/changes.json +6 -0
- package/src/step-dispatcher/changes.json +5 -0
- package/src/subscription-mollie/changes.json +7 -0
- package/src/subscription-stripe/changes.json +11 -0
- package/src/template-resolver/changes.json +7 -0
- package/src/tenant/changes.json +29 -0
- package/src/user-data-rights/changes.json +22 -0
- package/src/workflow-runner/changes.json +6 -0
|
@@ -11,9 +11,14 @@ export const CAP_OVERVIEW_I18N = {
|
|
|
11
11
|
"cap-overview.cards.empty": { en: "No caps configured for this tenant." },
|
|
12
12
|
"cap-overview.cards.loading": { en: "Loading usage…" },
|
|
13
13
|
"cap-overview.notMeasured": { en: "Not measured yet" },
|
|
14
|
+
"cap-overview.unlimited": { en: "{used} · unlimited" },
|
|
14
15
|
"cap-overview.errors.progressPrimitiveMissing": {
|
|
15
16
|
en: "Usage bar unavailable — Progress primitive is not registered.",
|
|
16
17
|
},
|
|
18
|
+
"cap-overview.errors.invalidCursor": { en: "The pagination cursor is invalid." },
|
|
19
|
+
"cap-overview.errors.filterFieldUnsupported": {
|
|
20
|
+
en: "Filtering by this field is not supported, or tier was filtered more than once.",
|
|
21
|
+
},
|
|
17
22
|
"cap-overview.errors.sortFieldUnsupported": { en: "Sorting by this field is not supported." },
|
|
18
23
|
"cap-overview.errors.tenantOverrideRequiresSystemAdmin": {
|
|
19
24
|
en: "Only SystemAdmin may query another tenant's data.",
|
|
@@ -52,10 +52,13 @@ export function CapCardsPanel({ filterParams }) {
|
|
|
52
52
|
// rule exists, since that file IS in the scanned surface.
|
|
53
53
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", "data-testid": "cap-cards-panel", children: [query.loading && rows.length === 0 && (_jsx("div", { className: "text-sm text-muted-foreground", children: t("cap-overview.cards.loading") })), rows.map((cap) => {
|
|
54
54
|
const Icon = cap.icon !== undefined ? CAP_ICONS[cap.icon] : undefined;
|
|
55
|
+
// An unlimited, measured cap has neither a badge nor a bar; StatCard still
|
|
56
|
+
// wraps any defined child in a spacing div, so pass undefined then.
|
|
57
|
+
const body = !isMeasured(cap) || cap.limit !== null ? (_jsxs("div", { className: "flex flex-col gap-2", children: [cap.percent !== null && (_jsx("div", { className: "flex items-center justify-between gap-2", children: _jsx(StatusBadge, { tone: TONE_TO_STATUS[cap.tone], children: `${cap.percent}%` }) })), _jsx(CapUsageBar, { usage: cap, showLabel: false })] })) : undefined;
|
|
55
58
|
return (_jsx(StatCard, { icon: Icon !== undefined ? _jsx(Icon, { className: "size-4" }) : undefined, label: t(cap.label), value: !isMeasured(cap)
|
|
56
59
|
? "—"
|
|
57
60
|
: cap.limit === null
|
|
58
61
|
? String(cap.used)
|
|
59
|
-
: `${cap.used} / ${cap.limit}`, ...(cap.accentColor !== undefined && { accentColor: cap.accentColor }), testId: "cap-card", children:
|
|
62
|
+
: `${cap.used} / ${cap.limit}`, ...(cap.accentColor !== undefined && { accentColor: cap.accentColor }), testId: "cap-card", children: body }, cap.id));
|
|
60
63
|
})] }));
|
|
61
64
|
}
|
|
@@ -10,7 +10,7 @@ export function CapUsageBar({ usage, showLabel = true, }) {
|
|
|
10
10
|
}
|
|
11
11
|
if (usage.limit === null) {
|
|
12
12
|
// A progress bar with no denominator says nothing — show the raw count instead.
|
|
13
|
-
return showLabel ? (_jsx(Text, { variant: "small", testId: "cap-usage-unlimited", children:
|
|
13
|
+
return showLabel ? (_jsx(Text, { variant: "small", testId: "cap-usage-unlimited", children: t("cap-overview.unlimited", { used: usage.used }) })) : null;
|
|
14
14
|
}
|
|
15
15
|
return (_jsxs("div", { className: "flex flex-col gap-1", children: [Progress ? (_jsx(Progress, { value: usage.fraction, tone: computeTone(usage.fraction), testId: "cap-usage-bar" })) : (_jsx(Banner, { variant: "error", testId: "cap-usage-bar-missing-primitive", children: t("cap-overview.errors.progressPrimitiveMissing") })), showLabel && _jsx(Text, { variant: "small", children: `${usage.used} / ${usage.limit}` })] }));
|
|
16
16
|
}
|
|
@@ -15,6 +15,10 @@ function stringHeaders(raw) {
|
|
|
15
15
|
}
|
|
16
16
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
17
17
|
}
|
|
18
|
+
const UNSUBSCRIBE_HEADER_NAMES = new Set([
|
|
19
|
+
"list-unsubscribe",
|
|
20
|
+
"list-unsubscribe-post",
|
|
21
|
+
]);
|
|
18
22
|
// Only the framework's own unsubscribe route can honor a one-click POST.
|
|
19
23
|
function listUnsubscribeHeaders(data) {
|
|
20
24
|
const rawUrl = data?.["unsubscribeUrl"];
|
|
@@ -43,7 +47,14 @@ function emailEnvelopeFrom(data) {
|
|
|
43
47
|
const replyTo = typeof data["replyTo"] === "string" ? data["replyTo"] : undefined;
|
|
44
48
|
const autoHeaders = listUnsubscribeHeaders(data);
|
|
45
49
|
const explicitHeaders = stringHeaders(data["headers"]);
|
|
46
|
-
|
|
50
|
+
// Header names are case-insensitive, and the auto pair only works together:
|
|
51
|
+
// an explicit override of either half must drop both, or a mail client would
|
|
52
|
+
// one-click POST to a URL that cannot handle it.
|
|
53
|
+
const overridesUnsubscribe = explicitHeaders !== undefined &&
|
|
54
|
+
Object.keys(explicitHeaders).some((name) => UNSUBSCRIBE_HEADER_NAMES.has(name.toLowerCase()));
|
|
55
|
+
const headers = (!overridesUnsubscribe && autoHeaders) || explicitHeaders
|
|
56
|
+
? { ...(overridesUnsubscribe ? undefined : autoHeaders), ...explicitHeaders }
|
|
57
|
+
: undefined;
|
|
47
58
|
return { ...(from && { from }), ...(replyTo && { replyTo }), ...(headers && { headers }) };
|
|
48
59
|
}
|
|
49
60
|
export function createEmailChannel(options) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireEntityTableMeta } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { boolean, instant, table as pgTable, serial, sql, text, uuid, } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
export const inAppMessagesTable = pgTable("in_app_messages", {
|
|
4
4
|
id: serial("id").primaryKey(),
|
|
@@ -14,8 +14,4 @@ export const inAppMessagesTable = pgTable("in_app_messages", {
|
|
|
14
14
|
});
|
|
15
15
|
// Derived from inAppMessagesTable (table() attaches the meta as a Symbol) so
|
|
16
16
|
// the two can't drift, instead of hand-duplicating the column list.
|
|
17
|
-
const
|
|
18
|
-
if (!derivedInAppMessagesTableMeta) {
|
|
19
|
-
throw new Error("inAppMessagesTable: table carries no EntityTableMeta — built via table()?");
|
|
20
|
-
}
|
|
21
|
-
export const inAppMessagesTableMeta = derivedInAppMessagesTableMeta;
|
|
17
|
+
export const inAppMessagesTableMeta = requireEntityTableMeta(inAppMessagesTable, "inAppMessagesTable");
|
|
@@ -19,7 +19,7 @@ export const subProcessorsQuery = defineQueryHandler({
|
|
|
19
19
|
name: "sub-processors",
|
|
20
20
|
schema: z.object({}),
|
|
21
21
|
access: { roles: ["anonymous", "Member", "User", "TenantAdmin", "SystemAdmin"] },
|
|
22
|
-
rateLimit: { per: "ip", limit: 30, windowSeconds: 60 },
|
|
22
|
+
rateLimit: { per: "ip+handler", limit: 30, windowSeconds: 60 },
|
|
23
23
|
description: "Returns the publicly readable Kumiko sub-processor list split into active and planned entries, for the GDPR Art. 28(2) disclosure a privacy policy or data-processing agreement links to.",
|
|
24
24
|
handler: async () => {
|
|
25
25
|
return {
|
|
@@ -17,7 +17,7 @@ export declare const tenantComplianceProfileEntity: import("@cosmicdrift/kumiko-
|
|
|
17
17
|
readonly "swiss-dsg"?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
|
|
18
18
|
} | undefined;
|
|
19
19
|
readonly optionsQuery?: string;
|
|
20
|
-
readonly optionsQueryPayload?:
|
|
20
|
+
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
21
21
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
22
22
|
required: true;
|
|
23
23
|
};
|
|
@@ -57,7 +57,7 @@ export declare const tenantComplianceProfileTable: import("@cosmicdrift/kumiko-f
|
|
|
57
57
|
readonly "swiss-dsg"?: import("@cosmicdrift/kumiko-types/fields").SelectOptionTone | undefined;
|
|
58
58
|
} | undefined;
|
|
59
59
|
readonly optionsQuery?: string;
|
|
60
|
-
readonly optionsQueryPayload?:
|
|
60
|
+
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
61
61
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
62
62
|
required: true;
|
|
63
63
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { configValuesTable } from "../../table.js";
|
|
3
|
-
// Method-form so a tenant-mode TenantDb stays filtered.
|
|
3
|
+
// Method-form so a tenant-mode TenantDb stays filtered. Two parallel selects
|
|
4
|
+
// (disjoint via userId IS NULL) instead of one OR query: the buckets don't share
|
|
5
|
+
// a snapshot, so a concurrent config write can mix old scope and new user values.
|
|
4
6
|
export async function selectConfigRowsForScope(db, systemTenantId, tenantId, userId) {
|
|
5
7
|
const [scopeRows, userRows] = await Promise.all([
|
|
6
8
|
selectMany(db, configValuesTable, {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
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
|
-
import { ConfigScopes, defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { ConfigScopes, defineWriteHandler, isEncryptedAtRest, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import { InternalError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { requireConfigEncryption, requireSystemDb } from "../feature.js";
|
|
8
|
+
import { MASKED } from "../read-redaction.js";
|
|
8
9
|
import { configValueEntity, configValuesTable } from "../table.js";
|
|
9
10
|
import { findConfigRow, prepareConfigWrite, resolvePiiSubject, validateBounds, validateExtensionPlugin, validatePattern, validateScope, validateType, } from "../write-helpers.js";
|
|
10
11
|
const scopeEnum = z.enum([ConfigScopes.system, ConfigScopes.tenant, ConfigScopes.user]);
|
|
@@ -53,7 +54,7 @@ export const setWrite = defineWriteHandler({
|
|
|
53
54
|
const typeError = validateType(event.payload.value, keyDef);
|
|
54
55
|
if (typeError)
|
|
55
56
|
return writeFailure(typeError);
|
|
56
|
-
const pluginError = validateExtensionPlugin(ctx.registry, event.payload.key, event.payload.value);
|
|
57
|
+
const pluginError = validateExtensionPlugin(ctx.registry, event.payload.key, event.payload.value, keyDef);
|
|
57
58
|
if (pluginError)
|
|
58
59
|
return writeFailure(pluginError);
|
|
59
60
|
// Bounds enforcement: hard-reject (not silent-clamp). A caller that
|
|
@@ -66,6 +67,9 @@ export const setWrite = defineWriteHandler({
|
|
|
66
67
|
const patternError = validatePattern(event.payload.value, keyDef);
|
|
67
68
|
if (patternError)
|
|
68
69
|
return writeFailure(patternError);
|
|
70
|
+
// The batch idempotency cache stores this result and only masks entity
|
|
71
|
+
// writeOnly fields, so an at-rest-encrypted value must not be echoed.
|
|
72
|
+
const echoedValue = isEncryptedAtRest(keyDef) ? MASKED : event.payload.value;
|
|
69
73
|
// backing="secrets": persist into the secrets store (system tenant, own
|
|
70
74
|
// envelope encryption + audit) instead of config_values. Same JSON
|
|
71
75
|
// serialization as a config row so the read path round-trips via
|
|
@@ -82,7 +86,7 @@ export const setWrite = defineWriteHandler({
|
|
|
82
86
|
});
|
|
83
87
|
return {
|
|
84
88
|
isSuccess: true,
|
|
85
|
-
data: { key: event.payload.key, value:
|
|
89
|
+
data: { key: event.payload.key, value: echoedValue, scope },
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
let serialized = JSON.stringify(event.payload.value);
|
|
@@ -139,7 +143,7 @@ export const setWrite = defineWriteHandler({
|
|
|
139
143
|
}
|
|
140
144
|
return {
|
|
141
145
|
isSuccess: true,
|
|
142
|
-
data: { key: event.payload.key, value:
|
|
146
|
+
data: { key: event.payload.key, value: echoedValue, scope },
|
|
143
147
|
};
|
|
144
148
|
},
|
|
145
149
|
});
|
package/dist/config/table.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
9
9
|
readonly filterable?: boolean;
|
|
10
10
|
readonly encrypted?: boolean;
|
|
11
11
|
readonly sensitive?: boolean;
|
|
12
|
+
readonly writeOnly?: boolean;
|
|
12
13
|
readonly readAsInstruction?: boolean;
|
|
13
14
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
14
15
|
readonly default?: string;
|
|
@@ -29,6 +30,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
29
30
|
readonly filterable?: boolean;
|
|
30
31
|
readonly encrypted?: boolean;
|
|
31
32
|
readonly sensitive?: boolean;
|
|
33
|
+
readonly writeOnly?: boolean;
|
|
32
34
|
readonly readAsInstruction?: boolean;
|
|
33
35
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
34
36
|
readonly default?: string;
|
|
@@ -49,6 +51,7 @@ export declare const configValueEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
49
51
|
readonly filterable?: boolean;
|
|
50
52
|
readonly encrypted?: boolean;
|
|
51
53
|
readonly sensitive?: boolean;
|
|
54
|
+
readonly writeOnly?: boolean;
|
|
52
55
|
readonly readAsInstruction?: boolean;
|
|
53
56
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
54
57
|
readonly default?: string;
|
|
@@ -71,6 +74,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
|
|
|
71
74
|
readonly filterable?: boolean;
|
|
72
75
|
readonly encrypted?: boolean;
|
|
73
76
|
readonly sensitive?: boolean;
|
|
77
|
+
readonly writeOnly?: boolean;
|
|
74
78
|
readonly readAsInstruction?: boolean;
|
|
75
79
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
76
80
|
readonly default?: string;
|
|
@@ -91,6 +95,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
|
|
|
91
95
|
readonly filterable?: boolean;
|
|
92
96
|
readonly encrypted?: boolean;
|
|
93
97
|
readonly sensitive?: boolean;
|
|
98
|
+
readonly writeOnly?: boolean;
|
|
94
99
|
readonly readAsInstruction?: boolean;
|
|
95
100
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
96
101
|
readonly default?: string;
|
|
@@ -111,6 +116,7 @@ export declare const configValuesTable: import("@cosmicdrift/kumiko-framework/db
|
|
|
111
116
|
readonly filterable?: boolean;
|
|
112
117
|
readonly encrypted?: boolean;
|
|
113
118
|
readonly sensitive?: boolean;
|
|
119
|
+
readonly writeOnly?: boolean;
|
|
114
120
|
readonly readAsInstruction?: boolean;
|
|
115
121
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
116
122
|
readonly default?: string;
|
package/dist/config/web/i18n.js
CHANGED
|
@@ -8,11 +8,15 @@
|
|
|
8
8
|
// for ITS keys (mask.title) and the per-feature group key `<feature>.settings`.
|
|
9
9
|
export const defaultTranslations = {
|
|
10
10
|
en: {
|
|
11
|
-
"config.secrets.delete": "
|
|
11
|
+
"config.secrets.delete": "Remove",
|
|
12
|
+
"config.secrets.deleteConfirm": "Remove this secret? Anything using it stops working, and it cannot be restored.",
|
|
12
13
|
"config.secrets.notSet": "Not set",
|
|
13
14
|
"config.secrets.placeholder": "Enter a value",
|
|
14
|
-
"config.secrets.replacePlaceholder": "Enter a new value to replace",
|
|
15
|
-
"config.secrets.
|
|
15
|
+
"config.secrets.replacePlaceholder": "Enter a new value to replace it",
|
|
16
|
+
"config.secrets.saved": "Saved",
|
|
17
|
+
"config.secrets.section": "Secrets",
|
|
18
|
+
"config.secrets.description": "Stored encrypted. Saved values are never shown again.",
|
|
19
|
+
"config.secrets.stored": "Stored: {preview}",
|
|
16
20
|
"config.secrets.set": "Set",
|
|
17
21
|
"config.secrets.title": "Secrets",
|
|
18
22
|
"config.settings.title": "Settings",
|
|
@@ -23,7 +27,12 @@ export const defaultTranslations = {
|
|
|
23
27
|
"config.errors.invalidScope": "This scope is not allowed for this key.",
|
|
24
28
|
"config.errors.unknownKey": "Unknown configuration key.",
|
|
25
29
|
"config.errors.unknownExtensionPlugin": "This provider is not available.",
|
|
26
|
-
"config.settings.extensionSelectorHint": "
|
|
30
|
+
"config.settings.extensionSelectorHint": "Choose a provider and save. Its settings appear below.",
|
|
31
|
+
"config.settings.provider": "Provider",
|
|
32
|
+
"config.settings.saveProvider": "Save provider",
|
|
33
|
+
"config.settings.audience.system": "Applies to every tenant unless a tenant sets its own value.",
|
|
34
|
+
"config.settings.audience.tenant": "Applies to everyone in this tenant. Platform defaults are shown where nothing is set here.",
|
|
35
|
+
"config.settings.audience.user": "Only applies to you.",
|
|
27
36
|
// Required by every generated screen (screenTitleKey, required-surface-keys.ts) —
|
|
28
37
|
// the secrets screen has a fixed id ("secrets"), so the framework ships its
|
|
29
38
|
// title translation directly instead of asking every app to declare it.
|
|
@@ -38,7 +38,7 @@ export declare function resolveScopeIds(scope: ConfigScope, tenantId: TenantId,
|
|
|
38
38
|
userId: string | null;
|
|
39
39
|
};
|
|
40
40
|
export declare function resolvePiiSubject(scope: Exclude<ConfigScope, "system">, tenantId: TenantId, userId: string | null): SubjectId;
|
|
41
|
-
export declare function validateExtensionPlugin(registry: Registry, key: string, value: string | number | boolean): KumikoError | null;
|
|
41
|
+
export declare function validateExtensionPlugin(registry: Pick<Registry, "getAllExtensionSelectors" | "getExtensionUsages">, key: string, value: string | number | boolean, keyDef: ConfigKeyDefinition): KumikoError | null;
|
|
42
42
|
export declare function validateType(value: string | number | boolean, keyDef: ConfigKeyDefinition): KumikoError | null;
|
|
43
43
|
export declare function validateBounds(value: string | number | boolean, keyDef: ConfigKeyDefinition): KumikoError | null;
|
|
44
44
|
export declare function validatePattern(value: string | number | boolean, keyDef: ConfigKeyDefinition): KumikoError | null;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Extracted from set.write.ts so reset.write.ts + values.query.ts don't
|
|
3
3
|
// have to cross-import from another handler file.
|
|
4
4
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
5
|
-
import { ConfigScopes, extensionSelectorTargets, SYSTEM_ROLE, SYSTEM_TENANT_ID, selectablePluginIds, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
+
import { ConfigScopes, extensionSelectorTargets, isEncryptedAtRest, SYSTEM_ROLE, SYSTEM_TENANT_ID, selectablePluginIds, } from "@cosmicdrift/kumiko-framework/engine";
|
|
6
6
|
import { AccessDeniedError, InternalError, NotFoundError, UnprocessableError, ValidationError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
7
7
|
import { assertUnreachable } from "@cosmicdrift/kumiko-framework/utils";
|
|
8
8
|
import { ConfigErrors } from "./constants.js";
|
|
@@ -134,9 +134,14 @@ export function resolvePiiSubject(scope, tenantId, userId) {
|
|
|
134
134
|
}
|
|
135
135
|
return { kind: "tenant", tenantId };
|
|
136
136
|
}
|
|
137
|
+
// An encrypted value must not travel back in an error (logs, idempotency cache),
|
|
138
|
+
// so rejection details spread this instead of naming `value` directly.
|
|
139
|
+
function echoableValue(keyDef, value) {
|
|
140
|
+
return isEncryptedAtRest(keyDef) ? {} : { value };
|
|
141
|
+
}
|
|
137
142
|
// An extension-selector key may only name a mounted plugin (or "" to clear) —
|
|
138
143
|
// otherwise the settings screen would show no panel for a typo'd value.
|
|
139
|
-
export function validateExtensionPlugin(registry, key, value) {
|
|
144
|
+
export function validateExtensionPlugin(registry, key, value, keyDef) {
|
|
140
145
|
const extensionName = extensionSelectorTargets(registry).get(key);
|
|
141
146
|
if (extensionName === undefined || value === "")
|
|
142
147
|
return null;
|
|
@@ -145,7 +150,7 @@ export function validateExtensionPlugin(registry, key, value) {
|
|
|
145
150
|
return null;
|
|
146
151
|
return new UnprocessableError(ConfigErrors.unknownExtensionPlugin, {
|
|
147
152
|
i18nKey: "config.errors.unknownExtensionPlugin",
|
|
148
|
-
details: { key, value, allowed },
|
|
153
|
+
details: { key, ...echoableValue(keyDef, value), allowed },
|
|
149
154
|
});
|
|
150
155
|
}
|
|
151
156
|
export function validateType(value, keyDef) {
|
|
@@ -172,7 +177,7 @@ export function validateType(value, keyDef) {
|
|
|
172
177
|
path: "value",
|
|
173
178
|
code: "invalid_option",
|
|
174
179
|
i18nKey: "errors.validation.invalid_option",
|
|
175
|
-
params: { value, options: keyDef.options },
|
|
180
|
+
params: { ...echoableValue(keyDef, value), options: keyDef.options },
|
|
176
181
|
},
|
|
177
182
|
],
|
|
178
183
|
});
|
|
@@ -254,13 +259,14 @@ export function validatePattern(value, keyDef) {
|
|
|
254
259
|
}
|
|
255
260
|
if (re.test(value))
|
|
256
261
|
return null;
|
|
262
|
+
const echoedParams = { ...echoableValue(keyDef, value), pattern: keyDef.pattern.regex };
|
|
257
263
|
return new ValidationError({
|
|
258
264
|
fields: [
|
|
259
265
|
{
|
|
260
266
|
path: "value",
|
|
261
267
|
code: "invalid_format",
|
|
262
268
|
i18nKey: "errors.validation.invalid_format",
|
|
263
|
-
params:
|
|
269
|
+
params: echoedParams,
|
|
264
270
|
},
|
|
265
271
|
],
|
|
266
272
|
});
|
|
@@ -133,6 +133,17 @@ getRunner) {
|
|
|
133
133
|
});
|
|
134
134
|
return effective.policy?.strategy === "blockDelete" ? deny() : undefined;
|
|
135
135
|
}
|
|
136
|
+
// AccessDeniedError.code is the generic "access_denied" for every branch;
|
|
137
|
+
// the specific branch lives in details.reason.
|
|
138
|
+
function denialReasonOf(failure) {
|
|
139
|
+
const details = failure.error.details;
|
|
140
|
+
if (typeof details === "object" && details !== null && "reason" in details) {
|
|
141
|
+
const { reason } = details;
|
|
142
|
+
if (typeof reason === "string")
|
|
143
|
+
return reason;
|
|
144
|
+
}
|
|
145
|
+
return failure.error.code;
|
|
146
|
+
}
|
|
136
147
|
// Denied cross-tenant probes must still leave an audit trail (fw#2348).
|
|
137
148
|
// Appended outside the handler tx (fw#2592): the caller's
|
|
138
149
|
// `return tenantScopeDenial` rolls that tx back.
|
|
@@ -228,18 +239,14 @@ export const forgetSubjectWrite = defineWriteHandler({
|
|
|
228
239
|
const subjectKey = subjectIdToKey(subject);
|
|
229
240
|
const tenantScopeDenial = await resolveTenantScopeDenial(ctx.db.unsafeRaw("tenant-scope check runs against the subject's tenant, not necessarily the caller's"), ctx.registry.features, event.user, raw);
|
|
230
241
|
if (tenantScopeDenial) {
|
|
231
|
-
const auditFailure = await appendDenialAuditEvent(ctx, event, subjectKey, raw.kind, tenantScopeDenial
|
|
242
|
+
const auditFailure = await appendDenialAuditEvent(ctx, event, subjectKey, raw.kind, denialReasonOf(tenantScopeDenial), ctx.dbOutsideTransaction?.unsafeRaw("denial audit append: names the prober's own tenant stream on the outside-transaction db"));
|
|
232
243
|
return auditFailure ?? tenantScopeDenial;
|
|
233
244
|
}
|
|
234
|
-
// Runs
|
|
235
|
-
//
|
|
236
|
-
//
|
|
245
|
+
// Runs after the tenant gate but before any key is touched: a retention
|
|
246
|
+
// check ahead of the tenant gate would leak a foreign entity's retention
|
|
247
|
+
// posture to a cross-tenant prober.
|
|
237
248
|
const retentionDenial = await resolveRetentionDenial(ctx, raw, () => ctx.db.unsafeRaw("retention check reads the record row's owning tenant, not the caller's"));
|
|
238
249
|
if (retentionDenial) {
|
|
239
|
-
// Own reason constant, not tenantScopeDenial's `.error.code` pattern:
|
|
240
|
-
// AccessDeniedError.code is the generic "access_denied" for every
|
|
241
|
-
// branch, so re-deriving it here would make a blockDelete refusal
|
|
242
|
-
// indistinguishable from a cross-tenant one in the audit trail.
|
|
243
250
|
const auditFailure = await appendDenialAuditEvent(ctx, event, subjectKey, raw.kind, TARGET_RECORD_RETENTION_BLOCK_DELETE, ctx.dbOutsideTransaction?.unsafeRaw("denial audit append: names the prober's own tenant stream on the outside-transaction db"));
|
|
244
251
|
return auditFailure ?? retentionDenial;
|
|
245
252
|
}
|
|
@@ -19,10 +19,7 @@ export const CustomFieldsHandlers = {
|
|
|
19
19
|
export const CustomFieldsQueries = {
|
|
20
20
|
fieldDefinitionList: "custom-fields:query:field-definition:list",
|
|
21
21
|
};
|
|
22
|
-
// Name the web component registers under in ExtensionSectionsProvider
|
|
23
|
-
// apps reference it in the screen schema via:
|
|
24
|
-
// { kind: "extension", title, contributesToFormSubmit: true,
|
|
25
|
-
// component: { react: { __component: CUSTOM_FIELDS_FORM_EXTENSION_NAME } } }
|
|
22
|
+
// Name the web component registers under in ExtensionSectionsProvider.
|
|
26
23
|
export const CUSTOM_FIELDS_FORM_EXTENSION_NAME = "CustomFieldsFormSection";
|
|
27
24
|
// Entity-CRUD auto-events for the `field-definition` entity. registry.ts emits
|
|
28
25
|
// these as `${entityName}.created`/`.updated` (dot form) — they do NOT run
|
|
@@ -9,6 +9,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
9
9
|
readonly filterable?: boolean;
|
|
10
10
|
readonly encrypted?: boolean;
|
|
11
11
|
readonly sensitive?: boolean;
|
|
12
|
+
readonly writeOnly?: boolean;
|
|
12
13
|
readonly readAsInstruction?: boolean;
|
|
13
14
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
14
15
|
readonly default?: string;
|
|
@@ -29,6 +30,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
29
30
|
readonly filterable?: boolean;
|
|
30
31
|
readonly encrypted?: boolean;
|
|
31
32
|
readonly sensitive?: boolean;
|
|
33
|
+
readonly writeOnly?: boolean;
|
|
32
34
|
readonly readAsInstruction?: boolean;
|
|
33
35
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
34
36
|
readonly default?: string;
|
|
@@ -49,6 +51,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
49
51
|
readonly filterable?: boolean;
|
|
50
52
|
readonly encrypted?: boolean;
|
|
51
53
|
readonly sensitive?: boolean;
|
|
54
|
+
readonly writeOnly?: boolean;
|
|
52
55
|
readonly readAsInstruction?: boolean;
|
|
53
56
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
54
57
|
readonly default?: string;
|
|
@@ -94,6 +97,7 @@ export declare const fieldDefinitionEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
94
97
|
readonly filterable?: boolean;
|
|
95
98
|
readonly encrypted?: boolean;
|
|
96
99
|
readonly sensitive?: boolean;
|
|
100
|
+
readonly writeOnly?: boolean;
|
|
97
101
|
readonly readAsInstruction?: boolean;
|
|
98
102
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
99
103
|
readonly default?: string;
|
|
@@ -10,9 +10,8 @@ export interface ResolveForTenantArgs {
|
|
|
10
10
|
/**
|
|
11
11
|
* Layer 2 — Tenant-Preset. `undefined` (the default) means this resolver
|
|
12
12
|
* derives it itself via resolveTenantRetentionPreset — one compliance-
|
|
13
|
-
* profile read per call, fine for single-lookup callers (`policy-for
|
|
14
|
-
*
|
|
15
|
-
* iterating N entities × M tenants (the forget-cleanup runner, the
|
|
13
|
+
* profile read per call, fine for single-lookup callers (`policy-for`). A
|
|
14
|
+
* bulk caller iterating N entities × M tenants (the forget-cleanup runner, the
|
|
16
15
|
* retention-cleanup cron) MUST resolve it ONCE per tenant and pass it in
|
|
17
16
|
* here instead, or it re-derives per entity. `null` means "resolved
|
|
18
17
|
* already, no preset applies" — the resolver falls back to Entity-Default
|