@cosmicdrift/kumiko-bundled-features 0.352.0 → 0.353.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/agent-tools/agent-manifest.js +8 -2
- package/dist/agent-tools/manifest-deny-qns.d.ts +3 -0
- package/dist/agent-tools/manifest-deny-qns.js +17 -0
- package/dist/agent-tools/tool-catalog.d.ts +2 -1
- package/dist/agent-tools/tool-catalog.js +4 -1
- package/dist/audit/handlers/list.query.js +13 -8
- package/dist/auth-email-password/handlers/signup-confirm.write.js +2 -3
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -0
- package/dist/auth-mfa/screens.js +2 -2
- package/dist/billing-foundation/checkout-core.d.ts +7 -0
- package/dist/billing-foundation/checkout-core.js +7 -2
- package/dist/billing-foundation/entities.d.ts +8 -0
- package/dist/billing-foundation/handlers/start-plan-checkout.write.js +7 -1
- package/dist/billing-foundation/projection.d.ts +8 -0
- package/dist/cap-counter/entity.d.ts +1 -0
- package/dist/cap-overview/access.js +2 -9
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -2
- package/dist/cap-overview/handlers/tenant-options.query.js +4 -5
- package/dist/compliance-profiles/schema/profile-selection.d.ts +8 -8
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +2 -4
- package/dist/config/handlers/values.query.d.ts +3 -1
- package/dist/config/handlers/values.query.js +7 -2
- package/dist/config/table.d.ts +6 -0
- package/dist/custom-fields/entity.d.ts +4 -0
- package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -0
- package/dist/delivery/public-names.js +1 -1
- package/dist/delivery/tables.d.ts +6 -0
- package/dist/document-ingest-foundation/entity.d.ts +4 -0
- package/dist/files-tenant-data/hooks.js +3 -2
- package/dist/folders/entity.d.ts +8 -0
- package/dist/folders/feature.js +2 -10
- package/dist/form-draft/db/queries/cleanup.d.ts +1 -1
- package/dist/form-draft/db/queries/cleanup.js +4 -3
- package/dist/form-draft/entity.d.ts +2 -0
- package/dist/form-draft/handlers/cleanup.job.d.ts +5 -1
- package/dist/form-draft/handlers/cleanup.job.js +11 -5
- package/dist/inbound-mail-foundation/entities.d.ts +25 -0
- package/dist/inbound-mail-foundation/projection.d.ts +20 -0
- package/dist/jobs/handlers/list.query.d.ts +2 -1
- package/dist/jobs/handlers/list.query.js +39 -14
- package/dist/jobs/i18n.js +1 -0
- package/dist/jobs/job-run-table.d.ts +6 -0
- package/dist/ledger/entity.d.ts +21 -8
- package/dist/ledger/entity.js +4 -0
- package/dist/ledger/schemas.js +8 -2
- package/dist/managed-pages/feature.js +28 -10
- package/dist/managed-pages/table.d.ts +12 -0
- package/dist/notes-history/entity.d.ts +12 -0
- package/dist/notes-history/feature.js +2 -10
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -0
- package/dist/personal-access-tokens/screens.js +14 -3
- package/dist/secrets/table.d.ts +1 -0
- package/dist/seo/feature.js +28 -6
- package/dist/sessions/schema/user-session.d.ts +4 -0
- package/dist/shared/assert-no-where-rule-in-ownership-write.d.ts +2 -0
- package/dist/shared/assert-no-where-rule-in-ownership-write.js +18 -0
- package/dist/shared/index.d.ts +2 -1
- package/dist/shared/index.js +2 -1
- package/dist/shared/rate-limited-text-response.d.ts +2 -0
- package/dist/shared/rate-limited-text-response.js +9 -0
- package/dist/tags/entity.d.ts +9 -0
- package/dist/tags/feature.js +2 -10
- package/dist/template-resolver/table.d.ts +40 -30
- package/dist/template-resolver/user-content-table.d.ts +30 -20
- package/dist/tenant/decrypt-tenant-names.d.ts +3 -0
- package/dist/tenant/decrypt-tenant-names.js +10 -0
- package/dist/tenant/handlers/me.query.js +5 -1
- package/dist/tenant/handlers/memberships.query.js +3 -2
- package/dist/tenant/handlers/tenant-directory.query.js +9 -3
- package/dist/tenant/index.d.ts +1 -0
- package/dist/tenant/index.js +1 -0
- package/dist/tenant/invitation-table.d.ts +14 -8
- package/dist/tenant/membership-table.d.ts +4 -0
- package/dist/tenant/schema/tenant.d.ts +16 -10
- package/dist/tenant/schema/tenant.js +4 -6
- package/dist/tenant/screens.js +1 -1
- package/dist/tenant-handover/move-entity-graph.js +11 -18
- package/dist/tenant-lifecycle/feature.js +1 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +3 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -0
- package/dist/tenant-lifecycle/stages.d.ts +2 -0
- package/dist/tenant-lifecycle/stages.js +7 -1
- package/dist/tier-engine/entity.d.ts +2 -0
- package/dist/tier-engine/tier-resolver.d.ts +2 -0
- package/dist/user/handlers/list.query.js +2 -2
- package/dist/user/schema/user.d.ts +20 -8
- package/dist/user-data-rights/boot-checks.js +5 -0
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +2 -2
- 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 +14 -8
- package/dist/waitlist/entity.d.ts +20 -6
- package/dist/workflow-runner/event-trigger.js +6 -3
- package/dist/workflow-runner/handlers/resume-run.write.js +3 -0
- package/package.json +9 -9
- package/src/agent-tools/changes.json +12 -0
- package/src/audit/changes.json +6 -0
- package/src/form-draft/changes.json +7 -1
- package/src/managed-pages/changes.json +6 -0
- package/src/seo/changes.json +5 -0
- package/src/tenant/changes.json +7 -0
- package/src/tenant-handover/changes.json +6 -0
- package/src/workflow-runner/changes.json +5 -0
- package/dist/shared/has-where-rule.d.ts +0 -2
- package/dist/shared/has-where-rule.js +0 -10
|
@@ -5,6 +5,7 @@ export declare const tenantEntity: import("@cosmicdrift/kumiko-types/fields").En
|
|
|
5
5
|
readonly type: "text";
|
|
6
6
|
readonly description?: string;
|
|
7
7
|
readonly maxLength?: number;
|
|
8
|
+
readonly minLength?: number;
|
|
8
9
|
readonly required?: boolean;
|
|
9
10
|
readonly searchable?: boolean;
|
|
10
11
|
readonly sortable?: boolean;
|
|
@@ -26,6 +27,7 @@ export declare const tenantEntity: import("@cosmicdrift/kumiko-types/fields").En
|
|
|
26
27
|
readonly type: "text";
|
|
27
28
|
readonly description?: string;
|
|
28
29
|
readonly maxLength?: number;
|
|
30
|
+
readonly minLength?: number;
|
|
29
31
|
readonly required?: boolean;
|
|
30
32
|
readonly searchable?: boolean;
|
|
31
33
|
readonly sortable?: boolean;
|
|
@@ -58,11 +60,11 @@ export declare const tenantEntity: import("@cosmicdrift/kumiko-types/fields").En
|
|
|
58
60
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
59
61
|
readonly display?: "radio" | "dropdown";
|
|
60
62
|
readonly optionTones?: {
|
|
61
|
-
readonly active?:
|
|
62
|
-
readonly destroyFailed?:
|
|
63
|
-
readonly destroyRequested?:
|
|
64
|
-
readonly destroyed?:
|
|
65
|
-
readonly destroying?:
|
|
63
|
+
readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
64
|
+
readonly destroyFailed?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
65
|
+
readonly destroyRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
66
|
+
readonly destroyed?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
67
|
+
readonly destroying?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
66
68
|
} | undefined;
|
|
67
69
|
readonly optionsQuery?: string;
|
|
68
70
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
@@ -89,6 +91,7 @@ export declare const tenantEntity: import("@cosmicdrift/kumiko-types/fields").En
|
|
|
89
91
|
readonly type: "text";
|
|
90
92
|
readonly description?: string;
|
|
91
93
|
readonly maxLength?: number;
|
|
94
|
+
readonly minLength?: number;
|
|
92
95
|
readonly required?: boolean;
|
|
93
96
|
readonly searchable?: boolean;
|
|
94
97
|
readonly sortable?: boolean;
|
|
@@ -157,6 +160,7 @@ export declare const tenantTable: import("@cosmicdrift/kumiko-framework/db").Ent
|
|
|
157
160
|
readonly type: "text";
|
|
158
161
|
readonly description?: string;
|
|
159
162
|
readonly maxLength?: number;
|
|
163
|
+
readonly minLength?: number;
|
|
160
164
|
readonly required?: boolean;
|
|
161
165
|
readonly searchable?: boolean;
|
|
162
166
|
readonly sortable?: boolean;
|
|
@@ -178,6 +182,7 @@ export declare const tenantTable: import("@cosmicdrift/kumiko-framework/db").Ent
|
|
|
178
182
|
readonly type: "text";
|
|
179
183
|
readonly description?: string;
|
|
180
184
|
readonly maxLength?: number;
|
|
185
|
+
readonly minLength?: number;
|
|
181
186
|
readonly required?: boolean;
|
|
182
187
|
readonly searchable?: boolean;
|
|
183
188
|
readonly sortable?: boolean;
|
|
@@ -210,11 +215,11 @@ export declare const tenantTable: import("@cosmicdrift/kumiko-framework/db").Ent
|
|
|
210
215
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
211
216
|
readonly display?: "radio" | "dropdown";
|
|
212
217
|
readonly optionTones?: {
|
|
213
|
-
readonly active?:
|
|
214
|
-
readonly destroyFailed?:
|
|
215
|
-
readonly destroyRequested?:
|
|
216
|
-
readonly destroyed?:
|
|
217
|
-
readonly destroying?:
|
|
218
|
+
readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
219
|
+
readonly destroyFailed?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
220
|
+
readonly destroyRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
221
|
+
readonly destroyed?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
222
|
+
readonly destroying?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
218
223
|
} | undefined;
|
|
219
224
|
readonly optionsQuery?: string;
|
|
220
225
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
@@ -241,6 +246,7 @@ export declare const tenantTable: import("@cosmicdrift/kumiko-framework/db").Ent
|
|
|
241
246
|
readonly type: "text";
|
|
242
247
|
readonly description?: string;
|
|
243
248
|
readonly maxLength?: number;
|
|
249
|
+
readonly minLength?: number;
|
|
244
250
|
readonly required?: boolean;
|
|
245
251
|
readonly searchable?: boolean;
|
|
246
252
|
readonly sortable?: boolean;
|
|
@@ -17,19 +17,17 @@ export const tenantEntity = createEntity({
|
|
|
17
17
|
key: createTextField({
|
|
18
18
|
required: true,
|
|
19
19
|
maxLength: 50,
|
|
20
|
+
sortable: true,
|
|
20
21
|
personal: false,
|
|
21
22
|
reason: "technical_reference",
|
|
22
23
|
}),
|
|
23
|
-
//
|
|
24
|
-
// same class as the `company.legalName` example in the boot-validator's
|
|
25
|
-
// own PII-heuristic doc-comment.
|
|
24
|
+
// Subject is the tenant record itself: tenant events live in the writer's (system) tenant stream, so `personal: "tenant"` would not resolve to this tenant.
|
|
26
25
|
name: createTextField({
|
|
27
26
|
required: true,
|
|
28
27
|
maxLength: 200,
|
|
29
28
|
searchable: true,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
reason: "is_business_data",
|
|
29
|
+
personal: { of: "id" },
|
|
30
|
+
find: "none",
|
|
33
31
|
}),
|
|
34
32
|
isEnabled: createBooleanField({ default: true, filterable: true }),
|
|
35
33
|
// Tenant-destroy lifecycle (tenant-lifecycle feature). Defaults keep
|
package/dist/tenant/screens.js
CHANGED
|
@@ -23,7 +23,7 @@ export const tenantListScreen = {
|
|
|
23
23
|
entityId: "id",
|
|
24
24
|
},
|
|
25
25
|
],
|
|
26
|
-
defaultSort: { field: "
|
|
26
|
+
defaultSort: { field: "key", dir: "asc" },
|
|
27
27
|
searchable: true,
|
|
28
28
|
access: { roles: ["SystemAdmin"] },
|
|
29
29
|
// Inert until an app navs it (see file header) — no nav area to resolve
|
|
@@ -174,7 +174,7 @@ export async function moveTransferGraph(args) {
|
|
|
174
174
|
let pending = [
|
|
175
175
|
{ entityName: rootEntityName, rowIds: [rootRowId] },
|
|
176
176
|
];
|
|
177
|
-
for (let round = 0; round
|
|
177
|
+
for (let round = 0; round <= MAX_TRANSFER_DEPTH && pending.length > 0; round++) {
|
|
178
178
|
// Merged per type so an edge leaving a type runs once per round, not once
|
|
179
179
|
// per edge that reached it.
|
|
180
180
|
const discovered = new Map();
|
|
@@ -231,24 +231,17 @@ export async function moveTransferGraph(args) {
|
|
|
231
231
|
}));
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
+
// The extra round past the limit is a probe: it only exists to find out
|
|
235
|
+
// whether rows still hang below the last allowed hop. Its moves are rolled
|
|
236
|
+
// back with the transaction when this throws.
|
|
237
|
+
const [strandedEntityName] = discovered.keys();
|
|
238
|
+
if (round === MAX_TRANSFER_DEPTH && strandedEntityName !== undefined) {
|
|
239
|
+
throw new UnprocessableError("transfer_graph_too_deep", {
|
|
240
|
+
i18nKey: "errors.tenantHandover.transferGraphTooDeep",
|
|
241
|
+
details: { entityName: strandedEntityName },
|
|
242
|
+
});
|
|
243
|
+
}
|
|
234
244
|
pending = [...discovered].map(([entityName, rowIds]) => ({ entityName, rowIds }));
|
|
235
245
|
}
|
|
236
|
-
// The depth limit is a safety net, not a licence to move part of a graph: if
|
|
237
|
-
// rounds ran out while rows remain whose type still leads somewhere, the
|
|
238
|
-
// declaration reaches further than the mover does, and returning quietly
|
|
239
|
-
// would leave exactly the orphaned rows #3088 is about.
|
|
240
|
-
//
|
|
241
|
-
// Rounds count hops of both edge kinds, while the boot validator measures
|
|
242
|
-
// reference chains between transferable entities only. A graph that fills the
|
|
243
|
-
// limit with reference hops and then adds a parentRef child therefore boots
|
|
244
|
-
// clean and fails here instead — the residual the validator cannot see, and
|
|
245
|
-
// the reason this check has to exist rather than trusting boot alone.
|
|
246
|
-
const stranded = pending.find(({ entityName }) => (adjacency.get(entityName) ?? []).length > 0);
|
|
247
|
-
if (stranded !== undefined) {
|
|
248
|
-
throw new UnprocessableError("transfer_graph_too_deep", {
|
|
249
|
-
i18nKey: "errors.tenantHandover.transferGraphTooDeep",
|
|
250
|
-
details: { entityName: stranded.entityName },
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
246
|
return movedCounts;
|
|
254
247
|
}
|
|
@@ -85,6 +85,7 @@ export function createTenantLifecycleFeature() {
|
|
|
85
85
|
now,
|
|
86
86
|
log: (message) => ctx.log?.warn(message),
|
|
87
87
|
fileProviderResolver: ctx._fileProviderResolver,
|
|
88
|
+
searchAdapter: ctx.searchAdapter,
|
|
88
89
|
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
89
90
|
actor: ctx.systemUser.id,
|
|
90
91
|
});
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { type EscapeHatchAuditSink, type Registry, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-framework/files";
|
|
5
|
+
import type { SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
5
6
|
export declare function runNextDestructionStage(args: {
|
|
6
7
|
readonly db: DbRunner;
|
|
7
8
|
readonly registry: Registry;
|
|
8
9
|
readonly tenantId: TenantId;
|
|
9
10
|
readonly log?: (message: string) => void;
|
|
10
11
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
12
|
+
readonly searchAdapter?: SearchAdapter;
|
|
11
13
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
12
14
|
readonly actor?: string;
|
|
13
15
|
}): Promise<{
|
|
@@ -23,6 +25,7 @@ export declare function runTenantDestructionSweep(args: {
|
|
|
23
25
|
readonly now?: Temporal.Instant;
|
|
24
26
|
readonly log?: (message: string) => void;
|
|
25
27
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
28
|
+
readonly searchAdapter?: SearchAdapter;
|
|
26
29
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
27
30
|
readonly actor?: string;
|
|
28
31
|
}): Promise<{
|
|
@@ -133,6 +133,7 @@ export async function runNextDestructionStage(args) {
|
|
|
133
133
|
tenantId: args.tenantId,
|
|
134
134
|
log: args.log,
|
|
135
135
|
fileProviderResolver: args.fileProviderResolver,
|
|
136
|
+
searchAdapter: args.searchAdapter,
|
|
136
137
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
137
138
|
actor: args.actor,
|
|
138
139
|
};
|
|
@@ -215,6 +216,7 @@ export async function runTenantDestructionSweep(args) {
|
|
|
215
216
|
tenantId: row.id,
|
|
216
217
|
log: args.log,
|
|
217
218
|
fileProviderResolver: args.fileProviderResolver,
|
|
219
|
+
searchAdapter: args.searchAdapter,
|
|
218
220
|
escapeHatchAuditSink: args.escapeHatchAuditSink,
|
|
219
221
|
actor: args.actor,
|
|
220
222
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import { type EscapeHatchAuditSink, type Registry, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-framework/files";
|
|
4
|
+
import { type SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
4
5
|
import type { TenantDestructionStageName } from "./constants.js";
|
|
5
6
|
export type DestructionStageCtx = {
|
|
6
7
|
readonly db: DbRunner;
|
|
@@ -8,6 +9,7 @@ export type DestructionStageCtx = {
|
|
|
8
9
|
readonly tenantId: TenantId;
|
|
9
10
|
readonly log?: (message: string) => void;
|
|
10
11
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
12
|
+
readonly searchAdapter?: SearchAdapter;
|
|
11
13
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
12
14
|
readonly actor?: string;
|
|
13
15
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import { configuredPiiSubjectKms } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
|
+
import { configuredPiiSubjectKms, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
3
|
import { createEventStoreExecutor, createTenantDb, } from "@cosmicdrift/kumiko-framework/db";
|
|
4
4
|
import { createSystemUser, EXT_EXTERNAL_RESOURCE, EXT_INFRA_RESOURCE, EXT_SEARCH_ADAPTER, EXT_STORAGE_PROVIDER, EXT_TENANT_DATA, extensionUsageEscapeHatchReason, isTenantDataExtensionHooks, isTenantResourceExtensionHooks, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import { createEscapeHatchReporter, UNATTRIBUTED_ACTOR, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
6
|
+
import { purgeSearchDocumentsForSubject, } from "@cosmicdrift/kumiko-framework/search";
|
|
6
7
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
7
8
|
import { tenantEntity, tenantMembershipEntity, tenantMembershipsTable, tenantTable, } from "../tenant/index.js";
|
|
8
9
|
import { invalidateTenantLifecycleGate } from "./lifecycle-gate.js";
|
|
@@ -56,8 +57,10 @@ async function eraseSubjectKeys(ctx) {
|
|
|
56
57
|
const memberships = await selectMany(ctx.db, tenantMembershipsTable, {
|
|
57
58
|
tenantId: ctx.tenantId,
|
|
58
59
|
});
|
|
60
|
+
const tenantRecordSubject = { kind: "record", entity: "tenant", id: ctx.tenantId };
|
|
59
61
|
const subjects = [
|
|
60
62
|
{ kind: "tenant", tenantId: ctx.tenantId },
|
|
63
|
+
tenantRecordSubject,
|
|
61
64
|
...memberships.map((m) => ({ kind: "user", userId: m.userId })),
|
|
62
65
|
];
|
|
63
66
|
for (const subject of subjects) {
|
|
@@ -66,6 +69,9 @@ async function eraseSubjectKeys(ctx) {
|
|
|
66
69
|
eraseReason: "tenant-destroy stage subject-keys",
|
|
67
70
|
});
|
|
68
71
|
}
|
|
72
|
+
if (ctx.searchAdapter) {
|
|
73
|
+
await purgeSearchDocumentsForSubject(ctx.db, ctx.registry.features, ctx.searchAdapter, subjectIdToKey(tenantRecordSubject), tenantRecordSubject);
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
76
|
async function purgeTenantCache(ctx) {
|
|
71
77
|
// ponytail: Redis SCAN+DEL is wired when ctx carries a redis client; until
|
|
@@ -3,6 +3,7 @@ export declare const tierAssignmentEntity: import("@cosmicdrift/kumiko-types/fie
|
|
|
3
3
|
readonly type: "text";
|
|
4
4
|
readonly description?: string;
|
|
5
5
|
readonly maxLength?: number;
|
|
6
|
+
readonly minLength?: number;
|
|
6
7
|
readonly required?: boolean;
|
|
7
8
|
readonly searchable?: boolean;
|
|
8
9
|
readonly sortable?: boolean;
|
|
@@ -24,6 +25,7 @@ export declare const tierAssignmentEntity: import("@cosmicdrift/kumiko-types/fie
|
|
|
24
25
|
readonly type: "text";
|
|
25
26
|
readonly description?: string;
|
|
26
27
|
readonly maxLength?: number;
|
|
28
|
+
readonly minLength?: number;
|
|
27
29
|
readonly required?: boolean;
|
|
28
30
|
readonly searchable?: boolean;
|
|
29
31
|
readonly sortable?: boolean;
|
|
@@ -11,6 +11,7 @@ export declare function createTierResolver<TTier extends string, TCaps>(deps: Ti
|
|
|
11
11
|
readonly type: "text";
|
|
12
12
|
readonly description?: string;
|
|
13
13
|
readonly maxLength?: number;
|
|
14
|
+
readonly minLength?: number;
|
|
14
15
|
readonly required?: boolean;
|
|
15
16
|
readonly searchable?: boolean;
|
|
16
17
|
readonly sortable?: boolean;
|
|
@@ -32,6 +33,7 @@ export declare function createTierResolver<TTier extends string, TCaps>(deps: Ti
|
|
|
32
33
|
readonly type: "text";
|
|
33
34
|
readonly description?: string;
|
|
34
35
|
readonly maxLength?: number;
|
|
36
|
+
readonly minLength?: number;
|
|
35
37
|
readonly required?: boolean;
|
|
36
38
|
readonly searchable?: boolean;
|
|
37
39
|
readonly sortable?: boolean;
|
|
@@ -2,7 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { access, defineEntityListHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
4
4
|
// kumiko-lint-ignore cross-feature-import SystemAdmin user-list joins memberships for tenants column
|
|
5
|
-
import { tenantMembershipsTable, tenantTable } from "../../tenant/index.js";
|
|
5
|
+
import { decryptTenantNames, tenantMembershipsTable, tenantTable } from "../../tenant/index.js";
|
|
6
6
|
import { userEntity } from "../schema/user.js";
|
|
7
7
|
const baseList = defineEntityListHandler("user", userEntity, {
|
|
8
8
|
access: { roles: access.systemAdmin },
|
|
@@ -59,7 +59,7 @@ export async function attachTenantLabels(rows, db) {
|
|
|
59
59
|
...new Set(memberships.map((m) => String(m.tenantId ?? "")).filter((id) => id !== "")),
|
|
60
60
|
];
|
|
61
61
|
const tenants = tenantIds.length > 0
|
|
62
|
-
? await selectMany(db, tenantTable, { id: { in: tenantIds } })
|
|
62
|
+
? await decryptTenantNames(await selectMany(db, tenantTable, { id: { in: tenantIds } }), "user:list")
|
|
63
63
|
: [];
|
|
64
64
|
const byUser = labelsByUserId(memberships, tenantLabelById(tenants));
|
|
65
65
|
return rows.map((row) => {
|
|
@@ -32,6 +32,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
32
32
|
readonly type: "text";
|
|
33
33
|
readonly description?: string;
|
|
34
34
|
readonly maxLength?: number;
|
|
35
|
+
readonly minLength?: number;
|
|
35
36
|
readonly required?: boolean;
|
|
36
37
|
readonly searchable?: boolean;
|
|
37
38
|
readonly sortable?: boolean;
|
|
@@ -53,6 +54,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
53
54
|
readonly type: "text";
|
|
54
55
|
readonly description?: string;
|
|
55
56
|
readonly maxLength?: number;
|
|
57
|
+
readonly minLength?: number;
|
|
56
58
|
readonly required?: boolean;
|
|
57
59
|
readonly searchable?: boolean;
|
|
58
60
|
readonly sortable?: boolean;
|
|
@@ -74,6 +76,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
74
76
|
readonly type: "text";
|
|
75
77
|
readonly description?: string;
|
|
76
78
|
readonly maxLength?: number;
|
|
79
|
+
readonly minLength?: number;
|
|
77
80
|
readonly required?: boolean;
|
|
78
81
|
readonly searchable?: boolean;
|
|
79
82
|
readonly sortable?: boolean;
|
|
@@ -95,6 +98,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
95
98
|
readonly type: "text";
|
|
96
99
|
readonly description?: string;
|
|
97
100
|
readonly maxLength?: number;
|
|
101
|
+
readonly minLength?: number;
|
|
98
102
|
readonly required?: boolean;
|
|
99
103
|
readonly searchable?: boolean;
|
|
100
104
|
readonly sortable?: boolean;
|
|
@@ -125,6 +129,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
125
129
|
readonly type: "text";
|
|
126
130
|
readonly description?: string;
|
|
127
131
|
readonly maxLength?: number;
|
|
132
|
+
readonly minLength?: number;
|
|
128
133
|
readonly required?: boolean;
|
|
129
134
|
readonly searchable?: boolean;
|
|
130
135
|
readonly sortable?: boolean;
|
|
@@ -158,10 +163,10 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
158
163
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
159
164
|
readonly display?: "radio" | "dropdown";
|
|
160
165
|
readonly optionTones?: {
|
|
161
|
-
readonly active?:
|
|
162
|
-
readonly deleted?:
|
|
163
|
-
readonly deletionRequested?:
|
|
164
|
-
readonly restricted?:
|
|
166
|
+
readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
167
|
+
readonly deleted?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
168
|
+
readonly deletionRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
169
|
+
readonly restricted?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
165
170
|
} | undefined;
|
|
166
171
|
readonly optionsQuery?: string;
|
|
167
172
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
@@ -188,6 +193,7 @@ export declare const userEntity: import("@cosmicdrift/kumiko-types/fields").Enti
|
|
|
188
193
|
readonly type: "text";
|
|
189
194
|
readonly description?: string;
|
|
190
195
|
readonly maxLength?: number;
|
|
196
|
+
readonly minLength?: number;
|
|
191
197
|
readonly required?: boolean;
|
|
192
198
|
readonly searchable?: boolean;
|
|
193
199
|
readonly sortable?: boolean;
|
|
@@ -211,6 +217,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
211
217
|
readonly type: "text";
|
|
212
218
|
readonly description?: string;
|
|
213
219
|
readonly maxLength?: number;
|
|
220
|
+
readonly minLength?: number;
|
|
214
221
|
readonly required?: boolean;
|
|
215
222
|
readonly searchable?: boolean;
|
|
216
223
|
readonly sortable?: boolean;
|
|
@@ -232,6 +239,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
232
239
|
readonly type: "text";
|
|
233
240
|
readonly description?: string;
|
|
234
241
|
readonly maxLength?: number;
|
|
242
|
+
readonly minLength?: number;
|
|
235
243
|
readonly required?: boolean;
|
|
236
244
|
readonly searchable?: boolean;
|
|
237
245
|
readonly sortable?: boolean;
|
|
@@ -253,6 +261,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
253
261
|
readonly type: "text";
|
|
254
262
|
readonly description?: string;
|
|
255
263
|
readonly maxLength?: number;
|
|
264
|
+
readonly minLength?: number;
|
|
256
265
|
readonly required?: boolean;
|
|
257
266
|
readonly searchable?: boolean;
|
|
258
267
|
readonly sortable?: boolean;
|
|
@@ -274,6 +283,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
274
283
|
readonly type: "text";
|
|
275
284
|
readonly description?: string;
|
|
276
285
|
readonly maxLength?: number;
|
|
286
|
+
readonly minLength?: number;
|
|
277
287
|
readonly required?: boolean;
|
|
278
288
|
readonly searchable?: boolean;
|
|
279
289
|
readonly sortable?: boolean;
|
|
@@ -304,6 +314,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
304
314
|
readonly type: "text";
|
|
305
315
|
readonly description?: string;
|
|
306
316
|
readonly maxLength?: number;
|
|
317
|
+
readonly minLength?: number;
|
|
307
318
|
readonly required?: boolean;
|
|
308
319
|
readonly searchable?: boolean;
|
|
309
320
|
readonly sortable?: boolean;
|
|
@@ -337,10 +348,10 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
337
348
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
338
349
|
readonly display?: "radio" | "dropdown";
|
|
339
350
|
readonly optionTones?: {
|
|
340
|
-
readonly active?:
|
|
341
|
-
readonly deleted?:
|
|
342
|
-
readonly deletionRequested?:
|
|
343
|
-
readonly restricted?:
|
|
351
|
+
readonly active?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
352
|
+
readonly deleted?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
353
|
+
readonly deletionRequested?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
354
|
+
readonly restricted?: "bad" | "neutral" | "ok" | "warn" | undefined;
|
|
344
355
|
} | undefined;
|
|
345
356
|
readonly optionsQuery?: string;
|
|
346
357
|
readonly optionsQueryPayload?: import("@cosmicdrift/kumiko-types/fields").OptionsQueryPayload;
|
|
@@ -367,6 +378,7 @@ export declare const userTable: import("@cosmicdrift/kumiko-framework/db").Entit
|
|
|
367
378
|
readonly type: "text";
|
|
368
379
|
readonly description?: string;
|
|
369
380
|
readonly maxLength?: number;
|
|
381
|
+
readonly minLength?: number;
|
|
370
382
|
readonly required?: boolean;
|
|
371
383
|
readonly searchable?: boolean;
|
|
372
384
|
readonly sortable?: boolean;
|
|
@@ -20,6 +20,11 @@ export function validateGdprHookCompleteness(features) {
|
|
|
20
20
|
continue;
|
|
21
21
|
const hasExport = typeof usage.options?.["export"] === "function";
|
|
22
22
|
const hasDelete = typeof usage.options?.["delete"] === "function";
|
|
23
|
+
// The compile-time "at least one hook" type is bypassed by string-typed
|
|
24
|
+
// extension names and casts; without this an empty bag registers nothing.
|
|
25
|
+
if (!hasExport && !hasDelete) {
|
|
26
|
+
throw new Error(`[kumiko:boot] Feature "${feature.name}" registers EXT_USER_DATA for entity "${usage.entityName}" with neither an export nor a delete hook — the entity silently stays out of the Art.15/17/20 pipeline. Register at least one hook.`);
|
|
27
|
+
}
|
|
23
28
|
if (hasExport && !hasDelete) {
|
|
24
29
|
throw new Error(`[kumiko:boot] Feature "${feature.name}" exports entity "${usage.entityName}" via EXT_USER_DATA but registers no delete hook — data is included in Art.20 exports but never erased on forget (Art.17 violation). Add a delete hook. If erasure is intentionally handled elsewhere (e.g. crypto-shredding key-erase, parent cascade), register a no-op delete: async () => {} with a comment explaining why.`);
|
|
25
30
|
}
|
|
@@ -28,7 +28,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
28
28
|
}
|
|
29
29
|
// Anonymous apex flow step 2: verify-link target. Verifies the HMAC token,
|
|
30
30
|
// extracts the userId, and flips the grace period through the shared logic —
|
|
31
|
-
// in ONE atomic step with the anchor spend
|
|
31
|
+
// in ONE atomic step with the anchor spend: commitDeletion carries
|
|
32
32
|
// the actual grace-period transition, so a crash after the spend can no
|
|
33
33
|
// longer lose the write step.
|
|
34
34
|
//
|
|
@@ -38,7 +38,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
38
38
|
// a cancel-deletion (status → Active, pendingDeletionRequestId → null), a
|
|
39
39
|
// replayed token fails against the nulled/renewed requestId — no re-arm.
|
|
40
40
|
//
|
|
41
|
-
// Concurrency
|
|
41
|
+
// Concurrency: two simultaneous confirms of the same token both read
|
|
42
42
|
// the same requestId and both verify the HMAC — commitDeletion spends the
|
|
43
43
|
// anchor AND writes the transition atomically (expect: status===Active &&
|
|
44
44
|
// pendingDeletionRequestId===requestId), so exactly one wins. The loser gets
|
|
@@ -3,6 +3,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
3
3
|
readonly type: "text";
|
|
4
4
|
readonly description?: string;
|
|
5
5
|
readonly maxLength?: number;
|
|
6
|
+
readonly minLength?: number;
|
|
6
7
|
readonly required?: boolean;
|
|
7
8
|
readonly searchable?: boolean;
|
|
8
9
|
readonly sortable?: boolean;
|
|
@@ -24,6 +25,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
24
25
|
readonly type: "text";
|
|
25
26
|
readonly description?: string;
|
|
26
27
|
readonly maxLength?: number;
|
|
28
|
+
readonly minLength?: number;
|
|
27
29
|
readonly required?: boolean;
|
|
28
30
|
readonly searchable?: boolean;
|
|
29
31
|
readonly sortable?: boolean;
|
|
@@ -45,6 +47,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
45
47
|
readonly type: "text";
|
|
46
48
|
readonly description?: string;
|
|
47
49
|
readonly maxLength?: number;
|
|
50
|
+
readonly minLength?: number;
|
|
48
51
|
readonly required?: boolean;
|
|
49
52
|
readonly searchable?: boolean;
|
|
50
53
|
readonly sortable?: boolean;
|
|
@@ -66,6 +69,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
66
69
|
readonly type: "text";
|
|
67
70
|
readonly description?: string;
|
|
68
71
|
readonly maxLength?: number;
|
|
72
|
+
readonly minLength?: number;
|
|
69
73
|
readonly required?: boolean;
|
|
70
74
|
readonly searchable?: boolean;
|
|
71
75
|
readonly sortable?: boolean;
|
|
@@ -87,6 +91,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
87
91
|
readonly type: "text";
|
|
88
92
|
readonly description?: string;
|
|
89
93
|
readonly maxLength?: number;
|
|
94
|
+
readonly minLength?: number;
|
|
90
95
|
readonly required?: boolean;
|
|
91
96
|
readonly searchable?: boolean;
|
|
92
97
|
readonly sortable?: boolean;
|
|
@@ -108,6 +113,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
108
113
|
readonly type: "text";
|
|
109
114
|
readonly description?: string;
|
|
110
115
|
readonly maxLength?: number;
|
|
116
|
+
readonly minLength?: number;
|
|
111
117
|
readonly required?: boolean;
|
|
112
118
|
readonly searchable?: boolean;
|
|
113
119
|
readonly sortable?: boolean;
|
|
@@ -129,6 +135,7 @@ export declare const downloadAttemptEntity: import("@cosmicdrift/kumiko-types/fi
|
|
|
129
135
|
readonly type: "text";
|
|
130
136
|
readonly description?: string;
|
|
131
137
|
readonly maxLength?: number;
|
|
138
|
+
readonly minLength?: number;
|
|
132
139
|
readonly required?: boolean;
|
|
133
140
|
readonly searchable?: boolean;
|
|
134
141
|
readonly sortable?: boolean;
|
|
@@ -167,6 +174,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
167
174
|
readonly type: "text";
|
|
168
175
|
readonly description?: string;
|
|
169
176
|
readonly maxLength?: number;
|
|
177
|
+
readonly minLength?: number;
|
|
170
178
|
readonly required?: boolean;
|
|
171
179
|
readonly searchable?: boolean;
|
|
172
180
|
readonly sortable?: boolean;
|
|
@@ -188,6 +196,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
188
196
|
readonly type: "text";
|
|
189
197
|
readonly description?: string;
|
|
190
198
|
readonly maxLength?: number;
|
|
199
|
+
readonly minLength?: number;
|
|
191
200
|
readonly required?: boolean;
|
|
192
201
|
readonly searchable?: boolean;
|
|
193
202
|
readonly sortable?: boolean;
|
|
@@ -209,6 +218,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
209
218
|
readonly type: "text";
|
|
210
219
|
readonly description?: string;
|
|
211
220
|
readonly maxLength?: number;
|
|
221
|
+
readonly minLength?: number;
|
|
212
222
|
readonly required?: boolean;
|
|
213
223
|
readonly searchable?: boolean;
|
|
214
224
|
readonly sortable?: boolean;
|
|
@@ -230,6 +240,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
230
240
|
readonly type: "text";
|
|
231
241
|
readonly description?: string;
|
|
232
242
|
readonly maxLength?: number;
|
|
243
|
+
readonly minLength?: number;
|
|
233
244
|
readonly required?: boolean;
|
|
234
245
|
readonly searchable?: boolean;
|
|
235
246
|
readonly sortable?: boolean;
|
|
@@ -251,6 +262,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
251
262
|
readonly type: "text";
|
|
252
263
|
readonly description?: string;
|
|
253
264
|
readonly maxLength?: number;
|
|
265
|
+
readonly minLength?: number;
|
|
254
266
|
readonly required?: boolean;
|
|
255
267
|
readonly searchable?: boolean;
|
|
256
268
|
readonly sortable?: boolean;
|
|
@@ -272,6 +284,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
272
284
|
readonly type: "text";
|
|
273
285
|
readonly description?: string;
|
|
274
286
|
readonly maxLength?: number;
|
|
287
|
+
readonly minLength?: number;
|
|
275
288
|
readonly required?: boolean;
|
|
276
289
|
readonly searchable?: boolean;
|
|
277
290
|
readonly sortable?: boolean;
|
|
@@ -293,6 +306,7 @@ export declare const downloadAttemptsTable: import("@cosmicdrift/kumiko-framewor
|
|
|
293
306
|
readonly type: "text";
|
|
294
307
|
readonly description?: string;
|
|
295
308
|
readonly maxLength?: number;
|
|
309
|
+
readonly minLength?: number;
|
|
296
310
|
readonly required?: boolean;
|
|
297
311
|
readonly searchable?: boolean;
|
|
298
312
|
readonly sortable?: boolean;
|
|
@@ -3,6 +3,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
|
|
|
3
3
|
readonly type: "text";
|
|
4
4
|
readonly description?: string;
|
|
5
5
|
readonly maxLength?: number;
|
|
6
|
+
readonly minLength?: number;
|
|
6
7
|
readonly required?: boolean;
|
|
7
8
|
readonly searchable?: boolean;
|
|
8
9
|
readonly sortable?: boolean;
|
|
@@ -24,6 +25,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
|
|
|
24
25
|
readonly type: "text";
|
|
25
26
|
readonly description?: string;
|
|
26
27
|
readonly maxLength?: number;
|
|
28
|
+
readonly minLength?: number;
|
|
27
29
|
readonly required?: boolean;
|
|
28
30
|
readonly searchable?: boolean;
|
|
29
31
|
readonly sortable?: boolean;
|
|
@@ -102,6 +104,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
|
|
|
102
104
|
readonly type: "text";
|
|
103
105
|
readonly description?: string;
|
|
104
106
|
readonly maxLength?: number;
|
|
107
|
+
readonly minLength?: number;
|
|
105
108
|
readonly required?: boolean;
|
|
106
109
|
readonly searchable?: boolean;
|
|
107
110
|
readonly sortable?: boolean;
|
|
@@ -123,6 +126,7 @@ export declare const exportDownloadTokenEntity: import("@cosmicdrift/kumiko-type
|
|
|
123
126
|
readonly type: "text";
|
|
124
127
|
readonly description?: string;
|
|
125
128
|
readonly maxLength?: number;
|
|
129
|
+
readonly minLength?: number;
|
|
126
130
|
readonly required?: boolean;
|
|
127
131
|
readonly searchable?: boolean;
|
|
128
132
|
readonly sortable?: boolean;
|
|
@@ -146,6 +150,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
|
|
|
146
150
|
readonly type: "text";
|
|
147
151
|
readonly description?: string;
|
|
148
152
|
readonly maxLength?: number;
|
|
153
|
+
readonly minLength?: number;
|
|
149
154
|
readonly required?: boolean;
|
|
150
155
|
readonly searchable?: boolean;
|
|
151
156
|
readonly sortable?: boolean;
|
|
@@ -167,6 +172,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
|
|
|
167
172
|
readonly type: "text";
|
|
168
173
|
readonly description?: string;
|
|
169
174
|
readonly maxLength?: number;
|
|
175
|
+
readonly minLength?: number;
|
|
170
176
|
readonly required?: boolean;
|
|
171
177
|
readonly searchable?: boolean;
|
|
172
178
|
readonly sortable?: boolean;
|
|
@@ -245,6 +251,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
|
|
|
245
251
|
readonly type: "text";
|
|
246
252
|
readonly description?: string;
|
|
247
253
|
readonly maxLength?: number;
|
|
254
|
+
readonly minLength?: number;
|
|
248
255
|
readonly required?: boolean;
|
|
249
256
|
readonly searchable?: boolean;
|
|
250
257
|
readonly sortable?: boolean;
|
|
@@ -266,6 +273,7 @@ export declare const exportDownloadTokensTable: import("@cosmicdrift/kumiko-fram
|
|
|
266
273
|
readonly type: "text";
|
|
267
274
|
readonly description?: string;
|
|
268
275
|
readonly maxLength?: number;
|
|
276
|
+
readonly minLength?: number;
|
|
269
277
|
readonly required?: boolean;
|
|
270
278
|
readonly searchable?: boolean;
|
|
271
279
|
readonly sortable?: boolean;
|