@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.
@@ -22,79 +22,18 @@ import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
22
22
  interface CountCacheLike {
23
23
  get(key: string): number | undefined;
24
24
  set(key: string, count: number): void;
25
- invalidate(prefix: string): void;
26
- }
27
- //#endregion
28
- //#region src/cursor.d.ts
29
- /** Cursor input for keyset pagination. */
30
- interface CursorInput {
31
- /** Sort field name (e.g. 'createdAt'). Must be a real column on the entity. */
32
- field: string;
33
- /** Last seen value of the sort field. */
34
- value: string | number;
35
- /** Sort direction — must match the ORDER BY direction. */
36
- direction: 'asc' | 'desc';
37
- /** Tie-breaker: last seen entity ID. Required for non-unique sort fields. */
38
- id?: string;
39
- }
40
- //#endregion
41
- //#region src/admin-config.d.ts
42
- /**
43
- * Optional admin UI configuration for entities.
44
- * Controls how entities appear in the admin sidebar, list pages, and forms.
45
- */
46
- interface EntityAdminConfig {
47
- /** Sidebar section: 'content' | 'structure' | custom string. Default: 'content' */
48
- group?: string;
49
- /** Plural display name for sidebar + list pages. Default: title-cased pluralized entity name */
50
- label?: string;
51
- /** Singular label for "New X" button. Default: title-cased entity name */
52
- labelSingular?: string;
53
- /** Lucide icon name as string, e.g. 'file-text' */
54
- icon?: string;
55
- /** Description shown on list page */
56
- description?: string;
57
- /** Form layout. Default: 'single' */
58
- layout?: 'single' | 'two-column';
59
- /** Fields to hide in the form */
60
- hiddenFields?: string[];
61
- /** Columns to hide in the list */
62
- hiddenColumns?: string[];
63
- /** Per-field label/description/placeholder overrides */
64
- fieldOverrides?: Record<string, {
65
- label?: string;
66
- description?: string;
67
- placeholder?: string;
68
- }>;
69
- /** Default list sort field. Default: 'createdAt' */
70
- defaultSort?: string;
71
- /** Default list sort direction. Default: 'desc' */
72
- defaultSortDirection?: 'asc' | 'desc';
73
- /** List page size. Default: 20 */
74
- pageSize?: number;
75
- /** For block editor: fields to show in Puck root config */
76
- rootFields?: string[];
77
- /** Suppress "New" button in the list page */
78
- disableCreate?: boolean;
79
- /** Order within sidebar group. Default: 0 */
80
- sortOrder?: number;
81
- /**
82
- * Hide the entity from BOTH dashboard and sidebar.
83
- * Use for system entities that should not appear in any UI.
84
- * Shorthand for `hideFromMenu: true` + `hideFromDashboard: true`.
85
- */
86
- hidden?: boolean;
87
25
  /**
88
- * Hide from sidebar menu only. Entity remains accessible via dashboard, direct URL,
89
- * and the entity API. Use for storage models accessed through specialized UI
90
- * (e.g., Ticket entities accessed via the inbox/board, not as a CRUD table).
91
- */
92
- hideFromMenu?: boolean;
93
- /**
94
- * Hide from dashboard widget grid only. Entity still appears in sidebar.
95
- * Use for entities that don't have meaningful counts/stats to show.
26
+ * Cache-or-compute with single-flight semantics.
27
+ *
28
+ * If the entry is cached and unexpired, returns it immediately (no promise
29
+ * allocation in the hot path). Otherwise, if a refresh is already in flight
30
+ * for the same key, returns that in-flight promise instead of starting a
31
+ * second one — preventing the thundering-herd burst of identical
32
+ * `count(*)` queries when N concurrent requests cross the TTL boundary
33
+ * together.
96
34
  */
97
- hideFromDashboard?: boolean;
35
+ getOrCompute(key: string, compute: () => Promise<number>): Promise<number> | number;
36
+ invalidate(prefix: string): void;
98
37
  }
99
38
  //#endregion
100
39
  //#region src/fields/base.d.ts
@@ -108,6 +47,23 @@ interface BaseFieldConfig {
108
47
  translatable?: boolean;
109
48
  indexed?: boolean;
110
49
  unique?: boolean;
50
+ /**
51
+ * Marks the field as **system-managed**: stored as a real column, populated
52
+ * by behavior hooks or trusted server-side transitions, but NOT writable
53
+ * through the public `AdminClient.create/update` surface.
54
+ *
55
+ * - Caller-supplied values for internal fields are silently stripped before
56
+ * hooks run (so an HTTP PATCH cannot poison a workflow state).
57
+ * - `beforeCreate` / `beforeUpdate` hooks may still set them (they run after
58
+ * the strip), and the values are preserved through validation.
59
+ * - Trusted server code that needs to write internals directly — e.g.
60
+ * workflow transitions invoked from an authorized admin route — must use
61
+ * `AdminClient.updateInternal()`, which bypasses the strip.
62
+ *
63
+ * Use this flag on any field added by a behavior whose value represents a
64
+ * controlled state machine (e.g. `_workflowStatus`), not user input.
65
+ */
66
+ internal?: boolean;
111
67
  access?: {
112
68
  view?: string;
113
69
  edit?: string;
@@ -188,12 +144,142 @@ interface BlocksField extends BaseFieldConfig {
188
144
  }
189
145
  type FieldConfig = IdField | TextField | NumberField | BooleanField | DateField | SelectField | ReferenceField | MediaField | RichTextField | SlugField | JsonField | BlocksField;
190
146
  //#endregion
147
+ //#region src/types/infer.d.ts
148
+ /**
149
+ * Recursive JSON-compatible value, for `field.json()` (jsonb column).
150
+ * Mirrors what Postgres jsonb can store: primitives, arrays, or objects.
151
+ */
152
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
153
+ [key: string]: JsonValue;
154
+ };
155
+ /**
156
+ * Maps a single FieldConfig to its TypeScript output type.
157
+ * Each branch is a shallow comparison — no recursion.
158
+ */
159
+ type FieldToTS<F extends FieldConfig> = F extends IdField ? string : F extends TextField ? string : F extends NumberField ? number : F extends BooleanField ? boolean : F extends DateField ? Date | string : F extends SelectField ? F['options'][number] : F extends ReferenceField ? F['cardinality'] extends 'many' ? string[] : string : F extends MediaField ? string : F extends RichTextField ? Record<string, unknown>[] : F extends SlugField ? string : F extends JsonField ? JsonValue : F extends BlocksField ? Array<{
160
+ _block: string;
161
+ _id: string;
162
+ [key: string]: unknown;
163
+ }> : never;
164
+ /**
165
+ * Maps a full field record to its TypeScript output type.
166
+ * Required fields are non-nullable; optional fields are `T | null | undefined`.
167
+ *
168
+ * The `id` field is always `string` and always present.
169
+ * The `id` key from Fields is excluded to avoid duplication since
170
+ * we hardcode `{ id: string }` at the front.
171
+ */
172
+ type InferEntityDTO<Fields extends Record<string, FieldConfig>> = {
173
+ id: string;
174
+ } & { [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 };
175
+ /** Fields that are auto-generated and should not appear in create input. */
176
+ type AutoGeneratedFields = 'id' | 'createdAt' | 'updatedAt' | 'createdBy' | 'updatedBy' | '_version';
177
+ /**
178
+ * The input type for creating an entity.
179
+ * - Omits auto-generated fields (id, timestamps, version)
180
+ * - Required fields stay required; optional fields stay optional
181
+ */
182
+ 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>;
183
+ /** Fields that cannot be changed after creation. */
184
+ type ImmutableFields = 'id' | 'createdAt' | 'createdBy';
185
+ type InferUpdateInput<Fields extends Record<string, FieldConfig>> = { [K in keyof Fields as K extends ImmutableFields ? never : K]?: FieldToTS<Fields[K]> | null };
186
+ //#endregion
187
+ //#region src/cursor.d.ts
188
+ /** Cursor input for keyset pagination. */
189
+ interface CursorInput {
190
+ /** Sort field name (e.g. 'createdAt'). Must be a real column on the entity. */
191
+ field: string;
192
+ /** Last seen value of the sort field. */
193
+ value: string | number;
194
+ /** Sort direction — must match the ORDER BY direction. */
195
+ direction: 'asc' | 'desc';
196
+ /** Tie-breaker: last seen entity ID. Required for non-unique sort fields. */
197
+ id?: string;
198
+ }
199
+ //#endregion
200
+ //#region src/admin-config.d.ts
201
+ /**
202
+ * Optional admin UI configuration for entities.
203
+ * Controls how entities appear in the admin sidebar, list pages, and forms.
204
+ */
205
+ interface EntityAdminConfig {
206
+ /** Sidebar section: 'content' | 'structure' | custom string. Default: 'content' */
207
+ group?: string;
208
+ /** Plural display name for sidebar + list pages. Default: title-cased pluralized entity name */
209
+ label?: string;
210
+ /** Singular label for "New X" button. Default: title-cased entity name */
211
+ labelSingular?: string;
212
+ /** Lucide icon name as string, e.g. 'file-text' */
213
+ icon?: string;
214
+ /** Description shown on list page */
215
+ description?: string;
216
+ /** Form layout. Default: 'single' */
217
+ layout?: 'single' | 'two-column';
218
+ /** Fields to hide in the form */
219
+ hiddenFields?: string[];
220
+ /** Columns to hide in the list */
221
+ hiddenColumns?: string[];
222
+ /** Per-field label/description/placeholder overrides */
223
+ fieldOverrides?: Record<string, {
224
+ label?: string;
225
+ description?: string;
226
+ placeholder?: string;
227
+ }>;
228
+ /** Default list sort field. Default: 'createdAt' */
229
+ defaultSort?: string;
230
+ /** Default list sort direction. Default: 'desc' */
231
+ defaultSortDirection?: 'asc' | 'desc';
232
+ /** List page size. Default: 20 */
233
+ pageSize?: number;
234
+ /** For block editor: fields to show in Puck root config */
235
+ rootFields?: string[];
236
+ /** Suppress "New" button in the list page */
237
+ disableCreate?: boolean;
238
+ /** Order within sidebar group. Default: 0 */
239
+ sortOrder?: number;
240
+ /**
241
+ * Hide the entity from BOTH dashboard and sidebar.
242
+ * Use for system entities that should not appear in any UI.
243
+ * Shorthand for `hideFromMenu: true` + `hideFromDashboard: true`.
244
+ */
245
+ hidden?: boolean;
246
+ /**
247
+ * Hide from sidebar menu only. Entity remains accessible via dashboard, direct URL,
248
+ * and the entity API. Use for storage models accessed through specialized UI
249
+ * (e.g., Ticket entities accessed via the inbox/board, not as a CRUD table).
250
+ */
251
+ hideFromMenu?: boolean;
252
+ /**
253
+ * Hide from dashboard widget grid only. Entity still appears in sidebar.
254
+ * Use for entities that don't have meaningful counts/stats to show.
255
+ */
256
+ hideFromDashboard?: boolean;
257
+ }
258
+ //#endregion
191
259
  //#region src/behaviors/types.d.ts
192
260
  /**
193
261
  * Context passed to behavior hooks. Resolved once per request by AdminClient
194
262
  * from its `contextResolver` and forwarded into every hook so behaviors never
195
263
  * have to reach into AsyncLocalStorage themselves — that pattern breaks under
196
264
  * bundlers (e.g. Turbopack) that duplicate module instances across boundaries.
265
+ *
266
+ * `loadCurrent` is a lazy, memoized loader for the *current* entity row,
267
+ * provided by AdminClient on update/delete codepaths. Hooks that need to
268
+ * validate against the existing state (e.g. workflowable's transition
269
+ * checker) call it; behaviors that don't, don't pay for the round trip.
270
+ * Returns `null` when no entity matches the id (rare — usually means the
271
+ * row was deleted concurrently). On create, `loadCurrent` is undefined.
272
+ *
273
+ * `viaInternal` is `true` when the hook is running on an `AdminClient.updateInternal`
274
+ * call (the trusted server-side path; public PATCH always sets it false).
275
+ * Hooks SHOULD treat this as informational only — the route layer has
276
+ * authorized the *capability*, but the hook is still responsible for
277
+ * enforcing structural invariants. Workflowable, for example, validates
278
+ * the `_workflowStatus` transition table on every update regardless of
279
+ * `viaInternal` so that even a route-layer bug cannot push the workflow
280
+ * row into an illegal state. Use `viaInternal` when a hook genuinely
281
+ * needs to differentiate (e.g. side effects that should only fire when
282
+ * a real user — not the seed loader — initiates the change).
197
283
  */
198
284
  interface BehaviorContext {
199
285
  user?: {
@@ -201,6 +287,8 @@ interface BehaviorContext {
201
287
  name?: string;
202
288
  email?: string;
203
289
  };
290
+ loadCurrent?: () => Promise<Record<string, unknown> | null>;
291
+ viaInternal?: boolean;
204
292
  }
205
293
  interface Behavior<F extends Record<string, FieldConfig> = {}> {
206
294
  name: string;
@@ -266,46 +354,6 @@ interface SecurityContext {
266
354
  */
267
355
  type ContextResolver = () => SecurityContext | undefined | Promise<SecurityContext | undefined>;
268
356
  //#endregion
269
- //#region src/types/infer.d.ts
270
- /**
271
- * Recursive JSON-compatible value, for `field.json()` (jsonb column).
272
- * Mirrors what Postgres jsonb can store: primitives, arrays, or objects.
273
- */
274
- type JsonValue = string | number | boolean | null | JsonValue[] | {
275
- [key: string]: JsonValue;
276
- };
277
- /**
278
- * Maps a single FieldConfig to its TypeScript output type.
279
- * Each branch is a shallow comparison — no recursion.
280
- */
281
- type FieldToTS<F extends FieldConfig> = F extends IdField ? string : F extends TextField ? string : F extends NumberField ? number : F extends BooleanField ? boolean : F extends DateField ? Date | string : F extends SelectField ? F['options'][number] : F extends ReferenceField ? F['cardinality'] extends 'many' ? string[] : string : F extends MediaField ? string : F extends RichTextField ? Record<string, unknown>[] : F extends SlugField ? string : F extends JsonField ? JsonValue : F extends BlocksField ? Array<{
282
- _block: string;
283
- _id: string;
284
- [key: string]: unknown;
285
- }> : never;
286
- /**
287
- * Maps a full field record to its TypeScript output type.
288
- * Required fields are non-nullable; optional fields are `T | null | undefined`.
289
- *
290
- * The `id` field is always `string` and always present.
291
- * The `id` key from Fields is excluded to avoid duplication since
292
- * we hardcode `{ id: string }` at the front.
293
- */
294
- type InferEntityDTO<Fields extends Record<string, FieldConfig>> = {
295
- id: string;
296
- } & { [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 };
297
- /** Fields that are auto-generated and should not appear in create input. */
298
- type AutoGeneratedFields = 'id' | 'createdAt' | 'updatedAt' | 'createdBy' | 'updatedBy' | '_version';
299
- /**
300
- * The input type for creating an entity.
301
- * - Omits auto-generated fields (id, timestamps, version)
302
- * - Required fields stay required; optional fields stay optional
303
- */
304
- 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>;
305
- /** Fields that cannot be changed after creation. */
306
- type ImmutableFields = 'id' | 'createdAt' | 'createdBy';
307
- type InferUpdateInput<Fields extends Record<string, FieldConfig>> = { [K in keyof Fields as K extends ImmutableFields ? never : K]?: FieldToTS<Fields[K]> | null };
308
- //#endregion
309
357
  //#region src/types/logger.d.ts
310
358
  /**
311
359
  * Minimal logger interface compatible with Pino.
@@ -366,6 +414,12 @@ interface FindManyOptions {
366
414
  * The cursor `field` must be a real column on the entity table.
367
415
  */
368
416
  cursor?: CursorInput;
417
+ /**
418
+ * Include fields marked `internal: true` (and legacy `_`-prefixed
419
+ * infrastructure columns) in the returned DTOs. Use only on trusted
420
+ * server-side reads that need to inspect state-machine fields.
421
+ */
422
+ includeInternal?: boolean;
369
423
  }
370
424
  interface CountOptions {
371
425
  where?: SQL | undefined;
@@ -395,22 +449,34 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
395
449
  private entity;
396
450
  private db;
397
451
  private logger?;
452
+ /** Public-surface create schema — internal fields excluded. */
398
453
  private createSchema;
454
+ /** Public-surface update schema — internal fields excluded. */
399
455
  private updateSchema;
456
+ /** Trusted-surface update schema — internal fields included. Used by `updateInternal()`. */
457
+ private updateInternalSchema;
458
+ /** Names of fields marked `internal: true`. Cached for O(1) strip / pick. */
459
+ private internalFieldNames;
400
460
  private table;
401
461
  private countCache?;
402
462
  private contextResolver?;
403
463
  private entityResolver?;
404
464
  /** Shared context for entity-data-ops functions. */
405
465
  private get ctx();
466
+ constructor(config: AdminClientConfig<AllFields>);
406
467
  /**
407
- * Resolve the BehaviorContext passed to lifecycle hooks. Reads the user
408
- * from the configured `contextResolver`; returns `undefined` (not throw)
409
- * when no resolver is configured so behavior tests and bare CRUD calls
410
- * still work security enforcement is the resolver's job, not this helper's.
468
+ * Strip caller-provided values for internal fields from the input.
469
+ *
470
+ * Returns a NEW object does not mutate the caller's input. Hooks run
471
+ * AFTER this strip, so they can still set internal fields legitimately;
472
+ * those values are then preserved through validation by `pickInternalFields`.
411
473
  */
412
- private resolveBehaviorContext;
413
- constructor(config: AdminClientConfig<AllFields>);
474
+ private stripCallerInternals;
475
+ /**
476
+ * Pick the internal-field values that hooks set on `data`.
477
+ * Used to re-attach them after schema validation strips unknown keys.
478
+ */
479
+ private pickInternalFields;
414
480
  /**
415
481
  * Create a new entity
416
482
  *
@@ -424,11 +490,16 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
424
490
  */
425
491
  create(data: InferCreateInput<AllFields>): Promise<InferEntityDTO<AllFields>>;
426
492
  /**
427
- * Find entity by ID
493
+ * Find entity by ID.
494
+ *
495
+ * Pass `includeInternal: true` from trusted server code (workflow
496
+ * transitions, behavior implementations) when you need to read fields
497
+ * marked `internal: true` — by default they are stripped from the DTO.
428
498
  */
429
499
  findById(id: string, options?: {
430
500
  locale?: string;
431
501
  defaultLocale?: string;
502
+ includeInternal?: boolean;
432
503
  }): Promise<InferEntityDTO<AllFields> | null>;
433
504
  /**
434
505
  * Find multiple entities
@@ -452,6 +523,27 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
452
523
  update(id: string, data: InferUpdateInput<AllFields>, options?: {
453
524
  locale?: string;
454
525
  }): Promise<InferEntityDTO<AllFields>>;
526
+ /**
527
+ * Update entity by ID, allowing writes to fields marked `internal: true`.
528
+ *
529
+ * Use this from trusted server code that has already authorized the
530
+ * transition out-of-band — typical example: workflow transitions invoked
531
+ * from an admin route that has already checked the `publish` permission.
532
+ * The HTTP `PATCH` surface uses the public {@link update} method, which
533
+ * silently strips internal fields so untrusted callers cannot poison
534
+ * state-machine values like `_workflowStatus`.
535
+ *
536
+ * The validation schema for this method INCLUDES internal fields — values
537
+ * are still type-checked and constrained (e.g. select-field options).
538
+ *
539
+ * **Security**: never call this from a code path that forwards request body
540
+ * fields directly. The caller must construct the internal-field values
541
+ * server-side from authorized state transitions.
542
+ */
543
+ updateInternal(id: string, data: Record<string, unknown>, options?: {
544
+ locale?: string;
545
+ }): Promise<InferEntityDTO<AllFields>>;
546
+ private updateImpl;
455
547
  /**
456
548
  * Delete entity by ID.
457
549
  *
@@ -500,6 +592,9 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
500
592
  /**
501
593
  * Internal: delete within an existing transaction (used by cascade).
502
594
  * Skips wrapping in a new transaction — the caller already has one.
595
+ *
596
+ * Receives `behaviorCtx` from the caller — auth was already enforced on
597
+ * this entity by the cascade orchestrator, so no extra resolver call here.
503
598
  */
504
599
  private deleteManyInTx;
505
600
  /**
@@ -538,6 +633,7 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
538
633
  */
539
634
  updateMany(where: SQL, data: Partial<InferUpdateInput<AllFields>>, options?: {
540
635
  expressions?: Record<string, SQL>;
636
+ allowInternal?: boolean;
541
637
  }): Promise<number>;
542
638
  /**
543
639
  * Run an aggregate query on this entity's table.
@@ -652,5 +748,5 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
652
748
  private invalidateCountCache;
653
749
  }
654
750
  //#endregion
655
- export { AdminClient, type AdminClientConfig, type CountCacheLike, type CountOptions, type FindManyOptions };
751
+ export { AdminClient, type AdminClientConfig, type CountCacheLike, type CountOptions, type FindManyOptions, type InferCreateInput, type InferEntityDTO, type InferUpdateInput };
656
752
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/count-cache.ts","../../src/cursor.ts","../../src/admin-config.ts","../../src/fields/base.ts","../../src/behaviors/types.ts","../../src/define-entity.ts","../../src/shared/entity-data-ops.ts","../../src/types/infer.ts","../../src/types/logger.ts","../../src/admin/client.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/count-cache.ts","../../src/fields/base.ts","../../src/types/infer.ts","../../src/cursor.ts","../../src/admin-config.ts","../../src/behaviors/types.ts","../../src/define-entity.ts","../../src/shared/entity-data-ops.ts","../../src/types/logger.ts","../../src/admin/client.ts"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{schemaRegistry as e}from"@murumets-ee/db";import{and as t,eq as n,getTableColumns as r,gt as i,inArray as a,isNull as o,lt as s,or as c,sql as l}from"drizzle-orm";import{index as u,pgTable as d,unique as f,uuid as p,varchar as m}from"drizzle-orm/pg-core";import{z as h}from"zod";function g(e,a){let o=r(e),l=o[a.field];if(!l)return null;let u=a.direction===`desc`?s:i,d=u(l,a.value);if(!a.id)return d;let f=o.id;if(!f)return d;let p=u(f,a.id);return c(d,t(n(l,a.value),p))}function _(e,t,n){if(!t)return null;let r={},i=n?.select||Object.keys(e.allFields);for(let a of i){if(!n?.includeInternal&&a.startsWith(`_`))continue;let i=e.allFields[a];i&&i.type!==`blocks`&&(r[a]=t[a])}return r}function v(e,t,n){return t.map(t=>_(e,t,n))}var y=class extends Error{entityName;entityId;usages;constructor(e,t,n){let r=n.length;super(`Cannot delete ${e} '${t}': referenced by ${r} other entit${r===1?`y`:`ies`}`),this.name=`ReferencedEntityError`,this.entityName=e,this.entityId=t,this.usages=n}};const b=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function x(e,t){let n=[],r=new Set;function i(e,t,i){if(!b.test(i))return;let a=`${e}|${t}|${i}`;r.has(a)||(r.add(a),n.push({sourceField:e,targetEntity:t,targetId:i}))}for(let[n,r]of Object.entries(e)){let e=t[n];if(e!=null)switch(r.type){case`media`:typeof e==`string`&&i(n,`media`,e);break;case`reference`:{let t=r;if(t.cardinality===`many`&&Array.isArray(e))for(let r of e)typeof r==`string`&&i(n,t.entity,r);else typeof e==`string`&&i(n,t.entity,e);break}case`blocks`:{let t=r;if(!Array.isArray(e))break;for(let r of e){let e=r,a=e._block;if(!a)continue;let o=t.blocks.find(e=>e.slug===a);if(o)for(let[t,r]of Object.entries(o.fields)){let a=e[t];if(a!=null&&(r.type===`media`&&typeof a==`string`&&i(n,`media`,a),r.type===`reference`)){let e=r;if(e.cardinality===`many`&&Array.isArray(a))for(let t of a)typeof t==`string`&&i(n,e.entity,t);else typeof a==`string`&&i(n,e.entity,a)}}}break}}}return n}const S=d(`entity_refs`,{sourceEntity:m(`source_entity`,{length:100}).notNull(),sourceId:p(`source_id`).notNull(),sourceField:m(`source_field`,{length:100}).notNull(),targetEntity:m(`target_entity`,{length:100}).notNull(),targetId:p(`target_id`).notNull()},e=>[f(`uq_entity_refs`).on(e.sourceEntity,e.sourceId,e.sourceField,e.targetEntity,e.targetId),u(`idx_entity_refs_target`).on(e.targetEntity,e.targetId),u(`idx_entity_refs_source`).on(e.sourceEntity,e.sourceId)]);function C(e){return e.entity.allFields}function w(e){return Object.entries(C(e)).filter(([e,t])=>t.type===`blocks`).map(([e,t])=>({name:e,config:t}))}async function T(r,i,o){if(!i.length)return i;let s=`${r.entity.name}_translations`,c=e.get(s);if(!c)return i;let l=i.map(e=>e.id),u=await r.db.select().from(c).where(t(a(c.entityId,l),n(c.locale,o))),d=Object.entries(C(r)).filter(([e,t])=>t.translatable).map(([e])=>e),f=new Map;for(let e of u){let t={};for(let n of d){let r=e[n];r!=null&&(t[n]=r)}f.set(e.entityId,t)}return i.map(e=>{let t=f.get(e.id);return t?{...e,...t}:e})}async function E(r,i,s,l){let u=w(r);if(u.length===0||i.length===0)return new Map;let d=e.get(`${r.entity.name}_layout`);if(!d)return new Map;let f=u.filter(({config:e})=>!(`localized`in e&&e.localized)),p=u.filter(({config:e})=>`localized`in e&&e.localized),m=new Map;if(f.length>0){let c=await r.db.select().from(d).where(t(a(d.entityId,i),o(d.locale))).orderBy(d.sortOrder),u;if(s&&c.length>0){let i=e.get(`${r.entity.name}_layout_translations`);if(i){let e=c.map(e=>e.id),o=await r.db.select().from(i).where(t(a(i.layoutId,e),n(i.locale,s)));u=new Map;for(let e of o)u.set(e.layoutId,e.fields??{})}}let p;if(l?.strictTranslations){p=new Map;for(let{config:e}of f)if(e.type===`blocks`)for(let t of e.blocks)p.set(t.slug,t.fields)}for(let e of c){let t=e.entityId,n=e.fieldName;m.has(t)||m.set(t,{});let r=m.get(t);r[n]||(r[n]=[]);let i=e.data??{},a=u?.get(e.id),o={_block:e.blockType,_id:e.id,...i,...a??{}};if(s&&p){let t=e.blockType,n=p.get(t);if(n)for(let[e,t]of Object.entries(n))t.translatable&&!a?.[e]&&(o[e]=``)}r[n].push(o)}}if(p.length>0){let e=!s||s===l?.defaultLocale,u=s?e?c(n(d.locale,s),o(d.locale)):n(d.locale,s):o(d.locale),f=await r.db.select().from(d).where(t(a(d.entityId,i),u)).orderBy(d.sortOrder),p=new Map;for(let e of f){let t=`${e.entityId}::${e.fieldName}`;p.has(t)||p.set(t,{localeRows:[],nullRows:[]});let n=p.get(t);e.locale?n.localeRows.push(e):n.nullRows.push(e)}for(let[,{localeRows:e,nullRows:t}]of p){let n=e.length>0?e:t;for(let e of n){let t=e.entityId,n=e.fieldName;m.has(t)||m.set(t,{});let r=m.get(t);r[n]||(r[n]=[]);let i=e.data??{};r[n].push({_block:e.blockType,_id:e.id,...i})}}}return m}function D(e,t,n){let r=w(e);if(r.length!==0)for(let e of t){let t=e.id,i=n.get(t)??{};for(let{name:t}of r)e[t]=i[t]??[]}}function O(e,t,n,r){let i=n?`${n}:${e}`:e;return r&&(i=`${i}@${r}`),t?`${i}:${String(t)}`:i}var k=class extends Error{constructor(e){super(e),this.name=`ForbiddenError`}};async function A(e){if(!e)throw new k(`No context resolver configured on AdminClient/QueryClient. Use createAdminClient() from @murumets-ee/core/clients, or pass a contextResolver in config.`);let t=await e();if(!t)throw new k(`Context resolver returned no context. Ensure auth is configured in your request handler, or use runAsCli() for CLI scripts.`);return t}async function j(e,t,n){let r=await A(t),i=r.user.groups[0];if(!i)throw new k(`User '${r.user.id}' has no role assigned.`);if(!r.checker(i,e.name,n))throw new k(`Forbidden: role '${i}' cannot ${n} '${e.name}'`)}async function M(e,t){if(!(!e.scope||e.scope===`global`))return(await A(t)).scope?.id}function N(e,t){return n(e.table._scopeId,t)}async function P(e,t){if(!e.scope||e.scope===`global`)return;let n=(await A(t)).scope?.id;if(!n)throw new k(`Entity '${e.name}' requires scope '${e.scope}' but no scope is set in context.`);return n}function F(e){let t;switch(e.type){case`id`:t=h.string().uuid();break;case`text`:{let n=h.string();e.maxLength&&(n=n.max(e.maxLength)),e.minLength&&(n=n.min(e.minLength)),e.pattern&&(n=n.regex(e.pattern)),t=n;break}case`number`:{let n=h.number();e.integer&&(n=n.int()),e.min!==void 0&&(n=n.min(e.min)),e.max!==void 0&&(n=n.max(e.max)),t=n;break}case`boolean`:t=h.boolean();break;case`date`:t=h.date().or(h.string().datetime());break;case`select`:t=h.enum(e.options);break;case`reference`:t=e.cardinality===`many`?h.array(h.string().uuid()):h.string().uuid();break;case`media`:t=h.string().uuid();break;case`richtext`:t=h.union([h.string(),h.array(h.record(h.unknown()))]);break;case`slug`:t=h.string().regex(/^[a-z0-9-]+$/);break;case`json`:t=h.union([h.record(h.unknown()),h.array(h.unknown()),h.string(),h.number(),h.boolean()]);break;case`blocks`:t=h.array(h.object({_block:h.string()}).passthrough());break;default:t=h.unknown()}return e.required||(t=t.nullable().optional()),e.default!==void 0&&(t=t.default(e.default)),t}function I(e){let t={},n=new Set([`id`,`createdAt`,`updatedAt`,`createdBy`,`_version`]);for(let[r,i]of Object.entries(e.allFields))n.has(r)||(t[r]=F(i));return h.object(t)}function L(e){let t={},n=new Set([`id`,`createdAt`,`createdBy`,`updatedAt`,`updatedBy`,`_version`]);for(let[r,i]of Object.entries(e.allFields))n.has(r)||(t[r]=F(i).optional());return h.object(t)}var R=class r{entity;db;logger;createSchema;updateSchema;table;countCache;contextResolver;entityResolver;get ctx(){return{entity:this.entity,db:this.db,table:this.table,resolveContext:this.contextResolver}}async resolveBehaviorContext(){if(!this.contextResolver)return;let e=await this.contextResolver();if(e?.user)return{user:{id:e.user.id,name:e.user.name,email:e.user.email}}}constructor(t){if(typeof window<`u`)throw Error(`AdminClient cannot be used in browser code. Use QueryClient for frontend data access.`);this.entity=t.entity,this.db=t.db,this.logger=t.logger,this.countCache=t.countCache,this.contextResolver=t.contextResolver,this.entityResolver=t.entityResolver,this.createSchema=I(t.entity),this.updateSchema=L(t.entity);let n=e.get(t.entity.name);if(!n)throw Error(`Schema for entity '${t.entity.name}' not found in registry. Ensure schemas are generated and registered before creating AdminClient.`);this.table=n}async create(e){this.logger?.info({entity:this.entity.name},`Creating entity`),await j(this.entity,this.contextResolver,`create`);let t=await P(this.entity,this.contextResolver),n=await this.resolveBehaviorContext(),r=e;for(let e of this.entity.behaviors??[])if(e.hooks?.beforeCreate){let t=await e.hooks.beforeCreate(r,n);t!==void 0&&(r=t)}let i={};for(let e of[`createdAt`,`updatedAt`,`createdBy`,`updatedBy`])r[e]!==void 0&&(i[e]=r[e]);r={...this.createSchema.parse(r),...i},t&&(r._scopeId=t);let a=this.prepareDataForInsert(r),[o]=await this.db.insert(this.table).values(a).returning();await this.saveBlocks(o.id,r),await this.syncRefs(o.id,r,`create`);for(let e of this.entity.behaviors??[])e.hooks?.afterCreate&&await e.hooks.afterCreate(o,n);this.invalidateCountCache();let s=_(this.entity,o);if(s&&w(this.ctx).length>0){let e=await E(this.ctx,[s.id],void 0,{strictTranslations:!0});D(this.ctx,[s],e)}return s}async findById(e,r){this.logger?.info({entity:this.entity.name,id:e},`Finding entity by ID`),await j(this.entity,this.contextResolver,`view`);let i=await M(this.entity,this.contextResolver),a=[n(this.table.id,e)];i&&a.push(N(this.ctx,i));let[o]=await this.db.select().from(this.table).where(t(...a));if(!o)return null;let s=_(this.entity,o);if(!s)return null;if(w(this.ctx).length>0){let e=await E(this.ctx,[s.id],r?.locale,{defaultLocale:r?.defaultLocale,strictTranslations:!0});D(this.ctx,[s],e)}if(r?.locale){let[e]=await T(this.ctx,[s],r.locale);return e}return s}async findMany(e){this.logger?.info({entity:this.entity.name,options:e},`Finding entities`),await j(this.entity,this.contextResolver,`view`);let n=await M(this.entity,this.contextResolver),r=this.db.select().from(this.table).$dynamic(),i=[];if(n&&i.push(N(this.ctx,n)),e?.where&&i.push(e.where),e?.cursor){let t=C(this.ctx);if(!(e.cursor.field in t)&&e.cursor.field!==`id`)throw Error(`Invalid cursor field: '${e.cursor.field}' is not a field on '${this.entity.name}'`);let n=g(this.table,e.cursor);n&&i.push(n)}if(i.length>0&&(r=r.where(t(...i))),e?.limit&&(r=r.limit(e.limit)),e?.offset&&!e?.cursor&&(r=r.offset(e.offset)),e?.orderBy){let t=Array.isArray(e.orderBy)?e.orderBy:[e.orderBy];r=r.orderBy(...t)}let a=await r,o=v(this.entity,a).filter(e=>e!==null);if(w(this.ctx).length>0&&o.length>0){let t=o.map(e=>e.id),n=await E(this.ctx,t,e?.locale,{defaultLocale:e?.defaultLocale,strictTranslations:!0});D(this.ctx,o,n)}return e?.locale?await T(this.ctx,o,e.locale):o}async count(e){this.logger?.info({entity:this.entity.name,options:e},`Counting entities`),await j(this.entity,this.contextResolver,`view`);let n=await M(this.entity,this.contextResolver),r=O(this.entity.name,e?.where,void 0,n);if(this.countCache){let e=this.countCache.get(r);if(e!==void 0)return this.logger?.debug?.({entity:this.entity.name,cached:e},`Count cache hit`),e}let i=this.db.select({count:l`count(*)`}).from(this.table).$dynamic(),a=[];n&&a.push(N(this.ctx,n)),e?.where&&a.push(e.where),a.length>0&&(i=i.where(t(...a)));let[o]=await i,s=Number(o.count);return this.countCache&&this.countCache.set(r,s),s}async update(e,r,i){this.logger?.info({entity:this.entity.name,id:e},`Updating entity`),await j(this.entity,this.contextResolver,`update`);let a=await P(this.entity,this.contextResolver),o=await this.resolveBehaviorContext(),s=r;for(let t of this.entity.behaviors??[])if(t.hooks?.beforeUpdate){let n=await t.hooks.beforeUpdate(e,s,o);n!==void 0&&(s=n)}let c={};for(let e of[`updatedAt`,`updatedBy`])s[e]!==void 0&&(c[e]=s[e]);s={...this.updateSchema.parse(s),...c};let l=this.prepareDataForUpdate(s),u=a?t(n(this.table.id,e),N(this.ctx,a)):n(this.table.id,e),d;if(Object.keys(l).length>0){let[e]=await this.db.update(this.table).set(l).where(u).returning();d=e}else{let[e]=await this.db.select().from(this.table).where(u);d=e}await this.saveBlocks(e,s,i),await this.syncRefs(e,s,`update`);for(let e of this.entity.behaviors??[])e.hooks?.afterUpdate&&await e.hooks.afterUpdate(d,o);let f=_(this.entity,d);if(f&&w(this.ctx).length>0){let t=await E(this.ctx,[e],void 0,{strictTranslations:!0});D(this.ctx,[f],t)}return f}async delete(e){this.logger?.info({entity:this.entity.name,id:e},`Deleting entity`),await j(this.entity,this.contextResolver,`delete`);let r=await P(this.entity,this.contextResolver),i=await this.resolveBehaviorContext();if(r){let[i]=await this.db.select({id:this.table.id}).from(this.table).where(t(n(this.table.id,e),N(this.ctx,r)));if(!i)return}await this.db.transaction(async r=>{await this.handleIncomingRefsForDelete(r,[e]);for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,i);await r.delete(this.table).where(n(this.table.id,e)),await r.delete(S).where(t(n(S.sourceEntity,this.entity.name),n(S.sourceId,e)))});for(let t of this.entity.behaviors??[])t.hooks?.afterDelete&&await t.hooks.afterDelete(e,i);this.invalidateCountCache()}async deleteMany(e){this.logger?.info({entity:this.entity.name},`Bulk deleting entities`),await j(this.entity,this.contextResolver,`delete`);let r=await P(this.entity,this.contextResolver),i=await this.resolveBehaviorContext(),o=[e];r&&o.push(N(this.ctx,r));let s=(await this.db.select({id:this.table.id}).from(this.table).where(t(...o))).map(e=>e.id);if(s.length===0)return 0;await this.db.transaction(async e=>{await this.handleIncomingRefsForDelete(e,s);for(let e of s)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,i);await e.delete(this.table).where(a(this.table.id,s)),await e.delete(S).where(t(n(S.sourceEntity,this.entity.name),a(S.sourceId,s)))});for(let e of s)for(let t of this.entity.behaviors??[])t.hooks?.afterDelete&&await t.hooks.afterDelete(e,i);return this.invalidateCountCache(),s.length}static MAX_CASCADE_DEPTH=10;async handleIncomingRefsForDelete(i,o,s=0){if(s>=r.MAX_CASCADE_DEPTH)throw Error(`Cascade delete exceeded maximum depth of ${r.MAX_CASCADE_DEPTH} while deleting '${this.entity.name}'. This likely indicates a circular reference chain.`);let c=await i.select({sourceEntity:S.sourceEntity,sourceId:S.sourceId,sourceField:S.sourceField}).from(S).where(t(n(S.targetEntity,this.entity.name),a(S.targetId,o)));if(c.length===0)return;let l=new Map;for(let e of c){let t=`${e.sourceEntity}:${e.sourceField}`,n=l.get(t);n||(n={sourceEntity:e.sourceEntity,sourceField:e.sourceField,sourceIds:[]},l.set(t,n)),n.sourceIds.push(e.sourceId)}let u=this.entityResolver?.();for(let[,c]of l){let l=u?.get(c.sourceEntity),d=l?.fields[c.sourceField]?.onDelete??`restrict`;if(d===`restrict`)throw new y(this.entity.name,o[0],c.sourceIds.map(e=>({sourceEntity:c.sourceEntity,sourceId:e,sourceField:c.sourceField})));if(d===`cascade`){let n=e.get(c.sourceEntity);if(n&&l){let e=new r({entity:l,db:i,logger:this.logger,contextResolver:this.contextResolver});await j(l,this.contextResolver,`delete`);let o=await M(l,this.contextResolver),u=a(n.id,c.sourceIds);if(o){let e={entity:l,db:i,table:n,resolveContext:this.contextResolver},r=t(u,N(e,o));r&&(u=r)}await e.deleteManyInTx(i,u,s+1)}}else if(d===`set-null`){let r=e.get(c.sourceEntity);if(r&&l&&r[c.sourceField]){await j(l,this.contextResolver,`update`);let e=await M(l,this.contextResolver),o=a(r.id,c.sourceIds);if(e){let n={entity:l,db:i,table:r,resolveContext:this.contextResolver},a=t(o,N(n,e));a&&(o=a)}await i.update(r).set({[c.sourceField]:null}).where(o),await i.delete(S).where(t(n(S.sourceEntity,c.sourceEntity),a(S.sourceId,c.sourceIds),n(S.sourceField,c.sourceField)))}}}}async deleteManyInTx(e,r,i){let o=(await e.select({id:this.table.id}).from(this.table).where(r)).map(e=>e.id);if(o.length===0)return 0;await this.handleIncomingRefsForDelete(e,o,i);let s=await this.resolveBehaviorContext();for(let e of o)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,s);return await e.delete(this.table).where(a(this.table.id,o)),await e.delete(S).where(t(n(S.sourceEntity,this.entity.name),a(S.sourceId,o))),this.invalidateCountCache(),o.length}async updateMany(e,n,r){this.logger?.info({entity:this.entity.name},`Bulk updating entities`),await j(this.entity,this.contextResolver,`update`);let i=await P(this.entity,this.contextResolver),a=this.updateSchema.parse(n),o=this.prepareDataForUpdate(a);if(r?.expressions){let e=C(this.ctx);for(let[t,n]of Object.entries(r.expressions)){if(t in o)throw Error(`updateMany: field '${t}' appears in both \`data\` and \`expressions\`. Choose one — expressions silently overriding validated values is unsafe.`);if(!(t in e)&&t!==`_scopeId`)throw Error(`updateMany.expressions: unknown column '${t}' on entity '${this.entity.name}'`);o[t]=n}}let s=[e];i&&s.push(N(this.ctx,i));let c=await this.db.update(this.table).set(o).where(t(...s)).returning({id:this.table.id});return this.invalidateCountCache(),c.length}async aggregate(e){this.logger?.info({entity:this.entity.name},`Aggregate query`),await j(this.entity,this.contextResolver,`view`);let n=await M(this.entity,this.contextResolver),r=this.db.select(e.select).from(this.table).$dynamic(),i=[];if(n&&i.push(N(this.ctx,n)),e.where&&i.push(e.where),i.length>0&&(r=r.where(t(...i))),e.groupBy&&e.groupBy.length>0&&(r=r.groupBy(...e.groupBy)),e.orderBy){let t=Array.isArray(e.orderBy)?e.orderBy:[e.orderBy];r=r.orderBy(...t)}return e.limit&&(r=r.limit(e.limit)),await r}getTable(){return this.table}getDb(){return this.db}prepareDataForInsert(e){let t={};for(let[n,r]of Object.entries(e)){let e=C(this.ctx)[n];e&&n!==`id`&&e.type!==`blocks`&&(t[n]=r)}return e._scopeId!==void 0&&(t._scopeId=e._scopeId),t}prepareDataForUpdate(e){let t={};for(let[n,r]of Object.entries(e)){let e=C(this.ctx)[n];e&&n!==`id`&&e.type!==`blocks`&&(t[n]=r)}return t}async saveTranslation(t,n,r){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:t,locale:n},`Saving translation`);let i=`${this.entity.name}_translations`,a=e.get(i);if(!a)throw Error(`Translation table ${i} not found in schema registry`);let o=Object.entries(C(this.ctx)).filter(([e,t])=>t.translatable).map(([e])=>e);if(o.length===0)throw Error(`Entity ${this.entity.name} has no translatable fields`);let s={entityId:t,locale:n},c={};for(let e of o)r[e]!==void 0&&(s[e]=r[e],c[e]=r[e]);await this.db.insert(a).values(s).onConflictDoUpdate({target:[a.entityId,a.locale],set:c}),this.logger?.info({entity:this.entity.name,entityId:t,locale:n},`Translation saved`)}async deleteTranslation(r,i){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Deleting translation`);let a=`${this.entity.name}_translations`,o=e.get(a);if(!o)throw Error(`Translation table ${a} not found in schema registry`);i?(await this.db.delete(o).where(t(n(o.entityId,r),n(o.locale,i))),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Translation deleted`)):(await this.db.delete(o).where(n(o.entityId,r)),this.logger?.info({entity:this.entity.name,entityId:r},`All translations deleted`))}async saveBlockTranslations(r,i,a){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Saving block translations`);let s=w(this.ctx);if(s.length===0)return;let c=e.get(`${this.entity.name}_layout_translations`);if(!c)return;let l=e.get(`${this.entity.name}_layout`);if(l){for(let{name:e,config:u}of s){let s=a[e];if(!Array.isArray(s)||u.type!==`blocks`)continue;let d=u.blocks;if(!d)continue;let f=await this.db.select({id:l.id,blockType:l.blockType}).from(l).where(t(n(l.entityId,r),n(l.fieldName,e),o(l.locale))).orderBy(l.sortOrder);for(let e=0;e<s.length;e++){let t=s[e],n=t._block;if(!n)continue;let r=f[e];if(!r||r.blockType!==n)continue;let a=d.find(e=>e.slug===n);if(!a)continue;let o={};for(let[e,n]of Object.entries(a.fields))n.translatable&&t[e]!==void 0&&(o[e]=t[e]);Object.keys(o).length!==0&&await this.db.insert(c).values({layoutId:r.id,locale:i,fields:o}).onConflictDoUpdate({target:[c.layoutId,c.locale],set:{fields:o}})}}this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Block translations saved`)}}async saveLocalizedBlocks(e,t,n){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:e,locale:t},`Saving localized blocks`),await this.saveBlocks(e,n,{locale:t})}async saveBlocks(r,i,a){let s=w(this.ctx);if(s.length===0)return;let c=e.get(`${this.entity.name}_layout`);if(c)for(let{name:e,config:l}of s){let s=i[e];if(!Array.isArray(s))continue;let u=`localized`in l&&l.localized===!0,d=u?a?.locale??null:null,f=[n(c.entityId,r),n(c.fieldName,e)];d?f.push(n(c.locale,d)):u||f.push(o(c.locale)),await this.db.delete(c).where(t(...f));for(let t=0;t<s.length;t++){let n=s[t],i=n._block;if(!i)continue;let{_block:a,_id:o,...l}=n;await this.db.insert(c).values({entityId:r,fieldName:e,blockType:i,sortOrder:t,data:l,locale:d})}}}async syncRefs(r,i,o){if(!e.has(`entity_refs`))return;let s=C(this.ctx);if(o===`update`){let e=Object.keys(i).filter(e=>{let t=s[e];return t&&(t.type===`media`||t.type===`reference`||t.type===`blocks`)});e.length>0&&await this.db.delete(S).where(t(n(S.sourceEntity,this.entity.name),n(S.sourceId,r),a(S.sourceField,e)))}let c=x(s,i);c.length>0&&await this.db.insert(S).values(c.map(e=>({sourceEntity:this.entity.name,sourceId:r,sourceField:e.sourceField,targetEntity:e.targetEntity,targetId:e.targetId}))).onConflictDoNothing()}invalidateCountCache(){this.countCache?.invalidate(this.entity.name)}};export{R as AdminClient};
1
+ import{schemaRegistry as e}from"@murumets-ee/db";import{and as t,eq as n,getTableColumns as r,gt as i,inArray as a,isNull as o,lt as s,or as c,sql as l}from"drizzle-orm";import{index as u,pgTable as d,unique as f,uuid as p,varchar as m}from"drizzle-orm/pg-core";import{z as h}from"zod";function g(e,a){let o=r(e),l=o[a.field];if(!l)return null;let u=a.direction===`desc`?s:i,d=u(l,a.value);if(!a.id)return d;let f=o.id;if(!f)return d;let p=u(f,a.id);return c(d,t(n(l,a.value),p))??d}function _(e,t,n){if(!t)return null;let r={},i=n?.select||Object.keys(e.allFields);for(let a of i){let i=e.allFields[a];if(!n?.includeInternal){let e=a.startsWith(`_`),t=i?.internal===!0;if(e||t)continue}i&&i.type!==`blocks`&&(r[a]=t[a])}return r}function v(e,t,n){return t.map(t=>_(e,t,n))}var y=class extends Error{entityName;entityId;usages;constructor(e,t,n){let r=n.length;super(`Cannot delete ${e} '${t}': referenced by ${r} other entit${r===1?`y`:`ies`}`),this.name=`ReferencedEntityError`,this.entityName=e,this.entityId=t,this.usages=n}};const b=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function x(e,t){let n=[],r=new Set;function i(e,t,i){if(!b.test(i))return;let a=`${e}|${t}|${i}`;r.has(a)||(r.add(a),n.push({sourceField:e,targetEntity:t,targetId:i}))}for(let[n,r]of Object.entries(e)){let e=t[n];if(e!=null)switch(r.type){case`media`:typeof e==`string`&&i(n,`media`,e);break;case`reference`:{let t=r;if(t.cardinality===`many`&&Array.isArray(e))for(let r of e)typeof r==`string`&&i(n,t.entity,r);else typeof e==`string`&&i(n,t.entity,e);break}case`blocks`:{let t=r;if(!Array.isArray(e))break;for(let r of e){let e=r,a=e._block;if(!a)continue;let o=t.blocks.find(e=>e.slug===a);if(o)for(let[t,r]of Object.entries(o.fields)){let a=e[t];if(a!=null&&(r.type===`media`&&typeof a==`string`&&i(n,`media`,a),r.type===`reference`)){let e=r;if(e.cardinality===`many`&&Array.isArray(a))for(let t of a)typeof t==`string`&&i(n,e.entity,t);else typeof a==`string`&&i(n,e.entity,a)}}}break}}}return n}const S=d(`entity_refs`,{sourceEntity:m(`source_entity`,{length:100}).notNull(),sourceId:p(`source_id`).notNull(),sourceField:m(`source_field`,{length:100}).notNull(),targetEntity:m(`target_entity`,{length:100}).notNull(),targetId:p(`target_id`).notNull()},e=>[f(`uq_entity_refs`).on(e.sourceEntity,e.sourceId,e.sourceField,e.targetEntity,e.targetId),u(`idx_entity_refs_target`).on(e.targetEntity,e.targetId),u(`idx_entity_refs_source`).on(e.sourceEntity,e.sourceId)]);function C(e){return e.entity.allFields}function w(e){return Object.entries(C(e)).filter(([e,t])=>t.type===`blocks`).map(([e,t])=>({name:e,config:t}))}async function T(r,i,o){if(!i.length)return i;let s=`${r.entity.name}_translations`,c=e.get(s);if(!c)return i;let l=i.map(e=>e.id),u=await r.db.select().from(c).where(t(a(c.entityId,l),n(c.locale,o))),d=Object.entries(C(r)).filter(([e,t])=>t.translatable).map(([e])=>e),f=new Map;for(let e of u){let t={};for(let n of d){let r=e[n];r!=null&&(t[n]=r)}f.set(e.entityId,t)}return i.map(e=>{let t=f.get(e.id);return t?{...e,...t}:e})}async function E(r,i,s,l){let u=w(r);if(u.length===0||i.length===0)return new Map;let d=e.get(`${r.entity.name}_layout`);if(!d)return new Map;let f=u.filter(({config:e})=>!(`localized`in e&&e.localized)),p=u.filter(({config:e})=>`localized`in e&&e.localized),m=new Map;if(f.length>0){let c=await r.db.select().from(d).where(t(a(d.entityId,i),o(d.locale))).orderBy(d.sortOrder),u;if(s&&c.length>0){let i=e.get(`${r.entity.name}_layout_translations`);if(i){let e=c.map(e=>e.id),o=await r.db.select().from(i).where(t(a(i.layoutId,e),n(i.locale,s)));u=new Map;for(let e of o)u.set(e.layoutId,e.fields??{})}}let p;if(l?.strictTranslations){p=new Map;for(let{config:e}of f)if(e.type===`blocks`)for(let t of e.blocks)p.set(t.slug,t.fields)}for(let e of c){let t=e.entityId,n=e.fieldName,r=m.get(t);r||(r={},m.set(t,r)),r[n]||(r[n]=[]);let i=e.data??{},a=u?.get(e.id),o={_block:e.blockType,_id:e.id,...i,...a??{}};if(s&&p){let t=e.blockType,n=p.get(t);if(n)for(let[e,t]of Object.entries(n))t.translatable&&!a?.[e]&&(o[e]=``)}r[n].push(o)}}if(p.length>0){let e=!s||s===l?.defaultLocale,u;u=s?e?c(n(d.locale,s),o(d.locale))??n(d.locale,s):n(d.locale,s):o(d.locale);let f=await r.db.select().from(d).where(t(a(d.entityId,i),u)).orderBy(d.sortOrder),p=new Map;for(let e of f){let t=`${e.entityId}::${e.fieldName}`,n=p.get(t);n||(n={localeRows:[],nullRows:[]},p.set(t,n)),e.locale?n.localeRows.push(e):n.nullRows.push(e)}for(let[,{localeRows:e,nullRows:t}]of p){let n=e.length>0?e:t;for(let e of n){let t=e.entityId,n=e.fieldName,r=m.get(t);r||(r={},m.set(t,r)),r[n]||(r[n]=[]);let i=e.data??{};r[n].push({_block:e.blockType,_id:e.id,...i})}}}return m}function D(e,t,n){let r=w(e);if(r.length!==0)for(let e of t){let t=e.id,i=n.get(t)??{};for(let{name:t}of r)e[t]=i[t]??[]}}function O(e,t,n,r){let i=n?`${n}:${e}`:e;return r&&(i=`${i}@${r}`),t?`${i}:${String(t)}`:i}var k=class extends Error{constructor(e){super(e),this.name=`ForbiddenError`}};async function A(e){if(!e)throw new k(`No context resolver configured on AdminClient/QueryClient. Use createAdminClient() from @murumets-ee/core/clients, or pass a contextResolver in config.`);let t=await e();if(!t)throw new k(`Context resolver returned no context. Ensure auth is configured in your request handler, or use runAsCli() for CLI scripts.`);return t}async function j(e,t,n){let r=await A(t),i=r.user.groups[0];if(!i)throw new k(`User '${r.user.id}' has no role assigned.`);if(!r.checker(i,e.name,n))throw new k(`Forbidden: role '${i}' cannot ${n} '${e.name}'`)}function M(e,t){return n(e.table._scopeId,t)}async function N(e,t,n,r){let i=await A(t),a=i.user.groups[0];if(!a)throw new k(`User '${i.user.id}' has no role assigned.`);if(!i.checker(a,e.name,n))throw new k(`Forbidden: role '${a}' cannot ${n} '${e.name}'`);let o;if(e.scope&&e.scope!==`global`&&(o=i.scope?.id,!o&&r?.strictScope))throw new k(`Entity '${e.name}' requires scope '${e.scope}' but no scope is set in context.`);let s={user:{id:i.user.id,name:i.user.name,email:i.user.email}};return{security:i,scopeId:o,behaviorCtx:s}}function P(e){let t;switch(e.type){case`id`:t=h.string().uuid();break;case`text`:{let n=h.string();e.maxLength&&(n=n.max(e.maxLength)),e.minLength&&(n=n.min(e.minLength)),e.pattern&&(n=n.regex(e.pattern)),t=n;break}case`number`:{let n=h.number();e.integer&&(n=n.int()),e.min!==void 0&&(n=n.min(e.min)),e.max!==void 0&&(n=n.max(e.max)),t=n;break}case`boolean`:t=h.boolean();break;case`date`:t=h.date().or(h.string().datetime());break;case`select`:t=h.enum(e.options);break;case`reference`:t=e.cardinality===`many`?h.array(h.string().uuid()):h.string().uuid();break;case`media`:t=h.string().uuid();break;case`richtext`:t=h.union([h.string(),h.array(h.record(h.unknown()))]);break;case`slug`:t=h.string().regex(/^[a-z0-9-]+$/);break;case`json`:t=h.union([h.record(h.unknown()),h.array(h.unknown()),h.string(),h.number(),h.boolean()]);break;case`blocks`:t=h.array(h.object({_block:h.string()}).passthrough());break;default:t=h.unknown()}return e.required||(t=t.nullable().optional()),e.default!==void 0&&(t=t.default(e.default)),t}function F(e){return e===`id`||e===`createdAt`||e===`createdBy`||e===`updatedAt`||e===`updatedBy`||e===`_version`}function I(e){return e===`id`||e===`createdAt`||e===`updatedAt`||e===`createdBy`||e===`_version`}function L(e,t={}){let n={};for(let[r,i]of Object.entries(e.allFields))I(r)||i.internal&&!t.includeInternal||(n[r]=P(i));return h.object(n)}function R(e,t={}){let n={};for(let[r,i]of Object.entries(e.allFields))F(r)||i.internal&&!t.includeInternal||(n[r]=P(i).optional());return h.object(n)}var z=class r{entity;db;logger;createSchema;updateSchema;updateInternalSchema;internalFieldNames;table;countCache;contextResolver;entityResolver;get ctx(){return{entity:this.entity,db:this.db,table:this.table,resolveContext:this.contextResolver}}constructor(t){if(typeof window<`u`)throw Error(`AdminClient cannot be used in browser code. Use QueryClient for frontend data access.`);this.entity=t.entity,this.db=t.db,this.logger=t.logger,this.countCache=t.countCache,this.contextResolver=t.contextResolver,this.entityResolver=t.entityResolver,this.createSchema=L(t.entity),this.updateSchema=R(t.entity),this.updateInternalSchema=R(t.entity,{includeInternal:!0}),this.internalFieldNames=new Set(Object.entries(t.entity.allFields).filter(([,e])=>e.internal).map(([e])=>e));let n=e.get(t.entity.name);if(!n)throw Error(`Schema for entity '${t.entity.name}' not found in registry. Ensure schemas are generated and registered before creating AdminClient.`);this.table=n}stripCallerInternals(e){if(this.internalFieldNames.size===0)return e;let t={};for(let[n,r]of Object.entries(e))this.internalFieldNames.has(n)||(t[n]=r);return t}pickInternalFields(e){if(this.internalFieldNames.size===0)return{};let t={};for(let n of this.internalFieldNames)e[n]!==void 0&&(t[n]=e[n]);return t}async create(e){this.logger?.info({entity:this.entity.name},`Creating entity`);let{scopeId:t,behaviorCtx:n}=await N(this.entity,this.contextResolver,`create`,{strictScope:!0}),r=this.stripCallerInternals(e);for(let e of this.entity.behaviors??[])if(e.hooks?.beforeCreate){let t=await e.hooks.beforeCreate(r,n);t!==void 0&&(r=t)}let i={};for(let e of[`createdAt`,`updatedAt`,`createdBy`,`updatedBy`])r[e]!==void 0&&(i[e]=r[e]);let a=this.pickInternalFields(r);r={...this.createSchema.parse(r),...i,...a},t&&(r._scopeId=t);let o=this.prepareDataForInsert(r),[s]=await this.db.insert(this.table).values(o).returning();await this.saveBlocks(s.id,r),await this.syncRefs(s.id,r,`create`);for(let e of this.entity.behaviors??[])e.hooks?.afterCreate&&await e.hooks.afterCreate(s,n);this.invalidateCountCache();let c=_(this.entity,s);if(c&&w(this.ctx).length>0){let e=await E(this.ctx,[c.id],void 0,{strictTranslations:!0});D(this.ctx,[c],e)}return c}async findById(e,r){this.logger?.info({entity:this.entity.name,id:e},`Finding entity by ID`);let{scopeId:i}=await N(this.entity,this.contextResolver,`view`),a=[n(this.table.id,e)];i&&a.push(M(this.ctx,i));let[o]=await this.db.select().from(this.table).where(t(...a));if(!o)return null;let s=r?.includeInternal?{includeInternal:!0}:void 0,c=_(this.entity,o,s);if(!c)return null;if(w(this.ctx).length>0){let e=await E(this.ctx,[c.id],r?.locale,{defaultLocale:r?.defaultLocale,strictTranslations:!0});D(this.ctx,[c],e)}if(r?.locale){let[e]=await T(this.ctx,[c],r.locale);return e}return c}async findMany(e){this.logger?.info({entity:this.entity.name,options:e},`Finding entities`);let{scopeId:n}=await N(this.entity,this.contextResolver,`view`),r=this.db.select().from(this.table).$dynamic(),i=[];if(n&&i.push(M(this.ctx,n)),e?.where&&i.push(e.where),e?.cursor){let t=C(this.ctx);if(!(e.cursor.field in t)&&e.cursor.field!==`id`)throw Error(`Invalid cursor field: '${e.cursor.field}' is not a field on '${this.entity.name}'`);let n=g(this.table,e.cursor);n&&i.push(n)}if(i.length>0&&(r=r.where(t(...i))),e?.limit&&(r=r.limit(e.limit)),e?.offset&&!e?.cursor&&(r=r.offset(e.offset)),e?.orderBy){let t=Array.isArray(e.orderBy)?e.orderBy:[e.orderBy];r=r.orderBy(...t)}let a=await r,o=e?.includeInternal?{includeInternal:!0}:void 0,s=v(this.entity,a,o).filter(e=>e!==null);if(w(this.ctx).length>0&&s.length>0){let t=s.map(e=>e.id),n=await E(this.ctx,t,e?.locale,{defaultLocale:e?.defaultLocale,strictTranslations:!0});D(this.ctx,s,n)}return e?.locale?await T(this.ctx,s,e.locale):s}async count(e){this.logger?.info({entity:this.entity.name,options:e},`Counting entities`);let{scopeId:n}=await N(this.entity,this.contextResolver,`view`),r=O(this.entity.name,e?.where,void 0,n),i=async()=>{let r=this.db.select({count:l`count(*)`}).from(this.table).$dynamic(),i=[];n&&i.push(M(this.ctx,n)),e?.where&&i.push(e.where),i.length>0&&(r=r.where(t(...i)));let[a]=await r;return Number(a.count)};return this.countCache?this.countCache.getOrCompute(r,i):i()}async update(e,t,n){return this.updateImpl(e,t,n,{allowInternal:!1})}async updateInternal(e,t,n){return this.updateImpl(e,t,n,{allowInternal:!0})}async updateImpl(e,r,i,a){this.logger?.info({entity:this.entity.name,id:e,internal:a.allowInternal||void 0},`Updating entity`);let{scopeId:o,behaviorCtx:s}=await N(this.entity,this.contextResolver,`update`,{strictScope:!0}),c=a.allowInternal?r:this.stripCallerInternals(r),l,u=async()=>(l===void 0&&(l=await this.findById(e)),l),d={...s,loadCurrent:u,viaInternal:a.allowInternal};for(let t of this.entity.behaviors??[])if(t.hooks?.beforeUpdate){let n=await t.hooks.beforeUpdate(e,c,d);n!==void 0&&(c=n)}let f={};for(let e of[`updatedAt`,`updatedBy`])c[e]!==void 0&&(f[e]=c[e]);let p=a.allowInternal?this.updateInternalSchema:this.updateSchema,m=a.allowInternal?{}:this.pickInternalFields(c);c={...p.parse(c),...f,...m};let h=this.prepareDataForUpdate(c),g=n(this.table.id,e),v=o?t(g,M(this.ctx,o))??g:g,y;if(Object.keys(h).length>0){let[e]=await this.db.update(this.table).set(h).where(v).returning();y=e}else{let[e]=await this.db.select().from(this.table).where(v);y=e}await this.saveBlocks(e,c,i),await this.syncRefs(e,c,`update`);for(let e of this.entity.behaviors??[])e.hooks?.afterUpdate&&await e.hooks.afterUpdate(y,s);let b=a.allowInternal?{includeInternal:!0}:void 0,x=_(this.entity,y,b);if(x&&w(this.ctx).length>0){let t=await E(this.ctx,[e],void 0,{strictTranslations:!0});D(this.ctx,[x],t)}return x}async delete(e){this.logger?.info({entity:this.entity.name,id:e},`Deleting entity`);let{scopeId:r,behaviorCtx:i}=await N(this.entity,this.contextResolver,`delete`,{strictScope:!0});if(r){let[i]=await this.db.select({id:this.table.id}).from(this.table).where(t(n(this.table.id,e),M(this.ctx,r)));if(!i)return}await this.db.transaction(async r=>{await this.handleIncomingRefsForDelete(r,[e]);for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,i);await r.delete(this.table).where(n(this.table.id,e)),await r.delete(S).where(t(n(S.sourceEntity,this.entity.name),n(S.sourceId,e)))});for(let t of this.entity.behaviors??[])t.hooks?.afterDelete&&await t.hooks.afterDelete(e,i);this.invalidateCountCache()}async deleteMany(e){this.logger?.info({entity:this.entity.name},`Bulk deleting entities`);let{scopeId:r,behaviorCtx:i}=await N(this.entity,this.contextResolver,`delete`,{strictScope:!0}),o=[e];r&&o.push(M(this.ctx,r));let s=(await this.db.select({id:this.table.id}).from(this.table).where(t(...o))).map(e=>e.id);if(s.length===0)return 0;await this.db.transaction(async e=>{await this.handleIncomingRefsForDelete(e,s);for(let e of s)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,i);await e.delete(this.table).where(a(this.table.id,s)),await e.delete(S).where(t(n(S.sourceEntity,this.entity.name),a(S.sourceId,s)))});for(let e of s)for(let t of this.entity.behaviors??[])t.hooks?.afterDelete&&await t.hooks.afterDelete(e,i);return this.invalidateCountCache(),s.length}static MAX_CASCADE_DEPTH=10;async handleIncomingRefsForDelete(i,o,s=0){if(s>=r.MAX_CASCADE_DEPTH)throw Error(`Cascade delete exceeded maximum depth of ${r.MAX_CASCADE_DEPTH} while deleting '${this.entity.name}'. This likely indicates a circular reference chain.`);let c=await i.select({sourceEntity:S.sourceEntity,sourceId:S.sourceId,sourceField:S.sourceField}).from(S).where(t(n(S.targetEntity,this.entity.name),a(S.targetId,o)));if(c.length===0)return;let l=new Map;for(let e of c){let t=`${e.sourceEntity}:${e.sourceField}`,n=l.get(t);n||(n={sourceEntity:e.sourceEntity,sourceField:e.sourceField,sourceIds:[]},l.set(t,n)),n.sourceIds.push(e.sourceId)}let u=this.entityResolver?.();for(let[,c]of l){let l=u?.get(c.sourceEntity),d=l?.fields[c.sourceField]?.onDelete??`restrict`;if(d===`restrict`)throw new y(this.entity.name,o[0],c.sourceIds.map(e=>({sourceEntity:c.sourceEntity,sourceId:e,sourceField:c.sourceField})));if(d===`cascade`){let n=e.get(c.sourceEntity);if(n&&l){let e=new r({entity:l,db:i,logger:this.logger,contextResolver:this.contextResolver}),o=await N(l,this.contextResolver,`delete`),u=a(n.id,c.sourceIds);if(o.scopeId){let e={entity:l,db:i,table:n,resolveContext:this.contextResolver},r=t(u,M(e,o.scopeId));r&&(u=r)}await e.deleteManyInTx(i,u,s+1,o.behaviorCtx)}}else if(d===`set-null`){let r=e.get(c.sourceEntity);if(r&&l&&r[c.sourceField]){let e=(await N(l,this.contextResolver,`update`)).scopeId,o=a(r.id,c.sourceIds);if(e){let n={entity:l,db:i,table:r,resolveContext:this.contextResolver},a=t(o,M(n,e));a&&(o=a)}await i.update(r).set({[c.sourceField]:null}).where(o),await i.delete(S).where(t(n(S.sourceEntity,c.sourceEntity),a(S.sourceId,c.sourceIds),n(S.sourceField,c.sourceField)))}}}}async deleteManyInTx(e,r,i,o){let s=(await e.select({id:this.table.id}).from(this.table).where(r)).map(e=>e.id);if(s.length===0)return 0;await this.handleIncomingRefsForDelete(e,s,i);for(let e of s)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e,o);return await e.delete(this.table).where(a(this.table.id,s)),await e.delete(S).where(t(n(S.sourceEntity,this.entity.name),a(S.sourceId,s))),this.invalidateCountCache(),s.length}async updateMany(e,n,r){this.logger?.info({entity:this.entity.name},`Bulk updating entities`);let{scopeId:i}=await N(this.entity,this.contextResolver,`update`,{strictScope:!0}),a=r?.allowInternal?n:this.stripCallerInternals(n),o=(r?.allowInternal?this.updateInternalSchema:this.updateSchema).parse(a),s=this.prepareDataForUpdate(o);if(r?.expressions){let e=C(this.ctx);for(let[t,n]of Object.entries(r.expressions)){if(t in s)throw Error(`updateMany: field '${t}' appears in both \`data\` and \`expressions\`. Choose one — expressions silently overriding validated values is unsafe.`);if(!(t in e)&&t!==`_scopeId`)throw Error(`updateMany.expressions: unknown column '${t}' on entity '${this.entity.name}'`);if(!r.allowInternal&&this.internalFieldNames.has(t))throw Error(`updateMany.expressions: '${t}' is internal — pass \`allowInternal: true\` from a trusted server context that has authorized the transition out-of-band.`);s[t]=n}}let c=[e];i&&c.push(M(this.ctx,i));let l=await this.db.update(this.table).set(s).where(t(...c)).returning({id:this.table.id});return this.invalidateCountCache(),l.length}async aggregate(e){this.logger?.info({entity:this.entity.name},`Aggregate query`);let{scopeId:n}=await N(this.entity,this.contextResolver,`view`),r=this.db.select(e.select).from(this.table).$dynamic(),i=[];if(n&&i.push(M(this.ctx,n)),e.where&&i.push(e.where),i.length>0&&(r=r.where(t(...i))),e.groupBy&&e.groupBy.length>0&&(r=r.groupBy(...e.groupBy)),e.orderBy){let t=Array.isArray(e.orderBy)?e.orderBy:[e.orderBy];r=r.orderBy(...t)}return e.limit&&(r=r.limit(e.limit)),await r}getTable(){return this.table}getDb(){return this.db}prepareDataForInsert(e){let t={};for(let[n,r]of Object.entries(e)){let e=C(this.ctx)[n];e&&n!==`id`&&e.type!==`blocks`&&(t[n]=r)}return e._scopeId!==void 0&&(t._scopeId=e._scopeId),t}prepareDataForUpdate(e){let t={};for(let[n,r]of Object.entries(e)){let e=C(this.ctx)[n];e&&n!==`id`&&e.type!==`blocks`&&(t[n]=r)}return t}async saveTranslation(t,n,r){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:t,locale:n},`Saving translation`);let i=`${this.entity.name}_translations`,a=e.get(i);if(!a)throw Error(`Translation table ${i} not found in schema registry`);let o=Object.entries(C(this.ctx)).filter(([e,t])=>t.translatable).map(([e])=>e);if(o.length===0)throw Error(`Entity ${this.entity.name} has no translatable fields`);let s={entityId:t,locale:n},c={};for(let e of o)r[e]!==void 0&&(s[e]=r[e],c[e]=r[e]);await this.db.insert(a).values(s).onConflictDoUpdate({target:[a.entityId,a.locale],set:c}),this.logger?.info({entity:this.entity.name,entityId:t,locale:n},`Translation saved`)}async deleteTranslation(r,i){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Deleting translation`);let a=`${this.entity.name}_translations`,o=e.get(a);if(!o)throw Error(`Translation table ${a} not found in schema registry`);i?(await this.db.delete(o).where(t(n(o.entityId,r),n(o.locale,i))),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Translation deleted`)):(await this.db.delete(o).where(n(o.entityId,r)),this.logger?.info({entity:this.entity.name,entityId:r},`All translations deleted`))}async saveBlockTranslations(r,i,a){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Saving block translations`);let s=w(this.ctx);if(s.length===0)return;let c=e.get(`${this.entity.name}_layout_translations`);if(!c)return;let l=e.get(`${this.entity.name}_layout`);if(l){for(let{name:e,config:u}of s){let s=a[e];if(!Array.isArray(s)||u.type!==`blocks`)continue;let d=u.blocks;if(!d)continue;let f=await this.db.select({id:l.id,blockType:l.blockType}).from(l).where(t(n(l.entityId,r),n(l.fieldName,e),o(l.locale))).orderBy(l.sortOrder);for(let e=0;e<s.length;e++){let t=s[e],n=t._block;if(!n)continue;let r=f[e];if(!r||r.blockType!==n)continue;let a=d.find(e=>e.slug===n);if(!a)continue;let o={};for(let[e,n]of Object.entries(a.fields))n.translatable&&t[e]!==void 0&&(o[e]=t[e]);Object.keys(o).length!==0&&await this.db.insert(c).values({layoutId:r.id,locale:i,fields:o}).onConflictDoUpdate({target:[c.layoutId,c.locale],set:{fields:o}})}}this.logger?.info({entity:this.entity.name,entityId:r,locale:i},`Block translations saved`)}}async saveLocalizedBlocks(e,t,n){await j(this.entity,this.contextResolver,`update`),this.logger?.info({entity:this.entity.name,entityId:e,locale:t},`Saving localized blocks`),await this.saveBlocks(e,n,{locale:t})}async saveBlocks(r,i,a){let s=w(this.ctx);if(s.length===0)return;let c=e.get(`${this.entity.name}_layout`);if(c)for(let{name:e,config:l}of s){let s=i[e];if(!Array.isArray(s))continue;let u=`localized`in l&&l.localized===!0,d=u?a?.locale??null:null,f=[n(c.entityId,r),n(c.fieldName,e)];d?f.push(n(c.locale,d)):u||f.push(o(c.locale)),await this.db.delete(c).where(t(...f));for(let t=0;t<s.length;t++){let n=s[t],i=n._block;if(!i)continue;let{_block:a,_id:o,...l}=n;await this.db.insert(c).values({entityId:r,fieldName:e,blockType:i,sortOrder:t,data:l,locale:d})}}}async syncRefs(r,i,o){if(!e.has(`entity_refs`))return;let s=C(this.ctx);if(o===`update`){let e=Object.keys(i).filter(e=>{let t=s[e];return t&&(t.type===`media`||t.type===`reference`||t.type===`blocks`)});e.length>0&&await this.db.delete(S).where(t(n(S.sourceEntity,this.entity.name),n(S.sourceId,r),a(S.sourceField,e)))}let c=x(s,i);c.length>0&&await this.db.insert(S).values(c.map(e=>({sourceEntity:this.entity.name,sourceId:r,sourceField:e.sourceField,targetEntity:e.targetEntity,targetId:e.targetId}))).onConflictDoNothing()}invalidateCountCache(){this.countCache?.invalidate(this.entity.name)}};export{z as AdminClient};
2
2
  //# sourceMappingURL=index.mjs.map