@cosmicdrift/kumiko-bundled-features 0.274.0 → 0.276.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__/cross-tenant-jobs-escape-hatch.test.ts +45 -0
- package/src/audit/feature.ts +99 -95
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +21 -13
- package/src/auth-mfa/feature.ts +16 -1
- package/src/auth-mfa/handlers/reencrypt.job.ts +7 -9
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +7 -2
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +9 -3
- package/src/config/__tests__/reencrypt-job-tenant-checks.integration.test.ts +8 -2
- package/src/config/handlers/reencrypt.job.ts +8 -5
- package/src/crypto-shredding/feature.ts +18 -14
- package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +4 -1
- package/src/data-retention/feature.ts +21 -15
- package/src/files-tenant-data/__tests__/hooks.integration.test.ts +4 -2
- package/src/files-tenant-data/__tests__/sweep-orphaned-derivatives.job.integration.test.ts +8 -2
- package/src/files-tenant-data/changes.json +9 -1
- package/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -13
- package/src/files-tenant-data/index.ts +17 -8
- package/src/form-draft/feature.ts +13 -1
- package/src/form-draft/handlers/cleanup.job.ts +7 -11
- package/src/inbound-mail-foundation/feature.ts +18 -14
- package/src/jobs/__tests__/job-system-user.integration.test.ts +39 -30
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +3 -3
- package/src/jobs/handlers/retention-cleanup.job.ts +5 -3
- package/src/jobs/handlers/stale-run-sweep.job.ts +5 -3
- package/src/presets/__tests__/security-baseline.test.ts +60 -1
- package/src/presets/security-baseline.ts +1 -2
- package/src/rate-limiting/feature.ts +15 -11
- package/src/secrets/__tests__/rotate.integration.test.ts +4 -4
- package/src/secrets/changes.json +7 -0
- package/src/secrets/feature.ts +19 -4
- package/src/secrets/handlers/rotate.job.ts +7 -9
- package/src/sessions/__tests__/cleanup.integration.test.ts +5 -5
- package/src/sessions/feature.ts +142 -128
- package/src/sessions/handlers/cleanup.job.ts +7 -10
- package/src/tenant-lifecycle/feature.ts +13 -10
- package/src/tenant-settings/__tests__/tenant-currency-read-access.integration.test.ts +50 -0
- package/src/tenant-settings/constants.ts +6 -1
- package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -2
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +11 -2
- package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +3 -1
- package/src/user-data-rights/feature.ts +26 -20
- package/src/workflow-runner/__tests__/event-wakeup.integration.test.ts +8 -2
- package/src/workflow-runner/__tests__/resume-loop.integration.test.ts +8 -2
- package/src/workflow-runner/feature.ts +10 -3
|
@@ -529,21 +529,24 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
529
529
|
});
|
|
530
530
|
|
|
531
531
|
// S2.U3 Atom 3b — Worker fuer Async Export-Pipeline. Cron-getriggert.
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
532
|
+
const RUN_EXPORT_JOBS_REASON = "processes pending export jobs across every tenant";
|
|
533
|
+
r.job({
|
|
534
|
+
name: "run-export-jobs",
|
|
535
|
+
trigger: { cron: "0 3 * * *" },
|
|
536
|
+
concurrency: "skip",
|
|
537
|
+
escapeHatch: { reason: RUN_EXPORT_JOBS_REASON },
|
|
538
|
+
handler: async (_payload, ctx) => {
|
|
539
|
+
if (!ctx.registry) {
|
|
540
|
+
throw new Error("run-export-jobs: ctx.registry required (JobContext incomplete)");
|
|
540
541
|
}
|
|
541
542
|
const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
|
|
542
543
|
// SYSTEM_USER_ID ist die framework-weite Konvention. Der job-
|
|
543
544
|
// Discriminator wird via handlerName="user-data-rights:run-export-
|
|
544
545
|
// jobs" im Secret-Read-Audit erfasst.
|
|
545
546
|
const exportUserId = ctx._userId ?? SYSTEM_USER_ID;
|
|
546
|
-
const exportDb = ctx.db
|
|
547
|
+
const exportDb = ctx.db.unsafeRaw(
|
|
548
|
+
RUN_EXPORT_JOBS_REASON,
|
|
549
|
+
) as import("@cosmicdrift/kumiko-framework/db").DbConnection; // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
547
550
|
const exportRegistry = ctx.registry;
|
|
548
551
|
|
|
549
552
|
// C6 — ohne eigene send*Email-Opts aber mit gemountetem mail-transport
|
|
@@ -609,25 +612,28 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
609
612
|
}),
|
|
610
613
|
});
|
|
611
614
|
},
|
|
612
|
-
);
|
|
615
|
+
});
|
|
613
616
|
|
|
614
617
|
// Autonomer Art.17-Forget-Cron. Spiegelt run-export-jobs: nach Ablauf der
|
|
615
618
|
// Grace-Period laeuft runForgetCleanup unbeaufsichtigt (der manuelle
|
|
616
619
|
// userDataRights.runForget-API bleibt fuer Operator-Runs). Ohne diesen
|
|
617
620
|
// Cron bleibt jeder Loesch-Antrag fuer immer in DeletionRequested haengen
|
|
618
621
|
// — Art.17 wuerde nie ausgefuehrt.
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
622
|
+
const RUN_FORGET_CLEANUP_REASON = "executes overdue Art.17 forget requests across every tenant";
|
|
623
|
+
r.job({
|
|
624
|
+
name: "run-forget-cleanup",
|
|
625
|
+
trigger: { cron: "0 3 * * *" },
|
|
626
|
+
concurrency: "skip",
|
|
627
|
+
escapeHatch: { reason: RUN_FORGET_CLEANUP_REASON },
|
|
628
|
+
handler: async (_payload, ctx) => {
|
|
629
|
+
if (!ctx.registry) {
|
|
630
|
+
throw new Error("run-forget-cleanup: ctx.registry required (JobContext incomplete)");
|
|
627
631
|
}
|
|
628
632
|
const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
|
|
629
633
|
const forgetUserId = ctx._userId ?? SYSTEM_USER_ID;
|
|
630
|
-
const forgetDb = ctx.db
|
|
634
|
+
const forgetDb = ctx.db.unsafeRaw(
|
|
635
|
+
RUN_FORGET_CLEANUP_REASON,
|
|
636
|
+
) as import("@cosmicdrift/kumiko-framework/db").DbConnection; // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
631
637
|
const forgetRegistry = ctx.registry;
|
|
632
638
|
const tenantModel = await resolveAppTenantModel({
|
|
633
639
|
registry: forgetRegistry,
|
|
@@ -686,7 +692,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
686
692
|
);
|
|
687
693
|
}
|
|
688
694
|
},
|
|
689
|
-
);
|
|
695
|
+
});
|
|
690
696
|
});
|
|
691
697
|
}
|
|
692
698
|
|
|
@@ -24,7 +24,12 @@
|
|
|
24
24
|
|
|
25
25
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
26
26
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
createTenantDb,
|
|
29
|
+
createUncheckedSystemDb,
|
|
30
|
+
insertOne,
|
|
31
|
+
selectMany,
|
|
32
|
+
} from "@cosmicdrift/kumiko-framework/db";
|
|
28
33
|
import {
|
|
29
34
|
type AwaitedEventType,
|
|
30
35
|
createSystemUser,
|
|
@@ -199,7 +204,8 @@ async function runResumeDueRunsJob(tenantId: string): Promise<void> {
|
|
|
199
204
|
|
|
200
205
|
const systemUser = createSystemUser(tenantId);
|
|
201
206
|
const ctx: JobContext = {
|
|
202
|
-
db: stack.db,
|
|
207
|
+
db: createTenantDb(stack.db, tenantId),
|
|
208
|
+
systemDb: createUncheckedSystemDb(createTenantDb(stack.db, tenantId, "system")),
|
|
203
209
|
registry: stack.registry,
|
|
204
210
|
systemUser,
|
|
205
211
|
log: noopLogger,
|
|
@@ -23,7 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
25
25
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
createTenantDb,
|
|
28
|
+
createUncheckedSystemDb,
|
|
29
|
+
insertOne,
|
|
30
|
+
selectMany,
|
|
31
|
+
} from "@cosmicdrift/kumiko-framework/db";
|
|
27
32
|
import {
|
|
28
33
|
createSystemUser,
|
|
29
34
|
defineFeature,
|
|
@@ -177,7 +182,8 @@ async function runResumeDueRunsJob(): Promise<void> {
|
|
|
177
182
|
|
|
178
183
|
const systemUser = createSystemUser(admin.tenantId);
|
|
179
184
|
const ctx: JobContext = {
|
|
180
|
-
db: stack.db,
|
|
185
|
+
db: createTenantDb(stack.db, admin.tenantId),
|
|
186
|
+
systemDb: createUncheckedSystemDb(createTenantDb(stack.db, admin.tenantId, "system")),
|
|
181
187
|
registry: stack.registry,
|
|
182
188
|
systemUser,
|
|
183
189
|
log: noopLogger,
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
//
|
|
25
25
|
// wait/retry/waitForEvent suspensions all resume automatically now.
|
|
26
26
|
|
|
27
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
27
28
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
28
29
|
import { selectDueWorkflowRunPending } from "./db/queries/due-runs";
|
|
29
30
|
import { resumeRunHandler } from "./handlers/resume-run.write";
|
|
@@ -58,8 +59,8 @@ export const workflowRunnerFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
58
59
|
"resume-due-runs",
|
|
59
60
|
{ trigger: { cron: "* * * * *" }, perTenant: true, concurrency: "skip" },
|
|
60
61
|
async (_payload, ctx) => {
|
|
61
|
-
if (!ctx.
|
|
62
|
-
throw new Error("resume-due-runs: ctx.
|
|
62
|
+
if (!ctx.systemDb) {
|
|
63
|
+
throw new Error("resume-due-runs: ctx.systemDb required (r.systemScope() feature)");
|
|
63
64
|
}
|
|
64
65
|
const tenantId = ctx.systemUser?.tenantId ?? ctx._tenantId;
|
|
65
66
|
if (tenantId === undefined) {
|
|
@@ -67,7 +68,13 @@ export const workflowRunnerFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
ctx.systemDb.assertTenantMatch(tenantId);
|
|
72
|
+
const dueRows = await selectDueWorkflowRunPending(
|
|
73
|
+
ctx.systemDb.unsafeRaw(
|
|
74
|
+
"due-row pickup needs FOR UPDATE SKIP LOCKED raw SQL; the query filters by this job's tenantId",
|
|
75
|
+
) as DbConnection, // @cast-boundary db-operator — DbRunner narrows to DbConnection, jobs never run inside a DbTx
|
|
76
|
+
tenantId,
|
|
77
|
+
);
|
|
71
78
|
|
|
72
79
|
for (const row of dueRows) {
|
|
73
80
|
try {
|