@cosmicdrift/kumiko-types 0.399.0 → 0.401.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/entity-row-visibility.d.ts +33 -0
- package/dist/entity-row-visibility.js +1 -0
- package/dist/event-store-executor-types.d.ts +12 -0
- package/dist/field-state-conditions.d.ts +48 -0
- package/dist/field-state-conditions.js +1 -0
- package/dist/fields-derived.d.ts +67 -13
- package/dist/fields-derived.js +10 -17
- package/dist/fields.d.ts +45 -54
- package/dist/fields.js +1 -0
- package/dist/handlers.d.ts +4 -0
- package/dist/screen-entity-edit.d.ts +12 -0
- package/dist/screen-expandable-row.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type ParentRefDef = {
|
|
2
|
+
/** Field holding the name of the host entity this row hangs off. */
|
|
3
|
+
readonly entityTypeField: string;
|
|
4
|
+
/** Field holding the host row's id. */
|
|
5
|
+
readonly entityIdField: string;
|
|
6
|
+
/** Narrow which registered entities may act as host. Omitted means every
|
|
7
|
+
* registered entity is a candidate, which also widens the read-gate's SQL. */
|
|
8
|
+
readonly allowedTypes?: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
export type ReadGateDef = {
|
|
11
|
+
/** Row readable only if at least one live row of `entity` with `field` = row.id is readable
|
|
12
|
+
* through that entity's own parentRef gate (host visibility). */
|
|
13
|
+
readonly anyLink?: {
|
|
14
|
+
readonly entity: string;
|
|
15
|
+
readonly field: string;
|
|
16
|
+
};
|
|
17
|
+
/** Like `anyLink`, but every live link row must be readable, and there must be at least one.
|
|
18
|
+
* For records whose links each widen the audience (a contract plus a contact shared across
|
|
19
|
+
* teams). Not combinable with `anyLink`. */
|
|
20
|
+
readonly allLinks?: {
|
|
21
|
+
readonly entity: string;
|
|
22
|
+
readonly field: string;
|
|
23
|
+
};
|
|
24
|
+
/** Row readable only if the file_ref in `field` is live and readable through its owner: owner
|
|
25
|
+
* row readable (ownership, parentRef, handler gate) and the field read access on the file_ref's
|
|
26
|
+
* fieldName passes. A file owned by this row itself passes this part. */
|
|
27
|
+
readonly fileOwner?: {
|
|
28
|
+
readonly field: string;
|
|
29
|
+
};
|
|
30
|
+
/** Roles that skip `anyLink` / `allLinks` (they also see unlinked rows). `fileOwner` still applies; only
|
|
31
|
+
* the framework SYSTEM user skips both. */
|
|
32
|
+
readonly linkExemptRoles?: readonly string[];
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,10 +4,13 @@ import type { SessionUser, WriteResult } from "./handlers.ts";
|
|
|
4
4
|
import type { DeleteContext, SaveContext } from "./hooks.ts";
|
|
5
5
|
import type { EntityId, TenantId } from "./identifiers.ts";
|
|
6
6
|
import type { ListAggregatePayload, ListAggregateResult } from "./list-aggregate-types.ts";
|
|
7
|
+
import type { SchemaTable } from "./schema-table-types.ts";
|
|
7
8
|
import type { SearchAdapter } from "./search-adapter.ts";
|
|
8
9
|
import type { TenantDb } from "./tenant-db-types.ts";
|
|
9
10
|
export type ParentVisibilityOption = {
|
|
10
11
|
readonly entities: ReadonlyMap<string, EntityDefinition>;
|
|
12
|
+
readonly canReadEntity?: (entityName: string) => boolean;
|
|
13
|
+
readonly resolveTable?: (entityName: string) => SchemaTable;
|
|
11
14
|
};
|
|
12
15
|
export type ParentVisibilityGateOption = {
|
|
13
16
|
readonly parentVisibility?: ParentVisibilityOption;
|
|
@@ -35,6 +38,7 @@ export type ListRuntimeOptions = {
|
|
|
35
38
|
readonly labelField: string;
|
|
36
39
|
}>;
|
|
37
40
|
readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
|
|
41
|
+
readonly resolveTable?: (entityName: string) => SchemaTable;
|
|
38
42
|
};
|
|
39
43
|
readonly referenceSort?: {
|
|
40
44
|
readonly fields: ReadonlyArray<{
|
|
@@ -43,8 +47,16 @@ export type ListRuntimeOptions = {
|
|
|
43
47
|
readonly labelField: string;
|
|
44
48
|
}>;
|
|
45
49
|
readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
|
|
50
|
+
readonly resolveTable?: (entityName: string) => SchemaTable;
|
|
46
51
|
};
|
|
47
52
|
readonly parentVisibility?: ParentVisibilityOption;
|
|
53
|
+
readonly derivedSql?: {
|
|
54
|
+
readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
|
|
55
|
+
readonly resolveTable: (entityName: string) => SchemaTable;
|
|
56
|
+
readonly canReadEntity: (entityName: string) => boolean;
|
|
57
|
+
readonly today: string;
|
|
58
|
+
readonly now: string;
|
|
59
|
+
};
|
|
48
60
|
};
|
|
49
61
|
type ListFilterOption = {
|
|
50
62
|
readonly field: string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type FieldCondition = boolean | {
|
|
2
|
+
readonly field: string;
|
|
3
|
+
readonly eq: unknown;
|
|
4
|
+
} | {
|
|
5
|
+
readonly field: string;
|
|
6
|
+
readonly ne: unknown;
|
|
7
|
+
} | {
|
|
8
|
+
readonly field: string;
|
|
9
|
+
readonly in: readonly unknown[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly field: string;
|
|
12
|
+
readonly notIn: readonly unknown[];
|
|
13
|
+
};
|
|
14
|
+
export type FieldStateConditions = {
|
|
15
|
+
/** Field is shown only while the condition holds on the record values. Hidden values are kept, never dropped. */
|
|
16
|
+
readonly visibleWhen?: Exclude<FieldCondition, boolean>;
|
|
17
|
+
/** Field is required while the condition holds (and the field is visible). Enforced by the server write path, not only the form. */
|
|
18
|
+
readonly requiredWhen?: Exclude<FieldCondition, boolean>;
|
|
19
|
+
};
|
|
20
|
+
/** Value is an array of FileRef ids; `file_refs.fieldName` stays the top-level embedded field name. */
|
|
21
|
+
export type EmbeddedFilesSubFieldProps = {
|
|
22
|
+
readonly maxSize?: string;
|
|
23
|
+
readonly accept?: readonly string[];
|
|
24
|
+
readonly maxCount?: number;
|
|
25
|
+
};
|
|
26
|
+
export type EmbeddedImagesSubFieldProps = EmbeddedFilesSubFieldProps & {
|
|
27
|
+
readonly capture?: "environment" | "user";
|
|
28
|
+
};
|
|
29
|
+
/** A cell computed from other cells of the same row. `from` names the
|
|
30
|
+
* sub-fields the operation reads (order matters for `subtract`). Renderer
|
|
31
|
+
* metadata — the derived value is rounded server- and client-side to the
|
|
32
|
+
* precision its target sub-field in `schema` declares (money → integer,
|
|
33
|
+
* decimal → `scale`) before it's validated. */
|
|
34
|
+
export type EmbeddedDerivedCellDef = {
|
|
35
|
+
/** Row-level condition on a sibling sub-field. When it does not hold for a row, the cell is not
|
|
36
|
+
* derived there: it stays a normal input and the row value is left untouched. */
|
|
37
|
+
readonly when?: Exclude<FieldCondition, boolean>;
|
|
38
|
+
} & ({
|
|
39
|
+
/** `percent` = `from[0] * from[1] / 100` (exactly two sources, e.g. amount and rate). */
|
|
40
|
+
readonly op: "multiply" | "sum" | "subtract" | "percent";
|
|
41
|
+
readonly from: readonly string[];
|
|
42
|
+
} | {
|
|
43
|
+
/** `from` is exactly `[value, min, max]`; bounds are inclusive and each may be missing. */
|
|
44
|
+
readonly op: "inRange";
|
|
45
|
+
readonly from: readonly string[];
|
|
46
|
+
readonly inside: string;
|
|
47
|
+
readonly outside: string;
|
|
48
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/fields-derived.d.ts
CHANGED
|
@@ -1,27 +1,81 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
/** Display type a derived value formats as — drives the column's renderer
|
|
3
|
-
* choice in the view-model, parallel to FieldDefinition["type"].
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
export type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp";
|
|
3
|
+
* choice in the view-model, parallel to FieldDefinition["type"]. `money` is
|
|
4
|
+
* SQL-only and only for a `related` + `first` measure of a money field: the
|
|
5
|
+
* value is the stored money read shape (`{ amount, currency, ... }`), the
|
|
6
|
+
* currency read next to the amount. A JS `derive` cannot return money. */
|
|
7
|
+
export type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp" | "money";
|
|
8
8
|
/** Clock injected into `derive` — never read `Temporal.Now`/`Date` inside a
|
|
9
9
|
* derive body (no-date-api guard + testability). The list-query handler passes
|
|
10
10
|
* the read-time instant; unit tests pass a fixed one. */
|
|
11
11
|
export type DeriveContext = {
|
|
12
12
|
readonly asOf: Temporal.Instant;
|
|
13
13
|
};
|
|
14
|
-
export type
|
|
14
|
+
export type JsDerivedFieldDef = {
|
|
15
15
|
readonly valueType: DerivedValueType;
|
|
16
16
|
/** Pure function of the stored row + clock. Returns the JSON-safe display
|
|
17
17
|
* value (e.g. integer minor units for a currency column, ISO string for a
|
|
18
|
-
* `date`). */
|
|
18
|
+
* `date`). Runs after SQL derived values are on the row. */
|
|
19
19
|
readonly derive: (row: Readonly<Record<string, unknown>>, ctx: DeriveContext) => unknown;
|
|
20
20
|
};
|
|
21
|
+
export type SqlDerivedFieldDef = {
|
|
22
|
+
readonly valueType: DerivedValueType;
|
|
23
|
+
readonly sortable?: boolean;
|
|
24
|
+
readonly sql: DerivedSqlSpec;
|
|
25
|
+
readonly derive?: undefined;
|
|
26
|
+
};
|
|
27
|
+
export type DerivedFieldDef = JsDerivedFieldDef | SqlDerivedFieldDef;
|
|
28
|
+
export type DerivedSqlSpec = {
|
|
29
|
+
readonly kind: "coalesce";
|
|
30
|
+
readonly fields: readonly string[];
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "related";
|
|
33
|
+
/** Same convention as a reference field's `entity` ("feature:entity"). */
|
|
34
|
+
readonly entity: string;
|
|
35
|
+
/** related.<field> = outer.<outer ?? "id"> */
|
|
36
|
+
readonly on: {
|
|
37
|
+
readonly field: string;
|
|
38
|
+
readonly outer?: string;
|
|
39
|
+
};
|
|
40
|
+
readonly measure: DerivedRelatedMeasure;
|
|
41
|
+
/** AND-ed. */
|
|
42
|
+
readonly where?: readonly DerivedCondition[];
|
|
43
|
+
};
|
|
44
|
+
export type DerivedRelatedMeasure = {
|
|
45
|
+
readonly kind: "count";
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "countDistinct";
|
|
48
|
+
readonly field: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: "first";
|
|
51
|
+
readonly field: string;
|
|
52
|
+
readonly orderBy: string;
|
|
53
|
+
readonly direction: "asc" | "desc";
|
|
54
|
+
/** Only for a reference field with labelField: yields the referenced row's label. */
|
|
55
|
+
readonly label?: boolean;
|
|
56
|
+
};
|
|
57
|
+
export type DerivedConditionValue = string | number | boolean | null | readonly (string | number)[] | {
|
|
58
|
+
readonly today: true;
|
|
59
|
+
} | {
|
|
60
|
+
readonly now: true;
|
|
61
|
+
};
|
|
62
|
+
export type DerivedCondition = {
|
|
63
|
+
readonly field: string;
|
|
64
|
+
readonly op: "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "in";
|
|
65
|
+
readonly value: DerivedConditionValue;
|
|
66
|
+
}
|
|
67
|
+
/** from <= today AND (to IS NULL OR to >= today) */
|
|
68
|
+
| {
|
|
69
|
+
readonly activeOn: {
|
|
70
|
+
readonly from: string;
|
|
71
|
+
readonly to: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
export declare function isSqlDerivedField(def: DerivedFieldDef): def is SqlDerivedFieldDef;
|
|
21
75
|
export type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
|
|
22
|
-
/** Client-facing projection of DerivedFieldDef
|
|
23
|
-
* not JSON-safe
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
76
|
+
/** Client-facing projection of DerivedFieldDef: `derive`/`sql` are server-only
|
|
77
|
+
* (not JSON-safe), so the browser schema only carries `valueType` and `sortable`. */
|
|
78
|
+
export type ClientDerivedFieldDef = {
|
|
79
|
+
readonly valueType: DerivedValueType;
|
|
80
|
+
readonly sortable?: true;
|
|
81
|
+
};
|
package/dist/fields-derived.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
// --- Derived (computed) fields ---
|
|
3
3
|
//
|
|
4
|
-
// A derived field is read-time only:
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// rows server-side and a column-header sort round-trips to the server, where
|
|
15
|
-
// `executor.list` sorts/filters/searches over real SQL columns — so a derived
|
|
16
|
-
// field (no column) silently no-ops. There is no client-side sort path. Need a
|
|
17
|
-
// derived value sortable/searchable? Materialize it as a stored field; it then
|
|
18
|
-
// rides the existing `searchable`/`sortable` machinery. Time-dependent values
|
|
19
|
-
// (as-of-today) can't be materialized without a daily re-index anyway.
|
|
20
|
-
export {};
|
|
4
|
+
// A derived field is read-time only: never persisted, no DB column, never in a
|
|
5
|
+
// write schema. It lives in `EntityDefinition.derivedFields`, not in `fields`.
|
|
6
|
+
// JS `derive` fields run in the list handler after the SQL fetch and are
|
|
7
|
+
// display-only. `sql` fields are computed inside the list SELECT as correlated
|
|
8
|
+
// subqueries (identifiers come from registry-validated field names, values are
|
|
9
|
+
// always bound params) and can opt into `sortable`. Time-dependent values stay
|
|
10
|
+
// read-time; neither kind enters the entity cache.
|
|
11
|
+
export function isSqlDerivedField(def) {
|
|
12
|
+
return "sql" in def;
|
|
13
|
+
}
|
package/dist/fields.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
import type { VariantSpec } from "./derivatives-types.ts";
|
|
3
|
+
import type { EmbeddedDerivedCellDef, EmbeddedFilesSubFieldProps, EmbeddedImagesSubFieldProps, FieldCondition, FieldStateConditions } from "./field-state-conditions.ts";
|
|
4
|
+
export type { EmbeddedDerivedCellDef, EmbeddedFilesSubFieldProps, EmbeddedImagesSubFieldProps, FieldCondition, FieldStateConditions, } from "./field-state-conditions.ts";
|
|
5
|
+
import type { ParentRefDef, ReadGateDef } from "./entity-row-visibility.ts";
|
|
3
6
|
import type { DerivedFieldsMap } from "./fields-derived.ts";
|
|
4
7
|
import type { EventUpcastCtx } from "./handlers.ts";
|
|
5
8
|
import type { OwnershipMap } from "./ownership.ts";
|
|
6
9
|
import type { EntityTenancy } from "./tenancy-brand.ts";
|
|
7
|
-
export type { ClientDerivedFieldDef, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, } from "./fields-derived.ts";
|
|
10
|
+
export type { ClientDerivedFieldDef, DeriveContext, DerivedCondition, DerivedConditionValue, DerivedFieldDef, DerivedFieldsMap, DerivedRelatedMeasure, DerivedSqlSpec, DerivedValueType, JsDerivedFieldDef, SqlDerivedFieldDef, } from "./fields-derived.ts";
|
|
11
|
+
export { isSqlDerivedField } from "./fields-derived.ts";
|
|
8
12
|
export type FieldAccess = {
|
|
9
13
|
readonly read?: OwnershipMap | readonly string[];
|
|
10
14
|
readonly write?: OwnershipMap | readonly string[];
|
|
@@ -30,6 +34,7 @@ export type ResolvedPiiFlags = {
|
|
|
30
34
|
* that lives purely in an FK column. */
|
|
31
35
|
readonly subjectRef?: true;
|
|
32
36
|
};
|
|
37
|
+
type PiiAndFieldState = ResolvedPiiFlags & FieldStateConditions;
|
|
33
38
|
export type EntityPiiAbsentSubject = "tenant" | "self";
|
|
34
39
|
export type PersonalSubject = "self" | {
|
|
35
40
|
readonly of: string;
|
|
@@ -137,7 +142,7 @@ export type TextFieldDef = {
|
|
|
137
142
|
readonly multiline?: boolean | {
|
|
138
143
|
readonly rows?: number;
|
|
139
144
|
};
|
|
140
|
-
} &
|
|
145
|
+
} & PiiAndFieldState;
|
|
141
146
|
/**
|
|
142
147
|
* Long-form text content — source code, markdown, blog posts, email
|
|
143
148
|
* templates, and anything that can be megabytes large. Its surface is
|
|
@@ -176,7 +181,7 @@ export type LongTextFieldDef = {
|
|
|
176
181
|
readonly multiline?: boolean | {
|
|
177
182
|
readonly rows?: number;
|
|
178
183
|
};
|
|
179
|
-
} &
|
|
184
|
+
} & PiiAndFieldState;
|
|
180
185
|
export type BooleanFieldDef = {
|
|
181
186
|
readonly type: "boolean";
|
|
182
187
|
readonly description?: string;
|
|
@@ -187,7 +192,7 @@ export type BooleanFieldDef = {
|
|
|
187
192
|
readonly sensitive?: boolean;
|
|
188
193
|
readonly default?: boolean;
|
|
189
194
|
readonly access?: FieldAccess;
|
|
190
|
-
};
|
|
195
|
+
} & FieldStateConditions;
|
|
191
196
|
export declare const SELECT_OPTION_TONES: readonly ["ok", "warn", "bad", "critical", "accent", "neutral"];
|
|
192
197
|
export type SelectOptionTone = (typeof SELECT_OPTION_TONES)[number];
|
|
193
198
|
/** One `optionsQueryPayload` entry: a literal, or `{ field }` = the current value of
|
|
@@ -200,19 +205,6 @@ export declare function isOptionsQueryFieldRef(value: OptionsQueryPayloadValue):
|
|
|
200
205
|
readonly field: string;
|
|
201
206
|
};
|
|
202
207
|
export declare function optionsQueryFieldRefs(payload: OptionsQueryPayload | undefined): string[];
|
|
203
|
-
export type FieldCondition = boolean | {
|
|
204
|
-
readonly field: string;
|
|
205
|
-
readonly eq: unknown;
|
|
206
|
-
} | {
|
|
207
|
-
readonly field: string;
|
|
208
|
-
readonly ne: unknown;
|
|
209
|
-
} | {
|
|
210
|
-
readonly field: string;
|
|
211
|
-
readonly in: readonly unknown[];
|
|
212
|
-
} | {
|
|
213
|
-
readonly field: string;
|
|
214
|
-
readonly notIn: readonly unknown[];
|
|
215
|
-
};
|
|
216
208
|
/** Options of a select field that are only available while `when` holds for the row values. */
|
|
217
209
|
export type SelectConditionalOptions<TOption extends string = string> = {
|
|
218
210
|
readonly options: readonly TOption[];
|
|
@@ -262,7 +254,7 @@ export type SelectFieldDef<TOptions extends readonly string[] = readonly string[
|
|
|
262
254
|
* in no rule are always available. The write path enforces it, the renderer only
|
|
263
255
|
* filters. Only valid on entity fields. */
|
|
264
256
|
readonly conditionalOptions?: readonly SelectConditionalOptions<TOptions[number]>[];
|
|
265
|
-
} &
|
|
257
|
+
} & PiiAndFieldState;
|
|
266
258
|
export type MultiSelectFieldDef<TOptions extends readonly string[] = readonly string[]> = {
|
|
267
259
|
readonly type: "multiSelect";
|
|
268
260
|
readonly description?: string;
|
|
@@ -291,7 +283,7 @@ export type MultiSelectFieldDef<TOptions extends readonly string[] = readonly st
|
|
|
291
283
|
* `hint` is ignored. Static `options` stay authoritative and the write path must
|
|
292
284
|
* enforce the same rule, the renderer only mirrors it. Called with an empty payload. */
|
|
293
285
|
readonly optionsAvailabilityQuery?: string;
|
|
294
|
-
} &
|
|
286
|
+
} & PiiAndFieldState;
|
|
295
287
|
/**
|
|
296
288
|
* Storage: `integer` flag decides the Postgres column type, not just Zod
|
|
297
289
|
* validation. `integer: true` → `integer` column (32-bit, ~±2.1 billion),
|
|
@@ -325,7 +317,7 @@ export type NumberFieldDef = {
|
|
|
325
317
|
* Default `true`. Set `false` for values that read wrong grouped (e.g. a
|
|
326
318
|
* model year: "2021", not "2.021"). Never affects the stored value. */
|
|
327
319
|
readonly grouping?: boolean;
|
|
328
|
-
} &
|
|
320
|
+
} & PiiAndFieldState;
|
|
329
321
|
/**
|
|
330
322
|
* 64-bit integer field for audit counters, byte sizes, event IDs, and other
|
|
331
323
|
* values that can exceed 2^31 (~2.1 billion). Stored as Postgres `bigint`,
|
|
@@ -347,7 +339,7 @@ export type BigIntFieldDef = {
|
|
|
347
339
|
readonly sensitive?: boolean;
|
|
348
340
|
readonly default?: number;
|
|
349
341
|
readonly access?: FieldAccess;
|
|
350
|
-
} &
|
|
342
|
+
} & PiiAndFieldState;
|
|
351
343
|
/**
|
|
352
344
|
* Exact decimal — Postgres `numeric(precision, scale)`. For values that need
|
|
353
345
|
* fractional precision the integer `number` field (32-bit int) and `money`
|
|
@@ -372,7 +364,7 @@ export type DecimalFieldDef = {
|
|
|
372
364
|
readonly sensitive?: boolean;
|
|
373
365
|
readonly default?: number;
|
|
374
366
|
readonly access?: FieldAccess;
|
|
375
|
-
} &
|
|
367
|
+
} & PiiAndFieldState;
|
|
376
368
|
/** Where a money field's currency comes from. `"tenant"` is the
|
|
377
369
|
* tenant-settings bundle's per-tenant currency (`tenant-settings:config:
|
|
378
370
|
* currency`); `"literal"` names a fixed ISO code and
|
|
@@ -405,7 +397,7 @@ export type MoneyFieldDef = {
|
|
|
405
397
|
* (`entity.defaultCurrency ?? "EUR"`); a stored value always keeps its
|
|
406
398
|
* own currency regardless of this declaration. */
|
|
407
399
|
readonly currency?: MoneyCurrencySource;
|
|
408
|
-
};
|
|
400
|
+
} & FieldStateConditions;
|
|
409
401
|
export type ReferenceFieldDef = {
|
|
410
402
|
readonly type: "reference";
|
|
411
403
|
readonly description?: string;
|
|
@@ -460,13 +452,13 @@ export type ReferenceFieldDef = {
|
|
|
460
452
|
* reference then deliberately crosses the tenant boundary after the
|
|
461
453
|
* handover. Use it for an entity that must not move with its host. */
|
|
462
454
|
readonly handover?: "stay";
|
|
463
|
-
} &
|
|
455
|
+
} & PiiAndFieldState;
|
|
464
456
|
export type DefaultCurrency = "EUR" | "USD" | "GBP" | "CHF" | "JPY" | "SEK" | "NOK" | "DKK" | "PLN" | "CZK" | "CAD" | "AUD" | "NZD" | "CNY" | "INR";
|
|
465
457
|
type EmbeddedSubFieldBase = {
|
|
466
458
|
readonly required?: boolean;
|
|
467
459
|
readonly searchable?: boolean;
|
|
468
460
|
readonly access?: FieldAccess;
|
|
469
|
-
};
|
|
461
|
+
} & FieldStateConditions;
|
|
470
462
|
/** `money` is a signed integer amount in minor units (cents); which currency
|
|
471
463
|
* it means is defined by the owning aggregate (a head-level field or tenant
|
|
472
464
|
* config), not per row. Unlike the top-level `money` field (BIGINT + currency
|
|
@@ -487,23 +479,23 @@ export type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & {
|
|
|
487
479
|
/** Same contract as `SelectFieldDef.display`; in a table cell the
|
|
488
480
|
* default stays a dropdown. */
|
|
489
481
|
readonly display?: "radio" | "dropdown";
|
|
482
|
+
readonly optionTones?: {
|
|
483
|
+
readonly [option: string]: SelectOptionTone;
|
|
484
|
+
};
|
|
490
485
|
}) | (EmbeddedSubFieldBase & {
|
|
491
486
|
readonly type: "reference";
|
|
492
487
|
readonly entity: string;
|
|
493
488
|
readonly labelField?: string;
|
|
494
489
|
/** Same contract as `ReferenceFieldDef.optionsQuery`. */
|
|
495
490
|
readonly optionsQuery?: string;
|
|
491
|
+
}) | (EmbeddedSubFieldBase & EmbeddedImagesSubFieldProps & {
|
|
492
|
+
readonly type: "images";
|
|
493
|
+
}) | (EmbeddedSubFieldBase & EmbeddedFilesSubFieldProps & {
|
|
494
|
+
readonly type: "files";
|
|
496
495
|
});
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
* precision its target sub-field in `schema` declares (money → integer,
|
|
501
|
-
* decimal → `scale`) before it's validated. */
|
|
502
|
-
export type EmbeddedDerivedCellDef = {
|
|
503
|
-
/** `percent` = `from[0] * from[1] / 100` (exactly two sources, e.g. amount and rate). */
|
|
504
|
-
readonly op: "multiply" | "sum" | "subtract" | "percent";
|
|
505
|
-
readonly from: readonly string[];
|
|
506
|
-
};
|
|
496
|
+
export type EmbeddedFileSubFieldDef = Extract<EmbeddedSubFieldDef, {
|
|
497
|
+
type: "images" | "files";
|
|
498
|
+
}>;
|
|
507
499
|
export type EmbeddedTotalDef = string | {
|
|
508
500
|
readonly label: string;
|
|
509
501
|
readonly from: readonly string[];
|
|
@@ -547,7 +539,7 @@ export type EmbeddedFieldDef = {
|
|
|
547
539
|
* rounded `derived` cells, i.e. "sum-of-rounded" not "round-of-sum" —
|
|
548
540
|
* see the embedded-list totals-matching behavior. */
|
|
549
541
|
readonly totalsMatch?: Readonly<Record<string, string>>;
|
|
550
|
-
} &
|
|
542
|
+
} & PiiAndFieldState;
|
|
551
543
|
export type JsonbFieldDef = {
|
|
552
544
|
readonly type: "jsonb";
|
|
553
545
|
readonly description?: string;
|
|
@@ -561,7 +553,7 @@ export type JsonbFieldDef = {
|
|
|
561
553
|
* run that already read the compromised value. See irreversible-operation-gate. */
|
|
562
554
|
readonly readAsInstruction?: boolean;
|
|
563
555
|
readonly access?: FieldAccess;
|
|
564
|
-
} &
|
|
556
|
+
} & PiiAndFieldState;
|
|
565
557
|
export type JsonbEmptyValue = "object" | "array";
|
|
566
558
|
export type DateFieldDef = {
|
|
567
559
|
readonly type: "date";
|
|
@@ -580,7 +572,7 @@ export type DateFieldDef = {
|
|
|
580
572
|
/** Format/locale override for display and input parsing (for example,
|
|
581
573
|
* "de-DE"). Default: app locale. */
|
|
582
574
|
readonly locale?: string;
|
|
583
|
-
} &
|
|
575
|
+
} & PiiAndFieldState;
|
|
584
576
|
export type TimestampFieldDef = {
|
|
585
577
|
readonly type: "timestamp";
|
|
586
578
|
readonly description?: string;
|
|
@@ -606,14 +598,14 @@ export type TimestampFieldDef = {
|
|
|
606
598
|
/** Format/locale override for display and input parsing. Default: app
|
|
607
599
|
* locale. */
|
|
608
600
|
readonly locale?: string;
|
|
609
|
-
} &
|
|
601
|
+
} & PiiAndFieldState;
|
|
610
602
|
export type TzFieldDef = {
|
|
611
603
|
readonly type: "tz";
|
|
612
604
|
readonly description?: string;
|
|
613
605
|
readonly required?: boolean;
|
|
614
606
|
readonly sensitive?: boolean;
|
|
615
607
|
readonly access?: FieldAccess;
|
|
616
|
-
} &
|
|
608
|
+
} & PiiAndFieldState;
|
|
617
609
|
export type LocatedTimestampFieldDef = {
|
|
618
610
|
readonly type: "locatedTimestamp";
|
|
619
611
|
readonly description?: string;
|
|
@@ -631,7 +623,7 @@ export type LocatedTimestampFieldDef = {
|
|
|
631
623
|
/** Format/locale override for display and input parsing. Default: app
|
|
632
624
|
* locale. */
|
|
633
625
|
readonly locale?: string;
|
|
634
|
-
} &
|
|
626
|
+
} & PiiAndFieldState;
|
|
635
627
|
export type FileFieldDef = {
|
|
636
628
|
readonly type: "file";
|
|
637
629
|
readonly description?: string;
|
|
@@ -642,7 +634,7 @@ export type FileFieldDef = {
|
|
|
642
634
|
readonly anonymousUpload?: boolean;
|
|
643
635
|
readonly accept?: readonly string[];
|
|
644
636
|
readonly access?: FieldAccess;
|
|
645
|
-
} &
|
|
637
|
+
} & PiiAndFieldState;
|
|
646
638
|
export type ImageFieldDef = {
|
|
647
639
|
readonly type: "image";
|
|
648
640
|
readonly description?: string;
|
|
@@ -660,7 +652,7 @@ export type ImageFieldDef = {
|
|
|
660
652
|
* camera instead of the file picker. */
|
|
661
653
|
readonly capture?: "environment" | "user";
|
|
662
654
|
readonly access?: FieldAccess;
|
|
663
|
-
} &
|
|
655
|
+
} & PiiAndFieldState;
|
|
664
656
|
export type FilesFieldDef = {
|
|
665
657
|
readonly type: "files";
|
|
666
658
|
readonly description?: string;
|
|
@@ -675,7 +667,7 @@ export type FilesFieldDef = {
|
|
|
675
667
|
* `maxCount`/`accept`/`maxSize` as hints. The stored value is the same array of FileRef ids. */
|
|
676
668
|
readonly display?: "list" | "dropzone";
|
|
677
669
|
readonly access?: FieldAccess;
|
|
678
|
-
} &
|
|
670
|
+
} & PiiAndFieldState;
|
|
679
671
|
export type ImagesFieldDef = {
|
|
680
672
|
readonly type: "images";
|
|
681
673
|
readonly description?: string;
|
|
@@ -694,7 +686,7 @@ export type ImagesFieldDef = {
|
|
|
694
686
|
* camera instead of the file picker. */
|
|
695
687
|
readonly capture?: "environment" | "user";
|
|
696
688
|
readonly access?: FieldAccess;
|
|
697
|
-
} &
|
|
689
|
+
} & PiiAndFieldState;
|
|
698
690
|
export type FieldDefinition = TextFieldDef | LongTextFieldDef | BooleanFieldDef | SelectFieldDef | MultiSelectFieldDef | NumberFieldDef | BigIntFieldDef | DecimalFieldDef | MoneyFieldDef | ReferenceFieldDef | EmbeddedFieldDef | JsonbFieldDef | DateFieldDef | TimestampFieldDef | TzFieldDef | LocatedTimestampFieldDef | FileFieldDef | ImageFieldDef | FilesFieldDef | ImagesFieldDef;
|
|
699
691
|
export declare const FIELD_TYPE_NAMES: readonly FieldDefinition["type"][];
|
|
700
692
|
/** FieldDefinition as an entity-less inline form screen (actionForm,
|
|
@@ -758,15 +750,7 @@ export type EntityTextFieldDef = TextFieldDef & ResolvedPiiStance;
|
|
|
758
750
|
export type EntityLongTextFieldDef = LongTextFieldDef & ResolvedPiiStance;
|
|
759
751
|
export type EntityFieldDefinition = Exclude<FieldDefinition, TextFieldDef | LongTextFieldDef> | EntityTextFieldDef | EntityLongTextFieldDef;
|
|
760
752
|
export type FieldsMap = Readonly<Record<string, EntityFieldDefinition>>;
|
|
761
|
-
export type ParentRefDef
|
|
762
|
-
/** Field holding the name of the host entity this row hangs off. */
|
|
763
|
-
readonly entityTypeField: string;
|
|
764
|
-
/** Field holding the host row's id. */
|
|
765
|
-
readonly entityIdField: string;
|
|
766
|
-
/** Narrow which registered entities may act as host. Omitted means every
|
|
767
|
-
* registered entity is a candidate, which also widens the read-gate's SQL. */
|
|
768
|
-
readonly allowedTypes?: readonly string[];
|
|
769
|
-
};
|
|
753
|
+
export type { ParentRefDef, ReadGateDef } from "./entity-row-visibility.ts";
|
|
770
754
|
export type EntityEventMigration = {
|
|
771
755
|
readonly fromVersion: number;
|
|
772
756
|
readonly toVersion: number;
|
|
@@ -841,6 +825,13 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
|
|
|
841
825
|
* registered entity is denied.
|
|
842
826
|
*/
|
|
843
827
|
readonly parentRef?: ParentRefDef;
|
|
828
|
+
/**
|
|
829
|
+
* Declarative read gate for rows readable only through other rows (a link to a visible host,
|
|
830
|
+
* a file whose owner is readable). AND-ed with `access.read` and `parentRef` in list, detail,
|
|
831
|
+
* count, aggregate, live signals and the writers' readability check. Without the request-time
|
|
832
|
+
* registry the gate denies every non-system caller.
|
|
833
|
+
*/
|
|
834
|
+
readonly readGate?: ReadGateDef;
|
|
844
835
|
/**
|
|
845
836
|
* Default retention policy for this entity. Tenant administrators can
|
|
846
837
|
* override it through compliance profiles and tenant overrides.
|
package/dist/fields.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
// --- Field Types ---
|
|
3
|
+
export { isSqlDerivedField } from "./fields-derived.js";
|
|
3
4
|
export const SELECT_OPTION_TONES = ["ok", "warn", "bad", "critical", "accent", "neutral"];
|
|
4
5
|
export function isOptionsQueryFieldRef(value) {
|
|
5
6
|
return typeof value === "object" && value !== null;
|
package/dist/handlers.d.ts
CHANGED
|
@@ -117,10 +117,14 @@ export type QueryEvent<TPayload = unknown> = {
|
|
|
117
117
|
readonly payload: TPayload;
|
|
118
118
|
readonly user: SessionUser;
|
|
119
119
|
};
|
|
120
|
+
import type { LifecycleResult } from "./hooks.ts";
|
|
120
121
|
import type { WriteFailure } from "./write-error-info-types.ts";
|
|
121
122
|
export type WriteResult<TData = unknown> = {
|
|
122
123
|
readonly isSuccess: true;
|
|
123
124
|
readonly data: TData;
|
|
125
|
+
/** The executor result behind a handler that answers with its own `data`. Hooks and
|
|
126
|
+
* projections run on it; the dispatcher removes it from what leaves the pipeline. */
|
|
127
|
+
readonly lifecycle?: LifecycleResult;
|
|
124
128
|
} | WriteFailure;
|
|
125
129
|
import type { Registry } from "./feature.ts";
|
|
126
130
|
import type { TenantId } from "./identifiers.ts";
|
|
@@ -79,6 +79,9 @@ export type FieldFillAction = {
|
|
|
79
79
|
export type EmbeddedListEditSpec = {
|
|
80
80
|
/** Sub-fields shown as read-only cells; every other cell stays editable. */
|
|
81
81
|
readonly readOnlyCells?: readonly string[];
|
|
82
|
+
/** Sub-fields that only steer a row (e.g. the kind that decides which controls
|
|
83
|
+
* appear) and are not shown on phone cards; the desktop table still shows them. */
|
|
84
|
+
readonly cardHiddenCells?: readonly string[];
|
|
82
85
|
/** `true` keeps the rows as they are: no add, remove, duplicate, move or
|
|
83
86
|
* paste-appended rows. Cells stay editable (unless in `readOnlyCells`). */
|
|
84
87
|
readonly fixedRows?: boolean;
|
|
@@ -195,6 +198,10 @@ export type EditRelatedListSection = {
|
|
|
195
198
|
readonly id?: string;
|
|
196
199
|
readonly kind: "relatedList";
|
|
197
200
|
readonly title: string;
|
|
201
|
+
/** Rows are aggregates or other values with no record of their own, so they lead nowhere on
|
|
202
|
+
* purpose. Declares that to the screen-conventions guard; with `rowClick`/`rowActions` it is a
|
|
203
|
+
* boot error. */
|
|
204
|
+
readonly readOnly?: boolean;
|
|
198
205
|
/** Fully qualified query QN. Same paged envelope as projectionList.query:
|
|
199
206
|
* `{ rows, nextCursor, total? }`. */
|
|
200
207
|
readonly query: string;
|
|
@@ -282,6 +289,11 @@ export type EditRelatedListSection = {
|
|
|
282
289
|
* Ignored outside tabs mode or when the field's value is not a finite
|
|
283
290
|
* number. */
|
|
284
291
|
readonly countField?: string;
|
|
292
|
+
/** Tab badge counted from this section's own query (`limit: 1, totalCount: true`
|
|
293
|
+
* → `total`), so it matches the tab's rows and stays live. Tabs mode only,
|
|
294
|
+
* exclusive with `countField`; the query's Zod schema must accept `totalCount`
|
|
295
|
+
* and `limit`. */
|
|
296
|
+
readonly count?: true;
|
|
285
297
|
/** Rendered top-right in the section's Card title row — same
|
|
286
298
|
* navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`,
|
|
287
299
|
* evaluated against the parent record. Every action must resolve an icon
|
|
@@ -9,7 +9,7 @@ export type ExpandableRowGrouping = Omit<ListTableGrouping, "field" | "userChoic
|
|
|
9
9
|
* layout and are therefore omitted. `kind` stays the discriminator so further
|
|
10
10
|
* expansion kinds can be added without a breaking change. `table` takes only
|
|
11
11
|
* `grouping`; the other table options belong on the entityList itself. */
|
|
12
|
-
export type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField" | "table" | "bulkActions"> & {
|
|
12
|
+
export type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField" | "count" | "table" | "bulkActions"> & {
|
|
13
13
|
readonly table?: {
|
|
14
14
|
readonly grouping?: ExpandableRowGrouping;
|
|
15
15
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.401.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": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|