@cosmicdrift/kumiko-bundled-features 0.382.0 → 0.383.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/audit/run-escape-hatch-retention.d.ts +1 -0
- package/dist/audit/run-escape-hatch-retention.js +54 -27
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -6
- package/dist/billing-foundation/entities.d.ts +8 -8
- package/dist/billing-foundation/projection.d.ts +8 -8
- package/dist/cap-counter/entity.d.ts +1 -1
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +21 -6
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +6 -4
- package/dist/config/table.d.ts +6 -6
- package/dist/custom-fields/entity.d.ts +4 -4
- package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -4
- package/dist/delivery/attempt-log.d.ts +2 -0
- package/dist/delivery/attempt-log.js +12 -1
- package/dist/delivery/delivery-service.js +8 -3
- package/dist/delivery/feature.js +7 -9
- package/dist/delivery/mask-recipient-address.d.ts +1 -0
- package/dist/delivery/mask-recipient-address.js +16 -0
- package/dist/delivery/tables.d.ts +6 -6
- package/dist/document-ingest-foundation/entity.d.ts +4 -4
- package/dist/folders/entity.d.ts +8 -8
- package/dist/form-draft/entity.d.ts +2 -2
- package/dist/inbound-mail-foundation/entities.d.ts +25 -25
- package/dist/inbound-mail-foundation/projection.d.ts +20 -20
- package/dist/jobs/job-run-logger.d.ts +1 -1
- package/dist/jobs/job-run-logger.js +20 -0
- package/dist/jobs/job-run-table.d.ts +6 -6
- package/dist/ledger/entity.d.ts +13 -13
- package/dist/managed-pages/table.d.ts +12 -12
- package/dist/notes-history/entity.d.ts +12 -12
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -6
- package/dist/secrets/table.d.ts +1 -1
- package/dist/sessions/schema/user-session.d.ts +4 -4
- package/dist/sessions/session-callbacks.js +23 -16
- package/dist/tags/entity.d.ts +9 -9
- package/dist/template-resolver/table.d.ts +10 -10
- package/dist/template-resolver/user-content-table.d.ts +10 -10
- package/dist/tenant/decrypt-tenant-names.d.ts +4 -0
- package/dist/tenant/decrypt-tenant-names.js +15 -0
- package/dist/tenant/handlers/tenant-directory.query.js +6 -7
- package/dist/tenant/invitation-table.d.ts +6 -6
- package/dist/tenant/membership-table.d.ts +4 -4
- package/dist/tenant/schema/tenant.d.ts +6 -6
- package/dist/tier-engine/entity.d.ts +1 -1
- package/dist/tier-engine/tier-resolver.d.ts +1 -1
- package/dist/user/schema/user.d.ts +12 -12
- package/dist/user-data-rights/schema/download-attempt.d.ts +14 -14
- package/dist/user-data-rights/schema/download-token.d.ts +8 -8
- package/dist/user-data-rights/schema/export-job.d.ts +6 -6
- package/dist/waitlist/entity.d.ts +14 -14
- package/package.json +9 -9
- package/src/audit/changes.json +6 -0
- package/src/auth-email-password/changes.json +6 -0
- package/src/cap-overview/changes.json +6 -0
- package/src/delivery/changes.json +6 -0
- package/src/jobs/changes.json +7 -0
- package/src/sessions/changes.json +6 -0
- package/src/tenant/changes.json +6 -0
|
@@ -13,7 +13,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
13
13
|
readonly sensitive?: boolean;
|
|
14
14
|
readonly writeOnly?: boolean;
|
|
15
15
|
readonly readAsInstruction?: boolean;
|
|
16
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
16
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
17
17
|
readonly default?: string;
|
|
18
18
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
19
19
|
readonly multiline?: boolean | {
|
|
@@ -36,7 +36,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
36
36
|
readonly sensitive?: boolean;
|
|
37
37
|
readonly writeOnly?: boolean;
|
|
38
38
|
readonly readAsInstruction?: boolean;
|
|
39
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
39
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
40
40
|
readonly default?: string;
|
|
41
41
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
42
42
|
readonly multiline?: boolean | {
|
|
@@ -59,7 +59,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
59
59
|
readonly sensitive?: boolean;
|
|
60
60
|
readonly writeOnly?: boolean;
|
|
61
61
|
readonly readAsInstruction?: boolean;
|
|
62
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
62
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
63
63
|
readonly default?: string;
|
|
64
64
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
65
65
|
readonly multiline?: boolean | {
|
|
@@ -82,7 +82,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
82
82
|
readonly sensitive?: boolean;
|
|
83
83
|
readonly writeOnly?: boolean;
|
|
84
84
|
readonly readAsInstruction?: boolean;
|
|
85
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
85
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
86
86
|
readonly default?: string;
|
|
87
87
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
88
88
|
readonly multiline?: boolean | {
|
|
@@ -105,7 +105,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
105
105
|
readonly sensitive?: boolean;
|
|
106
106
|
readonly writeOnly?: boolean;
|
|
107
107
|
readonly readAsInstruction?: boolean;
|
|
108
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
108
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
109
109
|
readonly default?: string;
|
|
110
110
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
111
111
|
readonly multiline?: boolean | {
|
|
@@ -128,7 +128,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
128
128
|
readonly sensitive?: boolean;
|
|
129
129
|
readonly writeOnly?: boolean;
|
|
130
130
|
readonly readAsInstruction?: boolean;
|
|
131
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
131
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
132
132
|
readonly default?: string;
|
|
133
133
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
134
134
|
readonly multiline?: boolean | {
|
package/dist/secrets/table.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const tenantSecretEntity: import("@cosmicdrift/kumiko-types/field
|
|
|
18
18
|
readonly sensitive?: boolean;
|
|
19
19
|
readonly writeOnly?: boolean;
|
|
20
20
|
readonly readAsInstruction?: boolean;
|
|
21
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
21
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
22
22
|
readonly default?: string;
|
|
23
23
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
24
24
|
readonly multiline?: boolean | {
|
|
@@ -13,7 +13,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
13
13
|
readonly sensitive?: boolean;
|
|
14
14
|
readonly writeOnly?: boolean;
|
|
15
15
|
readonly readAsInstruction?: boolean;
|
|
16
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
16
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
17
17
|
readonly default?: string;
|
|
18
18
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
19
19
|
readonly multiline?: boolean | {
|
|
@@ -36,7 +36,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
36
36
|
readonly sensitive?: boolean;
|
|
37
37
|
readonly writeOnly?: boolean;
|
|
38
38
|
readonly readAsInstruction?: boolean;
|
|
39
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
39
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
40
40
|
readonly default?: string;
|
|
41
41
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
42
42
|
readonly multiline?: boolean | {
|
|
@@ -119,7 +119,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
119
119
|
readonly sensitive?: boolean;
|
|
120
120
|
readonly writeOnly?: boolean;
|
|
121
121
|
readonly readAsInstruction?: boolean;
|
|
122
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
122
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
123
123
|
readonly default?: string;
|
|
124
124
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
125
125
|
readonly multiline?: boolean | {
|
|
@@ -142,7 +142,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
142
142
|
readonly sensitive?: boolean;
|
|
143
143
|
readonly writeOnly?: boolean;
|
|
144
144
|
readonly readAsInstruction?: boolean;
|
|
145
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
145
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
146
146
|
readonly default?: string;
|
|
147
147
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
148
148
|
readonly multiline?: boolean | {
|
|
@@ -11,6 +11,9 @@ import { userSessionEntity, userSessionTable } from "./schema/user-session.js";
|
|
|
11
11
|
import { SESSION_REVOKED_AGGREGATE_TYPE, SESSION_REVOKED_EVENT_QN, sessionRevokedSchema, } from "./session-revoked-event.js";
|
|
12
12
|
// Re-exported so existing `../sessions` importers keep working unchanged.
|
|
13
13
|
export { isPrincipalBlocked };
|
|
14
|
+
function settleLookup(lookup) {
|
|
15
|
+
return lookup.then((value) => ({ ok: true, value }), () => ({ ok: false }));
|
|
16
|
+
}
|
|
14
17
|
export function createSessionCallbacks(opts) {
|
|
15
18
|
const ttlMs = opts.expiresInMs ?? DEFAULT_SESSION_EXPIRY_MS;
|
|
16
19
|
const { db } = opts;
|
|
@@ -69,6 +72,12 @@ export function createSessionCallbacks(opts) {
|
|
|
69
72
|
},
|
|
70
73
|
// kumiko-lint-ignore complexity-budget session check + lastSeen refresh on hot path
|
|
71
74
|
async sessionChecker(sid, expectedUserId) {
|
|
75
|
+
// The user lookup is keyed by the JWT's userId, not the session row, so it
|
|
76
|
+
// overlaps the session read. Settled eagerly: a throwing session read must
|
|
77
|
+
// not leave this promise as an unhandled rejection.
|
|
78
|
+
const userLookup = settleLookup(fetchOne(db, userTable, {
|
|
79
|
+
id: expectedUserId,
|
|
80
|
+
}));
|
|
72
81
|
const row = await fetchOne(db, userSessionTable, { id: sid });
|
|
73
82
|
if (!row)
|
|
74
83
|
return "missing";
|
|
@@ -85,6 +94,12 @@ export function createSessionCallbacks(opts) {
|
|
|
85
94
|
if (row.expiresAt.epochMilliseconds <= Temporal.Now.instant().epochMilliseconds) {
|
|
86
95
|
return "expired";
|
|
87
96
|
}
|
|
97
|
+
// Membership is keyed by the session's tenantId, so it can only start now;
|
|
98
|
+
// it overlaps the user lookup and the lastSeen refresh.
|
|
99
|
+
const membershipLookup = settleLookup(fetchOne(db, tenantMembershipsTable, {
|
|
100
|
+
userId: expectedUserId,
|
|
101
|
+
tenantId: row.tenantId,
|
|
102
|
+
}));
|
|
88
103
|
// Hourly, not per-request: this check runs on every authenticated
|
|
89
104
|
// request, so a write here has to stay off the hot path.
|
|
90
105
|
const nowMs = Temporal.Now.instant().epochMilliseconds;
|
|
@@ -110,13 +125,10 @@ export function createSessionCallbacks(opts) {
|
|
|
110
125
|
// missing row is a legitimate "no tenant roles" outcome. Both branches
|
|
111
126
|
// return the bare "live" string (no re-derived roles) — the middleware
|
|
112
127
|
// falls back to the JWT's frozen roles claim.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
user = await fetchOne(db, userTable, { id: expectedUserId });
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
128
|
+
const userResult = await userLookup;
|
|
129
|
+
if (!userResult.ok)
|
|
118
130
|
return "live";
|
|
119
|
-
|
|
131
|
+
const user = userResult.value;
|
|
120
132
|
if (!user)
|
|
121
133
|
return "live";
|
|
122
134
|
if (isPrincipalRowBlocked(user))
|
|
@@ -125,17 +137,12 @@ export function createSessionCallbacks(opts) {
|
|
|
125
137
|
// DB error on the membership read must not lock the user out. A
|
|
126
138
|
// missing row (not a throw) is a valid outcome: the user genuinely has
|
|
127
139
|
// no tenant-scoped roles for row.tenantId, so membershipRoles is [].
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const membership = await fetchOne(db, tenantMembershipsTable, {
|
|
131
|
-
userId: expectedUserId,
|
|
132
|
-
tenantId: row.tenantId,
|
|
133
|
-
});
|
|
134
|
-
membershipRoles = membership ? parseRoles(membership.roles) : [];
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
140
|
+
const membershipResult = await membershipLookup;
|
|
141
|
+
if (!membershipResult.ok)
|
|
137
142
|
return "live";
|
|
138
|
-
|
|
143
|
+
const membershipRoles = membershipResult.value
|
|
144
|
+
? parseRoles(membershipResult.value.roles)
|
|
145
|
+
: [];
|
|
139
146
|
const globalRoles = parseRoles(user.roles);
|
|
140
147
|
const roles = buildSessionRoles(globalRoles, membershipRoles);
|
|
141
148
|
return { status: "live", roles };
|
package/dist/tags/entity.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
14
14
|
readonly sensitive?: boolean;
|
|
15
15
|
readonly writeOnly?: boolean;
|
|
16
16
|
readonly readAsInstruction?: boolean;
|
|
17
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
17
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
18
18
|
readonly default?: string;
|
|
19
19
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
20
20
|
readonly multiline?: boolean | {
|
|
@@ -37,7 +37,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
37
37
|
readonly sensitive?: boolean;
|
|
38
38
|
readonly writeOnly?: boolean;
|
|
39
39
|
readonly readAsInstruction?: boolean;
|
|
40
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
40
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
41
41
|
readonly default?: string;
|
|
42
42
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
43
43
|
readonly multiline?: boolean | {
|
|
@@ -60,7 +60,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
60
60
|
readonly sensitive?: boolean;
|
|
61
61
|
readonly writeOnly?: boolean;
|
|
62
62
|
readonly readAsInstruction?: boolean;
|
|
63
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
63
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
64
64
|
readonly default?: string;
|
|
65
65
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
66
66
|
readonly multiline?: boolean | {
|
|
@@ -85,7 +85,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
85
85
|
readonly sensitive?: boolean;
|
|
86
86
|
readonly writeOnly?: boolean;
|
|
87
87
|
readonly readAsInstruction?: boolean;
|
|
88
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
88
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
89
89
|
readonly default?: string;
|
|
90
90
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
91
91
|
readonly multiline?: boolean | {
|
|
@@ -108,7 +108,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
108
108
|
readonly sensitive?: boolean;
|
|
109
109
|
readonly writeOnly?: boolean;
|
|
110
110
|
readonly readAsInstruction?: boolean;
|
|
111
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
111
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
112
112
|
readonly default?: string;
|
|
113
113
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
114
114
|
readonly multiline?: boolean | {
|
|
@@ -131,7 +131,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
131
131
|
readonly sensitive?: boolean;
|
|
132
132
|
readonly writeOnly?: boolean;
|
|
133
133
|
readonly readAsInstruction?: boolean;
|
|
134
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
134
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
135
135
|
readonly default?: string;
|
|
136
136
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
137
137
|
readonly multiline?: boolean | {
|
|
@@ -156,7 +156,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
156
156
|
readonly sensitive?: boolean;
|
|
157
157
|
readonly writeOnly?: boolean;
|
|
158
158
|
readonly readAsInstruction?: boolean;
|
|
159
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
159
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
160
160
|
readonly default?: string;
|
|
161
161
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
162
162
|
readonly multiline?: boolean | {
|
|
@@ -179,7 +179,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
179
179
|
readonly sensitive?: boolean;
|
|
180
180
|
readonly writeOnly?: boolean;
|
|
181
181
|
readonly readAsInstruction?: boolean;
|
|
182
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
182
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
183
183
|
readonly default?: string;
|
|
184
184
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
185
185
|
readonly multiline?: boolean | {
|
|
@@ -202,7 +202,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
202
202
|
readonly sensitive?: boolean;
|
|
203
203
|
readonly writeOnly?: boolean;
|
|
204
204
|
readonly readAsInstruction?: boolean;
|
|
205
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
205
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
206
206
|
readonly default?: string;
|
|
207
207
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
208
208
|
readonly multiline?: boolean | {
|
|
@@ -14,7 +14,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
14
14
|
readonly sensitive?: boolean;
|
|
15
15
|
readonly writeOnly?: boolean;
|
|
16
16
|
readonly readAsInstruction?: boolean;
|
|
17
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
17
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
18
18
|
readonly default?: string;
|
|
19
19
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
20
20
|
readonly multiline?: boolean | {
|
|
@@ -66,7 +66,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
66
66
|
readonly sensitive?: boolean;
|
|
67
67
|
readonly writeOnly?: boolean;
|
|
68
68
|
readonly readAsInstruction?: boolean;
|
|
69
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
69
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
70
70
|
readonly default?: string;
|
|
71
71
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
72
72
|
readonly multiline?: boolean | {
|
|
@@ -89,7 +89,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
89
89
|
readonly sensitive?: boolean;
|
|
90
90
|
readonly writeOnly?: boolean;
|
|
91
91
|
readonly readAsInstruction?: boolean;
|
|
92
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
92
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
93
93
|
readonly default?: string;
|
|
94
94
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
95
95
|
readonly multiline?: boolean | {
|
|
@@ -112,7 +112,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
112
112
|
readonly sensitive?: boolean;
|
|
113
113
|
readonly writeOnly?: boolean;
|
|
114
114
|
readonly readAsInstruction?: boolean;
|
|
115
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
115
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
116
116
|
readonly default?: string;
|
|
117
117
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
118
118
|
readonly multiline?: boolean | {
|
|
@@ -245,7 +245,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
245
245
|
readonly sensitive?: boolean;
|
|
246
246
|
readonly writeOnly?: boolean;
|
|
247
247
|
readonly readAsInstruction?: boolean;
|
|
248
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
248
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
249
249
|
readonly default?: string;
|
|
250
250
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
251
251
|
readonly multiline?: boolean | {
|
|
@@ -343,7 +343,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
343
343
|
readonly sensitive?: boolean;
|
|
344
344
|
readonly writeOnly?: boolean;
|
|
345
345
|
readonly readAsInstruction?: boolean;
|
|
346
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
346
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
347
347
|
readonly default?: string;
|
|
348
348
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
349
349
|
readonly multiline?: boolean | {
|
|
@@ -395,7 +395,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
395
395
|
readonly sensitive?: boolean;
|
|
396
396
|
readonly writeOnly?: boolean;
|
|
397
397
|
readonly readAsInstruction?: boolean;
|
|
398
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
398
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
399
399
|
readonly default?: string;
|
|
400
400
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
401
401
|
readonly multiline?: boolean | {
|
|
@@ -418,7 +418,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
418
418
|
readonly sensitive?: boolean;
|
|
419
419
|
readonly writeOnly?: boolean;
|
|
420
420
|
readonly readAsInstruction?: boolean;
|
|
421
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
421
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
422
422
|
readonly default?: string;
|
|
423
423
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
424
424
|
readonly multiline?: boolean | {
|
|
@@ -441,7 +441,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
441
441
|
readonly sensitive?: boolean;
|
|
442
442
|
readonly writeOnly?: boolean;
|
|
443
443
|
readonly readAsInstruction?: boolean;
|
|
444
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
444
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
445
445
|
readonly default?: string;
|
|
446
446
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
447
447
|
readonly multiline?: boolean | {
|
|
@@ -574,7 +574,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
574
574
|
readonly sensitive?: boolean;
|
|
575
575
|
readonly writeOnly?: boolean;
|
|
576
576
|
readonly readAsInstruction?: boolean;
|
|
577
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
577
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
578
578
|
readonly default?: string;
|
|
579
579
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
580
580
|
readonly multiline?: boolean | {
|
|
@@ -14,7 +14,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
14
14
|
readonly sensitive?: boolean;
|
|
15
15
|
readonly writeOnly?: boolean;
|
|
16
16
|
readonly readAsInstruction?: boolean;
|
|
17
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
17
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
18
18
|
readonly default?: string;
|
|
19
19
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
20
20
|
readonly multiline?: boolean | {
|
|
@@ -37,7 +37,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
37
37
|
readonly sensitive?: boolean;
|
|
38
38
|
readonly writeOnly?: boolean;
|
|
39
39
|
readonly readAsInstruction?: boolean;
|
|
40
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
40
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
41
41
|
readonly default?: string;
|
|
42
42
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
43
43
|
readonly multiline?: boolean | {
|
|
@@ -89,7 +89,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
89
89
|
readonly sensitive?: boolean;
|
|
90
90
|
readonly writeOnly?: boolean;
|
|
91
91
|
readonly readAsInstruction?: boolean;
|
|
92
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
92
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
93
93
|
readonly default?: string;
|
|
94
94
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
95
95
|
readonly multiline?: boolean | {
|
|
@@ -112,7 +112,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
112
112
|
readonly sensitive?: boolean;
|
|
113
113
|
readonly writeOnly?: boolean;
|
|
114
114
|
readonly readAsInstruction?: boolean;
|
|
115
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
115
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
116
116
|
readonly default?: string;
|
|
117
117
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
118
118
|
readonly multiline?: boolean | {
|
|
@@ -135,7 +135,7 @@ export declare const userContentEntryEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
135
135
|
readonly sensitive?: boolean;
|
|
136
136
|
readonly writeOnly?: boolean;
|
|
137
137
|
readonly readAsInstruction?: boolean;
|
|
138
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
138
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
139
139
|
readonly default?: string;
|
|
140
140
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
141
141
|
readonly multiline?: boolean | {
|
|
@@ -200,7 +200,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
|
|
|
200
200
|
readonly sensitive?: boolean;
|
|
201
201
|
readonly writeOnly?: boolean;
|
|
202
202
|
readonly readAsInstruction?: boolean;
|
|
203
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
203
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
204
204
|
readonly default?: string;
|
|
205
205
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
206
206
|
readonly multiline?: boolean | {
|
|
@@ -223,7 +223,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
|
|
|
223
223
|
readonly sensitive?: boolean;
|
|
224
224
|
readonly writeOnly?: boolean;
|
|
225
225
|
readonly readAsInstruction?: boolean;
|
|
226
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
226
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
227
227
|
readonly default?: string;
|
|
228
228
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
229
229
|
readonly multiline?: boolean | {
|
|
@@ -275,7 +275,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
|
|
|
275
275
|
readonly sensitive?: boolean;
|
|
276
276
|
readonly writeOnly?: boolean;
|
|
277
277
|
readonly readAsInstruction?: boolean;
|
|
278
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
278
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
279
279
|
readonly default?: string;
|
|
280
280
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
281
281
|
readonly multiline?: boolean | {
|
|
@@ -298,7 +298,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
|
|
|
298
298
|
readonly sensitive?: boolean;
|
|
299
299
|
readonly writeOnly?: boolean;
|
|
300
300
|
readonly readAsInstruction?: boolean;
|
|
301
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
301
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
302
302
|
readonly default?: string;
|
|
303
303
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
304
304
|
readonly multiline?: boolean | {
|
|
@@ -321,7 +321,7 @@ export declare const userContentEntriesTable: import("@cosmicdrift/kumiko-framew
|
|
|
321
321
|
readonly sensitive?: boolean;
|
|
322
322
|
readonly writeOnly?: boolean;
|
|
323
323
|
readonly readAsInstruction?: boolean;
|
|
324
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
324
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
325
325
|
readonly default?: string;
|
|
326
326
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
327
327
|
readonly multiline?: boolean | {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export declare function decryptTenantNames<T extends {
|
|
2
2
|
readonly name?: unknown;
|
|
3
3
|
}>(rows: readonly T[], fallbackRequestId: string): Promise<T[]>;
|
|
4
|
+
export declare function takeTenantsMatchingDecryptedName<T extends {
|
|
5
|
+
readonly id: unknown;
|
|
6
|
+
readonly name?: unknown;
|
|
7
|
+
}>(rows: readonly T[], labelMatches: (label: string) => boolean, limit: number, fallbackRequestId: string): Promise<T[]>;
|
|
@@ -8,3 +8,18 @@ export async function decryptTenantNames(rows, fallbackRequestId) {
|
|
|
8
8
|
? { ...row, name: await decryptStoredPii(row.name, "name", fallbackRequestId) }
|
|
9
9
|
: row);
|
|
10
10
|
}
|
|
11
|
+
// Tenant names are not in the search index (find: "none"), so a name search has to decrypt;
|
|
12
|
+
// stopping at `limit` matches keeps a typed prefix from decrypting the whole scan window.
|
|
13
|
+
export async function takeTenantsMatchingDecryptedName(rows, labelMatches, limit, fallbackRequestId) {
|
|
14
|
+
const batchSize = Math.max(limit, KMS_POOL_CONCURRENCY);
|
|
15
|
+
const matches = [];
|
|
16
|
+
for (let start = 0; start < rows.length && matches.length < limit; start += batchSize) {
|
|
17
|
+
const decrypted = await decryptTenantNames(rows.slice(start, start + batchSize), fallbackRequestId);
|
|
18
|
+
for (const row of decrypted) {
|
|
19
|
+
const label = typeof row.name === "string" ? row.name : String(row.id);
|
|
20
|
+
if (labelMatches(label))
|
|
21
|
+
matches.push(row);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return matches.slice(0, limit);
|
|
25
|
+
}
|
|
@@ -2,7 +2,7 @@ import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/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 * as z from "zod";
|
|
5
|
-
import { decryptTenantNames } from "../decrypt-tenant-names.js";
|
|
5
|
+
import { decryptTenantNames, takeTenantsMatchingDecryptedName } from "../decrypt-tenant-names.js";
|
|
6
6
|
import { tenantTable } from "../schema/tenant.js";
|
|
7
7
|
import { isSystemAdmin } from "./is-system-admin.js";
|
|
8
8
|
// Label source behind every `tenant:tenant` reference column (fw#3142). The
|
|
@@ -51,17 +51,16 @@ export const tenantDirectoryQuery = definePagedQueryHandler({
|
|
|
51
51
|
}
|
|
52
52
|
// name is ciphertext at rest, so the SQL page is keyed by the plaintext
|
|
53
53
|
// `key`; label order and search happen in memory after decrypting.
|
|
54
|
-
const
|
|
55
|
-
const
|
|
54
|
+
const needle = search?.toLowerCase();
|
|
55
|
+
const named = needle === undefined
|
|
56
|
+
? await decryptTenantNames(tenants, "tenant:tenant-directory")
|
|
57
|
+
: await takeTenantsMatchingDecryptedName(tenants, (label) => label.toLowerCase().includes(needle), limit, "tenant:tenant-directory");
|
|
58
|
+
const rows = named
|
|
56
59
|
.map((tenant) => {
|
|
57
60
|
const id = String(tenant.id);
|
|
58
61
|
return { id, label: typeof tenant.name === "string" ? tenant.name : id };
|
|
59
62
|
})
|
|
60
63
|
.sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
|
|
61
|
-
const needle = search?.toLowerCase();
|
|
62
|
-
const rows = needle === undefined
|
|
63
|
-
? labeled
|
|
64
|
-
: labeled.filter((row) => row.label.toLowerCase().includes(needle)).slice(0, limit);
|
|
65
64
|
return { rows, nextCursor: null };
|
|
66
65
|
},
|
|
67
66
|
});
|
|
@@ -21,7 +21,7 @@ export declare const tenantInvitationEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
21
21
|
readonly sensitive?: boolean;
|
|
22
22
|
readonly writeOnly?: boolean;
|
|
23
23
|
readonly readAsInstruction?: boolean;
|
|
24
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
24
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
25
25
|
readonly default?: string;
|
|
26
26
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
27
27
|
readonly multiline?: boolean | {
|
|
@@ -44,7 +44,7 @@ export declare const tenantInvitationEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
44
44
|
readonly sensitive?: boolean;
|
|
45
45
|
readonly writeOnly?: boolean;
|
|
46
46
|
readonly readAsInstruction?: boolean;
|
|
47
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
47
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
48
48
|
readonly default?: string;
|
|
49
49
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
50
50
|
readonly multiline?: boolean | {
|
|
@@ -92,7 +92,7 @@ export declare const tenantInvitationEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
92
92
|
readonly sensitive?: boolean;
|
|
93
93
|
readonly writeOnly?: boolean;
|
|
94
94
|
readonly readAsInstruction?: boolean;
|
|
95
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
95
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
96
96
|
readonly default?: string;
|
|
97
97
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
98
98
|
readonly multiline?: boolean | {
|
|
@@ -151,7 +151,7 @@ export declare const tenantInvitationsTable: import("@cosmicdrift/kumiko-framewo
|
|
|
151
151
|
readonly sensitive?: boolean;
|
|
152
152
|
readonly writeOnly?: boolean;
|
|
153
153
|
readonly readAsInstruction?: boolean;
|
|
154
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
154
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
155
155
|
readonly default?: string;
|
|
156
156
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
157
157
|
readonly multiline?: boolean | {
|
|
@@ -174,7 +174,7 @@ export declare const tenantInvitationsTable: import("@cosmicdrift/kumiko-framewo
|
|
|
174
174
|
readonly sensitive?: boolean;
|
|
175
175
|
readonly writeOnly?: boolean;
|
|
176
176
|
readonly readAsInstruction?: boolean;
|
|
177
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
177
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
178
178
|
readonly default?: string;
|
|
179
179
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
180
180
|
readonly multiline?: boolean | {
|
|
@@ -222,7 +222,7 @@ export declare const tenantInvitationsTable: import("@cosmicdrift/kumiko-framewo
|
|
|
222
222
|
readonly sensitive?: boolean;
|
|
223
223
|
readonly writeOnly?: boolean;
|
|
224
224
|
readonly readAsInstruction?: boolean;
|
|
225
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
225
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
226
226
|
readonly default?: string;
|
|
227
227
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
228
228
|
readonly multiline?: boolean | {
|
|
@@ -13,7 +13,7 @@ export declare const tenantMembershipEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
13
13
|
readonly sensitive?: boolean;
|
|
14
14
|
readonly writeOnly?: boolean;
|
|
15
15
|
readonly readAsInstruction?: boolean;
|
|
16
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
16
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
17
17
|
readonly default?: string;
|
|
18
18
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
19
19
|
readonly multiline?: boolean | {
|
|
@@ -36,7 +36,7 @@ export declare const tenantMembershipEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
36
36
|
readonly sensitive?: boolean;
|
|
37
37
|
readonly writeOnly?: boolean;
|
|
38
38
|
readonly readAsInstruction?: boolean;
|
|
39
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
39
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
40
40
|
readonly default?: string;
|
|
41
41
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
42
42
|
readonly multiline?: boolean | {
|
|
@@ -61,7 +61,7 @@ export declare const tenantMembershipsTable: import("@cosmicdrift/kumiko-framewo
|
|
|
61
61
|
readonly sensitive?: boolean;
|
|
62
62
|
readonly writeOnly?: boolean;
|
|
63
63
|
readonly readAsInstruction?: boolean;
|
|
64
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
64
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
65
65
|
readonly default?: string;
|
|
66
66
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
67
67
|
readonly multiline?: boolean | {
|
|
@@ -84,7 +84,7 @@ export declare const tenantMembershipsTable: import("@cosmicdrift/kumiko-framewo
|
|
|
84
84
|
readonly sensitive?: boolean;
|
|
85
85
|
readonly writeOnly?: boolean;
|
|
86
86
|
readonly readAsInstruction?: boolean;
|
|
87
|
-
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
87
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
|
|
88
88
|
readonly default?: string;
|
|
89
89
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
90
90
|
readonly multiline?: boolean | {
|