@cosmicdrift/kumiko-samples 0.352.0 → 0.354.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.
- package/package.json +1 -1
- package/packages/bundled-features/package.json +18 -2
- package/packages/bundled-features/src/agent-tools/agent-doc-lint.ts +66 -0
- package/packages/bundled-features/src/agent-tools/agent-manifest.ts +26 -7
- package/packages/bundled-features/src/agent-tools/changes.json +12 -0
- package/packages/bundled-features/src/agent-tools/feature.ts +13 -2
- package/packages/bundled-features/src/agent-tools/index.ts +6 -1
- package/packages/bundled-features/src/agent-tools/manifest-deny-qns.ts +25 -0
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +4 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +1 -1
- package/packages/bundled-features/src/audit/changes.json +6 -0
- package/packages/bundled-features/src/audit/handlers/list.query.ts +20 -10
- package/packages/bundled-features/src/auth-email-password/changes.json +24 -0
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +21 -0
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +4 -1
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +7 -3
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +53 -96
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/index.ts +12 -0
- package/packages/bundled-features/src/auth-email-password/seeding.ts +38 -7
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +83 -0
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +9 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +7 -7
- package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +15 -0
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +2 -2
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +1 -1
- package/packages/bundled-features/src/auth-mfa/changes.json +7 -0
- package/packages/bundled-features/src/auth-mfa/feature.ts +5 -0
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -0
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -0
- package/packages/bundled-features/src/auth-mfa/screens.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +6 -8
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +18 -21
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +2 -2
- package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +14 -2
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +7 -1
- package/packages/bundled-features/src/cap-overview/access.ts +2 -9
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +5 -2
- package/packages/bundled-features/src/cap-overview/handlers/tenant-options.query.ts +6 -9
- package/packages/bundled-features/src/channel-in-app/changes.json +8 -1
- package/packages/bundled-features/src/channel-in-app/in-app-channel.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/tables.ts +18 -12
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +5 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +7 -2
- package/packages/bundled-features/src/crypto-shredding/changes.json +12 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +154 -69
- package/packages/bundled-features/src/delivery/changes.json +6 -0
- package/packages/bundled-features/src/delivery/db/queries/address-opt-outs.ts +18 -12
- package/packages/bundled-features/src/delivery/delivery-service.ts +103 -16
- package/packages/bundled-features/src/delivery/public-names.ts +1 -1
- package/packages/bundled-features/src/delivery/unsubscribe.ts +81 -13
- package/packages/bundled-features/src/feature-toggles/feature.ts +2 -3
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +34 -5
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +31 -8
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +10 -1
- package/packages/bundled-features/src/files/changes.json +24 -0
- package/packages/bundled-features/src/files-tenant-data/changes.json +5 -0
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +4 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +75 -25
- package/packages/bundled-features/src/folders/changes.json +6 -0
- package/packages/bundled-features/src/folders/feature.ts +2 -12
- package/packages/bundled-features/src/folders/handlers/delete-folder.write.ts +41 -9
- package/packages/bundled-features/src/form-draft/changes.json +7 -1
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +4 -2
- package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +18 -6
- package/packages/bundled-features/src/foundation-shared/index.ts +1 -0
- package/packages/bundled-features/src/foundation-shared/mail-host-policy.ts +16 -4
- package/packages/bundled-features/src/inbound-provider-imap/changes.json +6 -0
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +2 -0
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +3 -4
- package/packages/bundled-features/src/jobs/changes.json +30 -0
- package/packages/bundled-features/src/jobs/constants.ts +1 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +12 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +26 -2
- package/packages/bundled-features/src/jobs/feature.ts +7 -1
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +45 -12
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +94 -0
- package/packages/bundled-features/src/jobs/i18n.ts +1 -0
- package/packages/bundled-features/src/jobs/index.ts +2 -0
- package/packages/bundled-features/src/jobs/job-run-logger.ts +177 -10
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +27 -8
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +55 -0
- package/packages/bundled-features/src/ledger/changes.json +23 -0
- package/packages/bundled-features/src/ledger/entity.ts +17 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -0
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +15 -3
- package/packages/bundled-features/src/ledger/handlers/create-transaction.write.ts +1 -0
- package/packages/bundled-features/src/ledger/handlers/reverse-transaction.write.ts +1 -0
- package/packages/bundled-features/src/ledger/schedule-subject-pair.ts +27 -0
- package/packages/bundled-features/src/ledger/schemas.ts +8 -2
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -15
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +24 -11
- package/packages/bundled-features/src/notes-history/changes.json +13 -0
- package/packages/bundled-features/src/notes-history/entity.ts +4 -0
- package/packages/bundled-features/src/notes-history/feature.ts +2 -12
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +1 -0
- package/packages/bundled-features/src/personal-access-tokens/screens.ts +24 -3
- package/packages/bundled-features/src/seo/changes.json +5 -0
- package/packages/bundled-features/src/seo/feature.ts +23 -7
- package/packages/bundled-features/src/sessions/feature.ts +21 -8
- package/packages/bundled-features/src/sessions/handlers/detail.query.ts +34 -33
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +68 -65
- package/packages/bundled-features/src/sessions/screens.ts +10 -8
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +25 -0
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +15 -8
- package/packages/bundled-features/src/shared/index.ts +4 -2
- package/packages/bundled-features/src/shared/parent-visibility.ts +31 -1
- package/packages/bundled-features/src/shared/rate-limited-text-response.ts +11 -0
- package/packages/bundled-features/src/shared/row-bound-grant.ts +18 -2
- package/packages/bundled-features/src/shared/session-locale-field.ts +7 -0
- package/packages/bundled-features/src/tags/feature.ts +2 -12
- package/packages/bundled-features/src/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +5 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +2 -2
- package/packages/bundled-features/src/tenant/changes.json +19 -0
- package/packages/bundled-features/src/tenant/decrypt-tenant-names.ts +17 -0
- package/packages/bundled-features/src/tenant/feature.ts +8 -1
- package/packages/bundled-features/src/tenant/handlers/me.query.ts +4 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +7 -3
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +11 -5
- package/packages/bundled-features/src/tenant/handlers/toggle-enabled.write.ts +2 -0
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -0
- package/packages/bundled-features/src/tenant/index.ts +1 -0
- package/packages/bundled-features/src/tenant/invitation-table.ts +6 -0
- package/packages/bundled-features/src/tenant/invited-membership.ts +45 -12
- package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -6
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/changes.json +12 -0
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +12 -19
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +14 -96
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +16 -0
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +22 -0
- package/packages/bundled-features/src/tenant-lifecycle/constants.ts +8 -0
- package/packages/bundled-features/src/tenant-lifecycle/events.ts +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +14 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +105 -29
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +64 -10
- package/packages/bundled-features/src/tenant-lifecycle/testing.ts +58 -0
- package/packages/bundled-features/src/tier-engine/changes.json +5 -0
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +1 -0
- package/packages/bundled-features/src/user/changes.json +15 -0
- package/packages/bundled-features/src/user/handlers/detail.query.ts +1 -0
- package/packages/bundled-features/src/user/handlers/list.query.ts +8 -2
- package/packages/bundled-features/src/user/handlers/update.write.ts +1 -0
- package/packages/bundled-features/src/user/seeding.ts +34 -0
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +7 -0
- package/packages/bundled-features/src/user-data-rights/changes.json +6 -0
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -0
- package/packages/bundled-features/src/user-data-rights/index.ts +1 -0
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +17 -9
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -2
- package/packages/bundled-features/src/workflow-runner/changes.json +5 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +80 -14
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +11 -1
- package/packages/bundled-features/src/workflow-runner/runner.ts +8 -2
- package/samples/apps/showcase/src/features/items/feature.ts +2 -1
- package/samples/apps/showcase/src/features/items/schema/item.ts +0 -1
- package/samples/apps/styleguide/src/features/widgets/demo-locale.ts +47 -0
- package/samples/apps/styleguide/src/features/widgets/feature.ts +131 -15
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +40 -0
- package/samples/apps/styleguide/src/features/widgets/web/DashboardFilterEcho.tsx +5 -4
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +4 -0
- package/samples/apps/ui-walkthrough/package.json +3 -0
- package/samples/apps/use-all-bundled/README.md +1 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
- package/samples/apps/user-data-rights-demo/README.md +1 -3
- package/samples/recipes/legal-pages/README.md +3 -4
- package/samples/recipes/managed-pages/README.md +2 -2
- package/samples/recipes/public-share-token/README.md +4 -5
- package/samples/recipes/public-share-token/src/feature.ts +1 -1
- package/samples/recipes/security-baseline/README.md +6 -0
- package/samples/recipes/seed-migration/README.md +0 -2
- package/samples/recipes/tenant-settings/README.md +1 -1
- package/packages/bundled-features/src/shared/has-where-rule.ts +0 -11
|
@@ -22,14 +22,7 @@ export function MfaRecoveryCodesReveal({
|
|
|
22
22
|
const [acknowledged, setAcknowledged] = useState(false);
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
<Section
|
|
26
|
-
testId="mfa-regenerate-reveal"
|
|
27
|
-
actions={
|
|
28
|
-
<Button variant="primary" onClick={onDismiss} disabled={!acknowledged}>
|
|
29
|
-
{t("auth.mfa.regenerate.done")}
|
|
30
|
-
</Button>
|
|
31
|
-
}
|
|
32
|
-
>
|
|
25
|
+
<Section testId="mfa-regenerate-reveal">
|
|
33
26
|
<div className="flex flex-col items-center gap-2 text-center">
|
|
34
27
|
<span className="text-sm font-semibold">{t("auth.mfa.regenerate.newCodesTitle")}</span>
|
|
35
28
|
<span className="text-xs text-muted-foreground">
|
|
@@ -48,6 +41,11 @@ export function MfaRecoveryCodesReveal({
|
|
|
48
41
|
onChange={setAcknowledged}
|
|
49
42
|
/>
|
|
50
43
|
</Field>
|
|
44
|
+
<div className="flex justify-end">
|
|
45
|
+
<Button variant="primary" onClick={onDismiss} disabled={!acknowledged}>
|
|
46
|
+
{t("auth.mfa.regenerate.done")}
|
|
47
|
+
</Button>
|
|
48
|
+
</div>
|
|
51
49
|
</Section>
|
|
52
50
|
);
|
|
53
51
|
}
|
|
@@ -67,7 +67,7 @@ export type MfaSetupPreauthScreenProps = {
|
|
|
67
67
|
readonly subtitle?: ReactNode;
|
|
68
68
|
/** Fired once confirm succeeds — the caller owns session refresh, see the
|
|
69
69
|
* file-level comment above. */
|
|
70
|
-
readonly onSuccess?: () => void;
|
|
70
|
+
readonly onSuccess?: (landingPath?: string) => void;
|
|
71
71
|
readonly onCancel?: () => void;
|
|
72
72
|
};
|
|
73
73
|
|
|
@@ -146,7 +146,7 @@ export function MfaSetupPreauthScreen({
|
|
|
146
146
|
}
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
|
-
onSuccess?.();
|
|
149
|
+
onSuccess?.(res.data.landingPath);
|
|
150
150
|
} catch {
|
|
151
151
|
setError("setup_failed");
|
|
152
152
|
} finally {
|
|
@@ -173,9 +173,9 @@ export function MfaSetupPreauthScreen({
|
|
|
173
173
|
) : null}
|
|
174
174
|
|
|
175
175
|
{!setup && (
|
|
176
|
-
<Section
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
<Section testId="mfa-setup-preauth-intro">
|
|
177
|
+
<span className="text-sm text-muted-foreground">{t("auth.mfa.setup.intro")}</span>
|
|
178
|
+
<div className="flex justify-end">
|
|
179
179
|
<Button
|
|
180
180
|
variant="primary"
|
|
181
181
|
onClick={() => void startSetup()}
|
|
@@ -184,27 +184,13 @@ export function MfaSetupPreauthScreen({
|
|
|
184
184
|
>
|
|
185
185
|
{t("auth.mfa.setup.start")}
|
|
186
186
|
</Button>
|
|
187
|
-
|
|
188
|
-
>
|
|
189
|
-
<span className="text-sm text-muted-foreground">{t("auth.mfa.setup.intro")}</span>
|
|
187
|
+
</div>
|
|
190
188
|
</Section>
|
|
191
189
|
)}
|
|
192
190
|
|
|
193
191
|
{setup && (
|
|
194
192
|
<Form onSubmit={onSubmit}>
|
|
195
|
-
<Section
|
|
196
|
-
testId="mfa-setup-preauth-setup"
|
|
197
|
-
actions={
|
|
198
|
-
<Button
|
|
199
|
-
type="submit"
|
|
200
|
-
variant="primary"
|
|
201
|
-
loading={busy}
|
|
202
|
-
disabled={busy || !acknowledged || code.length !== 6}
|
|
203
|
-
>
|
|
204
|
-
{t("auth.mfa.setup.confirm")}
|
|
205
|
-
</Button>
|
|
206
|
-
}
|
|
207
|
-
>
|
|
193
|
+
<Section testId="mfa-setup-preauth-setup">
|
|
208
194
|
<div className="flex flex-col items-center gap-2 text-center">
|
|
209
195
|
<span className="text-sm font-semibold">{t("auth.mfa.enable.scanTitle")}</span>
|
|
210
196
|
{/* qrcode's own SVG string output, not user input — safe to inline */}
|
|
@@ -251,6 +237,17 @@ export function MfaSetupPreauthScreen({
|
|
|
251
237
|
autoComplete="one-time-code"
|
|
252
238
|
/>
|
|
253
239
|
</Field>
|
|
240
|
+
|
|
241
|
+
<div className="flex justify-end">
|
|
242
|
+
<Button
|
|
243
|
+
type="submit"
|
|
244
|
+
variant="primary"
|
|
245
|
+
loading={busy}
|
|
246
|
+
disabled={busy || !acknowledged || code.length !== 6}
|
|
247
|
+
>
|
|
248
|
+
{t("auth.mfa.setup.confirm")}
|
|
249
|
+
</Button>
|
|
250
|
+
</div>
|
|
254
251
|
</Section>
|
|
255
252
|
</Form>
|
|
256
253
|
)}
|
|
@@ -20,7 +20,7 @@ export type MfaVerifyScreenProps = {
|
|
|
20
20
|
/** Called after the server confirms the code and the session state has
|
|
21
21
|
* refreshed. Optional — apps that swap this screen back out purely on
|
|
22
22
|
* session.status changing to "authenticated" don't need it. */
|
|
23
|
-
readonly onSuccess?: () => void;
|
|
23
|
+
readonly onSuccess?: (landingPath?: string) => void;
|
|
24
24
|
// Some failures (challenge_expired, too_many_attempts) have no retry path —
|
|
25
25
|
// the challengeToken is dead. Without this, the host has no way back to
|
|
26
26
|
// login short of a full page reload. Optional: apps that always mount
|
|
@@ -68,7 +68,7 @@ export function MfaVerifyScreen({
|
|
|
68
68
|
if (res.kind === "success") {
|
|
69
69
|
await session.refresh();
|
|
70
70
|
setSubmitting(false);
|
|
71
|
-
onSuccess?.();
|
|
71
|
+
onSuccess?.(res.data.landingPath);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
setSubmitting(false);
|
|
@@ -39,19 +39,18 @@ export type BillingInfo<TTier extends string> = {
|
|
|
39
39
|
readonly tier: string;
|
|
40
40
|
readonly providerName: string;
|
|
41
41
|
} | null;
|
|
42
|
-
readonly prices:
|
|
42
|
+
readonly prices: TierPrices<TTier>;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
export type TierPrices<TTier extends string> = { readonly [K in TTier]?: string };
|
|
46
|
+
|
|
45
47
|
export type BillingInfoQueryDeps<TTier extends string> = {
|
|
46
48
|
readonly roles: readonly string[];
|
|
47
49
|
readonly resolveTier: (db: TenantDb, tenantId: TenantId) => Promise<TTier>;
|
|
48
|
-
// ctx is the same HandlerContext the returned handler receives —
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
readonly getBillingPrices: (
|
|
53
|
-
ctx: HandlerContext,
|
|
54
|
-
) => Readonly<Partial<Record<string, string>>> | null;
|
|
50
|
+
// ctx is the same HandlerContext the returned handler receives — apps read
|
|
51
|
+
// their extraContext fields (e.g. `billingPrices`) off it and validate them
|
|
52
|
+
// at that boundary.
|
|
53
|
+
readonly getBillingPrices: (ctx: HandlerContext) => TierPrices<TTier> | null;
|
|
55
54
|
};
|
|
56
55
|
|
|
57
56
|
export function createBillingInfoQueryConfig<TTier extends string>(
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.354.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "BillingInfo.prices and getBillingPrices are keyed by the tier union",
|
|
6
|
+
"migration": "A getBillingPrices that returns keys outside your tier union no longer compiles; remove or rename those keys."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.346.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -274,6 +274,13 @@ export type OpenCheckoutOptions = {
|
|
|
274
274
|
readonly baseUrl?: string;
|
|
275
275
|
readonly catalog?: BillingPlanCatalog;
|
|
276
276
|
readonly now: () => Temporal.Instant;
|
|
277
|
+
/** The caller already ran `assertBillingEnabled` under its own handler name
|
|
278
|
+
* (before a costly provider lookup), so the gate is not repeated here. */
|
|
279
|
+
readonly billingEnabledChecked?: boolean;
|
|
280
|
+
/** The caller already ran `assertNoActiveSubscription` and passes its result
|
|
281
|
+
* (null = tenant has none); mode:"subscription" skips the second read and
|
|
282
|
+
* the providerCustomerId check reuses it. */
|
|
283
|
+
readonly checkedSubscription?: Awaited<ReturnType<typeof getSubscriptionForTenant>>;
|
|
277
284
|
};
|
|
278
285
|
|
|
279
286
|
export type OpenCheckoutInput = {
|
|
@@ -358,7 +365,9 @@ export async function assertCheckoutAllowed(
|
|
|
358
365
|
// Checked before the redirect-origin hardening for every mode — a
|
|
359
366
|
// disabled provider must reject a mode:"payment" checkout just as much
|
|
360
367
|
// as a mode:"subscription" one.
|
|
361
|
-
|
|
368
|
+
if (options.billingEnabledChecked !== true) {
|
|
369
|
+
await assertBillingEnabled(ctx, plugin, "create-checkout-session");
|
|
370
|
+
}
|
|
362
371
|
assertRedirectOrigins([input.successUrl, input.cancelUrl], options.baseUrl);
|
|
363
372
|
|
|
364
373
|
const mode = input.mode ?? "subscription";
|
|
@@ -368,7 +377,10 @@ export async function assertCheckoutAllowed(
|
|
|
368
377
|
|
|
369
378
|
if (mode === "subscription") {
|
|
370
379
|
assertSubscriptionPriceAllowed(plugin, options.catalog, input);
|
|
371
|
-
ownSubscription =
|
|
380
|
+
ownSubscription =
|
|
381
|
+
options.checkedSubscription !== undefined
|
|
382
|
+
? options.checkedSubscription
|
|
383
|
+
: await assertNoActiveSubscription(ctx, options.now());
|
|
372
384
|
} else {
|
|
373
385
|
assertOneOffPriceAllowed(plugin, input);
|
|
374
386
|
}
|
package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts
CHANGED
|
@@ -74,7 +74,13 @@ export function createStartPlanCheckoutHandler(
|
|
|
74
74
|
const baseUrl = options.baseUrl ?? "";
|
|
75
75
|
const result = await openCheckout(
|
|
76
76
|
ctx,
|
|
77
|
-
{
|
|
77
|
+
{
|
|
78
|
+
baseUrl: options.baseUrl,
|
|
79
|
+
catalog,
|
|
80
|
+
now: options.now,
|
|
81
|
+
billingEnabledChecked: true,
|
|
82
|
+
checkedSubscription: existing,
|
|
83
|
+
},
|
|
78
84
|
{
|
|
79
85
|
providerName,
|
|
80
86
|
priceId: resolved.priceId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
|
+
import { access, BUILT_IN_MEMBERSHIP_ROLES } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
|
|
3
3
|
// Usage numbers are an operator concern by default; wider visibility is an
|
|
4
4
|
// explicit `usageVisibleTo` opt-in on createCapOverviewFeature. The my-caps
|
|
@@ -9,14 +9,7 @@ export const DEFAULT_CAP_USAGE_ROLES: readonly string[] = access.roles(...access
|
|
|
9
9
|
// Roles the engine itself knows (role-assignment.ts ranks). App-declared roles
|
|
10
10
|
// (assignableRole) only exist once the registry is composed, which is after
|
|
11
11
|
// this feature is defined, so they cannot be validated here.
|
|
12
|
-
const BUILT_IN_USAGE_VISIBLE_ROLES: ReadonlySet<string> = new Set(
|
|
13
|
-
"User",
|
|
14
|
-
"Member",
|
|
15
|
-
"Editor",
|
|
16
|
-
"Admin",
|
|
17
|
-
"TenantAdmin",
|
|
18
|
-
"SystemAdmin",
|
|
19
|
-
]);
|
|
12
|
+
const BUILT_IN_USAGE_VISIBLE_ROLES: ReadonlySet<string> = new Set(BUILT_IN_MEMBERSHIP_ROLES);
|
|
20
13
|
|
|
21
14
|
export function resolveCapUsageRoles(
|
|
22
15
|
usageVisibleTo: readonly string[] | undefined,
|
|
@@ -10,7 +10,7 @@ import { InternalError, ValidationError } from "@cosmicdrift/kumiko-framework/er
|
|
|
10
10
|
import * as z from "zod";
|
|
11
11
|
import { subscriptionsProjectionTable } from "../../billing-foundation/index.js";
|
|
12
12
|
import type { CapLimitContext } from "../../cap-counter/index.js";
|
|
13
|
-
import { tenantTable } from "../../tenant/index.js";
|
|
13
|
+
import { decryptTenantNames, tenantTable } from "../../tenant/index.js";
|
|
14
14
|
import { tierAssignmentEntity } from "../../tier-engine/index.js";
|
|
15
15
|
import { capFieldName } from "../constants.js";
|
|
16
16
|
import type { CapSpec, CapUsage } from "../types.js";
|
|
@@ -174,7 +174,10 @@ export function createTenantCapsListQuery(caps: readonly CapSpec[], listCaps: re
|
|
|
174
174
|
"cap-overview:tenant-caps:list — SystemAdmin platform-wide tenant overview",
|
|
175
175
|
);
|
|
176
176
|
|
|
177
|
-
const tenants = await
|
|
177
|
+
const tenants = await decryptTenantNames(
|
|
178
|
+
await selectMany<TenantRow>(db, tenantTable, {}),
|
|
179
|
+
"cap-overview:tenant-caps:list",
|
|
180
|
+
);
|
|
178
181
|
const assignments = await selectMany<TierAssignmentRow>(db, tierAssignmentTable, {});
|
|
179
182
|
const subscriptions = await selectMany<SubscriptionRow>(db, subscriptionsProjectionTable, {});
|
|
180
183
|
|
|
@@ -2,7 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
|
-
import { tenantTable } from "../../tenant/index.js";
|
|
5
|
+
import { decryptTenantNames, tenantTable } from "../../tenant/index.js";
|
|
6
6
|
|
|
7
7
|
type TenantRow = { readonly id: string; readonly name: string };
|
|
8
8
|
|
|
@@ -22,14 +22,11 @@ export const tenantOptionsQuery = defineQueryHandler({
|
|
|
22
22
|
const db = ctx.systemDb.acknowledgeCrossTenant(
|
|
23
23
|
"cap-overview:tenant-options — SystemAdmin dashboard tenant-filter options",
|
|
24
24
|
);
|
|
25
|
-
const tenants = await
|
|
26
|
-
db,
|
|
27
|
-
|
|
28
|
-
{},
|
|
29
|
-
{
|
|
30
|
-
orderBy: { col: "name", direction: "asc" },
|
|
31
|
-
},
|
|
25
|
+
const tenants = await decryptTenantNames(
|
|
26
|
+
await selectMany<TenantRow>(db, tenantTable, {}),
|
|
27
|
+
"cap-overview:tenant-options",
|
|
32
28
|
);
|
|
33
|
-
|
|
29
|
+
const sorted = [...tenants].sort((a, b) => a.name.localeCompare(b.name));
|
|
30
|
+
return { rows: sorted.map((tenant) => ({ value: tenant.id, label: tenant.name })) };
|
|
34
31
|
},
|
|
35
32
|
});
|
|
@@ -24,7 +24,7 @@ export const inAppChannel: DeliveryChannel = {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
if (ctx.sseBroker) {
|
|
27
|
-
ctx.sseBroker.pushToChannel(tenantChannel(ctx.tenantId), {
|
|
27
|
+
await ctx.sseBroker.pushToChannel(tenantChannel(ctx.tenantId), {
|
|
28
28
|
type: "channel-in-app:event:delivered",
|
|
29
29
|
data: {
|
|
30
30
|
id: row?.id,
|
|
@@ -2,6 +2,7 @@ import { requireEntityTableMeta } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import {
|
|
3
3
|
boolean,
|
|
4
4
|
type EntityTableMeta,
|
|
5
|
+
index,
|
|
5
6
|
instant,
|
|
6
7
|
table as pgTable,
|
|
7
8
|
serial,
|
|
@@ -10,18 +11,23 @@ import {
|
|
|
10
11
|
uuid,
|
|
11
12
|
} from "@cosmicdrift/kumiko-framework/db";
|
|
12
13
|
|
|
13
|
-
export const inAppMessagesTable = pgTable(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
export const inAppMessagesTable = pgTable(
|
|
15
|
+
"in_app_messages",
|
|
16
|
+
{
|
|
17
|
+
id: serial("id").primaryKey(),
|
|
18
|
+
tenantId: uuid("tenant_id").notNull(),
|
|
19
|
+
userId: text("user_id").notNull(),
|
|
20
|
+
notificationType: text("notification_type").notNull(),
|
|
21
|
+
title: text("title").notNull(),
|
|
22
|
+
body: text("body"),
|
|
23
|
+
data: text("data"), // JSON string for action, screen, etc.
|
|
24
|
+
isRead: boolean("is_read").default(false).notNull(),
|
|
25
|
+
readAt: instant("read_at"),
|
|
26
|
+
createdAt: instant("created_at").default(sql`now()`).notNull(),
|
|
27
|
+
},
|
|
28
|
+
// Every inbox, unread-count, mark-all-read and userData export/delete read filters by tenant + user.
|
|
29
|
+
(t) => [index("in_app_messages_tenant_user_created_idx").on(t.tenantId, t.userId, t.createdAt)],
|
|
30
|
+
);
|
|
25
31
|
|
|
26
32
|
// Derived from inAppMessagesTable (table() attaches the meta as a Symbol) so
|
|
27
33
|
// the two can't drift, instead of hand-duplicating the column list.
|
|
@@ -3,7 +3,7 @@ import { defineQueryHandler, type TenantId } from "@cosmicdrift/kumiko-framework
|
|
|
3
3
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { tenantComplianceProfileTable } from "../../compliance-profiles/index.js";
|
|
6
|
-
import { tenantTable } from "../../tenant/index.js";
|
|
6
|
+
import { decryptTenantNames, tenantTable } from "../../tenant/index.js";
|
|
7
7
|
|
|
8
8
|
// SystemAdmin platform-wide counterpart to needs-profile (#2089).
|
|
9
9
|
//
|
|
@@ -34,9 +34,10 @@ export const tenantsMissingProfileQuery = defineQueryHandler({
|
|
|
34
34
|
"platform operator scans every tenant for a missing compliance-profile selection",
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
const tenants = await
|
|
38
|
-
isEnabled: true,
|
|
39
|
-
|
|
37
|
+
const tenants = await decryptTenantNames(
|
|
38
|
+
await db.selectMany<{ id: TenantId; name: string }>(tenantTable, { isEnabled: true }),
|
|
39
|
+
"compliance-profiles-ops:tenants-missing-profile",
|
|
40
|
+
);
|
|
40
41
|
const profileRows = await db.selectMany<{ tenantId: TenantId }>(
|
|
41
42
|
tenantComplianceProfileTable,
|
|
42
43
|
{},
|
|
@@ -13,8 +13,10 @@ import { hasConfigAccess } from "../write-helpers.js";
|
|
|
13
13
|
export const valuesQuery = defineQueryHandler({
|
|
14
14
|
name: "values",
|
|
15
15
|
description:
|
|
16
|
-
"Returns the effective value, scope and winning source for every config key the caller may read, with encrypted and secret-backed values masked; use it to inspect the current settings.",
|
|
17
|
-
schema: z.object({
|
|
16
|
+
"Returns the effective value, scope and winning source for every config key the caller may read (optionally narrowed to `keys`), with encrypted and secret-backed values masked; use it to inspect the current settings.",
|
|
17
|
+
schema: z.object({
|
|
18
|
+
keys: z.array(z.string()).optional(),
|
|
19
|
+
}),
|
|
18
20
|
// Per-key read access enforced via hasConfigAccess inside the handler.
|
|
19
21
|
access: {
|
|
20
22
|
openToAll: {
|
|
@@ -37,7 +39,10 @@ export const valuesQuery = defineQueryHandler({
|
|
|
37
39
|
const allKeys = registry.getAllConfigKeys();
|
|
38
40
|
const keyDefs = new Map<string, ConfigKeyDefinition>();
|
|
39
41
|
const filteredKeys: string[] = [];
|
|
42
|
+
const requestedKeys =
|
|
43
|
+
query.payload.keys === undefined ? undefined : new Set(query.payload.keys);
|
|
40
44
|
for (const [qualifiedKey, keyDef] of allKeys) {
|
|
45
|
+
if (requestedKeys !== undefined && !requestedKeys.has(qualifiedKey)) continue;
|
|
41
46
|
if (!hasConfigAccess(keyDef.access.read, query.user.roles)) continue;
|
|
42
47
|
keyDefs.set(qualifiedKey, keyDef);
|
|
43
48
|
filteredKeys.push(qualifiedKey);
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.354.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "forget-subject: the SUBJECT_FORGOTTEN audit now commits before the handler tx does and records cleanupError when a derived-data sweep fails",
|
|
6
|
+
"detail": "The SUBJECT_FORGOTTEN audit event is appended through the outside-transaction db, so it commits before the handler transaction does and survives a later rollback. If the blind-index sweep or search purge fails, the event carries cleanupError (error code or name, never the message) and the handler then fails; retry to finish the cleanup. The user-lifecycle step runs in a savepoint and no longer aborts the handler transaction."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.354.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "Writing a personal field of an erased data subject answers 409 subject_erased instead of 500",
|
|
12
|
+
"detail": "Writing PII to an erased data subject answers 409 `subject_erased`, rebinding a hook's unsafeRaw grant no longer inherits db.global() writes, jobs wait for boot gates before consuming\nA write that has to encrypt a personal field for a subject whose key was erased (crypto-shredding) now fails with a `ConflictError` (409, reason `subject_erased`, i18n in en and de) instead of a 500, on the executor write path and in `encryptForDirectWrite`. A hook's own `unsafeRaw` grant (`withUnsafeRawGrant`) no longer carries the handler's `globalWrites` grant. The job runner starts its BullMQ worker only after the boot gates passed, so a job queued by a previous run cannot execute against an unmet boot precondition. `buildServer` warns at boot when `r.systemScope()` features exist but no RateLimitResolver is configured. The auth, MFA, PAT, change-email and request-deletion handlers declare only the escape-hatch grants they use."
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"version": "0.338.0",
|
|
4
16
|
"type": "improvement",
|