@cosmicdrift/kumiko-bundled-features 0.350.0 → 0.352.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 (155) hide show
  1. package/dist/audit/handlers/list.query.js +1 -1
  2. package/dist/auth-email-password/email-templates.js +1 -1
  3. package/dist/auth-email-password/handlers/invite-create.write.js +1 -1
  4. package/dist/auth-email-password/handlers/signup-request.write.js +1 -1
  5. package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
  6. package/dist/auth-email-password/reset-token.d.ts +1 -1
  7. package/dist/auth-email-password/unlock-token.d.ts +1 -1
  8. package/dist/auth-email-password/verification-token.d.ts +1 -1
  9. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -1
  10. package/dist/auth-mfa/handlers/enable-confirm.write.js +1 -1
  11. package/dist/auth-mfa/hmac-token-codec.d.ts +1 -1
  12. package/dist/auth-mfa/hmac-token-codec.js +1 -1
  13. package/dist/auth-mfa/mfa-challenge-token.d.ts +1 -1
  14. package/dist/auth-mfa/mfa-challenge-token.js +1 -1
  15. package/dist/auth-mfa/mfa-preauth-setup-token.d.ts +1 -1
  16. package/dist/auth-mfa/mfa-preauth-setup-token.js +1 -1
  17. package/dist/auth-mfa/mfa-setup-token.d.ts +1 -1
  18. package/dist/auth-mfa/mfa-setup-token.js +1 -1
  19. package/dist/billing-foundation/constants.d.ts +2 -2
  20. package/dist/billing-foundation/constants.js +3 -12
  21. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +1 -1
  22. package/dist/billing-foundation/consumer-protection/termination-mail.js +1 -1
  23. package/dist/billing-foundation/feature.js +2 -9
  24. package/dist/billing-foundation/handlers/sync-subscription.write.js +3 -6
  25. package/dist/cap-counter/book-cap-usage.d.ts +5 -1
  26. package/dist/cap-counter/book-cap-usage.js +66 -45
  27. package/dist/cap-counter/cap-reservation.d.ts +12 -0
  28. package/dist/cap-counter/cap-reservation.js +135 -0
  29. package/dist/cap-counter/constants.d.ts +3 -0
  30. package/dist/cap-counter/constants.js +6 -0
  31. package/dist/cap-counter/enforce-cap.d.ts +1 -1
  32. package/dist/cap-counter/enforce-cap.js +1 -8
  33. package/dist/cap-counter/feature.js +7 -2
  34. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +5 -0
  35. package/dist/cap-counter/handlers/increment-rolling.write.js +4 -0
  36. package/dist/cap-counter/index.d.ts +4 -2
  37. package/dist/cap-counter/index.js +3 -1
  38. package/dist/cap-counter/payload-field.d.ts +1 -0
  39. package/dist/cap-counter/payload-field.js +11 -0
  40. package/dist/cap-counter/reservation-chain.d.ts +2 -0
  41. package/dist/cap-counter/reservation-chain.js +44 -0
  42. package/dist/cap-counter/stock-cap-guard.d.ts +3 -0
  43. package/dist/cap-counter/tables.d.ts +3 -0
  44. package/dist/cap-counter/tables.js +36 -0
  45. package/dist/cap-counter/value-cap-guard.d.ts +18 -0
  46. package/dist/cap-counter/value-cap-guard.js +33 -0
  47. package/dist/cap-counter/with-cap-enforcement.d.ts +5 -7
  48. package/dist/cap-counter/with-cap-enforcement.js +35 -51
  49. package/dist/channel-discord/feature.js +1 -0
  50. package/dist/channel-slack/feature.js +1 -0
  51. package/dist/channel-teams/feature.js +1 -0
  52. package/dist/compliance-profiles/schema/profile-selection.d.ts +2 -0
  53. package/dist/delivery/attempt-log-retention.d.ts +9 -0
  54. package/dist/delivery/attempt-log-retention.js +23 -0
  55. package/dist/delivery/attempt-log.d.ts +1 -1
  56. package/dist/delivery/attempt-log.js +21 -2
  57. package/dist/delivery/constants.d.ts +1 -1
  58. package/dist/delivery/constants.js +1 -1
  59. package/dist/delivery/events.d.ts +1 -0
  60. package/dist/delivery/events.js +2 -0
  61. package/dist/delivery/feature.d.ts +1 -0
  62. package/dist/delivery/feature.js +39 -3
  63. package/dist/delivery/handlers/log.query.d.ts +1 -2
  64. package/dist/delivery/handlers/log.query.js +1 -1
  65. package/dist/delivery/index.d.ts +2 -1
  66. package/dist/delivery/index.js +2 -1
  67. package/dist/delivery/mask-recipient-address.d.ts +1 -0
  68. package/dist/delivery/mask-recipient-address.js +41 -0
  69. package/dist/delivery/public-names.d.ts +2 -0
  70. package/dist/delivery/public-names.js +3 -0
  71. package/dist/delivery/types.d.ts +1 -0
  72. package/dist/delivery/types.js +6 -2
  73. package/dist/feature-toggles/handlers/set.write.js +1 -1
  74. package/dist/form-draft/db/queries/cleanup.d.ts +1 -1
  75. package/dist/form-draft/db/queries/cleanup.js +1 -1
  76. package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
  77. package/dist/form-draft/lookup.d.ts +1 -1
  78. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +1 -1
  79. package/dist/inbound-mail-foundation/oauth-access-token.d.ts +1 -1
  80. package/dist/inbound-mail-foundation/oauth-access-token.js +1 -1
  81. package/dist/inbound-mail-foundation/oauth-state.d.ts +1 -1
  82. package/dist/inbound-mail-foundation/oauth-state.js +1 -1
  83. package/dist/inbound-mail-foundation/watch-supervisor.js +1 -1
  84. package/dist/inbound-provider-imap/feature.js +1 -1
  85. package/dist/inbound-provider-imap/imap-client.js +1 -1
  86. package/dist/jobs/job-instant.d.ts +1 -1
  87. package/dist/jobs/job-instant.js +1 -1
  88. package/dist/ledger/entity.d.ts +3 -0
  89. package/dist/metrics/compute.d.ts +1 -1
  90. package/dist/metrics/feature.js +1 -1
  91. package/dist/metrics/types.d.ts +1 -1
  92. package/dist/metrics/window.d.ts +1 -1
  93. package/dist/metrics/window.js +1 -1
  94. package/dist/personal-access-tokens/expiry.js +1 -1
  95. package/dist/personal-access-tokens/handlers/create.write.js +1 -1
  96. package/dist/personal-access-tokens/handlers/revoke.write.js +1 -1
  97. package/dist/personal-access-tokens/revoke-for-user.js +1 -1
  98. package/dist/renderer-foundation/types.d.ts +1 -0
  99. package/dist/renderer-simple/feature.js +4 -6
  100. package/dist/renderer-simple/index.d.ts +1 -0
  101. package/dist/renderer-simple/mail-blocks.d.ts +21 -0
  102. package/dist/renderer-simple/mail-blocks.js +107 -0
  103. package/dist/renderer-simple/simple-renderer.js +21 -3
  104. package/dist/sessions/handlers/revoke-all-for-user.write.js +1 -1
  105. package/dist/sessions/handlers/revoke-all-others.write.js +1 -1
  106. package/dist/sessions/handlers/revoke.write.js +1 -1
  107. package/dist/sessions/session-callbacks.js +1 -1
  108. package/dist/shared/grace-period.d.ts +2 -1
  109. package/dist/shared/grace-period.js +3 -7
  110. package/dist/shared/row-bound-grant.d.ts +1 -1
  111. package/dist/shared/signed-token.d.ts +1 -1
  112. package/dist/shared/signed-token.js +1 -1
  113. package/dist/subscription-mollie/verify-webhook.js +1 -1
  114. package/dist/subscription-stripe/verify-webhook.js +1 -1
  115. package/dist/template-resolver/api.d.ts +1 -1
  116. package/dist/template-resolver/handlers/by-slug.query.d.ts +1 -1
  117. package/dist/template-resolver/handlers/by-tenant.query.d.ts +2 -2
  118. package/dist/template-resolver/handlers/collection-item.query.d.ts +1 -1
  119. package/dist/template-resolver/handlers/collection-list.query.d.ts +1 -1
  120. package/dist/template-resolver/handlers/collection-shared.d.ts +2 -2
  121. package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -1
  122. package/dist/template-resolver/handlers/list.query.d.ts +1 -1
  123. package/dist/template-resolver/table.d.ts +9 -1
  124. package/dist/template-resolver/user-content-table.d.ts +4 -0
  125. package/dist/tenant/handlers/team-list.query.d.ts +1 -1
  126. package/dist/tenant/handlers/team-list.query.js +1 -1
  127. package/dist/tenant/invitation-table.d.ts +2 -0
  128. package/dist/tenant/invited-membership.d.ts +1 -1
  129. package/dist/tenant/invited-membership.js +1 -1
  130. package/dist/tenant/schema/tenant.d.ts +2 -0
  131. package/dist/tenant-handover/grant.d.ts +1 -1
  132. package/dist/tier-engine/index.d.ts +1 -0
  133. package/dist/tier-engine/index.js +1 -0
  134. package/dist/tier-engine/tier-option-gate.d.ts +32 -0
  135. package/dist/tier-engine/tier-option-gate.js +62 -0
  136. package/dist/user/schema/user.d.ts +2 -0
  137. package/dist/user-data-rights/db/queries/export-jobs.d.ts +1 -1
  138. package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
  139. package/dist/user-data-rights/db/queries/forget-cleanup.js +2 -8
  140. package/dist/user-data-rights/deletion-token.d.ts +1 -1
  141. package/dist/user-data-rights/email-templates.js +1 -1
  142. package/dist/user-data-rights/handlers/list-download-attempts.query.js +1 -1
  143. package/dist/user-data-rights/handlers/my-audit-log.query.js +1 -1
  144. package/dist/user-data-rights/schema/export-job.d.ts +2 -0
  145. package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +25 -6
  146. package/dist/waitlist/entity.d.ts +2 -0
  147. package/dist/waitlist/handlers/invite.write.js +1 -1
  148. package/dist/waitlist/handlers/submit.write.js +1 -1
  149. package/package.json +10 -10
  150. package/src/cap-counter/changes.json +28 -0
  151. package/src/delivery/changes.json +12 -0
  152. package/src/derivatives-sharp/changes.json +6 -0
  153. package/src/legal-pages/changes.json +6 -0
  154. package/src/renderer-simple/changes.json +6 -0
  155. package/src/tier-engine/changes.json +6 -0
@@ -0,0 +1,107 @@
1
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
2
+ // Literal types (as const) keep the colors compile-time known, so they can go into style
3
+ // attributes without escaping.
4
+ const TONE_COLORS = {
5
+ neutral: { bg: "#f3f4f6", fg: "#374151" },
6
+ info: { bg: "#dbeafe", fg: "#1e40af" },
7
+ success: { bg: "#dcfce7", fg: "#166534" },
8
+ warning: { bg: "#fef3c7", fg: "#92400e" },
9
+ danger: { bg: "#fee2e2", fg: "#991b1b" },
10
+ };
11
+ const STAGE_STATES = ["done", "current", "upcoming"];
12
+ const STAGE_DONE_COLOR = "#16a34a";
13
+ const STAGE_UPCOMING_BG = "#e5e7eb";
14
+ const STAGE_UPCOMING_FG = "#9ca3af";
15
+ function isRecord(value) {
16
+ return typeof value === "object" && value !== null;
17
+ }
18
+ function isMailTone(value) {
19
+ return typeof value === "string" && Object.hasOwn(TONE_COLORS, value);
20
+ }
21
+ function toneColors(tone) {
22
+ return TONE_COLORS[isMailTone(tone) ? tone : "neutral"];
23
+ }
24
+ function isStageState(value) {
25
+ return STAGE_STATES.some((state) => state === value);
26
+ }
27
+ // Template data is runtime data (variables of a stored template): entries that do not match are
28
+ // skipped instead of failing the whole mail.
29
+ function isStage(value) {
30
+ return isRecord(value) && typeof value["label"] === "string" && isStageState(value["state"]);
31
+ }
32
+ function isChip(value) {
33
+ return isRecord(value) && typeof value["label"] === "string";
34
+ }
35
+ export function parseBadge(value) {
36
+ return isChip(value) && value.label !== "" ? value : undefined;
37
+ }
38
+ function validStages(value) {
39
+ return Array.isArray(value) ? value.filter(isStage) : [];
40
+ }
41
+ function validChips(value) {
42
+ return Array.isArray(value) ? value.filter(isChip) : [];
43
+ }
44
+ function pill(chip, margin) {
45
+ const { bg, fg } = toneColors(chip.tone);
46
+ return `<span style="display:inline-block;margin:${margin};padding:2px 10px;border-radius:999px;background:${bg};color:${fg};font-size:12px;font-weight:600;line-height:18px">${escapeHtml(chip.label)}</span>`;
47
+ }
48
+ export function renderBadge(badge) {
49
+ return `<p style="margin:0 0 12px">${pill(badge, "0")}</p>`;
50
+ }
51
+ export function renderChips(chips) {
52
+ const valid = validChips(chips);
53
+ if (valid.length === 0)
54
+ return "";
55
+ return `<p style="margin:0 0 16px">${valid.map((chip) => pill(chip, "0 6px 6px 0")).join("")}</p>`;
56
+ }
57
+ function stageMarker(stage, position, primaryColor) {
58
+ const base = "display:inline-block;width:24px;height:24px;line-height:24px;border-radius:12px;text-align:center;font-size:12px;font-weight:700";
59
+ const primary = escapeHtmlAttr(primaryColor);
60
+ if (stage.state === "done") {
61
+ return `<span style="${base};background:${STAGE_DONE_COLOR};color:#fff">&#10003;</span>`;
62
+ }
63
+ if (stage.state === "current") {
64
+ return `<span style="${base};background:#fff;border:2px solid ${primary};line-height:20px;width:20px;height:20px;color:${primary}">${position}</span>`;
65
+ }
66
+ return `<span style="${base};background:${STAGE_UPCOMING_BG};color:${STAGE_UPCOMING_FG}">${position}</span>`;
67
+ }
68
+ function stageCell(stage, position, primaryColor) {
69
+ const labelColor = stage.state === "upcoming" ? STAGE_UPCOMING_FG : "#111";
70
+ const weight = stage.state === "current" ? 700 : 400;
71
+ return `<td align="center" valign="top" style="padding:0 4px;font-family:sans-serif">${stageMarker(stage, position, primaryColor)}<div style="margin:4px 0 0;font-size:12px;font-weight:${weight};color:${labelColor}">${escapeHtml(stage.label)}</div></td>`;
72
+ }
73
+ function stageConnector(previous) {
74
+ const color = previous.state === "done" ? STAGE_DONE_COLOR : STAGE_UPCOMING_BG;
75
+ return `<td valign="top" style="padding:11px 0 0"><div style="width:24px;height:2px;line-height:2px;font-size:2px;background:${color}">&nbsp;</div></td>`;
76
+ }
77
+ export function renderStages(stages, primaryColor) {
78
+ const valid = validStages(stages);
79
+ if (valid.length === 0)
80
+ return "";
81
+ const cellsHtml = valid
82
+ .map((stage, index) => {
83
+ const previous = valid[index - 1];
84
+ const connector = previous ? stageConnector(previous) : "";
85
+ return `${connector}${stageCell(stage, index + 1, primaryColor)}`;
86
+ })
87
+ .join("");
88
+ return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 16px"><tr>${cellsHtml}</tr></table>`;
89
+ }
90
+ const STAGE_TEXT_MARKER = {
91
+ done: "✓",
92
+ current: "▶",
93
+ upcoming: "○",
94
+ };
95
+ export function badgeText(badge) {
96
+ return `[${badge.label}]`;
97
+ }
98
+ export function stagesText(stages) {
99
+ return validStages(stages)
100
+ .map((stage) => `${STAGE_TEXT_MARKER[stage.state]} ${stage.label}`)
101
+ .join(" → ");
102
+ }
103
+ export function chipsText(chips) {
104
+ return validChips(chips)
105
+ .map((chip) => chip.label)
106
+ .join(" · ");
107
+ }
@@ -1,5 +1,6 @@
1
1
  import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
2
2
  import { renderSafeMarkdown } from "../page-render/index.js";
3
+ import { badgeText, chipsText, parseBadge, renderBadge, renderChips, renderStages, stagesText, } from "./mail-blocks.js";
3
4
  const DEFAULT_PRIMARY_COLOR = "#2563eb";
4
5
  const HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
5
6
  function isHttpUrl(value) {
@@ -107,6 +108,7 @@ function templateContent(variables) {
107
108
  const data = variables; // @cast-boundary render-helper
108
109
  // Without structured fields, title + body become the header and a single text section.
109
110
  return {
111
+ badge: parseBadge(data.badge),
110
112
  header: data.header ?? data.title,
111
113
  sections: data.sections ?? (data.body ? [{ text: data.body }] : undefined),
112
114
  footer: data.footer,
@@ -122,6 +124,10 @@ function sectionText(section) {
122
124
  return section.markdown;
123
125
  if ("button" in section)
124
126
  return `${section.button.label}: ${section.button.url}`;
127
+ if ("stages" in section)
128
+ return stagesText(section.stages);
129
+ if ("chips" in section)
130
+ return chipsText(section.chips);
125
131
  return "";
126
132
  }
127
133
  function renderSection(section, primaryColor) {
@@ -138,6 +144,10 @@ function renderSection(section, primaryColor) {
138
144
  if ("button" in section) {
139
145
  return `<p style="margin:0 0 16px"><a href="${escapeHtml(section.button.url)}" style="display:inline-block;padding:10px 24px;background:${escapeHtmlAttr(primaryColor)};color:#fff;text-decoration:none;border-radius:4px;font-size:14px">${escapeHtml(section.button.label)}</a></p>`;
140
146
  }
147
+ if ("stages" in section)
148
+ return renderStages(section.stages, primaryColor);
149
+ if ("chips" in section)
150
+ return renderChips(section.chips);
141
151
  return "";
142
152
  }
143
153
  // Simple Renderer: turns structured email template data into HTML with inline CSS.
@@ -150,11 +160,13 @@ export function createSimpleRenderer(branding) {
150
160
  return {
151
161
  name: "simple",
152
162
  async render(input) {
153
- const { header, sections, footer } = templateContent(input.variables);
163
+ const { badge, header, sections, footer } = templateContent(input.variables);
154
164
  const parts = [];
155
165
  parts.push('<!DOCTYPE html><html><body style="margin:0;padding:0;font-family:sans-serif">');
156
166
  parts.push('<div style="max-width:600px;margin:0 auto;padding:24px">');
157
167
  parts.push(renderBrandingHeader(branding, primaryColor, logoUrl));
168
+ if (badge)
169
+ parts.push(renderBadge(badge));
158
170
  if (header) {
159
171
  parts.push(`<h1 style="margin:0 0 24px;color:#111;font-size:20px;font-weight:600">${escapeHtml(header)}</h1>`);
160
172
  }
@@ -171,9 +183,15 @@ export function createSimpleRenderer(branding) {
171
183
  return parts.join("");
172
184
  },
173
185
  async renderText(input) {
174
- const { header, sections, footer } = templateContent(input.variables);
186
+ const { badge, header, sections, footer } = templateContent(input.variables);
175
187
  const brandingFooter = brandingFooterLines(branding, input.locale).join("\n");
176
- return [header, ...(sections ?? []).map(sectionText), footer, brandingFooter]
188
+ return [
189
+ badge ? badgeText(badge) : undefined,
190
+ header,
191
+ ...(sections ?? []).map(sectionText),
192
+ footer,
193
+ brandingFooter,
194
+ ]
177
195
  .filter((block) => block !== undefined && block !== "")
178
196
  .join("\n\n");
179
197
  },
@@ -4,7 +4,7 @@ import { access, defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumik
4
4
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
5
5
  import { append } from "@cosmicdrift/kumiko-framework/event-store";
6
6
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
7
- import { Temporal } from "temporal-polyfill";
7
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
8
8
  import * as z from "zod";
9
9
  import { userSessionTable } from "../schema/user-session.js";
10
10
  import { SESSION_REVOKED_AGGREGATE_TYPE, SESSION_REVOKED_EVENT_QN, sessionRevokedSchema, } from "../session-revoked-event.js";
@@ -2,7 +2,7 @@ import { updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
4
4
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
5
- import { Temporal } from "temporal-polyfill";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
6
  import * as z from "zod";
7
7
  import { SessionErrors } from "../constants.js";
8
8
  import { userSessionTable } from "../schema/user-session.js";
@@ -2,7 +2,7 @@ import { fetchOne, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
4
4
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
5
- import { Temporal } from "temporal-polyfill";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
6
  import * as z from "zod";
7
7
  import { SessionErrors, USER_SESSION_ENTITY_NAME } from "../constants.js";
8
8
  import { userSessionTable } from "../schema/user-session.js";
@@ -2,7 +2,7 @@ import { fetchOne, insertOne, transaction, updateMany } from "@cosmicdrift/kumik
2
2
  import { buildSessionRoles, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { append } from "@cosmicdrift/kumiko-framework/event-store";
4
4
  import { generateId, parseRoles } from "@cosmicdrift/kumiko-framework/utils";
5
- import { Temporal } from "temporal-polyfill";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
6
  import { encryptForDirectWrite } from "../shared/index.js";
7
7
  import { tenantMembershipsTable } from "../tenant/index.js";
8
8
  import { isPrincipalBlocked, userTable } from "../user/index.js";
@@ -1,7 +1,8 @@
1
1
  /** Shared grace-period check for user-data-rights + tenant-lifecycle cancel flows. */
2
2
  /// <reference types="temporal-polyfill/global" preserve="true" />
3
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
4
  /**
4
5
  * True when `gracePeriodEnd` is still in the future.
5
- * Uses the polyfill Instant API so callers don't depend on `globalThis.Temporal`.
6
+ * Uses the module's Temporal so callers don't depend on `globalThis.Temporal`.
6
7
  */
7
8
  export declare function isWithinGracePeriod(gracePeriodEnd: Temporal.Instant | null): boolean;
@@ -1,14 +1,10 @@
1
1
  /// <reference types="temporal-polyfill/global" preserve="true" />
2
2
  /** Shared grace-period check for user-data-rights + tenant-lifecycle cancel flows. */
3
- import { Temporal as TemporalPolyfill } from "temporal-polyfill";
3
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  /**
5
5
  * True when `gracePeriodEnd` is still in the future.
6
- * Uses the polyfill Instant API so callers don't depend on `globalThis.Temporal`.
6
+ * Uses the module's Temporal so callers don't depend on `globalThis.Temporal`.
7
7
  */
8
8
  export function isWithinGracePeriod(gracePeriodEnd) {
9
- // @cast-boundary temporal-polyfill-vs-ambient: same TC39 Temporal.Instant
10
- // at runtime — row types resolve against ambient Temporal; polyfill Instant
11
- // is a separate nominal type across the two .d.ts sources.
12
- return (gracePeriodEnd != null &&
13
- TemporalPolyfill.Instant.compare(gracePeriodEnd, TemporalPolyfill.Now.instant()) > 0);
9
+ return (gracePeriodEnd != null && Temporal.Instant.compare(gracePeriodEnd, Temporal.Now.instant()) > 0);
14
10
  }
@@ -1,4 +1,4 @@
1
- import type { Temporal } from "temporal-polyfill";
1
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  export type RowBoundGrantResult = {
3
3
  readonly ok: true;
4
4
  readonly subject: string;
@@ -1,4 +1,4 @@
1
- import { Temporal } from "temporal-polyfill";
1
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  export type VerifyResult = {
3
3
  readonly ok: true;
4
4
  readonly userId: string;
@@ -12,7 +12,7 @@
12
12
  // Timing-safe comparison on verify so a valid-length forgery can't leak
13
13
  // signal through a short-circuit.
14
14
  import { createHmac, timingSafeEqual } from "node:crypto";
15
- import { Temporal } from "temporal-polyfill";
15
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
16
16
  function sign(input, secret) {
17
17
  return createHmac("sha256", secret).update(input).digest("base64url");
18
18
  }
@@ -15,7 +15,7 @@
15
15
  // Created-Event mit subscription-id bekommt.
16
16
  import { parseProviderConsentId, SubscriptionEventTypes, SubscriptionStatuses, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
17
17
  import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
18
- import { Temporal } from "temporal-polyfill";
18
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
19
19
  import { MOLLIE_PROVIDER_NAME } from "./constants.js";
20
20
  const log = createFallbackLogger("subscription-mollie");
21
21
  export function verifyAndParseMollieWebhook(client, options) {
@@ -34,8 +34,8 @@
34
34
  import { BillingEventKinds, parseProviderConsentId, SubscriptionEventTypes, SubscriptionStatuses, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
35
35
  import { ExtraRouteRejection } from "@cosmicdrift/kumiko-framework/api";
36
36
  import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
37
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
37
38
  import Stripe from "stripe";
38
- import { Temporal } from "temporal-polyfill";
39
39
  import { STRIPE_PROVIDER_NAME, StripeEventTypes } from "./constants.js";
40
40
  const log = createFallbackLogger("subscription-stripe");
41
41
  export function isResourceMissingStripeError(error) {
@@ -1,6 +1,6 @@
1
1
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
2
2
  import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
- import type { Temporal } from "temporal-polyfill";
3
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  import type { ContentFormat, TemplateKind } from "./constants.js";
5
5
  export type TemplateResource = {
6
6
  readonly id: string;
@@ -10,5 +10,5 @@ export declare const bySlugQuery: import("@cosmicdrift/kumiko-types/define-handl
10
10
  content: string | null;
11
11
  contentFormat: string;
12
12
  folder: string | null;
13
- modifiedAt: import("temporal-spec").Temporal.Instant | null;
13
+ modifiedAt: Temporal.Instant | null;
14
14
  } | null, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -1,4 +1,4 @@
1
- import type { Temporal } from "temporal-polyfill";
1
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  import * as z from "zod";
3
3
  export type TextBlockSummary = {
4
4
  readonly slug: string;
@@ -17,6 +17,6 @@ export declare const byTenantQuery: import("@cosmicdrift/kumiko-types/define-han
17
17
  title: string | null;
18
18
  content: string | null;
19
19
  folder: string | null;
20
- modifiedAt: Temporal.Instant | null;
20
+ modifiedAt: globalThis.Temporal.Instant | null;
21
21
  }[];
22
22
  }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -11,5 +11,5 @@ export declare function makeCollectionItemQuery(collection: ContentCollectionDef
11
11
  content: string | null;
12
12
  contentFormat: string;
13
13
  folder: string | null;
14
- modifiedAt: import("temporal-spec").Temporal.Instant | null;
14
+ modifiedAt: Temporal.Instant | null;
15
15
  } | null, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -10,6 +10,6 @@ export declare function makeCollectionListQuery(collection: ContentCollectionDef
10
10
  content: string | null;
11
11
  contentFormat: string;
12
12
  folder: string | null;
13
- modifiedAt: import("temporal-spec").Temporal.Instant | null;
13
+ modifiedAt: Temporal.Instant | null;
14
14
  }[];
15
15
  }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -1,6 +1,6 @@
1
1
  import type { EventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
2
2
  import type { AccessRule, ContentCollectionDefinition, SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
- import type { Temporal } from "temporal-polyfill";
3
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  import { templateResourcesTable } from "../table.js";
5
5
  import { userContentEntriesTable } from "../user-content-table.js";
6
6
  export declare const DEFAULT_COLLECTION_ACCESS: AccessRule;
@@ -31,5 +31,5 @@ export declare function toCollectionEntry(row: CollectionEntryRow): {
31
31
  content: string | null;
32
32
  contentFormat: string;
33
33
  folder: string | null;
34
- modifiedAt: Temporal.Instant | null;
34
+ modifiedAt: globalThis.Temporal.Instant | null;
35
35
  };
@@ -15,5 +15,5 @@ export declare const findByIdQuery: import("@cosmicdrift/kumiko-types/define-han
15
15
  scope: string;
16
16
  parentTemplateId: string | null;
17
17
  status: string;
18
- modifiedAt: import("temporal-spec").Temporal.Instant | null;
18
+ modifiedAt: Temporal.Instant | null;
19
19
  } | null, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -22,5 +22,5 @@ export declare const listQuery: import("@cosmicdrift/kumiko-types/define-handler
22
22
  scope: string;
23
23
  status: string;
24
24
  contentFormat: string;
25
- modifiedAt: import("temporal-spec").Temporal.Instant | null;
25
+ modifiedAt: Temporal.Instant | null;
26
26
  }[], import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -1,4 +1,4 @@
1
- import type { Temporal } from "temporal-polyfill";
1
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/fields").EntityDefinition<{
3
3
  slug: {
4
4
  readonly type: "text";
@@ -42,6 +42,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
42
42
  } | undefined;
43
43
  readonly optionsQuery?: string;
44
44
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
45
+ readonly optionsAvailabilityQuery?: string;
45
46
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
46
47
  required: true;
47
48
  };
@@ -143,6 +144,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
143
144
  } | undefined;
144
145
  readonly optionsQuery?: string;
145
146
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
147
+ readonly optionsAvailabilityQuery?: string;
146
148
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
147
149
  required: true;
148
150
  };
@@ -195,6 +197,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
195
197
  } | undefined;
196
198
  readonly optionsQuery?: string;
197
199
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
200
+ readonly optionsAvailabilityQuery?: string;
198
201
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
199
202
  required: true;
200
203
  };
@@ -237,6 +240,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
237
240
  } | undefined;
238
241
  readonly optionsQuery?: string;
239
242
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
243
+ readonly optionsAvailabilityQuery?: string;
240
244
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
241
245
  required: true;
242
246
  };
@@ -284,6 +288,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
284
288
  } | undefined;
285
289
  readonly optionsQuery?: string;
286
290
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
291
+ readonly optionsAvailabilityQuery?: string;
287
292
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
288
293
  required: true;
289
294
  };
@@ -385,6 +390,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
385
390
  } | undefined;
386
391
  readonly optionsQuery?: string;
387
392
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
393
+ readonly optionsAvailabilityQuery?: string;
388
394
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
389
395
  required: true;
390
396
  };
@@ -437,6 +443,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
437
443
  } | undefined;
438
444
  readonly optionsQuery?: string;
439
445
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
446
+ readonly optionsAvailabilityQuery?: string;
440
447
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
441
448
  required: true;
442
449
  };
@@ -479,6 +486,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
479
486
  } | undefined;
480
487
  readonly optionsQuery?: string;
481
488
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
489
+ readonly optionsAvailabilityQuery?: string;
482
490
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
483
491
  required: true;
484
492
  };
@@ -63,6 +63,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
63
63
  } | undefined;
64
64
  readonly optionsQuery?: string;
65
65
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
66
+ readonly optionsAvailabilityQuery?: string;
66
67
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
67
68
  required: true;
68
69
  };
@@ -164,6 +165,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
164
165
  } | undefined;
165
166
  readonly optionsQuery?: string;
166
167
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
168
+ readonly optionsAvailabilityQuery?: string;
167
169
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
168
170
  required: true;
169
171
  };
@@ -232,6 +234,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
232
234
  } | undefined;
233
235
  readonly optionsQuery?: string;
234
236
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
237
+ readonly optionsAvailabilityQuery?: string;
235
238
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
236
239
  required: true;
237
240
  };
@@ -333,6 +336,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
333
336
  } | undefined;
334
337
  readonly optionsQuery?: string;
335
338
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
339
+ readonly optionsAvailabilityQuery?: string;
336
340
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
337
341
  required: true;
338
342
  };
@@ -1,4 +1,4 @@
1
- import { Temporal } from "temporal-polyfill";
1
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  import * as z from "zod";
3
3
  type TeamStatus = "active" | "pending";
4
4
  type TeamRow = {
@@ -2,7 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { access, definePagedQueryHandler, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
4
4
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
5
- import { Temporal } from "temporal-polyfill";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
6
  import * as z from "zod";
7
7
  import { userSessionTable } from "../../sessions/index.js";
8
8
  import { decryptStoredPii, mapWithConcurrency } from "../../shared/index.js";
@@ -69,6 +69,7 @@ export declare const tenantInvitationEntity: import("@cosmicdrift/kumiko-types/f
69
69
  } | undefined;
70
70
  readonly optionsQuery?: string;
71
71
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
72
+ readonly optionsAvailabilityQuery?: string;
72
73
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
73
74
  required: true;
74
75
  };
@@ -172,6 +173,7 @@ export declare const tenantInvitationsTable: import("@cosmicdrift/kumiko-framewo
172
173
  } | undefined;
173
174
  readonly optionsQuery?: string;
174
175
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
176
+ readonly optionsAvailabilityQuery?: string;
175
177
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
176
178
  required: true;
177
179
  };
@@ -1,6 +1,6 @@
1
1
  import { type DbRunner } from "@cosmicdrift/kumiko-framework/db";
2
2
  import { type TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
- import { Temporal } from "temporal-polyfill";
3
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  export type InvitedMembershipOptions = {
5
5
  readonly userId: string;
6
6
  readonly tenantId: TenantId;
@@ -3,7 +3,7 @@ import { createEventStoreExecutor, createTenantDb, } from "@cosmicdrift/kumiko-f
3
3
  import { createSystemUser } from "@cosmicdrift/kumiko-framework/engine";
4
4
  import { ConflictError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
5
5
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
6
- import { Temporal } from "temporal-polyfill";
6
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
7
7
  import { TenantErrors } from "./constants.js";
8
8
  import { findForbiddenMembershipRole, reservedMembershipRoleError } from "./membership-roles.js";
9
9
  import { tenantMembershipEntity, tenantMembershipsTable } from "./membership-table.js";
@@ -66,6 +66,7 @@ export declare const tenantEntity: import("@cosmicdrift/kumiko-types/fields").En
66
66
  } | undefined;
67
67
  readonly optionsQuery?: string;
68
68
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
69
+ readonly optionsAvailabilityQuery?: string;
69
70
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
70
71
  required: true;
71
72
  };
@@ -217,6 +218,7 @@ export declare const tenantTable: import("@cosmicdrift/kumiko-framework/db").Ent
217
218
  } | undefined;
218
219
  readonly optionsQuery?: string;
219
220
  readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
221
+ readonly optionsAvailabilityQuery?: string;
220
222
  } & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
221
223
  required: true;
222
224
  };
@@ -1,4 +1,4 @@
1
- import type { Temporal } from "temporal-polyfill";
1
+ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
2
  export declare function tenantHandoverPurpose(entityType: string): string;
3
3
  export declare function signTenantHandoverGrant(args: {
4
4
  readonly entityType: string;
@@ -3,5 +3,6 @@ export { type AddOnDefinition, type AddOnMap, type ComposeAppInput, type Compose
3
3
  export { TIER_ENGINE_FEATURE, TierAssignmentSources, TierEngineHandlers, TierEngineQueries, } from "./constants.js";
4
4
  export { tierAssignmentEntity } from "./entity.js";
5
5
  export { type CreateTierEngineOptions, createTierEngineFeature, tierEngineFeature, } from "./feature.js";
6
+ export { createTierOptionGate, type TierOptionAvailability, type TierOptionGate, type TierOptionGateDeps, type TierOptionSpec, } from "./tier-option-gate.js";
6
7
  export { createTierResolver, type TierResolver, type TierResolverDeps, } from "./tier-resolver.js";
7
8
  export { isTrialActive, type TrialPolicy } from "./trial.js";
@@ -11,5 +11,6 @@ export { composeApp, } from "./compose-app.js";
11
11
  export { TIER_ENGINE_FEATURE, TierAssignmentSources, TierEngineHandlers, TierEngineQueries, } from "./constants.js";
12
12
  export { tierAssignmentEntity } from "./entity.js";
13
13
  export { createTierEngineFeature, tierEngineFeature, } from "./feature.js";
14
+ export { createTierOptionGate, } from "./tier-option-gate.js";
14
15
  export { createTierResolver, } from "./tier-resolver.js";
15
16
  export { isTrialActive } from "./trial.js";
@@ -0,0 +1,32 @@
1
+ import type { EntityTableMeta, SchemaTable, TenantDb } from "@cosmicdrift/kumiko-framework/db";
2
+ import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { type WriteFailure } from "@cosmicdrift/kumiko-framework/errors";
4
+ import { type CapGuardContext, type CapLimitContext } from "../cap-counter/index.js";
5
+ export type TierOptionSpec<TCaps> = {
6
+ readonly field: string;
7
+ readonly options: readonly string[];
8
+ readonly isOptionAllowed: (option: string, caps: TCaps) => boolean;
9
+ readonly code: string;
10
+ readonly i18nKey: string;
11
+ /** Lets an update that resends the unchanged stored value of a no longer allowed
12
+ * option pass (edit forms submit the whole row). Without it the stored value is not looked up. */
13
+ readonly table?: SchemaTable | EntityTableMeta;
14
+ };
15
+ export type TierOptionAvailability = {
16
+ readonly value: string;
17
+ readonly disabled: boolean;
18
+ readonly hint?: string;
19
+ };
20
+ export type TierOptionGateDeps<TTier extends string, TCaps> = {
21
+ /** Ascending: the first tier that allows an option is the one named as required. */
22
+ readonly tierOrder: readonly TTier[];
23
+ readonly capsForTier: (tier: TTier, context: CapLimitContext) => TCaps | Promise<TCaps>;
24
+ readonly resolveTier: (db: TenantDb) => Promise<TTier>;
25
+ };
26
+ export type TierOptionGate<TTier extends string, TCaps> = {
27
+ readonly optionAvailability: (db: TenantDb, context: CapLimitContext, spec: Pick<TierOptionSpec<TCaps>, "options" | "isOptionAllowed">, hintFor: (requiredTier: TTier) => string) => Promise<readonly TierOptionAvailability[]>;
28
+ /** `recordId` is the id of the edited row; with `spec.table` it enables the unchanged-value pass. */
29
+ readonly checkTierOption: (ctx: CapGuardContext, value: unknown, spec: TierOptionSpec<TCaps>, recordId?: string) => Promise<WriteFailure | null>;
30
+ readonly withTierOptionGate: (handler: WriteHandlerDef, spec: TierOptionSpec<TCaps>) => WriteHandlerDef;
31
+ };
32
+ export declare function createTierOptionGate<TTier extends string, TCaps>(deps: TierOptionGateDeps<TTier, TCaps>): TierOptionGate<TTier, TCaps>;