@murumets-ee/entity 0.8.0 → 0.9.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/admin/index.d.mts +44 -6
- package/dist/admin/index.d.mts.map +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/index.d.mts +22 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/query/index.d.mts +21 -6
- package/dist/query/index.d.mts.map +1 -1
- package/dist/query/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/admin/index.d.mts
CHANGED
|
@@ -189,16 +189,29 @@ interface BlocksField extends BaseFieldConfig {
|
|
|
189
189
|
type FieldConfig = IdField | TextField | NumberField | BooleanField | DateField | SelectField | ReferenceField | MediaField | RichTextField | SlugField | JsonField | BlocksField;
|
|
190
190
|
//#endregion
|
|
191
191
|
//#region src/behaviors/types.d.ts
|
|
192
|
+
/**
|
|
193
|
+
* Context passed to behavior hooks. Resolved once per request by AdminClient
|
|
194
|
+
* from its `contextResolver` and forwarded into every hook so behaviors never
|
|
195
|
+
* have to reach into AsyncLocalStorage themselves — that pattern breaks under
|
|
196
|
+
* bundlers (e.g. Turbopack) that duplicate module instances across boundaries.
|
|
197
|
+
*/
|
|
198
|
+
interface BehaviorContext {
|
|
199
|
+
user?: {
|
|
200
|
+
id: string;
|
|
201
|
+
name?: string;
|
|
202
|
+
email?: string;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
192
205
|
interface Behavior<F extends Record<string, FieldConfig> = {}> {
|
|
193
206
|
name: string;
|
|
194
207
|
fields?: F;
|
|
195
208
|
hooks?: {
|
|
196
|
-
beforeCreate?: (data: Record<string, unknown
|
|
197
|
-
afterCreate?: (entity: Record<string, unknown
|
|
198
|
-
beforeUpdate?: (id: string, data: Record<string, unknown
|
|
199
|
-
afterUpdate?: (entity: Record<string, unknown
|
|
200
|
-
beforeDelete?: (id: string) => Promise<void>;
|
|
201
|
-
afterDelete?: (id: string) => Promise<void>;
|
|
209
|
+
beforeCreate?: (data: Record<string, unknown>, ctx?: BehaviorContext) => Promise<Record<string, unknown>>;
|
|
210
|
+
afterCreate?: (entity: Record<string, unknown>, ctx?: BehaviorContext) => Promise<void>;
|
|
211
|
+
beforeUpdate?: (id: string, data: Record<string, unknown>, ctx?: BehaviorContext) => Promise<Record<string, unknown>>;
|
|
212
|
+
afterUpdate?: (entity: Record<string, unknown>, ctx?: BehaviorContext) => Promise<void>;
|
|
213
|
+
beforeDelete?: (id: string, ctx?: BehaviorContext) => Promise<void>;
|
|
214
|
+
afterDelete?: (id: string, ctx?: BehaviorContext) => Promise<void>;
|
|
202
215
|
};
|
|
203
216
|
}
|
|
204
217
|
//#endregion
|
|
@@ -234,6 +247,8 @@ interface SecurityContext {
|
|
|
234
247
|
user: {
|
|
235
248
|
id: string;
|
|
236
249
|
groups: string[];
|
|
250
|
+
name?: string;
|
|
251
|
+
email?: string;
|
|
237
252
|
};
|
|
238
253
|
checker: (role: string, resource: string, action: string) => boolean;
|
|
239
254
|
scope?: {
|
|
@@ -306,6 +321,14 @@ interface Logger {
|
|
|
306
321
|
}
|
|
307
322
|
//#endregion
|
|
308
323
|
//#region src/admin/client.d.ts
|
|
324
|
+
/**
|
|
325
|
+
* Resolves the registry of all entities in the running app — used by cascade
|
|
326
|
+
* delete to look up `onDelete` strategy on referencing fields. Injected as a
|
|
327
|
+
* function so the entity package never has to import `@murumets-ee/core`
|
|
328
|
+
* (which would create a circular dependency and force runtime tricks that
|
|
329
|
+
* break under bundlers like Turbopack).
|
|
330
|
+
*/
|
|
331
|
+
type EntityResolver = () => Map<string, Entity> | undefined;
|
|
309
332
|
interface AdminClientConfig<AllFields extends Record<string, FieldConfig> = Record<string, FieldConfig>> {
|
|
310
333
|
entity: Entity<AllFields>;
|
|
311
334
|
db: PostgresJsDatabase;
|
|
@@ -318,6 +341,13 @@ interface AdminClientConfig<AllFields extends Record<string, FieldConfig> = Reco
|
|
|
318
341
|
* For direct `new AdminClient()` usage, pass your own resolver or use `runAsCli()`.
|
|
319
342
|
*/
|
|
320
343
|
contextResolver?: ContextResolver;
|
|
344
|
+
/**
|
|
345
|
+
* Resolves the running app's entity registry. Used by cascade delete to
|
|
346
|
+
* read each referencing field's `onDelete` strategy. When omitted, all
|
|
347
|
+
* incoming references are treated as `restrict` — a safe default that
|
|
348
|
+
* blocks deletes rather than guessing.
|
|
349
|
+
*/
|
|
350
|
+
entityResolver?: EntityResolver;
|
|
321
351
|
}
|
|
322
352
|
interface FindManyOptions {
|
|
323
353
|
where?: SQL | undefined;
|
|
@@ -370,8 +400,16 @@ declare class AdminClient<AllFields extends Record<string, FieldConfig> = Record
|
|
|
370
400
|
private table;
|
|
371
401
|
private countCache?;
|
|
372
402
|
private contextResolver?;
|
|
403
|
+
private entityResolver?;
|
|
373
404
|
/** Shared context for entity-data-ops functions. */
|
|
374
405
|
private get ctx();
|
|
406
|
+
/**
|
|
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.
|
|
411
|
+
*/
|
|
412
|
+
private resolveBehaviorContext;
|
|
375
413
|
constructor(config: AdminClientConfig<AllFields>);
|
|
376
414
|
/**
|
|
377
415
|
* Create a new entity
|
|
@@ -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":";;;;;;;;;;AAiBA;;;;;;;;;;;UAAiB,cAAA;EACf,GAAA,CAAI,GAAA;EACJ,GAAA,CAAI,GAAA,UAAa,KAAA;EACjB,UAAA,CAAW,MAAA;AAAA;;;;UCKI,WAAA;;EAEf,KAAA;EAF0B;EAI1B,KAAA;EAJ0B;EAM1B,SAAA;EAFA;EAIA,EAAA;AAAA;;;;;;;UC7Be,iBAAA;EFaA;EEXf,KAAA;;EAEA,KAAA;EFUA;EERA,aAAA;EFSA;EEPA,IAAA;EFOiB;EELjB,WAAA;EFMW;EEJX,MAAA;EFIyB;EEFzB,YAAA;;EAEA,aAAA;EDKe;ECHf,cAAA,GAAiB,MAAA;IAAiB,KAAA;IAAgB,WAAA;IAAsB,WAAA;EAAA;EDSxE;ECPA,WAAA;EDSE;ECPF,oBAAA;;EAEA,QAAA;;EAEA,UAAA;EA1BgC;EA4BhC,aAAA;EAVuB;EAYvB,SAAA;EA1BA;;;;;EAgCA,MAAA;EApBA;;;;;EA0BA,YAAA;EAtBA;;;;EA2BA,iBAAA;AAAA;;;;;;;UC9Ce,eAAA;EACf,QAAA;EACA,OAAA;EACA,YAAA;EACA,OAAA;EACA,MAAA;EACA,MAAA;IACE,IAAA;IACA,IAAA;EAAA;AAAA;AAAA,UAIa,OAAA,SAAgB,eAAA;EAC/B,IAAA;AAAA;AAAA,UAGe,SAAA,SAAkB,eAAA;EACjC,IAAA;EACA,SAAA;EACA,SAAA;EACA,OAAA,GAAU,MAAA;AAAA;AAAA,UAGK,WAAA,SAAoB,eAAA;EACnC,IAAA;EACA,GAAA;EACA,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,YAAA,SAAqB,eAAA;EACpC,IAAA;AAAA;AAAA,UAGe,SAAA,SAAkB,eAAA;EACjC,IAAA;EACA,OAAA,GAAU,IAAA;EACV,OAAA,GAAU,IAAA;AAAA;AAAA,UAGK,WAAA,SAAoB,eAAA;EACnC,IAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA,SAAuB,eAAA;EACtC,IAAA;EACA,MAAA;EACA,WAAA;EACA,QAAA;AAAA;AAAA,UAGe,UAAA,SAAmB,eAAA;EAClC,IAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,UAGe,aAAA,SAAsB,eAAA;EACrC,IAAA;EACA,MAAA;AAAA;AAAA,UAGe,SAAA,SAAkB,eAAA;EACjC,IAAA;EACA,IAAA;EACA,MAAA;AAAA;;;;AAlEF;UAyEiB,kBAAA;EACf,IAAA;EACA,MAAA,EAAQ,MAAA,SAAe,WAAA;AAAA;;;;;;;;;UAWR,SAAA,SAAkB,eAAA;EACjC,IAAA;AAAA;AAAA,UAGe,WAAA,SAAoB,eAAA;EACnC,IAAA;EACA,MAAA,WAAiB,kBAAA;EACjB,GAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,KAGU,WAAA,GACR,OAAA,GACA,SAAA,GACA,WAAA,GACA,YAAA,GACA,SAAA,GACA,WAAA,GACA,cAAA,GACA,UAAA,GACA,aAAA,GACA,SAAA,GACA,SAAA,GACA,WAAA
|
|
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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"}
|
package/dist/admin/index.mjs
CHANGED
|
@@ -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;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.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=e;for(let e of this.entity.behaviors??[])if(e.hooks?.beforeCreate){let t=await e.hooks.beforeCreate(n);t!==void 0&&(n=t)}let r={};for(let e of[`createdAt`,`updatedAt`,`createdBy`,`updatedBy`])n[e]!==void 0&&(r[e]=n[e]);n={...this.createSchema.parse(n),...r},t&&(n._scopeId=t);let i=this.prepareDataForInsert(n),[a]=await this.db.insert(this.table).values(i).returning();await this.saveBlocks(a.id,n),await this.syncRefs(a.id,n,`create`);for(let e of this.entity.behaviors??[])e.hooks?.afterCreate&&await e.hooks.afterCreate(a);this.invalidateCountCache();let o=_(this.entity,a);if(o&&w(this.ctx).length>0){let e=await E(this.ctx,[o.id],void 0,{strictTranslations:!0});D(this.ctx,[o],e)}return o}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=r;for(let t of this.entity.behaviors??[])if(t.hooks?.beforeUpdate){let n=await t.hooks.beforeUpdate(e,o);n!==void 0&&(o=n)}o=this.updateSchema.parse(o);let s=this.prepareDataForUpdate(o),c=a?t(n(this.table.id,e),N(this.ctx,a)):n(this.table.id,e),l;if(Object.keys(s).length>0){let[e]=await this.db.update(this.table).set(s).where(c).returning();l=e}else{let[e]=await this.db.select().from(this.table).where(c);l=e}await this.saveBlocks(e,o,i),await this.syncRefs(e,o,`update`);for(let e of this.entity.behaviors??[])e.hooks?.afterUpdate&&await e.hooks.afterUpdate(l);let u=_(this.entity,l);if(u&&w(this.ctx).length>0){let t=await E(this.ctx,[e],void 0,{strictTranslations:!0});D(this.ctx,[u],t)}return u}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);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);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);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=[e];r&&i.push(N(this.ctx,r));let o=(await this.db.select({id:this.table.id}).from(this.table).where(t(...i))).map(e=>e.id);if(o.length===0)return 0;await this.db.transaction(async e=>{await this.handleIncomingRefsForDelete(e,o);for(let e of o)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e);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)))});for(let e of o)for(let t of this.entity.behaviors??[])t.hooks?.afterDelete&&await t.hooks.afterDelete(e);return this.invalidateCountCache(),o.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;try{u=(await import([`@murumets-ee`,`core`].join(`/`))).getApp().entities}catch{}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);for(let e of o)for(let t of this.entity.behaviors??[])t.hooks?.beforeDelete&&await t.hooks.beforeDelete(e);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))}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};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|