@cosmicdrift/kumiko-types 0.387.0 → 0.389.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/config.d.ts +3 -0
- package/dist/db-connection.d.ts +4 -4
- package/dist/define-handler.d.ts +6 -0
- package/dist/event-store-executor-types.d.ts +37 -21
- package/dist/feature-registrar.d.ts +326 -0
- package/dist/feature-registrar.js +1 -0
- package/dist/feature.d.ts +6 -314
- package/dist/fields.js +1 -1
- package/dist/handlers.d.ts +8 -6
- package/dist/list-aggregate-types.d.ts +43 -0
- package/dist/list-aggregate-types.js +22 -0
- package/dist/screen-entity-edit.d.ts +8 -20
- package/dist/screen-entity-edit.js +6 -4
- package/dist/screen-entity-list.d.ts +21 -8
- package/dist/screen-expandable-row.d.ts +16 -0
- package/dist/screen-expandable-row.js +1 -0
- package/dist/screen-list-bulk.d.ts +12 -0
- package/dist/screen-list-bulk.js +3 -0
- package/dist/screen-list-table.d.ts +70 -0
- package/dist/screen-list-table.js +28 -0
- package/dist/screen-projection.d.ts +6 -0
- package/dist/screen.d.ts +12 -4
- package/dist/screen.js +2 -0
- package/dist/tenant-db-types.d.ts +5 -0
- package/package.json +20 -4
- package/src/changes.json +13 -0
package/dist/feature.d.ts
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import type * as z from "zod";
|
|
2
2
|
import type { ZodType } from "zod";
|
|
3
|
-
import type { ConfigKeyDefinition,
|
|
4
|
-
import type { QueryHandlerDefinition, StreamHandlerDefinition, WriteHandlerDefinition } from "./define-handler.ts";
|
|
5
|
-
import type { RegisterEntityCrudOptions } from "./entity-handlers.ts";
|
|
3
|
+
import type { ConfigKeyDefinition, ConfigSeedDef, ExtensionSelectorDef, ExtensionSelectorPanel, JobDefinition, NotificationDefinition, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionRegistration, TranslationKeys } from "./config.ts";
|
|
6
4
|
import type { EntityTableMeta } from "./entity-table-meta-types.ts";
|
|
7
|
-
import type { KumikoEventTypeMap } from "./event-type-map.ts";
|
|
8
|
-
import type { ExtensionOptionsArgs, ExtensionOptionsFor } from "./extension-options-map.ts";
|
|
9
5
|
import type { EntityDefinition } from "./fields.ts";
|
|
10
|
-
import type {
|
|
11
|
-
import type { EntityHookMap, HookMap, HookPhase, OwnedFn, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, SearchPayloadContributorFn
|
|
6
|
+
import type { AuthClaimsFn, AuthClaimsHookDef, ClaimKeyDefinition, EventDef, EventMigrationDef, EventUpcastFn, QueryHandlerDef, StreamHandlerDef, WriteHandlerDef } from "./handlers.ts";
|
|
7
|
+
import type { EntityHookMap, HookMap, HookPhase, OwnedFn, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, SearchPayloadContributorFn } from "./hooks.ts";
|
|
12
8
|
import type { HttpRouteDefinition } from "./http-route.ts";
|
|
13
9
|
import type { ContentCollectionDefinition, NavDefinition } from "./nav.ts";
|
|
14
10
|
import type { EntityProjectionExtension, MultiStreamProjectionDefinition, ProjectionDefinition } from "./projection.ts";
|
|
15
11
|
import type { EntityRelations, RelationDefinition } from "./relations.ts";
|
|
16
12
|
import type { ScreenDefinition } from "./screen.ts";
|
|
17
|
-
import type { TreeActionDef
|
|
13
|
+
import type { TreeActionDef } from "./tree-node.ts";
|
|
18
14
|
import type { WebSocketRouteDefinition } from "./websocket-route.ts";
|
|
19
15
|
import type { WorkspaceDefinition } from "./workspace.ts";
|
|
20
16
|
export type FeatureMetricType = "counter" | "histogram" | "gauge";
|
|
@@ -116,7 +112,7 @@ export type BootCheckContext = {
|
|
|
116
112
|
};
|
|
117
113
|
export type BootCheckFn = (ctx: BootCheckContext) => void;
|
|
118
114
|
/** runProdApp options a stack can depend on; runProdApp checks the set at boot. */
|
|
119
|
-
export type BootOptionName = "mail" | "kmsMode" | "fileStorage" | "billing" | "featureToggles" | "
|
|
115
|
+
export type BootOptionName = "mail" | "kmsMode" | "fileStorage" | "billing" | "featureToggles" | "rateLimit";
|
|
120
116
|
export type StackBootRequirement = {
|
|
121
117
|
readonly stack: string;
|
|
122
118
|
readonly options: readonly BootOptionName[];
|
|
@@ -133,6 +129,7 @@ export type FeatureDefinition = {
|
|
|
133
129
|
readonly requiredProjections: ReadonlySet<string>;
|
|
134
130
|
readonly requiredSteps: ReadonlySet<string>;
|
|
135
131
|
readonly toggleableDefault?: boolean;
|
|
132
|
+
readonly adoptsBundledLists?: true;
|
|
136
133
|
readonly uiHints?: UiHints;
|
|
137
134
|
readonly entities?: Readonly<Record<string, EntityDefinition>>;
|
|
138
135
|
readonly entityTables?: Readonly<Record<string, unknown>>;
|
|
@@ -198,310 +195,6 @@ export type FeatureDefinition = {
|
|
|
198
195
|
readonly envSchema?: z.ZodObject<z.ZodRawShape>;
|
|
199
196
|
readonly dedupeOptions?: Readonly<Record<string, unknown>>;
|
|
200
197
|
};
|
|
201
|
-
type RefOrRefs = NameOrRef | readonly NameOrRef[];
|
|
202
|
-
type HookTarget = RefOrRefs | {
|
|
203
|
-
readonly allOf: NameOrRef;
|
|
204
|
-
};
|
|
205
|
-
export type EntityObjectDefinition = {
|
|
206
|
-
readonly name: string;
|
|
207
|
-
readonly backingTable?: unknown;
|
|
208
|
-
} & EntityDefinition;
|
|
209
|
-
type HookEscapeHatch = {
|
|
210
|
-
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
211
|
-
};
|
|
212
|
-
type HookPhaseOption = {
|
|
213
|
-
readonly phase?: HookPhase;
|
|
214
|
-
};
|
|
215
|
-
export type HookDefinition = {
|
|
216
|
-
readonly type: "validation";
|
|
217
|
-
readonly target: RefOrRefs;
|
|
218
|
-
readonly handler: ValidationHookFn;
|
|
219
|
-
readonly escapeHatch?: never;
|
|
220
|
-
readonly phase?: never;
|
|
221
|
-
} | ({
|
|
222
|
-
readonly type: "preSave";
|
|
223
|
-
readonly target: RefOrRefs;
|
|
224
|
-
readonly handler: PreSaveHookFn;
|
|
225
|
-
readonly phase?: never;
|
|
226
|
-
} & HookEscapeHatch) | ({
|
|
227
|
-
readonly type: "postSave";
|
|
228
|
-
readonly target: HookTarget;
|
|
229
|
-
readonly handler: PostSaveHookFn;
|
|
230
|
-
} & HookEscapeHatch & HookPhaseOption) | ({
|
|
231
|
-
readonly type: "preDelete";
|
|
232
|
-
readonly target: HookTarget;
|
|
233
|
-
readonly handler: PreDeleteHookFn;
|
|
234
|
-
readonly phase?: never;
|
|
235
|
-
} & HookEscapeHatch) | ({
|
|
236
|
-
readonly type: "postDelete";
|
|
237
|
-
readonly target: HookTarget;
|
|
238
|
-
readonly handler: PostDeleteHookFn;
|
|
239
|
-
} & HookEscapeHatch & HookPhaseOption) | ({
|
|
240
|
-
readonly type: "preQuery";
|
|
241
|
-
readonly target: RefOrRefs;
|
|
242
|
-
readonly handler: PreQueryHookFn;
|
|
243
|
-
readonly phase?: never;
|
|
244
|
-
} & HookEscapeHatch) | ({
|
|
245
|
-
readonly type: "postQuery";
|
|
246
|
-
readonly target: HookTarget;
|
|
247
|
-
readonly handler: PostQueryHookFn;
|
|
248
|
-
readonly phase?: never;
|
|
249
|
-
} & HookEscapeHatch);
|
|
250
|
-
/**
|
|
251
|
-
* `r.requires({ features })` declares the features this one depends on; the
|
|
252
|
-
* `.projection` extension declares read-side projection tables that this
|
|
253
|
-
* feature's pipeline steps are allowed to write via
|
|
254
|
-
* `r.step.unsafeProjectionUpsert` (hard-required for any unsafeProjection-*
|
|
255
|
-
* step usage, see Q10).
|
|
256
|
-
*
|
|
257
|
-
* @example r.requires({ features: ["auth", "tenant"] });
|
|
258
|
-
*/
|
|
259
|
-
export type RequiresApi = ((options: {
|
|
260
|
-
readonly features: readonly string[];
|
|
261
|
-
}) => void) & {
|
|
262
|
-
readonly projection: (tableName: string) => void;
|
|
263
|
-
readonly step: (stepKind: string) => void;
|
|
264
|
-
};
|
|
265
|
-
/**
|
|
266
|
-
* `TFeature` is the literal feature-name from `defineFeature("foo", ...)` —
|
|
267
|
-
* default-`string` keeps every existing usage zero-config. Strict-typed
|
|
268
|
-
* features (apps that opt into the literal-name flavour) get propagated
|
|
269
|
-
* through to `defineEvent` so the returned `EventDef.name` is a literal
|
|
270
|
-
* `${CamelToKebab<TFeature>}:event:${CamelToKebab<TInner>}`. That literal
|
|
271
|
-
* threads through `ctx.appendEvent({ type: eventDef.name, ... })`,
|
|
272
|
-
* keeping strict-mode alive even when handlers route via `eventDef.name`
|
|
273
|
-
* instead of hand-typed string literals.
|
|
274
|
-
*/
|
|
275
|
-
export type FeatureRegistrar<TFeature extends string = string> = {
|
|
276
|
-
systemScope(): void;
|
|
277
|
-
describe(text: string): void;
|
|
278
|
-
requires: RequiresApi;
|
|
279
|
-
optionalRequires(...featureNames: string[]): void;
|
|
280
|
-
optionalRequires(options: {
|
|
281
|
-
readonly features: readonly string[];
|
|
282
|
-
}): void;
|
|
283
|
-
toggleable(options: {
|
|
284
|
-
default: boolean;
|
|
285
|
-
}): void;
|
|
286
|
-
uiHints(hints: UiHints): void;
|
|
287
|
-
/**
|
|
288
|
-
* @example r.entity({ name: "note", fields: { title: createTextField({ required: true }) } });
|
|
289
|
-
*/
|
|
290
|
-
entity(definition: EntityObjectDefinition): EntityRef;
|
|
291
|
-
crud(entityName: string, entity: EntityDefinition, options?: RegisterEntityCrudOptions): void;
|
|
292
|
-
/**
|
|
293
|
-
* @example r.writeHandler({ name: "note:archive", schema, handler, access: { roles: ["User"] } });
|
|
294
|
-
*/
|
|
295
|
-
writeHandler<TName extends string, TSchema extends ZodType, TPrepared = undefined>(def: WriteHandlerDefinition<TName, TSchema, unknown, KumikoEventTypeMap, TPrepared>): HandlerRef;
|
|
296
|
-
/**
|
|
297
|
-
* @example r.queryHandler({ name: "note:list", schema, handler, access: { roles: ["User"] } });
|
|
298
|
-
*/
|
|
299
|
-
queryHandler<TName extends string, TSchema extends ZodType>(def: QueryHandlerDefinition<TName, TSchema>): HandlerRef;
|
|
300
|
-
streamHandler<TName extends string, TSchema extends ZodType>(def: StreamHandlerDefinition<TName, TSchema>): HandlerRef;
|
|
301
|
-
streamHandler<TSchema extends ZodType>(name: string, schema: TSchema, handler: StreamHandlerFn<z.infer<TSchema>>, options: {
|
|
302
|
-
access: AccessRule;
|
|
303
|
-
rateLimit?: RateLimitDeclaration;
|
|
304
|
-
escapeHatch?: EscapeHatchDeclaration;
|
|
305
|
-
}): HandlerRef;
|
|
306
|
-
relation(entity: NameOrRef, relationName: string, definition: RelationDefinition): void;
|
|
307
|
-
relation<TDef extends RelationDefinition>(definition: {
|
|
308
|
-
readonly entity: NameOrRef;
|
|
309
|
-
readonly name: string;
|
|
310
|
-
} & TDef): void;
|
|
311
|
-
/**
|
|
312
|
-
* @example r.hook({ type: "postSave", target: { allOf: noteRef }, handler: notifyOwner });
|
|
313
|
-
*/
|
|
314
|
-
hook(definition: HookDefinition): void;
|
|
315
|
-
searchPayloadExtension(entity: NameOrRef, fn: SearchPayloadContributorFn): void;
|
|
316
|
-
config<T extends ConfigKeyType>(keyName: string, def: ConfigKeyDefinition<T>): ConfigKeyHandle<T>;
|
|
317
|
-
config<TKeys extends Readonly<Record<string, ConfigKeyDefinition<ConfigKeyType>>>>(definition: {
|
|
318
|
-
readonly keys: TKeys;
|
|
319
|
-
readonly seeds?: Readonly<Record<string, ConfigSeedDef>>;
|
|
320
|
-
}): {
|
|
321
|
-
readonly [K in keyof TKeys]: ConfigKeyHandle<TKeys[K]["type"]>;
|
|
322
|
-
};
|
|
323
|
-
job(name: string, options: Omit<JobDefinition, "name" | "handler">, handler: JobHandlerFn): void;
|
|
324
|
-
job(definition: JobDefinition): void;
|
|
325
|
-
notification(name: string, definition: {
|
|
326
|
-
readonly trigger?: {
|
|
327
|
-
readonly on: NameOrRef;
|
|
328
|
-
};
|
|
329
|
-
readonly recipient?: NotificationRecipientFn;
|
|
330
|
-
readonly data?: NotificationDataFn;
|
|
331
|
-
readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
|
|
332
|
-
readonly variables?: TemplateVariables;
|
|
333
|
-
readonly record?: NotificationRecordMapping;
|
|
334
|
-
readonly template?: Readonly<Record<string, NotificationTemplateBody>>;
|
|
335
|
-
readonly meta?: Readonly<Record<string, NotificationTemplateMeta>>;
|
|
336
|
-
readonly localizedTemplate?: () => NotificationLocalizedTemplate;
|
|
337
|
-
readonly renamedFrom?: readonly string[];
|
|
338
|
-
readonly systemOnly?: boolean;
|
|
339
|
-
readonly unsubscribe?: boolean;
|
|
340
|
-
readonly tenantTransport?: boolean;
|
|
341
|
-
}): void;
|
|
342
|
-
notification(definition: {
|
|
343
|
-
readonly name: string;
|
|
344
|
-
readonly trigger?: {
|
|
345
|
-
readonly on: NameOrRef;
|
|
346
|
-
};
|
|
347
|
-
readonly recipient?: NotificationRecipientFn;
|
|
348
|
-
readonly data?: NotificationDataFn;
|
|
349
|
-
readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
|
|
350
|
-
readonly variables?: TemplateVariables;
|
|
351
|
-
readonly record?: NotificationRecordMapping;
|
|
352
|
-
readonly template?: Readonly<Record<string, NotificationTemplateBody>>;
|
|
353
|
-
readonly meta?: Readonly<Record<string, NotificationTemplateMeta>>;
|
|
354
|
-
readonly localizedTemplate?: () => NotificationLocalizedTemplate;
|
|
355
|
-
readonly renamedFrom?: readonly string[];
|
|
356
|
-
readonly systemOnly?: boolean;
|
|
357
|
-
readonly unsubscribe?: boolean;
|
|
358
|
-
readonly tenantTransport?: boolean;
|
|
359
|
-
}): void;
|
|
360
|
-
translations(def: TranslationsDef): void;
|
|
361
|
-
defineEvent<const TInner extends string, TPayload>(name: TInner, schema: ZodType<TPayload>, options: {
|
|
362
|
-
readonly version?: number;
|
|
363
|
-
readonly piiFields: EventPiiStance;
|
|
364
|
-
readonly migrations?: readonly {
|
|
365
|
-
readonly fromVersion: number;
|
|
366
|
-
readonly toVersion: number;
|
|
367
|
-
readonly transform: EventUpcastFn | DeclarativeEventMigration;
|
|
368
|
-
}[];
|
|
369
|
-
}): EventDef<TPayload, QualifiedEventName<TFeature, TInner>>;
|
|
370
|
-
readsConfig(...qualifiedKeys: string[]): void;
|
|
371
|
-
readsConfig(options: {
|
|
372
|
-
readonly keys: readonly string[];
|
|
373
|
-
}): void;
|
|
374
|
-
referenceData(entity: NameOrRef, data: readonly Record<string, unknown>[], options?: {
|
|
375
|
-
upsertKey?: string;
|
|
376
|
-
}): void;
|
|
377
|
-
referenceData(definition: {
|
|
378
|
-
readonly entity: NameOrRef;
|
|
379
|
-
readonly data: readonly Record<string, unknown>[];
|
|
380
|
-
readonly upsertKey?: string;
|
|
381
|
-
}): void;
|
|
382
|
-
extendsRegistrar(name: string, def: RegistrarExtensionDef): void;
|
|
383
|
-
useExtension<N extends string>(extensionName: N, entity: NameOrRef, ...options: ExtensionOptionsArgs<N>): void;
|
|
384
|
-
useExtension<N extends string>(definition: {
|
|
385
|
-
readonly name: N;
|
|
386
|
-
readonly entity: NameOrRef;
|
|
387
|
-
} & ExtensionOptionsFor<N>): void;
|
|
388
|
-
/**
|
|
389
|
-
* Declares which config key selects the active provider under an
|
|
390
|
-
* extension point — called by the point-owning foundation (e.g.
|
|
391
|
-
* `r.extensionSelector("mailTransport", configKeys.provider)`).
|
|
392
|
-
* Readiness gating counts a provider-feature's `required` keys and
|
|
393
|
-
* secrets only while that provider is the selected one. Registry-build
|
|
394
|
-
* fails on duplicate declarations per extension and on selector keys
|
|
395
|
-
* that no mounted feature declares. A masked tenant selector key makes the
|
|
396
|
-
* Settings-Hub render one tenant dashboard for the owner: the selector as a
|
|
397
|
-
* select of the mounted plugin ids, the selected plugin's config and secrets
|
|
398
|
-
* as panels, and plugin features lose their own tenant nav. `config:write:set`
|
|
399
|
-
* rejects unknown plugin ids (`""` clears). `options.panels` adds the owner's own
|
|
400
|
-
* `custom`/`screen` panels to that dashboard, after the selection and before the
|
|
401
|
-
* plugin panels (a short `screen` id means a screen of the declaring feature).
|
|
402
|
-
*/
|
|
403
|
-
extensionSelector(extensionName: string, key: {
|
|
404
|
-
readonly name: string;
|
|
405
|
-
} | string, options?: {
|
|
406
|
-
readonly panels?: readonly ExtensionSelectorPanel[];
|
|
407
|
-
}): void;
|
|
408
|
-
/**
|
|
409
|
-
* Marker declaration: this feature provides a cross-feature API under the
|
|
410
|
-
* given name. The implementation is registered separately as a query or
|
|
411
|
-
* write handler under the QN pattern; `r.exposesApi` is only a boot-check
|
|
412
|
-
* surface.
|
|
413
|
-
*
|
|
414
|
-
* The boot validator checks that every `r.usesApi(name)` finds a matching
|
|
415
|
-
* exposer, that the exposer feature is listed in requires/optionalRequires,
|
|
416
|
-
* and that no API name is exposed more than once.
|
|
417
|
-
*
|
|
418
|
-
* ```ts
|
|
419
|
-
* defineFeature("compliance-profiles", (r) => {
|
|
420
|
-
* r.exposesApi("compliance.forTenant");
|
|
421
|
-
* r.queryHandler({
|
|
422
|
-
* name: "compliance:query:for-tenant",
|
|
423
|
-
* // ... actual implementation
|
|
424
|
-
* });
|
|
425
|
-
* });
|
|
426
|
-
* ```
|
|
427
|
-
*/
|
|
428
|
-
exposesApi(apiName: string): void;
|
|
429
|
-
/**
|
|
430
|
-
* Declares that this feature calls a cross-feature API. The boot validator
|
|
431
|
-
* checks that another feature provides `r.exposesApi(apiName)` and that
|
|
432
|
-
* this feature lists it in `r.requires/optionalRequires`.
|
|
433
|
-
*
|
|
434
|
-
* ```ts
|
|
435
|
-
* defineFeature("user-data-rights", (r) => {
|
|
436
|
-
* r.requires({ features: ["compliance-profiles"] });
|
|
437
|
-
* r.usesApi("compliance.forTenant");
|
|
438
|
-
* });
|
|
439
|
-
* ```
|
|
440
|
-
*/
|
|
441
|
-
usesApi(apiName: string): void;
|
|
442
|
-
/**
|
|
443
|
-
* Declares a boot-time mount-invariant for this feature. `fn` runs once
|
|
444
|
-
* at boot with a ctx exposing every mounted feature — throw to fail the
|
|
445
|
-
* boot with a clear message. Multiple calls per feature are allowed.
|
|
446
|
-
*
|
|
447
|
-
* Use for cross-feature invariants that `r.requires` can't express
|
|
448
|
-
* because they're conditional (only fail when the feature has a
|
|
449
|
-
* specific shape), e.g. "if this feature has PII entities, some
|
|
450
|
-
* user-data-hook feature must be mounted" (the prompt-store trap —
|
|
451
|
-
* a UserData feature was written but never mounted, and nothing caught
|
|
452
|
-
* it at boot):
|
|
453
|
-
*
|
|
454
|
-
* ```ts
|
|
455
|
-
* defineFeature("prompt-store", (r) => {
|
|
456
|
-
* const promptFields = { text: createTextField({ personal: "self", find: "none" }) };
|
|
457
|
-
* r.entity({ name: "prompt", fields: promptFields });
|
|
458
|
-
* r.bootCheck(({ features }) => {
|
|
459
|
-
* // Conditional on this feature's own shape (has a pii field) —
|
|
460
|
-
* // r.requires({ features: ["user-data-hook"] }) can't express that, it would fail
|
|
461
|
-
* // even for a prompt-store variant with no PII fields at all.
|
|
462
|
-
* const hasPiiField = Object.values(promptFields).some((f) => f.pii);
|
|
463
|
-
* const hasUserDataHook = features.some((f) => f.name === "user-data-hook");
|
|
464
|
-
* if (hasPiiField && !hasUserDataHook) {
|
|
465
|
-
* throw new Error("prompt-store has PII fields but no user-data-hook feature is mounted");
|
|
466
|
-
* }
|
|
467
|
-
* });
|
|
468
|
-
* });
|
|
469
|
-
* ```
|
|
470
|
-
*/
|
|
471
|
-
bootCheck(fn: BootCheckFn): void;
|
|
472
|
-
metric(shortName: string, options: MetricOptions): void;
|
|
473
|
-
metric(definition: {
|
|
474
|
-
readonly name: string;
|
|
475
|
-
} & MetricOptions): void;
|
|
476
|
-
secret(shortName: string, options: SecretOptions): SecretKeyHandle;
|
|
477
|
-
secret(definition: {
|
|
478
|
-
readonly name: string;
|
|
479
|
-
} & SecretOptions): SecretKeyHandle;
|
|
480
|
-
secretNamespace(shortName: string, options: SecretNamespaceOptions): SecretNamespaceHandle;
|
|
481
|
-
secretNamespace(definition: {
|
|
482
|
-
readonly name: string;
|
|
483
|
-
} & SecretNamespaceOptions): SecretNamespaceHandle;
|
|
484
|
-
projection(definition: ProjectionDefinition): void;
|
|
485
|
-
multiStreamProjection(definition: MultiStreamProjectionDefinition): void;
|
|
486
|
-
extendEntityProjection(entityName: string, extension: EntityProjectionExtension): void;
|
|
487
|
-
authClaims(fn: AuthClaimsFn): void;
|
|
488
|
-
claimKey<T extends ClaimKeyType>(shortName: string, options: {
|
|
489
|
-
readonly type: T;
|
|
490
|
-
}): ClaimKeyHandle<T>;
|
|
491
|
-
claimKey<T extends ClaimKeyType>(definition: {
|
|
492
|
-
readonly name: string;
|
|
493
|
-
readonly type: T;
|
|
494
|
-
}): ClaimKeyHandle<T>;
|
|
495
|
-
screen(definition: ScreenDefinition): void;
|
|
496
|
-
nav(definition: NavDefinition): void;
|
|
497
|
-
contentCollection(definition: ContentCollectionDefinition): string;
|
|
498
|
-
workspace(definition: WorkspaceDefinition): void;
|
|
499
|
-
httpRoute(definition: HttpRouteDefinition): void;
|
|
500
|
-
webSocketRoute(definition: WebSocketRouteDefinition): void;
|
|
501
|
-
storeTable(meta: EntityTableMeta, options: StoreTableOptions): void;
|
|
502
|
-
treeActions<const TActions extends Record<string, TreeActionDef>>(actions: TActions): TreeActionsHandle<TFeature, TActions>;
|
|
503
|
-
envSchema(schema: z.ZodObject<z.ZodRawShape>): void;
|
|
504
|
-
};
|
|
505
198
|
export type Registry = {
|
|
506
199
|
readonly features: ReadonlyMap<string, FeatureDefinition>;
|
|
507
200
|
getFeature(name: string): FeatureDefinition | undefined;
|
|
@@ -595,4 +288,3 @@ export type Registry = {
|
|
|
595
288
|
getDefaultWorkspace(): WorkspaceDefinition | undefined;
|
|
596
289
|
getTreeActions(featureName: string): Readonly<Record<string, TreeActionDef>> | undefined;
|
|
597
290
|
};
|
|
598
|
-
export {};
|
package/dist/fields.js
CHANGED
|
@@ -47,7 +47,7 @@ function isFieldTypeName(name) {
|
|
|
47
47
|
export const FIELD_TYPE_NAMES = Object.keys(FIELD_TYPE_NAME_FLAGS).filter(isFieldTypeName);
|
|
48
48
|
// Field types with no bound, editable widget on the auto-wired entityEdit
|
|
49
49
|
// path (render-field.tsx renders a read-only banner instead) — shared by
|
|
50
|
-
// the boot validator (packages/framework/src/engine/boot-validator/screens.ts)
|
|
50
|
+
// the boot validator (packages/framework/src/engine/boot-validator/screens-forms.ts)
|
|
51
51
|
// and the client-side required-field check (packages/renderer/src/app/form-schema.ts).
|
|
52
52
|
// Embedded LIST fields (`multiple: true`) are the one exception — they get
|
|
53
53
|
// their own EmbeddedListField grid widget — callers must check that
|
package/dist/handlers.d.ts
CHANGED
|
@@ -256,6 +256,7 @@ type SharedContextFields = {
|
|
|
256
256
|
readonly signal?: AbortSignal;
|
|
257
257
|
readonly effectiveFeatures?: (tenantId: TenantId) => ReadonlySet<string>;
|
|
258
258
|
readonly scheduleAfterCommit?: (hook: () => Promise<void>) => void;
|
|
259
|
+
readonly scheduleAfterRollback?: (hook: () => Promise<void>) => void;
|
|
259
260
|
readonly queryAsMember?: MemberReader;
|
|
260
261
|
};
|
|
261
262
|
export type AppContext = SharedContextFields & {
|
|
@@ -350,6 +351,10 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
|
|
|
350
351
|
readonly fetchForWriting: (args: FetchForWritingArgs) => Promise<AggregateStreamHandle>;
|
|
351
352
|
readonly loadAggregate: (aggregateId: string, options?: {
|
|
352
353
|
readonly asOf?: Temporal.Instant;
|
|
354
|
+
readonly atVersion?: never;
|
|
355
|
+
} | {
|
|
356
|
+
readonly atVersion?: number;
|
|
357
|
+
readonly asOf?: never;
|
|
353
358
|
}) => Promise<readonly import("./event-store-types.ts").StoredEvent[]>;
|
|
354
359
|
readonly archiveStream: (aggregateId: string, args: {
|
|
355
360
|
readonly aggregateType: string;
|
|
@@ -405,12 +410,6 @@ export type JobContext = SharedContextFields & {
|
|
|
405
410
|
readonly triggerEvent?: JobTriggerEvent;
|
|
406
411
|
readonly _tenantId?: TenantId;
|
|
407
412
|
};
|
|
408
|
-
/**
|
|
409
|
-
* What a provider plugin (`build`, `capabilitiesFor`, ...) may read. Both write/query handlers
|
|
410
|
-
* and jobs can produce one (`providerPluginContextFromHandler`, `providerPluginContextFromJob`),
|
|
411
|
-
* so a plugin never depends on a request-bound field.
|
|
412
|
-
*/
|
|
413
|
-
export type ProviderPluginContext = Pick<HandlerContext, "db" | "dbOutsideTransaction" | "systemDb" | "registry" | "user" | "log" | "triggeredBy" | "config" | "secrets">;
|
|
414
413
|
export type WriteHandlerFn<TPayload = unknown, TData = unknown> = (event: WriteEvent<TPayload>, context: HandlerContext) => Promise<WriteResult<TData>>;
|
|
415
414
|
export type QueryHandlerFn<TPayload = unknown, TResult = unknown> = (query: QueryEvent<TPayload>, context: HandlerContext) => Promise<TResult>;
|
|
416
415
|
export type StreamHandlerFn<TPayload = unknown, TChunk = unknown> = (query: QueryEvent<TPayload>, context: HandlerContext) => AsyncGenerator<TChunk>;
|
|
@@ -632,6 +631,9 @@ export type WriteHandlerDef = {
|
|
|
632
631
|
/** Per-payload-field limits, e.g. per recipient address. Complements
|
|
633
632
|
* `rateLimit`; anonymous handlers still need an ip-keyed `rateLimit`. */
|
|
634
633
|
readonly additionalRateLimits?: readonly PayloadRateLimitOption[];
|
|
634
|
+
/** Tokens each request books on every declared rateLimit bucket, computed from the validated
|
|
635
|
+
* payload (e.g. `(p) => p.files.length`). Replaces the options' `cost`; results below 1 count as 1. */
|
|
636
|
+
readonly rateLimitCost?: (payload: unknown) => number;
|
|
635
637
|
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
636
638
|
/** Reserves before the handler transaction opens. Reached through a nested ctx.write the
|
|
637
639
|
* reservation cannot run, so the dispatcher rejects it unless the top-level batch reserved it. */
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import type { AggregateKey } from "./aggregate-types.ts";
|
|
3
|
+
export declare const MAX_LIST_AGGREGATE_MEASURES = 20;
|
|
4
|
+
export declare const LIST_AGGREGATE_MEASURE_KINDS: readonly ["sum", "avg", "count", "min", "max"];
|
|
5
|
+
export type ListAggregateMeasureKind = (typeof LIST_AGGREGATE_MEASURE_KINDS)[number];
|
|
6
|
+
export declare const listAggregatePayloadSchema: z.ZodObject<{
|
|
7
|
+
groupBy: z.ZodOptional<z.ZodString>;
|
|
8
|
+
measures: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
9
|
+
field: z.ZodString;
|
|
10
|
+
kind: z.ZodEnum<{
|
|
11
|
+
avg: "avg";
|
|
12
|
+
count: "count";
|
|
13
|
+
max: "max";
|
|
14
|
+
min: "min";
|
|
15
|
+
sum: "sum";
|
|
16
|
+
}>;
|
|
17
|
+
}, z.core.$strip>>>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export type ListAggregatePayload = {
|
|
20
|
+
readonly groupBy?: string | undefined;
|
|
21
|
+
readonly measures: readonly {
|
|
22
|
+
readonly field: string;
|
|
23
|
+
readonly kind: ListAggregateMeasureKind;
|
|
24
|
+
}[];
|
|
25
|
+
};
|
|
26
|
+
export type ListAggregateCurrencyAmount = {
|
|
27
|
+
readonly currency: string;
|
|
28
|
+
readonly value: number;
|
|
29
|
+
};
|
|
30
|
+
export type ListAggregateValue = number | string | null | readonly ListAggregateCurrencyAmount[];
|
|
31
|
+
export declare function isListAggregateCurrencyAmounts(value: ListAggregateValue | undefined): value is readonly ListAggregateCurrencyAmount[];
|
|
32
|
+
export type ListAggregateGroup = {
|
|
33
|
+
readonly key: AggregateKey;
|
|
34
|
+
readonly label?: string | null;
|
|
35
|
+
readonly count: number;
|
|
36
|
+
readonly measures: readonly ListAggregateValue[];
|
|
37
|
+
};
|
|
38
|
+
export type ListAggregateResult = {
|
|
39
|
+
readonly groups: readonly ListAggregateGroup[];
|
|
40
|
+
readonly truncated?: true;
|
|
41
|
+
readonly searchTruncated?: true;
|
|
42
|
+
};
|
|
43
|
+
export declare function isListAggregateResult(value: unknown): value is ListAggregateResult;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { isPlainObject } from "./type-guards.js";
|
|
3
|
+
export const MAX_LIST_AGGREGATE_MEASURES = 20;
|
|
4
|
+
export const LIST_AGGREGATE_MEASURE_KINDS = ["sum", "avg", "count", "min", "max"];
|
|
5
|
+
export const listAggregatePayloadSchema = z.object({
|
|
6
|
+
groupBy: z.string().optional(),
|
|
7
|
+
measures: z
|
|
8
|
+
.array(z.object({ field: z.string(), kind: z.enum(LIST_AGGREGATE_MEASURE_KINDS) }))
|
|
9
|
+
.max(MAX_LIST_AGGREGATE_MEASURES)
|
|
10
|
+
.default([]),
|
|
11
|
+
});
|
|
12
|
+
export function isListAggregateCurrencyAmounts(value) {
|
|
13
|
+
return Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
const LIST_AGGREGATE_RESULT_KEYS = ["groups", "truncated", "searchTruncated"];
|
|
16
|
+
// Closed key set: a row that merely has a `groups` field (jsonb column) must not
|
|
17
|
+
// be mistaken for an aggregate and skip the row-level read filter.
|
|
18
|
+
export function isListAggregateResult(value) {
|
|
19
|
+
return (isPlainObject(value) &&
|
|
20
|
+
Array.isArray(value["groups"]) &&
|
|
21
|
+
Object.keys(value).every((key) => LIST_AGGREGATE_RESULT_KEYS.includes(key)));
|
|
22
|
+
}
|
|
@@ -6,6 +6,8 @@ import type { FieldRenderer, PlatformComponent, ScreenNavSugar, ScreenSlots } fr
|
|
|
6
6
|
import type { ActionFormRedirect } from "./screen-action-form.ts";
|
|
7
7
|
import type { DashboardPanelVisibility } from "./screen-dashboard.ts";
|
|
8
8
|
import type { ListColumnSpec, ListSortSpec, RelatedListToolbarAction, RowAction, RowActionMode, RowFieldExtractor } from "./screen-entity-list.ts";
|
|
9
|
+
import type { ListBulkAction } from "./screen-list-bulk.ts";
|
|
10
|
+
import type { ListTableGrouping, ListTableOptions } from "./screen-list-table.ts";
|
|
9
11
|
import type { ListFacetSpec } from "./screen-projection.ts";
|
|
10
12
|
export type EditFieldSpec = string | {
|
|
11
13
|
readonly field: string;
|
|
@@ -168,25 +170,9 @@ export type EditExtensionSection = {
|
|
|
168
170
|
* (declared or id-derived); the boot-validator rejects one that doesn't. */
|
|
169
171
|
readonly actions?: readonly RowAction[];
|
|
170
172
|
};
|
|
171
|
-
/**
|
|
172
|
-
* a collapsible header. */
|
|
173
|
-
export type RelatedListGroupBy = {
|
|
174
|
-
readonly field: string;
|
|
175
|
-
/** Groups whose field value equals this start collapsed; the others start open. */
|
|
176
|
-
readonly collapsedWhen?: string | number | boolean | null;
|
|
177
|
-
/** i18n key of the group header. Placeholders: `{count}` rows in the group,
|
|
178
|
-
* `{value}` the group's field value, `{lastDate}` the group's latest
|
|
179
|
-
* `dateField` value as a locale date. A group with neither `label` nor an
|
|
180
|
-
* entry in `labels` renders its rows without a header and never collapses. */
|
|
181
|
-
readonly label?: string;
|
|
182
|
-
/** Header i18n key per group value (as string), overriding `label`. */
|
|
183
|
-
readonly labels?: Readonly<Record<string, string>>;
|
|
184
|
-
/** Row field (ISO date or timestamp) behind `{lastDate}`. */
|
|
185
|
-
readonly dateField?: string;
|
|
186
|
-
};
|
|
187
|
-
/** Group key of a field value; `collapsedWhen` and row values both go through it. */
|
|
173
|
+
/** Group key of a field value; `collapsedWhen` and server group keys both go through it. */
|
|
188
174
|
export declare function relatedListGroupKey(value: unknown): string;
|
|
189
|
-
export declare function relatedListGroupHeaderLabel(
|
|
175
|
+
export declare function relatedListGroupHeaderLabel(grouping: Pick<ListTableGrouping, "label" | "labels">, groupKey: string): string | undefined;
|
|
190
176
|
/** Row tint rule: rows where the `FieldCondition` (`{ field, eq }`, `{ field,
|
|
191
177
|
* in }` …) holds get the tone. `bad` renders red. */
|
|
192
178
|
export type RelatedListRowTone = Exclude<FieldCondition, boolean> & {
|
|
@@ -240,6 +226,8 @@ export type EditRelatedListSection = {
|
|
|
240
226
|
* "3 Positionen"). Omitted: generic "entries" wording. */
|
|
241
227
|
readonly itemNoun?: string;
|
|
242
228
|
readonly pageSize?: number;
|
|
229
|
+
/** Opt-in table behaviour: remembered state, reset, column picker, density. */
|
|
230
|
+
readonly table?: ListTableOptions;
|
|
243
231
|
/** Initial sort on mount, applied client-side over the already-loaded rows
|
|
244
232
|
* (this section has no pager; if the fetch is truncated, `nextCursor !== null`,
|
|
245
233
|
* the section shows a hint banner that the sort covers only the loaded rows).
|
|
@@ -278,8 +266,8 @@ export type EditRelatedListSection = {
|
|
|
278
266
|
readonly rowActions?: readonly RowAction[];
|
|
279
267
|
/** Layout of the row-action column — see `RowActionMode`. */
|
|
280
268
|
readonly rowActionMode?: RowActionMode;
|
|
281
|
-
/**
|
|
282
|
-
readonly
|
|
269
|
+
/** Bulk actions over checkbox-selected rows, each pointing at a writeHandler row action. Adds a checkbox column and a selection bar; without it the list has no checkboxes. */
|
|
270
|
+
readonly bulkActions?: readonly ListBulkAction[];
|
|
283
271
|
/** Tints rows whose field matches — a declarative rule, not a function, so
|
|
284
272
|
* the screen definition stays serializable. */
|
|
285
273
|
readonly rowTone?: RelatedListRowTone;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
/** Group key of a field value; `collapsedWhen` and
|
|
1
|
+
/** Group key of a field value; `collapsedWhen` and server group keys both go through it. */
|
|
2
2
|
export function relatedListGroupKey(value) {
|
|
3
3
|
return value === undefined || value === null ? "" : String(value);
|
|
4
4
|
}
|
|
5
|
-
export function relatedListGroupHeaderLabel(
|
|
6
|
-
const labels =
|
|
7
|
-
return labels !== undefined && Object.hasOwn(labels, groupKey)
|
|
5
|
+
export function relatedListGroupHeaderLabel(grouping, groupKey) {
|
|
6
|
+
const labels = grouping.labels;
|
|
7
|
+
return labels !== undefined && Object.hasOwn(labels, groupKey)
|
|
8
|
+
? labels[groupKey]
|
|
9
|
+
: grouping.label;
|
|
8
10
|
}
|
|
@@ -4,7 +4,10 @@ import type { IconKey } from "./nav-icon.ts";
|
|
|
4
4
|
import type { FieldRenderer, PlatformComponent, ScreenNavSugar, ScreenSlots } from "./screen.ts";
|
|
5
5
|
import type { ActionFormRedirect } from "./screen-action-form.ts";
|
|
6
6
|
import type { DashboardPanelVisibility } from "./screen-dashboard.ts";
|
|
7
|
-
import type {
|
|
7
|
+
import type { ListEmptyStateSpec } from "./screen-entity-edit.ts";
|
|
8
|
+
import type { EntityListExpandableRow } from "./screen-expandable-row.ts";
|
|
9
|
+
import type { ListBulkAction } from "./screen-list-bulk.ts";
|
|
10
|
+
import type { ListColumnPin, ListTableOptions } from "./screen-list-table.ts";
|
|
8
11
|
export type ListColumnSpec = string | {
|
|
9
12
|
readonly field: string;
|
|
10
13
|
readonly renderer?: FieldRenderer;
|
|
@@ -49,6 +52,16 @@ export type ListColumnSpec = string | {
|
|
|
49
52
|
* `md` breakpoint (tables keep it). For columns that only make sense
|
|
50
53
|
* next to the others, like a sort key. */
|
|
51
54
|
readonly hideOnNarrow?: boolean;
|
|
55
|
+
/** Start state for the column picker: the column begins hidden and the
|
|
56
|
+
* user can show it. Only valid together with `table.columnPicker`. */
|
|
57
|
+
readonly hidden?: boolean;
|
|
58
|
+
/** Start pin for the column: it sticks to this edge while the table scrolls
|
|
59
|
+
* horizontally. Only valid together with `table.pinning`. */
|
|
60
|
+
readonly pinned?: ListColumnPin;
|
|
61
|
+
/** Start width in px. Only valid together with `table.resizing`. */
|
|
62
|
+
readonly width?: number;
|
|
63
|
+
/** Smallest width in px the user can resize to. Only valid together with `table.resizing`. */
|
|
64
|
+
readonly minWidth?: number;
|
|
52
65
|
/** Dashboard list panels only. "bar": field holds a 0..1 fraction, drawn
|
|
53
66
|
* as bar + percent. "badge": field holds a label, drawn as a pill whose
|
|
54
67
|
* tone comes from `badgeToneField`. "datetime": field holds an ISO string or
|
|
@@ -321,11 +334,6 @@ export type RelatedListToolbarAction = (Extract<ToolbarAction, {
|
|
|
321
334
|
}> & {
|
|
322
335
|
readonly visible?: FieldCondition;
|
|
323
336
|
});
|
|
324
|
-
/** Expandable area of an `entityList` row: a relatedList of the row's record.
|
|
325
|
-
* `id` (tab id) and `countField` (tab badge) have no meaning outside a tabs
|
|
326
|
-
* layout and are therefore omitted. `kind` stays the discriminator so further
|
|
327
|
-
* expansion kinds can be added without a breaking change. */
|
|
328
|
-
export type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField">;
|
|
329
337
|
/** An extra choice on a facet next to the field's own options: a named
|
|
330
338
|
* bundle of values ("Running" = several statuses) or "no filter" (`values:
|
|
331
339
|
* []`, e.g. "All"). Chips only. */
|
|
@@ -340,8 +348,9 @@ export type EntityListFacetExtraOption = {
|
|
|
340
348
|
};
|
|
341
349
|
export type EntityListFacetConfig = {
|
|
342
350
|
/** `"select"` (default): dropdown with checkboxes. `"chips"`: one toggle
|
|
343
|
-
* chip per option, single choice.
|
|
344
|
-
|
|
351
|
+
* chip per option, single choice. `"range"`: "from" and "to" inputs on a
|
|
352
|
+
* number, decimal, money or bigInt field, sent as `gte`/`lte`. */
|
|
353
|
+
readonly display?: "select" | "chips" | "range";
|
|
345
354
|
/** Chips show how many rows each option matches (other facets and the
|
|
346
355
|
* search apply). Counts come from the list query itself, one `limit: 1`
|
|
347
356
|
* count request per chip. */
|
|
@@ -387,6 +396,8 @@ export type EntityListScreenDefinition = {
|
|
|
387
396
|
readonly rowActions?: readonly RowAction[];
|
|
388
397
|
/** Layout of the row-action column — see `RowActionMode`. */
|
|
389
398
|
readonly rowActionMode?: RowActionMode;
|
|
399
|
+
/** Bulk actions over checkbox-selected rows, each pointing at a writeHandler row action. Adds a checkbox column and a selection bar; without it the list has no checkboxes. */
|
|
400
|
+
readonly bulkActions?: readonly ListBulkAction[];
|
|
390
401
|
/** Toolbar actions (list header). "Open Incident", "Schedule Maintenance"
|
|
391
402
|
* etc. — next to "+ New" if present. Array order = UI order,
|
|
392
403
|
* primary-style links. */
|
|
@@ -403,6 +414,8 @@ export type EntityListScreenDefinition = {
|
|
|
403
414
|
readonly filter?: ScreenFilter;
|
|
404
415
|
readonly pagination?: ListPaginationMode;
|
|
405
416
|
readonly pageSize?: number;
|
|
417
|
+
/** Opt-in table behaviour: remembered state, reset, column picker, density. */
|
|
418
|
+
readonly table?: ListTableOptions;
|
|
406
419
|
readonly defaultSort?: ListSortSpec;
|
|
407
420
|
/** Initial facet selection, per filterable field: option values (string
|
|
408
421
|
* facets) or a boolean. Applies only while the URL carries no value for
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EditRelatedListSection } from "./screen-entity-edit.ts";
|
|
2
|
+
import type { ListTableGrouping } from "./screen-list-table.ts";
|
|
3
|
+
/** Server-side grouping of an expandable row's sub-list: a fixed `field`, no "Group by" choice. */
|
|
4
|
+
export type ExpandableRowGrouping = Omit<ListTableGrouping, "field" | "userChoice" | "fields"> & {
|
|
5
|
+
readonly field: string;
|
|
6
|
+
};
|
|
7
|
+
/** Expandable area of an `entityList` row: a relatedList of the row's record.
|
|
8
|
+
* `id` (tab id) and `countField` (tab badge) have no meaning outside a tabs
|
|
9
|
+
* layout and are therefore omitted. `kind` stays the discriminator so further
|
|
10
|
+
* expansion kinds can be added without a breaking change. `table` takes only
|
|
11
|
+
* `grouping`; the other table options belong on the entityList itself. */
|
|
12
|
+
export type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField" | "table" | "bulkActions"> & {
|
|
13
|
+
readonly table?: {
|
|
14
|
+
readonly grouping?: ExpandableRowGrouping;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const LIST_BULK_ACTION_KEYS: readonly ["id", "rowAction", "confirm"];
|
|
2
|
+
/** Most rows one selection can hold; the loaded page caps it further. */
|
|
3
|
+
export declare const MAX_BULK_SELECTION = 100;
|
|
4
|
+
/** An action that runs a writeHandler row action once per checkbox-selected row. One `/api/write` per row, so access, validation and audit stay per row. */
|
|
5
|
+
export type ListBulkAction = {
|
|
6
|
+
/** Stable id, kebab-case, unique among the screen's bulk actions. */
|
|
7
|
+
readonly id: string;
|
|
8
|
+
/** Id of a writeHandler entry in the same screen's `rowActions`; it supplies handler, payload, label, style and `visible`. Rows the action's `visible` hides are skipped. */
|
|
9
|
+
readonly rowAction: string;
|
|
10
|
+
/** i18n key of the one confirm dialog for the whole selection; gets `{count}` and `{action}`. Without it a `confirm` or `style: "danger"` on the row action still asks once with the default text. */
|
|
11
|
+
readonly confirm?: string;
|
|
12
|
+
};
|