@cosmicdrift/kumiko-types 0.256.0 → 0.258.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/package.json +1 -1
- package/src/event-store-executor-types.ts +13 -0
- package/src/fields.ts +21 -1
- package/src/handlers.ts +10 -0
- package/src/identifiers.ts +5 -1
- package/src/nav-icon.ts +3 -1
- package/src/screen.ts +80 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.258.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>",
|
|
@@ -6,6 +6,15 @@ import type { EntityId } from "./identifiers";
|
|
|
6
6
|
import type { SearchAdapter } from "./search-adapter";
|
|
7
7
|
import type { TenantDb } from "./tenant-db-types";
|
|
8
8
|
|
|
9
|
+
// Read-time gate for join-row entities (EntityDefinition.parentRef): the
|
|
10
|
+
// registered-entity map the SQL clause checks host candidates against.
|
|
11
|
+
// Lives here (not in db/parent-ref-clause.ts) because the Registry only
|
|
12
|
+
// exists at request time — the same extension-point shape as
|
|
13
|
+
// referenceSearch/referenceSort below (fw#2660/fw#2741).
|
|
14
|
+
export type ParentVisibilityOption = {
|
|
15
|
+
readonly entities: ReadonlyMap<string, EntityDefinition>;
|
|
16
|
+
};
|
|
17
|
+
|
|
9
18
|
// Runs the caller's preSave hooks against changes not yet persisted, for
|
|
10
19
|
// the executor's create()/update() only — restore/delete/forget have no
|
|
11
20
|
// `changes` to shape. See HandlerContext.runPreSave for how this gets bound
|
|
@@ -120,6 +129,9 @@ export type EventStoreExecutor = {
|
|
|
120
129
|
}>;
|
|
121
130
|
readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
|
|
122
131
|
};
|
|
132
|
+
// Join-row read-gate (EntityDefinition.parentRef) — same request-time
|
|
133
|
+
// registry dependency as referenceSearch/referenceSort above.
|
|
134
|
+
readonly parentVisibility?: ParentVisibilityOption;
|
|
123
135
|
},
|
|
124
136
|
) => Promise<CursorResult<Record<string, unknown>>>;
|
|
125
137
|
|
|
@@ -127,5 +139,6 @@ export type EventStoreExecutor = {
|
|
|
127
139
|
payload: { id: EntityId },
|
|
128
140
|
user: SessionUser,
|
|
129
141
|
db: TenantDb,
|
|
142
|
+
options?: { readonly parentVisibility?: ParentVisibilityOption },
|
|
130
143
|
) => Promise<Record<string, unknown> | null>;
|
|
131
144
|
};
|
package/src/fields.ts
CHANGED
|
@@ -255,7 +255,8 @@ export type TextFieldDef = {
|
|
|
255
255
|
readonly filterable?: boolean;
|
|
256
256
|
readonly encrypted?: boolean;
|
|
257
257
|
readonly sensitive?: boolean;
|
|
258
|
-
|
|
258
|
+
/** "password" is a pure render hint (masked input) — no storage semantics. */
|
|
259
|
+
readonly format?: "email" | "url" | "phone" | "password";
|
|
259
260
|
readonly default?: string;
|
|
260
261
|
readonly access?: FieldAccess;
|
|
261
262
|
/** Mehrzeiliger Text — DefaultInput rendert dann ein <textarea> statt
|
|
@@ -903,6 +904,16 @@ export type EntityIndexDef = {
|
|
|
903
904
|
|
|
904
905
|
export type FieldsMap = Readonly<Record<string, FieldDefinition>>;
|
|
905
906
|
|
|
907
|
+
export type ParentRefDef = {
|
|
908
|
+
/** Field holding the name of the host entity this row hangs off. */
|
|
909
|
+
readonly entityTypeField: string;
|
|
910
|
+
/** Field holding the host row's id. */
|
|
911
|
+
readonly entityIdField: string;
|
|
912
|
+
/** Narrow which registered entities may act as host. Omitted means every
|
|
913
|
+
* registered entity is a candidate, which also widens the read-gate's SQL. */
|
|
914
|
+
readonly allowedTypes?: readonly string[];
|
|
915
|
+
};
|
|
916
|
+
|
|
906
917
|
export type EntityDefinition<F extends FieldsMap = FieldsMap> = {
|
|
907
918
|
readonly table?: string;
|
|
908
919
|
readonly fields: F;
|
|
@@ -943,6 +954,15 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap> = {
|
|
|
943
954
|
readonly read?: OwnershipMap;
|
|
944
955
|
readonly write?: OwnershipMap;
|
|
945
956
|
};
|
|
957
|
+
/**
|
|
958
|
+
* Join-row carrier reference. Rows of this entity hang off a host row named
|
|
959
|
+
* by two of the entity's own fields. Both paths derive one visibility gate
|
|
960
|
+
* from this single declaration: write handlers deny with NotFoundError, and
|
|
961
|
+
* list/detail filter in SQL against the host entity's own read path (tenant
|
|
962
|
+
* scope, soft-delete, `access.read` ownership). A host type that names no
|
|
963
|
+
* registered entity is denied.
|
|
964
|
+
*/
|
|
965
|
+
readonly parentRef?: ParentRefDef;
|
|
946
966
|
/**
|
|
947
967
|
* Default-Retention-Policy fuer diese Entity. Tenant-Admin kann via
|
|
948
968
|
* Compliance-Profile + Tenant-Override (Sprint 2) uebersteuern.
|
package/src/handlers.ts
CHANGED
|
@@ -1011,10 +1011,20 @@ export type WriteHandlerDef = {
|
|
|
1011
1011
|
readonly perform?: import("./step").PipelineDef;
|
|
1012
1012
|
};
|
|
1013
1013
|
|
|
1014
|
+
// Marker set by defineEntityQueryHandler on both of its verbs. A string, not
|
|
1015
|
+
// a Symbol(), for the same reason as PAGED_QUERY_HANDLER_BRAND: a symlinked
|
|
1016
|
+
// workspace can evaluate a module twice, and two Symbol() calls would not
|
|
1017
|
+
// compare equal across those instances.
|
|
1018
|
+
export const ENTITY_CONVENTION_QUERY_BRAND = "__kumikoEntityConventionQuery";
|
|
1019
|
+
|
|
1014
1020
|
export type QueryHandlerDef = {
|
|
1015
1021
|
readonly name: string;
|
|
1016
1022
|
readonly schema: ZodType;
|
|
1017
1023
|
readonly handler: QueryHandlerFn;
|
|
1024
|
+
/** Set only by defineEntityQueryHandler, which wires the parent-ref
|
|
1025
|
+
* read-gate. The boot-validator requires it on a `parentRef` entity's
|
|
1026
|
+
* list/detail handler — see boot-validator/parent-ref.ts. */
|
|
1027
|
+
readonly [ENTITY_CONVENTION_QUERY_BRAND]?: true;
|
|
1018
1028
|
readonly access?: AccessRule;
|
|
1019
1029
|
readonly description?: string;
|
|
1020
1030
|
readonly agent?: AgentHandlerHints;
|
package/src/identifiers.ts
CHANGED
|
@@ -13,7 +13,11 @@ export type TenantId = string;
|
|
|
13
13
|
// already holds a TenantId from a trusted source (JWT payload, server
|
|
14
14
|
// config) skips this — the helper is for **untrusted input** crossing
|
|
15
15
|
// the system boundary.
|
|
16
|
-
|
|
16
|
+
//
|
|
17
|
+
// Exported so SQL-side checks (a Postgres `~` regex match) can share the
|
|
18
|
+
// exact same shape instead of growing a second copy that drifts.
|
|
19
|
+
export const UUID_SHAPE_PATTERN = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
20
|
+
const TENANT_ID_REGEX = new RegExp(UUID_SHAPE_PATTERN);
|
|
17
21
|
|
|
18
22
|
// Shared shape check for any UUID-formatted identifier crossing an
|
|
19
23
|
// untrusted boundary (fileRefId, entity ids, ...) — same loose,
|
package/src/nav-icon.ts
CHANGED
|
@@ -86,7 +86,9 @@ export type NavIconKey =
|
|
|
86
86
|
| "info"
|
|
87
87
|
| "check-circle"
|
|
88
88
|
| "x-circle"
|
|
89
|
-
| "loader"
|
|
89
|
+
| "loader"
|
|
90
|
+
| "mic"
|
|
91
|
+
| "circle-stop";
|
|
90
92
|
|
|
91
93
|
// Alias kept because the union stopped being nav-only — actions and fields
|
|
92
94
|
// (added on this branch) also key their icons against it.
|
package/src/screen.ts
CHANGED
|
@@ -1129,6 +1129,85 @@ export type ActionFormRedirect = {
|
|
|
1129
1129
|
readonly idFrom: string;
|
|
1130
1130
|
};
|
|
1131
1131
|
|
|
1132
|
+
/** Author-declared field shown once after a mint-form submit — API token,
|
|
1133
|
+
* recovery codes, similar one-time secrets. */
|
|
1134
|
+
export type SecretRevealField = {
|
|
1135
|
+
/** Flat field name in the handler's success payload. */
|
|
1136
|
+
readonly field: string;
|
|
1137
|
+
/** i18n key for this value's label. */
|
|
1138
|
+
readonly label: string;
|
|
1139
|
+
/** "code" (default) renders one monospaced value; "list" renders a
|
|
1140
|
+
* string-array payload one value per line (recovery codes); "qr" renders
|
|
1141
|
+
* the value as a scannable QR code (otpauth://-style enrollment URI) —
|
|
1142
|
+
* platforms without a QR-capable SecretReveal primitive fall back to the
|
|
1143
|
+
* monospaced text display. */
|
|
1144
|
+
readonly display?: "code" | "list" | "qr";
|
|
1145
|
+
/** Default true — offer a copy-to-clipboard control next to the value. */
|
|
1146
|
+
readonly copyable?: boolean;
|
|
1147
|
+
};
|
|
1148
|
+
|
|
1149
|
+
/** Post-reveal proof-of-receipt step. A mint whose effect is only armed once
|
|
1150
|
+
* the user proves they received the secret (TOTP enrollment: scan the code,
|
|
1151
|
+
* then enter one) declares it here — the reveal card renders this form in
|
|
1152
|
+
* place of the bare acknowledge button. */
|
|
1153
|
+
export type SecretMintConfirmStep = {
|
|
1154
|
+
/** Write-handler QN dispatched when the confirm form is submitted. */
|
|
1155
|
+
readonly handler: string;
|
|
1156
|
+
readonly fields: Readonly<Record<string, FieldDefinition>>;
|
|
1157
|
+
readonly layout: EditLayout;
|
|
1158
|
+
/** Mint success-payload fields merged into the confirm payload (e.g. a
|
|
1159
|
+
* short-lived setup token). Held in component state only — never rendered,
|
|
1160
|
+
* never in the URL, a query cache, nav or a persisted draft. */
|
|
1161
|
+
readonly carry?: readonly string[];
|
|
1162
|
+
readonly submitLabel?: string;
|
|
1163
|
+
/** i18n key for the banner shown after a successful confirm when the screen
|
|
1164
|
+
* declares no `redirect`. Default "kumiko.secretMint.done". */
|
|
1165
|
+
readonly doneMessage?: string;
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
export type SecretReveal = {
|
|
1169
|
+
readonly fields: readonly SecretRevealField[];
|
|
1170
|
+
/** i18n key. Default "kumiko.secretMint.title". */
|
|
1171
|
+
readonly title?: string;
|
|
1172
|
+
/** i18n key for the "this is the only time you see it" warning.
|
|
1173
|
+
* Default "kumiko.secretMint.warning". */
|
|
1174
|
+
readonly warning?: string;
|
|
1175
|
+
/** i18n key for the acknowledge button. Default "kumiko.secretMint.confirm". */
|
|
1176
|
+
readonly confirmLabel?: string;
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
/** Mint form → one-time reveal → explicit confirm. The secret (API token,
|
|
1180
|
+
* recovery codes) lives only in the write-handler's own success payload —
|
|
1181
|
+
* no stored value backs it, so no query can redisplay it later. That gap
|
|
1182
|
+
* is why this needs its own screen type instead of a flag on `actionForm`,
|
|
1183
|
+
* which discards the success payload after extracting the navigation id. */
|
|
1184
|
+
export type SecretMintScreenDefinition = {
|
|
1185
|
+
readonly id: string;
|
|
1186
|
+
readonly type: "secretMint";
|
|
1187
|
+
readonly nav?: ScreenNavSugar;
|
|
1188
|
+
readonly detailFor?: string;
|
|
1189
|
+
readonly description?: string;
|
|
1190
|
+
readonly agent?: AgentHandlerHints;
|
|
1191
|
+
/** Write-handler QN dispatched on submit. */
|
|
1192
|
+
readonly handler: string;
|
|
1193
|
+
readonly fields: Readonly<Record<string, FieldDefinition>>;
|
|
1194
|
+
readonly layout: EditLayout;
|
|
1195
|
+
/** Navigate here after the reveal is confirmed. Short id (same-feature) or
|
|
1196
|
+
* a fully-qualified cross-feature screen QN — no result-id navigation
|
|
1197
|
+
* form exists here (unlike `ActionFormScreenDefinition.redirect`), since
|
|
1198
|
+
* the confirmed screen carries no entity id to thread through. */
|
|
1199
|
+
readonly redirect?: string;
|
|
1200
|
+
readonly cancelTarget?: string | false;
|
|
1201
|
+
readonly listScreenId?: string;
|
|
1202
|
+
readonly access?: AccessRule;
|
|
1203
|
+
readonly reveal: SecretReveal;
|
|
1204
|
+
/** i18n-key for the mint button. Default: renderer's own default. */
|
|
1205
|
+
readonly submitLabel?: string;
|
|
1206
|
+
/** Proof-of-receipt step rendered on the reveal card in place of the bare
|
|
1207
|
+
* acknowledge button — see `SecretMintConfirmStep` doc. */
|
|
1208
|
+
readonly confirm?: SecretMintConfirmStep;
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1132
1211
|
// --- custom ---
|
|
1133
1212
|
|
|
1134
1213
|
// Sub-route declared by a custom screen (Expo Router / URL-routing use).
|
|
@@ -1307,4 +1386,5 @@ export type ScreenDefinition =
|
|
|
1307
1386
|
| ActionFormScreenDefinition
|
|
1308
1387
|
| ConfigEditScreenDefinition
|
|
1309
1388
|
| SecretsEditScreenDefinition
|
|
1389
|
+
| SecretMintScreenDefinition
|
|
1310
1390
|
| CustomScreenDefinition;
|