@cosmicdrift/kumiko-samples 0.342.0 → 0.343.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 (61) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +1 -1
  3. package/packages/bundled-features/src/audit/changes.json +6 -0
  4. package/packages/bundled-features/src/audit/constants.ts +5 -1
  5. package/packages/bundled-features/src/audit/escape-hatch-audit-sink.ts +1 -0
  6. package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +99 -8
  7. package/packages/bundled-features/src/auth-email-password/changes.json +7 -0
  8. package/packages/bundled-features/src/auth-email-password/email-templates.ts +76 -16
  9. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -1
  10. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +4 -1
  11. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +3 -0
  12. package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +4 -0
  13. package/packages/bundled-features/src/channel-discord/feature.ts +1 -0
  14. package/packages/bundled-features/src/channel-email/feature.ts +1 -0
  15. package/packages/bundled-features/src/channel-in-app/feature.ts +1 -0
  16. package/packages/bundled-features/src/channel-push/feature.ts +1 -0
  17. package/packages/bundled-features/src/channel-slack/feature.ts +1 -0
  18. package/packages/bundled-features/src/channel-teams/changes.json +8 -0
  19. package/packages/bundled-features/src/channel-teams/feature.ts +1 -0
  20. package/packages/bundled-features/src/channel-teams/teams-channel.ts +16 -0
  21. package/packages/bundled-features/src/channel-telegram/feature.ts +1 -0
  22. package/packages/bundled-features/src/delivery/changes.json +32 -0
  23. package/packages/bundled-features/src/delivery/chat-webhook-channel.ts +7 -2
  24. package/packages/bundled-features/src/delivery/chat-webhook-sender.ts +60 -14
  25. package/packages/bundled-features/src/delivery/constants.ts +4 -0
  26. package/packages/bundled-features/src/delivery/delivery-service.ts +70 -5
  27. package/packages/bundled-features/src/delivery/events.ts +15 -1
  28. package/packages/bundled-features/src/delivery/feature.ts +32 -4
  29. package/packages/bundled-features/src/delivery/handlers/log.query.ts +65 -20
  30. package/packages/bundled-features/src/delivery/i18n.ts +55 -0
  31. package/packages/bundled-features/src/delivery/index.ts +2 -0
  32. package/packages/bundled-features/src/delivery/jobs.ts +43 -20
  33. package/packages/bundled-features/src/delivery/notify-factory.ts +19 -0
  34. package/packages/bundled-features/src/delivery/public-names.ts +4 -0
  35. package/packages/bundled-features/src/delivery/tables.ts +3 -0
  36. package/packages/bundled-features/src/delivery/testing.ts +5 -9
  37. package/packages/bundled-features/src/delivery/types.ts +6 -2
  38. package/packages/bundled-features/src/delivery/web/client-plugin.tsx +16 -1
  39. package/packages/bundled-features/src/delivery/web/delivery-channel-cell.tsx +13 -0
  40. package/packages/bundled-features/src/delivery/web/delivery-error-cell.tsx +17 -0
  41. package/packages/bundled-features/src/delivery/web/delivery-status-cell.tsx +10 -3
  42. package/packages/bundled-features/src/delivery/web/delivery-time-cell.tsx +8 -0
  43. package/packages/bundled-features/src/delivery/web/delivery-type-cell.tsx +20 -0
  44. package/packages/bundled-features/src/jobs/changes.json +6 -0
  45. package/packages/bundled-features/src/secrets/changes.json +6 -0
  46. package/packages/bundled-features/src/secrets/constants.ts +10 -0
  47. package/packages/bundled-features/src/secrets/feature.ts +4 -0
  48. package/packages/bundled-features/src/secrets/i18n.ts +5 -3
  49. package/packages/bundled-features/src/secrets/index.ts +8 -1
  50. package/packages/bundled-features/src/secrets/write-gate.ts +17 -5
  51. package/packages/bundled-features/src/shared/index.ts +6 -0
  52. package/packages/bundled-features/src/shared/redact.ts +27 -0
  53. package/packages/bundled-features/src/step-dispatcher/feature.ts +2 -6
  54. package/packages/bundled-features/src/step-dispatcher/mail-runner.ts +0 -6
  55. package/packages/bundled-features/src/tenant/web/member-roles-cell.tsx +1 -1
  56. package/packages/bundled-features/src/tenant/web/member-status-cell.tsx +1 -1
  57. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +12 -2
  58. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -8
  59. package/packages/bundled-features/src/user/index.ts +1 -0
  60. package/packages/bundled-features/src/user/resolve-display-names.ts +35 -0
  61. /package/packages/bundled-features/src/{tenant → shared}/web/translate-or-raw.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  /// <reference types="temporal-polyfill/global" preserve="true" />
2
2
  import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
3
- import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
3
+ import type { DbRunner, TenantDb, WhereObject } from "@cosmicdrift/kumiko-framework/db";
4
4
  import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
5
5
  import {
6
6
  createSystemUser,
@@ -307,6 +307,26 @@ export async function runNextDestructionStage(args: {
307
307
  }
308
308
  }
309
309
 
310
+ function destroyRequestedDueWhere(now: Temporal.Instant): WhereObject {
311
+ return {
312
+ status: "destroyRequested",
313
+ gracePeriodEnd: { lte: now.subtract({ milliseconds: TEARDOWN_GATE_SETTLE_MS }) },
314
+ };
315
+ }
316
+
317
+ const destroyingWhere: WhereObject = { status: "destroying" };
318
+
319
+ export async function hasTenantDestructionWork(
320
+ reads: Pick<TenantDb, "count">,
321
+ now: Temporal.Instant,
322
+ ): Promise<boolean> {
323
+ const [due, destroying] = await Promise.all([
324
+ reads.count(tenantTable, destroyRequestedDueWhere(now)),
325
+ reads.count(tenantTable, destroyingWhere),
326
+ ]);
327
+ return due > 0 || destroying > 0;
328
+ }
329
+
310
330
  /** Cron entry: start destruction for tenants past grace, then advance stages. */
311
331
  export async function runTenantDestructionSweep(args: {
312
332
  readonly db: DbRunner;
@@ -319,10 +339,7 @@ export async function runTenantDestructionSweep(args: {
319
339
  }): Promise<{ readonly triggered: number; readonly advanced: number }> {
320
340
  const T = getTemporal();
321
341
  const now = args.now ?? T.Now.instant();
322
- const due = await selectMany<{ id: string }>(args.db, tenantTable, {
323
- status: "destroyRequested",
324
- gracePeriodEnd: { lte: now.subtract({ milliseconds: TEARDOWN_GATE_SETTLE_MS }) },
325
- });
342
+ const due = await selectMany<{ id: string }>(args.db, tenantTable, destroyRequestedDueWhere(now));
326
343
 
327
344
  let triggered = 0;
328
345
  for (const row of due) {
@@ -354,9 +371,7 @@ export async function runTenantDestructionSweep(args: {
354
371
  }
355
372
 
356
373
  let advanced = 0;
357
- const destroying = await selectMany<{ id: string }>(args.db, tenantTable, {
358
- status: "destroying",
359
- });
374
+ const destroying = await selectMany<{ id: string }>(args.db, tenantTable, destroyingWhere);
360
375
  for (const row of destroying) {
361
376
  try {
362
377
  const result = await runNextDestructionStage({
@@ -6,6 +6,7 @@ export {
6
6
  } from "./db/queries/stream-tenant-backfill.js";
7
7
  export { createUserFeature } from "./feature.js";
8
8
  export { isPrincipalBlocked, principalStatusPlugin } from "./principal-status.js";
9
+ export { resolveUserDisplayNames } from "./resolve-display-names.js";
9
10
  export type { UserStatus } from "./schema/user.js";
10
11
  export {
11
12
  USER_ANONYMIZED_DISPLAY_NAME,
@@ -0,0 +1,35 @@
1
+ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
3
+ import { decryptStoredPii, mapWithConcurrency } from "../shared/index.js";
4
+ import { userTable } from "./schema/user.js";
5
+
6
+ // Shares the KMS adapter's small dedicated pool, same as member-directory.
7
+ const KMS_POOL_CONCURRENCY = 4;
8
+
9
+ // One batched lookup for a page of user ids. Users that are missing, have no
10
+ // display name, or whose name cannot be decrypted (e.g. crypto-shredded) are
11
+ // left out, so callers fall back to the raw id instead of failing the page.
12
+ export async function resolveUserDisplayNames(
13
+ db: TenantDb,
14
+ userIds: readonly string[],
15
+ ): Promise<ReadonlyMap<string, string>> {
16
+ const names = new Map<string, string>();
17
+ if (userIds.length === 0) return names;
18
+ const users = await selectMany(db, userTable, { id: [...userIds] });
19
+ const named = users.filter((user) => typeof user.displayName === "string");
20
+ await mapWithConcurrency(named, KMS_POOL_CONCURRENCY, async (user) => {
21
+ try {
22
+ names.set(
23
+ String(user.id),
24
+ await decryptStoredPii(
25
+ String(user.displayName),
26
+ "displayName",
27
+ "user:resolve-display-names",
28
+ ),
29
+ );
30
+ } catch {
31
+ // Unreadable name: the caller's fallback applies.
32
+ }
33
+ });
34
+ return names;
35
+ }