@cosmicdrift/kumiko-bundled-features 0.113.1 → 1.0.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.
Files changed (125) hide show
  1. package/package.json +6 -8
  2. package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +3 -3
  3. package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +3 -3
  4. package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -9
  5. package/src/auth-email-password/__tests__/auth.integration.test.ts +5 -5
  6. package/src/auth-email-password/__tests__/email-verification.integration.test.ts +4 -7
  7. package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
  8. package/src/auth-email-password/__tests__/password-reset.integration.test.ts +4 -3
  9. package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
  10. package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +3 -3
  11. package/src/auth-email-password/constants.ts +0 -4
  12. package/src/auth-email-password/feature.ts +4 -8
  13. package/src/auth-email-password/handlers/change-password.write.ts +11 -11
  14. package/src/auth-email-password/handlers/confirm-token-flow.ts +104 -33
  15. package/src/auth-email-password/handlers/login.write.ts +1 -4
  16. package/src/auth-email-password/password-hashing.ts +0 -11
  17. package/src/auth-email-password/stream-tenant.ts +31 -0
  18. package/src/auth-email-password/web/auth-form-primitives.tsx +1 -18
  19. package/src/auth-email-password/web/auth-gate.tsx +4 -3
  20. package/src/auth-email-password/web/invite-accept-screen.tsx +2 -2
  21. package/src/auth-email-password/web/reset-password-screen.tsx +11 -6
  22. package/src/auth-email-password/web/signup-complete-screen.tsx +5 -5
  23. package/src/auth-email-password/web/user-menu.tsx +1 -3
  24. package/src/auth-email-password/web/verify-email-screen.tsx +7 -2
  25. package/src/config/__tests__/config.integration.test.ts +8 -7
  26. package/src/config/__tests__/deserialize-value.test.ts +6 -3
  27. package/src/config/__tests__/inherited-redaction.integration.test.ts +3 -4
  28. package/src/config/feature.ts +8 -14
  29. package/src/config/handlers/set.write.ts +2 -2
  30. package/src/config/index.ts +3 -4
  31. package/src/config/resolver.ts +10 -34
  32. package/src/custom-fields/__tests__/custom-fields.integration.test.ts +0 -65
  33. package/src/custom-fields/constants.ts +0 -1
  34. package/src/custom-fields/events.ts +0 -6
  35. package/src/custom-fields/handlers/delete-system-field.write.ts +1 -2
  36. package/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -2
  37. package/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  38. package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -10
  39. package/src/custom-fields/wire-for-entity.ts +77 -116
  40. package/src/data-retention/__tests__/parse-override.test.ts +13 -13
  41. package/src/file-foundation/__tests__/file-foundation.integration.test.ts +3 -4
  42. package/src/folders/__tests__/folders.integration.test.ts +0 -3
  43. package/src/folders/web/__tests__/tree.test.ts +0 -8
  44. package/src/folders/web/folder-manager.tsx +3 -1
  45. package/src/folders/web/i18n.ts +2 -0
  46. package/src/folders/web/tree.ts +2 -13
  47. package/src/folders-user-data/__tests__/hooks.integration.test.ts +0 -1
  48. package/src/folders-user-data/index.ts +4 -4
  49. package/src/jobs/__tests__/job-system-user.integration.test.ts +8 -4
  50. package/src/jobs/handlers/projection-rebuild.job.ts +4 -14
  51. package/src/ledger/__tests__/ledger.integration.test.ts +3 -8
  52. package/src/ledger/handlers/confirm-schedule-period.write.ts +4 -17
  53. package/src/ledger/recurring.ts +8 -17
  54. package/src/ledger/web/index.ts +2 -3
  55. package/src/legal-pages/feature.ts +3 -1
  56. package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +3 -4
  57. package/src/managed-pages/__tests__/branding-coerce.test.ts +6 -2
  58. package/src/managed-pages/feature.ts +3 -1
  59. package/src/readiness/__tests__/readiness.integration.test.ts +3 -4
  60. package/src/secrets/handlers/rotate.job.ts +98 -77
  61. package/src/secrets/secrets-context.ts +38 -7
  62. package/src/secrets/table.ts +7 -6
  63. package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +4 -6
  64. package/src/sessions/__tests__/sessions.integration.test.ts +3 -3
  65. package/src/sessions/feature.ts +18 -51
  66. package/src/sessions/index.ts +2 -2
  67. package/src/subscription-stripe/__tests__/runtime.test.ts +24 -14
  68. package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +5 -6
  69. package/src/tags/__tests__/tags.integration.test.ts +1 -12
  70. package/src/tags/entity.ts +1 -6
  71. package/src/tags/schemas.ts +2 -9
  72. package/src/tags/web/tag-manager.tsx +1 -1
  73. package/src/template-resolver/testing.ts +1 -2
  74. package/src/tenant/__tests__/tenant.integration.test.ts +4 -8
  75. package/src/tier-engine/__tests__/tier-engine.integration.test.ts +4 -7
  76. package/src/user/__tests__/user.integration.test.ts +0 -26
  77. package/src/user/handlers/find-for-auth.query.ts +5 -7
  78. package/src/user/index.ts +0 -4
  79. package/src/user-data-rights/__tests__/download.integration.test.ts +4 -7
  80. package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +3 -3
  81. package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +3 -3
  82. package/src/user-data-rights/feature.ts +3 -1
  83. package/src/user-data-rights/handlers/download-by-job.query.ts +6 -4
  84. package/src/user-data-rights/lib/update-user-lifecycle.ts +24 -8
  85. package/src/user-data-rights/run-forget-cleanup.ts +0 -1
  86. package/src/user-data-rights/run-user-export.ts +4 -43
  87. package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +1 -7
  88. package/src/user-data-rights/web/client-plugin.tsx +1 -6
  89. package/src/user-data-rights/web/i18n.ts +10 -0
  90. package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +9 -31
  91. package/src/user-profile/__tests__/profile-screen.test.tsx +0 -4
  92. package/src/user-profile/handlers/change-email.write.ts +8 -11
  93. package/src/user-profile/web/profile-screen.tsx +1 -12
  94. package/src/auth-email-password/__tests__/feature-options.test.ts +0 -33
  95. package/src/auth-email-password/password-hashing.test.ts +0 -35
  96. package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +0 -29
  97. package/src/config/__tests__/encrypted-legacy-migration.integration.test.ts +0 -179
  98. package/src/config/handlers/reencrypt.job.ts +0 -197
  99. package/src/personal-access-tokens/__tests__/pat.integration.test.ts +0 -204
  100. package/src/personal-access-tokens/__tests__/scopes.test.ts +0 -42
  101. package/src/personal-access-tokens/constants.ts +0 -33
  102. package/src/personal-access-tokens/feature.ts +0 -78
  103. package/src/personal-access-tokens/handlers/available-scopes.query.ts +0 -21
  104. package/src/personal-access-tokens/handlers/create.write.ts +0 -41
  105. package/src/personal-access-tokens/handlers/list.query.ts +0 -47
  106. package/src/personal-access-tokens/handlers/revoke.write.ts +0 -37
  107. package/src/personal-access-tokens/hash.ts +0 -17
  108. package/src/personal-access-tokens/index.ts +0 -45
  109. package/src/personal-access-tokens/resolver.ts +0 -58
  110. package/src/personal-access-tokens/roles.ts +0 -29
  111. package/src/personal-access-tokens/schema/api-token.ts +0 -56
  112. package/src/personal-access-tokens/scopes.ts +0 -43
  113. package/src/personal-access-tokens/web/client-plugin.tsx +0 -29
  114. package/src/personal-access-tokens/web/i18n.ts +0 -69
  115. package/src/personal-access-tokens/web/index.ts +0 -7
  116. package/src/personal-access-tokens/web/pat-tokens-screen.tsx +0 -251
  117. package/src/sessions/__tests__/auto-revoke-binding.test.ts +0 -71
  118. package/src/shared/chunked-entity-migration.ts +0 -89
  119. package/src/shared/index.ts +0 -7
  120. package/src/tags/__tests__/schemas.test.ts +0 -34
  121. package/src/tags/web/__tests__/tag-manager.test.tsx +0 -64
  122. package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +0 -186
  123. package/src/user/db/queries/stream-tenant-backfill.ts +0 -112
  124. package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +0 -152
  125. package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +0 -108
@@ -1,152 +0,0 @@
1
- // Art. 20 + encrypted entity fields: export hooks read raw rows past the
2
- // executor's decrypt layer, so runUserExport must decrypt encrypted fields
3
- // centrally — plaintext with a configured cipher, an explicit marker
4
- // (never the raw ciphertext) without one.
5
-
6
- import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
7
- import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
8
- import {
9
- buildEntityTable,
10
- configureEntityFieldEncryption,
11
- resetEntityFieldEncryptionCacheForTests,
12
- } from "@cosmicdrift/kumiko-framework/db";
13
- import {
14
- createEntity,
15
- createRegistry,
16
- createTextField,
17
- defineFeature,
18
- EXT_USER_DATA,
19
- type UserDataExportHook,
20
- } from "@cosmicdrift/kumiko-framework/engine";
21
- import { setupTestStack, type TestStack } from "@cosmicdrift/kumiko-framework/stack";
22
- import { createTestEnvelopeCipher } from "@cosmicdrift/kumiko-framework/testing";
23
- import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
24
- import { createComplianceProfilesFeature } from "../../compliance-profiles";
25
- import { createDataRetentionFeature } from "../../data-retention";
26
- import { createSessionsFeature } from "../../sessions";
27
- import { createUserFeature } from "../../user";
28
- import { createUserDataRightsFeature } from "../feature";
29
- import { runUserExport } from "../run-user-export";
30
-
31
- const TENANT = "00000000-0000-4000-8000-00000000000a";
32
- const USER_ID = "cccccccc-cccc-4ccc-8ccc-000000000001";
33
- const PLAINTEXT_NOTE = "my private vault note";
34
-
35
- const cipher = createTestEnvelopeCipher();
36
-
37
- const vaultNoteEntity = createEntity({
38
- table: "read_vault_notes",
39
- fields: {
40
- ownerId: createTextField({ required: true }),
41
- note: createTextField({ encrypted: true }),
42
- },
43
- });
44
-
45
- const vaultNotesTableRef = buildEntityTable("vault-note", vaultNoteEntity);
46
-
47
- // Export hook mirrors real hooks (folders-user-data pattern): raw select,
48
- // no decrypt of its own.
49
- const vaultExportHook: UserDataExportHook = async (ctx) => {
50
- const rows = await selectMany<Record<string, unknown>>(ctx.db, vaultNotesTableRef, {
51
- ownerId: ctx.userId,
52
- });
53
- if (rows.length === 0) return null;
54
- return { entity: "vault-note", rows };
55
- };
56
-
57
- const vaultFeature = defineFeature("vault", (r) => {
58
- const entity = r.entity("vault-note", vaultNoteEntity);
59
- r.useExtension(EXT_USER_DATA, entity, {
60
- export: vaultExportHook,
61
- delete: async () => undefined,
62
- });
63
- });
64
-
65
- let stack: TestStack;
66
- let registry: ReturnType<typeof createRegistry>;
67
-
68
- beforeAll(async () => {
69
- stack = await setupTestStack({ features: [] });
70
- // user-data-rights hosts EXT_USER_DATA — without it the vault feature's
71
- // useExtension reference fails registry creation. Its hard requires
72
- // (user/data-retention/compliance-profiles/sessions) come along.
73
- registry = createRegistry([
74
- createUserFeature(),
75
- createDataRetentionFeature(),
76
- createComplianceProfilesFeature(),
77
- createSessionsFeature(),
78
- createUserDataRightsFeature(),
79
- vaultFeature,
80
- ]);
81
-
82
- await asRawClient(stack.db).unsafe(`
83
- CREATE TABLE IF NOT EXISTS read_tenant_memberships (
84
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
85
- tenant_id UUID NOT NULL,
86
- user_id TEXT NOT NULL,
87
- version INTEGER NOT NULL DEFAULT 0,
88
- inserted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
89
- modified_at TIMESTAMPTZ NOT NULL DEFAULT now(),
90
- inserted_by_id TEXT,
91
- modified_by_id TEXT,
92
- is_deleted BOOLEAN NOT NULL DEFAULT false,
93
- deleted_at TIMESTAMPTZ,
94
- deleted_by_id TEXT,
95
- roles TEXT NOT NULL DEFAULT '[]',
96
- UNIQUE(user_id, tenant_id)
97
- )
98
- `);
99
- await asRawClient(stack.db).unsafe(
100
- `INSERT INTO read_tenant_memberships (tenant_id, user_id) VALUES ('${TENANT}', '${USER_ID}')
101
- ON CONFLICT DO NOTHING`,
102
- );
103
-
104
- await asRawClient(stack.db).unsafe(`
105
- CREATE TABLE IF NOT EXISTS read_vault_notes (
106
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
107
- tenant_id UUID NOT NULL,
108
- version INTEGER NOT NULL DEFAULT 0,
109
- inserted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
110
- modified_at TIMESTAMPTZ NOT NULL DEFAULT now(),
111
- inserted_by_id TEXT,
112
- modified_by_id TEXT,
113
- owner_id TEXT NOT NULL,
114
- note TEXT
115
- )
116
- `);
117
- const storedNote = await cipher.encrypt(PLAINTEXT_NOTE);
118
- await asRawClient(stack.db).unsafe(
119
- `INSERT INTO read_vault_notes (tenant_id, owner_id, note) VALUES ('${TENANT}', '${USER_ID}', '${storedNote.replaceAll("'", "''")}')`,
120
- );
121
- });
122
-
123
- afterEach(() => {
124
- resetEntityFieldEncryptionCacheForTests();
125
- });
126
-
127
- afterAll(async () => {
128
- await stack.cleanup();
129
- });
130
-
131
- async function exportedNote(): Promise<unknown> {
132
- const bundle = await runUserExport({
133
- db: stack.db,
134
- registry,
135
- userId: USER_ID,
136
- now: getTemporal().Now.instant(),
137
- });
138
- const snippet = bundle.tenants.flatMap((t) => t.entities).find((e) => e.entity === "vault-note");
139
- return snippet?.rows[0]?.["note"];
140
- }
141
-
142
- describe("user export with encrypted entity fields", () => {
143
- test("decrypts encrypted fields when the cipher is configured", async () => {
144
- configureEntityFieldEncryption(cipher);
145
- expect(await exportedNote()).toBe(PLAINTEXT_NOTE);
146
- });
147
-
148
- test("exports an explicit marker — never ciphertext — without a cipher", async () => {
149
- const value = await exportedNote();
150
- expect(value).toBe("[encrypted:unavailable]");
151
- });
152
- });
@@ -1,108 +0,0 @@
1
- // A forget delete-hook receives the app registry so it can cascade custom child
2
- // projections for the executor's `<entity>.forgotten` event (runProjectionsForEvent).
3
- // executor.forget purges only the entity's OWN projection, and the forget pipeline
4
- // is a job — not a dispatched command — so the dispatcher's post-command projection
5
- // pass never fires. Without registry in the hook ctx the cascade is unreachable and
6
- // child read-model rows (m:n joins, per-parent detail projections) are orphaned on
7
- // live forget (a DSGVO gap). This pins that the plumbing delivers the real,
8
- // functional registry instance; the end-to-end cascade is covered consumer-side.
9
-
10
- import { afterAll, beforeAll, beforeEach, expect, test } from "bun:test";
11
- import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
12
- import {
13
- createEntity,
14
- createTextField,
15
- defineFeature,
16
- EXT_USER_DATA,
17
- type Registry,
18
- type UserDataDeleteHook,
19
- } from "@cosmicdrift/kumiko-framework/engine";
20
- import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
21
- import {
22
- setupTestStack,
23
- type TestStack,
24
- unsafeCreateEntityTable,
25
- } from "@cosmicdrift/kumiko-framework/stack";
26
- import { createComplianceProfilesFeature } from "../../compliance-profiles";
27
- import { createDataRetentionFeature, tenantRetentionOverrideEntity } from "../../data-retention";
28
- import { createSessionsFeature } from "../../sessions";
29
- import { createUserFeature, userEntity } from "../../user";
30
- import { createUserDataRightsFeature } from "../feature";
31
- import { runForgetCleanup } from "../run-forget-cleanup";
32
- import {
33
- createForgetSeeders,
34
- nowInstant,
35
- READ_TENANT_MEMBERSHIPS_DDL,
36
- TENANT_SYSTEM,
37
- } from "./forget-test-helpers";
38
-
39
- const PROBE = "forget-registry-probe";
40
- let capturedRegistry: Registry | undefined;
41
-
42
- const captureHook: UserDataDeleteHook = async (ctx) => {
43
- capturedRegistry = ctx.registry;
44
- };
45
-
46
- const probeEntity = createEntity({
47
- table: `read_${PROBE.replace(/-/g, "_")}`,
48
- fields: { name: createTextField({ required: true }) },
49
- });
50
-
51
- const probeFeature = defineFeature(PROBE, (r) => {
52
- r.entity(PROBE, probeEntity);
53
- r.useExtension(EXT_USER_DATA, PROBE, {
54
- export: async () => null,
55
- delete: captureHook,
56
- });
57
- });
58
-
59
- const FORGET_USER = "cccccccc-cccc-4ccc-8ccc-0000000000b1";
60
- let stack: TestStack;
61
- const seed = (db: unknown) =>
62
- // biome-ignore lint/suspicious/noExplicitAny: dummy writer; this seeder never writes binaries.
63
- createForgetSeeders(db as any, { write: async () => {} });
64
-
65
- beforeAll(async () => {
66
- stack = await setupTestStack({
67
- features: [
68
- createUserFeature(),
69
- createSessionsFeature(),
70
- createDataRetentionFeature(),
71
- createComplianceProfilesFeature(),
72
- createUserDataRightsFeature(),
73
- probeFeature,
74
- ],
75
- });
76
- await unsafeCreateEntityTable(stack.db, userEntity);
77
- await unsafeCreateEntityTable(stack.db, tenantRetentionOverrideEntity);
78
- await unsafeCreateEntityTable(stack.db, probeEntity);
79
- await createEventsTable(stack.db);
80
- await asRawClient(stack.db).unsafe(READ_TENANT_MEMBERSHIPS_DDL);
81
- });
82
-
83
- afterAll(async () => {
84
- await stack.cleanup();
85
- });
86
-
87
- beforeEach(() => {
88
- capturedRegistry = undefined;
89
- });
90
-
91
- test("forget delete-hook receives the app registry instance", async () => {
92
- await seed(stack.db).seedForgetUser(FORGET_USER);
93
- await seed(stack.db).seedMembership(FORGET_USER, TENANT_SYSTEM);
94
-
95
- const result = await runForgetCleanup({
96
- db: stack.db,
97
- registry: stack.registry,
98
- now: nowInstant(),
99
- });
100
-
101
- expect(result.errors, JSON.stringify(result.errors)).toHaveLength(0);
102
- expect(result.processedUserIds).toContain(FORGET_USER);
103
- // The hook ran and got the exact registry passed to runForgetCleanup — not
104
- // undefined, not a stand-in — so runProjectionsForEvent(event, ctx.registry, db)
105
- // reaches the real projections.
106
- expect(capturedRegistry).toBe(stack.registry);
107
- expect(capturedRegistry?.getProjectionsForSource(PROBE)).toBeDefined();
108
- });