@cosmicdrift/kumiko-bundled-features 0.89.0 → 0.90.2
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 -6
- package/src/folders/__tests__/drift.test.ts +43 -0
- package/src/folders/__tests__/feature.test.ts +168 -0
- package/src/folders/__tests__/folders.integration.test.ts +290 -0
- package/src/folders/aggregate-id.ts +23 -0
- package/src/folders/constants.ts +40 -0
- package/src/folders/entity.ts +42 -0
- package/src/folders/executor.ts +11 -0
- package/src/folders/feature.ts +106 -0
- package/src/folders/handlers/clear-folder.write.ts +35 -0
- package/src/folders/handlers/set-folder.write.ts +82 -0
- package/src/folders/index.ts +23 -0
- package/src/folders/schemas.ts +18 -0
- package/src/folders/web/__tests__/folder-section.test.tsx +181 -0
- package/src/folders/web/__tests__/tree.test.ts +58 -0
- package/src/folders/web/client-plugin.tsx +16 -0
- package/src/folders/web/folder-manager.tsx +323 -0
- package/src/folders/web/folder-section.tsx +198 -0
- package/src/folders/web/i18n.ts +55 -0
- package/src/folders/web/index.ts +6 -0
- package/src/folders/web/tree.ts +54 -0
- package/src/folders-user-data/hooks.ts +58 -0
- package/src/folders-user-data/index.ts +33 -0
- package/src/user-data-rights/feature.ts +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// EXT_USER_DATA hooks for the folder + folder-assignment entities (GDPR Art. 20
|
|
2
|
+
// export / Art. 17 erasure). Lives apart from the folders feature so folders
|
|
3
|
+
// consumers without the user-data-rights pipeline don't pull a hard dependency.
|
|
4
|
+
// Mirrors credit-user-data — standard tenant-scoped pattern, no name-stripping
|
|
5
|
+
// (a folder name is tenant data, not per-user PII).
|
|
6
|
+
|
|
7
|
+
import { deleteMany, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
8
|
+
import {
|
|
9
|
+
createEntityExecutor,
|
|
10
|
+
type UserDataDeleteHook,
|
|
11
|
+
type UserDataExportHook,
|
|
12
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
13
|
+
import { folderAssignmentEntity, folderEntity } from "../folders";
|
|
14
|
+
|
|
15
|
+
const { table: folderTable } = createEntityExecutor("folder", folderEntity);
|
|
16
|
+
const { table: folderAssignmentTable } = createEntityExecutor(
|
|
17
|
+
"folder-assignment",
|
|
18
|
+
folderAssignmentEntity,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Folders are tenant-scoped (no per-user owner column). Every tenant user reads
|
|
22
|
+
// all tenant folders in-app, so bundling them into the user's export is no new
|
|
23
|
+
// exposure — it gives the data subject the organisation of the loans they work with.
|
|
24
|
+
export const folderExportHook: UserDataExportHook = async (ctx) => {
|
|
25
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, folderTable, {
|
|
26
|
+
tenantId: ctx.tenantId,
|
|
27
|
+
});
|
|
28
|
+
if (rows.length === 0) return null;
|
|
29
|
+
return { entity: "folder", rows };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const folderAssignmentExportHook: UserDataExportHook = async (ctx) => {
|
|
33
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, folderAssignmentTable, {
|
|
34
|
+
tenantId: ctx.tenantId,
|
|
35
|
+
});
|
|
36
|
+
if (rows.length === 0) return null;
|
|
37
|
+
return { entity: "folder-assignment", rows };
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Tenant-scoped erasure is only safe when the tenant is effectively single-user
|
|
41
|
+
// (set by the forget orchestrator from the app's tenantModel + a runtime
|
|
42
|
+
// sole-member check). In a multi-user tenant this stays a no-op: deleting by
|
|
43
|
+
// tenant would destroy co-members' folders. anonymize is also a no-op — folder
|
|
44
|
+
// rows carry no person-link to strip (name is tenant data, not PII), so a
|
|
45
|
+
// retention hold simply keeps them.
|
|
46
|
+
function tenantScopedDelete(table: typeof folderTable): UserDataDeleteHook {
|
|
47
|
+
return async (ctx, strategy) => {
|
|
48
|
+
// skip: multi-user tenant — a tenant-wide delete would destroy co-members' folders
|
|
49
|
+
if (ctx.tenantModel !== "single-user") return;
|
|
50
|
+
// skip: anonymize is a no-op — folder rows carry no per-user PII to strip
|
|
51
|
+
if (strategy === "anonymize") return;
|
|
52
|
+
await deleteMany(ctx.db, table, { tenantId: ctx.tenantId });
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const folderDeleteHook: UserDataDeleteHook = tenantScopedDelete(folderTable);
|
|
57
|
+
export const folderAssignmentDeleteHook: UserDataDeleteHook =
|
|
58
|
+
tenantScopedDelete(folderAssignmentTable);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Provides the EXT_USER_DATA export/delete hooks for the folder + folder-assignment
|
|
2
|
+
// entities as a standalone feature — mount it alongside the folders feature +
|
|
3
|
+
// user-data-rights when an app needs folders in its GDPR export/forget pipeline.
|
|
4
|
+
// Kept separate from the folders feature (which only requires "tenant") so
|
|
5
|
+
// folders stays usable without the user-data-rights stack. Mirrors credit-user-data.
|
|
6
|
+
|
|
7
|
+
import { defineFeature, EXT_USER_DATA } from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
+
import {
|
|
9
|
+
folderAssignmentDeleteHook,
|
|
10
|
+
folderAssignmentExportHook,
|
|
11
|
+
folderDeleteHook,
|
|
12
|
+
folderExportHook,
|
|
13
|
+
} from "./hooks";
|
|
14
|
+
|
|
15
|
+
export const foldersUserDataFeature = defineFeature("folders-user-data", (r) => {
|
|
16
|
+
r.describe(
|
|
17
|
+
"GDPR (Art. 20 export / Art. 17 erasure) coverage for the `folders` feature's `folder` + `folder-assignment` entities. Mounts the EXT_USER_DATA export + delete hooks so a tenant's folder tree and its entity-to-folder assignments are included in the user-data export bundle and erased on a tenant-scoped forget (single-user tenants only; multi-user + anonymize are no-ops since folder rows carry no per-user PII). Kept separate from `folders` so folder consumers without the user-data-rights pipeline don't pull a hard dependency — requires `user-data-rights`, optionalRequires `folders`.",
|
|
18
|
+
);
|
|
19
|
+
// user-data-rights ist die harte Abhängigkeit (EXT_USER_DATA-Host). `folders` ist
|
|
20
|
+
// OPTIONAL: ist es toggleable(default=false) gemountet (z.B. per-Tenant via Tier),
|
|
21
|
+
// würde ein hartes r.requires eine „effectively disabled"-Boot-Warnung werfen,
|
|
22
|
+
// obwohl die folder-Entities existieren und die Hooks korrekt greifen.
|
|
23
|
+
r.requires("user-data-rights");
|
|
24
|
+
r.optionalRequires("folders");
|
|
25
|
+
r.useExtension(EXT_USER_DATA, "folder", {
|
|
26
|
+
export: folderExportHook,
|
|
27
|
+
delete: folderDeleteHook,
|
|
28
|
+
});
|
|
29
|
+
r.useExtension(EXT_USER_DATA, "folder-assignment", {
|
|
30
|
+
export: folderAssignmentExportHook,
|
|
31
|
+
delete: folderAssignmentDeleteHook,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -133,7 +133,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
133
133
|
let warnedMissingExportUrl = false;
|
|
134
134
|
return defineFeature("user-data-rights", (r) => {
|
|
135
135
|
r.describe(
|
|
136
|
-
'Implements GDPR Art. 15 (access / `my-audit-log` query), Art. 17 (erasure / `request-deletion` + `cancel-deletion`, plus the anonymous email-verified `request-deletion-by-email` + `confirm-deletion-by-token` flow for lockout-safe self-service, + cron cleanup with grace period), Art. 18 (restriction / `restrict-account` + `lift-restriction`), and Art. 20 (portability / async `request-export` \u2192 ZIP via `file-foundation`, Magic-Link download) as first-class HTTP handlers and cron jobs. Each domain feature opts in by calling `r.useExtension(EXT_USER_DATA, "<entity>", { export, delete })` \u2014 the feature then orchestrates the export and forget pipelines across all registered hooks automatically. Requires `user`, `data-retention`, `compliance-profiles`, and `sessions`.',
|
|
136
|
+
'Implements GDPR Art. 15 (access / `my-audit-log` query), Art. 17 (erasure / `request-deletion` + `cancel-deletion`, plus the anonymous email-verified `request-deletion-by-email` + `confirm-deletion-by-token` flow for lockout-safe self-service, + cron cleanup with grace period), Art. 18 (restriction / `restrict-account` + `lift-restriction`), and Art. 20 (portability / async `request-export` \u2192 ZIP via `file-foundation`, Magic-Link download) as first-class HTTP handlers and cron jobs. Each domain feature opts in by calling `r.useExtension(EXT_USER_DATA, "<entity>", { export, delete })` \u2014 the feature then orchestrates the export and forget pipelines across all registered hooks automatically. When `mail-foundation` and a `mail-transport-*` are mounted, it also sends the four GDPR notifications (export ready/failed, deletion requested/executed) itself with no app callback code, rendered in each recipient’s locale. Requires `user`, `data-retention`, `compliance-profiles`, and `sessions`.',
|
|
137
137
|
);
|
|
138
138
|
r.uiHints({
|
|
139
139
|
displayLabel: "User Data Rights \u00b7 GDPR",
|