@cosmicdrift/kumiko-bundled-features 0.333.0 → 0.335.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/dist/admin-shell/overview-panels.js +2 -2
- package/dist/agent-tools/agent-doc-lint.d.ts +1 -0
- package/dist/agent-tools/agent-doc-lint.js +33 -3
- package/dist/agent-tools/agent-manifest.js +47 -6
- package/dist/agent-tools/filter-ops.d.ts +2 -0
- package/dist/agent-tools/filter-ops.js +4 -0
- package/dist/agent-tools/tool-catalog.js +1 -4
- package/dist/agent-tools/tool-dispatch.js +1 -4
- package/dist/audit/constants.d.ts +1 -1
- package/dist/audit/constants.js +1 -1
- package/dist/audit/handlers/list.query.js +4 -3
- package/dist/auth-email-password/handlers/change-password.write.js +2 -0
- package/dist/auth-email-password/handlers/invite-create.write.js +12 -2
- package/dist/auth-email-password/handlers/login.write.js +2 -0
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/i18n.js +2 -0
- package/dist/auth-email-password/web/auth-client.d.ts +1 -0
- package/dist/auth-email-password/web/auth-client.js +1 -1
- package/dist/auth-email-password/web/auth-gate.js +6 -8
- package/dist/auth-email-password/web/index.d.ts +1 -1
- package/dist/auth-email-password/web/index.js +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -12
- package/dist/auth-email-password/web/login-screen.js +1 -1
- package/dist/auth-email-password/web/session-bootstrap-error.d.ts +5 -1
- package/dist/auth-email-password/web/session-bootstrap-error.js +2 -2
- package/dist/auth-email-password/web/session.d.ts +4 -0
- package/dist/auth-email-password/web/session.js +15 -0
- package/dist/auth-mfa/feature.js +3 -2
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +2 -2
- package/dist/auth-mfa/handlers/verify.write.js +2 -0
- package/dist/auth-mfa/hmac-token-codec.js +3 -0
- package/dist/auth-mfa/mfa-challenge-token.js +4 -0
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -0
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -0
- package/dist/auth-mfa/web/mfa-client.d.ts +1 -1
- package/dist/auth-mfa/web/mfa-client.js +4 -10
- package/dist/billing-foundation/billing-info-query.js +1 -1
- package/dist/billing-foundation/checkout-core.d.ts +5 -5
- package/dist/billing-foundation/checkout-core.js +10 -6
- package/dist/billing-foundation/entities.d.ts +8 -0
- package/dist/billing-foundation/handlers/switch-plan.write.js +5 -8
- package/dist/billing-foundation/projection.d.ts +8 -0
- package/dist/cap-counter/book-cap-usage.js +15 -5
- package/dist/cap-counter/enforce-cap.js +9 -3
- package/dist/cap-counter/entity.d.ts +1 -0
- package/dist/cap-counter/feature.js +3 -2
- package/dist/cap-counter/i18n.d.ts +1 -0
- package/dist/cap-counter/i18n.js +6 -5
- package/dist/cap-overview/handlers/caps-usage.query.js +9 -16
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +41 -15
- package/dist/cap-overview/i18n.js +5 -0
- package/dist/cap-overview/web/cap-cards-panel.js +4 -1
- package/dist/cap-overview/web/cap-usage-bar.js +1 -1
- package/dist/channel-email/email-channel.js +12 -1
- package/dist/channel-in-app/tables.js +2 -6
- package/dist/compliance-profiles/handlers/sub-processors.query.js +1 -1
- package/dist/compliance-profiles/schema/profile-selection.d.ts +2 -2
- package/dist/config/db/queries/resolver.js +3 -1
- package/dist/config/handlers/set.write.js +8 -4
- package/dist/config/table.d.ts +6 -0
- package/dist/config/web/i18n.js +13 -4
- package/dist/config/write-helpers.d.ts +1 -1
- package/dist/config/write-helpers.js +11 -5
- package/dist/crypto-shredding/handlers/forget-subject.write.js +15 -8
- package/dist/custom-fields/constants.js +1 -4
- package/dist/custom-fields/entity.d.ts +4 -0
- package/dist/data-retention/resolve-for-tenant.d.ts +2 -3
- package/dist/data-retention/run-retention-cleanup.js +100 -44
- package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -0
- package/dist/delivery/tables.d.ts +6 -0
- package/dist/delivery/types.d.ts +2 -1
- package/dist/delivery/types.js +7 -0
- package/dist/delivery/unsubscribe.js +5 -11
- package/dist/derivatives-sharp/render.js +27 -9
- package/dist/document-ingest-foundation/entity.d.ts +4 -0
- package/dist/document-ingest-foundation/feature.js +5 -5
- package/dist/document-ingest-foundation/providers.d.ts +1 -0
- package/dist/document-ingest-foundation/providers.js +6 -0
- package/dist/document-ingest-foundation/write-document-extract.d.ts +2 -2
- package/dist/document-ingest-foundation/write-document-extract.js +10 -0
- package/dist/feature-toggles/global-feature-state-table.js +2 -7
- package/dist/feature-toggles/toggle-sync-signal.d.ts +2 -1
- package/dist/feature-toggles/toggle-sync-signal.js +3 -2
- package/dist/file-provider-s3/feature.js +1 -1
- package/dist/files-tenant-data/index.js +3 -2
- package/dist/folders/entity.d.ts +8 -0
- package/dist/folders/handlers/clear-folder.write.js +4 -11
- package/dist/folders/handlers/set-folder.write.js +4 -10
- package/dist/form-draft/entity.d.ts +2 -0
- package/dist/form-draft/feature.js +3 -2
- package/dist/form-draft/handlers/discard.write.js +7 -8
- package/dist/form-draft/handlers/get.query.js +7 -1
- package/dist/foundation-shared/host-allowlist.d.ts +4 -0
- package/dist/foundation-shared/host-allowlist.js +14 -0
- package/dist/foundation-shared/index.d.ts +1 -0
- package/dist/foundation-shared/index.js +1 -0
- package/dist/foundation-shared/mail-host-policy.d.ts +0 -2
- package/dist/foundation-shared/mail-host-policy.js +3 -13
- package/dist/inbound-mail-foundation/connect-routes.js +7 -5
- package/dist/inbound-mail-foundation/entities.d.ts +25 -0
- package/dist/inbound-mail-foundation/projection.d.ts +20 -0
- package/dist/inbound-mail-foundation/watch-supervisor.js +47 -43
- package/dist/jobs/handlers/list.query.d.ts +7 -2
- package/dist/jobs/handlers/list.query.js +9 -2
- package/dist/jobs/handlers/retry.write.js +3 -2
- package/dist/jobs/handlers/tenant-failures.query.js +3 -9
- package/dist/jobs/job-run-logger.js +18 -2
- package/dist/jobs/job-run-table.d.ts +6 -0
- package/dist/jobs/tenant-job-failure-table.js +2 -6
- package/dist/ledger/entity.d.ts +16 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +2 -2
- package/dist/ledger/handlers/reverse-transaction.write.js +2 -2
- package/dist/ledger/subject-reference.d.ts +4 -0
- package/dist/ledger/subject-reference.js +7 -0
- package/dist/legal-pages/feature.js +8 -1
- package/dist/managed-pages/handlers/branding.query.js +1 -1
- package/dist/managed-pages/handlers/by-slug.query.js +1 -1
- package/dist/managed-pages/handlers/by-tenant-published.query.js +1 -1
- package/dist/managed-pages/table.d.ts +12 -0
- package/dist/metrics/compute.js +7 -1
- package/dist/metrics/window.js +5 -0
- package/dist/notes-history/entity.d.ts +12 -0
- package/dist/notes-history/handlers/add-note.write.js +4 -9
- package/dist/notes-history-user-data/hooks.js +8 -1
- package/dist/personal-access-tokens/constants.d.ts +1 -0
- package/dist/personal-access-tokens/constants.js +1 -0
- package/dist/personal-access-tokens/feature.js +2 -2
- package/dist/personal-access-tokens/handlers/create.write.js +2 -2
- package/dist/personal-access-tokens/handlers/revoke.write.js +2 -2
- package/dist/personal-access-tokens/i18n.js +12 -13
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -0
- package/dist/personal-access-tokens/schema/api-token.js +2 -1
- package/dist/personal-access-tokens/screens.js +5 -1
- package/dist/rate-limiting/feature.js +2 -0
- package/dist/rate-limiting/handlers/bucket-access.js +15 -7
- package/dist/rate-limiting/i18n.d.ts +10 -0
- package/dist/rate-limiting/i18n.js +10 -0
- package/dist/secrets/feature.js +3 -2
- package/dist/secrets/table.d.ts +1 -0
- package/dist/seo/handlers/seo-config.query.js +1 -1
- package/dist/sessions/constants.d.ts +1 -0
- package/dist/sessions/constants.js +1 -0
- package/dist/sessions/feature.js +5 -3
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -0
- package/dist/sessions/handlers/revoke-all-for-user.write.js +12 -2
- package/dist/sessions/handlers/revoke.write.js +2 -2
- package/dist/sessions/schema/user-session.d.ts +4 -0
- package/dist/sessions/schema/user-session.js +2 -1
- package/dist/sessions/screens.js +5 -1
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/lockout-counter.js +1 -1
- package/dist/shared/parent-visibility.d.ts +5 -0
- package/dist/shared/parent-visibility.js +17 -1
- package/dist/shared/row-bound-grant.js +20 -9
- package/dist/shared/single-use-token-store.js +5 -13
- package/dist/step-dispatcher/webhook-runner.d.ts +0 -2
- package/dist/step-dispatcher/webhook-runner.js +18 -17
- package/dist/subscription-mollie/plugin-methods.d.ts +3 -2
- package/dist/subscription-mollie/plugin-methods.js +7 -1
- package/dist/subscription-mollie/verify-webhook.js +6 -0
- package/dist/subscription-stripe/feature.js +3 -3
- package/dist/subscription-stripe/plugin-methods.js +25 -6
- package/dist/subscription-stripe/verify-webhook.js +13 -6
- package/dist/tags/entity.d.ts +9 -0
- package/dist/tags/handlers/assign-tag.write.js +4 -10
- package/dist/tags/handlers/remove-tag.write.js +4 -11
- package/dist/template-resolver/api.d.ts +2 -1
- package/dist/template-resolver/api.js +1 -1
- package/dist/template-resolver/handlers/by-slug.query.d.ts +1 -1
- package/dist/template-resolver/handlers/by-slug.query.js +2 -2
- package/dist/template-resolver/handlers/by-tenant.query.d.ts +3 -2
- package/dist/template-resolver/handlers/by-tenant.query.js +2 -2
- package/dist/template-resolver/handlers/collection-item.query.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-list.query.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-shared.d.ts +3 -2
- package/dist/template-resolver/handlers/collection-shared.js +1 -1
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -1
- package/dist/template-resolver/handlers/find-by-id.query.js +1 -1
- package/dist/template-resolver/handlers/list.query.d.ts +1 -1
- package/dist/template-resolver/handlers/list.query.js +1 -1
- package/dist/template-resolver/seeding.js +41 -39
- package/dist/template-resolver/table.d.ts +26 -13
- package/dist/template-resolver/testing.js +2 -2
- package/dist/template-resolver/user-content-table.d.ts +16 -9
- package/dist/template-resolver/web/client-plugin.d.ts +1 -1
- package/dist/template-resolver-user-data/hooks.js +1 -1
- package/dist/tenant/cancel-pending-invitation.d.ts +8 -0
- package/dist/tenant/cancel-pending-invitation.js +22 -0
- package/dist/tenant/feature.js +4 -2
- package/dist/tenant/handlers/cancel-invitation.write.js +6 -21
- package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/member-directory.query.js +17 -4
- package/dist/tenant/handlers/remove-member.write.js +9 -14
- package/dist/tenant/handlers/team-list.query.js +2 -2
- package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/tenant-directory.query.js +13 -3
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update-member-roles.write.js +28 -12
- package/dist/tenant/i18n.js +4 -0
- package/dist/tenant/index.d.ts +1 -1
- package/dist/tenant/index.js +1 -1
- package/dist/tenant/invitation-table.d.ts +8 -2
- package/dist/tenant/last-tenant-admin.d.ts +1 -1
- package/dist/tenant/last-tenant-admin.js +5 -3
- package/dist/tenant/membership-table.d.ts +4 -0
- package/dist/tenant/schema/tenant.d.ts +8 -2
- package/dist/tenant/seeding.d.ts +4 -0
- package/dist/tenant/seeding.js +9 -2
- package/dist/tenant-lifecycle/boot-checks.d.ts +1 -0
- package/dist/tenant-lifecycle/boot-checks.js +27 -1
- package/dist/tenant-lifecycle/feature.js +2 -1
- package/dist/tenant-lifecycle/stages.js +2 -2
- package/dist/tenant-settings/feature.js +9 -6
- package/dist/tier-engine/entity.d.ts +2 -0
- package/dist/tier-engine/feature.js +3 -2
- package/dist/tier-engine/tier-resolver.d.ts +2 -0
- package/dist/user/command-schemas.d.ts +2 -2
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/create.write.js +8 -2
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/handlers/update.write.js +8 -2
- package/dist/user/schema/user.d.ts +14 -2
- package/dist/user-data-rights/db/queries/export-jobs.d.ts +2 -0
- package/dist/user-data-rights/feature.js +22 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +4 -7
- package/dist/user-data-rights/handlers/deletion-grace-period.d.ts +3 -2
- package/dist/user-data-rights/handlers/deletion-grace-period.js +30 -8
- package/dist/user-data-rights/handlers/download-by-token.query.js +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +3 -6
- package/dist/user-data-rights/lib/update-user-lifecycle.js +7 -14
- package/dist/user-data-rights/run-export-jobs.d.ts +1 -0
- package/dist/user-data-rights/run-export-jobs.js +74 -41
- package/dist/user-data-rights/run-forget-cleanup.js +2 -2
- package/dist/user-data-rights/run-user-export.js +8 -4
- package/dist/user-data-rights/schema/download-attempt.d.ts +14 -0
- package/dist/user-data-rights/schema/download-token.d.ts +8 -0
- package/dist/user-data-rights/schema/export-job.d.ts +8 -2
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +4 -1
- package/dist/user-profile/feature.js +6 -1
- package/dist/user-profile/handlers/change-email.write.js +2 -0
- package/dist/workflow-runner/db/queries/due-runs.js +6 -9
- package/dist/workflow-runner/event-subscriber.js +29 -9
- package/dist/workflow-runner/handlers/resume-run.write.js +8 -5
- package/dist/workflow-runner/pending-projection.js +8 -1
- package/package.json +11 -9
- package/src/agent-tools/changes.json +12 -0
- package/src/auth-email-password/changes.json +18 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +7 -0
- package/src/cap-counter/changes.json +7 -0
- package/src/cap-overview/changes.json +12 -0
- package/src/channel-email/changes.json +6 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/data-retention/changes.json +12 -0
- package/src/delivery/changes.json +7 -0
- package/src/derivatives-sharp/changes.json +11 -0
- package/src/document-ingest-foundation/changes.json +12 -0
- package/src/inbound-mail-foundation/changes.json +12 -0
- package/src/jobs/changes.json +13 -0
- package/src/legal-pages/changes.json +8 -1
- package/src/managed-pages/changes.json +5 -0
- package/src/metrics/changes.json +6 -0
- package/src/rate-limiting/changes.json +5 -0
- package/src/secrets/changes.json +7 -0
- package/src/sessions/changes.json +5 -0
- package/src/shared/changes.json +6 -0
- package/src/step-dispatcher/changes.json +5 -0
- package/src/subscription-mollie/changes.json +7 -0
- package/src/subscription-stripe/changes.json +11 -0
- package/src/template-resolver/changes.json +7 -0
- package/src/tenant/changes.json +29 -0
- package/src/user-data-rights/changes.json +22 -0
- package/src/workflow-runner/changes.json +6 -0
|
@@ -9,6 +9,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
9
9
|
readonly filterable?: boolean;
|
|
10
10
|
readonly encrypted?: boolean;
|
|
11
11
|
readonly sensitive?: boolean;
|
|
12
|
+
readonly writeOnly?: boolean;
|
|
12
13
|
readonly readAsInstruction?: boolean;
|
|
13
14
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
14
15
|
readonly default?: string;
|
|
@@ -29,6 +30,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
29
30
|
readonly filterable?: boolean;
|
|
30
31
|
readonly encrypted?: boolean;
|
|
31
32
|
readonly sensitive?: boolean;
|
|
33
|
+
readonly writeOnly?: boolean;
|
|
32
34
|
readonly readAsInstruction?: boolean;
|
|
33
35
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
34
36
|
readonly default?: string;
|
|
@@ -49,6 +51,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
49
51
|
readonly filterable?: boolean;
|
|
50
52
|
readonly encrypted?: boolean;
|
|
51
53
|
readonly sensitive?: boolean;
|
|
54
|
+
readonly writeOnly?: boolean;
|
|
52
55
|
readonly readAsInstruction?: boolean;
|
|
53
56
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
54
57
|
readonly default?: string;
|
|
@@ -69,6 +72,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
69
72
|
readonly filterable?: boolean;
|
|
70
73
|
readonly encrypted?: boolean;
|
|
71
74
|
readonly sensitive?: boolean;
|
|
75
|
+
readonly writeOnly?: boolean;
|
|
72
76
|
readonly readAsInstruction?: boolean;
|
|
73
77
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
74
78
|
readonly default?: string;
|
|
@@ -89,6 +93,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
89
93
|
readonly filterable?: boolean;
|
|
90
94
|
readonly encrypted?: boolean;
|
|
91
95
|
readonly sensitive?: boolean;
|
|
96
|
+
readonly writeOnly?: boolean;
|
|
92
97
|
readonly readAsInstruction?: boolean;
|
|
93
98
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
94
99
|
readonly default?: string;
|
|
@@ -109,6 +114,7 @@ export declare const pageEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
109
114
|
readonly filterable?: boolean;
|
|
110
115
|
readonly encrypted?: boolean;
|
|
111
116
|
readonly sensitive?: boolean;
|
|
117
|
+
readonly writeOnly?: boolean;
|
|
112
118
|
readonly readAsInstruction?: boolean;
|
|
113
119
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
114
120
|
readonly default?: string;
|
|
@@ -134,6 +140,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
134
140
|
readonly filterable?: boolean;
|
|
135
141
|
readonly encrypted?: boolean;
|
|
136
142
|
readonly sensitive?: boolean;
|
|
143
|
+
readonly writeOnly?: boolean;
|
|
137
144
|
readonly readAsInstruction?: boolean;
|
|
138
145
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
139
146
|
readonly default?: string;
|
|
@@ -154,6 +161,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
154
161
|
readonly filterable?: boolean;
|
|
155
162
|
readonly encrypted?: boolean;
|
|
156
163
|
readonly sensitive?: boolean;
|
|
164
|
+
readonly writeOnly?: boolean;
|
|
157
165
|
readonly readAsInstruction?: boolean;
|
|
158
166
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
159
167
|
readonly default?: string;
|
|
@@ -174,6 +182,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
174
182
|
readonly filterable?: boolean;
|
|
175
183
|
readonly encrypted?: boolean;
|
|
176
184
|
readonly sensitive?: boolean;
|
|
185
|
+
readonly writeOnly?: boolean;
|
|
177
186
|
readonly readAsInstruction?: boolean;
|
|
178
187
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
179
188
|
readonly default?: string;
|
|
@@ -194,6 +203,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
194
203
|
readonly filterable?: boolean;
|
|
195
204
|
readonly encrypted?: boolean;
|
|
196
205
|
readonly sensitive?: boolean;
|
|
206
|
+
readonly writeOnly?: boolean;
|
|
197
207
|
readonly readAsInstruction?: boolean;
|
|
198
208
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
199
209
|
readonly default?: string;
|
|
@@ -214,6 +224,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
214
224
|
readonly filterable?: boolean;
|
|
215
225
|
readonly encrypted?: boolean;
|
|
216
226
|
readonly sensitive?: boolean;
|
|
227
|
+
readonly writeOnly?: boolean;
|
|
217
228
|
readonly readAsInstruction?: boolean;
|
|
218
229
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
219
230
|
readonly default?: string;
|
|
@@ -234,6 +245,7 @@ export declare const pagesTable: import("@cosmicdrift/kumiko-framework/db").Enti
|
|
|
234
245
|
readonly filterable?: boolean;
|
|
235
246
|
readonly encrypted?: boolean;
|
|
236
247
|
readonly sensitive?: boolean;
|
|
248
|
+
readonly writeOnly?: boolean;
|
|
237
249
|
readonly readAsInstruction?: boolean;
|
|
238
250
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
239
251
|
readonly default?: string;
|
package/dist/metrics/compute.js
CHANGED
|
@@ -111,7 +111,13 @@ export async function runMetric(db, metric, params) {
|
|
|
111
111
|
? bucketStartsMs(window, bucket, params.timeZone)
|
|
112
112
|
: [];
|
|
113
113
|
const fillBuckets = (byBucketMs) => bucketStarts.map((atMs) => ({ atMs, value: byBucketMs.get(atMs) ?? gapValue }));
|
|
114
|
-
|
|
114
|
+
// Grouped metrics carry their time series in `series`; fetchAggregates skips the total series, so filling gaps here would fake a flat zero line.
|
|
115
|
+
const points = metric.groupBy === undefined
|
|
116
|
+
? fillBuckets(new Map(aggregates.points.map((row) => [
|
|
117
|
+
Number(row.keys[0]),
|
|
118
|
+
row.value,
|
|
119
|
+
])))
|
|
120
|
+
: [];
|
|
115
121
|
const rows = buildRows(metric, aggregates, value, labelOf);
|
|
116
122
|
return {
|
|
117
123
|
value,
|
package/dist/metrics/window.js
CHANGED
|
@@ -12,7 +12,12 @@ const RANGE_DAYS = {
|
|
|
12
12
|
"14d": 14,
|
|
13
13
|
"30d": 30,
|
|
14
14
|
};
|
|
15
|
+
// Offset strings ("+01:00") pass Intl/Temporal as ISO offsets but Postgres
|
|
16
|
+
// date_trunc reads them as POSIX zones with the opposite sign, so only named
|
|
17
|
+
// zones are accepted.
|
|
15
18
|
export function isValidTimeZone(timeZone) {
|
|
19
|
+
if (/^[+-]/.test(timeZone))
|
|
20
|
+
return false;
|
|
16
21
|
try {
|
|
17
22
|
new Intl.DateTimeFormat(undefined, { timeZone });
|
|
18
23
|
return true;
|
|
@@ -10,6 +10,7 @@ export declare function createNoteEntryEntity(access?: EntityDefinition["access"
|
|
|
10
10
|
readonly filterable?: boolean;
|
|
11
11
|
readonly encrypted?: boolean;
|
|
12
12
|
readonly sensitive?: boolean;
|
|
13
|
+
readonly writeOnly?: boolean;
|
|
13
14
|
readonly readAsInstruction?: boolean;
|
|
14
15
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
15
16
|
readonly default?: string;
|
|
@@ -30,6 +31,7 @@ export declare function createNoteEntryEntity(access?: EntityDefinition["access"
|
|
|
30
31
|
readonly filterable?: boolean;
|
|
31
32
|
readonly encrypted?: boolean;
|
|
32
33
|
readonly sensitive?: boolean;
|
|
34
|
+
readonly writeOnly?: boolean;
|
|
33
35
|
readonly readAsInstruction?: boolean;
|
|
34
36
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
35
37
|
readonly default?: string;
|
|
@@ -50,6 +52,7 @@ export declare function createNoteEntryEntity(access?: EntityDefinition["access"
|
|
|
50
52
|
readonly filterable?: boolean;
|
|
51
53
|
readonly encrypted?: boolean;
|
|
52
54
|
readonly sensitive?: boolean;
|
|
55
|
+
readonly writeOnly?: boolean;
|
|
53
56
|
readonly readAsInstruction?: boolean;
|
|
54
57
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
55
58
|
readonly default?: string;
|
|
@@ -70,6 +73,7 @@ export declare function createNoteEntryEntity(access?: EntityDefinition["access"
|
|
|
70
73
|
readonly filterable?: boolean;
|
|
71
74
|
readonly encrypted?: boolean;
|
|
72
75
|
readonly sensitive?: boolean;
|
|
76
|
+
readonly writeOnly?: boolean;
|
|
73
77
|
readonly readAsInstruction?: boolean;
|
|
74
78
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
75
79
|
readonly default?: string;
|
|
@@ -108,6 +112,7 @@ export declare const noteEntryEntity: EntityDefinition<{
|
|
|
108
112
|
readonly filterable?: boolean;
|
|
109
113
|
readonly encrypted?: boolean;
|
|
110
114
|
readonly sensitive?: boolean;
|
|
115
|
+
readonly writeOnly?: boolean;
|
|
111
116
|
readonly readAsInstruction?: boolean;
|
|
112
117
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
113
118
|
readonly default?: string;
|
|
@@ -128,6 +133,7 @@ export declare const noteEntryEntity: EntityDefinition<{
|
|
|
128
133
|
readonly filterable?: boolean;
|
|
129
134
|
readonly encrypted?: boolean;
|
|
130
135
|
readonly sensitive?: boolean;
|
|
136
|
+
readonly writeOnly?: boolean;
|
|
131
137
|
readonly readAsInstruction?: boolean;
|
|
132
138
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
133
139
|
readonly default?: string;
|
|
@@ -148,6 +154,7 @@ export declare const noteEntryEntity: EntityDefinition<{
|
|
|
148
154
|
readonly filterable?: boolean;
|
|
149
155
|
readonly encrypted?: boolean;
|
|
150
156
|
readonly sensitive?: boolean;
|
|
157
|
+
readonly writeOnly?: boolean;
|
|
151
158
|
readonly readAsInstruction?: boolean;
|
|
152
159
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
153
160
|
readonly default?: string;
|
|
@@ -168,6 +175,7 @@ export declare const noteEntryEntity: EntityDefinition<{
|
|
|
168
175
|
readonly filterable?: boolean;
|
|
169
176
|
readonly encrypted?: boolean;
|
|
170
177
|
readonly sensitive?: boolean;
|
|
178
|
+
readonly writeOnly?: boolean;
|
|
171
179
|
readonly readAsInstruction?: boolean;
|
|
172
180
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
173
181
|
readonly default?: string;
|
|
@@ -206,6 +214,7 @@ export declare function createNoteMentionEntity(): EntityDefinition<{
|
|
|
206
214
|
readonly filterable?: boolean;
|
|
207
215
|
readonly encrypted?: boolean;
|
|
208
216
|
readonly sensitive?: boolean;
|
|
217
|
+
readonly writeOnly?: boolean;
|
|
209
218
|
readonly readAsInstruction?: boolean;
|
|
210
219
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
211
220
|
readonly default?: string;
|
|
@@ -226,6 +235,7 @@ export declare function createNoteMentionEntity(): EntityDefinition<{
|
|
|
226
235
|
readonly filterable?: boolean;
|
|
227
236
|
readonly encrypted?: boolean;
|
|
228
237
|
readonly sensitive?: boolean;
|
|
238
|
+
readonly writeOnly?: boolean;
|
|
229
239
|
readonly readAsInstruction?: boolean;
|
|
230
240
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
231
241
|
readonly default?: string;
|
|
@@ -248,6 +258,7 @@ export declare const noteMentionEntity: EntityDefinition<{
|
|
|
248
258
|
readonly filterable?: boolean;
|
|
249
259
|
readonly encrypted?: boolean;
|
|
250
260
|
readonly sensitive?: boolean;
|
|
261
|
+
readonly writeOnly?: boolean;
|
|
251
262
|
readonly readAsInstruction?: boolean;
|
|
252
263
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
253
264
|
readonly default?: string;
|
|
@@ -268,6 +279,7 @@ export declare const noteMentionEntity: EntityDefinition<{
|
|
|
268
279
|
readonly filterable?: boolean;
|
|
269
280
|
readonly encrypted?: boolean;
|
|
270
281
|
readonly sensitive?: boolean;
|
|
282
|
+
readonly writeOnly?: boolean;
|
|
271
283
|
readonly readAsInstruction?: boolean;
|
|
272
284
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
273
285
|
readonly default?: string;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { fetchOne, runInSavepointIfSupported } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import {
|
|
3
|
-
import { decryptStoredPii, joinRowParentIsVisible } from "../../shared/index.js";
|
|
2
|
+
import { decryptStoredPii, denyUnlessJoinRowParentVisible } from "../../shared/index.js";
|
|
4
3
|
import { userTable } from "../../user/index.js";
|
|
5
4
|
import { DEFAULT_NOTES_HISTORY_ACCESS } from "../constants.js";
|
|
6
5
|
import { noteEntryExecutor, noteMentionExecutor } from "../executor.js";
|
|
@@ -29,13 +28,9 @@ export function createAddNoteHandler(access = DEFAULT_NOTES_HISTORY_ACCESS) {
|
|
|
29
28
|
},
|
|
30
29
|
handler: async (event, ctx) => {
|
|
31
30
|
const payload = event.payload; // @cast-boundary engine-payload
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const visible = await joinRowParentIsVisible(ctx.registry, "note-entry", payload, event.user, ctx.db);
|
|
36
|
-
if (!visible) {
|
|
37
|
-
return writeFailure(new NotFoundError(payload.entityType, payload.entityId));
|
|
38
|
-
}
|
|
31
|
+
const denied = await denyUnlessJoinRowParentVisible(ctx.registry, "note-entry", payload, event.user, ctx.db);
|
|
32
|
+
if (denied)
|
|
33
|
+
return denied;
|
|
39
34
|
let authorName = null;
|
|
40
35
|
try {
|
|
41
36
|
// read_users is tenant-agnostic → ctx.db.unsafeRaw, not the tenant-scoped ctx.db.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// dependency. Mirrors job-run/delivery-attempt (user-data-rights-defaults):
|
|
5
5
|
// export-only, erasure via crypto-shredding.
|
|
6
6
|
import { configuredPiiSubjectKms } from "@cosmicdrift/kumiko-framework/crypto";
|
|
7
|
-
import { resolveRetentionPolicyForTenant } from "../data-retention/index.js";
|
|
7
|
+
import { resolveRetentionPolicyForTenant, resolveTenantRetentionPreset, } from "../data-retention/index.js";
|
|
8
8
|
import { noteEntryTable, noteMentionTable } from "../notes-history/index.js";
|
|
9
9
|
import { policyToStrategy } from "../user-data-rights/index.js";
|
|
10
10
|
// note-entry has no per-tenant scope quirk (unlike folders) — it's genuinely
|
|
@@ -58,6 +58,12 @@ export const noteEntryDeleteHook = async (ctx) => {
|
|
|
58
58
|
if (mentions.length === 0)
|
|
59
59
|
return;
|
|
60
60
|
const noteIds = new Set(mentions.map((m) => m.noteId));
|
|
61
|
+
// Once per hook run: the preset depends on the tenant only, not the note.
|
|
62
|
+
const tenantPreset = await resolveTenantRetentionPreset({
|
|
63
|
+
db: ctx.db,
|
|
64
|
+
registry: ctx.registry,
|
|
65
|
+
tenantId: ctx.tenantId,
|
|
66
|
+
});
|
|
61
67
|
for (const noteId of noteIds) {
|
|
62
68
|
const note = await ctx.db.fetchOne(noteEntryTable, { id: noteId });
|
|
63
69
|
// Defensive: append-only rows are never hard-deleted, so this shouldn't
|
|
@@ -69,6 +75,7 @@ export const noteEntryDeleteHook = async (ctx) => {
|
|
|
69
75
|
registry: ctx.registry,
|
|
70
76
|
tenantId: ctx.tenantId,
|
|
71
77
|
entityName: note.entityType,
|
|
78
|
+
preloadedTenantPreset: tenantPreset,
|
|
72
79
|
});
|
|
73
80
|
if (policyToStrategy(hostPolicy.policy?.strategy ?? null) === "anonymize")
|
|
74
81
|
continue;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Pure constants — client-marked so the web screen (web/) may import screen-id
|
|
3
3
|
// and QN names without pulling the feature's server runtime barrel.
|
|
4
4
|
export const PAT_FEATURE = "personal-access-tokens";
|
|
5
|
+
export const API_TOKEN_ENTITY_NAME = "api-token";
|
|
5
6
|
// Snake_case reason strings (Error-Reasons guard: no colons/dashes).
|
|
6
7
|
export const PatErrors = {
|
|
7
8
|
ownershipDenied: "ownership_denied",
|
|
@@ -2,7 +2,7 @@ import { EXT_TOKEN_VERIFIER, } from "@cosmicdrift/kumiko-bundled-features/auth-f
|
|
|
2
2
|
import { PAT_TOKEN_PREFIX } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { deriveEntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
|
|
4
4
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
-
import { PAT_DEFAULT_RATE_LIMIT, PAT_FEATURE } from "./constants.js";
|
|
5
|
+
import { API_TOKEN_ENTITY_NAME, PAT_DEFAULT_RATE_LIMIT, PAT_FEATURE, } from "./constants.js";
|
|
6
6
|
import { buildAvailableScopesQuery } from "./handlers/available-scopes.query.js";
|
|
7
7
|
import { createPatCreateHandler } from "./handlers/create.write.js";
|
|
8
8
|
import { listPatQuery } from "./handlers/list.query.js";
|
|
@@ -62,7 +62,7 @@ export function createPersonalAccessTokensFeature(options) {
|
|
|
62
62
|
// Direct-write store like store_user_sessions: create/revoke write it, the
|
|
63
63
|
// resolver point-reads it. r.entity would make it a rebuildable projection
|
|
64
64
|
// whose replay (no token events) would wipe every live token (#498/#494).
|
|
65
|
-
r.storeTable(deriveEntityTableMeta(
|
|
65
|
+
r.storeTable(deriveEntityTableMeta(API_TOKEN_ENTITY_NAME, apiTokenEntity, { source: "unmanaged" }), {
|
|
66
66
|
reason: "read_side.api_tokens_direct_write",
|
|
67
67
|
// create.write encrypts `name` via encryptForDirectWrite (#820).
|
|
68
68
|
piiEncryptedOnWrite: true,
|
|
@@ -6,7 +6,7 @@ import { Temporal } from "temporal-polyfill";
|
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { encryptForDirectWrite, verifyPassword } from "../../shared/index.js";
|
|
8
8
|
import { UserQueries } from "../../user/index.js";
|
|
9
|
-
import { PAT_DEFAULT_EXPIRES_IN_DAYS, PatErrors } from "../constants.js";
|
|
9
|
+
import { API_TOKEN_ENTITY_NAME, PAT_DEFAULT_EXPIRES_IN_DAYS, PatErrors } from "../constants.js";
|
|
10
10
|
import { mintPatToken } from "../hash.js";
|
|
11
11
|
import { apiTokenEntity, apiTokenTable } from "../schema/api-token.js";
|
|
12
12
|
// @wrapper-known error-helper
|
|
@@ -67,7 +67,7 @@ export function createPatCreateHandler(opts = {}) {
|
|
|
67
67
|
const { raw, hash, prefix } = mintPatToken();
|
|
68
68
|
const now = Temporal.Now.instant();
|
|
69
69
|
const id = generateId();
|
|
70
|
-
const row = await encryptForDirectWrite(apiTokenEntity,
|
|
70
|
+
const row = await encryptForDirectWrite(apiTokenEntity, API_TOKEN_ENTITY_NAME, {
|
|
71
71
|
id,
|
|
72
72
|
userId: event.user.id,
|
|
73
73
|
tenantId: event.user.tenantId,
|
|
@@ -4,7 +4,7 @@ import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/
|
|
|
4
4
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
5
|
import { Temporal } from "temporal-polyfill";
|
|
6
6
|
import * as z from "zod";
|
|
7
|
-
import { PatErrors } from "../constants.js";
|
|
7
|
+
import { API_TOKEN_ENTITY_NAME, PatErrors } from "../constants.js";
|
|
8
8
|
import { PAT_REVOKED_AGGREGATE_TYPE, PAT_REVOKED_EVENT_QN } from "../pat-revoked-event.js";
|
|
9
9
|
import { apiTokenTable } from "../schema/api-token.js";
|
|
10
10
|
// Revoke one of the caller's own tokens. Ownership is enforced in the WHERE
|
|
@@ -43,7 +43,7 @@ export const revokePatWrite = defineWriteHandler({
|
|
|
43
43
|
i18nKey: "errors.ownershipDenied",
|
|
44
44
|
details: {
|
|
45
45
|
scope: "entity",
|
|
46
|
-
entityName:
|
|
46
|
+
entityName: API_TOKEN_ENTITY_NAME,
|
|
47
47
|
action: "revoke",
|
|
48
48
|
userId: event.user.id,
|
|
49
49
|
},
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { ACTION_FORM_ENTITY } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
import { fieldLabelKey, fieldOptionLabelKey } from "@cosmicdrift/kumiko-headless";
|
|
3
|
+
import { PAT_FEATURE } from "./constants.js";
|
|
4
|
+
const mintFieldKey = (field) => fieldLabelKey(PAT_FEATURE, ACTION_FORM_ENTITY, field);
|
|
5
|
+
const scopeOptionKey = (grant) => fieldOptionLabelKey(PAT_FEATURE, ACTION_FORM_ENTITY, "scopes", grant);
|
|
1
6
|
export const PAT_FEATURE_I18N = {
|
|
2
7
|
"screen:api-tokens.title": { en: "Personal Access Tokens" },
|
|
3
8
|
"screen:api-token-create.title": { en: "Create a new token" },
|
|
@@ -23,13 +28,11 @@ export const PAT_FEATURE_I18N = {
|
|
|
23
28
|
// (`__action-form__`, see action-form-shim.ts) — required by the i18n
|
|
24
29
|
// boot-validator (requiredKeysFromScreen); the declarative renderer
|
|
25
30
|
// resolves these through the schema payload, not a client component's t().
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
"personal-access-tokens:entity:__action-form__:field:mfaCode": { en: "2FA code (if enabled)" },
|
|
31
|
+
[mintFieldKey("name")]: { en: "Name" },
|
|
32
|
+
[mintFieldKey("scopes")]: { en: "Per-API access" },
|
|
33
|
+
[mintFieldKey("expiresInDays")]: { en: "Expires in (days)" },
|
|
34
|
+
[mintFieldKey("currentPassword")]: { en: "Password (to confirm)" },
|
|
35
|
+
[mintFieldKey("mfaCode")]: { en: "2FA code (if enabled)" },
|
|
33
36
|
};
|
|
34
37
|
// Per-grant-option labels for the mint form's `scopes` multiSelect —
|
|
35
38
|
// "<domain> (read)" / "<domain> (read & write)", matching the wording the
|
|
@@ -38,13 +41,9 @@ export const PAT_FEATURE_I18N = {
|
|
|
38
41
|
export function patScopeOptionTranslations(scopes) {
|
|
39
42
|
const out = {};
|
|
40
43
|
for (const [domain, def] of Object.entries(scopes)) {
|
|
41
|
-
out[
|
|
42
|
-
en: `${def.label} (read)`,
|
|
43
|
-
};
|
|
44
|
+
out[scopeOptionKey(`${domain}:read`)] = { en: `${def.label} (read)` };
|
|
44
45
|
if (def.write && def.write.length > 0) {
|
|
45
|
-
out[
|
|
46
|
-
en: `${def.label} (read & write)`,
|
|
47
|
-
};
|
|
46
|
+
out[scopeOptionKey(`${domain}:write`)] = { en: `${def.label} (read & write)` };
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
return out;
|
|
@@ -9,6 +9,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
9
9
|
readonly filterable?: boolean;
|
|
10
10
|
readonly encrypted?: boolean;
|
|
11
11
|
readonly sensitive?: boolean;
|
|
12
|
+
readonly writeOnly?: boolean;
|
|
12
13
|
readonly readAsInstruction?: boolean;
|
|
13
14
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
14
15
|
readonly default?: string;
|
|
@@ -29,6 +30,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
29
30
|
readonly filterable?: boolean;
|
|
30
31
|
readonly encrypted?: boolean;
|
|
31
32
|
readonly sensitive?: boolean;
|
|
33
|
+
readonly writeOnly?: boolean;
|
|
32
34
|
readonly readAsInstruction?: boolean;
|
|
33
35
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
34
36
|
readonly default?: string;
|
|
@@ -49,6 +51,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
49
51
|
readonly filterable?: boolean;
|
|
50
52
|
readonly encrypted?: boolean;
|
|
51
53
|
readonly sensitive?: boolean;
|
|
54
|
+
readonly writeOnly?: boolean;
|
|
52
55
|
readonly readAsInstruction?: boolean;
|
|
53
56
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
54
57
|
readonly default?: string;
|
|
@@ -69,6 +72,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
69
72
|
readonly filterable?: boolean;
|
|
70
73
|
readonly encrypted?: boolean;
|
|
71
74
|
readonly sensitive?: boolean;
|
|
75
|
+
readonly writeOnly?: boolean;
|
|
72
76
|
readonly readAsInstruction?: boolean;
|
|
73
77
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
74
78
|
readonly default?: string;
|
|
@@ -89,6 +93,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
89
93
|
readonly filterable?: boolean;
|
|
90
94
|
readonly encrypted?: boolean;
|
|
91
95
|
readonly sensitive?: boolean;
|
|
96
|
+
readonly writeOnly?: boolean;
|
|
92
97
|
readonly readAsInstruction?: boolean;
|
|
93
98
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
94
99
|
readonly default?: string;
|
|
@@ -109,6 +114,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
109
114
|
readonly filterable?: boolean;
|
|
110
115
|
readonly encrypted?: boolean;
|
|
111
116
|
readonly sensitive?: boolean;
|
|
117
|
+
readonly writeOnly?: boolean;
|
|
112
118
|
readonly readAsInstruction?: boolean;
|
|
113
119
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
114
120
|
readonly default?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { deriveEntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import { access, createEntity, createTextField, createTimestampField, } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { API_TOKEN_ENTITY_NAME } from "../constants.js";
|
|
3
4
|
// One row per Personal Access Token. Like store_user_sessions this is a
|
|
4
5
|
// direct-write store (r.unmanagedTable): the create/revoke handlers write it and
|
|
5
6
|
// the resolver point-reads it on the hot auth path. `tokenHash` is the SHA-256
|
|
@@ -80,6 +81,6 @@ export const apiTokenEntity = createEntity({
|
|
|
80
81
|
// deriveEntityTableMeta (not buildEntityTable): this is a direct-write store, so
|
|
81
82
|
// the table must be a WritableTable (post ES-write-brand #742) — same as
|
|
82
83
|
// sessions' userSessionTable. buildEntityTable is branded executor-only.
|
|
83
|
-
export const apiTokenTable = deriveEntityTableMeta(
|
|
84
|
+
export const apiTokenTable = deriveEntityTableMeta(API_TOKEN_ENTITY_NAME, apiTokenEntity, {
|
|
84
85
|
source: "unmanaged",
|
|
85
86
|
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { i18nKey, } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
|
+
import { i18nKey, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { PAT_MINT_SCREEN_ID, PAT_SCREEN_ID, PatHandlers, PatQueries } from "./constants.js";
|
|
3
3
|
const PAT_STATUS_OPTION_KEY_PREFIX = "pat.list.status.";
|
|
4
4
|
export const patListScreen = {
|
|
5
5
|
id: PAT_SCREEN_ID,
|
|
6
6
|
type: "projectionList",
|
|
7
7
|
query: PatQueries.mine,
|
|
8
|
+
// The `mine` handler honours `limit` only (no offset/total), so a pager would
|
|
9
|
+
// show page 1 forever: send one max-size request and render no pager.
|
|
10
|
+
pagination: false,
|
|
11
|
+
pageSize: MAX_LIST_LIMIT,
|
|
8
12
|
defaultSort: { field: "createdAt", dir: "desc" },
|
|
9
13
|
columns: [
|
|
10
14
|
{ field: "name", label: i18nKey("pat.list.col.name") },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { rateLimitStatus } from "./handlers/status.query.js";
|
|
3
|
+
import { RATE_LIMITING_I18N } from "./i18n.js";
|
|
3
4
|
// Opt-in feature. Loading it does NOT install rate-limit middleware —
|
|
4
5
|
// the framework auto-wires the L3 dispatcher hook and the resolver
|
|
5
6
|
// when (a) at least one handler declared a rateLimit option, OR (b) the
|
|
@@ -17,5 +18,6 @@ export function createRateLimitingFeature() {
|
|
|
17
18
|
recommended: false,
|
|
18
19
|
});
|
|
19
20
|
r.queryHandler(rateLimitStatus);
|
|
21
|
+
r.translations({ keys: RATE_LIMITING_I18N });
|
|
20
22
|
}, { dedupeOptions: {} });
|
|
21
23
|
}
|
|
@@ -1,23 +1,31 @@
|
|
|
1
|
+
import { ROLES } from "@cosmicdrift/kumiko-framework/auth";
|
|
1
2
|
import { AccessDeniedError } from "@cosmicdrift/kumiko-framework/errors";
|
|
2
3
|
import { RateLimitErrors } from "../constants.js";
|
|
4
|
+
// Typed against RateLimitPer so a renamed dimension in bucket.ts
|
|
5
|
+
// breaks the typecheck instead of silently denying access.
|
|
6
|
+
const BUCKET_OWNER_KIND = {
|
|
7
|
+
tenant: "tenant",
|
|
8
|
+
"tenant+handler": "tenant",
|
|
9
|
+
user: "user",
|
|
10
|
+
"user+handler": "user",
|
|
11
|
+
};
|
|
12
|
+
function isOwnableDimension(dimension) {
|
|
13
|
+
return Object.hasOwn(BUCKET_OWNER_KIND, dimension);
|
|
14
|
+
}
|
|
3
15
|
function ownsBucket(bucket, caller) {
|
|
4
16
|
const [dimension, subject, ...handler] = bucket.split(":");
|
|
5
17
|
if (dimension === undefined)
|
|
6
18
|
return false;
|
|
7
|
-
|
|
8
|
-
? caller.tenantId
|
|
9
|
-
: dimension === "user" || dimension === "user+handler"
|
|
10
|
-
? caller.id
|
|
11
|
-
: undefined;
|
|
12
|
-
if (owner === undefined)
|
|
19
|
+
if (!isOwnableDimension(dimension))
|
|
13
20
|
return false;
|
|
21
|
+
const owner = BUCKET_OWNER_KIND[dimension] === "tenant" ? caller.tenantId : caller.id;
|
|
14
22
|
// Segment-exact: a startsWith check would pass `tenant:<own-id>-other`.
|
|
15
23
|
if (subject !== owner)
|
|
16
24
|
return false;
|
|
17
25
|
return dimension.endsWith("+handler") ? handler.length > 0 : handler.length === 0;
|
|
18
26
|
}
|
|
19
27
|
export function bucketAccessDenied(bucket, caller) {
|
|
20
|
-
if (caller.roles.includes(
|
|
28
|
+
if (caller.roles.includes(ROLES.SystemAdmin))
|
|
21
29
|
return undefined;
|
|
22
30
|
if (ownsBucket(bucket, caller))
|
|
23
31
|
return undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const RATE_LIMITING_I18N: {
|
|
2
|
+
readonly "rateLimiting.errors.bucketOutsideTenant": {
|
|
3
|
+
readonly en: "You can only inspect rate-limit buckets that belong to your own tenant or user.";
|
|
4
|
+
readonly de: "Du kannst nur Rate-Limit-Buckets deines eigenen Mandanten oder Benutzers einsehen.";
|
|
5
|
+
};
|
|
6
|
+
readonly "rateLimiting.errors.resolverUnavailable": {
|
|
7
|
+
readonly en: "Rate limiting is not available on this server.";
|
|
8
|
+
readonly de: "Rate-Limiting ist auf diesem Server nicht verfügbar.";
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const RATE_LIMITING_I18N = {
|
|
2
|
+
"rateLimiting.errors.bucketOutsideTenant": {
|
|
3
|
+
en: "You can only inspect rate-limit buckets that belong to your own tenant or user.",
|
|
4
|
+
de: "Du kannst nur Rate-Limit-Buckets deines eigenen Mandanten oder Benutzers einsehen.",
|
|
5
|
+
},
|
|
6
|
+
"rateLimiting.errors.resolverUnavailable": {
|
|
7
|
+
en: "Rate limiting is not available on this server.",
|
|
8
|
+
de: "Rate-Limiting ist auf diesem Server nicht verfügbar.",
|
|
9
|
+
},
|
|
10
|
+
};
|
package/dist/secrets/feature.js
CHANGED
|
@@ -9,6 +9,7 @@ import { rotateJob } from "./handlers/rotate.job.js";
|
|
|
9
9
|
import { createSetHandler } from "./handlers/set.write.js";
|
|
10
10
|
import { secretReadSchema } from "./secrets-context.js";
|
|
11
11
|
import { tenantSecretEntity, tenantSecretsTable } from "./table.js";
|
|
12
|
+
const ROTATE_ESCAPE_HATCH_REASON = "re-encrypts every tenant's secrets with the new master key";
|
|
12
13
|
/**
|
|
13
14
|
* Env-vars contract for the `secrets` feature. Apps merge this via
|
|
14
15
|
* `composeEnvSchema({ features: [secretsFeature, ...] })` so boot-time
|
|
@@ -130,9 +131,9 @@ export function createSecretsFeature(opts = {}) {
|
|
|
130
131
|
name: "rotate",
|
|
131
132
|
trigger: { manual: true },
|
|
132
133
|
escapeHatch: {
|
|
133
|
-
reason:
|
|
134
|
+
reason: ROTATE_ESCAPE_HATCH_REASON,
|
|
134
135
|
},
|
|
135
|
-
handler: (payload, ctx) => rotateJob(payload, ctx, ctx.db.unsafeRaw(
|
|
136
|
+
handler: (payload, ctx) => rotateJob(payload, ctx, ctx.db.unsafeRaw(ROTATE_ESCAPE_HATCH_REASON)),
|
|
136
137
|
});
|
|
137
138
|
// Pre-ES had a separate `retention-cleanup` job scrubbing the audit
|
|
138
139
|
// table on a compliance-driven schedule (90d default). Post-ES the
|
package/dist/secrets/table.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const tenantSecretEntity: import("@cosmicdrift/kumiko-types/field
|
|
|
14
14
|
readonly filterable?: boolean;
|
|
15
15
|
readonly encrypted?: boolean;
|
|
16
16
|
readonly sensitive?: boolean;
|
|
17
|
+
readonly writeOnly?: boolean;
|
|
17
18
|
readonly readAsInstruction?: boolean;
|
|
18
19
|
readonly format?: "email" | "url" | "phone" | "password";
|
|
19
20
|
readonly default?: string;
|
|
@@ -17,7 +17,7 @@ export const seoConfigQuery = defineQueryHandler({
|
|
|
17
17
|
name: "config",
|
|
18
18
|
schema: z.object({}),
|
|
19
19
|
access: { roles: ["anonymous", "User", "TenantAdmin", "SystemAdmin"] },
|
|
20
|
-
rateLimit: { per: "ip", limit: 60, windowSeconds: 60 },
|
|
20
|
+
rateLimit: { per: "ip+handler", limit: 60, windowSeconds: 60 },
|
|
21
21
|
description: "Reads the tenant's SEO metadata settings — organization name and logo URL, Twitter site handle, LLMs summary and default OG image — as plain strings; use it to inspect what the sitemap, llms.txt and page meta tags will publish.",
|
|
22
22
|
handler: async (_query, ctx) => {
|
|
23
23
|
const read = async (qualifiedKey) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const SESSIONS_FEATURE: "sessions";
|
|
2
|
+
export declare const USER_SESSION_ENTITY_NAME: "user-session";
|
|
2
3
|
export declare const SessionHandlers: {
|
|
3
4
|
readonly revoke: "sessions:write:user-session:revoke";
|
|
4
5
|
readonly revokeAllOthers: "sessions:write:user-session:revoke-all-others";
|
package/dist/sessions/feature.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EXT_SESSION_STORE, } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
2
2
|
import { deriveEntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { USER_SESSION_ENTITY_NAME } from "./constants.js";
|
|
4
5
|
import { cleanupJob } from "./handlers/cleanup.job.js";
|
|
5
6
|
import { detailQuery } from "./handlers/detail.query.js";
|
|
6
7
|
import { listQuery } from "./handlers/list.query.js";
|
|
@@ -13,6 +14,7 @@ import { userSessionEntity } from "./schema/user-session.js";
|
|
|
13
14
|
import { sessionDetailScreen, sessionListScreen, sessionMineScreen } from "./screens.js";
|
|
14
15
|
import { createSessionCallbacks, } from "./session-callbacks.js";
|
|
15
16
|
import { SESSION_REVOKED_EVENT_SHORT, sessionRevokedSchema } from "./session-revoked-event.js";
|
|
17
|
+
const CLEANUP_ESCAPE_HATCH_REASON = "deletes expired session rows of every tenant";
|
|
16
18
|
// Reads the late-bind setter off a mounted sessions feature's exports.
|
|
17
19
|
// run{Prod,Dev}App call it once the DB connection is concrete — the feature
|
|
18
20
|
// itself is constructed in app run-config long before a db exists, so the
|
|
@@ -66,7 +68,7 @@ export function createSessionsFeature(options) {
|
|
|
66
68
|
// (#498/#494). r.storeTable keeps the migration DDL but opts the
|
|
67
69
|
// table out of implicit rebuild, like jobs/channel-in-app/feature-toggles
|
|
68
70
|
// which are direct-write stores too.
|
|
69
|
-
r.storeTable(deriveEntityTableMeta(
|
|
71
|
+
r.storeTable(deriveEntityTableMeta(USER_SESSION_ENTITY_NAME, userSessionEntity, { source: "unmanaged" }), {
|
|
70
72
|
reason: "read_side.user_sessions_direct_write",
|
|
71
73
|
// sessionCreator encrypts ip/userAgent via encryptForDirectWrite (#820).
|
|
72
74
|
piiEncryptedOnWrite: true,
|
|
@@ -118,8 +120,8 @@ export function createSessionsFeature(options) {
|
|
|
118
120
|
r.job({
|
|
119
121
|
name: "cleanup",
|
|
120
122
|
trigger: { manual: true },
|
|
121
|
-
escapeHatch: { reason:
|
|
122
|
-
handler: (payload, ctx) => cleanupJob(payload, ctx, ctx.db.unsafeRaw(
|
|
123
|
+
escapeHatch: { reason: CLEANUP_ESCAPE_HATCH_REASON },
|
|
124
|
+
handler: (payload, ctx) => cleanupJob(payload, ctx, ctx.db.unsafeRaw(CLEANUP_ESCAPE_HATCH_REASON)),
|
|
123
125
|
});
|
|
124
126
|
// Cross-feature entity hook on "user". `r.entityHook` (NOT `r.hook`) is
|
|
125
127
|
// the supported cross-feature path: entity-keyed, not prefixed by the
|