@cosmicdrift/kumiko-bundled-features 0.354.0 → 0.355.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-tools/agent-manifest.js +2 -0
- package/dist/agent-tools/types.d.ts +2 -0
- package/dist/audit/escape-hatch-audit-sink.d.ts +1 -0
- package/dist/audit/escape-hatch-audit-sink.js +2 -0
- package/dist/audit/handlers/resolve-audit-tenant.js +8 -4
- package/dist/billing-foundation/consumer-protection/register-consent.js +1 -2
- package/dist/billing-foundation/consumer-protection/register-termination.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/register-termination.js +1 -2
- package/dist/cap-overview/handlers/caps-usage.query.js +8 -5
- package/dist/compliance-profiles/handlers/set-profile.write.js +1 -4
- package/dist/delivery/delivery-service.js +1 -1
- package/dist/managed-pages/handlers/by-tenant-published.query.js +1 -4
- package/dist/managed-pages/handlers/set.write.js +1 -5
- package/dist/template-resolver/handlers/by-slug.query.js +1 -4
- package/dist/template-resolver/handlers/by-tenant.query.js +1 -4
- package/dist/template-resolver/handlers/collection-item.query.js +1 -4
- package/dist/template-resolver/handlers/collection-list.query.js +1 -4
- package/dist/template-resolver/handlers/collection-set.write.js +1 -5
- package/dist/template-resolver/handlers/set.write.js +1 -5
- package/dist/template-resolver/handlers/upsert-tenant.write.js +1 -4
- package/dist/template-resolver/seeding.js +1 -2
- package/dist/tenant-lifecycle/feature.js +1 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +3 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -0
- package/dist/tenant-lifecycle/stages.d.ts +2 -0
- package/dist/tenant-lifecycle/stages.js +2 -0
- package/dist/user-data-rights/feature.js +2 -0
- package/dist/user-data-rights/run-export-jobs.d.ts +2 -0
- package/dist/user-data-rights/run-export-jobs.js +2 -0
- package/dist/user-data-rights/run-forget-cleanup.d.ts +2 -0
- package/dist/user-data-rights/run-forget-cleanup.js +4 -0
- package/dist/user-data-rights/run-user-export.d.ts +2 -0
- package/dist/user-data-rights/run-user-export.js +2 -0
- package/package.json +9 -9
|
@@ -163,6 +163,7 @@ function buildHandlerEntry(qn, kind, def, roles, getHandlerEntity, denyQns) {
|
|
|
163
163
|
return undefined;
|
|
164
164
|
}
|
|
165
165
|
const entity = getHandlerEntity(qn);
|
|
166
|
+
const excludedFields = "excludedFields" in def ? def.excludedFields : undefined;
|
|
166
167
|
return {
|
|
167
168
|
qn,
|
|
168
169
|
kind,
|
|
@@ -170,6 +171,7 @@ function buildHandlerEntry(qn, kind, def, roles, getHandlerEntity, denyQns) {
|
|
|
170
171
|
risk: exposure.risk,
|
|
171
172
|
inputSchema,
|
|
172
173
|
...(entity !== undefined && { entity }),
|
|
174
|
+
...(excludedFields !== undefined && { excludedFields }),
|
|
173
175
|
};
|
|
174
176
|
}
|
|
175
177
|
function buildHandlers(queryHandlers, writeHandlers, roles, getHandlerEntity, denyQns) {
|
|
@@ -130,6 +130,8 @@ export type AgentManifestHandler = {
|
|
|
130
130
|
readonly risk: AgentRisk;
|
|
131
131
|
readonly inputSchema: Readonly<Record<string, unknown>>;
|
|
132
132
|
readonly entity?: string;
|
|
133
|
+
/** Fields this generic create/update handler never writes; sending one fails validation. */
|
|
134
|
+
readonly excludedFields?: readonly string[];
|
|
133
135
|
};
|
|
134
136
|
export type AgentManifestScreen = {
|
|
135
137
|
readonly id: string;
|
|
@@ -15,6 +15,7 @@ export declare const escapeHatchUsedSchema: z.ZodObject<{
|
|
|
15
15
|
}>;
|
|
16
16
|
reason: z.ZodString;
|
|
17
17
|
actor: z.ZodString;
|
|
18
|
+
caller: z.ZodOptional<z.ZodString>;
|
|
18
19
|
targetUserId: z.ZodOptional<z.ZodString>;
|
|
19
20
|
targetTenantId: z.ZodOptional<z.ZodString>;
|
|
20
21
|
}, z.core.$strip>;
|
|
@@ -18,6 +18,7 @@ export const escapeHatchUsedSchema = z.object({
|
|
|
18
18
|
kind: z.enum(escapeHatchKinds),
|
|
19
19
|
reason: z.string(),
|
|
20
20
|
actor: z.string(),
|
|
21
|
+
caller: z.string().optional(),
|
|
21
22
|
targetUserId: z.string().optional(),
|
|
22
23
|
targetTenantId: z.string().optional(),
|
|
23
24
|
});
|
|
@@ -28,6 +29,7 @@ export function createEscapeHatchAuditSink(opts) {
|
|
|
28
29
|
kind: event.kind,
|
|
29
30
|
reason: event.reason,
|
|
30
31
|
actor: event.actor,
|
|
32
|
+
...(event.caller !== undefined && { caller: event.caller }),
|
|
31
33
|
...(event.target && { targetUserId: event.target.id, targetTenantId: event.target.tenantId }),
|
|
32
34
|
});
|
|
33
35
|
// Appends on the unbound pool, never the handler tx — an audit entry must
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mayOverrideTenant } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { AccessDeniedError, FrameworkReasons } from "@cosmicdrift/kumiko-framework/errors";
|
|
2
3
|
import { APP_INSTANCE_STREAM_TYPE } from "@cosmicdrift/kumiko-framework/event-store";
|
|
3
4
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-types/identifiers";
|
|
4
5
|
import { AuditScopes } from "../constants.js";
|
|
@@ -7,8 +8,11 @@ import { AuditScopes } from "../constants.js";
|
|
|
7
8
|
export function resolveAuditScopeFilter(user, scope) {
|
|
8
9
|
if (scope !== AuditScopes.system)
|
|
9
10
|
return { tenantId: user.tenantId };
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
if (!mayOverrideTenant(user)) {
|
|
12
|
+
throw new AccessDeniedError({
|
|
13
|
+
i18nKey: "audit.errors.systemScopeRequiresSystemAdmin",
|
|
14
|
+
details: { reason: FrameworkReasons.tenantOverrideRequiresSystemAdmin },
|
|
15
|
+
});
|
|
16
|
+
}
|
|
13
17
|
return { tenantId: SYSTEM_TENANT_ID, aggregateType: APP_INSTANCE_STREAM_TYPE };
|
|
14
18
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createSystemUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
1
|
import { paymentAggregateId, subscriptionAggregateId } from "../aggregate-id.js";
|
|
3
2
|
import { SubscriptionFoundationHandlers, SubscriptionStatuses } from "../constants.js";
|
|
4
3
|
import { CHECKOUT_CONSENT_RECORDED_EVENT_SHORT, CONTRACT_CONFIRMATION_ISSUED_EVENT_SHORT, checkoutConsentRecordedPayloadSchema, contractConfirmationIssuedPayloadSchema, INVOICE_PAID_EVENT_QN, PAYMENT_RECEIVED_EVENT_QN, SUBSCRIPTION_CREATED_EVENT_QN, SUBSCRIPTION_UPDATED_EVENT_QN, } from "../events.js";
|
|
@@ -48,7 +47,7 @@ export function registerConsumerProtection(r, options, consumerProtection) {
|
|
|
48
47
|
// user), never from payload data.
|
|
49
48
|
const tenantId = ctx.systemUser.tenantId;
|
|
50
49
|
const currentPeriodEndIso = payload["currentPeriodEndIso"];
|
|
51
|
-
const result = await ctx.
|
|
50
|
+
const result = await ctx.write(SubscriptionFoundationHandlers.issueContractConfirmation, {
|
|
52
51
|
consentId,
|
|
53
52
|
sourceAggregateId: isSubscriptionEvent
|
|
54
53
|
? subscriptionAggregateId(tenantId)
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FeatureRegistrar } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.js";
|
|
3
3
|
export declare function registerContractTermination(r: FeatureRegistrar, options: ResolvedBillingFoundationOptions, consumerProtection: ConsumerProtectionOptions): void;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createSystemUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
1
|
import { SubscriptionCancelTimings, SubscriptionFoundationHandlers } from "../constants.js";
|
|
3
2
|
import { CONTRACT_TERMINATION_DECLARED_EVENT_QN, CONTRACT_TERMINATION_DECLARED_EVENT_SHORT, CONTRACT_TERMINATION_REQUESTED_EVENT_SHORT, CONTRACT_TERMINATION_UNMATCHED_EVENT_SHORT, contractTerminationDeclaredPayloadSchema, contractTerminationRequestedPayloadSchema, contractTerminationUnmatchedPayloadSchema, } from "../events.js";
|
|
4
3
|
import { createDeclareContractTerminationHandler, createRecordContractTerminationHandler, createRecordUnmatchedContractTerminationHandler, createTerminableSubscriptionQuery, } from "./termination-record.js";
|
|
@@ -22,7 +21,7 @@ export function registerContractTermination(r, options, consumerProtection) {
|
|
|
22
21
|
// skip: trigger event carries no usable declaration
|
|
23
22
|
if (!declared.success)
|
|
24
23
|
return;
|
|
25
|
-
const result = await ctx.
|
|
24
|
+
const result = await ctx.write(SubscriptionFoundationHandlers.recordContractTermination, {
|
|
26
25
|
requestId: declared.data.requestId,
|
|
27
26
|
declarationType: declared.data.declarationType,
|
|
28
27
|
terminationKind: declared.data.terminationKind,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { buildEntityTable } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
4
|
-
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import { defineQueryHandler, mayOverrideTenant, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { AccessDeniedError, FrameworkReasons, InternalError, } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
import { tierAssignmentEntity } from "../../tier-engine/index.js";
|
|
7
7
|
import { computeFraction, computeTone, computeUnclampedFraction } from "../usage-math.js";
|
|
@@ -24,9 +24,12 @@ export function createCapsUsageQuery(caps, usageRoles) {
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
const override = query.payload.tenantId;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (override !== undefined && !mayOverrideTenant(query.user)) {
|
|
28
|
+
throw new AccessDeniedError({
|
|
29
|
+
i18nKey: "cap-overview.errors.tenantOverrideRequiresSystemAdmin",
|
|
30
|
+
details: { reason: FrameworkReasons.tenantOverrideRequiresSystemAdmin },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
30
33
|
const targetTenantId = override ?? query.user.tenantId;
|
|
31
34
|
// Both branches return the SAME unfiltered system-mode db —
|
|
32
35
|
// assertTenantMatch is a self-check on the caller, not a query
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { complianceProfileOverrideSchema, SELECTABLE_PROFILE_KEYS, } from "@cosmicdrift/kumiko-framework/compliance";
|
|
3
3
|
import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
4
|
-
import { access,
|
|
4
|
+
import { access, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import { UnprocessableError, validationErrorFromZod, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { tenantComplianceProfileEntity, tenantComplianceProfileTable, } from "../schema/profile-selection.js";
|
|
@@ -44,9 +44,6 @@ export const setProfileWrite = defineWriteHandler({
|
|
|
44
44
|
description: "Sets or replaces a tenant's compliance profile key plus an optional JSON override, for onboarding or a later region change; a system admin may target a different tenant through tenantIdOverride.",
|
|
45
45
|
handler: async (event, ctx) => {
|
|
46
46
|
const tenantOverride = event.payload.tenantIdOverride;
|
|
47
|
-
const overrideDenied = crossTenantOverrideDenied(event.user, tenantOverride, "complianceProfiles.errors.tenantOverrideRequiresSystemAdmin");
|
|
48
|
-
if (overrideDenied)
|
|
49
|
-
return writeFailure(overrideDenied);
|
|
50
47
|
const tenantId = (tenantOverride ?? event.user.tenantId); // @cast-boundary engine-payload
|
|
51
48
|
const executorUser = tenantOverride !== undefined ? { ...event.user, tenantId } : event.user;
|
|
52
49
|
// Override-Validation: muss parseables JSON-Object sein UND dem
|
|
@@ -92,7 +92,7 @@ export function createDeliveryService(options) {
|
|
|
92
92
|
tenantId,
|
|
93
93
|
actor: systemUser.id,
|
|
94
94
|
sink: escapeHatchAuditSink,
|
|
95
|
-
log,
|
|
95
|
+
log: logError,
|
|
96
96
|
});
|
|
97
97
|
// ctx.db/dbOutsideTransaction are tenant-filtered even for a systemScope handler:
|
|
98
98
|
// the dispatcher closes them off entirely there, so "system" mode would hand this
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { castTenantRows } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
3
|
+
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { pagesTable } from "../table.js";
|
|
6
6
|
export const byTenantPublishedQuery = defineQueryHandler({
|
|
@@ -15,9 +15,6 @@ export const byTenantPublishedQuery = defineQueryHandler({
|
|
|
15
15
|
description: "Lists every published page of a tenant with slug, language, title and last-change time but no body; use it to enumerate the public pages for sitemap.xml or llms.txt, rather than by-slug which fetches one page's content.",
|
|
16
16
|
handler: async (query, ctx) => {
|
|
17
17
|
const override = query.payload.tenantIdOverride;
|
|
18
|
-
const overrideDenied = crossTenantOverrideDenied(query.user, override, "managedPages.errors.tenantOverrideRequiresSystemAdmin");
|
|
19
|
-
if (overrideDenied)
|
|
20
|
-
throw overrideDenied;
|
|
21
18
|
const tenantId = override ?? query.user.tenantId;
|
|
22
19
|
const rows = castTenantRows(await selectMany(ctx.db, pagesTable, { tenantId: tenantId, published: true }));
|
|
23
20
|
return {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
4
|
-
import { writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
4
|
import * as z from "zod";
|
|
6
5
|
import { pageEntity, pagesTable } from "../table.js";
|
|
7
6
|
const slugSchema = z
|
|
@@ -44,9 +43,6 @@ export const setWrite = defineWriteHandler({
|
|
|
44
43
|
handler: async (event, ctx) => {
|
|
45
44
|
const db = ctx.db;
|
|
46
45
|
const override = event.payload.tenantIdOverride;
|
|
47
|
-
const overrideDenied = crossTenantOverrideDenied(event.user, override, "managedPages.errors.tenantOverrideRequiresSystemAdmin");
|
|
48
|
-
if (overrideDenied)
|
|
49
|
-
return writeFailure(overrideDenied);
|
|
50
46
|
const tenantId = override ?? event.user.tenantId;
|
|
51
47
|
// override: point the executor context at the target tenant, else getStreamVersion runs against user.tenantId → version_conflict.
|
|
52
48
|
const executorUser = override !== undefined ? { ...event.user, tenantId: override } : event.user; // @cast-boundary engine-bridge
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
2
|
+
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { TEXT_BLOCK_KIND } from "../constants.js";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
@@ -28,9 +28,6 @@ export const bySlugQuery = defineQueryHandler({
|
|
|
28
28
|
description: "Reads one text-block of a tenant by slug and locale together with its body and format; it is pinned to the text-block kind so mail templates and AI prompts in the same table stay unreachable through this anonymous-capable path.",
|
|
29
29
|
handler: async (query, ctx) => {
|
|
30
30
|
const override = query.payload.tenantIdOverride;
|
|
31
|
-
const overrideDenied = crossTenantOverrideDenied(query.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
32
|
-
if (overrideDenied)
|
|
33
|
-
throw overrideDenied;
|
|
34
31
|
const tenantId = override ?? query.user.tenantId;
|
|
35
32
|
const row = await fetchOne(ctx.db, templateResourcesTable, {
|
|
36
33
|
tenantId,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { castTenantRows } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
3
|
+
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { TEXT_BLOCK_KIND } from "../constants.js";
|
|
6
6
|
import { templateResourcesTable } from "../table.js";
|
|
@@ -15,9 +15,6 @@ export const byTenantQuery = defineQueryHandler({
|
|
|
15
15
|
description: "Lists every text-block of a tenant with slug, locale, title and body so a public content tree can be rendered in one call; use it for the whole sidebar, and by-slug when only one block is needed.",
|
|
16
16
|
handler: async (query, ctx) => {
|
|
17
17
|
const override = query.payload.tenantIdOverride;
|
|
18
|
-
const overrideDenied = crossTenantOverrideDenied(query.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
19
|
-
if (overrideDenied)
|
|
20
|
-
throw overrideDenied;
|
|
21
18
|
const tenantId = override ?? query.user.tenantId;
|
|
22
19
|
const rows = castTenantRows(await selectMany(ctx.db, templateResourcesTable, { tenantId, kind: TEXT_BLOCK_KIND }));
|
|
23
20
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
2
|
+
import { defineQueryHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { collectionStore, DEFAULT_COLLECTION_ACCESS, toCollectionEntry, } from "./collection-shared.js";
|
|
5
5
|
// Single entry of one collection, for the editor behind a tree node. Same
|
|
@@ -20,9 +20,6 @@ export function makeCollectionItemQuery(collection) {
|
|
|
20
20
|
: `Reads one tenant-wide "${collection.id}" entry by slug and locale, returning null when it does not exist; use it to open a single entry in the editor rather than to list the collection.`,
|
|
21
21
|
handler: async (query, ctx) => {
|
|
22
22
|
const override = query.payload.tenantIdOverride;
|
|
23
|
-
const overrideDenied = crossTenantOverrideDenied(query.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
24
|
-
if (overrideDenied)
|
|
25
|
-
throw overrideDenied;
|
|
26
23
|
const tenantId = override ?? query.user.tenantId;
|
|
27
24
|
const row = await fetchOne(ctx.db, store.table, {
|
|
28
25
|
tenantId,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { castTenantRows } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
3
|
+
import { defineQueryHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { collectionStore, DEFAULT_COLLECTION_ACCESS, toCollectionEntry, } from "./collection-shared.js";
|
|
6
6
|
// One list handler per declared collection: `kind`, `ownership` and `access`
|
|
@@ -25,9 +25,6 @@ export function makeCollectionListQuery(collection) {
|
|
|
25
25
|
: `Lists the tenant-wide entries of the "${collection.id}" content collection (${collection.kind} content) with slug, locale, title and body; every caller who may reach this collection sees the same shared set.`,
|
|
26
26
|
handler: async (query, ctx) => {
|
|
27
27
|
const override = query.payload.tenantIdOverride;
|
|
28
|
-
const overrideDenied = crossTenantOverrideDenied(query.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
29
|
-
if (overrideDenied)
|
|
30
|
-
throw overrideDenied;
|
|
31
28
|
const tenantId = override ?? query.user.tenantId;
|
|
32
29
|
const rows = castTenantRows(await selectMany(ctx.db, store.table, {
|
|
33
30
|
tenantId,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
3
|
-
import { writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
2
|
+
import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
3
|
import * as z from "zod";
|
|
5
4
|
import { collectionStore, DEFAULT_COLLECTION_ACCESS, } from "./collection-shared.js";
|
|
6
5
|
import { contentFormatSchema, folderSchema, localeSchema, slugSchema } from "./shared.js";
|
|
@@ -32,9 +31,6 @@ export function makeCollectionSetWrite(collection) {
|
|
|
32
31
|
handler: async (event, ctx) => {
|
|
33
32
|
const db = ctx.db;
|
|
34
33
|
const override = event.payload.tenantIdOverride;
|
|
35
|
-
const overrideDenied = crossTenantOverrideDenied(event.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
36
|
-
if (overrideDenied)
|
|
37
|
-
return writeFailure(overrideDenied);
|
|
38
34
|
// @cast-boundary engine-payload — override is a zod-validated string, the
|
|
39
35
|
// user's own tenantId is already TenantId-branded.
|
|
40
36
|
const tenantId = (override ?? event.user.tenantId);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
3
|
-
import { writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
2
|
+
import { defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
3
|
import * as z from "zod";
|
|
5
4
|
import { TEMPLATE_KINDS, TEXT_BLOCK_KIND } from "../constants.js";
|
|
6
5
|
import { templateResourcesTable } from "../table.js";
|
|
@@ -45,9 +44,6 @@ export const setWrite = defineWriteHandler({
|
|
|
45
44
|
handler: async (event, ctx) => {
|
|
46
45
|
const db = ctx.db;
|
|
47
46
|
const override = event.payload.tenantIdOverride;
|
|
48
|
-
const overrideDenied = crossTenantOverrideDenied(event.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
49
|
-
if (overrideDenied)
|
|
50
|
-
return writeFailure(overrideDenied);
|
|
51
47
|
// @cast-boundary engine-payload — override is a zod-validated string, the
|
|
52
48
|
// user's own tenantId is already TenantId-branded.
|
|
53
49
|
const tenantId = (override ?? event.user.tenantId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
2
|
+
import { defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { AccessDeniedError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
@@ -20,9 +20,6 @@ export const upsertTenantWrite = defineWriteHandler({
|
|
|
20
20
|
handler: async (event, ctx) => {
|
|
21
21
|
const db = ctx.db;
|
|
22
22
|
const override = event.payload.tenantIdOverride;
|
|
23
|
-
const overrideDenied = crossTenantOverrideDenied(event.user, override, "templateResolver.errors.tenantOverrideRequiresSystemAdmin");
|
|
24
|
-
if (overrideDenied)
|
|
25
|
-
return writeFailure(overrideDenied);
|
|
26
23
|
// upsertTenant erzeugt scope='tenant'. SYSTEM_TENANT_ID-Override würde
|
|
27
24
|
// scope='tenant' unter SYSTEM_TENANT_ID schreiben → inkonsistenter Zustand
|
|
28
25
|
// (Resolver-Logik trennt sauber zwischen system+tenant). SystemAdmin muss
|
|
@@ -29,8 +29,7 @@ async function resolveExistingForEventStoreSeed(db, tdb, existingRow, tenantId)
|
|
|
29
29
|
if (unscoped !== 0) {
|
|
30
30
|
throw new Error(`seed orphan check: projection ${id} has no events for tenant ${tenantId} but unscoped stream version is ${unscoped} — refusing delete/recreate to avoid wiping a live stream`);
|
|
31
31
|
}
|
|
32
|
-
//
|
|
33
|
-
// plain table ident trips guard-direct-entity-writes (infra EXCLUDE follow-up).
|
|
32
|
+
// The cast strips the executor-only brand that WritableTable rejects; the orphan row has no events, so no executor verb applies.
|
|
34
33
|
await deleteMany(tdb, templateResourcesTable, { id, tenantId });
|
|
35
34
|
return null;
|
|
36
35
|
}
|
|
@@ -89,6 +89,7 @@ export function createTenantLifecycleFeature() {
|
|
|
89
89
|
fileProviderResolver: ctx._fileProviderResolver,
|
|
90
90
|
searchAdapter: ctx.searchAdapter,
|
|
91
91
|
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
92
|
+
escapeHatchAuditLog: ctx.log,
|
|
92
93
|
actor: ctx.systemUser.id,
|
|
93
94
|
});
|
|
94
95
|
},
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { type EscapeHatchAuditSink, type Registry, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-framework/files";
|
|
5
|
+
import type { Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
5
6
|
import type { SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
6
7
|
type StageTickResult = {
|
|
7
8
|
readonly done: boolean;
|
|
@@ -17,6 +18,7 @@ export declare function runNextDestructionStage(args: {
|
|
|
17
18
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
18
19
|
readonly searchAdapter?: SearchAdapter;
|
|
19
20
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
21
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
20
22
|
readonly actor?: string;
|
|
21
23
|
}): Promise<StageTickResult>;
|
|
22
24
|
export declare function hasTenantDestructionWork(reads: Pick<TenantDb, "count">, now: Temporal.Instant): Promise<boolean>;
|
|
@@ -29,6 +31,7 @@ export declare function runTenantDestructionSweep(args: {
|
|
|
29
31
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
30
32
|
readonly searchAdapter?: SearchAdapter;
|
|
31
33
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
34
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
32
35
|
readonly actor?: string;
|
|
33
36
|
}): Promise<{
|
|
34
37
|
readonly triggered: number;
|
|
@@ -162,6 +162,7 @@ export async function runNextDestructionStage(args) {
|
|
|
162
162
|
fileProviderResolver: args.fileProviderResolver,
|
|
163
163
|
searchAdapter: args.searchAdapter,
|
|
164
164
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
165
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
165
166
|
actor: args.actor,
|
|
166
167
|
deadlineAt: Date.now() + DESTRUCTION_STAGE_TICK_BUDGET_MS,
|
|
167
168
|
};
|
|
@@ -258,6 +259,7 @@ export async function runTenantDestructionSweep(args) {
|
|
|
258
259
|
fileProviderResolver: args.fileProviderResolver,
|
|
259
260
|
searchAdapter: args.searchAdapter,
|
|
260
261
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
262
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
261
263
|
actor: args.actor,
|
|
262
264
|
});
|
|
263
265
|
if (!result.error && !result.halted)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import { type EscapeHatchAuditSink, type Registry, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-framework/files";
|
|
4
|
+
import { type Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
4
5
|
import { type SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
5
6
|
import type { TenantDestructionStageName } from "./constants.js";
|
|
6
7
|
export type DestructionStageCtx = {
|
|
@@ -11,6 +12,7 @@ export type DestructionStageCtx = {
|
|
|
11
12
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
12
13
|
readonly searchAdapter?: SearchAdapter;
|
|
13
14
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
15
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
14
16
|
readonly actor?: string;
|
|
15
17
|
readonly deadlineAt: number;
|
|
16
18
|
};
|
|
@@ -2,6 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { configuredPiiSubjectKms, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
3
|
import { createEventStoreExecutor, createTenantDb, } from "@cosmicdrift/kumiko-framework/db";
|
|
4
4
|
import { createSystemUser, EXT_EXTERNAL_RESOURCE, EXT_INFRA_RESOURCE, EXT_SEARCH_ADAPTER, EXT_STORAGE_PROVIDER, EXT_TENANT_DATA, extensionUsageEscapeHatchReason, isTenantDataExtensionHooks, isTenantResourceExtensionHooks, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
+
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
5
6
|
import { createEscapeHatchReporter, UNATTRIBUTED_ACTOR, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
6
7
|
import { purgeSearchDocumentsForSubject, } from "@cosmicdrift/kumiko-framework/search";
|
|
7
8
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
@@ -51,6 +52,7 @@ async function runTenantDataHooks(ctx) {
|
|
|
51
52
|
tenantId: ctx.tenantId,
|
|
52
53
|
actor: ctx.actor ?? UNATTRIBUTED_ACTOR,
|
|
53
54
|
sink: ctx.escapeHatchAuditSink,
|
|
55
|
+
log: ctx.escapeHatchAuditLog ?? createFallbackLogger("tenant-lifecycle"),
|
|
54
56
|
});
|
|
55
57
|
const hookCtx = {
|
|
56
58
|
db: createTenantDb(ctx.db, ctx.tenantId, "tenant", undefined, undefined, undefined, {
|
|
@@ -477,6 +477,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
477
477
|
appExportDownloadUrl: opts.appExportDownloadUrl,
|
|
478
478
|
}),
|
|
479
479
|
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
480
|
+
escapeHatchAuditLog: ctx.log,
|
|
480
481
|
actor: ctx.systemUser.id,
|
|
481
482
|
metrics: exportMetrics,
|
|
482
483
|
});
|
|
@@ -541,6 +542,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
541
542
|
}),
|
|
542
543
|
...(sendDeletionExecutedEmail && { sendDeletionExecutedEmail }),
|
|
543
544
|
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
545
|
+
escapeHatchAuditLog: ctx.log,
|
|
544
546
|
actor: ctx.systemUser.id,
|
|
545
547
|
});
|
|
546
548
|
// Persist each incomplete report on the job-run log (#1572). The write
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
1
2
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
3
|
import type { EscapeHatchAuditSink, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
4
|
import { type FileStorageProvider } from "@cosmicdrift/kumiko-framework/files";
|
|
@@ -75,6 +76,7 @@ export interface RunExportJobsArgs {
|
|
|
75
76
|
* Callback durch. Required wenn sendExportReadyEmail gesetzt. */
|
|
76
77
|
readonly appExportDownloadUrl?: string;
|
|
77
78
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
79
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
78
80
|
readonly actor?: string;
|
|
79
81
|
/**
|
|
80
82
|
* Reports EXPORT_CLEANUP_BACKLOG_AGE_METRIC after the storage-cleanup
|
|
@@ -106,6 +106,7 @@ export async function runExportJobs(args) {
|
|
|
106
106
|
now,
|
|
107
107
|
job,
|
|
108
108
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
109
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
109
110
|
actor: args.actor,
|
|
110
111
|
});
|
|
111
112
|
if (outcome.kind === "done") {
|
|
@@ -230,6 +231,7 @@ async function processJob(args) {
|
|
|
230
231
|
userId: job.userId,
|
|
231
232
|
now,
|
|
232
233
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
234
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
233
235
|
actor: args.actor,
|
|
234
236
|
});
|
|
235
237
|
const profile = await resolveProfileForTenant({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
1
2
|
import { type KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
3
|
import { type DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
3
4
|
import { type EscapeHatchAuditSink, type Registry, type TenantId, type TenantUserModel, type UserDataDeleteStrategy, type UserDataStorageProvider } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -69,6 +70,7 @@ export interface RunForgetCleanupArgs {
|
|
|
69
70
|
*/
|
|
70
71
|
readonly tenantModel?: TenantUserModel;
|
|
71
72
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
73
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
72
74
|
readonly actor?: string;
|
|
73
75
|
}
|
|
74
76
|
export interface ForgetCleanupError {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
1
2
|
// Forget-Cleanup-Runner (S2.U5b) — pure-Function Pipeline.
|
|
2
3
|
//
|
|
3
4
|
// After the grace period (S2.U5a sets gracePeriodEnd) this runner iterates
|
|
@@ -51,6 +52,7 @@ function buildHookDb(db, tenantId, entry, args) {
|
|
|
51
52
|
tenantId,
|
|
52
53
|
actor: args.actor ?? UNATTRIBUTED_ACTOR,
|
|
53
54
|
sink: args.escapeHatchAuditSink,
|
|
55
|
+
log: args.escapeHatchAuditLog ?? createFallbackLogger("user-data-rights"),
|
|
54
56
|
});
|
|
55
57
|
return createTenantDb(db, tenantId, "tenant", undefined, undefined, undefined, {
|
|
56
58
|
unsafeRaw: entry.escapeHatchReason !== undefined ? { reason: entry.escapeHatchReason } : undefined,
|
|
@@ -117,6 +119,7 @@ export async function runForgetCleanup(args) {
|
|
|
117
119
|
kms,
|
|
118
120
|
searchAdapter: args.searchAdapter,
|
|
119
121
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
122
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
120
123
|
actor: args.actor,
|
|
121
124
|
});
|
|
122
125
|
hookCallsAttempted += userResult.hookCallsAttempted;
|
|
@@ -235,6 +238,7 @@ async function processUser(args) {
|
|
|
235
238
|
const hookResult = await entry.deleteHook({
|
|
236
239
|
db: buildHookDb(tx, tenantId, entry, {
|
|
237
240
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
241
|
+
escapeHatchAuditLog: args.escapeHatchAuditLog,
|
|
238
242
|
actor: args.actor,
|
|
239
243
|
}),
|
|
240
244
|
registry,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
1
2
|
import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
2
3
|
import { type EscapeHatchAuditSink, type Registry, type TenantId, type UserDataExportSnippet } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
4
|
import type { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
@@ -8,6 +9,7 @@ export interface RunUserExportArgs {
|
|
|
8
9
|
readonly userId: string;
|
|
9
10
|
readonly now: Instant;
|
|
10
11
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
12
|
+
readonly escapeHatchAuditLog?: Logger;
|
|
11
13
|
readonly actor?: string;
|
|
12
14
|
}
|
|
13
15
|
export interface UserExportFileRef {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
1
2
|
// User-Data-Export-Pipeline (S2.U3) — DSGVO Art. 15 (Auskunft) +
|
|
2
3
|
// Art. 20 (Datenportabilität).
|
|
3
4
|
//
|
|
@@ -32,6 +33,7 @@ function buildHookDb(db, tenantId, entry, args) {
|
|
|
32
33
|
tenantId,
|
|
33
34
|
actor: args.actor ?? UNATTRIBUTED_ACTOR,
|
|
34
35
|
sink: args.escapeHatchAuditSink,
|
|
36
|
+
log: args.escapeHatchAuditLog ?? createFallbackLogger("user-data-rights"),
|
|
35
37
|
});
|
|
36
38
|
return createTenantDb(db, tenantId, "tenant", undefined, undefined, undefined, {
|
|
37
39
|
unsafeRaw: entry.escapeHatchReason !== undefined ? { reason: entry.escapeHatchReason } : undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-bundled-features",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.355.0",
|
|
4
4
|
"description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -516,12 +516,12 @@
|
|
|
516
516
|
}
|
|
517
517
|
},
|
|
518
518
|
"dependencies": {
|
|
519
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
520
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
521
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
522
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
523
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
524
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
519
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.355.0",
|
|
520
|
+
"@cosmicdrift/kumiko-framework": "0.355.0",
|
|
521
|
+
"@cosmicdrift/kumiko-headless": "0.355.0",
|
|
522
|
+
"@cosmicdrift/kumiko-renderer": "0.355.0",
|
|
523
|
+
"@cosmicdrift/kumiko-renderer-web": "0.355.0",
|
|
524
|
+
"@cosmicdrift/kumiko-types": "0.355.0",
|
|
525
525
|
"@mollie/api-client": "^4.5.0",
|
|
526
526
|
"@node-rs/argon2": "^2.0.2",
|
|
527
527
|
"@types/mailparser": "^3.4.6",
|
|
@@ -1050,8 +1050,8 @@
|
|
|
1050
1050
|
],
|
|
1051
1051
|
"devDependencies": {
|
|
1052
1052
|
"@testing-library/user-event": "^14.6.1",
|
|
1053
|
-
"@cosmicdrift/kumiko-locale-de": "0.
|
|
1054
|
-
"@cosmicdrift/kumiko-locale-es": "0.
|
|
1053
|
+
"@cosmicdrift/kumiko-locale-de": "0.355.0",
|
|
1054
|
+
"@cosmicdrift/kumiko-locale-es": "0.355.0",
|
|
1055
1055
|
"jsqr": "^1.4.0"
|
|
1056
1056
|
}
|
|
1057
1057
|
}
|