@cosmicdrift/kumiko-bundled-features 0.374.0 → 0.377.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 (205) hide show
  1. package/dist/account-security/constants.d.ts +1 -0
  2. package/dist/account-security/constants.js +1 -0
  3. package/dist/account-security/index.d.ts +1 -1
  4. package/dist/account-security/index.js +1 -1
  5. package/dist/admin-shell/web/client-plugin.d.ts +1 -1
  6. package/dist/audit/web/client-plugin.d.ts +1 -1
  7. package/dist/auth-email-password/mail-notifications.js +1 -1
  8. package/dist/auth-email-password/web/auth-client.js +1 -1
  9. package/dist/auth-email-password/web/auth-form-primitives.js +1 -1
  10. package/dist/auth-email-password/web/default-topbar-actions.js +1 -1
  11. package/dist/auth-email-password/web/index.js +1 -1
  12. package/dist/auth-email-password/web/session.js +1 -1
  13. package/dist/auth-email-password/web/tenant-menu-items.js +1 -1
  14. package/dist/auth-email-password/web/tenant-switcher.js +1 -1
  15. package/dist/auth-email-password/web/user-menu.js +1 -1
  16. package/dist/auth-foundation/env.d.ts +9 -0
  17. package/dist/auth-foundation/env.js +21 -0
  18. package/dist/auth-foundation/index.d.ts +1 -0
  19. package/dist/auth-foundation/index.js +1 -0
  20. package/dist/auth-mfa/schema/user-mfa.d.ts +6 -6
  21. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +1 -1
  22. package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -1
  23. package/dist/billing-foundation/contact-card.d.ts +2 -0
  24. package/dist/billing-foundation/contact-card.js +16 -0
  25. package/dist/billing-foundation/entities.d.ts +8 -8
  26. package/dist/billing-foundation/handlers/billing-plans.query.js +1 -1
  27. package/dist/billing-foundation/index.d.ts +1 -1
  28. package/dist/billing-foundation/plan-catalog.js +1 -0
  29. package/dist/billing-foundation/projection.d.ts +8 -8
  30. package/dist/billing-foundation/types.d.ts +11 -0
  31. package/dist/billing-foundation/validate-options.js +13 -0
  32. package/dist/billing-foundation/web/billing-contact-plan.d.ts +5 -0
  33. package/dist/billing-foundation/web/billing-contact-plan.js +17 -0
  34. package/dist/billing-foundation/web/billing-plans-panel.js +5 -13
  35. package/dist/billing-foundation/web/cancel-contract-dialog.js +2 -2
  36. package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +1 -1
  37. package/dist/billing-foundation/web/checkout-consent-dialog.js +1 -0
  38. package/dist/billing-foundation/web/client-plugin.d.ts +1 -1
  39. package/dist/billing-foundation/web/localized-params.d.ts +1 -0
  40. package/dist/billing-foundation/web/localized-params.js +11 -0
  41. package/dist/cap-counter/entity.d.ts +1 -1
  42. package/dist/cap-overview/web/cap-cards-panel.js +3 -3
  43. package/dist/cap-overview/web/client-plugin.d.ts +1 -1
  44. package/dist/channel-email/email-channel.d.ts +2 -1
  45. package/dist/compliance-profiles/web/client-plugin.d.ts +1 -1
  46. package/dist/config/feature.js +2 -2
  47. package/dist/config/table.d.ts +6 -6
  48. package/dist/custom-fields/entity.d.ts +4 -4
  49. package/dist/custom-fields/web/client-plugin.d.ts +1 -1
  50. package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -4
  51. package/dist/delivery/tables.d.ts +6 -6
  52. package/dist/delivery/unsubscribe-page.js +1 -1
  53. package/dist/delivery/web/client-plugin.d.ts +1 -1
  54. package/dist/delivery/web/delivery-status-cell.js +1 -1
  55. package/dist/document-ingest-foundation/entity.d.ts +4 -4
  56. package/dist/feature-toggles/web/client-plugin.d.ts +1 -1
  57. package/dist/file-provider-s3/env-named-feature.d.ts +17 -0
  58. package/dist/file-provider-s3/env-named-feature.js +23 -0
  59. package/dist/file-provider-s3/env-schema.d.ts +7 -0
  60. package/dist/file-provider-s3/env-schema.js +15 -10
  61. package/dist/file-provider-s3/index.d.ts +1 -0
  62. package/dist/file-provider-s3/index.js +1 -0
  63. package/dist/folders/entity.d.ts +8 -8
  64. package/dist/folders/web/client-plugin.d.ts +1 -1
  65. package/dist/form-draft/entity.d.ts +2 -2
  66. package/dist/host-routing/constants.d.ts +2 -0
  67. package/dist/host-routing/constants.js +3 -0
  68. package/dist/host-routing/dispatch.d.ts +1 -1
  69. package/dist/host-routing/dispatch.js +35 -25
  70. package/dist/host-routing/feature.js +100 -27
  71. package/dist/host-routing/host.d.ts +9 -0
  72. package/dist/host-routing/host.js +24 -2
  73. package/dist/host-routing/index.d.ts +2 -1
  74. package/dist/host-routing/index.js +1 -0
  75. package/dist/host-routing/options.d.ts +14 -0
  76. package/dist/host-routing/redirects.d.ts +6 -0
  77. package/dist/host-routing/redirects.js +27 -0
  78. package/dist/host-routing/tenant-lookup.d.ts +7 -0
  79. package/dist/host-routing/tenant-lookup.js +39 -0
  80. package/dist/host-routing/validate-options.js +34 -1
  81. package/dist/inbound-mail-foundation/entities.d.ts +25 -25
  82. package/dist/inbound-mail-foundation/feature.js +7 -0
  83. package/dist/inbound-mail-foundation/i18n.d.ts +3 -0
  84. package/dist/inbound-mail-foundation/i18n.js +35 -0
  85. package/dist/inbound-mail-foundation/index.d.ts +1 -0
  86. package/dist/inbound-mail-foundation/index.js +1 -0
  87. package/dist/inbound-mail-foundation/projection.d.ts +20 -20
  88. package/dist/inbound-mail-foundation/screens.d.ts +7 -0
  89. package/dist/inbound-mail-foundation/screens.js +86 -0
  90. package/dist/inbound-mail-foundation/types.d.ts +2 -2
  91. package/dist/jobs/job-run-table.d.ts +6 -6
  92. package/dist/jobs/web/client-plugin.d.ts +1 -1
  93. package/dist/ledger/entity.d.ts +13 -13
  94. package/dist/legal-pages/feature.js +11 -1
  95. package/dist/legal-pages/layout.d.ts +2 -2
  96. package/dist/legal-pages/layout.js +4 -4
  97. package/dist/legal-pages/web/client-plugin.d.ts +1 -1
  98. package/dist/mail-foundation/feature.d.ts +20 -21
  99. package/dist/mail-foundation/feature.js +1 -1
  100. package/dist/managed-pages/table.d.ts +12 -12
  101. package/dist/managed-pages/web/client-plugin.d.ts +1 -1
  102. package/dist/notes-history/entity.d.ts +12 -12
  103. package/dist/notes-history/web/client-plugin.d.ts +1 -1
  104. package/dist/page-render/branding.js +1 -1
  105. package/dist/page-render/layout.d.ts +1 -1
  106. package/dist/page-render/layout.js +2 -2
  107. package/dist/page-render/markdown.js +1 -1
  108. package/dist/personal-access-tokens/i18n.js +1 -1
  109. package/dist/personal-access-tokens/schema/api-token.d.ts +6 -6
  110. package/dist/personal-access-tokens/screens.js +1 -1
  111. package/dist/secrets/feature.d.ts +1 -1
  112. package/dist/secrets/feature.js +3 -3
  113. package/dist/secrets/table.d.ts +1 -1
  114. package/dist/seo/sitemap.js +1 -1
  115. package/dist/sessions/schema/user-session.d.ts +4 -4
  116. package/dist/shared/mail-template-body.js +1 -1
  117. package/dist/shared/purge-files-reaching-subject.js +98 -3
  118. package/dist/tags/entity.d.ts +9 -9
  119. package/dist/tags/web/client-plugin.d.ts +1 -1
  120. package/dist/template-resolver/document-blocks.d.ts +5 -0
  121. package/dist/template-resolver/document-blocks.js +21 -0
  122. package/dist/template-resolver/document-ops.d.ts +167 -0
  123. package/dist/template-resolver/document-ops.js +390 -0
  124. package/dist/template-resolver/document-sanitizer.d.ts +3 -0
  125. package/dist/template-resolver/document-sanitizer.js +6 -0
  126. package/dist/template-resolver/feature.d.ts +2 -0
  127. package/dist/template-resolver/feature.js +4 -0
  128. package/dist/template-resolver/handlers/bundle-import.js +1 -0
  129. package/dist/template-resolver/handlers/document-base.d.ts +23 -0
  130. package/dist/template-resolver/handlers/document-base.js +55 -0
  131. package/dist/template-resolver/handlers/document-blocks.query.d.ts +22 -0
  132. package/dist/template-resolver/handlers/document-blocks.query.js +46 -0
  133. package/dist/template-resolver/handlers/export-bundle.query.d.ts +1 -0
  134. package/dist/template-resolver/handlers/export-bundle.query.js +9 -2
  135. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -0
  136. package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -0
  137. package/dist/template-resolver/handlers/patch-draft.write.d.ts +66 -0
  138. package/dist/template-resolver/handlers/patch-draft.write.js +105 -0
  139. package/dist/template-resolver/handlers/preview.query.d.ts +1 -0
  140. package/dist/template-resolver/handlers/preview.query.js +2 -1
  141. package/dist/template-resolver/handlers/send-test.write.d.ts +1 -0
  142. package/dist/template-resolver/handlers/send-test.write.js +2 -1
  143. package/dist/template-resolver/handlers/template-write.d.ts +2 -0
  144. package/dist/template-resolver/handlers/template-write.js +14 -2
  145. package/dist/template-resolver/handlers/validate.query.d.ts +10 -4
  146. package/dist/template-resolver/handlers/validate.query.js +12 -7
  147. package/dist/template-resolver/{web/placeholder-text.js → placeholder-text.js} +1 -1
  148. package/dist/template-resolver/qualified-names.d.ts +2 -0
  149. package/dist/template-resolver/qualified-names.js +2 -0
  150. package/dist/template-resolver/seeding.d.ts +1 -0
  151. package/dist/template-resolver/seeding.js +2 -1
  152. package/dist/template-resolver/table.d.ts +10 -10
  153. package/dist/template-resolver/template-bundle.d.ts +1 -0
  154. package/dist/template-resolver/template-bundle.js +6 -1
  155. package/dist/template-resolver/template-preview.d.ts +4 -1
  156. package/dist/template-resolver/template-preview.js +16 -3
  157. package/dist/template-resolver/template-validation.d.ts +2 -1
  158. package/dist/template-resolver/template-validation.js +1 -1
  159. package/dist/template-resolver/user-content-table.d.ts +10 -10
  160. package/dist/template-resolver/web/client-plugin.d.ts +1 -1
  161. package/dist/template-resolver/web/client-plugin.js +3 -3
  162. package/dist/template-resolver/web/document-tiptap-editor.js +1 -1
  163. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +2 -5
  164. package/dist/template-resolver/web/document-toolbar-blocks.js +1 -21
  165. package/dist/template-resolver/web/i18n.js +14 -0
  166. package/dist/template-resolver/web/placeholder-line-editor.js +2 -2
  167. package/dist/tenant/invitation-table.d.ts +6 -6
  168. package/dist/tenant/membership-table.d.ts +4 -4
  169. package/dist/tenant/schema/tenant.d.ts +6 -6
  170. package/dist/tenant/web/client-plugin.d.ts +1 -1
  171. package/dist/tenant/web/member-status-cell.js +1 -1
  172. package/dist/tier-engine/entity.d.ts +1 -1
  173. package/dist/tier-engine/tier-resolver.d.ts +1 -1
  174. package/dist/tier-engine/web/client-plugin.d.ts +1 -1
  175. package/dist/user/schema/user.d.ts +12 -12
  176. package/dist/user-data-rights/env.d.ts +5 -0
  177. package/dist/user-data-rights/env.js +11 -0
  178. package/dist/user-data-rights/feature.js +4 -2
  179. package/dist/user-data-rights/index.d.ts +1 -0
  180. package/dist/user-data-rights/index.js +1 -0
  181. package/dist/user-data-rights/lib/mail-transport-resolver.js +1 -1
  182. package/dist/user-data-rights/lib/storage-provider-resolver.js +1 -1
  183. package/dist/user-data-rights/mail-notifications.js +1 -1
  184. package/dist/user-data-rights/schema/download-attempt.d.ts +14 -14
  185. package/dist/user-data-rights/schema/download-token.d.ts +8 -8
  186. package/dist/user-data-rights/schema/export-job.d.ts +6 -6
  187. package/dist/user-data-rights/web/client-plugin.d.ts +1 -1
  188. package/dist/user-data-rights/web/privacy-center-screen.js +2 -2
  189. package/dist/waitlist/entity.d.ts +14 -14
  190. package/dist/waitlist/legacy-migrate.d.ts +2 -0
  191. package/dist/waitlist/legacy-migrate.js +38 -3
  192. package/package.json +16 -11
  193. package/src/account-security/changes.json +6 -0
  194. package/src/auth-foundation/changes.json +6 -0
  195. package/src/billing-foundation/changes.json +6 -0
  196. package/src/channel-email/changes.json +6 -0
  197. package/src/crypto-shredding/changes.json +6 -0
  198. package/src/file-provider-s3/changes.json +6 -0
  199. package/src/host-routing/changes.json +6 -0
  200. package/src/inbound-mail-foundation/changes.json +7 -0
  201. package/src/legal-pages/changes.json +6 -0
  202. package/src/template-resolver/changes.json +18 -0
  203. package/src/user-data-rights/changes.json +6 -0
  204. package/src/waitlist/changes.json +6 -0
  205. /package/dist/template-resolver/{web/placeholder-text.d.ts → placeholder-text.d.ts} +0 -0
@@ -13,7 +13,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
13
13
  readonly sensitive?: boolean;
14
14
  readonly writeOnly?: boolean;
15
15
  readonly readAsInstruction?: boolean;
16
- readonly format?: "email" | "url" | "phone" | "password";
16
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
17
17
  readonly default?: string;
18
18
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
19
19
  readonly multiline?: boolean | {
@@ -36,7 +36,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
36
36
  readonly sensitive?: boolean;
37
37
  readonly writeOnly?: boolean;
38
38
  readonly readAsInstruction?: boolean;
39
- readonly format?: "email" | "url" | "phone" | "password";
39
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
40
40
  readonly default?: string;
41
41
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
42
42
  readonly multiline?: boolean | {
@@ -59,7 +59,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
59
59
  readonly sensitive?: boolean;
60
60
  readonly writeOnly?: boolean;
61
61
  readonly readAsInstruction?: boolean;
62
- readonly format?: "email" | "url" | "phone" | "password";
62
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
63
63
  readonly default?: string;
64
64
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
65
65
  readonly multiline?: boolean | {
@@ -82,7 +82,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
82
82
  readonly sensitive?: boolean;
83
83
  readonly writeOnly?: boolean;
84
84
  readonly readAsInstruction?: boolean;
85
- readonly format?: "email" | "url" | "phone" | "password";
85
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
86
86
  readonly default?: string;
87
87
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
88
88
  readonly multiline?: boolean | {
@@ -105,7 +105,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
105
105
  readonly sensitive?: boolean;
106
106
  readonly writeOnly?: boolean;
107
107
  readonly readAsInstruction?: boolean;
108
- readonly format?: "email" | "url" | "phone" | "password";
108
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
109
109
  readonly default?: string;
110
110
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
111
111
  readonly multiline?: boolean | {
@@ -128,7 +128,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
128
128
  readonly sensitive?: boolean;
129
129
  readonly writeOnly?: boolean;
130
130
  readonly readAsInstruction?: boolean;
131
- readonly format?: "email" | "url" | "phone" | "password";
131
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
132
132
  readonly default?: string;
133
133
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
134
134
  readonly multiline?: boolean | {
@@ -1,6 +1,6 @@
1
1
  import { i18nKey, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { ACTION_FORM_ENTITY } from "@cosmicdrift/kumiko-framework/ui-types";
3
- import { fieldOptionLabelKeyPrefix } from "@cosmicdrift/kumiko-headless";
3
+ import { fieldOptionLabelKeyPrefix } from "@cosmicdrift/kumiko-renderer/headless";
4
4
  import { PAT_FEATURE, PAT_MINT_SCREEN_ID, PAT_SCREEN_ID, PatHandlers, PatQueries, } from "./constants.js";
5
5
  const PAT_STATUS_OPTION_KEY_PREFIX = "pat.list.status.";
6
6
  // Same keys the mint form's `scopes` multiSelect options resolve through
@@ -26,7 +26,7 @@ export { type StoredEnvelope, type StoredMetadata, tenantSecretsTable } from "./
26
26
  export declare const JOIN_REQUEST_TRANSACTION_REASON = "the secret write runs on the request transaction so a failing batch rolls it back together with the config writes";
27
27
  export declare function requireSecretsContext(ctx: {
28
28
  readonly secrets?: SecretsContext;
29
- readonly _userId?: string | undefined;
29
+ readonly auditUserId?: string | undefined;
30
30
  readonly user?: {
31
31
  readonly id: string;
32
32
  };
@@ -57,7 +57,7 @@ export const JOIN_REQUEST_TRANSACTION_REASON = "the secret write runs on the req
57
57
  // FileProviderContext-Pfade (S3-Plugin im Worker) liefern dieselben
58
58
  // zwei Felder, also reicht die schmale ctx-shape — kein voller
59
59
  // HandlerContext-Import noetig.
60
- // Handler contexts carry the caller as ctx.user, provider contexts set _userId (takes precedence).
60
+ // Handler contexts carry the caller as ctx.user, provider contexts set auditUserId (takes precedence).
61
61
  export function requireSecretsContext(ctx, handlerName) {
62
62
  if (!ctx.secrets) {
63
63
  throw new InternalError({
@@ -65,10 +65,10 @@ export function requireSecretsContext(ctx, handlerName) {
65
65
  "createSecretsContext({db, masterKeyProvider, registry}) via extraContext.secrets at boot.",
66
66
  });
67
67
  }
68
- const userId = ctx._userId ?? ctx.user?.id;
68
+ const userId = ctx.auditUserId ?? ctx.user?.id;
69
69
  if (!userId) {
70
70
  throw new InternalError({
71
- message: `[${handlerName}] ctx._userId missing (and no ctx.user) — cannot audit secret reads without a caller identity.`,
71
+ message: `[${handlerName}] ctx.auditUserId missing (and no ctx.user) — cannot audit secret reads without a caller identity.`,
72
72
  });
73
73
  }
74
74
  const withAudit = (raw) => ({
@@ -18,7 +18,7 @@ export declare const tenantSecretEntity: import("@cosmicdrift/kumiko-types/field
18
18
  readonly sensitive?: boolean;
19
19
  readonly writeOnly?: boolean;
20
20
  readonly readAsInstruction?: boolean;
21
- readonly format?: "email" | "url" | "phone" | "password";
21
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
22
22
  readonly default?: string;
23
23
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
24
24
  readonly multiline?: boolean | {
@@ -1,4 +1,4 @@
1
- import { escapeXml } from "@cosmicdrift/kumiko-headless";
1
+ import { escapeXml } from "@cosmicdrift/kumiko-renderer/headless";
2
2
  // Pure XML builder — sitemaps.org urlset + xhtml:link alternates. No
3
3
  // dedup/sort: callers control ordering, entries are trusted app/feature data
4
4
  // (see feature.ts's trust boundary — same posture as ApexCta.href).
@@ -13,7 +13,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
13
13
  readonly sensitive?: boolean;
14
14
  readonly writeOnly?: boolean;
15
15
  readonly readAsInstruction?: boolean;
16
- readonly format?: "email" | "url" | "phone" | "password";
16
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
17
17
  readonly default?: string;
18
18
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
19
19
  readonly multiline?: boolean | {
@@ -36,7 +36,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
36
36
  readonly sensitive?: boolean;
37
37
  readonly writeOnly?: boolean;
38
38
  readonly readAsInstruction?: boolean;
39
- readonly format?: "email" | "url" | "phone" | "password";
39
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
40
40
  readonly default?: string;
41
41
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
42
42
  readonly multiline?: boolean | {
@@ -119,7 +119,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
119
119
  readonly sensitive?: boolean;
120
120
  readonly writeOnly?: boolean;
121
121
  readonly readAsInstruction?: boolean;
122
- readonly format?: "email" | "url" | "phone" | "password";
122
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
123
123
  readonly default?: string;
124
124
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
125
125
  readonly multiline?: boolean | {
@@ -142,7 +142,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
142
142
  readonly sensitive?: boolean;
143
143
  readonly writeOnly?: boolean;
144
144
  readonly readAsInstruction?: boolean;
145
- readonly format?: "email" | "url" | "phone" | "password";
145
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
146
146
  readonly default?: string;
147
147
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
148
148
  readonly multiline?: boolean | {
@@ -1,4 +1,4 @@
1
- import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
1
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-renderer/headless";
2
2
  const SLOT_PATTERN = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
3
3
  // Author text uses `{name}` slots. The text is escaped first and the slots are
4
4
  // swapped for variable nodes afterwards, so a slot can never smuggle markup.
@@ -1,7 +1,7 @@
1
1
  import { resolveSubjectForField, SubjectResolutionError, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
2
2
  import { createEventStoreExecutor, createTenantDb, selectMany, } from "@cosmicdrift/kumiko-framework/db";
3
- import { createSystemUser, parseTenantId, } from "@cosmicdrift/kumiko-framework/engine";
4
- import { fileRefEntity, fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
3
+ import { createSystemUser, parseTenantId, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
4
+ import { FILE_REF_ENTITY_NAME, fileRefEntity, fileRefsTable, } from "@cosmicdrift/kumiko-framework/files";
5
5
  import { collectSingleFileHolders, planFileFields, purgeRowFileRefs, } from "./purge-row-file-refs.js";
6
6
  // Callers' error labels read err.code, so audit events name the failure class.
7
7
  export class FileSweepError extends Error {
@@ -95,7 +95,8 @@ export async function purgeFilesReachingSubject(args) {
95
95
  const fileHolders = collectSingleFileHolders(registry);
96
96
  const storageByTenant = new Map();
97
97
  let firstFailure;
98
- for (const fileEntity of collectFileEntities(registry)) {
98
+ const fileEntities = collectFileEntities(registry);
99
+ for (const fileEntity of fileEntities) {
99
100
  const seenRowIds = new Set();
100
101
  for (const where of candidateWheres(subject, recordTenantId, fileEntity)) {
101
102
  const rows = await selectMany(db, fileEntity.table, where);
@@ -141,6 +142,100 @@ export async function purgeFilesReachingSubject(args) {
141
142
  }
142
143
  }
143
144
  }
145
+ // Runs even after an entity-sweep failure: one failed row must not leave the pending uploads.
146
+ const pendingFailure = await purgePendingUploadsOfUser({
147
+ db,
148
+ registry,
149
+ subject,
150
+ fileFieldKeys: pendingFieldKeys(fileEntities),
151
+ fileHolders,
152
+ storageByTenant,
153
+ resolveDeleteBytes,
154
+ missingStorage,
155
+ });
156
+ firstFailure ??= pendingFailure;
144
157
  if (firstFailure)
145
158
  throw firstFailure;
146
159
  }
160
+ const pendingFieldKey = (entityType, fieldName) => `${entityType}\u0000${fieldName}`;
161
+ function pendingFieldKeys(fileEntities) {
162
+ const keys = new Set();
163
+ for (const { entityName, fileFields } of fileEntities) {
164
+ for (const fieldName of fileFields)
165
+ keys.add(pendingFieldKey(entityName, fieldName));
166
+ }
167
+ return keys;
168
+ }
169
+ async function isFileRefHeldByRecord(db, holders, tenantId, fileRefId) {
170
+ for (const holder of holders) {
171
+ for (const field of holder.singleFields) {
172
+ const where = { [field]: fileRefId };
173
+ if (holder.tableHasTenantId)
174
+ where["tenantId"] = tenantId;
175
+ if ((await selectMany(db, holder.table, where, { limit: 1 })).length > 0)
176
+ return true;
177
+ }
178
+ }
179
+ return false;
180
+ }
181
+ // An upload the user made but never bound to a record has no entity row to find it through, so
182
+ // the uploader id is the only link. Only refs that originate from a registered entity file
183
+ // field count as pending uploads: config saves (a tenant logo) and server-made files never
184
+ // bind and must outlive the uploader. A legacy unbound ref a record's single file column still
185
+ // names is not pending but in use and stays with that record.
186
+ async function purgePendingUploadsOfUser(args) {
187
+ const { db, registry, subject, fileFieldKeys } = args;
188
+ // skip: only a user uploads files without binding them to a record
189
+ if (subject.kind !== "user")
190
+ return undefined;
191
+ // skip: the system user is not a person; its server-made files are not pending uploads
192
+ if (subject.userId === SYSTEM_USER_ID)
193
+ return undefined;
194
+ // skip: without the files feature there is no file_refs table to sweep
195
+ if (registry.getEntity(FILE_REF_ENTITY_NAME) === undefined)
196
+ return undefined;
197
+ const pending = await selectMany(db, fileRefsTable, {
198
+ insertedById: subject.userId,
199
+ entityId: null,
200
+ });
201
+ let firstFailure;
202
+ for (const fileRef of pending) {
203
+ // skip: not uploaded for a registered entity file field (config, server-made, unknown origin)
204
+ if (!fileFieldKeys.has(pendingFieldKey(fileRef["entityType"], fileRef["fieldName"])))
205
+ continue;
206
+ firstFailure ??= await purgePendingFileRef(args, fileRef);
207
+ }
208
+ return firstFailure;
209
+ }
210
+ async function purgePendingFileRef(args, fileRef) {
211
+ const { db, fileHolders, storageByTenant, resolveDeleteBytes, missingStorage } = args;
212
+ const tenantId = parseTenantId(fileRef["tenantId"]);
213
+ const fileRefId = String(fileRef["id"]);
214
+ const storageKey = fileRef["storageKey"];
215
+ // skip: a ref without tenant or key has no bytes this sweep could address
216
+ if (tenantId === null || typeof storageKey !== "string" || storageKey === "")
217
+ return undefined;
218
+ // skip: a record still uses this file
219
+ if (await isFileRefHeldByRecord(db, fileHolders, tenantId, fileRefId))
220
+ return undefined;
221
+ let storagePromise = storageByTenant.get(tenantId);
222
+ if (storagePromise === undefined) {
223
+ storagePromise = resolveDeleteBytes(tenantId);
224
+ storageByTenant.set(tenantId, storagePromise);
225
+ }
226
+ const deleteBytes = await storagePromise;
227
+ if (deleteBytes === undefined) {
228
+ if (missingStorage.mode === "skip") {
229
+ missingStorage.warn(tenantId);
230
+ return undefined;
231
+ }
232
+ return new FileSweepError("missing_file_storage", FILE_REF_ENTITY_NAME);
233
+ }
234
+ if ((await deleteBytesFailingClosed(deleteBytes, storageKey)).length > 0) {
235
+ return new FileSweepError("file_delete_failed", FILE_REF_ENTITY_NAME);
236
+ }
237
+ const forgotten = await fileRefExecutor.forget({ id: fileRefId }, createSystemUser(tenantId), createTenantDb({ db, tenantId, mode: "system" }));
238
+ return forgotten.isSuccess
239
+ ? undefined
240
+ : new FileSweepError("file_delete_failed", FILE_REF_ENTITY_NAME);
241
+ }
@@ -14,7 +14,7 @@ export declare const tagEntity: EntityDefinition<{
14
14
  readonly sensitive?: boolean;
15
15
  readonly writeOnly?: boolean;
16
16
  readonly readAsInstruction?: boolean;
17
- readonly format?: "email" | "url" | "phone" | "password";
17
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
18
18
  readonly default?: string;
19
19
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
20
20
  readonly multiline?: boolean | {
@@ -37,7 +37,7 @@ export declare const tagEntity: EntityDefinition<{
37
37
  readonly sensitive?: boolean;
38
38
  readonly writeOnly?: boolean;
39
39
  readonly readAsInstruction?: boolean;
40
- readonly format?: "email" | "url" | "phone" | "password";
40
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
41
41
  readonly default?: string;
42
42
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
43
43
  readonly multiline?: boolean | {
@@ -60,7 +60,7 @@ export declare const tagEntity: EntityDefinition<{
60
60
  readonly sensitive?: boolean;
61
61
  readonly writeOnly?: boolean;
62
62
  readonly readAsInstruction?: boolean;
63
- readonly format?: "email" | "url" | "phone" | "password";
63
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
64
64
  readonly default?: string;
65
65
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
66
66
  readonly multiline?: boolean | {
@@ -85,7 +85,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
85
85
  readonly sensitive?: boolean;
86
86
  readonly writeOnly?: boolean;
87
87
  readonly readAsInstruction?: boolean;
88
- readonly format?: "email" | "url" | "phone" | "password";
88
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
89
89
  readonly default?: string;
90
90
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
91
91
  readonly multiline?: boolean | {
@@ -108,7 +108,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
108
108
  readonly sensitive?: boolean;
109
109
  readonly writeOnly?: boolean;
110
110
  readonly readAsInstruction?: boolean;
111
- readonly format?: "email" | "url" | "phone" | "password";
111
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
112
112
  readonly default?: string;
113
113
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
114
114
  readonly multiline?: boolean | {
@@ -131,7 +131,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
131
131
  readonly sensitive?: boolean;
132
132
  readonly writeOnly?: boolean;
133
133
  readonly readAsInstruction?: boolean;
134
- readonly format?: "email" | "url" | "phone" | "password";
134
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
135
135
  readonly default?: string;
136
136
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
137
137
  readonly multiline?: boolean | {
@@ -156,7 +156,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
156
156
  readonly sensitive?: boolean;
157
157
  readonly writeOnly?: boolean;
158
158
  readonly readAsInstruction?: boolean;
159
- readonly format?: "email" | "url" | "phone" | "password";
159
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
160
160
  readonly default?: string;
161
161
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
162
162
  readonly multiline?: boolean | {
@@ -179,7 +179,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
179
179
  readonly sensitive?: boolean;
180
180
  readonly writeOnly?: boolean;
181
181
  readonly readAsInstruction?: boolean;
182
- readonly format?: "email" | "url" | "phone" | "password";
182
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
183
183
  readonly default?: string;
184
184
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
185
185
  readonly multiline?: boolean | {
@@ -202,7 +202,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
202
202
  readonly sensitive?: boolean;
203
203
  readonly writeOnly?: boolean;
204
204
  readonly readAsInstruction?: boolean;
205
- readonly format?: "email" | "url" | "phone" | "password";
205
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
206
206
  readonly default?: string;
207
207
  readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
208
208
  readonly multiline?: boolean | {
@@ -1,2 +1,2 @@
1
- import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
1
+ import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer/web";
2
2
  export declare function tagsClient(): ClientFeatureDefinition;
@@ -0,0 +1,5 @@
1
+ export type DocumentToolbarBlock = "image" | "table" | "button" | "divider" | "repeat" | "pageBreak" | "align" | "steps";
2
+ export declare const MAIL_DOCUMENT_BLOCKS: readonly ["image", "table", "button", "divider", "repeat", "steps"];
3
+ export declare const PDF_DOCUMENT_BLOCKS: readonly ["align", "image", "table", "divider", "repeat", "steps", "pageBreak"];
4
+ /** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
5
+ export declare function documentBlocksForKind(kind: string | undefined): readonly DocumentToolbarBlock[];
@@ -0,0 +1,21 @@
1
+ export const MAIL_DOCUMENT_BLOCKS = [
2
+ "image",
3
+ "table",
4
+ "button",
5
+ "divider",
6
+ "repeat",
7
+ "steps",
8
+ ];
9
+ export const PDF_DOCUMENT_BLOCKS = [
10
+ "align",
11
+ "image",
12
+ "table",
13
+ "divider",
14
+ "repeat",
15
+ "steps",
16
+ "pageBreak",
17
+ ];
18
+ /** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
19
+ export function documentBlocksForKind(kind) {
20
+ return kind === "document-pdf" ? PDF_DOCUMENT_BLOCKS : MAIL_DOCUMENT_BLOCKS;
21
+ }
@@ -0,0 +1,167 @@
1
+ import type { FieldIssue } from "@cosmicdrift/kumiko-framework/errors";
2
+ import { type JSONContent } from "@tiptap/core";
3
+ import * as z from "zod";
4
+ import type { DocumentHtmlKind } from "./constants.ts";
5
+ export declare const MAX_DOCUMENT_OPS = 50;
6
+ export type DocumentBlockType = "paragraph" | "heading" | "list" | "table" | "repeat" | "image" | "button" | "divider" | "pageBreak" | "steps";
7
+ export type DocumentBlock = {
8
+ readonly id: string;
9
+ readonly type: DocumentBlockType;
10
+ /** Inline content with `{{name}}` for placeholders; containers list one line per text block. */
11
+ readonly text?: string;
12
+ readonly level?: number;
13
+ readonly align?: string;
14
+ readonly showIf?: string;
15
+ readonly resource?: string;
16
+ readonly alt?: string;
17
+ readonly href?: string;
18
+ readonly each?: string;
19
+ };
20
+ export declare function documentBlockId(index: number): string;
21
+ export declare function documentBlocksOf(doc: JSONContent): DocumentBlock[];
22
+ declare const blockInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
23
+ type: z.ZodLiteral<"paragraph">;
24
+ text: z.ZodString;
25
+ align: z.ZodOptional<z.ZodEnum<{
26
+ center: "center";
27
+ justify: "justify";
28
+ left: "left";
29
+ right: "right";
30
+ }>>;
31
+ }, z.core.$strip>, z.ZodObject<{
32
+ type: z.ZodLiteral<"heading">;
33
+ level: z.ZodNumber;
34
+ text: z.ZodString;
35
+ }, z.core.$strip>, z.ZodObject<{
36
+ type: z.ZodLiteral<"image">;
37
+ resource: z.ZodString;
38
+ alt: z.ZodOptional<z.ZodString>;
39
+ }, z.core.$strip>, z.ZodObject<{
40
+ type: z.ZodLiteral<"button">;
41
+ text: z.ZodString;
42
+ href: z.ZodString;
43
+ }, z.core.$strip>, z.ZodObject<{
44
+ type: z.ZodLiteral<"divider">;
45
+ }, z.core.$strip>, z.ZodObject<{
46
+ type: z.ZodLiteral<"pageBreak">;
47
+ }, z.core.$strip>], "type">;
48
+ export type BlockInput = z.infer<typeof blockInputSchema>;
49
+ export declare const documentOpSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
50
+ op: z.ZodLiteral<"insertBlock">;
51
+ after: z.ZodNullable<z.ZodString>;
52
+ block: z.ZodDiscriminatedUnion<[z.ZodObject<{
53
+ type: z.ZodLiteral<"paragraph">;
54
+ text: z.ZodString;
55
+ align: z.ZodOptional<z.ZodEnum<{
56
+ center: "center";
57
+ justify: "justify";
58
+ left: "left";
59
+ right: "right";
60
+ }>>;
61
+ }, z.core.$strip>, z.ZodObject<{
62
+ type: z.ZodLiteral<"heading">;
63
+ level: z.ZodNumber;
64
+ text: z.ZodString;
65
+ }, z.core.$strip>, z.ZodObject<{
66
+ type: z.ZodLiteral<"image">;
67
+ resource: z.ZodString;
68
+ alt: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$strip>, z.ZodObject<{
70
+ type: z.ZodLiteral<"button">;
71
+ text: z.ZodString;
72
+ href: z.ZodString;
73
+ }, z.core.$strip>, z.ZodObject<{
74
+ type: z.ZodLiteral<"divider">;
75
+ }, z.core.$strip>, z.ZodObject<{
76
+ type: z.ZodLiteral<"pageBreak">;
77
+ }, z.core.$strip>], "type">;
78
+ }, z.core.$strip>, z.ZodObject<{
79
+ op: z.ZodLiteral<"replaceText">;
80
+ blockId: z.ZodString;
81
+ find: z.ZodString;
82
+ replace: z.ZodString;
83
+ }, z.core.$strip>, z.ZodObject<{
84
+ op: z.ZodLiteral<"removeBlock">;
85
+ blockId: z.ZodString;
86
+ }, z.core.$strip>, z.ZodObject<{
87
+ op: z.ZodLiteral<"setResource">;
88
+ blockId: z.ZodString;
89
+ resource: z.ZodString;
90
+ alt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
91
+ }, z.core.$strip>, z.ZodObject<{
92
+ op: z.ZodLiteral<"setMeta">;
93
+ meta: z.ZodRecord<z.ZodString, z.ZodUnknown>;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ op: z.ZodLiteral<"setVariable">;
96
+ name: z.ZodString;
97
+ definition: z.ZodNullable<z.ZodUnknown>;
98
+ }, z.core.$strip>], "op">;
99
+ export declare const documentOpsSchema: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
100
+ op: z.ZodLiteral<"insertBlock">;
101
+ after: z.ZodNullable<z.ZodString>;
102
+ block: z.ZodDiscriminatedUnion<[z.ZodObject<{
103
+ type: z.ZodLiteral<"paragraph">;
104
+ text: z.ZodString;
105
+ align: z.ZodOptional<z.ZodEnum<{
106
+ center: "center";
107
+ justify: "justify";
108
+ left: "left";
109
+ right: "right";
110
+ }>>;
111
+ }, z.core.$strip>, z.ZodObject<{
112
+ type: z.ZodLiteral<"heading">;
113
+ level: z.ZodNumber;
114
+ text: z.ZodString;
115
+ }, z.core.$strip>, z.ZodObject<{
116
+ type: z.ZodLiteral<"image">;
117
+ resource: z.ZodString;
118
+ alt: z.ZodOptional<z.ZodString>;
119
+ }, z.core.$strip>, z.ZodObject<{
120
+ type: z.ZodLiteral<"button">;
121
+ text: z.ZodString;
122
+ href: z.ZodString;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ type: z.ZodLiteral<"divider">;
125
+ }, z.core.$strip>, z.ZodObject<{
126
+ type: z.ZodLiteral<"pageBreak">;
127
+ }, z.core.$strip>], "type">;
128
+ }, z.core.$strip>, z.ZodObject<{
129
+ op: z.ZodLiteral<"replaceText">;
130
+ blockId: z.ZodString;
131
+ find: z.ZodString;
132
+ replace: z.ZodString;
133
+ }, z.core.$strip>, z.ZodObject<{
134
+ op: z.ZodLiteral<"removeBlock">;
135
+ blockId: z.ZodString;
136
+ }, z.core.$strip>, z.ZodObject<{
137
+ op: z.ZodLiteral<"setResource">;
138
+ blockId: z.ZodString;
139
+ resource: z.ZodString;
140
+ alt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
141
+ }, z.core.$strip>, z.ZodObject<{
142
+ op: z.ZodLiteral<"setMeta">;
143
+ meta: z.ZodRecord<z.ZodString, z.ZodUnknown>;
144
+ }, z.core.$strip>, z.ZodObject<{
145
+ op: z.ZodLiteral<"setVariable">;
146
+ name: z.ZodString;
147
+ definition: z.ZodNullable<z.ZodUnknown>;
148
+ }, z.core.$strip>], "op">>;
149
+ export type DocumentOp = z.infer<typeof documentOpSchema>;
150
+ export type DocumentOpsInput = {
151
+ readonly doc: JSONContent;
152
+ readonly meta: Readonly<Record<string, unknown>>;
153
+ readonly variableSchema: Readonly<Record<string, unknown>>;
154
+ readonly kind: DocumentHtmlKind;
155
+ readonly ops: readonly DocumentOp[];
156
+ };
157
+ export type DocumentOpsResult = {
158
+ readonly ok: true;
159
+ readonly doc: JSONContent;
160
+ readonly meta: Record<string, unknown>;
161
+ readonly variableSchema: Record<string, unknown>;
162
+ } | {
163
+ readonly ok: false;
164
+ readonly issues: FieldIssue[];
165
+ };
166
+ export declare function applyDocumentOps(input: DocumentOpsInput): DocumentOpsResult;
167
+ export {};