@murumets-ee/entity 0.9.0 → 0.11.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/index.d.mts CHANGED
@@ -74,6 +74,23 @@ interface BaseFieldConfig {
74
74
  translatable?: boolean;
75
75
  indexed?: boolean;
76
76
  unique?: boolean;
77
+ /**
78
+ * Marks the field as **system-managed**: stored as a real column, populated
79
+ * by behavior hooks or trusted server-side transitions, but NOT writable
80
+ * through the public `AdminClient.create/update` surface.
81
+ *
82
+ * - Caller-supplied values for internal fields are silently stripped before
83
+ * hooks run (so an HTTP PATCH cannot poison a workflow state).
84
+ * - `beforeCreate` / `beforeUpdate` hooks may still set them (they run after
85
+ * the strip), and the values are preserved through validation.
86
+ * - Trusted server code that needs to write internals directly — e.g.
87
+ * workflow transitions invoked from an authorized admin route — must use
88
+ * `AdminClient.updateInternal()`, which bypasses the strip.
89
+ *
90
+ * Use this flag on any field added by a behavior whose value represents a
91
+ * controlled state machine (e.g. `_workflowStatus`), not user input.
92
+ */
93
+ internal?: boolean;
77
94
  access?: {
78
95
  view?: string;
79
96
  edit?: string;
@@ -182,6 +199,17 @@ type FieldToTS<F extends FieldConfig> = F extends IdField ? string : F extends T
182
199
  type InferEntityDTO<Fields extends Record<string, FieldConfig>> = {
183
200
  id: string;
184
201
  } & { [K in keyof Fields as K extends 'id' ? never : Fields[K]['required'] extends true ? K : never]: FieldToTS<Fields[K]> } & { [K in keyof Fields as Fields[K]['required'] extends true ? never : K]?: FieldToTS<Fields[K]> | null };
202
+ /** Fields that are auto-generated and should not appear in create input. */
203
+ type AutoGeneratedFields = 'id' | 'createdAt' | 'updatedAt' | 'createdBy' | 'updatedBy' | '_version';
204
+ /**
205
+ * The input type for creating an entity.
206
+ * - Omits auto-generated fields (id, timestamps, version)
207
+ * - Required fields stay required; optional fields stay optional
208
+ */
209
+ type InferCreateInput<Fields extends Record<string, FieldConfig>> = Omit<{ [K in keyof Fields as K extends 'id' ? never : Fields[K]['required'] extends true ? K : never]: FieldToTS<Fields[K]> } & { [K in keyof Fields as Fields[K]['required'] extends true ? never : K]?: FieldToTS<Fields[K]> | null }, AutoGeneratedFields>;
210
+ /** Fields that cannot be changed after creation. */
211
+ type ImmutableFields = 'id' | 'createdAt' | 'createdBy';
212
+ type InferUpdateInput<Fields extends Record<string, FieldConfig>> = { [K in keyof Fields as K extends ImmutableFields ? never : K]?: FieldToTS<Fields[K]> | null };
185
213
  /**
186
214
  * Behavior field types use `type` (not `interface`) because interfaces
187
215
  * lack implicit index signatures needed for Record<string, FieldConfig>.
@@ -237,6 +265,24 @@ type InferEntity<E> = E extends {
237
265
  * from its `contextResolver` and forwarded into every hook so behaviors never
238
266
  * have to reach into AsyncLocalStorage themselves — that pattern breaks under
239
267
  * bundlers (e.g. Turbopack) that duplicate module instances across boundaries.
268
+ *
269
+ * `loadCurrent` is a lazy, memoized loader for the *current* entity row,
270
+ * provided by AdminClient on update/delete codepaths. Hooks that need to
271
+ * validate against the existing state (e.g. workflowable's transition
272
+ * checker) call it; behaviors that don't, don't pay for the round trip.
273
+ * Returns `null` when no entity matches the id (rare — usually means the
274
+ * row was deleted concurrently). On create, `loadCurrent` is undefined.
275
+ *
276
+ * `viaInternal` is `true` when the hook is running on an `AdminClient.updateInternal`
277
+ * call (the trusted server-side path; public PATCH always sets it false).
278
+ * Hooks SHOULD treat this as informational only — the route layer has
279
+ * authorized the *capability*, but the hook is still responsible for
280
+ * enforcing structural invariants. Workflowable, for example, validates
281
+ * the `_workflowStatus` transition table on every update regardless of
282
+ * `viaInternal` so that even a route-layer bug cannot push the workflow
283
+ * row into an illegal state. Use `viaInternal` when a hook genuinely
284
+ * needs to differentiate (e.g. side effects that should only fire when
285
+ * a real user — not the seed loader — initiates the change).
240
286
  */
241
287
  interface BehaviorContext {
242
288
  user?: {
@@ -244,6 +290,8 @@ interface BehaviorContext {
244
290
  name?: string;
245
291
  email?: string;
246
292
  };
293
+ loadCurrent?: () => Promise<Record<string, unknown> | null>;
294
+ viaInternal?: boolean;
247
295
  }
248
296
  interface Behavior<F extends Record<string, FieldConfig> = {}> {
249
297
  name: string;
@@ -404,10 +452,22 @@ declare const behavior: {
404
452
  interface CountCacheLike {
405
453
  get(key: string): number | undefined;
406
454
  set(key: string, count: number): void;
455
+ /**
456
+ * Cache-or-compute with single-flight semantics.
457
+ *
458
+ * If the entry is cached and unexpired, returns it immediately (no promise
459
+ * allocation in the hot path). Otherwise, if a refresh is already in flight
460
+ * for the same key, returns that in-flight promise instead of starting a
461
+ * second one — preventing the thundering-herd burst of identical
462
+ * `count(*)` queries when N concurrent requests cross the TTL boundary
463
+ * together.
464
+ */
465
+ getOrCompute(key: string, compute: () => Promise<number>): Promise<number> | number;
407
466
  invalidate(prefix: string): void;
408
467
  }
409
468
  declare class CountCache implements CountCacheLike {
410
469
  private cache;
470
+ private inflight;
411
471
  private ttlMs;
412
472
  constructor(ttlMs?: number);
413
473
  /**
@@ -419,10 +479,23 @@ declare class CountCache implements CountCacheLike {
419
479
  */
420
480
  set(key: string, count: number): void;
421
481
  /**
422
- * Invalidate all cache entries whose key starts with the given prefix.
423
- * Typically called with the entity name after mutations (create/update/delete).
482
+ * Cache-or-compute with single-flight semantics. See `CountCacheLike` doc.
483
+ *
484
+ * Cache hits return synchronously (the call site keeps the await but no
485
+ * actual microtask is scheduled). Cache misses register an in-flight
486
+ * promise so concurrent callers share one DB round-trip; the promise is
487
+ * removed once it resolves (or rejects) so the next miss can refresh.
488
+ * On rejection, nothing is cached — the next caller retries.
424
489
  */
425
- invalidate(prefix: string): void;
490
+ getOrCompute(key: string, compute: () => Promise<number>): Promise<number> | number;
491
+ /**
492
+ * Invalidate all cache entries for a given entity name.
493
+ * Matches the exact entity name OR any key where the entity is followed
494
+ * by one of the cache-key separators (`:` for WHERE, `@` for scope).
495
+ * Avoids accidental cross-entity invalidation (e.g. invalidating 'user'
496
+ * must not clear 'users:where=...' entries).
497
+ */
498
+ invalidate(entityName: string): void;
426
499
  /**
427
500
  * Remove all expired entries. Useful for periodic cleanup in long-running processes.
428
501
  */
@@ -584,42 +657,6 @@ declare function defineEntity<F extends Record<string, FieldConfig>, const B ext
584
657
  id: IdField;
585
658
  } & ExtractBehaviorFields<B> & F>;
586
659
  //#endregion
587
- //#region src/shared/entity-data-ops.d.ts
588
- /**
589
- * Security context resolved per-request. Provided by the consumer
590
- * (e.g., via React.cache() in Next.js, or runAsCli for CLI).
591
- * The entity package has zero knowledge of how this is resolved.
592
- */
593
- interface SecurityContext {
594
- user: {
595
- id: string;
596
- groups: string[];
597
- name?: string;
598
- email?: string;
599
- };
600
- checker: (role: string, resource: string, action: string) => boolean;
601
- scope?: {
602
- type: string;
603
- id: string;
604
- };
605
- }
606
- /**
607
- * Function that resolves the current request's security context.
608
- * Injected at construction time — the entity package never imports
609
- * @murumets-ee/core or any framework-specific code.
610
- *
611
- * Returns undefined only when intentionally skipping enforcement
612
- * (should not happen in production — throw ForbiddenError instead).
613
- */
614
- type ContextResolver = () => SecurityContext | undefined | Promise<SecurityContext | undefined>;
615
- /**
616
- * Thrown when a user lacks permission for an entity operation.
617
- * Consumers (api-handler) catch this and return HTTP 403.
618
- */
619
- declare class ForbiddenError extends Error {
620
- constructor(message: string);
621
- }
622
- //#endregion
623
660
  //#region src/refs/find-usages.d.ts
624
661
  interface EntityUsage {
625
662
  sourceEntity: string;
@@ -843,6 +880,15 @@ declare function generateLayoutSchema(entity: Entity, parent?: PgTable): _$drizz
843
880
  };
844
881
  dialect: "pg";
845
882
  }> | null;
883
+ /**
884
+ * Check whether an entity has a behavior with the given name.
885
+ *
886
+ * Single source of truth replacing per-behavior predicates that used to
887
+ * inline the `entity.behaviors?.some(b => b.name === 'X') ?? false`
888
+ * pattern across the codebase. Use this whenever you need to branch on a
889
+ * behavior's presence — admin UI, schema generation, route dispatch, etc.
890
+ */
891
+ declare function hasBehavior(entity: Entity, name: string): boolean;
846
892
  /**
847
893
  * Check if an entity has the versionable behavior
848
894
  */
@@ -856,8 +902,18 @@ declare function needsLocaleStatus(entity: Entity): boolean;
856
902
  * Check if an entity is publishable (has publishable() behavior).
857
903
  */
858
904
  declare function isPublishable(entity: Entity): boolean;
859
- /** Per-locale publish status table for publishable + translatable entities. */
860
- declare function generateLocaleStatusSchema(entity: Entity, parent: PgTable): _$drizzle_orm_pg_core0.PgTableWithColumns<{
905
+ /**
906
+ * Check if an entity has blocks fields with translatable block content (non-localized mode).
907
+ */
908
+ declare function hasTranslatableBlocks(entity: Entity): boolean;
909
+ /**
910
+ * Generate layout translation table for non-localized blocks with translatable fields.
911
+ *
912
+ * `layoutParent` is optional — when provided, emits a `.references()` FK
913
+ * pointing at the corresponding `{entity}_layout` table (for migration
914
+ * generation).
915
+ */
916
+ declare function generateLayoutTranslationSchema(entity: Entity, layoutParent?: PgTable): _$drizzle_orm_pg_core0.PgTableWithColumns<{
861
917
  name: string;
862
918
  schema: undefined;
863
919
  columns: {
@@ -878,8 +934,8 @@ declare function generateLocaleStatusSchema(entity: Entity, parent: PgTable): _$
878
934
  identity: undefined;
879
935
  generated: undefined;
880
936
  }, {}, {}>;
881
- entityId: _$drizzle_orm_pg_core0.PgColumn<{
882
- name: "entity_id";
937
+ layoutId: _$drizzle_orm_pg_core0.PgColumn<{
938
+ name: "layout_id";
883
939
  tableName: string;
884
940
  dataType: "string";
885
941
  columnType: "PgUUID";
@@ -914,625 +970,8 @@ declare function generateLocaleStatusSchema(entity: Entity, parent: PgTable): _$
914
970
  }, {}, {
915
971
  length: 10;
916
972
  }>;
917
- status: _$drizzle_orm_pg_core0.PgColumn<{
918
- name: "status";
919
- tableName: string;
920
- dataType: "string";
921
- columnType: "PgVarchar";
922
- data: string;
923
- driverParam: string;
924
- notNull: true;
925
- hasDefault: true;
926
- isPrimaryKey: false;
927
- isAutoincrement: false;
928
- hasRuntimeDefault: false;
929
- enumValues: [string, ...string[]];
930
- baseColumn: never;
931
- identity: undefined;
932
- generated: undefined;
933
- }, {}, {
934
- length: 20;
935
- }>;
936
- publishedAt: _$drizzle_orm_pg_core0.PgColumn<{
937
- name: "published_at";
938
- tableName: string;
939
- dataType: "date";
940
- columnType: "PgTimestamp";
941
- data: Date;
942
- driverParam: string;
943
- notNull: false;
944
- hasDefault: false;
945
- isPrimaryKey: false;
946
- isAutoincrement: false;
947
- hasRuntimeDefault: false;
948
- enumValues: undefined;
949
- baseColumn: never;
950
- identity: undefined;
951
- generated: undefined;
952
- }, {}, {}>;
953
- };
954
- dialect: "pg";
955
- }>;
956
- /** Drafts overlay table for publishable entities. */
957
- declare function generateDraftsSchema(entity: Entity, parent: PgTable): _$drizzle_orm_pg_core0.PgTableWithColumns<{
958
- name: string;
959
- schema: undefined;
960
- columns: {
961
- id: _$drizzle_orm_pg_core0.PgColumn<{
962
- name: "id";
963
- tableName: string;
964
- dataType: "string";
965
- columnType: "PgUUID";
966
- data: string;
967
- driverParam: string;
968
- notNull: true;
969
- hasDefault: true;
970
- isPrimaryKey: true;
971
- isAutoincrement: false;
972
- hasRuntimeDefault: false;
973
- enumValues: undefined;
974
- baseColumn: never;
975
- identity: undefined;
976
- generated: undefined;
977
- }, {}, {}>;
978
- entityId: _$drizzle_orm_pg_core0.PgColumn<{
979
- name: "entity_id";
980
- tableName: string;
981
- dataType: "string";
982
- columnType: "PgUUID";
983
- data: string;
984
- driverParam: string;
985
- notNull: true;
986
- hasDefault: false;
987
- isPrimaryKey: false;
988
- isAutoincrement: false;
989
- hasRuntimeDefault: false;
990
- enumValues: undefined;
991
- baseColumn: never;
992
- identity: undefined;
993
- generated: undefined;
994
- }, {}, {}>;
995
- locale: _$drizzle_orm_pg_core0.PgColumn<{
996
- name: "locale";
997
- tableName: string;
998
- dataType: "string";
999
- columnType: "PgVarchar";
1000
- data: string;
1001
- driverParam: string;
1002
- notNull: true;
1003
- hasDefault: true;
1004
- isPrimaryKey: false;
1005
- isAutoincrement: false;
1006
- hasRuntimeDefault: false;
1007
- enumValues: [string, ...string[]];
1008
- baseColumn: never;
1009
- identity: undefined;
1010
- generated: undefined;
1011
- }, {}, {
1012
- length: 10;
1013
- }>;
1014
- data: _$drizzle_orm_pg_core0.PgColumn<{
1015
- name: "data";
1016
- tableName: string;
1017
- dataType: "json";
1018
- columnType: "PgJsonb";
1019
- data: unknown;
1020
- driverParam: unknown;
1021
- notNull: true;
1022
- hasDefault: false;
1023
- isPrimaryKey: false;
1024
- isAutoincrement: false;
1025
- hasRuntimeDefault: false;
1026
- enumValues: undefined;
1027
- baseColumn: never;
1028
- identity: undefined;
1029
- generated: undefined;
1030
- }, {}, {}>;
1031
- createdBy: _$drizzle_orm_pg_core0.PgColumn<{
1032
- name: "created_by";
1033
- tableName: string;
1034
- dataType: "string";
1035
- columnType: "PgVarchar";
1036
- data: string;
1037
- driverParam: string;
1038
- notNull: true;
1039
- hasDefault: false;
1040
- isPrimaryKey: false;
1041
- isAutoincrement: false;
1042
- hasRuntimeDefault: false;
1043
- enumValues: [string, ...string[]];
1044
- baseColumn: never;
1045
- identity: undefined;
1046
- generated: undefined;
1047
- }, {}, {
1048
- length: 255;
1049
- }>;
1050
- createdByName: _$drizzle_orm_pg_core0.PgColumn<{
1051
- name: "created_by_name";
1052
- tableName: string;
1053
- dataType: "string";
1054
- columnType: "PgVarchar";
1055
- data: string;
1056
- driverParam: string;
1057
- notNull: false;
1058
- hasDefault: false;
1059
- isPrimaryKey: false;
1060
- isAutoincrement: false;
1061
- hasRuntimeDefault: false;
1062
- enumValues: [string, ...string[]];
1063
- baseColumn: never;
1064
- identity: undefined;
1065
- generated: undefined;
1066
- }, {}, {
1067
- length: 255;
1068
- }>;
1069
- createdAt: _$drizzle_orm_pg_core0.PgColumn<{
1070
- name: "created_at";
1071
- tableName: string;
1072
- dataType: "date";
1073
- columnType: "PgTimestamp";
1074
- data: Date;
1075
- driverParam: string;
1076
- notNull: true;
1077
- hasDefault: true;
1078
- isPrimaryKey: false;
1079
- isAutoincrement: false;
1080
- hasRuntimeDefault: false;
1081
- enumValues: undefined;
1082
- baseColumn: never;
1083
- identity: undefined;
1084
- generated: undefined;
1085
- }, {}, {}>;
1086
- updatedAt: _$drizzle_orm_pg_core0.PgColumn<{
1087
- name: "updated_at";
1088
- tableName: string;
1089
- dataType: "date";
1090
- columnType: "PgTimestamp";
1091
- data: Date;
1092
- driverParam: string;
1093
- notNull: true;
1094
- hasDefault: true;
1095
- isPrimaryKey: false;
1096
- isAutoincrement: false;
1097
- hasRuntimeDefault: false;
1098
- enumValues: undefined;
1099
- baseColumn: never;
1100
- identity: undefined;
1101
- generated: undefined;
1102
- }, {}, {}>;
1103
- };
1104
- dialect: "pg";
1105
- }>;
1106
- /** Versions history table for versionable entities. */
1107
- declare function generateVersionsSchema(entity: Entity, parent: PgTable): _$drizzle_orm_pg_core0.PgTableWithColumns<{
1108
- name: string;
1109
- schema: undefined;
1110
- columns: {
1111
- id: _$drizzle_orm_pg_core0.PgColumn<{
1112
- name: "id";
1113
- tableName: string;
1114
- dataType: "string";
1115
- columnType: "PgUUID";
1116
- data: string;
1117
- driverParam: string;
1118
- notNull: true;
1119
- hasDefault: true;
1120
- isPrimaryKey: true;
1121
- isAutoincrement: false;
1122
- hasRuntimeDefault: false;
1123
- enumValues: undefined;
1124
- baseColumn: never;
1125
- identity: undefined;
1126
- generated: undefined;
1127
- }, {}, {}>;
1128
- entityId: _$drizzle_orm_pg_core0.PgColumn<{
1129
- name: "entity_id";
1130
- tableName: string;
1131
- dataType: "string";
1132
- columnType: "PgUUID";
1133
- data: string;
1134
- driverParam: string;
1135
- notNull: true;
1136
- hasDefault: false;
1137
- isPrimaryKey: false;
1138
- isAutoincrement: false;
1139
- hasRuntimeDefault: false;
1140
- enumValues: undefined;
1141
- baseColumn: never;
1142
- identity: undefined;
1143
- generated: undefined;
1144
- }, {}, {}>;
1145
- version: _$drizzle_orm_pg_core0.PgColumn<{
1146
- name: "version";
1147
- tableName: string;
1148
- dataType: "number";
1149
- columnType: "PgInteger";
1150
- data: number;
1151
- driverParam: string | number;
1152
- notNull: true;
1153
- hasDefault: false;
1154
- isPrimaryKey: false;
1155
- isAutoincrement: false;
1156
- hasRuntimeDefault: false;
1157
- enumValues: undefined;
1158
- baseColumn: never;
1159
- identity: undefined;
1160
- generated: undefined;
1161
- }, {}, {}>;
1162
- locale: _$drizzle_orm_pg_core0.PgColumn<{
1163
- name: "locale";
1164
- tableName: string;
1165
- dataType: "string";
1166
- columnType: "PgVarchar";
1167
- data: string;
1168
- driverParam: string;
1169
- notNull: true;
1170
- hasDefault: true;
1171
- isPrimaryKey: false;
1172
- isAutoincrement: false;
1173
- hasRuntimeDefault: false;
1174
- enumValues: [string, ...string[]];
1175
- baseColumn: never;
1176
- identity: undefined;
1177
- generated: undefined;
1178
- }, {}, {
1179
- length: 10;
1180
- }>;
1181
- data: _$drizzle_orm_pg_core0.PgColumn<{
1182
- name: "data";
1183
- tableName: string;
1184
- dataType: "json";
1185
- columnType: "PgJsonb";
1186
- data: unknown;
1187
- driverParam: unknown;
1188
- notNull: true;
1189
- hasDefault: false;
1190
- isPrimaryKey: false;
1191
- isAutoincrement: false;
1192
- hasRuntimeDefault: false;
1193
- enumValues: undefined;
1194
- baseColumn: never;
1195
- identity: undefined;
1196
- generated: undefined;
1197
- }, {}, {}>;
1198
- delta: _$drizzle_orm_pg_core0.PgColumn<{
1199
- name: "delta";
1200
- tableName: string;
1201
- dataType: "json";
1202
- columnType: "PgJsonb";
1203
- data: unknown;
1204
- driverParam: unknown;
1205
- notNull: false;
1206
- hasDefault: false;
1207
- isPrimaryKey: false;
1208
- isAutoincrement: false;
1209
- hasRuntimeDefault: false;
1210
- enumValues: undefined;
1211
- baseColumn: never;
1212
- identity: undefined;
1213
- generated: undefined;
1214
- }, {}, {}>;
1215
- status: _$drizzle_orm_pg_core0.PgColumn<{
1216
- name: "status";
1217
- tableName: string;
1218
- dataType: "string";
1219
- columnType: "PgVarchar";
1220
- data: string;
1221
- driverParam: string;
1222
- notNull: false;
1223
- hasDefault: false;
1224
- isPrimaryKey: false;
1225
- isAutoincrement: false;
1226
- hasRuntimeDefault: false;
1227
- enumValues: [string, ...string[]];
1228
- baseColumn: never;
1229
- identity: undefined;
1230
- generated: undefined;
1231
- }, {}, {
1232
- length: 20;
1233
- }>;
1234
- createdBy: _$drizzle_orm_pg_core0.PgColumn<{
1235
- name: "created_by";
1236
- tableName: string;
1237
- dataType: "string";
1238
- columnType: "PgVarchar";
1239
- data: string;
1240
- driverParam: string;
1241
- notNull: false;
1242
- hasDefault: false;
1243
- isPrimaryKey: false;
1244
- isAutoincrement: false;
1245
- hasRuntimeDefault: false;
1246
- enumValues: [string, ...string[]];
1247
- baseColumn: never;
1248
- identity: undefined;
1249
- generated: undefined;
1250
- }, {}, {
1251
- length: 255;
1252
- }>;
1253
- createdByName: _$drizzle_orm_pg_core0.PgColumn<{
1254
- name: "created_by_name";
1255
- tableName: string;
1256
- dataType: "string";
1257
- columnType: "PgVarchar";
1258
- data: string;
1259
- driverParam: string;
1260
- notNull: false;
1261
- hasDefault: false;
1262
- isPrimaryKey: false;
1263
- isAutoincrement: false;
1264
- hasRuntimeDefault: false;
1265
- enumValues: [string, ...string[]];
1266
- baseColumn: never;
1267
- identity: undefined;
1268
- generated: undefined;
1269
- }, {}, {
1270
- length: 255;
1271
- }>;
1272
- createdAt: _$drizzle_orm_pg_core0.PgColumn<{
1273
- name: "created_at";
1274
- tableName: string;
1275
- dataType: "date";
1276
- columnType: "PgTimestamp";
1277
- data: Date;
1278
- driverParam: string;
1279
- notNull: true;
1280
- hasDefault: true;
1281
- isPrimaryKey: false;
1282
- isAutoincrement: false;
1283
- hasRuntimeDefault: false;
1284
- enumValues: undefined;
1285
- baseColumn: never;
1286
- identity: undefined;
1287
- generated: undefined;
1288
- }, {}, {}>;
1289
- isAutosave: _$drizzle_orm_pg_core0.PgColumn<{
1290
- name: "is_autosave";
1291
- tableName: string;
1292
- dataType: "boolean";
1293
- columnType: "PgBoolean";
1294
- data: boolean;
1295
- driverParam: boolean;
1296
- notNull: true;
1297
- hasDefault: true;
1298
- isPrimaryKey: false;
1299
- isAutoincrement: false;
1300
- hasRuntimeDefault: false;
1301
- enumValues: undefined;
1302
- baseColumn: never;
1303
- identity: undefined;
1304
- generated: undefined;
1305
- }, {}, {}>;
1306
- };
1307
- dialect: "pg";
1308
- }>;
1309
- /**
1310
- * Shared content locks table — one per project, independent of any entity.
1311
- * Included exactly once in `buildRuntimeSchema()` when any entity is publishable.
1312
- */
1313
- declare function generateContentLocksSchema(): _$drizzle_orm_pg_core0.PgTableWithColumns<{
1314
- name: "toolkit_content_locks";
1315
- schema: undefined;
1316
- columns: {
1317
- id: _$drizzle_orm_pg_core0.PgColumn<{
1318
- name: "id";
1319
- tableName: "toolkit_content_locks";
1320
- dataType: "string";
1321
- columnType: "PgUUID";
1322
- data: string;
1323
- driverParam: string;
1324
- notNull: true;
1325
- hasDefault: true;
1326
- isPrimaryKey: true;
1327
- isAutoincrement: false;
1328
- hasRuntimeDefault: false;
1329
- enumValues: undefined;
1330
- baseColumn: never;
1331
- identity: undefined;
1332
- generated: undefined;
1333
- }, {}, {}>;
1334
- entityType: _$drizzle_orm_pg_core0.PgColumn<{
1335
- name: "entity_type";
1336
- tableName: "toolkit_content_locks";
1337
- dataType: "string";
1338
- columnType: "PgVarchar";
1339
- data: string;
1340
- driverParam: string;
1341
- notNull: true;
1342
- hasDefault: false;
1343
- isPrimaryKey: false;
1344
- isAutoincrement: false;
1345
- hasRuntimeDefault: false;
1346
- enumValues: [string, ...string[]];
1347
- baseColumn: never;
1348
- identity: undefined;
1349
- generated: undefined;
1350
- }, {}, {
1351
- length: 100;
1352
- }>;
1353
- entityId: _$drizzle_orm_pg_core0.PgColumn<{
1354
- name: "entity_id";
1355
- tableName: "toolkit_content_locks";
1356
- dataType: "string";
1357
- columnType: "PgVarchar";
1358
- data: string;
1359
- driverParam: string;
1360
- notNull: true;
1361
- hasDefault: false;
1362
- isPrimaryKey: false;
1363
- isAutoincrement: false;
1364
- hasRuntimeDefault: false;
1365
- enumValues: [string, ...string[]];
1366
- baseColumn: never;
1367
- identity: undefined;
1368
- generated: undefined;
1369
- }, {}, {
1370
- length: 255;
1371
- }>;
1372
- locale: _$drizzle_orm_pg_core0.PgColumn<{
1373
- name: "locale";
1374
- tableName: "toolkit_content_locks";
1375
- dataType: "string";
1376
- columnType: "PgVarchar";
1377
- data: string;
1378
- driverParam: string;
1379
- notNull: true;
1380
- hasDefault: true;
1381
- isPrimaryKey: false;
1382
- isAutoincrement: false;
1383
- hasRuntimeDefault: false;
1384
- enumValues: [string, ...string[]];
1385
- baseColumn: never;
1386
- identity: undefined;
1387
- generated: undefined;
1388
- }, {}, {
1389
- length: 10;
1390
- }>;
1391
- lockedBy: _$drizzle_orm_pg_core0.PgColumn<{
1392
- name: "locked_by";
1393
- tableName: "toolkit_content_locks";
1394
- dataType: "string";
1395
- columnType: "PgVarchar";
1396
- data: string;
1397
- driverParam: string;
1398
- notNull: true;
1399
- hasDefault: false;
1400
- isPrimaryKey: false;
1401
- isAutoincrement: false;
1402
- hasRuntimeDefault: false;
1403
- enumValues: [string, ...string[]];
1404
- baseColumn: never;
1405
- identity: undefined;
1406
- generated: undefined;
1407
- }, {}, {
1408
- length: 255;
1409
- }>;
1410
- lockedByName: _$drizzle_orm_pg_core0.PgColumn<{
1411
- name: "locked_by_name";
1412
- tableName: "toolkit_content_locks";
1413
- dataType: "string";
1414
- columnType: "PgVarchar";
1415
- data: string;
1416
- driverParam: string;
1417
- notNull: false;
1418
- hasDefault: false;
1419
- isPrimaryKey: false;
1420
- isAutoincrement: false;
1421
- hasRuntimeDefault: false;
1422
- enumValues: [string, ...string[]];
1423
- baseColumn: never;
1424
- identity: undefined;
1425
- generated: undefined;
1426
- }, {}, {
1427
- length: 255;
1428
- }>;
1429
- lockedAt: _$drizzle_orm_pg_core0.PgColumn<{
1430
- name: "locked_at";
1431
- tableName: "toolkit_content_locks";
1432
- dataType: "date";
1433
- columnType: "PgTimestamp";
1434
- data: Date;
1435
- driverParam: string;
1436
- notNull: true;
1437
- hasDefault: true;
1438
- isPrimaryKey: false;
1439
- isAutoincrement: false;
1440
- hasRuntimeDefault: false;
1441
- enumValues: undefined;
1442
- baseColumn: never;
1443
- identity: undefined;
1444
- generated: undefined;
1445
- }, {}, {}>;
1446
- expiresAt: _$drizzle_orm_pg_core0.PgColumn<{
1447
- name: "expires_at";
1448
- tableName: "toolkit_content_locks";
1449
- dataType: "date";
1450
- columnType: "PgTimestamp";
1451
- data: Date;
1452
- driverParam: string;
1453
- notNull: true;
1454
- hasDefault: false;
1455
- isPrimaryKey: false;
1456
- isAutoincrement: false;
1457
- hasRuntimeDefault: false;
1458
- enumValues: undefined;
1459
- baseColumn: never;
1460
- identity: undefined;
1461
- generated: undefined;
1462
- }, {}, {}>;
1463
- };
1464
- dialect: "pg";
1465
- }>;
1466
- /**
1467
- * Check if an entity has blocks fields with translatable block content (non-localized mode).
1468
- */
1469
- declare function hasTranslatableBlocks(entity: Entity): boolean;
1470
- /**
1471
- * Generate layout translation table for non-localized blocks with translatable fields.
1472
- *
1473
- * `layoutParent` is optional — when provided, emits a `.references()` FK
1474
- * pointing at the corresponding `{entity}_layout` table (for migration
1475
- * generation).
1476
- */
1477
- declare function generateLayoutTranslationSchema(entity: Entity, layoutParent?: PgTable): _$drizzle_orm_pg_core0.PgTableWithColumns<{
1478
- name: string;
1479
- schema: undefined;
1480
- columns: {
1481
- id: _$drizzle_orm_pg_core0.PgColumn<{
1482
- name: "id";
1483
- tableName: string;
1484
- dataType: "string";
1485
- columnType: "PgUUID";
1486
- data: string;
1487
- driverParam: string;
1488
- notNull: true;
1489
- hasDefault: true;
1490
- isPrimaryKey: true;
1491
- isAutoincrement: false;
1492
- hasRuntimeDefault: false;
1493
- enumValues: undefined;
1494
- baseColumn: never;
1495
- identity: undefined;
1496
- generated: undefined;
1497
- }, {}, {}>;
1498
- layoutId: _$drizzle_orm_pg_core0.PgColumn<{
1499
- name: "layout_id";
1500
- tableName: string;
1501
- dataType: "string";
1502
- columnType: "PgUUID";
1503
- data: string;
1504
- driverParam: string;
1505
- notNull: true;
1506
- hasDefault: false;
1507
- isPrimaryKey: false;
1508
- isAutoincrement: false;
1509
- hasRuntimeDefault: false;
1510
- enumValues: undefined;
1511
- baseColumn: never;
1512
- identity: undefined;
1513
- generated: undefined;
1514
- }, {}, {}>;
1515
- locale: _$drizzle_orm_pg_core0.PgColumn<{
1516
- name: "locale";
1517
- tableName: string;
1518
- dataType: "string";
1519
- columnType: "PgVarchar";
1520
- data: string;
1521
- driverParam: string;
1522
- notNull: true;
1523
- hasDefault: false;
1524
- isPrimaryKey: false;
1525
- isAutoincrement: false;
1526
- hasRuntimeDefault: false;
1527
- enumValues: [string, ...string[]];
1528
- baseColumn: never;
1529
- identity: undefined;
1530
- generated: undefined;
1531
- }, {}, {
1532
- length: 10;
1533
- }>;
1534
- fields: _$drizzle_orm_pg_core0.PgColumn<{
1535
- name: "fields";
973
+ fields: _$drizzle_orm_pg_core0.PgColumn<{
974
+ name: "fields";
1536
975
  tableName: string;
1537
976
  dataType: "json";
1538
977
  columnType: "PgJsonb";
@@ -1567,5 +1006,41 @@ declare function generateLayoutTranslationSchema(entity: Entity, layoutParent?:
1567
1006
  */
1568
1007
  declare function buildEntitySchemaMap(entities: Entity[]): Record<string, PgTable>;
1569
1008
  //#endregion
1570
- export { type AuditableFields, type Behavior, type BehaviorContext, type BlockDefinitionRef, type BlocksField, type BooleanField, type ContextResolver, CountCache, type CountCacheLike, type CursorInput, type DateField, type Entity, type EntityAdminConfig, type EntityDefinition, type EntityInput, type EntityUsage, type FieldConfig, type FieldToTS, ForbiddenError, type IdField, type InferEntity, type InferEntityDTO, type JsonField, type JsonValue, type MediaField, type NumberField, type PublishableFields, type ReferenceField, ReferencedEntityError, type RichTextField, type SecurityContext, type SelectField, type SlugField, type TextField, auditable, behavior, buildCursorCondition, buildEntitySchemaMap, decodeCursor, defineEntity, encodeCursor, estimateRowCount, field, generateContentLocksSchema, generateDraftsSchema, generateLayoutSchema, generateLayoutTranslationSchema, generateLocaleStatusSchema, generateSchema, generateTranslationSchema, generateVersionsSchema, hasBlocksFields, hasTranslatableBlocks, hierarchical, isPublishable, isVersionable, needsLocaleStatus, publishable, revisionable, sluggable, slugify, timestamped };
1009
+ //#region src/shared/entity-data-ops.d.ts
1010
+ /**
1011
+ * Security context resolved per-request. Provided by the consumer
1012
+ * (e.g., via React.cache() in Next.js, or runAsCli for CLI).
1013
+ * The entity package has zero knowledge of how this is resolved.
1014
+ */
1015
+ interface SecurityContext {
1016
+ user: {
1017
+ id: string;
1018
+ groups: string[];
1019
+ name?: string;
1020
+ email?: string;
1021
+ };
1022
+ checker: (role: string, resource: string, action: string) => boolean;
1023
+ scope?: {
1024
+ type: string;
1025
+ id: string;
1026
+ };
1027
+ }
1028
+ /**
1029
+ * Function that resolves the current request's security context.
1030
+ * Injected at construction time — the entity package never imports
1031
+ * @murumets-ee/core or any framework-specific code.
1032
+ *
1033
+ * Returns undefined only when intentionally skipping enforcement
1034
+ * (should not happen in production — throw ForbiddenError instead).
1035
+ */
1036
+ type ContextResolver = () => SecurityContext | undefined | Promise<SecurityContext | undefined>;
1037
+ /**
1038
+ * Thrown when a user lacks permission for an entity operation.
1039
+ * Consumers (api-handler) catch this and return HTTP 403.
1040
+ */
1041
+ declare class ForbiddenError extends Error {
1042
+ constructor(message: string);
1043
+ }
1044
+ //#endregion
1045
+ export { type AuditableFields, type Behavior, type BehaviorContext, type BlockDefinitionRef, type BlocksField, type BooleanField, type ContextResolver, CountCache, type CountCacheLike, type CursorInput, type DateField, type Entity, type EntityAdminConfig, type EntityDefinition, type EntityInput, type EntityUsage, type FieldConfig, type FieldToTS, ForbiddenError, type IdField, type InferCreateInput, type InferEntity, type InferEntityDTO, type InferUpdateInput, type JsonField, type JsonValue, type MediaField, type NumberField, type PublishableFields, type ReferenceField, ReferencedEntityError, type RichTextField, type SecurityContext, type SelectField, type SlugField, type TextField, auditable, behavior, buildCursorCondition, buildEntitySchemaMap, decodeCursor, defineEntity, encodeCursor, estimateRowCount, field, generateLayoutSchema, generateLayoutTranslationSchema, generateSchema, generateTranslationSchema, hasBehavior, hasBlocksFields, hasTranslatableBlocks, hierarchical, isPublishable, isVersionable, needsLocaleStatus, publishable, revisionable, sluggable, slugify, timestamped };
1571
1046
  //# sourceMappingURL=index.d.mts.map