@murumets-ee/content 0.111.0 → 0.112.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.
@@ -1 +1 @@
1
- import{i as e,t}from"./resolve-context-DklN53BH.mjs";import{r as n}from"./enumerate-CYHJKQz6.mjs";import{n as r}from"./breadcrumbs-BqSuMNac.mjs";import{createSystemReachResolver as i}from"@murumets-ee/core";import{QueryClient as a}from"@murumets-ee/entity/query";import{inArray as o}from"drizzle-orm";import{collectLinkValues as s,linkValueKey as c}from"@murumets-ee/blocks/links";import{blockTypesInTree as l,breadcrumbLabelOf as u,canvasSiteFormat as d,collectBoundColumnsInTree as f,collectEmbeds as p,contributedBlockDataProviders as m,createCollectionReader as h,detailNeedsBody as g,detailPartsInTree as _,distinctEmbeddedDocuments as v,effectiveCollectionSources as y,effectiveDetailSpecs as b,embedReferenceKey as x,hasConfiguredLiveSources as S,loadCanvasLiveValues as C,materializeServerTemplate as w,readSpotCrop as T,resolveBlockData as E,resolveDocumentDetail as D,resolveLinks as O,resolveTreeCollections as k,sameSpotCrop as A,serverBlockStructureLookup as j,serverBoundColumnsInTree as ee,serverCollectionFieldLookup as M,serverEmbedLookup as N,serverTemplateMaterializer as P,templateNamespace as F}from"@murumets-ee/blocks/server";function I(e){return Array.isArray(e)}function L(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function R(e,t){let n=structuredClone(e),r=e=>{if(I(e))for(let n of e)L(n)&&(typeof n.id==`string`&&(n.id=`${t}${n.id}`),r(n.children))};return r(n),Array.isArray(n)?n:[]}async function te(e,t,n,r){if(n===void 0||n.lookup.size===0)return{expanded:[]};z(e,n.lookup);let i=p(e,n.lookup,{onDropped:e=>r?.warn({entity:t.entity,dropped:e},`content: a block tree embeds more documents than one page may show; the rest show nothing`)});if(i.length===0)return{expanded:[]};let a=v(i),o=t.budget.claim(`block`,a.length),s=a.slice(0,o);if(s.length<a.length&&r?.warn({entity:t.entity,dropped:a.length-s.length},`content: the response budget is spent; some embedded documents show nothing`),s.length===0)return{expanded:[]};let c;try{c=await n.load(s,t.locale)}catch(e){return r?.warn({err:e,entity:t.entity},`content: loading embedded documents failed; they show nothing`),{expanded:s}}let l=new Map(i.map(e=>[e.blockId,e])),u=e=>{if(I(e))for(let t of e){if(!L(t))continue;let e=typeof t.id==`string`?l.get(t.id):void 0;if(e===void 0){u(t.children);continue}let n=c.get(x(e));n!==void 0&&(t.children=R(n,`${e.blockId}~`))}};return u(e),{expanded:s}}function z(e,t){let n=(e,r)=>{if(!(!I(e)||r>64)){for(let i of e)if(L(i)){if(typeof i.type==`string`&&t.has(i.type)){i.children!==void 0&&(i.children=[]);continue}n(i.children,r+1)}}};n(e,0)}const B=i(`embed-resolver`);function V(e){for(let[t,n]of Object.entries(e.allFields))if(n.type===`blockTree`)return t}async function H(e,t,n){let r=new Map,i=new Map;for(let e of t){let t=i.get(e.entity)??[];t.push(e.id),i.set(e.entity,t)}for(let[t,s]of i){let i=e.entities.get(t),c=i===void 0?void 0:V(i);if(i===void 0||c===void 0)continue;let l;try{let r=new a({entity:i,db:e.db.readOnly,logger:e.logger.child({resolve:`embedded-document`,entity:t}),contextResolver:B}),c=r.getTable().id;l=await r.findMany({where:o(c,s),locale:n,limit:s.length})}catch(n){e.logger.warn({err:n,entity:t,count:s.length},`content: reading embedded documents failed — they render empty`);continue}for(let e of l){let n=e,i=n.id,a=n[c];typeof i==`string`&&Array.isArray(a)&&r.set(x({entity:t,id:i}),a)}}return r}const U=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function W(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function G(e){return Array.isArray(e)}const K=new Set(Object.keys({id:!0,url:!0,mimeType:!0,width:!0,height:!0,alt:!0,title:!0,variants:!0,sources:!0,avifSources:!0,focalPoint:!0,blurDataUrl:!0,crop:!0,spotSources:!0}));function q(e,t){if(!W(e))return null;let n=e.id;if(typeof n!=`string`||!U.test(n)||typeof e.url!=`string`||e.url===``)return null;let r=Object.keys(e).filter(e=>!K.has(e));return r.length>0?(t?.(r),null):!J(e.alt,`string`)||!J(e.title,`string`)||!J(e.mimeType,`string`)||!J(e.width,`number`)||!J(e.height,`number`)||!J(e.blurDataUrl,`string`)||e.variants!==void 0&&e.variants!==null&&!W(e.variants)||e.focalPoint!==void 0&&e.focalPoint!==null&&!W(e.focalPoint)||e.sources!==void 0&&e.sources!==null&&(!W(e.sources)||!Object.values(e.sources).every(e=>Array.isArray(e)))||e.avifSources!==void 0&&e.avifSources!==null&&(!W(e.avifSources)||!Object.values(e.avifSources).every(e=>Array.isArray(e)))||e.crop!==void 0&&e.crop!==null&&!W(e.crop)||e.spotSources!==void 0&&e.spotSources!==null&&!Array.isArray(e.spotSources)?null:n}function J(e,t){return e==null||typeof e===t}function Y(e,t,n,r){if(n>256){r.truncated=!0;return}let i=e=>{for(let t of e)r.nearMissKeys.add(t)};if(G(e)){for(let a=0;a<e.length;a+=1){let o=q(e[a],i);o===null?Y(e[a],t,n+1,r):t.push({container:e,key:a,id:o,crop:T(e[a])})}return}if(W(e))for(let[a,o]of Object.entries(e)){let s=q(o,i);s===null?Y(o,t,n+1,r):t.push({container:e,key:a,id:s,crop:T(o)})}}function X(e){let t={id:e.id,url:e.url,variants:e.variants,focalPoint:e.focalPoint,sources:e.sources};return e.avifSources!==void 0&&Object.keys(e.avifSources).length>0&&(t.avifSources=e.avifSources),e.mimeType!==null&&(t.mimeType=e.mimeType),e.width!==null&&(t.width=e.width),e.height!==null&&(t.height=e.height),e.alt!==null&&(t.alt=e.alt),e.title!==null&&(t.title=e.title),e.blurDataUrl!==null&&(t.blurDataUrl=e.blurDataUrl),t}function ne(e,t){if(!(t===void 0||e.spotSources===void 0))return e.spotSources.find(e=>A(e.crop,t)&&e.sources.length>0)}function re(e,t){let n=X(e),r=ne(e,t),i=r?.sources[r.sources.length-1];if(r!==void 0&&i!==void 0){n.url=i.url,n.width=i.width,n.height=i.height,n.focalPoint={x:.5,y:.5},n.sources={natural:r.sources};let e=r.avifSources;e!==void 0&&e.length===r.sources.length&&e.every((e,t)=>e.width===r.sources[t]?.width&&e.height===r.sources[t]?.height)?n.avifSources={natural:e}:delete n.avifSources}return n.crop={x:t.x,y:t.y,w:t.w,h:t.h},n}function ie(e,t){let n=t===null?null:e.crop===void 0?X(t):re(t,e.crop);if(Array.isArray(e.container)){typeof e.key==`number`&&(e.container[e.key]=n);return}typeof e.key==`string`&&(e.container[e.key]=n)}async function Z(e,t,n){let r=e,i=[];if(t.template!==void 0){let e=t.boundValues?.id;if(typeof e==`string`&&e!==``){let a=(n.materializeTemplate??w)(t.template,F(t.entity,e));r=a.blocks,i=a.boundColumns}else r=[],n.logger?.warn({entity:t.entity,field:t.field},`content: a templated document arrived without its row id, so its template is served EMPTY — its block ids could not be namespaced to it (editor F124)`)}else if(t.rowIsForeign!==!0&&t.boundValues!==void 0&&G(e))try{i=(n.boundColumnsOf??ee)(e)}catch(e){n.logger?.warn({err:e,entity:t.entity,field:t.field},`content: could not enumerate the bound columns of a block tree, so its bound-field links render plain`)}let a=t.boundValues??{},o=i.filter(e=>Object.hasOwn(a,e)).map(e=>a[e]);if(!G(r))throw n.logger?.warn({entity:t.entity,field:t.field},`content: stored block tree is not an array — nothing to resolve, so this field is served EMPTY rather than as stored (the caller retries this throw with an exhausted budget, and a non-array fails that identically)`),TypeError(`content: block tree \`${t.entity}.${t.field}\` is not an array; nothing to resolve.`);let{expanded:u}=await te(r,{locale:t.locale,budget:t.budget,entity:t.entity},n.embeddedDocuments,n.logger),d=[],f={truncated:!1,nearMissKeys:new Set};Y(r,d,0,f),f.truncated&&n.logger?.warn({entity:t.entity,field:t.field,cap:256},`content: block tree is nested deeper than the media walk descends — any media ref below the bound keeps the url recorded when an editor picked it, which may be signed`),f.nearMissKeys.size>0&&n.logger?.debug({entity:t.entity,field:t.field,keys:[...f.nearMissKeys].sort()},`content: a value looked like a media ref but carried keys outside the projection, so it was left alone. If these are media refs, they keep their stored urls`);let p=[...s(r,64),...s(o,64)],m=[],h=[];for(let e of p)e.kind===`media`&&(U.test(e.id)?m.push(e.id):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let g=Q([...d.map(e=>e.id),...m]),v=[];for(let e of p)e.kind===`entity`&&(U.test(e.id)?v.push($(e.entity,e.id)):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let y=[...h];!n.resolveMedia&&g.length>0&&n.logger?.debug({entity:t.entity,field:t.field,count:g.length},`content: no media resolver wired for block trees — every media ref resolves to null`);let b=n.resolveMedia?t.budget.claim(`block`,g.length):0,x=g.slice(0,b),S=n.resolveMedia?new Set(g.slice(b)):new Set;for(let e of g.slice(b))y.push({kind:`media`,ref:e,reason:S.has(e)?`budget`:`unresolvable`});let C=Q(v),T=t.budget.claim(`block`,C.length),E=C.slice(0,T),D=new Set(C.slice(T)),k=await le(x,t.locale,n,t.entity);for(let e of d)ie(e,k.get(e.id)??null);for(let e of x)k.has(e)||y.push({kind:`media`,ref:e,reason:`unresolvable`});let A=await ue(E,t.locale,n,t.entity),j=await O(p,{renderContext:`request`,locale:t.locale,resolveEntityHref:({entity:e,id:t})=>A.get($(e,t))??null,resolveMediaHref:e=>k.get(e)?.url??null}),M={};for(let[e,t]of j)M[e]=t.href;for(let e of p){if(e.kind!==`entity`&&e.kind!==`media`)continue;let t=c(e);M[t]!==null&&M[t]!==void 0||y.push({kind:`link`,ref:t,reason:se(e,D,S)})}let N=n.resolveLiveValues,P=N===void 0?0:t.budget.claim(`block`,1),[{results:I,cardMedia:L},R]=await Promise.all([ae(r,t,n),P>0&&N!==void 0?(async()=>N(r,t.locale))().catch(e=>{n.logger?.warn({err:e},`blocks: resolveLiveValues rejected; this tree draws no live values`)}):Promise.resolve(void 0)]),z,B=[];if(t.boundValues!==void 0){let e=_(G(r)?r:[]),i=new Set(e);if(i.delete(`breadcrumbs`),n.resolveDetail!==void 0)try{let e=i;if(e.has(`body`)&&n.detailNeedsBody?.(t.entity,e,t.boundValues)===!0&&t.budget.claim(`block`,1)<1){let r=new Set(e);r.delete(`body`),e=r,n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response budget is spent; this tree is delivered without its HTML body`)}e.size>0&&(z=n.resolveDetail(t.entity,t.boundValues,t.locale,e))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveDetail threw; this tree carries no detail`)}if(e.has(`breadcrumbs`)&&n.resolveBreadcrumbs!==void 0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its breadcrumbs`);else try{let e=await n.resolveBreadcrumbs(t.entity,t.boundValues,t.locale);e!==void 0&&e.steps.length>0&&(z={...z,breadcrumbs:e.steps},B.push(...e.ancestors))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBreadcrumbs threw; this tree carries no breadcrumbs`)}}let V;if(n.resolveBlockData!==void 0&&n.blockDataTypes!==void 0){let e=[...l(r)].filter(e=>n.blockDataTypes?.has(e));if(e.length>0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its block data`);else try{let r=await n.resolveBlockData(e,t.locale);Object.keys(r).length>0&&(V=r)}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBlockData threw; this tree carries no block data`)}}let H=new Map;for(let e of k.keys())H.set($(`media`,e),{entity:`media`,id:e});for(let e of L)H.set($(`media`,e),{entity:`media`,id:e});for(let e of I.values())if(e.entity!==null)for(let t of e.rows)H.set($(e.entity,t.id),{entity:e.entity,id:t.id});for(let e of B)H.set($(e.entity,e.id),e);for(let e of u)H.set($(e.entity,e.id),{entity:e.entity,id:e.id});for(let e of A.keys()){if(H.has(e))continue;let t=e.indexOf(` `);if(t<=0)continue;let n=e.slice(0,t),r=e.slice(t+1);r!==``&&H.set(e,{entity:n,id:r})}return{resolved:1,blocks:r,links:M,...y.length>0&&{unresolved:ce(y)},...H.size>0&&{refs:[...H.values()]},...I.size>0&&{collections:Object.fromEntries(I)},...R!==void 0&&Object.keys(R.sources).length>0&&{live:{sources:R.sources,values:R.values}},...n.currency!==void 0&&{currency:n.currency},...z!==void 0&&{detail:z},...V!==void 0&&{blockData:V}}}async function ae(e,t,n){let r=n.collections,i=t.canReadEntity,a=t.claimCollection;if(r===void 0||i===void 0||a===void 0)return{results:new Map,cardMedia:new Set};let o=n.resolveMedia,s=new Set;return{results:await k(e,r.sourceFieldOf,{sources:r.sources,getEntity:r.getEntity,clientFor:r.clientFor,canRead:i,resolveImages:o===void 0?void 0:async e=>{let n=[...await o(e,{locale:t.locale})].filter(([,e])=>e.mimeType?.startsWith(`image/`)===!0);for(let[e]of n)s.add(e);return new Map(n.map(([e,t])=>[e,{url:t.url,alt:t.alt,width:t.width,height:t.height,focalPoint:t.focalPoint,sources:t.sources,...t.avifSources!==void 0&&Object.keys(t.avifSources).length>0&&{avifSources:t.avifSources},blurDataUrl:t.blurDataUrl}]))},resolveHrefs:(e,r)=>n.resolvePublishedPaths(e,r,t.locale),locale:t.locale,renderContext:`request`,current:oe(t.entity,t.boundValues),...t.page!==void 0&&{page:t.page},claim:()=>a()&&t.budget.claim(`block`,1)>=1,logger:n.logger}),cardMedia:s}}function oe(e,t){let n=t?.id;if(!(t===void 0||typeof n!=`string`||n===``))return{entity:e,id:n,values:t}}function se(e,t,n){return e.kind===`entity`&&t.has($(e.entity,e.id))||e.kind===`media`&&n.has(e.id)?`budget`:`unresolvable`}function Q(e){return[...new Set(e)]}function $(e,t){return[e,t].join(` `)}function ce(e){let t=new Set,n=[];for(let r of e){let e=`${r.kind} ${r.ref}`;t.has(e)||(t.add(e),n.push(r))}return n}async function le(e,t,n,r){if(e.length===0||!n.resolveMedia)return new Map;try{return await n.resolveMedia(e,{locale:t,spots:!0})}catch(e){return n.logger?.warn({entity:r,err:e},"content: media resolver threw while resolving a block tree — its media refs resolve to null (if this is a ForbiddenError, the requesting role is missing `media:view`)"),new Map}}async function ue(e,t,n,r){let i=new Map;if(e.length===0)return i;let a=new Map;for(let t of e){let e=t.indexOf(` `),n=t.slice(0,e),r=t.slice(e+1),i=a.get(n);i?i.push(r):a.set(n,[r])}for(let[e,o]of a)try{let r=await n.resolvePublishedPaths(e,o,t);for(let[t,n]of r)i.set($(e,t),n)}catch(t){n.logger?.warn({entity:r,entityType:e,err:t},`content: path resolution threw for a block-tree link target — its links render as plain text`)}return i}function de(i,a){let o=null,s=i=>{if(o?.app===i)return o.deps;let s=e(i),{sources:c}=y(i),l=c.length===0?void 0:{sources:c,sourceFieldOf:M(),getEntity:e=>i.entities.get(e),clientFor:e=>h(e,i)},p=P(),_=j(),v=b(i).specs,x={...l!==void 0&&{collections:l},boundColumnsOf:e=>f(e,_),materializeTemplate:(e,t)=>p(e,t),resolveMedia:a.resolveMedia,resolvePublishedPaths:async(e,t,r)=>{let i=await s.filterPublished(e,t,r);return i.size===0?new Map:n(e,[...i],r,s)},logger:i.logger,...S()&&{resolveLiveValues:(e,t)=>C(e,{locale:t,logger:i.logger})},currency:d(a.defaultLocale).currency,resolveBreadcrumbs:async(e,n,a)=>{let o=n.id;if(typeof o!=`string`||o===``)return;let s=i.entities.get(e);if(s===void 0)return;let c=u(s,n),l=await r({entityType:e,id:o,locale:a,...c!==void 0&&{label:c}},t(i));return l===void 0?void 0:{steps:l.steps,ancestors:l.ancestors}},...v.size>0&&{resolveDetail:(e,t,n,r)=>{let i=v.get(e);return i===void 0?void 0:D(i,t,n,r)},detailNeedsBody:(e,t,n)=>{let r=v.get(e);return r!==void 0&&g(r,t,n)}}},w=m();w.size>0&&(x.blockDataTypes=new Set(w.keys()),x.resolveBlockData=(e,t)=>E(e,{locale:t,surface:`site`,logger:i.logger,providers:w}));let T=N();return T.size>0&&(x.embeddedDocuments={lookup:T,load:(e,t)=>H(i,e,t)}),o={app:i,deps:x},x};return async(e,t)=>{let n=await i();return Z(e,{...t,locale:t.locale??a.defaultLocale},s(n))}}export{de as createBlockResolver,Z as resolveBlockTree};
1
+ import{i as e,t}from"./resolve-context-DklN53BH.mjs";import{r as n}from"./enumerate-CYHJKQz6.mjs";import{n as r}from"./breadcrumbs-Cewtn9FO.mjs";import{createSystemReachResolver as i}from"@murumets-ee/core";import{QueryClient as a}from"@murumets-ee/entity/query";import{inArray as o}from"drizzle-orm";import{collectLinkValues as s,linkValueKey as c}from"@murumets-ee/blocks/links";import{blockTypesInTree as l,breadcrumbLabelOf as u,canvasSiteFormat as d,collectBoundColumnsInTree as f,collectEmbeds as p,contributedBlockDataProviders as m,createCollectionReader as h,detailNeedsBody as g,detailPartsInTree as _,distinctEmbeddedDocuments as v,effectiveCollectionSources as y,effectiveDetailSpecs as b,embedReferenceKey as x,hasConfiguredLiveSources as S,loadCanvasLiveValues as C,materializeServerTemplate as w,readSpotCrop as T,resolveBlockData as E,resolveDocumentDetail as D,resolveLinks as O,resolveTreeCollections as k,sameSpotCrop as A,serverBlockStructureLookup as j,serverBoundColumnsInTree as ee,serverCollectionFieldLookup as M,serverEmbedLookup as N,serverTemplateMaterializer as P,templateNamespace as F}from"@murumets-ee/blocks/server";function I(e){return Array.isArray(e)}function L(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function R(e,t){let n=structuredClone(e),r=e=>{if(I(e))for(let n of e)L(n)&&(typeof n.id==`string`&&(n.id=`${t}${n.id}`),r(n.children))};return r(n),Array.isArray(n)?n:[]}async function te(e,t,n,r){if(n===void 0||n.lookup.size===0)return{expanded:[]};z(e,n.lookup);let i=p(e,n.lookup,{onDropped:e=>r?.warn({entity:t.entity,dropped:e},`content: a block tree embeds more documents than one page may show; the rest show nothing`)});if(i.length===0)return{expanded:[]};let a=v(i),o=t.budget.claim(`block`,a.length),s=a.slice(0,o);if(s.length<a.length&&r?.warn({entity:t.entity,dropped:a.length-s.length},`content: the response budget is spent; some embedded documents show nothing`),s.length===0)return{expanded:[]};let c;try{c=await n.load(s,t.locale)}catch(e){return r?.warn({err:e,entity:t.entity},`content: loading embedded documents failed; they show nothing`),{expanded:s}}let l=new Map(i.map(e=>[e.blockId,e])),u=e=>{if(I(e))for(let t of e){if(!L(t))continue;let e=typeof t.id==`string`?l.get(t.id):void 0;if(e===void 0){u(t.children);continue}let n=c.get(x(e));n!==void 0&&(t.children=R(n,`${e.blockId}~`))}};return u(e),{expanded:s}}function z(e,t){let n=(e,r)=>{if(!(!I(e)||r>64)){for(let i of e)if(L(i)){if(typeof i.type==`string`&&t.has(i.type)){i.children!==void 0&&(i.children=[]);continue}n(i.children,r+1)}}};n(e,0)}const B=i(`embed-resolver`);function V(e){for(let[t,n]of Object.entries(e.allFields))if(n.type===`blockTree`)return t}async function H(e,t,n){let r=new Map,i=new Map;for(let e of t){let t=i.get(e.entity)??[];t.push(e.id),i.set(e.entity,t)}for(let[t,s]of i){let i=e.entities.get(t),c=i===void 0?void 0:V(i);if(i===void 0||c===void 0)continue;let l;try{let r=new a({entity:i,db:e.db.readOnly,logger:e.logger.child({resolve:`embedded-document`,entity:t}),contextResolver:B}),c=r.getTable().id;l=await r.findMany({where:o(c,s),locale:n,limit:s.length})}catch(n){e.logger.warn({err:n,entity:t,count:s.length},`content: reading embedded documents failed — they render empty`);continue}for(let e of l){let n=e,i=n.id,a=n[c];typeof i==`string`&&Array.isArray(a)&&r.set(x({entity:t,id:i}),a)}}return r}const U=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function W(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function G(e){return Array.isArray(e)}const K=new Set(Object.keys({id:!0,url:!0,mimeType:!0,width:!0,height:!0,alt:!0,title:!0,variants:!0,sources:!0,avifSources:!0,focalPoint:!0,blurDataUrl:!0,crop:!0,spotSources:!0}));function q(e,t){if(!W(e))return null;let n=e.id;if(typeof n!=`string`||!U.test(n)||typeof e.url!=`string`||e.url===``)return null;let r=Object.keys(e).filter(e=>!K.has(e));return r.length>0?(t?.(r),null):!J(e.alt,`string`)||!J(e.title,`string`)||!J(e.mimeType,`string`)||!J(e.width,`number`)||!J(e.height,`number`)||!J(e.blurDataUrl,`string`)||e.variants!==void 0&&e.variants!==null&&!W(e.variants)||e.focalPoint!==void 0&&e.focalPoint!==null&&!W(e.focalPoint)||e.sources!==void 0&&e.sources!==null&&(!W(e.sources)||!Object.values(e.sources).every(e=>Array.isArray(e)))||e.avifSources!==void 0&&e.avifSources!==null&&(!W(e.avifSources)||!Object.values(e.avifSources).every(e=>Array.isArray(e)))||e.crop!==void 0&&e.crop!==null&&!W(e.crop)||e.spotSources!==void 0&&e.spotSources!==null&&!Array.isArray(e.spotSources)?null:n}function J(e,t){return e==null||typeof e===t}function Y(e,t,n,r){if(n>256){r.truncated=!0;return}let i=e=>{for(let t of e)r.nearMissKeys.add(t)};if(G(e)){for(let a=0;a<e.length;a+=1){let o=q(e[a],i);o===null?Y(e[a],t,n+1,r):t.push({container:e,key:a,id:o,crop:T(e[a])})}return}if(W(e))for(let[a,o]of Object.entries(e)){let s=q(o,i);s===null?Y(o,t,n+1,r):t.push({container:e,key:a,id:s,crop:T(o)})}}function X(e){let t={id:e.id,url:e.url,variants:e.variants,focalPoint:e.focalPoint,sources:e.sources};return e.avifSources!==void 0&&Object.keys(e.avifSources).length>0&&(t.avifSources=e.avifSources),e.mimeType!==null&&(t.mimeType=e.mimeType),e.width!==null&&(t.width=e.width),e.height!==null&&(t.height=e.height),e.alt!==null&&(t.alt=e.alt),e.title!==null&&(t.title=e.title),e.blurDataUrl!==null&&(t.blurDataUrl=e.blurDataUrl),t}function ne(e,t){if(!(t===void 0||e.spotSources===void 0))return e.spotSources.find(e=>A(e.crop,t)&&e.sources.length>0)}function re(e,t){let n=X(e),r=ne(e,t),i=r?.sources[r.sources.length-1];if(r!==void 0&&i!==void 0){n.url=i.url,n.width=i.width,n.height=i.height,n.focalPoint={x:.5,y:.5},n.sources={natural:r.sources};let e=r.avifSources;e!==void 0&&e.length===r.sources.length&&e.every((e,t)=>e.width===r.sources[t]?.width&&e.height===r.sources[t]?.height)?n.avifSources={natural:e}:delete n.avifSources}return n.crop={x:t.x,y:t.y,w:t.w,h:t.h},n}function ie(e,t){let n=t===null?null:e.crop===void 0?X(t):re(t,e.crop);if(Array.isArray(e.container)){typeof e.key==`number`&&(e.container[e.key]=n);return}typeof e.key==`string`&&(e.container[e.key]=n)}async function Z(e,t,n){let r=e,i=[];if(t.template!==void 0){let e=t.boundValues?.id;if(typeof e==`string`&&e!==``){let a=(n.materializeTemplate??w)(t.template,F(t.entity,e));r=a.blocks,i=a.boundColumns}else r=[],n.logger?.warn({entity:t.entity,field:t.field},`content: a templated document arrived without its row id, so its template is served EMPTY — its block ids could not be namespaced to it (editor F124)`)}else if(t.rowIsForeign!==!0&&t.boundValues!==void 0&&G(e))try{i=(n.boundColumnsOf??ee)(e)}catch(e){n.logger?.warn({err:e,entity:t.entity,field:t.field},`content: could not enumerate the bound columns of a block tree, so its bound-field links render plain`)}let a=t.boundValues??{},o=i.filter(e=>Object.hasOwn(a,e)).map(e=>a[e]);if(!G(r))throw n.logger?.warn({entity:t.entity,field:t.field},`content: stored block tree is not an array — nothing to resolve, so this field is served EMPTY rather than as stored (the caller retries this throw with an exhausted budget, and a non-array fails that identically)`),TypeError(`content: block tree \`${t.entity}.${t.field}\` is not an array; nothing to resolve.`);let{expanded:u}=await te(r,{locale:t.locale,budget:t.budget,entity:t.entity},n.embeddedDocuments,n.logger),d=[],f={truncated:!1,nearMissKeys:new Set};Y(r,d,0,f),f.truncated&&n.logger?.warn({entity:t.entity,field:t.field,cap:256},`content: block tree is nested deeper than the media walk descends — any media ref below the bound keeps the url recorded when an editor picked it, which may be signed`),f.nearMissKeys.size>0&&n.logger?.debug({entity:t.entity,field:t.field,keys:[...f.nearMissKeys].sort()},`content: a value looked like a media ref but carried keys outside the projection, so it was left alone. If these are media refs, they keep their stored urls`);let p=[...s(r,64),...s(o,64)],m=[],h=[];for(let e of p)e.kind===`media`&&(U.test(e.id)?m.push(e.id):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let g=Q([...d.map(e=>e.id),...m]),v=[];for(let e of p)e.kind===`entity`&&(U.test(e.id)?v.push($(e.entity,e.id)):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let y=[...h];!n.resolveMedia&&g.length>0&&n.logger?.debug({entity:t.entity,field:t.field,count:g.length},`content: no media resolver wired for block trees — every media ref resolves to null`);let b=n.resolveMedia?t.budget.claim(`block`,g.length):0,x=g.slice(0,b),S=n.resolveMedia?new Set(g.slice(b)):new Set;for(let e of g.slice(b))y.push({kind:`media`,ref:e,reason:S.has(e)?`budget`:`unresolvable`});let C=Q(v),T=t.budget.claim(`block`,C.length),E=C.slice(0,T),D=new Set(C.slice(T)),k=await le(x,t.locale,n,t.entity);for(let e of d)ie(e,k.get(e.id)??null);for(let e of x)k.has(e)||y.push({kind:`media`,ref:e,reason:`unresolvable`});let A=await ue(E,t.locale,n,t.entity),j=await O(p,{renderContext:`request`,locale:t.locale,resolveEntityHref:({entity:e,id:t})=>A.get($(e,t))??null,resolveMediaHref:e=>k.get(e)?.url??null}),M={};for(let[e,t]of j)M[e]=t.href;for(let e of p){if(e.kind!==`entity`&&e.kind!==`media`)continue;let t=c(e);M[t]!==null&&M[t]!==void 0||y.push({kind:`link`,ref:t,reason:se(e,D,S)})}let N=n.resolveLiveValues,P=N===void 0?0:t.budget.claim(`block`,1),[{results:I,cardMedia:L},R]=await Promise.all([ae(r,t,n),P>0&&N!==void 0?(async()=>N(r,t.locale))().catch(e=>{n.logger?.warn({err:e},`blocks: resolveLiveValues rejected; this tree draws no live values`)}):Promise.resolve(void 0)]),z,B=[];if(t.boundValues!==void 0){let e=_(G(r)?r:[]),i=new Set(e);if(i.delete(`breadcrumbs`),n.resolveDetail!==void 0)try{let e=i;if(e.has(`body`)&&n.detailNeedsBody?.(t.entity,e,t.boundValues)===!0&&t.budget.claim(`block`,1)<1){let r=new Set(e);r.delete(`body`),e=r,n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response budget is spent; this tree is delivered without its HTML body`)}e.size>0&&(z=n.resolveDetail(t.entity,t.boundValues,t.locale,e))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveDetail threw; this tree carries no detail`)}if(e.has(`breadcrumbs`)&&n.resolveBreadcrumbs!==void 0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its breadcrumbs`);else try{let e=await n.resolveBreadcrumbs(t.entity,t.boundValues,t.locale);e!==void 0&&e.steps.length>0&&(z={...z,breadcrumbs:e.steps},B.push(...e.ancestors))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBreadcrumbs threw; this tree carries no breadcrumbs`)}}let V;if(n.resolveBlockData!==void 0&&n.blockDataTypes!==void 0){let e=[...l(r)].filter(e=>n.blockDataTypes?.has(e));if(e.length>0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its block data`);else try{let r=await n.resolveBlockData(e,t.locale);Object.keys(r).length>0&&(V=r)}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBlockData threw; this tree carries no block data`)}}let H=new Map;for(let e of k.keys())H.set($(`media`,e),{entity:`media`,id:e});for(let e of L)H.set($(`media`,e),{entity:`media`,id:e});for(let e of I.values())if(e.entity!==null)for(let t of e.rows)H.set($(e.entity,t.id),{entity:e.entity,id:t.id});for(let e of B)H.set($(e.entity,e.id),e);for(let e of u)H.set($(e.entity,e.id),{entity:e.entity,id:e.id});for(let e of A.keys()){if(H.has(e))continue;let t=e.indexOf(` `);if(t<=0)continue;let n=e.slice(0,t),r=e.slice(t+1);r!==``&&H.set(e,{entity:n,id:r})}return{resolved:1,blocks:r,links:M,...y.length>0&&{unresolved:ce(y)},...H.size>0&&{refs:[...H.values()]},...I.size>0&&{collections:Object.fromEntries(I)},...R!==void 0&&Object.keys(R.sources).length>0&&{live:{sources:R.sources,values:R.values}},...n.currency!==void 0&&{currency:n.currency},...z!==void 0&&{detail:z},...V!==void 0&&{blockData:V}}}async function ae(e,t,n){let r=n.collections,i=t.canReadEntity,a=t.claimCollection;if(r===void 0||i===void 0||a===void 0)return{results:new Map,cardMedia:new Set};let o=n.resolveMedia,s=new Set;return{results:await k(e,r.sourceFieldOf,{sources:r.sources,getEntity:r.getEntity,clientFor:r.clientFor,canRead:i,resolveImages:o===void 0?void 0:async e=>{let n=[...await o(e,{locale:t.locale})].filter(([,e])=>e.mimeType?.startsWith(`image/`)===!0);for(let[e]of n)s.add(e);return new Map(n.map(([e,t])=>[e,{url:t.url,alt:t.alt,width:t.width,height:t.height,focalPoint:t.focalPoint,sources:t.sources,...t.avifSources!==void 0&&Object.keys(t.avifSources).length>0&&{avifSources:t.avifSources},blurDataUrl:t.blurDataUrl}]))},resolveHrefs:(e,r)=>n.resolvePublishedPaths(e,r,t.locale),locale:t.locale,renderContext:`request`,current:oe(t.entity,t.boundValues),...t.page!==void 0&&{page:t.page},claim:()=>a()&&t.budget.claim(`block`,1)>=1,logger:n.logger}),cardMedia:s}}function oe(e,t){let n=t?.id;if(!(t===void 0||typeof n!=`string`||n===``))return{entity:e,id:n,values:t}}function se(e,t,n){return e.kind===`entity`&&t.has($(e.entity,e.id))||e.kind===`media`&&n.has(e.id)?`budget`:`unresolvable`}function Q(e){return[...new Set(e)]}function $(e,t){return[e,t].join(` `)}function ce(e){let t=new Set,n=[];for(let r of e){let e=`${r.kind} ${r.ref}`;t.has(e)||(t.add(e),n.push(r))}return n}async function le(e,t,n,r){if(e.length===0||!n.resolveMedia)return new Map;try{return await n.resolveMedia(e,{locale:t,spots:!0})}catch(e){return n.logger?.warn({entity:r,err:e},"content: media resolver threw while resolving a block tree — its media refs resolve to null (if this is a ForbiddenError, the requesting role is missing `media:view`)"),new Map}}async function ue(e,t,n,r){let i=new Map;if(e.length===0)return i;let a=new Map;for(let t of e){let e=t.indexOf(` `),n=t.slice(0,e),r=t.slice(e+1),i=a.get(n);i?i.push(r):a.set(n,[r])}for(let[e,o]of a)try{let r=await n.resolvePublishedPaths(e,o,t);for(let[t,n]of r)i.set($(e,t),n)}catch(t){n.logger?.warn({entity:r,entityType:e,err:t},`content: path resolution threw for a block-tree link target — its links render as plain text`)}return i}function de(i,a){let o=null,s=i=>{if(o?.app===i)return o.deps;let s=e(i),{sources:c}=y(i),l=c.length===0?void 0:{sources:c,sourceFieldOf:M(),getEntity:e=>i.entities.get(e),clientFor:e=>h(e,i)},p=P(),_=j(),v=b(i).specs,x={...l!==void 0&&{collections:l},boundColumnsOf:e=>f(e,_),materializeTemplate:(e,t)=>p(e,t),resolveMedia:a.resolveMedia,resolvePublishedPaths:async(e,t,r)=>{let i=await s.filterPublished(e,t,r);return i.size===0?new Map:n(e,[...i],r,s)},logger:i.logger,...S()&&{resolveLiveValues:(e,t)=>C(e,{locale:t,logger:i.logger})},currency:d(a.defaultLocale).currency,resolveBreadcrumbs:async(e,n,a)=>{let o=n.id;if(typeof o!=`string`||o===``)return;let s=i.entities.get(e);if(s===void 0)return;let c=u(s,n),l=await r({entityType:e,id:o,locale:a,...c!==void 0&&{label:c}},t(i));return l===void 0?void 0:{steps:l.steps,ancestors:l.ancestors}},...v.size>0&&{resolveDetail:(e,t,n,r)=>{let i=v.get(e);return i===void 0?void 0:D(i,t,n,r)},detailNeedsBody:(e,t,n)=>{let r=v.get(e);return r!==void 0&&g(r,t,n)}}},w=m();w.size>0&&(x.blockDataTypes=new Set(w.keys()),x.resolveBlockData=(e,t)=>E(e,{locale:t,surface:`site`,logger:i.logger,providers:w}));let T=N();return T.size>0&&(x.embeddedDocuments={lookup:T,load:(e,t)=>H(i,e,t)}),o={app:i,deps:x},x};return async(e,t)=>{let n=await i();return Z(e,{...t,locale:t.locale??a.defaultLocale},s(n))}}export{de as createBlockResolver,Z as resolveBlockTree};
@@ -1 +1 @@
1
- import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";var t=e({MAX_BREADCRUMBS:()=>8,ancestorPaths:()=>n,resolveBreadcrumbTrail:()=>r});function n(e){if(!e.startsWith(`/`))return[];let t=e.split(`/`).filter(e=>e!==``),n=[];for(let e=1;e<t.length;e++)n.push(`/${t.slice(0,e).join(`/`)}`);return n}async function r(e,t){let r=(await t.pathsClient.findMany({where:{entityType:e.entityType,entityId:{in:[e.id]},locale:e.locale,isCanonical:!0},limit:1}))[0]?.path;if(r===void 0)return;let i=n(r).slice(-7),a=[],o=[];if(i.length>0){let n=await t.pathsClient.findMany({where:{locale:e.locale,isCanonical:!0,path:{in:i}},limit:i.length}),r=new Map;for(let e of n){let t=r.get(e.entityType);t===void 0?r.set(e.entityType,[e.entityId]):t.push(e.entityId)}let s=new Map;for(let[n,i]of r)for(let[r,a]of await t.publishedTitles(n,i,e.locale))s.set(`${n}\u0000${r}`,a);let c=new Map(n.map(e=>[e.path,e]));for(let e of n)o.push({entity:e.entityType,id:e.entityId});for(let e of i){let t=c.get(e);if(t===void 0)continue;let n=s.get(`${t.entityType}\u0000${t.entityId}`);n!==void 0&&a.push({label:n,href:e})}}if(a.length!==0)return e.label!==void 0&&a.push({label:e.label}),{steps:a,ancestors:o}}export{r as n,t};
1
+ import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";var t=e({MAX_BREADCRUMBS:()=>8,ancestorPaths:()=>n,resolveBreadcrumbTrail:()=>r});function n(e){if(!e.startsWith(`/`))return[];let t=e.split(`/`).filter(e=>e!==``),n=[];for(let e=1;e<t.length;e++)n.push(`/${t.slice(0,e).join(`/`)}`);return n}async function r(e,t){let r=(await t.pathsClient.findMany({where:{entityType:e.entityType,entityId:{in:[e.id]},locale:e.locale,isCanonical:!0},limit:1}))[0]?.path;if(r===void 0)return;let i=n(r).slice(-7),a=[],o=[];if(i.length>0){let n=await t.pathsClient.findMany({where:{locale:e.locale,isCanonical:!0,path:{in:i}},limit:i.length}),r=new Map;for(let e of n){let t=r.get(e.entityType);t===void 0?r.set(e.entityType,[e.entityId]):t.push(e.entityId)}let s=new Map;for(let[n,i]of r)for(let[r,a]of await t.publishedTitles(n,i,e.locale))s.set(`${n}\u0000${r}`,a);let c=new Map(n.map(e=>[e.path,e]));for(let e of n)o.push({entity:e.entityType,id:e.entityId});for(let e of i){let t=c.get(e);if(t===void 0)continue;let n=s.get(`${t.entityType}\u0000${t.entityId}`);n!==void 0&&a.push({label:n,href:e})}}if(e.linkSelf===!0){let n=(await t.publishedTitles(e.entityType,[e.id],e.locale)).get(e.id);return n===void 0?e.label!==void 0&&a.push({label:e.label}):a.push({label:n,href:r}),a.length===0?void 0:{steps:a,ancestors:o}}if(a.length!==0)return e.label!==void 0&&a.push({label:e.label}),{steps:a,ancestors:o}}export{r as n,t};
package/dist/client.d.mts CHANGED
@@ -3,7 +3,21 @@ import { h as FieldDiff } from "./index-cT8Qk4gu.mjs";
3
3
  import { Entity, FieldConfig } from "@murumets-ee/entity";
4
4
  import { Logger } from "@murumets-ee/core";
5
5
  import { AdminClient, FindManyOptions, InferCreateInput, InferEntityDTO, InferUpdateInput } from "@murumets-ee/entity/admin";
6
-
6
+ //#region src/drafts.d.ts
7
+ /** Options for {@link mergeDraft}. */
8
+ interface MergeDraftOptions {
9
+ /**
10
+ * For a `field.reference({ mirror })` column in the patch: the list as the
11
+ * EDITOR last knew it — loaded, or last saved. With it, the patch is merged
12
+ * three ways with the stored list (`mergeMirroredList`), so an id the mirror
13
+ * added or removed while the editor was open is not undone by the editor's
14
+ * stale copy. Without it (a caller that sends none), the patch's list
15
+ * replaces the stored one, as for any other column. Ignored for every other
16
+ * column.
17
+ */
18
+ readonly listBases?: Readonly<Record<string, readonly string[]>> | undefined;
19
+ }
20
+ //#endregion
7
21
  //#region src/client.d.ts
8
22
  interface ContentClientConfig<AllFields extends Record<string, FieldConfig> = Record<string, FieldConfig>> {
9
23
  admin: AdminClient<AllFields>;
@@ -61,7 +75,7 @@ declare class ContentClient<AllFields extends Record<string, FieldConfig> = Reco
61
75
  mergeDraft(entityId: string, patch: InferUpdateInput<AllFields>, locale?: string, user?: {
62
76
  id: string;
63
77
  name?: string;
64
- }): Promise<DraftEntry<InferUpdateInput<AllFields>> | null>;
78
+ }, options?: MergeDraftOptions): Promise<DraftEntry<InferUpdateInput<AllFields>> | null>;
65
79
  getDraft(entityId: string, locale?: string): Promise<DraftEntry<InferUpdateInput<AllFields>> | null>;
66
80
  deleteDraft(entityId: string, locale?: string): Promise<boolean>;
67
81
  publishDraft(entityId: string, locale?: string, user?: {
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./versionable-Br_G5VGR.mjs";import{n as t,r as n,t as r}from"./errors-NRNILhhm.mjs";import{t as i}from"./content-config-store-8omQF5p5.mjs";import{a,d as o,f as s,h as c,i as l,l as u,m as d,n as f,o as p,p as m,r as h,s as g,t as _,u as v}from"./versions-w2ui-Vzb.mjs";import{n as y}from"./plugin-CjgqBSFy.mjs";import{t as b}from"./locale-utils-B0mFoV47.mjs";import{i as x,n as S,r as C}from"./context-D7eEItCY.mjs";import{c as w,f as T,r as E}from"./translation-provenance-B41lEDM-.mjs";import{blockTreeFieldsOf as D,buildPartitionCondition as O}from"@murumets-ee/entity";import{getApp as k,getContext as A,requireSiteFor as ee}from"@murumets-ee/core";import{schemaRegistry as te}from"@murumets-ee/db";import{and as ne,inArray as j}from"drizzle-orm";import"server-only";import{createAdminClient as M}from"@murumets-ee/core/clients";function N(e){return{id:e.id,entityId:e.entityId,locale:e.locale,data:e.data,createdBy:e.createdBy,createdByName:e.createdByName??null,createdAt:e.createdAt,updatedAt:e.updatedAt}}async function P(e,t,n){try{return await e.admin.findById(t,n===`_`?{}:{locale:n})??void 0}catch(n){e.logger?.warn({entity:e.entity.name,entityId:t,error:n},`draft: the live row could not be read to grandfather its block tree`);return}}async function F(e,t,n,r,i){let a=typeof i==`object`&&i?i:void 0;if(!D(e.entity).some(({name:e})=>Object.hasOwn(r,e)&&!Array.isArray(a?.[e])))return a;let o=await P(e,t,n);return o===void 0?a:{...o,...a}}async function I(e,n,r,i=`_`,a){b(i);let s=await C(e);if(!s)throw new t(e.entity.name,`drafts`);let c=o(e.entity,r);e.admin.assertBlockTreesValid(c);let l=D(e.entity).some(({name:e})=>Object.hasOwn(c,e))?await s.findOne({entityId:n,locale:i}):null;await e.admin.assertBlockTreesFitDocument(c,()=>F(e,n,i,c,l?.data),{loadAreaRow:()=>P(e,n,i)}),await e.admin.assertMediaKindsValid(c,l?.data);let u=new Date,d=a?.id??``,f=a?.name??null,p=await s.upsert({entityId:n,locale:i,data:c,createdBy:d,createdByName:f,createdAt:u,updatedAt:u},{target:[`entityId`,`locale`],set:{data:c,updatedAt:u}});return await w({entityName:e.entity.name,entityId:n,locale:i,userId:a?.id,draftData:p.data??{}}),N(p)}const L=[`slug`,`slugMode`,`parent`];async function R(e,n,r,i=`_`,a){b(i);let s=await C(e);if(!s)throw new t(e.entity.name,`drafts`);let c=o(e.entity,r);if(Object.keys(c).length===0){let e=await s.findOne({entityId:n,locale:i});return e?N(e):null}e.admin.assertBlockTreesValid(c),e.admin.assertUpdateValid(c);let l=D(e.entity).some(({name:e})=>Object.hasOwn(c,e)),u=e.entity.frontend?.path!==void 0&&L.some(e=>Object.hasOwn(c,e)),d=l||u?await s.findOne({entityId:n,locale:i}):null;if(await e.admin.assertBlockTreesFitDocument(c,()=>F(e,n,i,c,d?.data),{loadAreaRow:()=>P(e,n,i)}),await e.admin.assertMediaKindsValid(c,d?.data),u){let t={...d?.data??{},...c};if(i===`_`)await e.admin.assertPathAvailable(n,t);else{let r=e.entity.allFields,a={},o={};for(let[e,n]of Object.entries(t))r[e]?.translatable===!0?a[e]=n:o[e]=n;let s=e=>L.some(t=>Object.hasOwn(e,t)&&Object.hasOwn(c,t));s(a)&&await e.admin.assertPathAvailable(n,a,i),s(o)&&await e.admin.assertPathAvailable(n,o)}}let f=new Date,p=await s.upsert({entityId:n,locale:i,data:c,createdBy:a?.id??``,createdByName:a?.name??null,createdAt:f,updatedAt:f},{target:[`entityId`,`locale`],set:{updatedAt:f},mergeJsonb:[`data`]});return await w({entityName:e.entity.name,entityId:n,locale:i,userId:a?.id,draftData:p.data??{}}),N(p)}async function z(e,t,n=`_`){b(n);let r=await C(e);if(!r)return null;let i=await r.findOne({entityId:t,locale:n});return i?N(i):null}async function B(e,t,n=`_`){b(n);let r=await C(e);if(!r)return!1;let i=await r.deleteMany({entityId:t,locale:n});return i.length>0&&await w({entityName:e.entity.name,entityId:t,locale:n,reverted:!0}),i.length>0}async function V(e,n,i=`_`,a){if(b(i),!await C(e))throw new t(e.entity.name,`drafts`);let c=e.versionableOptions?await e.admin.findById(n,m(i)):null;return e.db.transaction(async l=>{let u={...e,db:l},d=await C(u);if(!d)throw new t(e.entity.name,`drafts`);let[p]=await d.deleteMany({entityId:n,locale:i});if(!p)throw new r(n,i);let{publishedAt:m,...h}=p.data??{},g=await s(u,n,o(e.entity,{...h,status:`published`}),i,l),_=H(e.entity,h);return _!==void 0&&await e.admin.updateInternal(n,{_overrides:_},{tx:l}),e.versionableOptions&&await f(u,n,g,c,i,a),g})}function H(e,t){if(!(e.behaviors??[]).some(e=>e.name===`withOverrides`))return;let n=e.allFields._overrides;if(n===void 0||n.internal!==!0)return;let r=t._overrides;if(!(typeof r!=`object`||!r||Array.isArray(r)))return r}async function U(e,t,n=`_`){b(n);let r=await C(e);return r?r.exists({entityId:t,locale:n}):!1}async function W(e,t=`_`){b(t);let n=await C(e);return n?n.count({locale:t}):0}async function G(e,t,n=`_`){b(n);let r=new Set;if(t.length===0)return r;let i=await C(e);if(!i)return r;let a=await i.findMany({where:{entityId:{in:t},locale:n}});for(let e of a)r.add(e.entityId);return r}async function K(e,t,n=`_`){b(n);let r=await z(e,t,n);if(!r)return null;let a=i().defaultLocale,o=n===`_`?{defaultLocale:a}:{locale:n,defaultLocale:a},s=await e.admin.findById(t,o);if(!s)return null;let l=e.entity.allFields,u=s,f=r.data,p={};for(let e of Object.keys(f))p[e]=u[e];let m=c(p,f,l);for(let e of m)e.type===`blockTree`&&(e.blockDiffs=d(Array.isArray(e.from)?e.from:[],Array.isArray(e.to)?e.to:[]));return{diffs:m,current:s,draft:r}}async function q(e,t){let n=(await Y(e,[t])).get(t);return n?Object.values(n):[]}async function J(e,n,r,a){b(r);let o=i();if(r===o.defaultLocale){let t={status:a};a===`published`&&(t.publishedAt=new Date),await e.admin.update(n,t);return}let s=await x(e);if(!s)throw new t(e.entity.name,`localeStatus`);let c={status:a};await e.admin.updateForLocale(n,c,r,{defaultLocale:o.defaultLocale,localeStatusWriter:(e,t,n)=>y(s,e,t,n)})}async function Y(e,t){let n=new Map;if(t.length===0)return n;let r=await x(e);if(!r)return n;let i=await r.findMany({where:{entityId:{in:t}}});for(let e of i){let t=e.entityId,r=e.locale,i=n.get(t);i||(i={},n.set(t,i)),i[r]={locale:r,status:e.status,publishedAt:e.publishedAt??null}}return n}function X(){let e=i(),t=A();return{locales:(t?.availableLocales??e.locales.map(e=>e.code)).map(e=>({code:e})),defaultLocale:t?.defaultLocale??e.defaultLocale}}function re(e,t,n,r){let i={};for(let a of n){if(a.code===r)continue;let n=t?.get(a.code)??{},o=[];for(let t of e)(n[t]===void 0||n[t]===null)&&o.push(t);i[a.code]={complete:o.length===0,total:e.length,translated:e.length-o.length,missing:o}}return i}async function ie(e,t){return(await Z(e,[t])).get(t)??{}}async function Z(e,t){let n=new Map;if(t.length===0)return n;let{locales:r,defaultLocale:i}=X(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return n;let o=te.get(`${e.entity.name}_translations`);if(!o)return n;let s=await ee(e.entity),c=j(o.entityId,t),l=await e.db.select().from(o).where(s===void 0?c:ne(c,O(o,{site:s},e.entity.name))),u=new Map;for(let e of l){let t=e,n=t.entityId,r=t.locale,i={};for(let e of a)i[e]=t[e];let o=u.get(n);o||(o=new Map,u.set(n,o)),o.set(r,i)}let d=await E(e.entity.name,t,e.db);for(let e of t){let t=re(a,u.get(e),r,i),o=d.get(e);for(let[e,n]of Object.entries(t)){let t=T(o,e);t&&(n.freshness=t)}n.set(e,t)}return n}const Q={total:0,complete:0,partial:0,missing:0,outdated:0,machineUnreviewed:0};async function ae(e,t){let{locales:r,defaultLocale:i}=X(),a=t?.locale??r.find(e=>e.code!==i)?.code;if(!a)return{...Q};if(!r.some(e=>e.code===a))throw new n(a,r.map(e=>e.code));let o=await e.admin.findMany();if(o.length===0)return{...Q};let s=await Z(e,o.map(e=>e.id)),c=0,l=0,u=0,d=0,f=0;for(let e of o){let t=s.get(e.id)?.[a];!t||t.translated===0?u++:t.complete?c++:l++,t?.freshness?.outdated===!0&&d++,t?.freshness?.machineUnreviewed===!0&&f++}return{total:o.length,complete:c,partial:l,missing:u,outdated:d,machineUnreviewed:f}}var $=class{ctx;constructor(t){let n=e(t.entity.behaviors);this.ctx=S(t,n)}async create(e){let t=await this.ctx.admin.create(e);return this.ctx.versionableOptions&&await g(this.ctx,t.id,t,null,!1),t}findById(e,t){return this.ctx.admin.findById(e,t)}findMany(e){return this.ctx.admin.findMany(e)}async update(e,t){let n=null;this.ctx.versionableOptions&&(n=await this.ctx.admin.findById(e));let r=await this.ctx.admin.update(e,t);if(this.ctx.versionableOptions&&n){let t=v(n,r),i=await g(this.ctx,e,r,t,!1);if(i!==void 0)return await u(this.ctx,e,i),await a(this.ctx,e),{...r,_version:i};await a(this.ctx,e)}return r}delete(e){return this.ctx.admin.delete(e)}saveTranslation(e,t,n){return this.ctx.admin.saveTranslation(e,t,n)}deleteTranslation(e,t){return this.ctx.admin.deleteTranslation(e,t)}getVersions(e,t){return l(this.ctx,e,t)}getVersion(e,t,n){return h(this.ctx,e,t,n)}compareVersions(e,t,n,r){return _(this.ctx,e,t,n,r)}restoreVersion(e,t,n){return p(this.ctx,e,t,n)}pruneVersions(e,t){return a(this.ctx,e,t)}createVersionSnapshot(e,t,n,r,i){return f(this.ctx,e,t,n,r,i)}getTranslationStatus(e){return ie(this.ctx,e)}getTranslationStatusBatch(e){return Z(this.ctx,e)}getTranslationReport(e){return ae(this.ctx,e)}getLocaleStatus(e){return q(this.ctx,e)}setLocaleStatus(e,t,n){return J(this.ctx,e,t,n)}getLocaleStatusBatch(e){return Y(this.ctx,e)}saveDraft(e,t,n,r){return I(this.ctx,e,t,n,r)}mergeDraft(e,t,n,r){return R(this.ctx,e,t,n,r)}getDraft(e,t){return z(this.ctx,e,t)}deleteDraft(e,t){return B(this.ctx,e,t)}publishDraft(e,t,n){return V(this.ctx,e,t,n)}hasDraft(e,t){return U(this.ctx,e,t)}countDrafts(e){return W(this.ctx,e)}hasDraftBatch(e,t){return G(this.ctx,e,t)}compareDraftWithCurrent(e,t){return K(this.ctx,e,t)}};function oe(e){return new $({admin:M(e),entity:e,logger:k().logger.child({content:e.name})})}export{$ as ContentClient,oe as createContentClient};
1
+ import{t as e}from"./versionable-Br_G5VGR.mjs";import{n as t,r as n,t as r}from"./errors-NRNILhhm.mjs";import{t as i}from"./content-config-store-8omQF5p5.mjs";import{a,d as o,f as s,h as c,i as l,l as u,m as d,n as f,o as p,p as m,r as h,s as g,t as _,u as v}from"./versions-z7zE9x-1.mjs";import{t as y}from"./registry-CaPbY5lm.mjs";import{n as ee}from"./plugin-Jwqhfnc7.mjs";import{t as b}from"./locale-utils-B0mFoV47.mjs";import{i as x,n as te,r as S}from"./context-D7eEItCY.mjs";import{c as C,f as w,r as T}from"./translation-provenance-B41lEDM-.mjs";import{blockTreeFieldsOf as E,buildPartitionCondition as D}from"@murumets-ee/entity";import{getApp as O,getContext as ne,requireSiteFor as re}from"@murumets-ee/core";import{schemaRegistry as ie}from"@murumets-ee/db";import{and as k,eq as A,inArray as ae}from"drizzle-orm";import"server-only";import{createAdminClient as j}from"@murumets-ee/core/clients";import{mergeMirroredList as M,readIdList as N}from"@murumets-ee/entity/admin";function P(e){return{id:e.id,entityId:e.entityId,locale:e.locale,data:e.data,createdBy:e.createdBy,createdByName:e.createdByName??null,createdAt:e.createdAt,updatedAt:e.updatedAt}}async function F(e,t,n){try{return await e.admin.findById(t,n===`_`?{}:{locale:n})??void 0}catch(n){e.logger?.warn({entity:e.entity.name,entityId:t,error:n},`draft: the live row could not be read to grandfather its block tree`);return}}async function I(e,t,n,r,i){let a=typeof i==`object`&&i?i:void 0;if(!E(e.entity).some(({name:e})=>Object.hasOwn(r,e)&&!Array.isArray(a?.[e])))return a;let o=await F(e,t,n);return o===void 0?a:{...o,...a}}async function L(e,n,r,i=`_`,a){b(i);let s=await S(e);if(!s)throw new t(e.entity.name,`drafts`);let c=o(e.entity,r);e.admin.assertBlockTreesValid(c);let l=E(e.entity).some(({name:e})=>Object.hasOwn(c,e))?await s.findOne({entityId:n,locale:i}):null;await e.admin.assertBlockTreesFitDocument(c,()=>I(e,n,i,c,l?.data),{loadAreaRow:()=>F(e,n,i)}),await e.admin.assertMediaKindsValid(c,l?.data);let u=new Date,d=a?.id??``,f=a?.name??null,p=await s.upsert({entityId:n,locale:i,data:c,createdBy:d,createdByName:f,createdAt:u,updatedAt:u},{target:[`entityId`,`locale`],set:{data:c,updatedAt:u}});return await C({entityName:e.entity.name,entityId:n,locale:i,userId:a?.id,draftData:p.data??{}}),P(p)}function R(e,t,n){let r=new Map;if(n===void 0)return r;for(let[i,a]of Object.entries(n)){let n=Object.hasOwn(e.allFields,i)?e.allFields[i]:void 0;n?.type!==`reference`||n.mirror===void 0||Array.isArray(t[i])&&r.set(i,a)}return r}async function z(e,t,n,r,i){let a=y(e).drafts,o=a?.table,s=o?.id,c=o?.entityId,l=o?.locale;if(!a||!s||!c||!l)throw Error(`${e}_drafts has no id/entityId/locale column to lock`);return await t.select({id:s}).from(a.table).where(k(A(c,r),A(l,i))).for(`update`),n.findOne({entityId:r,locale:i})}const B=[`slug`,`slugMode`,`parent`];async function V(e,n,r,i=`_`,a,s={}){b(i);let c=await S(e);if(!c)throw new t(e.entity.name,`drafts`);let l=o(e.entity,r);if(Object.keys(l).length===0){let e=await c.findOne({entityId:n,locale:i});return e?P(e):null}e.admin.assertBlockTreesValid(l),e.admin.assertUpdateValid(l);let u=E(e.entity).some(({name:e})=>Object.hasOwn(l,e)),d=e.entity.frontend?.path!==void 0&&B.some(e=>Object.hasOwn(l,e)),f=u||d?await c.findOne({entityId:n,locale:i}):null;if(await e.admin.assertBlockTreesFitDocument(l,()=>I(e,n,i,l,f?.data),{loadAreaRow:()=>F(e,n,i)}),await e.admin.assertMediaKindsValid(l,f?.data),d){let t={...f?.data??{},...l};if(i===`_`)await e.admin.assertPathAvailable(n,t);else{let r=e.entity.allFields,a={},o={};for(let[e,n]of Object.entries(t))r[e]?.translatable===!0?a[e]=n:o[e]=n;let s=e=>B.some(t=>Object.hasOwn(e,t)&&Object.hasOwn(l,t));s(a)&&await e.admin.assertPathAvailable(n,a,i),s(o)&&await e.admin.assertPathAvailable(n,o)}}let p=new Date,m=e=>e.upsert({entityId:n,locale:i,data:l,createdBy:a?.id??``,createdByName:a?.name??null,createdAt:p,updatedAt:p},{target:[`entityId`,`locale`],set:{updatedAt:p},mergeJsonb:[`data`]}),h=R(e.entity,l,s.listBases),g=h.size===0?await m(c):await e.db.transaction(async r=>{let a=await S({...e,db:r});if(!a)throw new t(e.entity.name,`drafts`);let o=(await z(e.entity.name,r,a,n,i))?.data??{},s;for(let[t,i]of h){let a=o[t];Array.isArray(a)||(s??=await e.admin.findById(n,{tx:r,lock:`update`}),a=s?.[t]),l[t]=M(i,N(l[t]),N(a))}return m(a)});return await C({entityName:e.entity.name,entityId:n,locale:i,userId:a?.id,draftData:g.data??{}}),P(g)}async function H(e,t,n=`_`){b(n);let r=await S(e);if(!r)return null;let i=await r.findOne({entityId:t,locale:n});return i?P(i):null}async function U(e,t,n=`_`){b(n);let r=await S(e);if(!r)return!1;let i=await r.deleteMany({entityId:t,locale:n});return i.length>0&&await C({entityName:e.entity.name,entityId:t,locale:n,reverted:!0}),i.length>0}async function W(e,n,i=`_`,a){if(b(i),!await S(e))throw new t(e.entity.name,`drafts`);let c=e.versionableOptions?await e.admin.findById(n,m(i)):null;return e.db.transaction(async l=>{let u={...e,db:l},d=await S(u);if(!d)throw new t(e.entity.name,`drafts`);let[p]=await d.deleteMany({entityId:n,locale:i});if(!p)throw new r(n,i);let{publishedAt:m,...h}=p.data??{},g=await s(u,n,o(e.entity,{...h,status:`published`}),i,l),_=G(e.entity,h);return _!==void 0&&await e.admin.updateInternal(n,{_overrides:_},{tx:l}),e.versionableOptions&&await f(u,n,g,c,i,a),g})}function G(e,t){if(!(e.behaviors??[]).some(e=>e.name===`withOverrides`))return;let n=e.allFields._overrides;if(n===void 0||n.internal!==!0)return;let r=t._overrides;if(!(typeof r!=`object`||!r||Array.isArray(r)))return r}async function K(e,t,n=`_`){b(n);let r=await S(e);return r?r.exists({entityId:t,locale:n}):!1}async function q(e,t=`_`){b(t);let n=await S(e);return n?n.count({locale:t}):0}async function oe(e,t,n=`_`){b(n);let r=new Set;if(t.length===0)return r;let i=await S(e);if(!i)return r;let a=await i.findMany({where:{entityId:{in:t},locale:n}});for(let e of a)r.add(e.entityId);return r}async function se(e,t,n=`_`){b(n);let r=await H(e,t,n);if(!r)return null;let a=i().defaultLocale,o=n===`_`?{defaultLocale:a}:{locale:n,defaultLocale:a},s=await e.admin.findById(t,o);if(!s)return null;let l=e.entity.allFields,u=s,f=r.data,p={};for(let e of Object.keys(f))p[e]=u[e];let m=c(p,f,l);for(let e of m)e.type===`blockTree`&&(e.blockDiffs=d(Array.isArray(e.from)?e.from:[],Array.isArray(e.to)?e.to:[]));return{diffs:m,current:s,draft:r}}async function ce(e,t){let n=(await J(e,[t])).get(t);return n?Object.values(n):[]}async function le(e,n,r,a){b(r);let o=i();if(r===o.defaultLocale){let t={status:a};a===`published`&&(t.publishedAt=new Date),await e.admin.update(n,t);return}let s=await x(e);if(!s)throw new t(e.entity.name,`localeStatus`);let c={status:a};await e.admin.updateForLocale(n,c,r,{defaultLocale:o.defaultLocale,localeStatusWriter:(e,t,n)=>ee(s,e,t,n)})}async function J(e,t){let n=new Map;if(t.length===0)return n;let r=await x(e);if(!r)return n;let i=await r.findMany({where:{entityId:{in:t}}});for(let e of i){let t=e.entityId,r=e.locale,i=n.get(t);i||(i={},n.set(t,i)),i[r]={locale:r,status:e.status,publishedAt:e.publishedAt??null}}return n}function Y(){let e=i(),t=ne();return{locales:(t?.availableLocales??e.locales.map(e=>e.code)).map(e=>({code:e})),defaultLocale:t?.defaultLocale??e.defaultLocale}}function X(e,t,n,r){let i={};for(let a of n){if(a.code===r)continue;let n=t?.get(a.code)??{},o=[];for(let t of e)(n[t]===void 0||n[t]===null)&&o.push(t);i[a.code]={complete:o.length===0,total:e.length,translated:e.length-o.length,missing:o}}return i}async function ue(e,t){return(await Z(e,[t])).get(t)??{}}async function Z(e,t){let n=new Map;if(t.length===0)return n;let{locales:r,defaultLocale:i}=Y(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return n;let o=ie.get(`${e.entity.name}_translations`);if(!o)return n;let s=await re(e.entity),c=ae(o.entityId,t),l=await e.db.select().from(o).where(s===void 0?c:k(c,D(o,{site:s},e.entity.name))),u=new Map;for(let e of l){let t=e,n=t.entityId,r=t.locale,i={};for(let e of a)i[e]=t[e];let o=u.get(n);o||(o=new Map,u.set(n,o)),o.set(r,i)}let d=await T(e.entity.name,t,e.db);for(let e of t){let t=X(a,u.get(e),r,i),o=d.get(e);for(let[e,n]of Object.entries(t)){let t=w(o,e);t&&(n.freshness=t)}n.set(e,t)}return n}const Q={total:0,complete:0,partial:0,missing:0,outdated:0,machineUnreviewed:0};async function de(e,t){let{locales:r,defaultLocale:i}=Y(),a=t?.locale??r.find(e=>e.code!==i)?.code;if(!a)return{...Q};if(!r.some(e=>e.code===a))throw new n(a,r.map(e=>e.code));let o=await e.admin.findMany();if(o.length===0)return{...Q};let s=await Z(e,o.map(e=>e.id)),c=0,l=0,u=0,d=0,f=0;for(let e of o){let t=s.get(e.id)?.[a];!t||t.translated===0?u++:t.complete?c++:l++,t?.freshness?.outdated===!0&&d++,t?.freshness?.machineUnreviewed===!0&&f++}return{total:o.length,complete:c,partial:l,missing:u,outdated:d,machineUnreviewed:f}}var $=class{ctx;constructor(t){let n=e(t.entity.behaviors);this.ctx=te(t,n)}async create(e){let t=await this.ctx.admin.create(e);return this.ctx.versionableOptions&&await g(this.ctx,t.id,t,null,!1),t}findById(e,t){return this.ctx.admin.findById(e,t)}findMany(e){return this.ctx.admin.findMany(e)}async update(e,t){let n=null;this.ctx.versionableOptions&&(n=await this.ctx.admin.findById(e));let r=await this.ctx.admin.update(e,t);if(this.ctx.versionableOptions&&n){let t=v(n,r),i=await g(this.ctx,e,r,t,!1);if(i!==void 0)return await u(this.ctx,e,i),await a(this.ctx,e),{...r,_version:i};await a(this.ctx,e)}return r}delete(e){return this.ctx.admin.delete(e)}saveTranslation(e,t,n){return this.ctx.admin.saveTranslation(e,t,n)}deleteTranslation(e,t){return this.ctx.admin.deleteTranslation(e,t)}getVersions(e,t){return l(this.ctx,e,t)}getVersion(e,t,n){return h(this.ctx,e,t,n)}compareVersions(e,t,n,r){return _(this.ctx,e,t,n,r)}restoreVersion(e,t,n){return p(this.ctx,e,t,n)}pruneVersions(e,t){return a(this.ctx,e,t)}createVersionSnapshot(e,t,n,r,i){return f(this.ctx,e,t,n,r,i)}getTranslationStatus(e){return ue(this.ctx,e)}getTranslationStatusBatch(e){return Z(this.ctx,e)}getTranslationReport(e){return de(this.ctx,e)}getLocaleStatus(e){return ce(this.ctx,e)}setLocaleStatus(e,t,n){return le(this.ctx,e,t,n)}getLocaleStatusBatch(e){return J(this.ctx,e)}saveDraft(e,t,n,r){return L(this.ctx,e,t,n,r)}mergeDraft(e,t,n,r,i){return V(this.ctx,e,t,n,r,i)}getDraft(e,t){return H(this.ctx,e,t)}deleteDraft(e,t){return U(this.ctx,e,t)}publishDraft(e,t,n){return W(this.ctx,e,t,n)}hasDraft(e,t){return K(this.ctx,e,t)}countDrafts(e){return q(this.ctx,e)}hasDraftBatch(e,t){return oe(this.ctx,e,t)}compareDraftWithCurrent(e,t){return se(this.ctx,e,t)}};function fe(e){return new $({admin:j(e),entity:e,logger:O().logger.child({content:e.name})})}export{$ as ContentClient,fe as createContentClient};
@@ -1 +1 @@
1
- import{t as e}from"./versionable-Br_G5VGR.mjs";import{r as t}from"./errors-NRNILhhm.mjs";import{r as n,t as r}from"./locale-entity-CoSq3C77.mjs";import{n as i,t as a}from"./content-config-store-8omQF5p5.mjs";import{r as o}from"./routing-strategy-D6B4ngT9.mjs";import{n as s,t as c}from"./registry-CaPbY5lm.mjs";import"./locale-utils-B0mFoV47.mjs";import{n as l}from"./document-site-B8tmEqLw.mjs";import{m as u,n as d,p as f}from"./translation-provenance-B41lEDM-.mjs";import{n as p}from"./chrome-entity-names-zYJzP-_U.mjs";import{r as m}from"./detail-template-names-CD1Y9n1U.mjs";import{c as h,i as g,n as _,o as v,r as y,t as b}from"./schema-BYrYzsmO.mjs";import{l as x,t as S,u as C}from"./schema-LlK6EkOJ.mjs";import{r as w,t as T}from"./routes-siXmjKDA.mjs";import{n as E}from"./locks-5779ugSW.mjs";import{MAX_PATH_DEPTH as ee,buildPartitionCondition as D,hasPathParent as O,isSiteBound as k,resolveEffectiveSlugMode as A,resolvePattern as j}from"@murumets-ee/entity";import{definePlugin as M,getApp as N,getContext as P,getCurrentSite as F,requireRequestSite as I,resolveImplicitSite as L,runAsCli as R,runInEverySite as z,runInSite as B}from"@murumets-ee/core";import{column as V,defineTable as te,schemaRegistry as H}from"@murumets-ee/db";import{and as U,asc as W,eq as G,getTableColumns as ne,inArray as re,sql as ie}from"drizzle-orm";import{createHash as ae}from"node:crypto";import{z as K}from"zod";const oe=Symbol.for(`@murumets-ee/content:locale-status-writer`);async function q(e,t,n,r){let i=r===`published`?new Date:null;await e.upsert({entityId:t,locale:n,status:r,publishedAt:i},{target:[`entityId`,`locale`],set:{status:r,publishedAt:i}})}function se(e){globalThis[oe]=(n,r)=>{let{localeStatus:i}=c(n.name);if(i)return async(n,a,o,s)=>{let c=P()?.availableLocales??e;if(!c.includes(a))throw new t(a,[...c]);await q(i.makeClient(r,s),n,a,o)}}}function ce(){return{async writeDraft({documentType:e,documentId:t,locale:n,dataPatch:r,user:i,tx:a}){let{drafts:o}=c(e);if(!o)return!1;let s=o.makeClient(a,await l(e)),u=new Date,f=await s.upsert({entityId:t,locale:n,data:r,createdBy:i.id,createdByName:i.name??null,createdAt:u,updatedAt:u},{target:[`entityId`,`locale`],set:{updatedAt:u},mergeJsonb:[`data`]});return d({entityName:e,entityId:t,locale:n,userId:i.id,draftData:f.data??r}),!0},async readDraft({documentType:e,documentId:t,locale:n,db:r}){let{drafts:i}=c(e);if(!i)return null;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?{data:a.data??{},createdBy:a.createdBy??null,createdByName:a.createdByName??null,updatedAt:a.updatedAt??null}:null},async readLocaleStatus({documentType:e,documentId:t,locale:n,db:r}){let{localeStatus:i}=c(e);if(!i)return null;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?a.status===`published`?`published`:`draft`:null},async hasAnyDraft({documentType:e,documentId:t,db:n}){let{drafts:r}=c(e);return r?r.makeClient(n,await l(e)).exists({entityId:t}):!1},async hasAnyLocaleActivity({documentType:e,documentId:t,db:n}){let{localeStatus:r}=c(e),i=await l(e);if(r&&await r.makeClient(n,i).exists({entityId:t}))return!0;let a=H.get(`${e}_translations`);if(a){let r=a.entityId;if(r!==void 0){let o=i===void 0?G(r,t):U(G(r,t),D(a,{site:i},e));if((await n.select({one:ie`1`}).from(a).where(o).limit(1)).length>0)return!0}}return!1}}}function le(e){let{defaultLocale:t}=a();return e===`_`||e===t?{defaultLocale:t}:{locale:e,defaultLocale:t}}function ue(e){let{defaultLocale:t}=a();return e===`_`||e===t?`_`:e}async function de(e,t,n,r){let{drafts:i}=c(e);if(!i)return;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?a.data??void 0:void 0}function fe(){return{async snapshotCurrentState({documentType:t,documentId:n,locale:r,user:i}){let a=N(),o=a.entities.get(t);if(!o)return!1;let s=e(o.behaviors);if(!s)return!1;let c=a.getClient(o),l=c.getDb(),u=ue(r),d=await c.findById(n,le(u));if(!d)return!1;let f=await de(t,n,u,l),p=f?{...d,...f}:{...d},[{createContentContext:m},{createVersionSnapshot:h}]=await Promise.all([import(`./context-D7eEItCY.mjs`).then(e=>e.t),import(`./versions-w2ui-Vzb.mjs`).then(e=>e.c)]);return await h(m({admin:c,entity:o},s),n,p,d,u,{id:i.id,...i.name==null?{}:{name:i.name}}),!0}}}async function pe(e,t,n){let r=ne(b),i=r[y],a=r.entityType,o=r.entityId;if(!i||!a||!o)throw Error(`toolkit_paths is missing '${y}'/entity columns`);let s=await e.selectDistinct({site:i}).from(b).where(U(G(a,t),G(o,n))).limit(2);if(s.length>1)return null;let c=s[0]?.site;return typeof c==`string`?c:void 0}const me=Symbol.for(`@murumets-ee/content:path-writers-factory`),J=Symbol.for(`@murumets-ee/content:cascade-trigger-factory`);function he(e){globalThis[J]=e}function ge(){let e=globalThis[J];return typeof e==`function`?e:void 0}function _e(e,t){return async r=>{let i=await ve(e,t,r);if(i===null)return;let a=_.makeClient(e.db.readWrite,i),o=F();if(o!==void 0&&o!==i&&!(r.action===`delete`||r.action===`release`)){e.logger.warn({entity:r.entityName,id:r.id,site:i,requestSite:o},"toolkit_paths: a cascade reached a document on another site — its path is left for `lumi paths:rebuild` on that site rather than recomputed from this request");return}return o===void 0&&r.viaCascade===!0&&k(t)?B(i,()=>n(a,r)):n(a,r)};async function n(n,r){if(r.action===`delete`){await n.deleteMany({entityType:r.entityName,entityId:r.id});return}if(r.action===`release`){await n.deleteMany({entityType:r.entityName,entityId:r.id,isCanonical:!0});return}let i=a(),o=i.defaultLocale,s=i.reservedPathPrefixes??[],c=r.locale?[r.locale]:i.locales.map(e=>e.code),l=r.viaCascade===!0?e.getSystemClient(t):e.getClient(t),u=async n=>{if(!O(t))return;let i=ge();if(i)try{await i(e,t,r.id,n)}catch(t){e.logger.warn({err:t,entity:r.entityName,id:r.id,locale:n},`toolkit_paths: descendant-cascade trigger threw — the entity write already committed, so tolerating and continuing (paths are rebuildable, D005)`)}};for(let i of c){let a=i===r.locale?r.row??null:await l.findById(r.id,{locale:i,defaultLocale:o});if(!a){await v({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:null,skipReason:`row-unreadable`,reservedPathPrefixes:s,logger:e.logger});continue}let c=await x({entity:t,row:a,locale:i,defaultLocale:o,pathsClient:n}),d=await v({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:c.path,...c.skipReason!==void 0&&{skipReason:c.skipReason},reservedPathPrefixes:s,logger:e.logger});(d.outcome===`renamed`||r.action===`restore`&&d.outcome===`created`)&&await u(i)}}}async function ve(e,t,n){if(n.viaCascade===!0&&k(t)){let t=await pe(e.db.readWrite,n.entityName,n.id);if(t===null)return e.logger.warn({entity:n.entityName,id:n.id},"toolkit_paths: a document has path rows on SEVERAL sites — refusing to guess which one this cascade maintains; `lumi paths:rebuild` per site repairs it"),null;if(t!==void 0)return t;let r=F();if(r!==void 0)return r;let i=await L(e).catch(()=>void 0);return i===void 0?(e.logger.warn({entity:n.entityName,id:n.id,action:n.action},"toolkit_paths: cascade child has no path rows and the request names no site — nothing to maintain here; `lumi paths:rebuild` recomputes it per site"),null):i}return I(`toolkit_paths`,e)}function ye(e,t){return async n=>{let r=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),i=a(),o=i.defaultLocale,s=i.reservedPathPrefixes??[],c=n.locale??o,l=null;n.id!==void 0&&(l=await e.getClient(t).findById(n.id,{locale:c,defaultLocale:o}));let u=A(n.data.slugMode,l?.slugMode);await C({entity:t,data:n.data,currentRow:l,id:n.id,locale:c,defaultLocale:o,slugMode:u,reservedPathPrefixes:s,pathsClient:r})}}function be(){globalThis[me]=(e,t)=>({pathIndexWriter:_e(e,t),pathConflictGuard:ye(e,t)})}const Y=K.object({entityType:K.string().min(1),parentId:K.string().uuid(),locale:K.string().min(1)}),X=K.object({entity:K.string().min(1).optional(),locale:K.string().min(1).optional(),dryRun:K.boolean()}),Z=K.object({dryRun:K.boolean()});async function xe(e,t,n={}){let{entityType:r,parentId:i,locale:o}=t,s=n.maxItems??2e3,c=n.concurrency??5,l={processed:0,regenerated:0,unchanged:0,skipped:0,failed:0,truncated:!1},u=e=>({phase:e,...l}),d=()=>({entityType:r,parentId:i,locale:o,cap:s,...l}),f=e.entities.get(r);if(!f)return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: unknown entity type — nothing to regenerate`),n.onProgress?.(u(`done`)),d();if(!O(f))return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: entity is not a pathParent() hierarchy — nothing to regenerate`),n.onProgress?.(u(`done`)),d();let p=a(),m=p.defaultLocale,g=p.reservedPathPrefixes??[],y=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),b=e.getClient(f),S=b.getTable().parent,C=async t=>{let n=t.id;try{let i=await x({entity:f,row:t,locale:o,defaultLocale:m,pathsClient:y});switch((await v({pathsClient:y,entityType:r,entityId:n,locale:o,path:i.path,...i.skipReason!==void 0&&{skipReason:i.skipReason},reservedPathPrefixes:g,logger:e.logger})).outcome){case`created`:case`renamed`:l.regenerated+=1;break;case`unchanged`:l.unchanged+=1;break;default:l.skipped+=1;break}}catch(t){l.failed+=1,e.logger.warn({err:t,entityType:r,id:n,locale:o},`paths-cascade: failed to regenerate one descendant path — continuing (per-item isolation)`)}},w=[i];for(;w.length>0;){let e=s-l.processed;if(e<=0){l.truncated=!0;break}let t=await b.findMany({where:re(S,w),locale:o,defaultLocale:m,limit:e+1});if(t.length===0)break;let r=t;if(t.length>e&&(r=t.slice(0,e),l.truncated=!0),await h(r,c,C),l.processed+=r.length,n.onProgress?.(u(`walking`)),l.truncated)break;w=r.map(e=>e.id)}return l.truncated&&e.logger.warn({entityType:r,parentId:i,locale:o,processed:l.processed,cap:s},`paths-cascade: descendant subtree exceeded the cap — deeper/remaining descendants were NOT regenerated. Run \`lumi paths:rebuild --entity ${r}\` for guaranteed full coverage.`),n.onProgress?.(u(`done`)),d()}async function Se(e,t,n={}){let r=n.pageSize??200,i=n.concurrency??5,o=n.sampleCap??25,s=t.dryRun,c=a(),l=c.defaultLocale,u=c.reservedPathPrefixes??[],d=c.locales.map(e=>e.code),f=t.locale?d.filter(e=>e===t.locale):d,p=[...e.entities.values()].filter(e=>!!e.frontend?.path&&(t.entity===void 0||e.name===t.entity)),m=t.entity!==void 0&&p.length===0,g=t.locale!==void 0&&f.length===0;m&&e.logger.warn({entity:t.entity,dryRun:s},`paths-rebuild: --entity matches no routable entity (one declaring frontend.path) — nothing to rebuild (likely a typo in the entity name)`),g&&e.logger.warn({locale:t.locale,configuredLocales:d,dryRun:s},`paths-rebuild: --locale matches no configured locale — nothing to rebuild (likely a typo in the locale code)`);let y=m||g?{entity:m,locale:g}:void 0,b=p.some(e=>O(e)),S=s&&b?`Preview reflects a SINGLE pass; a pathParent() hierarchy drifted at multiple levels may need the real (non-dry-run) rebuild's multi-pass convergence to fully resolve. The real run always converges.`:void 0,C=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),w={scanned:0,changed:0,applied:0,failed:0},T=new Map,E={missing:0,conflicts:0,unchanged:0,skipped:0},D=(e,t)=>{let n=T.get(e);n!==t&&(n!==void 0&&n!==`resolved`&&--E[n],T.set(e,t),t!==`resolved`&&(E[t]+=1))},k=[],A=!1,j=e=>{k.length<o?k.push(e):A=!0},M=0,N=e=>({phase:e,dryRun:s,scope:{entity:t.entity??null,locale:t.locale??null},scanned:w.scanned,changed:w.changed,applied:w.applied,failed:w.failed,missing:E.missing,conflicts:E.conflicts,unchanged:E.unchanged,skipped:E.skipped,passes:M,sample:k,sampleTruncated:A,...S===void 0?{}:{hierarchyNote:S},...y===void 0?{}:{unmatchedScope:y}}),P=async(t,n,r,i)=>{let a=r.id,o=!1;try{let c=await x({entity:t,row:r,locale:i,defaultLocale:l,pathsClient:C}),d=await v({pathsClient:C,entityType:n,entityId:a,locale:i,path:c.path,...c.skipReason!==void 0&&{skipReason:c.skipReason},suppressNoteworthy:[`parent-not-indexed`],reservedPathPrefixes:u,logger:e.logger,dryRun:s}),f=`${n}:${a}:${i}`;switch(d.outcome){case`unchanged`:D(f,`unchanged`);break;case`skipped-null`:case`skipped-reserved`:D(f,`skipped`);break;case`conflict`:D(f,`conflicts`),j({kind:`conflict`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath,...d.conflictWith?{conflictWith:d.conflictWith.entityId}:{}});break;case`created`:s?D(f,`missing`):(w.applied+=1,o=!0,D(f,`resolved`)),j({kind:`missing`,entityType:n,entityId:a,locale:i,oldPath:null,newPath:d.newPath});break;case`renamed`:w.changed+=1,s||(w.applied+=1,o=!0),D(f,`resolved`),j({kind:`changed`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath});break}}catch(t){w.failed+=1,e.logger.warn({err:t,entityType:n,id:a,locale:i},`paths-rebuild: failed to process one document — continuing (per-item isolation)`)}return o},F=async t=>{let a=0;for(let o of t){let t=e.getClient(o),s=o.name,c=t.getTable().id;for(let e of f){let u;for(;;){let d=await t.findMany({locale:e,defaultLocale:l,limit:r,orderBy:W(c),...u===void 0?{}:{cursor:{field:`id`,value:u,direction:`asc`}}});if(d.length===0)break;await h(d,i,async t=>{await P(o,s,t,e)&&(a+=1)}),w.scanned+=d.length;let f=d[d.length-1];if(f&&(u=f.id),n.onProgress?.(N(`scanning`)),d.length<r)break}}}return a},L=async t=>{let r=[];for(let[n,i]of T){if(i!==`conflicts`&&i!==`skipped`&&i!==`missing`)continue;let a=n.indexOf(`:`),o=n.lastIndexOf(`:`);if(a<=0||o<=a)continue;let s=n.slice(0,a);if(t.has(s))continue;let c=e.entities.get(s);c!==void 0&&r.push({entity:c,entityType:s,entityId:n.slice(a+1,o),locale:n.slice(o+1)})}if(r.length===0)return 0;let a=0;return await h(r,i,async t=>{let n=await e.getClient(t.entity).findById(t.entityId,{locale:t.locale,defaultLocale:l});n!==null&&(w.scanned+=1,await P(t.entity,t.entityType,n,t.locale)&&(a+=1))}),n.onProgress?.(N(`scanning`)),a},R=p.filter(e=>O(e)),z=new Set(R.map(e=>e.name)),B=s||!b?1:ee;for(let e=0;e<B;e++){M=e+1;let t=await F(e===0?p:R);if(e>0&&(t+=await L(z)),t===0)break}A&&e.logger.warn({scanned:w.scanned,changed:w.changed,missing:E.missing,conflicts:E.conflicts,sampleCap:o},`paths-rebuild: more diff entries than the progress sample can hold — see the aggregate counts; the stored sample is truncated`);let V=N(`done`);n.onProgress?.(V);let{phase:te,...H}=V;return H}async function Ce(e,t,n={}){let r=n.maxRows??1e4,i=n.batchSize??500,a={dryRun:t.dryRun,orphans:[],found:0,deleted:0,truncated:!1,refused:!1},o=new Set([...e.entities.values()].filter(e=>!!e.frontend?.path).map(e=>e.name));if(o.size===0)return e.logger.warn({dryRun:t.dryRun},`paths-prune: no entity declares frontend.path — refusing to treat every row as an orphan`),a.refused=!0,a;let s=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),c=(await s.distinct(`entityType`)).filter(e=>!o.has(e));for(let e of c){let t=await s.count({entityType:e});t>0&&a.orphans.push({entityType:e,rows:t}),a.found+=t}if(t.dryRun)return a;for(let{entityType:e}of a.orphans)for(;a.deleted<r;){let t=await s.findMany({where:{entityType:e},limit:Math.min(i,r-a.deleted)});if(t.length===0)break;let n=await s.deleteMany({id:{in:t.map(e=>e.id)}});a.deleted+=n.length}return a.truncated=a.deleted<a.found&&a.deleted>=r,a.truncated&&e.logger.warn({deleted:a.deleted,found:a.found,maxRows:r},`paths-prune: row cap reached — run again to continue`),a}async function we(e){let{defineJob:t,registerJob:n,QueueClient:r}=await import(`@murumets-ee/queue/client`),i=t({name:`content:paths-cascade`,description:`Regenerate toolkit_paths canonical rows for the descendants of a renamed routable-hierarchy ancestor (one locale). Walks the full bounded subtree in a single invocation.`,schema:Y}),a=t({name:`content:paths-rebuild`,description:`Recompute every routable document's canonical toolkit_paths row (optionally scoped to one entity/locale); dryRun reports the diff without writing. On-demand (lumi paths:rebuild).`,schema:X});n(t({name:`content:paths-prune`,description:`Delete toolkit_paths rows of entity types that are no longer routable (lost frontend.path or unregistered). dryRun reports them without deleting. On-demand (lumi paths:rebuild --orphans).`,schema:Z}),async t=>{let n={dryRun:Z.parse(t.payload).dryRun,orphans:[],found:0,deleted:0,truncated:!1,refused:!1};await R(()=>z(async()=>{let r=await Ce(e,Z.parse(t.payload));for(let e of r.orphans){let t=n.orphans.find(t=>t.entityType===e.entityType);t?t.rows+=e.rows:n.orphans.push({...e})}n.found+=r.found,n.deleted+=r.deleted,n.truncated||=r.truncated,n.refused||=r.refused,t.updateProgress(n),e.logger.info(r,`paths-prune: sweep complete`)},{app:e,label:`paths-prune`}))}),n(i,async t=>{await R(async()=>{let n=await xe(e,Y.parse(t.payload),{onProgress:e=>t.updateProgress(e)});e.logger.info(n,`paths-cascade: descendant regeneration complete`)})}),n(a,async t=>{await R(()=>z(async()=>{let n=await Se(e,X.parse(t.payload),{onProgress:e=>t.updateProgress({...e,site:F()??null})});e.logger.info({dryRun:n.dryRun,scanned:n.scanned,changed:n.changed,missing:n.missing,conflicts:n.conflicts,applied:n.applied,failed:n.failed,passes:n.passes,...n.hierarchyNote===void 0?{}:{hierarchyNote:n.hierarchyNote}},`paths-rebuild: sweep complete`)},{app:e,label:`paths-rebuild`}))}),he(async(e,t,n,a)=>{await new r({app:e,logger:e.logger}).enqueue(i,{entityType:t.name,parentId:n,locale:a})})}const Q=te({name:`toolkit_path_patterns`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityType:V.varchar({length:100,notNull:!0,pgName:`entity_type`}),locale:V.varchar({length:10,notNull:!0}),patternHash:V.varchar({length:64,notNull:!0,pgName:`pattern_hash`}),updatedAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityType`,`locale`]}]}),Te=Q.table;function Ee(e,t,n){let r=j(e,t,n);if(r===null)return null;let i=typeof r==`function`?r.toString():JSON.stringify(r);return ae(`sha256`).update(i).digest(`hex`)}async function De(e){let t=a(),n=t.defaultLocale,r=t.locales.map(e=>e.code),i=Q.makeClient(e.db.readWrite);for(let[t,a]of e.entities){let o=a.frontend?.path;if(o!==void 0)for(let a of r){let r=Ee(o,a,n);if(r===null)continue;let s=await i.findOne({entityType:t,locale:a});s!==null&&s.patternHash===r||(s!==null&&e.logger.warn({entity:t,locale:a,previousHash:s.patternHash,currentHash:r},`toolkit_paths: frontend.path pattern for '${t}' (locale '${a}') changed since last boot — stale toolkit_paths rows may exist; run \`lumi paths:rebuild --entity ${t}\``),await i.upsert({entityType:t,locale:a,patternHash:r},{target:[`entityType`,`locale`],set:{patternHash:r,updatedAt:new Date}}))}}}const Oe=async(e,t,n)=>{if(t.length===0)return new Map;let[{buildEnumerateDeps:r},{getCanonicalPaths:i}]=await Promise.all([import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),import(`./enumerate-CYHJKQz6.mjs`).then(e=>e.n)]),a=r(N()),o=await a.filterPublished(e,t,n);return o.size===0?new Map:i(e,[...o],n,a)},ke=async(e,t)=>{if(e.length===0)return new Set;let{buildEnumerateDeps:n}=await import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),r=n(N()),i=[...new Set(e)],a=i.slice(0,48);i.length>a.length&&N().logger.warn({paths:i.length,max:48},`content: publishedPathsAmong was asked for more paths than it reads; the rest count as unpublished`);let o=await r.pathsClient.findMany({where:{locale:t,isCanonical:!0,path:{in:a}},limit:a.length}),s=new Map;for(let e of o){let t=s.get(e.entityType);t===void 0&&(t=new Map,s.set(e.entityType,t)),t.set(e.entityId,e.path)}let c=new Set;for(let[e,n]of s){let i=await r.filterPublished(e,[...n.keys()],t);for(let[e,t]of n)i.has(e)&&c.add(t)}return c},Ae=async e=>{let[{buildBreadcrumbDeps:t},{resolveBreadcrumbTrail:n}]=await Promise.all([import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),import(`./breadcrumbs-BqSuMNac.mjs`).then(e=>e.t)]);return(await n(e,t(N())))?.steps};function $(e){return g(e,{name:`${e.name}_drafts`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),locale:V.varchar({length:10,notNull:!0,default:`_`}),data:V.jsonb({notNull:!0}),createdBy:V.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:V.varchar({length:255,pgName:`created_by_name`}),createdAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function je(e){return g(e,{name:`${e.name}_locale_status`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),locale:V.varchar({length:10,notNull:!0}),status:V.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:V.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function Me(e){return g(e,{name:`${e.name}_versions`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),version:V.integer({notNull:!0}),locale:V.varchar({length:10,notNull:!0,default:`_`}),data:V.jsonb({notNull:!0}),delta:V.jsonb(),status:V.varchar({length:20}),createdBy:V.varchar({length:255,pgName:`created_by`}),createdByName:V.varchar({length:255,pgName:`created_by_name`}),createdAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:V.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}function Ne(e){if(!e.locales||e.locales.length===0)throw Error(`content() requires at least one locale`);if(!e.defaultLocale)throw Error(`content() requires a defaultLocale`);if(!e.locales.some(t=>t.code===e.defaultLocale))throw Error(`defaultLocale "${e.defaultLocale}" not found in locales: [${e.locales.map(e=>e.code).join(`, `)}]`);if(e.reservedPathPrefixes){for(let t of e.reservedPathPrefixes)if(typeof t!=`string`||!t.startsWith(`/`))throw Error(`content(): reservedPathPrefixes entries must be absolute paths starting with '/', got ${JSON.stringify(t)}`)}let t=e.autoTrash??!0,a=t===!1?void 0:{publishable:!0,...typeof t==`object`&&t.exclude?{exclude:t.exclude}:{}};return M({name:`@murumets-ee/content`,contributes:{"blocks.documentDraftStore":ce(),"blocks.documentVersionStore":fe(),"blocks.publishedPathResolver":Oe,"entity.writeListeners":f,"blocks.publishedPathsAmong":ke,"blocks.breadcrumbTrail":Ae,"sites.baseLocale":async()=>(await import(`./locale-registry-COsJrVZl.mjs`).then(e=>e.n)).resolveInstallBaseLocale(),...a?{"trash.autoEnable":a}:{},...e.reservedPathPrefixes?{"routing.reservedPathPrefixes":e.reservedPathPrefixes}:{}},shared:{settings:[n],features:[{resource:`redirect`,actions:[`manage`],defaultRoles:[`admin`],description:`Create, edit, and delete manual URL redirects (toolkit_redirects).`},{resource:`path`,actions:[`resolve`],defaultRoles:[`admin`,`editor`],description:`Resolve a site URL path to the CMS document that serves it.`}]},server:{entities:[r,o],tables:{toolkitContentLocks:E,toolkitPaths:b,toolkitPathPatterns:Te,toolkitRedirects:S,toolkitTranslationProvenance:u},routes:[...T(),...w()],seeders:[{name:`content:site-chrome`,description:`Create the empty site_chrome row for each placement (header, footer), as a DRAFT, so they can be edited on the page canvas. Publishing one is what makes /v1/_chrome serve it instead of the delivery template's own fallback. Idempotent — an existing row for a placement is left alone.`,available:p,bootstrap:!0,run:async()=>{let{seedSiteChrome:e}=await import(`@murumets-ee/content/seed`);return await e()}},{name:`content:detail-templates`,description:`Create the DRAFT detail template for each entity drawn by one (block-library D009), so its page layout can be edited on the canvas. Until a template is published its rows keep the plain fallback page. Idempotent — an existing template is left alone.`,available:m,bootstrap:!0,run:async()=>{let{seedDetailTemplates:e}=await import(`@murumets-ee/content/seed`);return await e()}}],init:async t=>{i(e);let n=0,r=0,a=0;for(let[e,i]of t.entities){if(i.behaviors?.some(e=>e.name===`versionable`)){let t=`${e}_versions`;if(!H.has(t)){let n=Me(i);H.register(t,n.table),s(e,{versions:n})}n++}let t=i.behaviors?.some(e=>e.name===`publishable`),o=i.allFields,c=Object.values(o).some(e=>e.translatable);if(t&&c){let t=`${e}_locale_status`;if(!H.has(t)){let n=je(i);H.register(t,n.table),s(e,{localeStatus:n})}r++}if(t){let t=`${e}_drafts`;if(!H.has(t)){let n=$(i);H.register(t,n.table),s(e,{drafts:n})}a++}}se(e.locales.map(e=>e.code)),be();try{await we(t)}catch(e){t.logger.warn({err:e},"content: failed to register routing path jobs — descendant cascade + `lumi paths:rebuild` will be unavailable (path writes still succeed)")}t.logger.info({locales:e.locales.map(e=>e.code),defaultLocale:e.defaultLocale,versionableEntities:n,localeStatusEntities:r,draftEntities:a},`Content plugin initialized`)},onReady:async e=>{try{await De(e)}catch(t){e.logger.error({err:t},"content: boot-time frontend-path pattern-drift check failed — continuing app boot without it. Inspect the logged `err` for the cause: a transient DB error can be retried (and `lumi paths:rebuild` re-derives the index if a pattern actually changed), whereas a structural cause — e.g. a missing migration leaving `toolkit_path_patterns` absent — will recur on every boot and needs `lumi migrate`")}}}})}export{q as n,Ne as t};
1
+ import{t as e}from"./versionable-Br_G5VGR.mjs";import{r as t}from"./errors-NRNILhhm.mjs";import{r as n,t as r}from"./locale-entity-CoSq3C77.mjs";import{n as i,t as a}from"./content-config-store-8omQF5p5.mjs";import{r as o}from"./routing-strategy-D6B4ngT9.mjs";import{n as s,t as c}from"./registry-CaPbY5lm.mjs";import"./locale-utils-B0mFoV47.mjs";import{n as l}from"./document-site-B8tmEqLw.mjs";import{m as u,n as d,p as f}from"./translation-provenance-B41lEDM-.mjs";import{n as p}from"./chrome-entity-names-zYJzP-_U.mjs";import{r as m}from"./detail-template-names-CD1Y9n1U.mjs";import{c as h,i as g,n as _,o as v,r as y,t as b}from"./schema-BYrYzsmO.mjs";import{l as x,t as S,u as C}from"./schema-LlK6EkOJ.mjs";import{r as w,t as T}from"./routes-siXmjKDA.mjs";import{n as E}from"./locks-5779ugSW.mjs";import{MAX_PATH_DEPTH as ee,buildPartitionCondition as D,hasPathParent as O,isSiteBound as k,resolveEffectiveSlugMode as A,resolvePattern as j}from"@murumets-ee/entity";import{definePlugin as M,getApp as N,getContext as P,getCurrentSite as F,requireRequestSite as I,resolveImplicitSite as L,runAsCli as R,runInEverySite as z,runInSite as B}from"@murumets-ee/core";import{column as V,defineTable as te,schemaRegistry as H}from"@murumets-ee/db";import{and as U,asc as W,eq as G,getTableColumns as ne,inArray as re,sql as ie}from"drizzle-orm";import{createHash as ae}from"node:crypto";import{z as K}from"zod";const oe=Symbol.for(`@murumets-ee/content:locale-status-writer`);async function q(e,t,n,r){let i=r===`published`?new Date:null;await e.upsert({entityId:t,locale:n,status:r,publishedAt:i},{target:[`entityId`,`locale`],set:{status:r,publishedAt:i}})}function se(e){globalThis[oe]=(n,r)=>{let{localeStatus:i}=c(n.name);if(i)return async(n,a,o,s)=>{let c=P()?.availableLocales??e;if(!c.includes(a))throw new t(a,[...c]);await q(i.makeClient(r,s),n,a,o)}}}function ce(){return{async writeDraft({documentType:e,documentId:t,locale:n,dataPatch:r,user:i,tx:a}){let{drafts:o}=c(e);if(!o)return!1;let s=o.makeClient(a,await l(e)),u=new Date,f=await s.upsert({entityId:t,locale:n,data:r,createdBy:i.id,createdByName:i.name??null,createdAt:u,updatedAt:u},{target:[`entityId`,`locale`],set:{updatedAt:u},mergeJsonb:[`data`]});return d({entityName:e,entityId:t,locale:n,userId:i.id,draftData:f.data??r}),!0},async readDraft({documentType:e,documentId:t,locale:n,db:r}){let{drafts:i}=c(e);if(!i)return null;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?{data:a.data??{},createdBy:a.createdBy??null,createdByName:a.createdByName??null,updatedAt:a.updatedAt??null}:null},async readLocaleStatus({documentType:e,documentId:t,locale:n,db:r}){let{localeStatus:i}=c(e);if(!i)return null;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?a.status===`published`?`published`:`draft`:null},async hasAnyDraft({documentType:e,documentId:t,db:n}){let{drafts:r}=c(e);return r?r.makeClient(n,await l(e)).exists({entityId:t}):!1},async hasAnyLocaleActivity({documentType:e,documentId:t,db:n}){let{localeStatus:r}=c(e),i=await l(e);if(r&&await r.makeClient(n,i).exists({entityId:t}))return!0;let a=H.get(`${e}_translations`);if(a){let r=a.entityId;if(r!==void 0){let o=i===void 0?G(r,t):U(G(r,t),D(a,{site:i},e));if((await n.select({one:ie`1`}).from(a).where(o).limit(1)).length>0)return!0}}return!1}}}function le(e){let{defaultLocale:t}=a();return e===`_`||e===t?{defaultLocale:t}:{locale:e,defaultLocale:t}}function ue(e){let{defaultLocale:t}=a();return e===`_`||e===t?`_`:e}async function de(e,t,n,r){let{drafts:i}=c(e);if(!i)return;let a=await i.makeClient(r,await l(e)).findOne({entityId:t,locale:n});return a?a.data??void 0:void 0}function fe(){return{async snapshotCurrentState({documentType:t,documentId:n,locale:r,user:i}){let a=N(),o=a.entities.get(t);if(!o)return!1;let s=e(o.behaviors);if(!s)return!1;let c=a.getClient(o),l=c.getDb(),u=ue(r),d=await c.findById(n,le(u));if(!d)return!1;let f=await de(t,n,u,l),p=f?{...d,...f}:{...d},[{createContentContext:m},{createVersionSnapshot:h}]=await Promise.all([import(`./context-D7eEItCY.mjs`).then(e=>e.t),import(`./versions-z7zE9x-1.mjs`).then(e=>e.c)]);return await h(m({admin:c,entity:o},s),n,p,d,u,{id:i.id,...i.name==null?{}:{name:i.name}}),!0}}}async function pe(e,t,n){let r=ne(b),i=r[y],a=r.entityType,o=r.entityId;if(!i||!a||!o)throw Error(`toolkit_paths is missing '${y}'/entity columns`);let s=await e.selectDistinct({site:i}).from(b).where(U(G(a,t),G(o,n))).limit(2);if(s.length>1)return null;let c=s[0]?.site;return typeof c==`string`?c:void 0}const me=Symbol.for(`@murumets-ee/content:path-writers-factory`),J=Symbol.for(`@murumets-ee/content:cascade-trigger-factory`);function he(e){globalThis[J]=e}function ge(){let e=globalThis[J];return typeof e==`function`?e:void 0}function _e(e,t){return async r=>{let i=await ve(e,t,r);if(i===null)return;let a=_.makeClient(e.db.readWrite,i),o=F();if(o!==void 0&&o!==i&&!(r.action===`delete`||r.action===`release`)){e.logger.warn({entity:r.entityName,id:r.id,site:i,requestSite:o},"toolkit_paths: a cascade reached a document on another site — its path is left for `lumi paths:rebuild` on that site rather than recomputed from this request");return}return o===void 0&&r.viaCascade===!0&&k(t)?B(i,()=>n(a,r)):n(a,r)};async function n(n,r){if(r.action===`delete`){await n.deleteMany({entityType:r.entityName,entityId:r.id});return}if(r.action===`release`){await n.deleteMany({entityType:r.entityName,entityId:r.id,isCanonical:!0});return}let i=a(),o=i.defaultLocale,s=i.reservedPathPrefixes??[],c=r.locale?[r.locale]:i.locales.map(e=>e.code),l=r.viaCascade===!0?e.getSystemClient(t):e.getClient(t),u=async n=>{if(!O(t))return;let i=ge();if(i)try{await i(e,t,r.id,n)}catch(t){e.logger.warn({err:t,entity:r.entityName,id:r.id,locale:n},`toolkit_paths: descendant-cascade trigger threw — the entity write already committed, so tolerating and continuing (paths are rebuildable, D005)`)}};for(let i of c){let a=i===r.locale?r.row??null:await l.findById(r.id,{locale:i,defaultLocale:o});if(!a){await v({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:null,skipReason:`row-unreadable`,reservedPathPrefixes:s,logger:e.logger});continue}let c=await x({entity:t,row:a,locale:i,defaultLocale:o,pathsClient:n}),d=await v({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:c.path,...c.skipReason!==void 0&&{skipReason:c.skipReason},reservedPathPrefixes:s,logger:e.logger});(d.outcome===`renamed`||r.action===`restore`&&d.outcome===`created`)&&await u(i)}}}async function ve(e,t,n){if(n.viaCascade===!0&&k(t)){let t=await pe(e.db.readWrite,n.entityName,n.id);if(t===null)return e.logger.warn({entity:n.entityName,id:n.id},"toolkit_paths: a document has path rows on SEVERAL sites — refusing to guess which one this cascade maintains; `lumi paths:rebuild` per site repairs it"),null;if(t!==void 0)return t;let r=F();if(r!==void 0)return r;let i=await L(e).catch(()=>void 0);return i===void 0?(e.logger.warn({entity:n.entityName,id:n.id,action:n.action},"toolkit_paths: cascade child has no path rows and the request names no site — nothing to maintain here; `lumi paths:rebuild` recomputes it per site"),null):i}return I(`toolkit_paths`,e)}function ye(e,t){return async n=>{let r=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),i=a(),o=i.defaultLocale,s=i.reservedPathPrefixes??[],c=n.locale??o,l=null;n.id!==void 0&&(l=await e.getClient(t).findById(n.id,{locale:c,defaultLocale:o}));let u=A(n.data.slugMode,l?.slugMode);await C({entity:t,data:n.data,currentRow:l,id:n.id,locale:c,defaultLocale:o,slugMode:u,reservedPathPrefixes:s,pathsClient:r})}}function be(){globalThis[me]=(e,t)=>({pathIndexWriter:_e(e,t),pathConflictGuard:ye(e,t)})}const Y=K.object({entityType:K.string().min(1),parentId:K.string().uuid(),locale:K.string().min(1)}),X=K.object({entity:K.string().min(1).optional(),locale:K.string().min(1).optional(),dryRun:K.boolean()}),Z=K.object({dryRun:K.boolean()});async function xe(e,t,n={}){let{entityType:r,parentId:i,locale:o}=t,s=n.maxItems??2e3,c=n.concurrency??5,l={processed:0,regenerated:0,unchanged:0,skipped:0,failed:0,truncated:!1},u=e=>({phase:e,...l}),d=()=>({entityType:r,parentId:i,locale:o,cap:s,...l}),f=e.entities.get(r);if(!f)return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: unknown entity type — nothing to regenerate`),n.onProgress?.(u(`done`)),d();if(!O(f))return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: entity is not a pathParent() hierarchy — nothing to regenerate`),n.onProgress?.(u(`done`)),d();let p=a(),m=p.defaultLocale,g=p.reservedPathPrefixes??[],y=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),b=e.getClient(f),S=b.getTable().parent,C=async t=>{let n=t.id;try{let i=await x({entity:f,row:t,locale:o,defaultLocale:m,pathsClient:y});switch((await v({pathsClient:y,entityType:r,entityId:n,locale:o,path:i.path,...i.skipReason!==void 0&&{skipReason:i.skipReason},reservedPathPrefixes:g,logger:e.logger})).outcome){case`created`:case`renamed`:l.regenerated+=1;break;case`unchanged`:l.unchanged+=1;break;default:l.skipped+=1;break}}catch(t){l.failed+=1,e.logger.warn({err:t,entityType:r,id:n,locale:o},`paths-cascade: failed to regenerate one descendant path — continuing (per-item isolation)`)}},w=[i];for(;w.length>0;){let e=s-l.processed;if(e<=0){l.truncated=!0;break}let t=await b.findMany({where:re(S,w),locale:o,defaultLocale:m,limit:e+1});if(t.length===0)break;let r=t;if(t.length>e&&(r=t.slice(0,e),l.truncated=!0),await h(r,c,C),l.processed+=r.length,n.onProgress?.(u(`walking`)),l.truncated)break;w=r.map(e=>e.id)}return l.truncated&&e.logger.warn({entityType:r,parentId:i,locale:o,processed:l.processed,cap:s},`paths-cascade: descendant subtree exceeded the cap — deeper/remaining descendants were NOT regenerated. Run \`lumi paths:rebuild --entity ${r}\` for guaranteed full coverage.`),n.onProgress?.(u(`done`)),d()}async function Se(e,t,n={}){let r=n.pageSize??200,i=n.concurrency??5,o=n.sampleCap??25,s=t.dryRun,c=a(),l=c.defaultLocale,u=c.reservedPathPrefixes??[],d=c.locales.map(e=>e.code),f=t.locale?d.filter(e=>e===t.locale):d,p=[...e.entities.values()].filter(e=>!!e.frontend?.path&&(t.entity===void 0||e.name===t.entity)),m=t.entity!==void 0&&p.length===0,g=t.locale!==void 0&&f.length===0;m&&e.logger.warn({entity:t.entity,dryRun:s},`paths-rebuild: --entity matches no routable entity (one declaring frontend.path) — nothing to rebuild (likely a typo in the entity name)`),g&&e.logger.warn({locale:t.locale,configuredLocales:d,dryRun:s},`paths-rebuild: --locale matches no configured locale — nothing to rebuild (likely a typo in the locale code)`);let y=m||g?{entity:m,locale:g}:void 0,b=p.some(e=>O(e)),S=s&&b?`Preview reflects a SINGLE pass; a pathParent() hierarchy drifted at multiple levels may need the real (non-dry-run) rebuild's multi-pass convergence to fully resolve. The real run always converges.`:void 0,C=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),w={scanned:0,changed:0,applied:0,failed:0},T=new Map,E={missing:0,conflicts:0,unchanged:0,skipped:0},D=(e,t)=>{let n=T.get(e);n!==t&&(n!==void 0&&n!==`resolved`&&--E[n],T.set(e,t),t!==`resolved`&&(E[t]+=1))},k=[],A=!1,j=e=>{k.length<o?k.push(e):A=!0},M=0,N=e=>({phase:e,dryRun:s,scope:{entity:t.entity??null,locale:t.locale??null},scanned:w.scanned,changed:w.changed,applied:w.applied,failed:w.failed,missing:E.missing,conflicts:E.conflicts,unchanged:E.unchanged,skipped:E.skipped,passes:M,sample:k,sampleTruncated:A,...S===void 0?{}:{hierarchyNote:S},...y===void 0?{}:{unmatchedScope:y}}),P=async(t,n,r,i)=>{let a=r.id,o=!1;try{let c=await x({entity:t,row:r,locale:i,defaultLocale:l,pathsClient:C}),d=await v({pathsClient:C,entityType:n,entityId:a,locale:i,path:c.path,...c.skipReason!==void 0&&{skipReason:c.skipReason},suppressNoteworthy:[`parent-not-indexed`],reservedPathPrefixes:u,logger:e.logger,dryRun:s}),f=`${n}:${a}:${i}`;switch(d.outcome){case`unchanged`:D(f,`unchanged`);break;case`skipped-null`:case`skipped-reserved`:D(f,`skipped`);break;case`conflict`:D(f,`conflicts`),j({kind:`conflict`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath,...d.conflictWith?{conflictWith:d.conflictWith.entityId}:{}});break;case`created`:s?D(f,`missing`):(w.applied+=1,o=!0,D(f,`resolved`)),j({kind:`missing`,entityType:n,entityId:a,locale:i,oldPath:null,newPath:d.newPath});break;case`renamed`:w.changed+=1,s||(w.applied+=1,o=!0),D(f,`resolved`),j({kind:`changed`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath});break}}catch(t){w.failed+=1,e.logger.warn({err:t,entityType:n,id:a,locale:i},`paths-rebuild: failed to process one document — continuing (per-item isolation)`)}return o},F=async t=>{let a=0;for(let o of t){let t=e.getClient(o),s=o.name,c=t.getTable().id;for(let e of f){let u;for(;;){let d=await t.findMany({locale:e,defaultLocale:l,limit:r,orderBy:W(c),...u===void 0?{}:{cursor:{field:`id`,value:u,direction:`asc`}}});if(d.length===0)break;await h(d,i,async t=>{await P(o,s,t,e)&&(a+=1)}),w.scanned+=d.length;let f=d[d.length-1];if(f&&(u=f.id),n.onProgress?.(N(`scanning`)),d.length<r)break}}}return a},L=async t=>{let r=[];for(let[n,i]of T){if(i!==`conflicts`&&i!==`skipped`&&i!==`missing`)continue;let a=n.indexOf(`:`),o=n.lastIndexOf(`:`);if(a<=0||o<=a)continue;let s=n.slice(0,a);if(t.has(s))continue;let c=e.entities.get(s);c!==void 0&&r.push({entity:c,entityType:s,entityId:n.slice(a+1,o),locale:n.slice(o+1)})}if(r.length===0)return 0;let a=0;return await h(r,i,async t=>{let n=await e.getClient(t.entity).findById(t.entityId,{locale:t.locale,defaultLocale:l});n!==null&&(w.scanned+=1,await P(t.entity,t.entityType,n,t.locale)&&(a+=1))}),n.onProgress?.(N(`scanning`)),a},R=p.filter(e=>O(e)),z=new Set(R.map(e=>e.name)),B=s||!b?1:ee;for(let e=0;e<B;e++){M=e+1;let t=await F(e===0?p:R);if(e>0&&(t+=await L(z)),t===0)break}A&&e.logger.warn({scanned:w.scanned,changed:w.changed,missing:E.missing,conflicts:E.conflicts,sampleCap:o},`paths-rebuild: more diff entries than the progress sample can hold — see the aggregate counts; the stored sample is truncated`);let V=N(`done`);n.onProgress?.(V);let{phase:te,...H}=V;return H}async function Ce(e,t,n={}){let r=n.maxRows??1e4,i=n.batchSize??500,a={dryRun:t.dryRun,orphans:[],found:0,deleted:0,truncated:!1,refused:!1},o=new Set([...e.entities.values()].filter(e=>!!e.frontend?.path).map(e=>e.name));if(o.size===0)return e.logger.warn({dryRun:t.dryRun},`paths-prune: no entity declares frontend.path — refusing to treat every row as an orphan`),a.refused=!0,a;let s=_.makeClient(e.db.readWrite,await I(`toolkit_paths`,e)),c=(await s.distinct(`entityType`)).filter(e=>!o.has(e));for(let e of c){let t=await s.count({entityType:e});t>0&&a.orphans.push({entityType:e,rows:t}),a.found+=t}if(t.dryRun)return a;for(let{entityType:e}of a.orphans)for(;a.deleted<r;){let t=await s.findMany({where:{entityType:e},limit:Math.min(i,r-a.deleted)});if(t.length===0)break;let n=await s.deleteMany({id:{in:t.map(e=>e.id)}});a.deleted+=n.length}return a.truncated=a.deleted<a.found&&a.deleted>=r,a.truncated&&e.logger.warn({deleted:a.deleted,found:a.found,maxRows:r},`paths-prune: row cap reached — run again to continue`),a}async function we(e){let{defineJob:t,registerJob:n,QueueClient:r}=await import(`@murumets-ee/queue/client`),i=t({name:`content:paths-cascade`,description:`Regenerate toolkit_paths canonical rows for the descendants of a renamed routable-hierarchy ancestor (one locale). Walks the full bounded subtree in a single invocation.`,schema:Y}),a=t({name:`content:paths-rebuild`,description:`Recompute every routable document's canonical toolkit_paths row (optionally scoped to one entity/locale); dryRun reports the diff without writing. On-demand (lumi paths:rebuild).`,schema:X});n(t({name:`content:paths-prune`,description:`Delete toolkit_paths rows of entity types that are no longer routable (lost frontend.path or unregistered). dryRun reports them without deleting. On-demand (lumi paths:rebuild --orphans).`,schema:Z}),async t=>{let n={dryRun:Z.parse(t.payload).dryRun,orphans:[],found:0,deleted:0,truncated:!1,refused:!1};await R(()=>z(async()=>{let r=await Ce(e,Z.parse(t.payload));for(let e of r.orphans){let t=n.orphans.find(t=>t.entityType===e.entityType);t?t.rows+=e.rows:n.orphans.push({...e})}n.found+=r.found,n.deleted+=r.deleted,n.truncated||=r.truncated,n.refused||=r.refused,t.updateProgress(n),e.logger.info(r,`paths-prune: sweep complete`)},{app:e,label:`paths-prune`}))}),n(i,async t=>{await R(async()=>{let n=await xe(e,Y.parse(t.payload),{onProgress:e=>t.updateProgress(e)});e.logger.info(n,`paths-cascade: descendant regeneration complete`)})}),n(a,async t=>{await R(()=>z(async()=>{let n=await Se(e,X.parse(t.payload),{onProgress:e=>t.updateProgress({...e,site:F()??null})});e.logger.info({dryRun:n.dryRun,scanned:n.scanned,changed:n.changed,missing:n.missing,conflicts:n.conflicts,applied:n.applied,failed:n.failed,passes:n.passes,...n.hierarchyNote===void 0?{}:{hierarchyNote:n.hierarchyNote}},`paths-rebuild: sweep complete`)},{app:e,label:`paths-rebuild`}))}),he(async(e,t,n,a)=>{await new r({app:e,logger:e.logger}).enqueue(i,{entityType:t.name,parentId:n,locale:a})})}const Q=te({name:`toolkit_path_patterns`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityType:V.varchar({length:100,notNull:!0,pgName:`entity_type`}),locale:V.varchar({length:10,notNull:!0}),patternHash:V.varchar({length:64,notNull:!0,pgName:`pattern_hash`}),updatedAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityType`,`locale`]}]}),Te=Q.table;function Ee(e,t,n){let r=j(e,t,n);if(r===null)return null;let i=typeof r==`function`?r.toString():JSON.stringify(r);return ae(`sha256`).update(i).digest(`hex`)}async function De(e){let t=a(),n=t.defaultLocale,r=t.locales.map(e=>e.code),i=Q.makeClient(e.db.readWrite);for(let[t,a]of e.entities){let o=a.frontend?.path;if(o!==void 0)for(let a of r){let r=Ee(o,a,n);if(r===null)continue;let s=await i.findOne({entityType:t,locale:a});s!==null&&s.patternHash===r||(s!==null&&e.logger.warn({entity:t,locale:a,previousHash:s.patternHash,currentHash:r},`toolkit_paths: frontend.path pattern for '${t}' (locale '${a}') changed since last boot — stale toolkit_paths rows may exist; run \`lumi paths:rebuild --entity ${t}\``),await i.upsert({entityType:t,locale:a,patternHash:r},{target:[`entityType`,`locale`],set:{patternHash:r,updatedAt:new Date}}))}}}const Oe=async(e,t,n)=>{if(t.length===0)return new Map;let[{buildEnumerateDeps:r},{getCanonicalPaths:i}]=await Promise.all([import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),import(`./enumerate-CYHJKQz6.mjs`).then(e=>e.n)]),a=r(N()),o=await a.filterPublished(e,t,n);return o.size===0?new Map:i(e,[...o],n,a)},ke=async(e,t)=>{if(e.length===0)return new Set;let{buildEnumerateDeps:n}=await import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),r=n(N()),i=[...new Set(e)],a=i.slice(0,48);i.length>a.length&&N().logger.warn({paths:i.length,max:48},`content: publishedPathsAmong was asked for more paths than it reads; the rest count as unpublished`);let o=await r.pathsClient.findMany({where:{locale:t,isCanonical:!0,path:{in:a}},limit:a.length}),s=new Map;for(let e of o){let t=s.get(e.entityType);t===void 0&&(t=new Map,s.set(e.entityType,t)),t.set(e.entityId,e.path)}let c=new Set;for(let[e,n]of s){let i=await r.filterPublished(e,[...n.keys()],t);for(let[e,t]of n)i.has(e)&&c.add(t)}return c},Ae=async e=>{let[{buildBreadcrumbDeps:t},{resolveBreadcrumbTrail:n}]=await Promise.all([import(`./resolve-context-DklN53BH.mjs`).then(e=>e.u),import(`./breadcrumbs-Cewtn9FO.mjs`).then(e=>e.t)]);return(await n(e,t(N())))?.steps};function $(e){return g(e,{name:`${e.name}_drafts`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),locale:V.varchar({length:10,notNull:!0,default:`_`}),data:V.jsonb({notNull:!0}),createdBy:V.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:V.varchar({length:255,pgName:`created_by_name`}),createdAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function je(e){return g(e,{name:`${e.name}_locale_status`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),locale:V.varchar({length:10,notNull:!0}),status:V.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:V.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function Me(e){return g(e,{name:`${e.name}_versions`,columns:{id:V.uuid({primaryKey:!0,defaultRandom:!0}),entityId:V.uuid({notNull:!0,pgName:`entity_id`}),version:V.integer({notNull:!0}),locale:V.varchar({length:10,notNull:!0,default:`_`}),data:V.jsonb({notNull:!0}),delta:V.jsonb(),status:V.varchar({length:20}),createdBy:V.varchar({length:255,pgName:`created_by`}),createdByName:V.varchar({length:255,pgName:`created_by_name`}),createdAt:V.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:V.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}function Ne(e){if(!e.locales||e.locales.length===0)throw Error(`content() requires at least one locale`);if(!e.defaultLocale)throw Error(`content() requires a defaultLocale`);if(!e.locales.some(t=>t.code===e.defaultLocale))throw Error(`defaultLocale "${e.defaultLocale}" not found in locales: [${e.locales.map(e=>e.code).join(`, `)}]`);if(e.reservedPathPrefixes){for(let t of e.reservedPathPrefixes)if(typeof t!=`string`||!t.startsWith(`/`))throw Error(`content(): reservedPathPrefixes entries must be absolute paths starting with '/', got ${JSON.stringify(t)}`)}let t=e.autoTrash??!0,a=t===!1?void 0:{publishable:!0,...typeof t==`object`&&t.exclude?{exclude:t.exclude}:{}};return M({name:`@murumets-ee/content`,contributes:{"blocks.documentDraftStore":ce(),"blocks.documentVersionStore":fe(),"blocks.publishedPathResolver":Oe,"entity.writeListeners":f,"entity.relationMirror":{apply:async e=>(await import(`./relation-mirror-BS-gk5GT.mjs`)).applyRelationMirror(e),backfill:async e=>(await import(`./relation-mirror-BS-gk5GT.mjs`)).backfillRelationMirror(e)},"blocks.publishedPathsAmong":ke,"blocks.breadcrumbTrail":Ae,"sites.baseLocale":async()=>(await import(`./locale-registry-COsJrVZl.mjs`).then(e=>e.n)).resolveInstallBaseLocale(),...a?{"trash.autoEnable":a}:{},...e.reservedPathPrefixes?{"routing.reservedPathPrefixes":e.reservedPathPrefixes}:{}},shared:{settings:[n],features:[{resource:`redirect`,actions:[`manage`],defaultRoles:[`admin`],description:`Create, edit, and delete manual URL redirects (toolkit_redirects).`},{resource:`path`,actions:[`resolve`],defaultRoles:[`admin`,`editor`],description:`Resolve a site URL path to the CMS document that serves it.`}]},server:{entities:[r,o],tables:{toolkitContentLocks:E,toolkitPaths:b,toolkitPathPatterns:Te,toolkitRedirects:S,toolkitTranslationProvenance:u},routes:[...T(),...w()],seeders:[{name:`content:site-chrome`,description:`Create the empty site_chrome row for each placement (header, footer), as a DRAFT, so they can be edited on the page canvas. Publishing one is what makes /v1/_chrome serve it instead of the delivery template's own fallback. Idempotent — an existing row for a placement is left alone.`,available:p,bootstrap:!0,run:async()=>{let{seedSiteChrome:e}=await import(`@murumets-ee/content/seed`);return await e()}},{name:`content:detail-templates`,description:`Create the DRAFT detail template for each entity drawn by one (block-library D009), so its page layout can be edited on the canvas. Until a template is published its rows keep the plain fallback page. Idempotent — an existing template is left alone.`,available:m,bootstrap:!0,run:async()=>{let{seedDetailTemplates:e}=await import(`@murumets-ee/content/seed`);return await e()}}],init:async t=>{i(e);let n=0,r=0,a=0;for(let[e,i]of t.entities){if(i.behaviors?.some(e=>e.name===`versionable`)){let t=`${e}_versions`;if(!H.has(t)){let n=Me(i);H.register(t,n.table),s(e,{versions:n})}n++}let t=i.behaviors?.some(e=>e.name===`publishable`),o=i.allFields,c=Object.values(o).some(e=>e.translatable);if(t&&c){let t=`${e}_locale_status`;if(!H.has(t)){let n=je(i);H.register(t,n.table),s(e,{localeStatus:n})}r++}if(t){let t=`${e}_drafts`;if(!H.has(t)){let n=$(i);H.register(t,n.table),s(e,{drafts:n})}a++}}se(e.locales.map(e=>e.code)),be();try{await we(t)}catch(e){t.logger.warn({err:e},"content: failed to register routing path jobs — descendant cascade + `lumi paths:rebuild` will be unavailable (path writes still succeed)")}t.logger.info({locales:e.locales.map(e=>e.code),defaultLocale:e.defaultLocale,versionableEntities:n,localeStatusEntities:r,draftEntities:a},`Content plugin initialized`)},onReady:async e=>{try{await De(e)}catch(t){e.logger.error({err:t},"content: boot-time frontend-path pattern-drift check failed — continuing app boot without it. Inspect the logged `err` for the cause: a transient DB error can be retried (and `lumi paths:rebuild` re-derives the index if a pattern actually changed), whereas a structural cause — e.g. a missing migration leaving `toolkit_path_patterns` absent — will recur on every boot and needs `lumi migrate`")}}}})}export{q as n,Ne as t};
package/dist/plugin.mjs CHANGED
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"./locale-entity-CoSq3C77.mjs";import{t as r}from"./content-config-store-8omQF5p5.mjs";import{n as i,r as a,t as o}from"./routing-strategy-D6B4ngT9.mjs";import{t as s}from"./plugin-CjgqBSFy.mjs";export{n as Locale,a as Message,e as ROUTING_STRATEGIES,s as content,r as getContentConfig,t as i18nSettings,o as localePrefixForStrategy,i as prefixDefaultLocaleForStrategy};
1
+ import{n as e,r as t,t as n}from"./locale-entity-CoSq3C77.mjs";import{t as r}from"./content-config-store-8omQF5p5.mjs";import{n as i,r as a,t as o}from"./routing-strategy-D6B4ngT9.mjs";import{t as s}from"./plugin-Jwqhfnc7.mjs";export{n as Locale,a as Message,e as ROUTING_STRATEGIES,s as content,r as getContentConfig,t as i18nSettings,o as localePrefixForStrategy,i as prefixDefaultLocaleForStrategy};
@@ -0,0 +1 @@
1
+ import{t as e}from"./registry-CaPbY5lm.mjs";import{getEntityTitleField as t}from"@murumets-ee/entity";import{RELATION_MIRROR_BACKFILL_MAX_ROWS as n,getAppOrUndefined as r,getContext as i,requireSiteFor as a}from"@murumets-ee/core";import{and as o,asc as s,eq as c,gt as l,inArray as u}from"drizzle-orm";import"server-only";import{createSystemAdminClient as d}from"@murumets-ee/core/clients";import{RELATION_MIRROR_LIST_CAP as f,applyRelationMirrorEdit as p,isRelationMirrorLive as m,readIdList as h,relationMirrorsOf as g}from"@murumets-ee/entity/admin";import{createAuditDbWriterMany as _,createAuditLogger as v}from"@murumets-ee/logging";const y=`entity.relation.mirror`,b=2*f,x=new WeakMap;function S(){let e=i(),t=e===void 0?void 0:x.get(e);t===void 0&&(t={written:0},e!==void 0&&x.set(e,t));let n=t;return()=>n.written>=b?!1:(n.written+=1,!0)}function C(e,t,n){return{row:e,...n!==void 0&&{from:n.from,to:n.to},draftsChanged:t.changed,draftsFull:t.full}}async function w(e,t,n){let r=_(t),i;if(await v({logger:e.logger,dbWriterMany:async e=>{try{await r(e)}catch(e){throw i={err:e},e}}}).logMany?.(n),i!==void 0)throw i.err}async function T(t){let{app:n,target:r,targetField:i,targetId:a,sourceId:o,op:s,site:l}=t,u=d(r,n),f=e(r.name).drafts,m=async e=>{let d={changed:0,full:0};if(f!==void 0){let t=f.makeClient(e??n.db.readWrite,l);if(e!==void 0){let{id:t,entityId:n}=f.table;if(t===void 0||n===void 0)throw Error(`${r.name}_drafts has no id/entityId column to lock`);await e.select({id:t}).from(f.table).where(c(n,a)).for(`update`)}let u=await t.findMany({where:{entityId:a}});for(let n of u){let r=n.data??{};if(!Array.isArray(r[i]))continue;let a=p(r[i],o,s);if(`skipped`in a){a.skipped===`full`&&(d.full+=1);continue}d.changed+=1,e!==void 0&&await t.update({id:n.id},{data:{...r,[i]:a.next}})}}let m=await u.findById(a,e===void 0?{}:{tx:e,lock:`update`}),g;if(m===null)g=C(`missing`,d);else{let t=h(m[i]),n=p(m[i],o,s);if(`skipped`in n)g=C(n.skipped,d);else{if(e!==void 0){let t={[i]:n.next};await u.update(a,t,{tx:e,relationMirror:!1})}g=C(`changed`,d,{from:t,to:n.next})}}if(e!==void 0&&(g.row===`changed`||g.draftsChanged>0)){let r=t.audit?.(g)??null;r!==null&&await w(n,e,[r])}return g};return t.dryRun?m(void 0):n.db.readWrite.transaction(e=>m(e))}async function E(t){let{app:n,target:r,targetField:i,targetIds:a,sourceId:o,op:s,site:c}=t;if(a.length===0)return{pending:[],full:[]};let l=[...a],f=d(r,n),m=f.getTable(),h=await f.findMany({where:u(m.id,l),limit:l.length}),g=e(r.name).drafts,_=g===void 0?[]:await g.makeClient(n.db.readWrite,c).findMany({where:{entityId:{in:l}}}),v=new Set,y=new Set,b=(e,t)=>{let n=p(t,o,s);`skipped`in n?n.skipped===`full`&&y.add(e):v.add(e)};for(let e of h)b(e.id,e[i]);let x=new Set(h.map(e=>e.id));for(let e of _){let t=e.data??{};!x.has(e.entityId)||!Array.isArray(t[i])||b(e.entityId,t[i])}return{pending:l.filter(e=>v.has(e)),full:l.filter(e=>y.has(e)&&!v.has(e))}}function D(e,n,r){let i=n[t(e)];return typeof i==`string`&&i.length>0?i:r}function O(){return i()?.app??r()}async function k(e,t){try{return await a(e,t)}catch{return null}}async function A(e){let t=O();if(!t)return;let n=t.entities.get(e.entityName);if(!n)return;let r=t.logger;if(await k(n,t)!==e.site){r.warn({entity:e.entityName,id:e.id,site:e.site},`relation mirror refused: the request acts on a different site than the written row`);return}e.dropped>0&&r.warn({entity:e.entityName,id:e.id,dropped:e.dropped,cap:f},`relation mirror: more ids changed than one write carries — the rest were left out`);let i=D(n,e.row,e.id),a=S(),o=0;for(let s of e.changes){let c=t.entities.get(s.targetEntity);if(!c)continue;let l=[{op:`remove`,ids:s.removed},{op:`add`,ids:s.added}];for(let{op:u,ids:d}of l){let l;try{l=await E({app:t,target:c,targetField:s.targetField,targetIds:d,sourceId:e.id,op:u,site:e.site})}catch(t){r.warn({err:t,entity:e.entityName,id:e.id,target:c.name},`relation mirror: could not read the other side — skipped`);continue}l.full.length>0&&r.warn({entity:e.entityName,id:e.id,target:c.name,targetIds:l.full,cap:f},`relation mirror: the other list is full — not appended`);for(let d of l.pending){if(!a()){o+=1;continue}let l=u===`add`?`mirrored from ${n.name} "${i}": added`:`mirrored from ${n.name} "${i}": removed`,p;try{p=await T({app:t,target:c,targetField:s.targetField,targetId:d,sourceId:e.id,op:u,site:e.site,audit:t=>({action:y,entityType:c.name,entityId:d,...e.user!==void 0&&{userId:e.user.id},...e.user?.name!==void 0&&{userName:e.user.name},...t.from!==void 0&&t.to!==void 0&&{changes:{[s.targetField]:{from:t.from,to:t.to}}},metadata:{note:l,op:u,cause:e.cause,field:s.targetField,mirroredFrom:{entity:n.name,id:e.id,field:s.field,title:i},draftsChanged:t.draftsChanged,...e.site!==void 0&&{site:e.site}}})})}catch(t){r.warn({err:t,entity:e.entityName,id:e.id,target:c.name,targetId:d},`relation mirror: could not update one document — skipped`);continue}(p.row===`full`||p.draftsFull>0)&&r.warn({entity:e.entityName,id:e.id,target:c.name,targetId:d,cap:f},`relation mirror: the other list is full — not appended`)}}}o>0&&r.warn({entity:e.entityName,id:e.id,skipped:o,cap:b},"relation mirror: this request has written as many documents as one request may — the rest were left out; run `lumi relations:mirror-backfill` to restore missing links")}async function j(e){let t=O();if(!t)throw Error(`relations:mirror-backfill needs a booted app`);let r=t.entities.get(e.entity);if(!r)throw Error(`relations:mirror-backfill: no entity '${e.entity}'`);let u=g(r).find(t=>t.field===e.field);if(!u)throw Error(`relations:mirror-backfill: '${e.entity}.${e.field}' declares no mirror — add mirror: '<field on the other entity>' to both fields first.`);let p=t.entities.get(u.targetEntity);if(!p)throw Error(`relations:mirror-backfill: no entity '${u.targetEntity}'`);let _=await a(r,t),v=e.dryRun===!0,b=Math.max(1,Math.min(e.maxRows??n,n)),x=d(r,t),S=x.getTable(),C={scanned:0,links:0,rowsAdded:0,draftsAdded:0,alreadyPresent:0,skippedFull:0,missingTargets:0},w=[],E=i()?.user,k=!1,A;for(;;){let e=[];A!==void 0&&e.push(l(S.id,A)),S.status!==void 0&&e.push(c(S.status,`published`));let n=b-C.scanned;if(n<=0){k=!0;break}let i=Math.min(100,n),a=await x.findMany({...e.length>0&&{where:o(...e)},orderBy:s(S.id),limit:i});if(a.length===0)break;for(let e of a){let n=e.id;if(A=n,C.scanned+=1,!m(r,e))continue;let i=D(r,e,n),a=h(e[u.field]).filter(e=>e!==n).slice(0,f);for(let e of a){C.links+=1;let a;try{a=await T({app:t,target:p,targetField:u.targetField,targetId:e,sourceId:n,op:`add`,site:_,dryRun:v,audit:t=>({action:y,entityType:p.name,entityId:e,...E!==void 0&&{userId:E.id},...t.from!==void 0&&t.to!==void 0&&{changes:{[u.targetField]:{from:t.from,to:t.to}}},metadata:{note:`mirrored from ${r.name} "${i}": added (backfill)`,op:`add`,cause:`backfill`,field:u.targetField,mirroredFrom:{entity:r.name,id:n,field:u.field,title:i},draftsChanged:t.draftsChanged,..._!==void 0&&{site:_}}})})}catch(i){t.logger.warn({err:i,entity:r.name,id:n,target:p.name,targetId:e},`relations:mirror-backfill: could not update one document — skipped`);continue}a.row===`missing`?C.missingTargets+=1:a.row===`full`?C.skippedFull+=1:a.row===`unchanged`?C.alreadyPresent+=1:C.rowsAdded+=1,C.draftsAdded+=a.draftsChanged,C.skippedFull+=a.draftsFull,a.row===`changed`&&w.length<25&&w.push({targetId:e,sourceId:n,where:`row`}),a.draftsChanged>0&&w.length<25&&w.push({targetId:e,sourceId:n,where:`draft`})}}if(a.length<i)break}return{entity:r.name,field:u.field,targetEntity:p.name,targetField:u.targetField,site:_,dryRun:v,...C,truncated:k,sample:w}}export{A as applyRelationMirror,j as backfillRelationMirror};
@@ -1 +1 @@
1
- import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";import{i as t,n}from"./errors-NRNILhhm.mjs";import{t as r}from"./content-config-store-8omQF5p5.mjs";import{n as i}from"./plugin-CjgqBSFy.mjs";import{t as a}from"./locale-utils-B0mFoV47.mjs";import{a as o,i as s}from"./context-D7eEItCY.mjs";import{schemaRegistry as c}from"@murumets-ee/db";import{eq as l}from"drizzle-orm";import"server-only";function u(e){return e.match(/\S+|\s+/g)??[]}function d(e,t){if(e===t)return e?[{type:`equal`,value:e}]:[];if(!e)return[{type:`add`,value:t}];if(!t)return[{type:`remove`,value:e}];let n=u(e),r=u(t),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=1;e<=i;e++){let t=o[e],i=o[e-1];if(!(!t||!i))for(let o=1;o<=a;o++)n[e-1]===r[o-1]?t[o]=(i[o-1]??0)+1:t[o]=Math.max(i[o]??0,t[o-1]??0)}let s=[],c=i,l=a,d=[];for(;c>0||l>0;){let e=c>0?n[c-1]:void 0,t=l>0?r[l-1]:void 0;if(c>0&&l>0&&e!==void 0&&e===t)d.push({type:`equal`,value:e}),c--,l--;else if(l>0&&t!==void 0&&(c===0||(o[c]?.[l-1]??0)>=(o[c-1]?.[l]??0)))d.push({type:`add`,value:t}),l--;else if(e!==void 0)d.push({type:`remove`,value:e}),c--;else break}d.reverse();for(let e of d){let t=s[s.length-1];t&&t.type===e.type?t.value+=e.value:s.push({...e})}return s}function f(e){return e.replace(/<[^>]*>/g,` `).replace(/\s+/g,` `).trim()}function p(e){return e==null?``:e instanceof Date?e.toISOString():typeof e==`object`?JSON.stringify(e):String(e)}function m(e,t,n,r){if(JSON.stringify(t)===JSON.stringify(n))return null;switch(r?.type??g(t,n)){case`text`:case`slug`:return{field:e,type:`text`,from:t,to:n,segments:d(p(t),p(n))};case`richtext`:return{field:e,type:`richtext`,from:t,to:n,segments:d(f(p(t)),f(p(n)))};case`number`:return{field:e,type:`number`,from:t,to:n};case`boolean`:return{field:e,type:`boolean`,from:t,to:n};case`date`:return{field:e,type:`date`,from:t,to:n};case`select`:return{field:e,type:`select`,from:t,to:n};case`media`:return{field:e,type:`media`,from:t,to:n};case`json`:return{field:e,type:`json`,from:t,to:n};case`blockTree`:return{field:e,type:`blockTree`,from:t,to:n};default:return{field:e,type:`unknown`,from:t,to:n}}}function h(e,t,n){let r=new Set([...Object.keys(e),...Object.keys(t)]),i=[];for(let a of r){if(a===`id`||a===`createdAt`||a===`updatedAt`)continue;let r=m(a,e[a],t[a],n?.[a]);r&&i.push(r)}return i}function g(e,t){let n=e??t;if(typeof n==`number`)return`number`;if(typeof n==`boolean`)return`boolean`;if(n instanceof Date)return`date`;if(typeof n==`string`)return/^\d{4}-\d{2}-\d{2}/.test(n)?`date`:`text`;if(Array.isArray(n)){let e=n[0];return n.length>0&&typeof e==`object`&&e&&`type`in e&&`id`in e?`blockTree`:`json`}return typeof n==`object`?`json`:`unknown`}function _(e,t){if(typeof e==`string`||typeof t==`string`)return d(p(e),p(t))}function v(e){if(typeof e!=`object`||!e)return null;let t=e;if(typeof t.id!=`string`||typeof t.type!=`string`)return null;let n={...t.props??{}};return t.traits!==void 0&&(n.traits=t.traits),t.children!==void 0&&(n.children=t.children),{id:t.id,type:t.type,props:n}}function y(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let i of n){let n=e[i],a=t[i];JSON.stringify(n)!==JSON.stringify(a)&&r.push({field:i,from:n,to:a,segments:_(n,a)})}return r}function b(e){let t=new Map,n=0;for(let r of e){let e=v(r);e&&(t.set(e.id,{block:e,index:n}),n++)}return t}function x(e,t){let n=b(e),r=b(t),i=[],a=new Set;for(let[e,{block:t,index:o}]of r){a.add(e);let r=n.get(e);if(!r){i.push({blockId:e,blockType:t.type,status:`added`,toIndex:o});continue}let s=r.block,c=r.index,l=y(s.props,t.props),u=c!==o;l.length===0&&!u?i.push({blockId:e,blockType:t.type,status:`unchanged`,fromIndex:c,toIndex:o}):l.length>0?i.push({blockId:e,blockType:t.type,status:`changed`,fromIndex:c,toIndex:o,propDiffs:l}):i.push({blockId:e,blockType:t.type,status:`moved`,fromIndex:c,toIndex:o})}for(let[e,{block:t,index:r}]of n)a.has(e)||i.push({blockId:e,blockType:t.type,status:`removed`,fromIndex:r});return i}function S(e){return e===`_`||e===r().defaultLocale}function C(e){let{defaultLocale:t}=r();return S(e)?{defaultLocale:t}:{locale:e,defaultLocale:t}}async function w(e,t,n,o,c){if(c!==void 0&&e.db!==c)throw Error("applyLocaleRoutedUpdate: `tx` must be the context's own db handle");let l=await s(e);return e.admin.updateForLocale(t,n,o,{defaultLocale:r().defaultLocale,...c!==void 0&&{tx:c},...l!==null&&{localeStatusWriter:(e,t,n)=>(a(t),i(l,e,t,n))}})}const T=new Set([`id`,`createdAt`,`createdBy`,`updatedAt`,`updatedBy`]);function E(e,t){let n=e.allFields,r={};for(let e of Object.keys(t)){if(T.has(e)||e.startsWith(`_`)||!Object.hasOwn(n,e))continue;let i=n[e];i&&i.internal===!0||(r[e]=t[e])}return r}function D(e,t){return JSON.stringify(e)===JSON.stringify(t)}function O(e,t){let n={};for(let r of Object.keys(t))r===`id`||r.startsWith(`_`)||D(e[r],t[r])||(n[r]=t[r]);return n}var k=e({compareVersions:()=>M,createVersionSnapshot:()=>I,getVersion:()=>j,getVersions:()=>A,pruneVersions:()=>P,restoreVersion:()=>N,saveVersionSnapshot:()=>F,writeVersionColumn:()=>L});async function A(e,t,n){n?.locale!==void 0&&a(n.locale);let r=await o(e);return r?await r.findMany({where:{entityId:t,locale:n?.locale??`_`},orderBy:[{column:`version`,dir:`desc`}],...n?.limit===void 0?{}:{limit:n.limit},...n?.offset===void 0?{}:{offset:n.offset}}):[]}async function j(e,t,n,r=`_`){a(r);let i=await o(e);return i?await i.findOne({entityId:t,version:n,locale:r})??null:null}async function M(e,t,n,r,i=`_`){a(i);let[o,s]=await Promise.all([j(e,t,n,i),j(e,t,r,i)]);if(!o||!s)return null;let c=e.entity.allFields,l=h(o.data,s.data,c);for(let e of l)e.type===`blockTree`&&(e.blockDiffs=x(Array.isArray(e.from)?e.from:[],Array.isArray(e.to)?e.to:[]));return{diffs:l,fromVersion:o,toVersion:s}}async function N(e,r,i,s=`_`){if(a(s),!await o(e))throw new n(e.entity.name,`versions`);let c=await j(e,r,i,s);if(!c)throw new t(r,i,s);let l=c.data;return w(e,r,E(e.entity,l),s)}async function P(e,t,n=`_`){if(a(n),!e.versionableOptions)return;let r=await o(e);if(!r)return;let i=e.versionableOptions.maxPerDoc,s=await r.findMany({where:{entityId:t,locale:n},orderBy:[{column:`version`,dir:`desc`}],limit:i});if(s.length<i)return;let c=s.map(e=>e.id);await r.deleteMany({entityId:t,locale:n,id:{notIn:c}})}async function F(e,t,n,r,i,s=`_`,c){a(s);let l=await o(e);if(!l)return;let[u]=await l.aggregate({select:{maxVersion:{fn:`max`,column:`version`}},where:{entityId:t,locale:s}}),d=Number(u?.maxVersion??0)+1,f=n.status??null;if(i){let e=await l.findOne({entityId:t,isAutosave:!0,version:d,locale:s});if(e)return await l.update({id:e.id},{data:n,delta:r,status:f,createdAt:new Date}),d}return await l.insert({entityId:t,version:d,locale:s,data:n,delta:r,status:f,createdBy:c?.id??null,createdByName:c?.name??null,isAutosave:i}),d}async function I(e,t,n,r,i,o){if(i!==void 0&&a(i),!e.versionableOptions)return;let s=i??`_`,c=await F(e,t,n,r?O(r,n):null,!1,s,o);c!==void 0&&s===`_`&&await L(e,t,c),await P(e,t,s)}async function L(e,t,n){let r=c.get(e.entity.name);r&&await e.db.update(r).set({_version:n}).where(l(r.id,t))}export{P as a,k as c,E as d,w as f,h,A as i,L as l,x as m,I as n,N as o,C as p,j as r,F as s,M as t,O as u};
1
+ import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";import{i as t,n}from"./errors-NRNILhhm.mjs";import{t as r}from"./content-config-store-8omQF5p5.mjs";import{n as i}from"./plugin-Jwqhfnc7.mjs";import{t as a}from"./locale-utils-B0mFoV47.mjs";import{a as o,i as s}from"./context-D7eEItCY.mjs";import{schemaRegistry as c}from"@murumets-ee/db";import{eq as l}from"drizzle-orm";import"server-only";function u(e){return e.match(/\S+|\s+/g)??[]}function d(e,t){if(e===t)return e?[{type:`equal`,value:e}]:[];if(!e)return[{type:`add`,value:t}];if(!t)return[{type:`remove`,value:e}];let n=u(e),r=u(t),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=1;e<=i;e++){let t=o[e],i=o[e-1];if(!(!t||!i))for(let o=1;o<=a;o++)n[e-1]===r[o-1]?t[o]=(i[o-1]??0)+1:t[o]=Math.max(i[o]??0,t[o-1]??0)}let s=[],c=i,l=a,d=[];for(;c>0||l>0;){let e=c>0?n[c-1]:void 0,t=l>0?r[l-1]:void 0;if(c>0&&l>0&&e!==void 0&&e===t)d.push({type:`equal`,value:e}),c--,l--;else if(l>0&&t!==void 0&&(c===0||(o[c]?.[l-1]??0)>=(o[c-1]?.[l]??0)))d.push({type:`add`,value:t}),l--;else if(e!==void 0)d.push({type:`remove`,value:e}),c--;else break}d.reverse();for(let e of d){let t=s[s.length-1];t&&t.type===e.type?t.value+=e.value:s.push({...e})}return s}function f(e){return e.replace(/<[^>]*>/g,` `).replace(/\s+/g,` `).trim()}function p(e){return e==null?``:e instanceof Date?e.toISOString():typeof e==`object`?JSON.stringify(e):String(e)}function m(e,t,n,r){if(JSON.stringify(t)===JSON.stringify(n))return null;switch(r?.type??g(t,n)){case`text`:case`slug`:return{field:e,type:`text`,from:t,to:n,segments:d(p(t),p(n))};case`richtext`:return{field:e,type:`richtext`,from:t,to:n,segments:d(f(p(t)),f(p(n)))};case`number`:return{field:e,type:`number`,from:t,to:n};case`boolean`:return{field:e,type:`boolean`,from:t,to:n};case`date`:return{field:e,type:`date`,from:t,to:n};case`select`:return{field:e,type:`select`,from:t,to:n};case`media`:return{field:e,type:`media`,from:t,to:n};case`json`:return{field:e,type:`json`,from:t,to:n};case`blockTree`:return{field:e,type:`blockTree`,from:t,to:n};default:return{field:e,type:`unknown`,from:t,to:n}}}function h(e,t,n){let r=new Set([...Object.keys(e),...Object.keys(t)]),i=[];for(let a of r){if(a===`id`||a===`createdAt`||a===`updatedAt`)continue;let r=m(a,e[a],t[a],n?.[a]);r&&i.push(r)}return i}function g(e,t){let n=e??t;if(typeof n==`number`)return`number`;if(typeof n==`boolean`)return`boolean`;if(n instanceof Date)return`date`;if(typeof n==`string`)return/^\d{4}-\d{2}-\d{2}/.test(n)?`date`:`text`;if(Array.isArray(n)){let e=n[0];return n.length>0&&typeof e==`object`&&e&&`type`in e&&`id`in e?`blockTree`:`json`}return typeof n==`object`?`json`:`unknown`}function _(e,t){if(typeof e==`string`||typeof t==`string`)return d(p(e),p(t))}function v(e){if(typeof e!=`object`||!e)return null;let t=e;if(typeof t.id!=`string`||typeof t.type!=`string`)return null;let n={...t.props??{}};return t.traits!==void 0&&(n.traits=t.traits),t.children!==void 0&&(n.children=t.children),{id:t.id,type:t.type,props:n}}function y(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let i of n){let n=e[i],a=t[i];JSON.stringify(n)!==JSON.stringify(a)&&r.push({field:i,from:n,to:a,segments:_(n,a)})}return r}function b(e){let t=new Map,n=0;for(let r of e){let e=v(r);e&&(t.set(e.id,{block:e,index:n}),n++)}return t}function x(e,t){let n=b(e),r=b(t),i=[],a=new Set;for(let[e,{block:t,index:o}]of r){a.add(e);let r=n.get(e);if(!r){i.push({blockId:e,blockType:t.type,status:`added`,toIndex:o});continue}let s=r.block,c=r.index,l=y(s.props,t.props),u=c!==o;l.length===0&&!u?i.push({blockId:e,blockType:t.type,status:`unchanged`,fromIndex:c,toIndex:o}):l.length>0?i.push({blockId:e,blockType:t.type,status:`changed`,fromIndex:c,toIndex:o,propDiffs:l}):i.push({blockId:e,blockType:t.type,status:`moved`,fromIndex:c,toIndex:o})}for(let[e,{block:t,index:r}]of n)a.has(e)||i.push({blockId:e,blockType:t.type,status:`removed`,fromIndex:r});return i}function S(e){return e===`_`||e===r().defaultLocale}function C(e){let{defaultLocale:t}=r();return S(e)?{defaultLocale:t}:{locale:e,defaultLocale:t}}async function w(e,t,n,o,c){if(c!==void 0&&e.db!==c)throw Error("applyLocaleRoutedUpdate: `tx` must be the context's own db handle");let l=await s(e);return e.admin.updateForLocale(t,n,o,{defaultLocale:r().defaultLocale,...c!==void 0&&{tx:c},...l!==null&&{localeStatusWriter:(e,t,n)=>(a(t),i(l,e,t,n))}})}const T=new Set([`id`,`createdAt`,`createdBy`,`updatedAt`,`updatedBy`]);function E(e,t){let n=e.allFields,r={};for(let e of Object.keys(t)){if(T.has(e)||e.startsWith(`_`)||!Object.hasOwn(n,e))continue;let i=n[e];i&&i.internal===!0||(r[e]=t[e])}return r}function D(e,t){return JSON.stringify(e)===JSON.stringify(t)}function O(e,t){let n={};for(let r of Object.keys(t))r===`id`||r.startsWith(`_`)||D(e[r],t[r])||(n[r]=t[r]);return n}var k=e({compareVersions:()=>M,createVersionSnapshot:()=>I,getVersion:()=>j,getVersions:()=>A,pruneVersions:()=>P,restoreVersion:()=>N,saveVersionSnapshot:()=>F,writeVersionColumn:()=>L});async function A(e,t,n){n?.locale!==void 0&&a(n.locale);let r=await o(e);return r?await r.findMany({where:{entityId:t,locale:n?.locale??`_`},orderBy:[{column:`version`,dir:`desc`}],...n?.limit===void 0?{}:{limit:n.limit},...n?.offset===void 0?{}:{offset:n.offset}}):[]}async function j(e,t,n,r=`_`){a(r);let i=await o(e);return i?await i.findOne({entityId:t,version:n,locale:r})??null:null}async function M(e,t,n,r,i=`_`){a(i);let[o,s]=await Promise.all([j(e,t,n,i),j(e,t,r,i)]);if(!o||!s)return null;let c=e.entity.allFields,l=h(o.data,s.data,c);for(let e of l)e.type===`blockTree`&&(e.blockDiffs=x(Array.isArray(e.from)?e.from:[],Array.isArray(e.to)?e.to:[]));return{diffs:l,fromVersion:o,toVersion:s}}async function N(e,r,i,s=`_`){if(a(s),!await o(e))throw new n(e.entity.name,`versions`);let c=await j(e,r,i,s);if(!c)throw new t(r,i,s);let l=c.data;return w(e,r,E(e.entity,l),s)}async function P(e,t,n=`_`){if(a(n),!e.versionableOptions)return;let r=await o(e);if(!r)return;let i=e.versionableOptions.maxPerDoc,s=await r.findMany({where:{entityId:t,locale:n},orderBy:[{column:`version`,dir:`desc`}],limit:i});if(s.length<i)return;let c=s.map(e=>e.id);await r.deleteMany({entityId:t,locale:n,id:{notIn:c}})}async function F(e,t,n,r,i,s=`_`,c){a(s);let l=await o(e);if(!l)return;let[u]=await l.aggregate({select:{maxVersion:{fn:`max`,column:`version`}},where:{entityId:t,locale:s}}),d=Number(u?.maxVersion??0)+1,f=n.status??null;if(i){let e=await l.findOne({entityId:t,isAutosave:!0,version:d,locale:s});if(e)return await l.update({id:e.id},{data:n,delta:r,status:f,createdAt:new Date}),d}return await l.insert({entityId:t,version:d,locale:s,data:n,delta:r,status:f,createdBy:c?.id??null,createdByName:c?.name??null,isAutosave:i}),d}async function I(e,t,n,r,i,o){if(i!==void 0&&a(i),!e.versionableOptions)return;let s=i??`_`,c=await F(e,t,n,r?O(r,n):null,!1,s,o);c!==void 0&&s===`_`&&await L(e,t,c),await P(e,t,s)}async function L(e,t,n){let r=c.get(e.entity.name);r&&await e.db.update(r).set({_version:n}).where(l(r.id,t))}export{P as a,k as c,E as d,w as f,h,A as i,L as l,x as m,I as n,N as o,C as p,j as r,F as s,M as t,O as u};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@murumets-ee/content",
3
- "version": "0.111.0",
3
+ "version": "0.112.0",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,21 +64,21 @@
64
64
  "drizzle-orm": "^0.45.2",
65
65
  "server-only": "^0.0.1",
66
66
  "zod": "^3.24.1",
67
- "@murumets-ee/blocks": "0.111.0",
68
- "@murumets-ee/core": "0.111.0",
69
- "@murumets-ee/db": "0.111.0",
70
- "@murumets-ee/entity": "0.111.0",
71
- "@murumets-ee/i18n": "0.111.0",
72
- "@murumets-ee/logging": "0.111.0",
73
- "@murumets-ee/queue": "0.111.0",
74
- "@murumets-ee/settings": "0.111.0"
67
+ "@murumets-ee/blocks": "0.112.0",
68
+ "@murumets-ee/core": "0.112.0",
69
+ "@murumets-ee/db": "0.112.0",
70
+ "@murumets-ee/entity": "0.112.0",
71
+ "@murumets-ee/i18n": "0.112.0",
72
+ "@murumets-ee/logging": "0.112.0",
73
+ "@murumets-ee/queue": "0.112.0",
74
+ "@murumets-ee/settings": "0.112.0"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@types/node": "^20.19.42",
78
78
  "tsdown": "^0.22.2",
79
79
  "typescript": "^5.7.3",
80
80
  "vitest": "^2.1.8",
81
- "@murumets-ee/content-api": "0.111.0"
81
+ "@murumets-ee/content-api": "0.112.0"
82
82
  },
83
83
  "typeCoverage": {
84
84
  "atLeast": 99.48