@cosmicdrift/kumiko-bundled-features 0.307.0 → 0.309.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -9
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +107 -0
- package/src/__tests__/access-invalidation-stream.integration.test.ts +1 -1
- package/src/__tests__/credential-scoped-sse-revoke.integration.test.ts +296 -0
- package/src/agent-tools/__tests__/agent-doc-lint.test.ts +1 -1
- package/src/agent-tools/__tests__/agent-manifest.test.ts +1 -1
- package/src/agent-tools/__tests__/agent-optout.test.ts +1 -1
- package/src/agent-tools/__tests__/agent-tools.integration.test.ts +1 -1
- package/src/agent-tools/__tests__/feature.test.ts +1 -1
- package/src/agent-tools/__tests__/tool-catalog.test.ts +1 -1
- package/src/agent-tools/agent-manifest.ts +1 -1
- package/src/audit/__tests__/escape-hatch-audit.integration.test.ts +1 -1
- package/src/audit/escape-hatch-audit-sink.ts +1 -1
- package/src/audit/handlers/details.query.ts +1 -1
- package/src/audit/handlers/list.query.ts +1 -1
- package/src/auth-email-password/__tests__/query-as-member.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/seed-admin-guarded.integration.test.ts +31 -0
- package/src/auth-email-password/changes.json +6 -0
- package/src/auth-email-password/feature.ts +1 -1
- package/src/auth-email-password/handlers/change-password.write.ts +1 -1
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +1 -1
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/src/auth-email-password/handlers/invite-accept.write.ts +1 -1
- package/src/auth-email-password/handlers/invite-create.write.ts +1 -1
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +1 -1
- package/src/auth-email-password/handlers/login.write.ts +1 -1
- package/src/auth-email-password/handlers/logout.write.ts +1 -1
- package/src/auth-email-password/handlers/reset-password.write.ts +1 -1
- package/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/src/auth-email-password/handlers/signup-confirm.write.ts +1 -1
- package/src/auth-email-password/handlers/signup-request.write.ts +1 -1
- package/src/auth-email-password/handlers/token-request-handler.ts +1 -1
- package/src/auth-email-password/handlers/verify-email.write.ts +1 -1
- package/src/auth-email-password/password-policy.ts +1 -1
- package/src/auth-email-password/seeding.ts +16 -3
- package/src/auth-mfa/__tests__/session-scoped-sse-revoke.integration.test.ts +1 -1
- package/src/auth-mfa/handlers/disable.write.ts +1 -1
- package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -1
- package/src/auth-mfa/handlers/enable-confirm.write.ts +1 -1
- package/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/src/auth-mfa/handlers/enable-start.write.ts +1 -1
- package/src/auth-mfa/handlers/regenerate-recovery.write.ts +1 -1
- package/src/auth-mfa/handlers/status.query.ts +1 -1
- package/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/src/billing-foundation/billing-info-query.ts +1 -1
- package/src/billing-foundation/events.ts +1 -1
- package/src/billing-foundation/handlers/create-checkout-session.write.ts +1 -1
- package/src/billing-foundation/handlers/create-portal-session.write.ts +1 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
- package/src/billing-foundation/handlers/process-event.write.ts +1 -1
- package/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/src/cap-counter/__tests__/cap-counter.integration.test.ts +1 -1
- package/src/cap-counter/__tests__/stock-cap-guard.integration.test.ts +1 -1
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +1 -1
- package/src/cap-counter/book-cap-usage.ts +1 -1
- package/src/cap-counter/handlers/get-counter.query.ts +1 -1
- package/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
- package/src/cap-counter/handlers/increment.write.ts +1 -1
- package/src/cap-counter/handlers/mark-soft-warned.write.ts +1 -1
- package/src/cap-overview/handlers/caps-usage.query.ts +1 -1
- package/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/src/cap-overview/handlers/tenant-options.query.ts +1 -1
- package/src/channel-in-app/handlers/inbox.query.ts +1 -1
- package/src/channel-in-app/handlers/mark-all-read.write.ts +1 -1
- package/src/channel-in-app/handlers/mark-read.write.ts +1 -1
- package/src/channel-in-app/handlers/unread-count.query.ts +1 -1
- package/src/compliance-profiles/handlers/for-tenant.query.ts +1 -1
- package/src/compliance-profiles/handlers/list-profiles.query.ts +1 -1
- package/src/compliance-profiles/handlers/needs-profile.query.ts +1 -1
- package/src/compliance-profiles/handlers/set-profile.write.ts +1 -1
- package/src/compliance-profiles/handlers/sub-processors.query.ts +1 -1
- package/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +1 -1
- package/src/config/__tests__/backing-secrets.integration.test.ts +1 -1
- package/src/config/__tests__/config.integration.test.ts +1 -1
- package/src/config/__tests__/tz-resolution.integration.test.ts +1 -1
- package/src/config/handlers/cascade.query.ts +1 -1
- package/src/config/handlers/readiness.query.ts +1 -1
- package/src/config/handlers/reset.write.ts +1 -1
- package/src/config/handlers/schema.query.ts +1 -1
- package/src/config/handlers/set.write.ts +1 -1
- package/src/config/handlers/values.query.ts +1 -1
- package/src/crypto-shredding/handlers/forget-subject.write.ts +1 -1
- package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/cross-tenant-field-delete.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/cross-tenant-set-write.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/custom-fields.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/field-access.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/quota.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/retention.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +1 -1
- package/src/custom-fields/events.ts +1 -1
- package/src/custom-fields/feature.ts +1 -1
- package/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
- package/src/custom-fields/lib/value-schema.ts +1 -1
- package/src/custom-fields/schemas.ts +1 -1
- package/src/data-retention/__tests__/retention-cleanup-files.integration.test.ts +454 -0
- package/src/data-retention/changes.json +6 -0
- package/src/data-retention/feature.ts +1 -0
- package/src/data-retention/handlers/policy-for.query.ts +1 -1
- package/src/data-retention/override-schema.ts +1 -1
- package/src/data-retention/run-retention-cleanup.ts +312 -5
- package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +1 -1
- package/src/delivery/__tests__/delivery.integration.test.ts +1 -1
- package/src/delivery/events.ts +1 -1
- package/src/delivery/feature.ts +1 -1
- package/src/delivery/handlers/log.query.ts +1 -1
- package/src/delivery/handlers/preferences.query.ts +1 -1
- package/src/delivery/handlers/set-preference.write.ts +1 -1
- package/src/delivery/jobs.ts +1 -1
- package/src/delivery/unsubscribe.ts +1 -1
- package/src/derivatives-sharp/__tests__/derivatives-sharp.integration.test.ts +1 -1
- package/src/document-ingest-foundation/events.ts +1 -1
- package/src/document-ingest-foundation/feature.ts +1 -1
- package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +1 -1
- package/src/feature-toggles/events.ts +1 -1
- package/src/feature-toggles/handlers/list.query.ts +1 -1
- package/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/src/feature-toggles/handlers/set.write.ts +1 -1
- package/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/src/file-foundation/__tests__/file-foundation.integration.test.ts +1 -1
- package/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +1 -1
- package/src/folders/handlers/delete-folder.write.ts +1 -1
- package/src/folders/schemas.ts +1 -1
- package/src/form-draft/schemas.ts +1 -1
- package/src/inbound-mail-foundation/events.ts +1 -1
- package/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -1
- package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
- package/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
- package/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
- package/src/inbound-provider-imap/credential-document.ts +1 -1
- package/src/jobs/__tests__/jobs-catalog.integration.test.ts +1 -1
- package/src/jobs/__tests__/tenant-job-failures.integration.test.ts +1 -1
- package/src/jobs/events.ts +1 -1
- package/src/jobs/handlers/catalog.query.ts +1 -1
- package/src/jobs/handlers/detail.query.ts +1 -1
- package/src/jobs/handlers/list.query.ts +1 -1
- package/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/src/jobs/handlers/retry.write.ts +1 -1
- package/src/jobs/handlers/tenant-failures.query.ts +1 -1
- package/src/jobs/handlers/trigger.write.ts +1 -1
- package/src/ledger/handlers/reports.query.ts +1 -1
- package/src/ledger/schemas.ts +1 -1
- package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +1 -1
- package/src/managed-pages/handlers/branding.query.ts +1 -1
- package/src/managed-pages/handlers/by-slug.query.ts +1 -1
- package/src/managed-pages/handlers/by-tenant-published.query.ts +1 -1
- package/src/managed-pages/handlers/set.write.ts +1 -1
- package/src/notes-history/changes.json +7 -0
- package/src/notes-history/schemas.ts +1 -1
- package/src/notes-history/web/notes-section.tsx +37 -36
- package/src/personal-access-tokens/feature.ts +10 -0
- package/src/personal-access-tokens/handlers/available-scopes.query.ts +1 -1
- package/src/personal-access-tokens/handlers/create.write.ts +1 -1
- package/src/personal-access-tokens/handlers/list.query.ts +1 -1
- package/src/personal-access-tokens/handlers/revoke.write.ts +18 -2
- package/src/personal-access-tokens/index.ts +7 -0
- package/src/personal-access-tokens/pat-revoked-event.ts +23 -0
- package/src/personal-access-tokens/revoke-for-user.ts +31 -0
- package/src/rate-limiting/__tests__/rate-limiting.integration.test.ts +1 -1
- package/src/rate-limiting/handlers/status.query.ts +1 -1
- package/src/readiness/handlers/status.query.ts +1 -1
- package/src/secrets/feature.ts +1 -1
- package/src/secrets/handlers/delete.write.ts +1 -1
- package/src/secrets/handlers/list.query.ts +1 -1
- package/src/secrets/handlers/set.write.ts +1 -1
- package/src/secrets/secrets-context.ts +1 -1
- package/src/seo/handlers/seo-config.query.ts +1 -1
- package/src/sessions/handlers/detail.query.ts +1 -1
- package/src/sessions/handlers/list.query.ts +1 -1
- package/src/sessions/handlers/mine.query.ts +1 -1
- package/src/sessions/handlers/revoke-all-for-user.write.ts +1 -1
- package/src/sessions/handlers/revoke-all-others.write.ts +1 -1
- package/src/sessions/handlers/revoke.write.ts +1 -1
- package/src/sessions/session-callbacks.ts +25 -1
- package/src/sessions/session-revoked-event.ts +6 -1
- package/src/shared/__tests__/row-bound-grant.integration.test.ts +1 -1
- package/src/step-dispatcher/mail-runner.ts +1 -1
- package/src/step-dispatcher/webhook-runner.ts +1 -1
- package/src/subscription-mollie/feature.ts +1 -1
- package/src/subscription-stripe/feature.ts +1 -1
- package/src/tags/schemas.ts +1 -1
- package/src/template-resolver/handlers/by-slug.query.ts +1 -1
- package/src/template-resolver/handlers/by-tenant.query.ts +1 -1
- package/src/template-resolver/handlers/collection-item.query.ts +1 -1
- package/src/template-resolver/handlers/collection-list.query.ts +1 -1
- package/src/template-resolver/handlers/collection-set.write.ts +1 -1
- package/src/template-resolver/handlers/find-by-id.query.ts +1 -1
- package/src/template-resolver/handlers/list.query.ts +1 -1
- package/src/template-resolver/handlers/set.write.ts +1 -1
- package/src/template-resolver/handlers/shared.ts +1 -1
- package/src/template-resolver/handlers/toggle-status.write.ts +1 -1
- package/src/template-resolver/handlers/upsert-tenant.write.ts +1 -1
- package/src/tenant/__tests__/tenant-timezone-boot.integration.test.ts +1 -1
- package/src/tenant/handlers/active-tenant-ids.query.ts +1 -1
- package/src/tenant/handlers/add-member.write.ts +1 -1
- package/src/tenant/handlers/cancel-invitation.write.ts +1 -1
- package/src/tenant/handlers/create.write.ts +1 -1
- package/src/tenant/handlers/invitations.query.ts +1 -1
- package/src/tenant/handlers/list.query.ts +1 -1
- package/src/tenant/handlers/me.query.ts +1 -1
- package/src/tenant/handlers/member-directory.query.ts +1 -1
- package/src/tenant/handlers/members.query.ts +1 -1
- package/src/tenant/handlers/memberships.query.ts +1 -1
- package/src/tenant/handlers/remove-member.write.ts +1 -1
- package/src/tenant/handlers/resolve-user-ids.query.ts +1 -1
- package/src/tenant/handlers/team-list.query.ts +1 -1
- package/src/tenant/handlers/tenant-directory.query.ts +1 -1
- package/src/tenant/handlers/toggle-enabled.write.ts +1 -1
- package/src/tenant/handlers/update-member-roles.write.ts +1 -1
- package/src/tenant/handlers/update.write.ts +1 -1
- package/src/tenant-handover/events.ts +1 -1
- package/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +8 -24
- package/src/tenant-lifecycle/events.ts +1 -1
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +1 -1
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -1
- package/src/tenant-settings/tenant-defaults.ts +1 -1
- package/src/tier-engine/__tests__/tier-resolver.integration.test.ts +1 -1
- package/src/tier-engine/feature.ts +1 -1
- package/src/tier-engine/handlers/active-tier.query.ts +1 -1
- package/src/tier-engine/handlers/get-tenant-tier.query.ts +1 -1
- package/src/tier-engine/handlers/set-tenant-tier.write.ts +1 -1
- package/src/user/__tests__/user-global-tenancy.integration.test.ts +1 -1
- package/src/user/handlers/create.write.ts +1 -1
- package/src/user/handlers/find-for-auth.query.ts +1 -1
- package/src/user/handlers/me.query.ts +1 -1
- package/src/user/handlers/update.write.ts +1 -1
- package/src/user/roles-input-schema.ts +1 -1
- package/src/user/seeding.ts +29 -20
- package/src/user-data-rights/__tests__/boot-checks.test.ts +1 -1
- package/src/user-data-rights/changes.json +6 -0
- package/src/user-data-rights/feature.ts +1 -0
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +1 -1
- package/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
- package/src/user-data-rights/handlers/download-by-job.query.ts +1 -1
- package/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
- package/src/user-data-rights/handlers/export-status.query.ts +1 -1
- package/src/user-data-rights/handlers/lift-restriction.write.ts +1 -1
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -1
- package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -1
- package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +1 -1
- package/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
- package/src/user-data-rights/handlers/request-export.write.ts +1 -1
- package/src/user-data-rights/handlers/restrict-account.write.ts +1 -1
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -1
- package/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +8 -61
- package/src/user-profile/handlers/change-email.write.ts +1 -1
- package/src/workflow-runner/__tests__/pending-projection.integration.test.ts +1 -1
- package/src/workflow-runner/handlers/resume-run.write.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod";
|
|
4
4
|
import { parseRetentionOverrideOrNull } from "../_internal/parse-override";
|
|
5
5
|
import { type EffectiveRetentionPolicy, resolveRetentionPolicy } from "../resolver";
|
|
6
6
|
import { tenantRetentionOverrideTable } from "../schema/tenant-retention-override";
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
// Strategy auf Rows an deren reference-Timestamp aelter als der keepFor-Cutoff
|
|
7
7
|
// ist:
|
|
8
8
|
//
|
|
9
|
-
// - hardDelete → executor.forget pro Row (Event → rebuild-safe hard purge)
|
|
9
|
+
// - hardDelete → executor.forget pro Row (Event → rebuild-safe hard purge).
|
|
10
|
+
// Entities with file/image/files/images fields: bytes first
|
|
11
|
+
// (incl. derivatives), then the fileRef row(s), then the row;
|
|
12
|
+
// shared fileRefs (other owner, other row of the same table)
|
|
13
|
+
// stay untouched.
|
|
10
14
|
// - softDelete → executor.delete pro Row (Event → rebuild-safe soft-delete),
|
|
11
15
|
// nur auf noch-nicht-geloeschte (isDeleted:false-Filter)
|
|
12
16
|
// - anonymize → executor.update pro Row mit den Werten der per-Feld
|
|
@@ -39,14 +43,22 @@ import {
|
|
|
39
43
|
createEventStoreExecutor,
|
|
40
44
|
createTenantDb,
|
|
41
45
|
type DbRunner,
|
|
46
|
+
type TenantDb,
|
|
42
47
|
type WhereObject,
|
|
43
48
|
} from "@cosmicdrift/kumiko-framework/db";
|
|
49
|
+
import { deleteStoredFileAndDerivatives } from "@cosmicdrift/kumiko-framework/derivatives";
|
|
44
50
|
import {
|
|
45
51
|
createSystemUser,
|
|
46
52
|
type EntityId,
|
|
47
53
|
type Registry,
|
|
54
|
+
type SessionUser,
|
|
48
55
|
type TenantId,
|
|
49
56
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
57
|
+
import {
|
|
58
|
+
type FileContext,
|
|
59
|
+
fileRefEntity,
|
|
60
|
+
fileRefsTable,
|
|
61
|
+
} from "@cosmicdrift/kumiko-framework/files";
|
|
50
62
|
import { computeCutoff, type Instant } from "./keep-for";
|
|
51
63
|
import type { RetentionPresetKey } from "./presets";
|
|
52
64
|
import { resolveRetentionPolicyForTenant } from "./resolve-for-tenant";
|
|
@@ -76,6 +88,10 @@ export interface RunRetentionCleanupArgs {
|
|
|
76
88
|
/** Now-Injection — Tests pinnen den Wert ohne Date-Mock (Pattern keep-for.ts). */
|
|
77
89
|
readonly now: Instant;
|
|
78
90
|
readonly batchLimit?: number;
|
|
91
|
+
/** Storage access for hardDelete entities with file/image/files/images fields.
|
|
92
|
+
* Missing while fileRefs are due → the row stays (skipped "missing_file_storage"),
|
|
93
|
+
* fail-closed instead of orphaning bytes. */
|
|
94
|
+
readonly files?: FileContext;
|
|
79
95
|
}
|
|
80
96
|
|
|
81
97
|
export interface RetentionCleanupSkip {
|
|
@@ -83,7 +99,9 @@ export interface RetentionCleanupSkip {
|
|
|
83
99
|
readonly reason:
|
|
84
100
|
| "missing_reference_column"
|
|
85
101
|
| "missing_softdelete_columns"
|
|
86
|
-
| "missing_anonymize_fields"
|
|
102
|
+
| "missing_anonymize_fields"
|
|
103
|
+
| "missing_file_storage"
|
|
104
|
+
| "file_delete_failed";
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
export interface RunRetentionCleanupResult {
|
|
@@ -113,6 +131,272 @@ async function purgeMatchingRows(
|
|
|
113
131
|
return count;
|
|
114
132
|
}
|
|
115
133
|
|
|
134
|
+
// --- hardDelete + file-fields (kumiko-framework#3089) ---
|
|
135
|
+
|
|
136
|
+
type FileFieldPlan = {
|
|
137
|
+
/** type file|image — stores the fileRef-id directly in the entity column. */
|
|
138
|
+
readonly singleFields: readonly string[];
|
|
139
|
+
/** type files|images — no entity column, resolved via file_refs. */
|
|
140
|
+
readonly multiFields: readonly string[];
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const NO_FILE_FIELDS: FileFieldPlan = { singleFields: [], multiFields: [] };
|
|
144
|
+
|
|
145
|
+
// file/image = single-column fields (fileRef-id lives directly in the entity
|
|
146
|
+
// row); files/images = plural, no column, resolved via file_refs. Same set
|
|
147
|
+
// as boot-validator/entity-handler.ts FILE_FIELD_TYPES and engine's
|
|
148
|
+
// isFileField — inlined here rather than importing across the package
|
|
149
|
+
// barrel since this module already needs the single/multi split, not just
|
|
150
|
+
// membership.
|
|
151
|
+
function planFileFields(fields: Readonly<Record<string, unknown>>): FileFieldPlan {
|
|
152
|
+
const singleFields: string[] = [];
|
|
153
|
+
const multiFields: string[] = [];
|
|
154
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
155
|
+
const type = (field as { readonly type?: unknown }).type; // @cast-boundary schema-walk
|
|
156
|
+
if (type === "file" || type === "image") singleFields.push(name);
|
|
157
|
+
else if (type === "files" || type === "images") multiFields.push(name);
|
|
158
|
+
}
|
|
159
|
+
return singleFields.length > 0 || multiFields.length > 0
|
|
160
|
+
? { singleFields, multiFields }
|
|
161
|
+
: NO_FILE_FIELDS;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Every fileRef-id a row's file/image/files/images-fields reference: single
|
|
165
|
+
// fields hold the id in their own column, plural fields have no column and
|
|
166
|
+
// resolve via file_refs (tenantId, entityType, entityId, fieldName).
|
|
167
|
+
async function collectFileRefIds(
|
|
168
|
+
db: DbRunner,
|
|
169
|
+
tenantId: TenantId,
|
|
170
|
+
entityName: string,
|
|
171
|
+
row: Record<string, unknown>,
|
|
172
|
+
plan: FileFieldPlan,
|
|
173
|
+
): Promise<readonly string[]> {
|
|
174
|
+
const ids = new Set<string>();
|
|
175
|
+
for (const field of plan.singleFields) {
|
|
176
|
+
const value = row[field];
|
|
177
|
+
if (typeof value === "string" && value.length > 0) ids.add(value);
|
|
178
|
+
}
|
|
179
|
+
if (plan.multiFields.length > 0) {
|
|
180
|
+
const refRows = await selectMany<{ id: string }>(db, fileRefsTable, {
|
|
181
|
+
tenantId,
|
|
182
|
+
entityType: entityName,
|
|
183
|
+
entityId: String(row["id"]),
|
|
184
|
+
fieldName: { in: plan.multiFields },
|
|
185
|
+
});
|
|
186
|
+
for (const r of refRows) ids.add(r.id);
|
|
187
|
+
}
|
|
188
|
+
return Array.from(ids);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// A fileRef is bound to a DIFFERENT entity/record than the row currently
|
|
192
|
+
// being purged — its binding (entityType/entityId), not the row's own
|
|
193
|
+
// single-field column, decides ownership. A single-field value can point at
|
|
194
|
+
// a fileRef uploaded/attached elsewhere; that fileRef's bytes belong to
|
|
195
|
+
// whatever it's actually bound to, not to this row.
|
|
196
|
+
function isFileRefBoundElsewhere(
|
|
197
|
+
fileRef: Record<string, unknown>,
|
|
198
|
+
entityName: string,
|
|
199
|
+
rowId: string,
|
|
200
|
+
): boolean {
|
|
201
|
+
const entityType = fileRef["entityType"];
|
|
202
|
+
const entityId = fileRef["entityId"];
|
|
203
|
+
if (typeof entityType === "string" && entityType !== entityName) return true;
|
|
204
|
+
if (typeof entityId === "string" && entityId !== rowId) return true;
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A single-field fileRef-id can be shared by more than one row of the SAME
|
|
209
|
+
// table (e.g. a stock-photo re-picked across records). Purging its bytes
|
|
210
|
+
// would corrupt the row that isn't being deleted. Bounded by batchLimit rows
|
|
211
|
+
// per run and singleFields.length queries per fileRef — the same per-row
|
|
212
|
+
// event-cost tradeoff as the rest of this cron (see header).
|
|
213
|
+
async function isFileRefSharedByOtherRow(
|
|
214
|
+
db: DbRunner,
|
|
215
|
+
table: Parameters<typeof selectMany>[1],
|
|
216
|
+
tableHasTenantId: boolean,
|
|
217
|
+
tenantId: TenantId,
|
|
218
|
+
rowId: string,
|
|
219
|
+
singleFields: readonly string[],
|
|
220
|
+
fileRefId: string,
|
|
221
|
+
): Promise<boolean> {
|
|
222
|
+
for (const field of singleFields) {
|
|
223
|
+
const where: WhereObject = { [field]: fileRefId, id: { ne: rowId } };
|
|
224
|
+
if (tableHasTenantId) where["tenantId"] = tenantId;
|
|
225
|
+
const others = await selectMany(db, table, where, { limit: 1 });
|
|
226
|
+
if (others.length > 0) return true;
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// hardDelete for ONE row of an entity that has file/image/files/images
|
|
232
|
+
// fields: delete the un-shared fileRefs' bytes (+ derivatives) first, then
|
|
233
|
+
// their fileRef rows, then the entity row itself — bytes before rows so a
|
|
234
|
+
// crash mid-way leaves an orphaned fileRef pointing at already-gone bytes
|
|
235
|
+
// (safe: a later run's provider.delete is idempotent) rather than a gone
|
|
236
|
+
// fileRef whose bytes leak forever.
|
|
237
|
+
async function purgeHardDeleteRowWithFiles(args: {
|
|
238
|
+
readonly db: DbRunner;
|
|
239
|
+
readonly table: Parameters<typeof selectMany>[1];
|
|
240
|
+
readonly tableHasTenantId: boolean;
|
|
241
|
+
readonly tenantId: TenantId;
|
|
242
|
+
readonly entityName: string;
|
|
243
|
+
readonly row: Record<string, unknown>;
|
|
244
|
+
readonly plan: FileFieldPlan;
|
|
245
|
+
readonly files: FileContext | undefined;
|
|
246
|
+
readonly entityExecutor: ReturnType<typeof createEventStoreExecutor>;
|
|
247
|
+
readonly fileRefExecutor: ReturnType<typeof createEventStoreExecutor>;
|
|
248
|
+
readonly systemUser: SessionUser;
|
|
249
|
+
readonly tdb: TenantDb;
|
|
250
|
+
readonly onSkip: (reason: "missing_file_storage" | "file_delete_failed") => void;
|
|
251
|
+
}): Promise<boolean> {
|
|
252
|
+
const rowId = String(args.row["id"]);
|
|
253
|
+
const fileRefIds = await collectFileRefIds(
|
|
254
|
+
args.db,
|
|
255
|
+
args.tenantId,
|
|
256
|
+
args.entityName,
|
|
257
|
+
args.row,
|
|
258
|
+
args.plan,
|
|
259
|
+
);
|
|
260
|
+
if (fileRefIds.length === 0) {
|
|
261
|
+
return (
|
|
262
|
+
await args.entityExecutor.forget(
|
|
263
|
+
{ id: args.row["id"] as EntityId },
|
|
264
|
+
args.systemUser,
|
|
265
|
+
args.tdb,
|
|
266
|
+
)
|
|
267
|
+
).isSuccess;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const fileRefRows = await selectMany<Record<string, unknown>>(args.db, fileRefsTable, {
|
|
271
|
+
id: { in: fileRefIds },
|
|
272
|
+
tenantId: args.tenantId,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const toDelete: Record<string, unknown>[] = [];
|
|
276
|
+
for (const fileRef of fileRefRows) {
|
|
277
|
+
if (isFileRefBoundElsewhere(fileRef, args.entityName, rowId)) continue;
|
|
278
|
+
const shared = await isFileRefSharedByOtherRow(
|
|
279
|
+
args.db,
|
|
280
|
+
args.table,
|
|
281
|
+
args.tableHasTenantId,
|
|
282
|
+
args.tenantId,
|
|
283
|
+
rowId,
|
|
284
|
+
args.plan.singleFields,
|
|
285
|
+
String(fileRef["id"]),
|
|
286
|
+
);
|
|
287
|
+
if (shared) continue;
|
|
288
|
+
toDelete.push(fileRef);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (toDelete.length === 0) {
|
|
292
|
+
return (
|
|
293
|
+
await args.entityExecutor.forget(
|
|
294
|
+
{ id: args.row["id"] as EntityId },
|
|
295
|
+
args.systemUser,
|
|
296
|
+
args.tdb,
|
|
297
|
+
)
|
|
298
|
+
).isSuccess;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!args.files) {
|
|
302
|
+
args.onSkip("missing_file_storage");
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const files = args.files;
|
|
306
|
+
const store = { list: files.list, delete: (key: string) => files.ref(key).delete() };
|
|
307
|
+
|
|
308
|
+
let anyFailed = false;
|
|
309
|
+
for (const fileRef of toDelete) {
|
|
310
|
+
const key = fileRef["storageKey"];
|
|
311
|
+
if (typeof key !== "string" || key.length === 0) continue;
|
|
312
|
+
const failedKeys = await deleteStoredFileAndDerivatives(
|
|
313
|
+
key,
|
|
314
|
+
store,
|
|
315
|
+
"data-retention:hardDelete",
|
|
316
|
+
);
|
|
317
|
+
if (failedKeys.length > 0) {
|
|
318
|
+
anyFailed = true;
|
|
319
|
+
// biome-ignore lint/suspicious/noConsole: operator-visibility for storage-cleanup failures
|
|
320
|
+
console.warn(
|
|
321
|
+
`[data-retention:hardDelete] tenant=${args.tenantId} entity=${args.entityName} row=${rowId} storage delete failed keys=${failedKeys.join(",")}`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (anyFailed) {
|
|
326
|
+
args.onSkip("file_delete_failed");
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
for (const fileRef of toDelete) {
|
|
331
|
+
const res = await args.fileRefExecutor.forget(
|
|
332
|
+
{ id: fileRef["id"] as EntityId },
|
|
333
|
+
args.systemUser,
|
|
334
|
+
args.tdb,
|
|
335
|
+
);
|
|
336
|
+
if (!res.isSuccess) {
|
|
337
|
+
args.onSkip("file_delete_failed");
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
await args.entityExecutor.forget({ id: args.row["id"] as EntityId }, args.systemUser, args.tdb)
|
|
344
|
+
).isSuccess;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// hardDelete page for an entity WITH file/image/files/images fields — loads
|
|
348
|
+
// full rows (not just id) so file-field values are available, and reports
|
|
349
|
+
// each skip reason at most once per entity per run (repeat skips would just
|
|
350
|
+
// be noise; the per-row failure is already warned above).
|
|
351
|
+
async function purgeHardDeleteRowsWithFiles(args: {
|
|
352
|
+
readonly db: DbRunner;
|
|
353
|
+
readonly table: Parameters<typeof selectMany>[1];
|
|
354
|
+
readonly tableHasTenantId: boolean;
|
|
355
|
+
readonly where: WhereObject;
|
|
356
|
+
readonly batchLimit: number;
|
|
357
|
+
readonly tenantId: TenantId;
|
|
358
|
+
readonly entityName: string;
|
|
359
|
+
readonly plan: FileFieldPlan;
|
|
360
|
+
readonly files: FileContext | undefined;
|
|
361
|
+
readonly entityExecutor: ReturnType<typeof createEventStoreExecutor>;
|
|
362
|
+
readonly fileRefExecutor: ReturnType<typeof createEventStoreExecutor>;
|
|
363
|
+
readonly systemUser: SessionUser;
|
|
364
|
+
readonly tdb: TenantDb;
|
|
365
|
+
readonly skipped: RetentionCleanupSkip[];
|
|
366
|
+
}): Promise<number> {
|
|
367
|
+
const rows = await selectMany<Record<string, unknown>>(args.db, args.table, args.where, {
|
|
368
|
+
limit: args.batchLimit,
|
|
369
|
+
});
|
|
370
|
+
const reasonsSeen = new Set<RetentionCleanupSkip["reason"]>();
|
|
371
|
+
const onSkip = (reason: "missing_file_storage" | "file_delete_failed") => {
|
|
372
|
+
// skip: reason already reported for this entity in this run
|
|
373
|
+
if (reasonsSeen.has(reason)) return;
|
|
374
|
+
reasonsSeen.add(reason);
|
|
375
|
+
args.skipped.push({ entityName: args.entityName, reason });
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
let count = 0;
|
|
379
|
+
for (const row of rows) {
|
|
380
|
+
const ok = await purgeHardDeleteRowWithFiles({
|
|
381
|
+
db: args.db,
|
|
382
|
+
table: args.table,
|
|
383
|
+
tableHasTenantId: args.tableHasTenantId,
|
|
384
|
+
tenantId: args.tenantId,
|
|
385
|
+
entityName: args.entityName,
|
|
386
|
+
row,
|
|
387
|
+
plan: args.plan,
|
|
388
|
+
files: args.files,
|
|
389
|
+
entityExecutor: args.entityExecutor,
|
|
390
|
+
fileRefExecutor: args.fileRefExecutor,
|
|
391
|
+
systemUser: args.systemUser,
|
|
392
|
+
tdb: args.tdb,
|
|
393
|
+
onSkip,
|
|
394
|
+
});
|
|
395
|
+
if (ok) count++;
|
|
396
|
+
}
|
|
397
|
+
return count;
|
|
398
|
+
}
|
|
399
|
+
|
|
116
400
|
// PiiAnnotations.anonymize — structural view, avoids reaching into the
|
|
117
401
|
// engine's field-type internals from here.
|
|
118
402
|
type AnonymizeCapableField = { readonly anonymize?: () => unknown | Promise<unknown> };
|
|
@@ -244,9 +528,32 @@ export async function runRetentionCleanup(
|
|
|
244
528
|
switch (policy.strategy) {
|
|
245
529
|
case "hardDelete": {
|
|
246
530
|
const executor = createEventStoreExecutor(proj.table, entity, { entityName });
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
531
|
+
const filePlan = planFileFields(entity.fields);
|
|
532
|
+
if (filePlan.singleFields.length === 0 && filePlan.multiFields.length === 0) {
|
|
533
|
+
hardDeleted += await purgeMatchingRows(db, proj.table, where, batchLimit, (id) =>
|
|
534
|
+
executor.forget({ id }, systemUser, tdb),
|
|
535
|
+
);
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
const fileRefExecutor = createEventStoreExecutor(fileRefsTable, fileRefEntity, {
|
|
539
|
+
entityName: "fileRef",
|
|
540
|
+
});
|
|
541
|
+
hardDeleted += await purgeHardDeleteRowsWithFiles({
|
|
542
|
+
db,
|
|
543
|
+
table: proj.table,
|
|
544
|
+
tableHasTenantId: table["tenantId"] !== undefined,
|
|
545
|
+
where,
|
|
546
|
+
batchLimit,
|
|
547
|
+
tenantId,
|
|
548
|
+
entityName,
|
|
549
|
+
plan: filePlan,
|
|
550
|
+
files: args.files,
|
|
551
|
+
entityExecutor: executor,
|
|
552
|
+
fileRefExecutor,
|
|
553
|
+
systemUser,
|
|
554
|
+
tdb,
|
|
555
|
+
skipped,
|
|
556
|
+
});
|
|
250
557
|
break;
|
|
251
558
|
}
|
|
252
559
|
case "softDelete": {
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
unsafePushTables,
|
|
29
29
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
30
30
|
import { resetPiiSubjectKmsForTests } from "@cosmicdrift/kumiko-framework/testing";
|
|
31
|
-
import
|
|
31
|
+
import * as z from "zod";
|
|
32
32
|
import { createChannelEmailFeature } from "../../channel-email/feature";
|
|
33
33
|
import { createInMemoryTransport } from "../../channel-email/types";
|
|
34
34
|
import { createConfigFeature } from "../../config/feature";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
testTenantId,
|
|
27
27
|
unsafePushTables,
|
|
28
28
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
29
|
-
import
|
|
29
|
+
import * as z from "zod";
|
|
30
30
|
import { createChannelEmailFeature } from "../../channel-email/feature";
|
|
31
31
|
import { createInMemoryTransport, type EmailMessage } from "../../channel-email/types";
|
|
32
32
|
import { InAppHandlers, InAppQueries } from "../../channel-in-app/constants";
|
package/src/delivery/events.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// delivery-service.ts (validates payloads before the low-level append()
|
|
4
4
|
// — out-of-dispatcher writes otherwise skip schema enforcement).
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import * as z from "zod";
|
|
7
7
|
import { DeliveryStatus } from "./constants";
|
|
8
8
|
|
|
9
9
|
export const deliveryAttemptSchema = z.object({
|
package/src/delivery/feature.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
9
9
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
10
10
|
import { Temporal } from "temporal-polyfill";
|
|
11
|
-
import
|
|
11
|
+
import * as z from "zod";
|
|
12
12
|
import { decryptStoredPii } from "../../shared";
|
|
13
13
|
import type { DeliveryStatusValue } from "../constants";
|
|
14
14
|
import { deliveryAttemptsTable } from "../tables";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
-
import
|
|
4
|
+
import * as z from "zod";
|
|
5
5
|
import { notificationPreferencesTable } from "../tables";
|
|
6
6
|
|
|
7
7
|
export const preferencesQuery = defineQueryHandler({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod";
|
|
4
4
|
import { upsertPreference } from "../upsert-preference";
|
|
5
5
|
|
|
6
6
|
export const setPreferenceWrite = defineWriteHandler({
|
package/src/delivery/jobs.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
15
15
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
16
16
|
import type { JobRunner } from "@cosmicdrift/kumiko-framework/jobs";
|
|
17
|
-
import
|
|
17
|
+
import * as z from "zod";
|
|
18
18
|
import { appendAttemptEvent } from "./attempt-log";
|
|
19
19
|
import { buildChannelContext } from "./channel-context";
|
|
20
20
|
import { DeliveryJobs, deliveryPriorityRank } from "./constants";
|
|
@@ -2,7 +2,7 @@ import { createTenantDb, type DbConnection } from "@cosmicdrift/kumiko-framework
|
|
|
2
2
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import * as jose from "jose";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod";
|
|
6
6
|
import { upsertPreference } from "./upsert-preference";
|
|
7
7
|
|
|
8
8
|
// Shape des verified-JWT-payloads. tenantId kommt als string aus jose und
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
patchFileInstanceofForBunTest,
|
|
19
19
|
} from "@cosmicdrift/kumiko-framework/testing";
|
|
20
20
|
import sharp from "sharp";
|
|
21
|
-
import
|
|
21
|
+
import * as z from "zod";
|
|
22
22
|
import { createConfigFeature } from "../../config";
|
|
23
23
|
import { fileDerivativesFeature } from "../../file-derivatives";
|
|
24
24
|
import { fileFoundationFeature } from "../../file-foundation";
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import { createLateBoundHolder, seedRow, waitFor } from "@cosmicdrift/kumiko-framework/testing";
|
|
30
30
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
31
31
|
import { Temporal } from "temporal-polyfill";
|
|
32
|
-
import
|
|
32
|
+
import * as z from "zod";
|
|
33
33
|
import { createFeatureTogglesFeature } from "../feature";
|
|
34
34
|
import { globalFeatureStateTable } from "../global-feature-state-table";
|
|
35
35
|
import { GlobalFeatureToggleRuntime } from "../toggle-runtime";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { FEATURE_TOGGLE_CROSS_TENANT_REASON } from "../constants";
|
|
4
4
|
import { globalFeatureStateTable } from "../global-feature-state-table";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineQueryHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
import { FEATURE_TOGGLE_CROSS_TENANT_REASON } from "../constants";
|
|
4
4
|
import { globalFeatureStateTable } from "../global-feature-state-table";
|
|
5
5
|
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
type TenantId,
|
|
29
29
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
30
30
|
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
31
|
-
import
|
|
31
|
+
import * as z from "zod";
|
|
32
32
|
|
|
33
33
|
type FileRefRow = {
|
|
34
34
|
readonly entityType: string | null;
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
createTestEnvelopeCipher,
|
|
29
29
|
type MutableMasterKeyProvider,
|
|
30
30
|
} from "@cosmicdrift/kumiko-framework/testing";
|
|
31
|
-
import
|
|
31
|
+
import * as z from "zod";
|
|
32
32
|
import { createConfigFeature } from "../../config";
|
|
33
33
|
import { ConfigHandlers } from "../../config/constants";
|
|
34
34
|
import { createConfigAccessorFactory } from "../../config/feature";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
type FileStorageProvider,
|
|
29
29
|
fileRefsTable,
|
|
30
30
|
} from "@cosmicdrift/kumiko-framework/files";
|
|
31
|
-
import
|
|
31
|
+
import * as z from "zod";
|
|
32
32
|
import { runChunkedMigration } from "../../shared";
|
|
33
33
|
import { tenantTable } from "../../tenant";
|
|
34
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AccessRule, WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod";
|
|
4
4
|
import { DEFAULT_FOLDER_ACCESS } from "../constants";
|
|
5
5
|
import { folderAssignmentExecutor, folderExecutor } from "../executor";
|
|
6
6
|
|
package/src/folders/schemas.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// eraseSubjectKeys macht Event-Log UND Projection unlesbar
|
|
20
20
|
// (crypto-shredding, Muster billing-foundation #800).
|
|
21
21
|
|
|
22
|
-
import
|
|
22
|
+
import * as z from "zod";
|
|
23
23
|
import { INBOUND_MAIL_FOUNDATION_FEATURE, InboundMailAccountStatuses } from "./constants";
|
|
24
24
|
|
|
25
25
|
// Aggregate-types für den event-store.
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "@cosmicdrift/kumiko-framework/crypto";
|
|
20
20
|
import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
21
21
|
import { failUnprocessable } from "@cosmicdrift/kumiko-framework/errors";
|
|
22
|
-
import
|
|
22
|
+
import * as z from "zod";
|
|
23
23
|
import { InboundMailAccountStatuses, InboundMailAuthMethods } from "../constants";
|
|
24
24
|
import { MAIL_ACCOUNT_PII_FIELDS, mailAccountEntity } from "../entities";
|
|
25
25
|
import {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
11
11
|
import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
|
|
12
|
-
import
|
|
12
|
+
import * as z from "zod";
|
|
13
13
|
import { InboundMailAccountStatuses } from "../constants";
|
|
14
14
|
import {
|
|
15
15
|
MAIL_ACCOUNT_AGGREGATE_TYPE,
|
|
@@ -29,7 +29,7 @@ import { acquireNamespacedAdvisoryLock } from "@cosmicdrift/kumiko-framework/db"
|
|
|
29
29
|
import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
30
30
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
31
31
|
import { Temporal } from "temporal-polyfill";
|
|
32
|
-
import
|
|
32
|
+
import * as z from "zod";
|
|
33
33
|
import { inboundMessageAggregateId, mailThreadAggregateId } from "../aggregate-id";
|
|
34
34
|
import {
|
|
35
35
|
INBOUND_MESSAGE_PII_FIELDS,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
decryptPiiFieldValues,
|
|
13
13
|
} from "@cosmicdrift/kumiko-framework/crypto";
|
|
14
14
|
import type { QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
15
|
-
import
|
|
15
|
+
import * as z from "zod";
|
|
16
16
|
import { MAIL_ACCOUNT_PII_FIELDS } from "../entities";
|
|
17
17
|
import { mailAccountsProjectionTable } from "../projection";
|
|
18
18
|
import { isVisibleToCaller } from "./scope-visibility";
|