@cosmicdrift/kumiko-types 0.359.0 → 0.361.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 +1 -0
- package/dist/event-store-executor-types.d.ts +11 -2
- package/dist/event-store-executor-types.js +3 -1
- package/dist/fields.d.ts +15 -0
- package/dist/fields.js +1 -6
- package/dist/screen.d.ts +65 -6
- package/dist/session-cookies.d.ts +20 -0
- package/dist/session-cookies.js +31 -0
- package/package.json +9 -1
- package/src/changes.json +30 -0
package/dist/config.d.ts
CHANGED
|
@@ -12,6 +12,15 @@ export type PreSaveRunner = (changes: Record<string, unknown>, previous: Readonl
|
|
|
12
12
|
export type StreamTenantOption = {
|
|
13
13
|
readonly streamTenantId?: TenantId | undefined;
|
|
14
14
|
};
|
|
15
|
+
export declare const WritePurposes: {
|
|
16
|
+
readonly retention: "retention";
|
|
17
|
+
readonly erasure: "erasure";
|
|
18
|
+
};
|
|
19
|
+
export type WritePurpose = (typeof WritePurposes)[keyof typeof WritePurposes];
|
|
20
|
+
export type LockGuardOption = {
|
|
21
|
+
readonly unsafeSkipLockGuard?: boolean;
|
|
22
|
+
readonly writePurpose?: WritePurpose;
|
|
23
|
+
};
|
|
15
24
|
export type EventStoreExecutor = {
|
|
16
25
|
create: (payload: Record<string, unknown>, user: SessionUser, db: TenantDb, options?: StreamTenantOption & {
|
|
17
26
|
preSave?: PreSaveRunner;
|
|
@@ -20,7 +29,7 @@ export type EventStoreExecutor = {
|
|
|
20
29
|
id: EntityId;
|
|
21
30
|
version?: number | undefined;
|
|
22
31
|
changes: Record<string, unknown>;
|
|
23
|
-
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & {
|
|
32
|
+
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & {
|
|
24
33
|
skipOptimisticLock?: boolean;
|
|
25
34
|
skipUnchanged?: boolean;
|
|
26
35
|
includeDeleted?: boolean;
|
|
@@ -29,7 +38,7 @@ export type EventStoreExecutor = {
|
|
|
29
38
|
}) => Promise<WriteResult<SaveContext>>;
|
|
30
39
|
delete: (payload: {
|
|
31
40
|
id: EntityId;
|
|
32
|
-
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption) => Promise<WriteResult<DeleteContext>>;
|
|
41
|
+
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption) => Promise<WriteResult<DeleteContext>>;
|
|
33
42
|
forget: (payload: {
|
|
34
43
|
id: EntityId;
|
|
35
44
|
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption) => Promise<WriteResult<DeleteContext>>;
|
package/dist/fields.d.ts
CHANGED
|
@@ -472,6 +472,9 @@ export type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & {
|
|
|
472
472
|
}) | (EmbeddedSubFieldBase & {
|
|
473
473
|
readonly type: "select";
|
|
474
474
|
readonly options: readonly string[];
|
|
475
|
+
/** Same contract as `SelectFieldDef.display`; in a table cell the
|
|
476
|
+
* default stays a dropdown. */
|
|
477
|
+
readonly display?: "radio" | "dropdown";
|
|
475
478
|
}) | (EmbeddedSubFieldBase & {
|
|
476
479
|
readonly type: "reference";
|
|
477
480
|
readonly entity: string;
|
|
@@ -641,6 +644,9 @@ export type ImagesFieldDef = {
|
|
|
641
644
|
readonly accept?: readonly string[];
|
|
642
645
|
readonly maxCount?: number;
|
|
643
646
|
readonly variants?: Readonly<Record<string, VariantSpec>>;
|
|
647
|
+
/** Forwarded to the file input's `capture` attribute so a phone opens the
|
|
648
|
+
* camera instead of the file picker. */
|
|
649
|
+
readonly capture?: "environment" | "user";
|
|
644
650
|
readonly access?: FieldAccess;
|
|
645
651
|
} & ResolvedPiiFlags;
|
|
646
652
|
export type FieldDefinition = TextFieldDef | LongTextFieldDef | BooleanFieldDef | SelectFieldDef | MultiSelectFieldDef | NumberFieldDef | BigIntFieldDef | DecimalFieldDef | MoneyFieldDef | ReferenceFieldDef | EmbeddedFieldDef | JsonbFieldDef | DateFieldDef | TimestampFieldDef | TzFieldDef | LocatedTimestampFieldDef | FileFieldDef | ImageFieldDef | FilesFieldDef | ImagesFieldDef;
|
|
@@ -748,6 +754,15 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
|
|
|
748
754
|
readonly eventMigrations?: readonly EntityEventMigration[];
|
|
749
755
|
readonly searchWeight?: number;
|
|
750
756
|
readonly defaultCurrency?: string;
|
|
757
|
+
/** Rows whose stored state meets this condition are read-only: every executor
|
|
758
|
+
* update and delete on them is rejected with a 409 (`errors.recordLocked`), whatever
|
|
759
|
+
* handler or job calls it. The write that sets the lock passes, because the check reads the
|
|
760
|
+
* state before the write. Forget is exempt, and so are framework system-user writes that name
|
|
761
|
+
* a `writePurpose` (retention, erasure); a system user without one is blocked like anyone else.
|
|
762
|
+
* A call site that must unlock a row passes `{ unsafeSkipLockGuard: true }` to that one executor call. The entityEdit
|
|
763
|
+
* screen shows such rows in view mode. The condition field must be a plaintext text, select,
|
|
764
|
+
* boolean, number or reference field. */
|
|
765
|
+
readonly lockedWhen?: FieldCondition;
|
|
751
766
|
/** Allowed state transitions per field. Boot validates against select options. */
|
|
752
767
|
readonly transitions?: Readonly<Record<string, TransitionMap>>;
|
|
753
768
|
/** Composite-Indices über mehrere Felder. Single-column FK-Indices und
|
package/dist/fields.js
CHANGED
|
@@ -52,9 +52,4 @@ export const FIELD_TYPE_NAMES = Object.keys(FIELD_TYPE_NAME_FLAGS);
|
|
|
52
52
|
// A plain array, not a Set: kumiko-types stays free of module-scope runtime
|
|
53
53
|
// identity (guard-types-class-free.test.ts) since a consumer may resolve two
|
|
54
54
|
// copies of this package.
|
|
55
|
-
export const NO_WIDGET_FIELD_TYPES = [
|
|
56
|
-
"jsonb",
|
|
57
|
-
"embedded",
|
|
58
|
-
"files",
|
|
59
|
-
"images",
|
|
60
|
-
];
|
|
55
|
+
export const NO_WIDGET_FIELD_TYPES = ["jsonb", "embedded"];
|
package/dist/screen.d.ts
CHANGED
|
@@ -55,6 +55,15 @@ export interface FieldFormatRegistry {
|
|
|
55
55
|
json: {
|
|
56
56
|
readonly indent?: number;
|
|
57
57
|
};
|
|
58
|
+
/** List cell: FileRef id (or id array) as a thumbnail. `variant` names an
|
|
59
|
+
* image variant to request (falls back to the original if it is not served).
|
|
60
|
+
* Outside list cells the raw value is shown. */
|
|
61
|
+
image: {
|
|
62
|
+
readonly variant?: string;
|
|
63
|
+
};
|
|
64
|
+
/** List cell: FileRef id (or id array) as a download link with the file name.
|
|
65
|
+
* Outside list cells the raw value is shown. */
|
|
66
|
+
file: object;
|
|
58
67
|
}
|
|
59
68
|
export type FormatSpec = {
|
|
60
69
|
[K in keyof FieldFormatRegistry]: {
|
|
@@ -440,6 +449,8 @@ export type EntityListScreenDefinition = {
|
|
|
440
449
|
* through `defaultFilters` and the URL). Fields without an entry keep the
|
|
441
450
|
* default dropdown. */
|
|
442
451
|
readonly facets?: Readonly<Record<string, EntityListFacetConfig | false>>;
|
|
452
|
+
/** Replaces the default empty-list title/hint; the create button stays when the list can create. */
|
|
453
|
+
readonly emptyState?: ListEmptyStateSpec;
|
|
443
454
|
/** i18n key for the primary create button label (default `kumiko.actions.create`). */
|
|
444
455
|
readonly createLabel?: string;
|
|
445
456
|
/** i18n key for the search field placeholder (default `kumiko.list.search-placeholder`). */
|
|
@@ -519,6 +530,8 @@ export type ProjectionListScreenDefinition = {
|
|
|
519
530
|
readonly pagination?: ListPaginationMode;
|
|
520
531
|
readonly pageSize?: number;
|
|
521
532
|
readonly defaultSort?: ListSortSpec;
|
|
533
|
+
/** Replaces the default empty-list title/hint; the create button stays when the list can create. */
|
|
534
|
+
readonly emptyState?: ListEmptyStateSpec;
|
|
522
535
|
/** i18n key for the primary create button label (default `kumiko.actions.create`). */
|
|
523
536
|
readonly createLabel?: string;
|
|
524
537
|
/** i18n key for the search field placeholder (default `kumiko.list.search-placeholder`). */
|
|
@@ -562,7 +575,10 @@ export type RecordHeaderSpec = {
|
|
|
562
575
|
* (`target="_blank"`) instead of plain text. Only valid with the string
|
|
563
576
|
* form of `subtitle`. */
|
|
564
577
|
readonly subtitleHref?: string;
|
|
565
|
-
/**
|
|
578
|
+
/** Record field whose value selects the `statusTones` entry, for a badge text that
|
|
579
|
+
* carries more than the state (e.g. "Terminated on 31.03.2027"). Defaults to `status`. */
|
|
580
|
+
readonly statusToneField?: string;
|
|
581
|
+
/** Badge tone per `statusToneField` value (default: the `status` value); unlisted values fall back to the value heuristic. */
|
|
566
582
|
readonly statusTones?: {
|
|
567
583
|
readonly [statusValue: string]: SelectOptionTone;
|
|
568
584
|
};
|
|
@@ -642,6 +658,12 @@ export type ProjectionDetailScreenDefinition = {
|
|
|
642
658
|
* `rowClick` has no target here (there is no row to click) and is
|
|
643
659
|
* rejected by the boot-validator. */
|
|
644
660
|
readonly actions?: readonly RowAction[];
|
|
661
|
+
/** Id of the action that stays the visible header button when the actions
|
|
662
|
+
* collapse into a menu (and the one that keeps the phone icon slot when it
|
|
663
|
+
* has an icon and no confirm). An `actions` id or `"edit"` (the default edit
|
|
664
|
+
* action); default precedence is `edit`, then `style: "primary"`, then the
|
|
665
|
+
* first action. */
|
|
666
|
+
readonly primaryAction?: string;
|
|
645
667
|
/** How head fields render. Default "text" — plain text instead of a
|
|
646
668
|
* disabled Input, since every field on this screen type is forced
|
|
647
669
|
* readOnly anyway (there is no write path, see the shim doc above).
|
|
@@ -934,6 +956,25 @@ export type EditFieldSpec = string | {
|
|
|
934
956
|
/** Row field on the referenced entity shown as the label (default
|
|
935
957
|
* "id"). Only meaningful together with `refEntity`. */
|
|
936
958
|
readonly refLabelField?: string;
|
|
959
|
+
/** Embedded-list fields only (`createEmbeddedListField`). */
|
|
960
|
+
readonly embeddedList?: EmbeddedListEditSpec;
|
|
961
|
+
/** Fills an empty value when the create form opens; the update form and
|
|
962
|
+
* values from URL params or a handoff are never touched. Unlike a
|
|
963
|
+
* field `default`, it is not applied server-side. */
|
|
964
|
+
readonly prefill?: EditFieldPrefill;
|
|
965
|
+
};
|
|
966
|
+
export type EmbeddedListEditSpec = {
|
|
967
|
+
/** Sub-fields shown as read-only cells; every other cell stays editable. */
|
|
968
|
+
readonly readOnlyCells?: readonly string[];
|
|
969
|
+
/** `true` keeps the rows as they are: no add, remove, duplicate, move or
|
|
970
|
+
* paste-appended rows. Cells stay editable (unless in `readOnlyCells`). */
|
|
971
|
+
readonly fixedRows?: boolean;
|
|
972
|
+
};
|
|
973
|
+
export type EditFieldPrefill = {
|
|
974
|
+
readonly kind: "today";
|
|
975
|
+
} | {
|
|
976
|
+
readonly kind: "currentUser";
|
|
977
|
+
readonly property: "displayName" | "email" | "id";
|
|
937
978
|
};
|
|
938
979
|
export type EditSectionSpec = EditFieldsSection | EditExtensionSection | EditRelatedListSection | EditWriteFormSection;
|
|
939
980
|
export type EditFieldsSection = {
|
|
@@ -1037,6 +1078,16 @@ export declare function relatedListGroupHeaderLabel(groupBy: RelatedListGroupBy,
|
|
|
1037
1078
|
export type RelatedListRowTone = Exclude<FieldCondition, boolean> & {
|
|
1038
1079
|
readonly tone: SelectOptionTone;
|
|
1039
1080
|
};
|
|
1081
|
+
/** Replacement for the default "no rows" box of a list. `title`/`description`
|
|
1082
|
+
* are i18n keys. `action` is a record action evaluated against an empty
|
|
1083
|
+
* record on list screens (the parent record on a relatedList); its icon must
|
|
1084
|
+
* resolve, same as `ProjectionDetailScreenDefinition.actions`. A narrowed list
|
|
1085
|
+
* (search term or changed filters) shows the neutral "no matches" text instead. */
|
|
1086
|
+
export type ListEmptyStateSpec = {
|
|
1087
|
+
readonly title: string;
|
|
1088
|
+
readonly description?: string;
|
|
1089
|
+
readonly action?: RowAction;
|
|
1090
|
+
};
|
|
1040
1091
|
export type EditRelatedListSection = {
|
|
1041
1092
|
/** Kebab-case tab id, used as the `?tab=` value. Required by the
|
|
1042
1093
|
* boot-validator when the enclosing `EditLayout.mode` is "tabs". */
|
|
@@ -1137,11 +1188,7 @@ export type EditRelatedListSection = {
|
|
|
1137
1188
|
* section's query returns zero rows. Omitted: the DataTable default
|
|
1138
1189
|
* ("kumiko.list.no-entries") applies. `action`'s icon must resolve, same
|
|
1139
1190
|
* as `actions` above. */
|
|
1140
|
-
readonly emptyState?:
|
|
1141
|
-
readonly title: string;
|
|
1142
|
-
readonly description?: string;
|
|
1143
|
-
readonly action?: RowAction;
|
|
1144
|
-
};
|
|
1191
|
+
readonly emptyState?: ListEmptyStateSpec;
|
|
1145
1192
|
};
|
|
1146
1193
|
export type EditWriteFormSection = {
|
|
1147
1194
|
/** Kebab-case tab id, used as the `?tab=` value. Required by the
|
|
@@ -1246,6 +1293,14 @@ export type EntityEditScreenDefinition = {
|
|
|
1246
1293
|
* wizard: "Add {make} {model}"). Falls back to the plain title while the
|
|
1247
1294
|
* placeholders are still empty. */
|
|
1248
1295
|
readonly titleTemplate?: string;
|
|
1296
|
+
/** Shows a saved record read-only while the condition holds for the loaded
|
|
1297
|
+
* record (not for unsaved form values): all fields disabled, no save, discard
|
|
1298
|
+
* or delete button; record `actions` stay. The title uses
|
|
1299
|
+
* `screen:<id>.view.title`, then `screen:<id>.title`. Never applies to create.
|
|
1300
|
+
* The entity's `lockedWhen` always applies as well; set this only to add a
|
|
1301
|
+
* UI-only condition (either one shows the view).
|
|
1302
|
+
* UI only: the update handler still has to enforce access. */
|
|
1303
|
+
readonly readOnly?: FieldCondition;
|
|
1249
1304
|
readonly layout: EditLayout;
|
|
1250
1305
|
/** Optionaler i18n-Key (oder Roh-String) für den Submit-Button. Default
|
|
1251
1306
|
* `kumiko.actions.save`. Lässt den Auto-Edit-Screen domain-spezifische
|
|
@@ -1449,6 +1504,10 @@ export type ActionFormRedirect = {
|
|
|
1449
1504
|
* `projectionDetail`); a non-string or missing value navigates without
|
|
1450
1505
|
* an id, same as a payload without `id` does today. */
|
|
1451
1506
|
readonly idFrom: string;
|
|
1507
|
+
/** Section id to open on the target `projectionDetail` with `layout.mode:
|
|
1508
|
+
* "tabs"` (the `tab` search param, same as `ToolbarAction` navigate `tab`).
|
|
1509
|
+
* Boot rejects other targets and unknown section ids. */
|
|
1510
|
+
readonly tab?: string;
|
|
1452
1511
|
};
|
|
1453
1512
|
/** Author-declared field shown once after a mint-form submit — API token,
|
|
1454
1513
|
* recovery codes, similar one-time secrets. */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const AUTH_COOKIE_BASE = "kumiko_auth";
|
|
2
|
+
export declare const CSRF_COOKIE_BASE = "kumiko_csrf";
|
|
3
|
+
export declare const TENANT_COOKIE_BASE = "kumiko_tenant";
|
|
4
|
+
export declare const HOST_COOKIE_PREFIX = "__Host-";
|
|
5
|
+
export type SessionCookieBase = typeof AUTH_COOKIE_BASE | typeof CSRF_COOKIE_BASE | typeof TENANT_COOKIE_BASE;
|
|
6
|
+
export type SessionCookieNames = {
|
|
7
|
+
readonly auth: string;
|
|
8
|
+
readonly csrf: string;
|
|
9
|
+
readonly tenant: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function sessionCookieName(base: SessionCookieBase, options: {
|
|
12
|
+
readonly secure: boolean;
|
|
13
|
+
readonly domain?: string | undefined;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function isHostPrefixedCookieName(name: string): boolean;
|
|
16
|
+
export declare function resolveSessionCookieNames(options: {
|
|
17
|
+
readonly secure: boolean;
|
|
18
|
+
readonly domain?: string | undefined;
|
|
19
|
+
}): SessionCookieNames;
|
|
20
|
+
export declare function sessionCookieNameCandidates(base: SessionCookieBase): readonly string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Single source of the session cookie names, shared by the server and by
|
|
2
|
+
// browser/native clients (this package has no server dependencies).
|
|
3
|
+
//
|
|
4
|
+
// A cookie gets the `__Host-` prefix exactly when it is written Secure and
|
|
5
|
+
// without a Domain attribute: the browser then refuses any Set-Cookie for that
|
|
6
|
+
// name that is not Secure, Path=/ and host-only, so a sibling subdomain cannot
|
|
7
|
+
// set or shadow it.
|
|
8
|
+
export const AUTH_COOKIE_BASE = "kumiko_auth";
|
|
9
|
+
export const CSRF_COOKIE_BASE = "kumiko_csrf";
|
|
10
|
+
export const TENANT_COOKIE_BASE = "kumiko_tenant";
|
|
11
|
+
export const HOST_COOKIE_PREFIX = "__Host-";
|
|
12
|
+
export function sessionCookieName(base, options) {
|
|
13
|
+
return options.secure && options.domain === undefined ? `${HOST_COOKIE_PREFIX}${base}` : base;
|
|
14
|
+
}
|
|
15
|
+
export function isHostPrefixedCookieName(name) {
|
|
16
|
+
return name.startsWith(HOST_COOKIE_PREFIX);
|
|
17
|
+
}
|
|
18
|
+
// The tenant cookie is always host-only, so a cookieDomain only affects the
|
|
19
|
+
// auth and csrf names.
|
|
20
|
+
export function resolveSessionCookieNames(options) {
|
|
21
|
+
return {
|
|
22
|
+
auth: sessionCookieName(AUTH_COOKIE_BASE, options),
|
|
23
|
+
csrf: sessionCookieName(CSRF_COOKIE_BASE, options),
|
|
24
|
+
tenant: sessionCookieName(TENANT_COOKIE_BASE, { secure: options.secure }),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Browser-side candidates, preferred first. The client cannot know the server
|
|
28
|
+
// config; when both cookies exist the prefixed one is the trustworthy one.
|
|
29
|
+
export function sessionCookieNameCandidates(base) {
|
|
30
|
+
return [sessionCookieName(base, { secure: true }), sessionCookieName(base, { secure: false })];
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.361.0",
|
|
4
4
|
"description": "Framework-Type-Definitions für Kumiko — FeatureDefinition, BootCheck-Types und die reinen Engine-Types. Erlaubt Downstream-Konsumenten, gegen die Type-Contracts zu bauen, ohne das ganze Framework-Package zu importieren. Enthaelt keine identitaets-sensitiven Runtime-Werte mehr (Error-Klassen leben seit #1629 in kumiko-framework, Brand-Symbole nutzen Symbol.for) und ist deshalb eine plain dependency, keine peerDependency.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./dist/identifiers.d.ts",
|
|
27
27
|
"default": "./dist/identifiers.js"
|
|
28
28
|
},
|
|
29
|
+
"./session-cookies": {
|
|
30
|
+
"types": "./dist/session-cookies.d.ts",
|
|
31
|
+
"default": "./dist/session-cookies.js"
|
|
32
|
+
},
|
|
29
33
|
"./target-ref": {
|
|
30
34
|
"types": "./dist/target-ref.d.ts",
|
|
31
35
|
"default": "./dist/target-ref.js"
|
|
@@ -293,6 +297,10 @@
|
|
|
293
297
|
"types": "./dist/identifiers.d.ts",
|
|
294
298
|
"default": "./dist/identifiers.js"
|
|
295
299
|
},
|
|
300
|
+
"./session-cookies": {
|
|
301
|
+
"types": "./dist/session-cookies.d.ts",
|
|
302
|
+
"default": "./dist/session-cookies.js"
|
|
303
|
+
},
|
|
296
304
|
"./target-ref": {
|
|
297
305
|
"types": "./dist/target-ref.d.ts",
|
|
298
306
|
"default": "./dist/target-ref.js"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.361.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "EditFieldSpec.prefill",
|
|
6
|
+
"detail": "New EditFieldPrefill, today or currentUser with displayName, email or id, on the object form of EditFieldSpec."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.361.0",
|
|
10
|
+
"type": "improvement",
|
|
11
|
+
"title": "entityEdit screens accept a readOnly condition",
|
|
12
|
+
"detail": "EntityEditScreenDefinition.readOnly is a FieldCondition evaluated against the loaded record. UI only, the server is not affected."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.361.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "EditFieldSpec.embeddedList for read-only cells and fixed rows",
|
|
18
|
+
"detail": "New EmbeddedListEditSpec with readOnlyCells and fixedRows on the object form of EditFieldSpec."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.361.0",
|
|
22
|
+
"type": "improvement",
|
|
23
|
+
"title": "display on embedded select sub-fields",
|
|
24
|
+
"detail": "The select variant of EmbeddedSubFieldDef accepts display radio or dropdown."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"version": "0.361.0",
|
|
28
|
+
"type": "improvement",
|
|
29
|
+
"title": "EntityDefinition.lockedWhen and the executor options unsafeSkipLockGuard and writePurpose",
|
|
30
|
+
"detail": "lockedWhen is a FieldCondition evaluated against the stored row. update and delete on the EventStoreExecutor take unsafeSkipLockGuard to opt one call out, and writePurpose (WritePurposes.retention or erasure) lets the framework system user through. EntityEditScreenDefinition.readOnly is combined with the entity's lockedWhen (either shows the view)."
|
|
31
|
+
},
|
|
2
32
|
{
|
|
3
33
|
"version": "0.357.0",
|
|
4
34
|
"type": "improvement",
|