@murumets-ee/content 0.94.0 → 0.95.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs-BqSuMNac.mjs","names":[],"sources":["../src/paths/breadcrumbs.ts"],"sourcesContent":["/**\n * The breadcrumb trail of one document, derived from the path index\n * (block-library D003).\n *\n * A document at `/tooted/spreiolid/rapsioli-sprei` sits under `/tooted` and\n * `/tooted/spreiolid`. If the index holds a PUBLISHED document at each of those\n * paths, each is a step of the trail; a prefix with no published document\n * behind it is skipped (the trail is \"what the visitor can click\", not \"what\n * the URL looks like\"). The document itself ends the trail, as a step with no\n * link.\n *\n * ## Cost\n *\n * Bounded by {@link MAX_BREADCRUMBS}, not by anything a visitor controls:\n * one index read for the document's own path, ONE index read for every\n * ancestor path at once (`path IN (…)`, never one per ancestor), then one title\n * read per ENTITY TYPE found among the ancestors — at most a handful, because a\n * site's hierarchy is made of a few routable types. The title read is the same\n * publish-gated read the path resolvers use, so an unpublished or trashed\n * ancestor yields no step.\n *\n * ## Site\n *\n * `pathsClient` is the SITE-CONFINED reader (`sitePathsReader`): a site's\n * ancestor lookup cannot resolve another site's path. This module takes the\n * reader as a parameter so it imports nothing from the app graph.\n */\n\nimport type { PathEnumReader } from './enumerate.js'\n\n/** Most steps a trail carries, the document's own included. Mirrors `@murumets-ee/blocks`. */\nexport const MAX_BREADCRUMBS = 8\n\n/** One step of a trail, as the blocks package's `DocumentBreadcrumb`. */\nexport interface TrailStep {\n readonly label: string\n readonly href?: string\n}\n\n/** A trail, and the documents its ancestor steps came from (what a cached page depends on). */\nexport interface BreadcrumbTrail {\n readonly steps: readonly TrailStep[]\n readonly ancestors: readonly { readonly entity: string; readonly id: string }[]\n}\n\n/** What the trail needs from the app — injected so the logic is testable without one. */\nexport interface BreadcrumbTrailDeps {\n readonly pathsClient: PathEnumReader\n /**\n * Titles of the PUBLISHED rows among `ids` of one entity type in `locale`, as\n * `id → title`. A row that is unpublished, trashed or title-less is absent.\n */\n readonly publishedTitles: (\n entityType: string,\n ids: readonly string[],\n locale: string,\n ) => Promise<ReadonlyMap<string, string>>\n}\n\n/**\n * The paths above `path`, nearest-to-the-top first, excluding `/` itself\n * (the site's home is the block's own \"show home\" option) and `path`.\n * `/a/b/c` → `['/a', '/a/b']`.\n */\nexport function ancestorPaths(path: string): string[] {\n if (!path.startsWith('/')) return []\n const segments = path.split('/').filter((segment) => segment !== '')\n const out: string[] = []\n for (let depth = 1; depth < segments.length; depth++) {\n out.push(`/${segments.slice(0, depth).join('/')}`)\n }\n return out\n}\n\n/**\n * The trail for the document `entityType`/`id` in `locale`, or `undefined` when\n * the document has no canonical path (not routable, or not indexed yet) or is\n * the site's top page (nothing above it).\n *\n * `label` names the document's own step — the caller reads it from the row it\n * is drawing. Without it the trail ends at the last ancestor.\n */\nexport async function resolveBreadcrumbTrail(\n request: {\n readonly entityType: string\n readonly id: string\n readonly locale: string\n readonly label?: string | undefined\n },\n deps: BreadcrumbTrailDeps,\n): Promise<BreadcrumbTrail | undefined> {\n const own = await deps.pathsClient.findMany({\n where: {\n entityType: request.entityType,\n entityId: { in: [request.id] },\n locale: request.locale,\n isCanonical: true,\n },\n limit: 1,\n })\n const currentPath = own[0]?.path\n if (currentPath === undefined) return undefined\n\n // The nearest ancestors win when a path is deeper than the cap allows.\n const wanted = ancestorPaths(currentPath).slice(-(MAX_BREADCRUMBS - 1))\n const steps: TrailStep[] = []\n const ancestors: { entity: string; id: string }[] = []\n if (wanted.length > 0) {\n const rows = await deps.pathsClient.findMany({\n where: { locale: request.locale, isCanonical: true, path: { in: wanted } },\n limit: wanted.length,\n })\n const idsByType = new Map<string, string[]>()\n for (const row of rows) {\n const ids = idsByType.get(row.entityType)\n if (ids === undefined) idsByType.set(row.entityType, [row.entityId])\n else ids.push(row.entityId)\n }\n // Serial and bounded: one read per entity type among at most seven paths.\n const titles = new Map<string, string>()\n for (const [entityType, ids] of idsByType) {\n for (const [id, title] of await deps.publishedTitles(entityType, ids, request.locale)) {\n titles.set(`${entityType}\\u0000${id}`, title)\n }\n }\n const byPath = new Map(rows.map((row) => [row.path, row]))\n // Every document found at an ancestor path is something the trail DEPENDS\n // on — including one that is unpublished or untitled today and so adds no\n // step: publishing it tomorrow must refresh a cached page.\n for (const row of rows) ancestors.push({ entity: row.entityType, id: row.entityId })\n for (const path of wanted) {\n const row = byPath.get(path)\n if (row === undefined) continue\n const title = titles.get(`${row.entityType}\\u0000${row.entityId}`)\n if (title === undefined) continue\n steps.push({ label: title, href: path })\n }\n }\n // A trail is \"where this page sits UNDER something\": with no resolvable\n // ancestor there is none to draw (a lone current item is a landmark pointing\n // at itself), though what was found still gates the page's cache.\n if (steps.length === 0) return undefined\n if (request.label !== undefined) steps.push({ label: request.label })\n return { steps, ancestors }\n}\n"],"mappings":"sIAgEA,SAAgB,EAAc,EAAwB,CACpD,GAAI,CAAC,EAAK,WAAW,GAAG,EAAG,MAAO,CAAC,EACnC,IAAM,EAAW,EAAK,MAAM,GAAG,CAAC,CAAC,OAAQ,GAAY,IAAY,EAAE,EAC7D,EAAgB,CAAC,EACvB,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAS,OAAQ,IAC3C,EAAI,KAAK,IAAI,EAAS,MAAM,EAAG,CAAK,CAAC,CAAC,KAAK,GAAG,GAAG,EAEnD,OAAO,CACT,CAUA,eAAsB,EACpB,EAMA,EACsC,CAUtC,IAAM,GAAc,MATF,EAAK,YAAY,SAAS,CAC1C,MAAO,CACL,WAAY,EAAQ,WACpB,SAAU,CAAE,GAAI,CAAC,EAAQ,EAAE,CAAE,EAC7B,OAAQ,EAAQ,OAChB,YAAa,EACf,EACA,MAAO,CACT,CAAC,EAAA,CACuB,EAAE,EAAE,KAC5B,GAAI,IAAgB,IAAA,GAAW,OAG/B,IAAM,EAAS,EAAc,CAAW,CAAC,CAAC,MAAM,EAAsB,EAChE,EAAqB,CAAC,EACtB,EAA8C,CAAC,EACrD,GAAI,EAAO,OAAS,EAAG,CACrB,IAAM,EAAO,MAAM,EAAK,YAAY,SAAS,CAC3C,MAAO,CAAE,OAAQ,EAAQ,OAAQ,YAAa,GAAM,KAAM,CAAE,GAAI,CAAO,CAAE,EACzE,MAAO,EAAO,MAChB,CAAC,EACK,EAAY,IAAI,IACtB,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAM,EAAU,IAAI,EAAI,UAAU,EACpC,IAAQ,IAAA,GAAW,EAAU,IAAI,EAAI,WAAY,CAAC,EAAI,QAAQ,CAAC,EAC9D,EAAI,KAAK,EAAI,QAAQ,CAC5B,CAEA,IAAM,EAAS,IAAI,IACnB,IAAK,GAAM,CAAC,EAAY,KAAQ,EAC9B,IAAK,GAAM,CAAC,EAAI,KAAU,MAAM,EAAK,gBAAgB,EAAY,EAAK,EAAQ,MAAM,EAClF,EAAO,IAAI,GAAG,EAAW,QAAQ,IAAM,CAAK,EAGhD,IAAM,EAAS,IAAI,IAAI,EAAK,IAAK,GAAQ,CAAC,EAAI,KAAM,CAAG,CAAC,CAAC,EAIzD,IAAK,IAAM,KAAO,EAAM,EAAU,KAAK,CAAE,OAAQ,EAAI,WAAY,GAAI,EAAI,QAAS,CAAC,EACnF,IAAK,IAAM,KAAQ,EAAQ,CACzB,IAAM,EAAM,EAAO,IAAI,CAAI,EAC3B,GAAI,IAAQ,IAAA,GAAW,SACvB,IAAM,EAAQ,EAAO,IAAI,GAAG,EAAI,WAAW,QAAQ,EAAI,UAAU,EAC7D,IAAU,IAAA,IACd,EAAM,KAAK,CAAE,MAAO,EAAO,KAAM,CAAK,CAAC,CACzC,CACF,CAII,KAAM,SAAW,EAErB,OADI,EAAQ,QAAU,IAAA,IAAW,EAAM,KAAK,CAAE,MAAO,EAAQ,KAAM,CAAC,EAC7D,CAAE,QAAO,WAAU,CAC5B"}
package/dist/client.mjs CHANGED
@@ -1,2 +1,2 @@
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-B0qa13BY.mjs";import{n as y}from"./plugin-C8ETWu3n.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{blockTreeFieldsOf as w,buildPartitionCondition as T}from"@murumets-ee/entity";import{getApp as E,getContext as D,requireSiteFor as O}from"@murumets-ee/core";import{schemaRegistry as k}from"@murumets-ee/db";import{and as A,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,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=w(e.entity).some(({name:e})=>Object.hasOwn(c,e))?await s.findOne({entityId:n,locale:i}):null;await e.admin.assertBlockTreesStrictForCaller(c,()=>l?.data),await e.admin.assertMediaKindsValid(c,l?.data);let u=new Date,d=a?.id??``,f=a?.name??null;return N(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}}))}const F=[`slug`,`slugMode`,`parent`];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);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=w(e.entity).some(({name:e})=>Object.hasOwn(c,e)),u=e.entity.frontend?.path!==void 0&&F.some(e=>Object.hasOwn(c,e)),d=l||u?await s.findOne({entityId:n,locale:i}):null;if(await e.admin.assertBlockTreesStrictForCaller(c,()=>d?.data),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=>F.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;return N(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`]}))}async function L(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 R(e,t,n=`_`){b(n);let r=await C(e);return r?(await r.deleteMany({entityId:t,locale:n})).length>0:!1}async function z(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),_=B(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 B(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 V(e,t,n=`_`){b(n);let r=await C(e);return r?r.exists({entityId:t,locale:n}):!1}async function H(e,t=`_`){b(t);let n=await C(e);return n?n.count({locale:t}):0}async function U(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 W(e,t,n=`_`){b(n);let r=await L(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 G(e,t){let n=(await q(e,[t])).get(t);return n?Object.values(n):[]}async function K(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 q(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 J(){let e=i(),t=D();return{locales:(t?.availableLocales??e.locales.map(e=>e.code)).map(e=>({code:e})),defaultLocale:t?.defaultLocale??e.defaultLocale}}function Y(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 X(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}=J(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return n;let o=k.get(`${e.entity.name}_translations`);if(!o)return n;let s=await O(e.entity),c=j(o.entityId,t),l=await e.db.select().from(o).where(s===void 0?c:A(c,T(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)}for(let e of t)n.set(e,Y(a,u.get(e),r,i));return n}async function Q(e,t){let{locales:r,defaultLocale:i}=J(),a=t?.locale??r.find(e=>e.code!==i)?.code;if(!a)return{total:0,complete:0,partial:0,missing:0};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{total:0,complete:0,partial:0,missing:0};let s=await Z(e,o.map(e=>e.id)),c=0,l=0,u=0;for(let e of o){let t=s.get(e.id)?.[a];!t||t.translated===0?u++:t.complete?c++:l++}return{total:o.length,complete:c,partial:l,missing:u}}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 X(this.ctx,e)}getTranslationStatusBatch(e){return Z(this.ctx,e)}getTranslationReport(e){return Q(this.ctx,e)}getLocaleStatus(e){return G(this.ctx,e)}setLocaleStatus(e,t,n){return K(this.ctx,e,t,n)}getLocaleStatusBatch(e){return q(this.ctx,e)}saveDraft(e,t,n,r){return P(this.ctx,e,t,n,r)}mergeDraft(e,t,n,r){return I(this.ctx,e,t,n,r)}getDraft(e,t){return L(this.ctx,e,t)}deleteDraft(e,t){return R(this.ctx,e,t)}publishDraft(e,t,n){return z(this.ctx,e,t,n)}hasDraft(e,t){return V(this.ctx,e,t)}countDrafts(e){return H(this.ctx,e)}hasDraftBatch(e,t){return U(this.ctx,e,t)}compareDraftWithCurrent(e,t){return W(this.ctx,e,t)}};function ee(e){return new $({admin:M(e),entity:e,logger:E().logger.child({content:e.name})})}export{$ as ContentClient,ee 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-B8feZTS9.mjs";import{n as y}from"./plugin-ChCEq_Qy.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{blockTreeFieldsOf as w,buildPartitionCondition as T}from"@murumets-ee/entity";import{getApp as E,getContext as D,requireSiteFor as O}from"@murumets-ee/core";import{schemaRegistry as k}from"@murumets-ee/db";import{and as A,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,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=w(e.entity).some(({name:e})=>Object.hasOwn(c,e))?await s.findOne({entityId:n,locale:i}):null;await e.admin.assertBlockTreesStrictForCaller(c,()=>l?.data),await e.admin.assertMediaKindsValid(c,l?.data);let u=new Date,d=a?.id??``,f=a?.name??null;return N(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}}))}const F=[`slug`,`slugMode`,`parent`];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);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=w(e.entity).some(({name:e})=>Object.hasOwn(c,e)),u=e.entity.frontend?.path!==void 0&&F.some(e=>Object.hasOwn(c,e)),d=l||u?await s.findOne({entityId:n,locale:i}):null;if(await e.admin.assertBlockTreesStrictForCaller(c,()=>d?.data),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=>F.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;return N(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`]}))}async function L(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 R(e,t,n=`_`){b(n);let r=await C(e);return r?(await r.deleteMany({entityId:t,locale:n})).length>0:!1}async function z(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),_=B(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 B(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 V(e,t,n=`_`){b(n);let r=await C(e);return r?r.exists({entityId:t,locale:n}):!1}async function H(e,t=`_`){b(t);let n=await C(e);return n?n.count({locale:t}):0}async function U(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 W(e,t,n=`_`){b(n);let r=await L(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 G(e,t){let n=(await q(e,[t])).get(t);return n?Object.values(n):[]}async function K(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 q(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 J(){let e=i(),t=D();return{locales:(t?.availableLocales??e.locales.map(e=>e.code)).map(e=>({code:e})),defaultLocale:t?.defaultLocale??e.defaultLocale}}function Y(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 X(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}=J(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return n;let o=k.get(`${e.entity.name}_translations`);if(!o)return n;let s=await O(e.entity),c=j(o.entityId,t),l=await e.db.select().from(o).where(s===void 0?c:A(c,T(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)}for(let e of t)n.set(e,Y(a,u.get(e),r,i));return n}async function Q(e,t){let{locales:r,defaultLocale:i}=J(),a=t?.locale??r.find(e=>e.code!==i)?.code;if(!a)return{total:0,complete:0,partial:0,missing:0};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{total:0,complete:0,partial:0,missing:0};let s=await Z(e,o.map(e=>e.id)),c=0,l=0,u=0;for(let e of o){let t=s.get(e.id)?.[a];!t||t.translated===0?u++:t.complete?c++:l++}return{total:o.length,complete:c,partial:l,missing:u}}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 X(this.ctx,e)}getTranslationStatusBatch(e){return Z(this.ctx,e)}getTranslationReport(e){return Q(this.ctx,e)}getLocaleStatus(e){return G(this.ctx,e)}setLocaleStatus(e,t,n){return K(this.ctx,e,t,n)}getLocaleStatusBatch(e){return q(this.ctx,e)}saveDraft(e,t,n,r){return P(this.ctx,e,t,n,r)}mergeDraft(e,t,n,r){return I(this.ctx,e,t,n,r)}getDraft(e,t){return L(this.ctx,e,t)}deleteDraft(e,t){return R(this.ctx,e,t)}publishDraft(e,t,n){return z(this.ctx,e,t,n)}hasDraft(e,t){return V(this.ctx,e,t)}countDrafts(e){return H(this.ctx,e)}hasDraftBatch(e,t){return U(this.ctx,e,t)}compareDraftWithCurrent(e,t){return W(this.ctx,e,t)}};function ee(e){return new $({admin:M(e),entity:e,logger:E().logger.child({content:e.name})})}export{$ as ContentClient,ee as createContentClient};
2
2
  //# sourceMappingURL=client.mjs.map
@@ -1,2 +1,2 @@
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"./chrome-entity-names-zYJzP-_U.mjs";import{r as u}from"./detail-template-names-CD1Y9n1U.mjs";import{c as d,i as f,n as p,o as m,r as h,t as g}from"./schema-BYrYzsmO.mjs";import{l as _,t as v,u as y}from"./schema-IML1rRU_.mjs";import{r as b,t as x}from"./routes-BmNSYyuT.mjs";import{n as S}from"./locks-5779ugSW.mjs";import{MAX_PATH_DEPTH as C,buildPartitionCondition as w,hasPathParent as T,isSiteBound as E,resolveEffectiveSlugMode as D,resolvePattern as O}from"@murumets-ee/entity";import{definePlugin as k,getApp as A,getAppOrUndefined as j,getContext as M,getCurrentSite as N,requireRequestSite as P,requireSiteFor as F,resolveImplicitSite as I,runAsCli as L,runInEverySite as R,runInSite as z}from"@murumets-ee/core";import{column as B,defineTable as V,schemaRegistry as H}from"@murumets-ee/db";import{and as U,asc as ee,eq as W,getTableColumns as te,inArray as ne,sql as re}from"drizzle-orm";import{z as G}from"zod";import{createHash as ie}from"node:crypto";const ae=Symbol.for(`@murumets-ee/content:locale-status-writer`);async function K(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 q(e){globalThis[ae]=(n,r)=>{let{localeStatus:i}=c(n.name);if(i)return async(n,a,o,s)=>{let c=M()?.availableLocales??e;if(!c.includes(a))throw new t(a,[...c]);await K(i.makeClient(r,s),n,a,o)}}}async function J(e){let t=M()?.app??j();if(!t)return;let n=t.entities.get(e);if(n)return F(n,t)}function oe(){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 J(e)),l=new Date;return await s.upsert({entityId:t,locale:n,data:r,createdBy:i.id,createdByName:i.name??null,createdAt:l,updatedAt:l},{target:[`entityId`,`locale`],set:{updatedAt:l},mergeJsonb:[`data`]}),!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 J(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 J(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 J(e)).exists({entityId:t}):!1},async hasAnyLocaleActivity({documentType:e,documentId:t,db:n}){let{localeStatus:r}=c(e),i=await J(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?W(r,t):U(W(r,t),w(a,{site:i},e));if((await n.select({one:re`1`}).from(a).where(o).limit(1)).length>0)return!0}}return!1}}}function se(e){let{defaultLocale:t}=a();return e===`_`||e===t?{defaultLocale:t}:{locale:e,defaultLocale:t}}function ce(e){let{defaultLocale:t}=a();return e===`_`||e===t?`_`:e}async function le(e,t,n,r){let{drafts:i}=c(e);if(!i)return;let a=await i.makeClient(r,await J(e)).findOne({entityId:t,locale:n});return a?a.data??void 0:void 0}function ue(){return{async snapshotCurrentState({documentType:t,documentId:n,locale:r,user:i}){let a=A(),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=ce(r),d=await c.findById(n,se(u));if(!d)return!1;let f=await le(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-B0qa13BY.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 de(e,t,n){let r=te(g),i=r[h],a=r.entityType,o=r.entityId;if(!i||!a||!o)throw Error(`toolkit_paths is missing '${h}'/entity columns`);let s=await e.selectDistinct({site:i}).from(g).where(U(W(a,t),W(o,n))).limit(2);if(s.length>1)return null;let c=s[0]?.site;return typeof c==`string`?c:void 0}const fe=Symbol.for(`@murumets-ee/content:path-writers-factory`),Y=Symbol.for(`@murumets-ee/content:cascade-trigger-factory`);function pe(e){globalThis[Y]=e}function X(){let e=globalThis[Y];return typeof e==`function`?e:void 0}function me(e,t){return async r=>{let i=await he(e,t,r);if(i===null)return;let a=p.makeClient(e.db.readWrite,i),o=N();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&&E(t)?z(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(!T(t))return;let i=X();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 m({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:null,skipReason:`row-unreadable`,reservedPathPrefixes:s,logger:e.logger});continue}let c=await _({entity:t,row:a,locale:i,defaultLocale:o,pathsClient:n}),d=await m({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 he(e,t,n){if(n.viaCascade===!0&&E(t)){let t=await de(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=N();if(r!==void 0)return r;let i=await I(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 P(`toolkit_paths`,e)}function ge(e,t){return async n=>{let r=p.makeClient(e.db.readWrite,await P(`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=D(n.data.slugMode,l?.slugMode);await y({entity:t,data:n.data,currentRow:l,id:n.id,locale:c,defaultLocale:o,slugMode:u,reservedPathPrefixes:s,pathsClient:r})}}function _e(){globalThis[fe]=(e,t)=>({pathIndexWriter:me(e,t),pathConflictGuard:ge(e,t)})}const Z=G.object({entityType:G.string().min(1),parentId:G.string().uuid(),locale:G.string().min(1)}),Q=G.object({entity:G.string().min(1).optional(),locale:G.string().min(1).optional(),dryRun:G.boolean()});async function ve(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}),f=()=>({entityType:r,parentId:i,locale:o,cap:s,...l}),h=e.entities.get(r);if(!h)return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: unknown entity type — nothing to regenerate`),n.onProgress?.(u(`done`)),f();if(!T(h))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`)),f();let g=a(),v=g.defaultLocale,y=g.reservedPathPrefixes??[],b=p.makeClient(e.db.readWrite,await P(`toolkit_paths`,e)),x=e.getClient(h),S=x.getTable().parent,C=async t=>{let n=t.id;try{let i=await _({entity:h,row:t,locale:o,defaultLocale:v,pathsClient:b});switch((await m({pathsClient:b,entityType:r,entityId:n,locale:o,path:i.path,...i.skipReason!==void 0&&{skipReason:i.skipReason},reservedPathPrefixes:y,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 x.findMany({where:ne(S,w),locale:o,defaultLocale:v,limit:e+1});if(t.length===0)break;let r=t;if(t.length>e&&(r=t.slice(0,e),l.truncated=!0),await d(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`)),f()}async function ye(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??[],f=c.locales.map(e=>e.code),h=t.locale?f.filter(e=>e===t.locale):f,g=[...e.entities.values()].filter(e=>!!e.frontend?.path&&(t.entity===void 0||e.name===t.entity)),v=t.entity!==void 0&&g.length===0,y=t.locale!==void 0&&h.length===0;v&&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)`),y&&e.logger.warn({locale:t.locale,configuredLocales:f,dryRun:s},`paths-rebuild: --locale matches no configured locale — nothing to rebuild (likely a typo in the locale code)`);let b=v||y?{entity:v,locale:y}:void 0,x=g.some(e=>T(e)),S=s&&x?`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,w=p.makeClient(e.db.readWrite,await P(`toolkit_paths`,e)),E={scanned:0,changed:0,applied:0,failed:0},D=new Map,O={missing:0,conflicts:0,unchanged:0,skipped:0},k=(e,t)=>{let n=D.get(e);n!==t&&(n!==void 0&&n!==`resolved`&&--O[n],D.set(e,t),t!==`resolved`&&(O[t]+=1))},A=[],j=!1,M=e=>{A.length<o?A.push(e):j=!0},N=0,F=e=>({phase:e,dryRun:s,scope:{entity:t.entity??null,locale:t.locale??null},scanned:E.scanned,changed:E.changed,applied:E.applied,failed:E.failed,missing:O.missing,conflicts:O.conflicts,unchanged:O.unchanged,skipped:O.skipped,passes:N,sample:A,sampleTruncated:j,...S===void 0?{}:{hierarchyNote:S},...b===void 0?{}:{unmatchedScope:b}}),I=async(t,n,r,i)=>{let a=r.id,o=!1;try{let c=await _({entity:t,row:r,locale:i,defaultLocale:l,pathsClient:w}),d=await m({pathsClient:w,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`:k(f,`unchanged`);break;case`skipped-null`:case`skipped-reserved`:k(f,`skipped`);break;case`conflict`:k(f,`conflicts`),M({kind:`conflict`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath,...d.conflictWith?{conflictWith:d.conflictWith.entityId}:{}});break;case`created`:s?k(f,`missing`):(E.applied+=1,o=!0,k(f,`resolved`)),M({kind:`missing`,entityType:n,entityId:a,locale:i,oldPath:null,newPath:d.newPath});break;case`renamed`:E.changed+=1,s||(E.applied+=1,o=!0),k(f,`resolved`),M({kind:`changed`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath});break}}catch(t){E.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},L=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 h){let u;for(;;){let f=await t.findMany({locale:e,defaultLocale:l,limit:r,orderBy:ee(c),...u===void 0?{}:{cursor:{field:`id`,value:u,direction:`asc`}}});if(f.length===0)break;await d(f,i,async t=>{await I(o,s,t,e)&&(a+=1)}),E.scanned+=f.length;let p=f[f.length-1];if(p&&(u=p.id),n.onProgress?.(F(`scanning`)),f.length<r)break}}}return a},R=async t=>{let r=[];for(let[n,i]of D){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 d(r,i,async t=>{let n=await e.getClient(t.entity).findById(t.entityId,{locale:t.locale,defaultLocale:l});n!==null&&(E.scanned+=1,await I(t.entity,t.entityType,n,t.locale)&&(a+=1))}),n.onProgress?.(F(`scanning`)),a},z=g.filter(e=>T(e)),B=new Set(z.map(e=>e.name)),V=s||!x?1:C;for(let e=0;e<V;e++){N=e+1;let t=await L(e===0?g:z);if(e>0&&(t+=await R(B)),t===0)break}j&&e.logger.warn({scanned:E.scanned,changed:E.changed,missing:O.missing,conflicts:O.conflicts,sampleCap:o},`paths-rebuild: more diff entries than the progress sample can hold — see the aggregate counts; the stored sample is truncated`);let H=F(`done`);n.onProgress?.(H);let{phase:U,...W}=H;return W}async function be(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:Z}),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:Q});n(i,async t=>{await L(async()=>{let n=await ve(e,Z.parse(t.payload),{onProgress:e=>t.updateProgress(e)});e.logger.info(n,`paths-cascade: descendant regeneration complete`)})}),n(a,async t=>{await L(()=>R(async()=>{let n=await ye(e,Q.parse(t.payload),{onProgress:e=>t.updateProgress({...e,site:N()??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`}))}),pe(async(e,t,n,a)=>{await new r({app:e,logger:e.logger}).enqueue(i,{entityType:t.name,parentId:n,locale:a})})}const $=V({name:`toolkit_path_patterns`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityType:B.varchar({length:100,notNull:!0,pgName:`entity_type`}),locale:B.varchar({length:10,notNull:!0}),patternHash:B.varchar({length:64,notNull:!0,pgName:`pattern_hash`}),updatedAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityType`,`locale`]}]}),xe=$.table;function Se(e,t,n){let r=O(e,t,n);if(r===null)return null;let i=typeof r==`function`?r.toString():JSON.stringify(r);return ie(`sha256`).update(i).digest(`hex`)}async function Ce(e){let t=a(),n=t.defaultLocale,r=t.locales.map(e=>e.code),i=$.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=Se(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 we=async(e,t,n)=>{if(t.length===0)return new Map;let[{buildEnumerateDeps:r},{getCanonicalPaths:i}]=await Promise.all([import(`./resolve-context-Cvc1aNJe.mjs`).then(e=>e.l),import(`./enumerate-CYHJKQz6.mjs`).then(e=>e.n)]),a=r(A()),o=await a.filterPublished(e,t,n);return o.size===0?new Map:i(e,[...o],n,a)},Te=async(e,t)=>{if(e.length===0)return new Set;let{buildEnumerateDeps:n}=await import(`./resolve-context-Cvc1aNJe.mjs`).then(e=>e.l),r=n(A()),i=[...new Set(e)],a=i.slice(0,48);i.length>a.length&&A().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};function Ee(e){return f(e,{name:`${e.name}_drafts`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),locale:B.varchar({length:10,notNull:!0,default:`_`}),data:B.jsonb({notNull:!0}),createdBy:B.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:B.varchar({length:255,pgName:`created_by_name`}),createdAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function De(e){return f(e,{name:`${e.name}_locale_status`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),locale:B.varchar({length:10,notNull:!0}),status:B.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:B.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function Oe(e){return f(e,{name:`${e.name}_versions`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),version:B.integer({notNull:!0}),locale:B.varchar({length:10,notNull:!0,default:`_`}),data:B.jsonb({notNull:!0}),delta:B.jsonb(),status:B.varchar({length:20}),createdBy:B.varchar({length:255,pgName:`created_by`}),createdByName:B.varchar({length:255,pgName:`created_by_name`}),createdAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:B.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}function ke(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 k({name:`@murumets-ee/content`,contributes:{"blocks.documentDraftStore":oe(),"blocks.documentVersionStore":ue(),"blocks.publishedPathResolver":we,"blocks.publishedPathsAmong":Te,...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:S,toolkitPaths:g,toolkitPathPatterns:xe,toolkitRedirects:v},routes:[...x(),...b()],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:l,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:u,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=Oe(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=De(i);H.register(t,n.table),s(e,{localeStatus:n})}r++}if(t){let t=`${e}_drafts`;if(!H.has(t)){let n=Ee(i);H.register(t,n.table),s(e,{drafts:n})}a++}}q(e.locales.map(e=>e.code)),_e();try{await be(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 Ce(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{K as n,ke as t};
2
- //# sourceMappingURL=plugin-C8ETWu3n.mjs.map
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"./chrome-entity-names-zYJzP-_U.mjs";import{r as u}from"./detail-template-names-CD1Y9n1U.mjs";import{c as d,i as f,n as p,o as m,r as h,t as g}from"./schema-BYrYzsmO.mjs";import{l as _,t as v,u as y}from"./schema-IML1rRU_.mjs";import{r as b,t as x}from"./routes-DlvqU8bO.mjs";import{n as S}from"./locks-5779ugSW.mjs";import{MAX_PATH_DEPTH as C,buildPartitionCondition as w,hasPathParent as T,isSiteBound as E,resolveEffectiveSlugMode as D,resolvePattern as O}from"@murumets-ee/entity";import{definePlugin as k,getApp as A,getAppOrUndefined as j,getContext as M,getCurrentSite as N,requireRequestSite as P,requireSiteFor as F,resolveImplicitSite as I,runAsCli as L,runInEverySite as R,runInSite as z}from"@murumets-ee/core";import{column as B,defineTable as V,schemaRegistry as H}from"@murumets-ee/db";import{and as U,asc as ee,eq as W,getTableColumns as te,inArray as ne,sql as re}from"drizzle-orm";import{z as G}from"zod";import{createHash as ie}from"node:crypto";const ae=Symbol.for(`@murumets-ee/content:locale-status-writer`);async function K(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 q(e){globalThis[ae]=(n,r)=>{let{localeStatus:i}=c(n.name);if(i)return async(n,a,o,s)=>{let c=M()?.availableLocales??e;if(!c.includes(a))throw new t(a,[...c]);await K(i.makeClient(r,s),n,a,o)}}}async function J(e){let t=M()?.app??j();if(!t)return;let n=t.entities.get(e);if(n)return F(n,t)}function oe(){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 J(e)),l=new Date;return await s.upsert({entityId:t,locale:n,data:r,createdBy:i.id,createdByName:i.name??null,createdAt:l,updatedAt:l},{target:[`entityId`,`locale`],set:{updatedAt:l},mergeJsonb:[`data`]}),!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 J(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 J(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 J(e)).exists({entityId:t}):!1},async hasAnyLocaleActivity({documentType:e,documentId:t,db:n}){let{localeStatus:r}=c(e),i=await J(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?W(r,t):U(W(r,t),w(a,{site:i},e));if((await n.select({one:re`1`}).from(a).where(o).limit(1)).length>0)return!0}}return!1}}}function se(e){let{defaultLocale:t}=a();return e===`_`||e===t?{defaultLocale:t}:{locale:e,defaultLocale:t}}function ce(e){let{defaultLocale:t}=a();return e===`_`||e===t?`_`:e}async function le(e,t,n,r){let{drafts:i}=c(e);if(!i)return;let a=await i.makeClient(r,await J(e)).findOne({entityId:t,locale:n});return a?a.data??void 0:void 0}function ue(){return{async snapshotCurrentState({documentType:t,documentId:n,locale:r,user:i}){let a=A(),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=ce(r),d=await c.findById(n,se(u));if(!d)return!1;let f=await le(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-B8feZTS9.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 de(e,t,n){let r=te(g),i=r[h],a=r.entityType,o=r.entityId;if(!i||!a||!o)throw Error(`toolkit_paths is missing '${h}'/entity columns`);let s=await e.selectDistinct({site:i}).from(g).where(U(W(a,t),W(o,n))).limit(2);if(s.length>1)return null;let c=s[0]?.site;return typeof c==`string`?c:void 0}const fe=Symbol.for(`@murumets-ee/content:path-writers-factory`),Y=Symbol.for(`@murumets-ee/content:cascade-trigger-factory`);function pe(e){globalThis[Y]=e}function X(){let e=globalThis[Y];return typeof e==`function`?e:void 0}function me(e,t){return async r=>{let i=await he(e,t,r);if(i===null)return;let a=p.makeClient(e.db.readWrite,i),o=N();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&&E(t)?z(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(!T(t))return;let i=X();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 m({pathsClient:n,entityType:r.entityName,entityId:r.id,locale:i,path:null,skipReason:`row-unreadable`,reservedPathPrefixes:s,logger:e.logger});continue}let c=await _({entity:t,row:a,locale:i,defaultLocale:o,pathsClient:n}),d=await m({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 he(e,t,n){if(n.viaCascade===!0&&E(t)){let t=await de(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=N();if(r!==void 0)return r;let i=await I(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 P(`toolkit_paths`,e)}function ge(e,t){return async n=>{let r=p.makeClient(e.db.readWrite,await P(`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=D(n.data.slugMode,l?.slugMode);await y({entity:t,data:n.data,currentRow:l,id:n.id,locale:c,defaultLocale:o,slugMode:u,reservedPathPrefixes:s,pathsClient:r})}}function _e(){globalThis[fe]=(e,t)=>({pathIndexWriter:me(e,t),pathConflictGuard:ge(e,t)})}const Z=G.object({entityType:G.string().min(1),parentId:G.string().uuid(),locale:G.string().min(1)}),Q=G.object({entity:G.string().min(1).optional(),locale:G.string().min(1).optional(),dryRun:G.boolean()});async function ve(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}),f=()=>({entityType:r,parentId:i,locale:o,cap:s,...l}),h=e.entities.get(r);if(!h)return e.logger.warn({entityType:r,parentId:i,locale:o},`paths-cascade: unknown entity type — nothing to regenerate`),n.onProgress?.(u(`done`)),f();if(!T(h))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`)),f();let g=a(),v=g.defaultLocale,y=g.reservedPathPrefixes??[],b=p.makeClient(e.db.readWrite,await P(`toolkit_paths`,e)),x=e.getClient(h),S=x.getTable().parent,C=async t=>{let n=t.id;try{let i=await _({entity:h,row:t,locale:o,defaultLocale:v,pathsClient:b});switch((await m({pathsClient:b,entityType:r,entityId:n,locale:o,path:i.path,...i.skipReason!==void 0&&{skipReason:i.skipReason},reservedPathPrefixes:y,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 x.findMany({where:ne(S,w),locale:o,defaultLocale:v,limit:e+1});if(t.length===0)break;let r=t;if(t.length>e&&(r=t.slice(0,e),l.truncated=!0),await d(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`)),f()}async function ye(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??[],f=c.locales.map(e=>e.code),h=t.locale?f.filter(e=>e===t.locale):f,g=[...e.entities.values()].filter(e=>!!e.frontend?.path&&(t.entity===void 0||e.name===t.entity)),v=t.entity!==void 0&&g.length===0,y=t.locale!==void 0&&h.length===0;v&&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)`),y&&e.logger.warn({locale:t.locale,configuredLocales:f,dryRun:s},`paths-rebuild: --locale matches no configured locale — nothing to rebuild (likely a typo in the locale code)`);let b=v||y?{entity:v,locale:y}:void 0,x=g.some(e=>T(e)),S=s&&x?`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,w=p.makeClient(e.db.readWrite,await P(`toolkit_paths`,e)),E={scanned:0,changed:0,applied:0,failed:0},D=new Map,O={missing:0,conflicts:0,unchanged:0,skipped:0},k=(e,t)=>{let n=D.get(e);n!==t&&(n!==void 0&&n!==`resolved`&&--O[n],D.set(e,t),t!==`resolved`&&(O[t]+=1))},A=[],j=!1,M=e=>{A.length<o?A.push(e):j=!0},N=0,F=e=>({phase:e,dryRun:s,scope:{entity:t.entity??null,locale:t.locale??null},scanned:E.scanned,changed:E.changed,applied:E.applied,failed:E.failed,missing:O.missing,conflicts:O.conflicts,unchanged:O.unchanged,skipped:O.skipped,passes:N,sample:A,sampleTruncated:j,...S===void 0?{}:{hierarchyNote:S},...b===void 0?{}:{unmatchedScope:b}}),I=async(t,n,r,i)=>{let a=r.id,o=!1;try{let c=await _({entity:t,row:r,locale:i,defaultLocale:l,pathsClient:w}),d=await m({pathsClient:w,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`:k(f,`unchanged`);break;case`skipped-null`:case`skipped-reserved`:k(f,`skipped`);break;case`conflict`:k(f,`conflicts`),M({kind:`conflict`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath,...d.conflictWith?{conflictWith:d.conflictWith.entityId}:{}});break;case`created`:s?k(f,`missing`):(E.applied+=1,o=!0,k(f,`resolved`)),M({kind:`missing`,entityType:n,entityId:a,locale:i,oldPath:null,newPath:d.newPath});break;case`renamed`:E.changed+=1,s||(E.applied+=1,o=!0),k(f,`resolved`),M({kind:`changed`,entityType:n,entityId:a,locale:i,oldPath:d.oldPath,newPath:d.newPath});break}}catch(t){E.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},L=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 h){let u;for(;;){let f=await t.findMany({locale:e,defaultLocale:l,limit:r,orderBy:ee(c),...u===void 0?{}:{cursor:{field:`id`,value:u,direction:`asc`}}});if(f.length===0)break;await d(f,i,async t=>{await I(o,s,t,e)&&(a+=1)}),E.scanned+=f.length;let p=f[f.length-1];if(p&&(u=p.id),n.onProgress?.(F(`scanning`)),f.length<r)break}}}return a},R=async t=>{let r=[];for(let[n,i]of D){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 d(r,i,async t=>{let n=await e.getClient(t.entity).findById(t.entityId,{locale:t.locale,defaultLocale:l});n!==null&&(E.scanned+=1,await I(t.entity,t.entityType,n,t.locale)&&(a+=1))}),n.onProgress?.(F(`scanning`)),a},z=g.filter(e=>T(e)),B=new Set(z.map(e=>e.name)),V=s||!x?1:C;for(let e=0;e<V;e++){N=e+1;let t=await L(e===0?g:z);if(e>0&&(t+=await R(B)),t===0)break}j&&e.logger.warn({scanned:E.scanned,changed:E.changed,missing:O.missing,conflicts:O.conflicts,sampleCap:o},`paths-rebuild: more diff entries than the progress sample can hold — see the aggregate counts; the stored sample is truncated`);let H=F(`done`);n.onProgress?.(H);let{phase:U,...W}=H;return W}async function be(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:Z}),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:Q});n(i,async t=>{await L(async()=>{let n=await ve(e,Z.parse(t.payload),{onProgress:e=>t.updateProgress(e)});e.logger.info(n,`paths-cascade: descendant regeneration complete`)})}),n(a,async t=>{await L(()=>R(async()=>{let n=await ye(e,Q.parse(t.payload),{onProgress:e=>t.updateProgress({...e,site:N()??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`}))}),pe(async(e,t,n,a)=>{await new r({app:e,logger:e.logger}).enqueue(i,{entityType:t.name,parentId:n,locale:a})})}const $=V({name:`toolkit_path_patterns`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityType:B.varchar({length:100,notNull:!0,pgName:`entity_type`}),locale:B.varchar({length:10,notNull:!0}),patternHash:B.varchar({length:64,notNull:!0,pgName:`pattern_hash`}),updatedAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityType`,`locale`]}]}),xe=$.table;function Se(e,t,n){let r=O(e,t,n);if(r===null)return null;let i=typeof r==`function`?r.toString():JSON.stringify(r);return ie(`sha256`).update(i).digest(`hex`)}async function Ce(e){let t=a(),n=t.defaultLocale,r=t.locales.map(e=>e.code),i=$.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=Se(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 we=async(e,t,n)=>{if(t.length===0)return new Map;let[{buildEnumerateDeps:r},{getCanonicalPaths:i}]=await Promise.all([import(`./resolve-context-D8UXwWwr.mjs`).then(e=>e.u),import(`./enumerate-CYHJKQz6.mjs`).then(e=>e.n)]),a=r(A()),o=await a.filterPublished(e,t,n);return o.size===0?new Map:i(e,[...o],n,a)},Te=async(e,t)=>{if(e.length===0)return new Set;let{buildEnumerateDeps:n}=await import(`./resolve-context-D8UXwWwr.mjs`).then(e=>e.u),r=n(A()),i=[...new Set(e)],a=i.slice(0,48);i.length>a.length&&A().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},Ee=async e=>{let[{buildBreadcrumbDeps:t},{resolveBreadcrumbTrail:n}]=await Promise.all([import(`./resolve-context-D8UXwWwr.mjs`).then(e=>e.u),import(`./breadcrumbs-BqSuMNac.mjs`).then(e=>e.t)]);return(await n(e,t(A())))?.steps};function De(e){return f(e,{name:`${e.name}_drafts`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),locale:B.varchar({length:10,notNull:!0,default:`_`}),data:B.jsonb({notNull:!0}),createdBy:B.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:B.varchar({length:255,pgName:`created_by_name`}),createdAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function Oe(e){return f(e,{name:`${e.name}_locale_status`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),locale:B.varchar({length:10,notNull:!0}),status:B.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:B.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function ke(e){return f(e,{name:`${e.name}_versions`,columns:{id:B.uuid({primaryKey:!0,defaultRandom:!0}),entityId:B.uuid({notNull:!0,pgName:`entity_id`}),version:B.integer({notNull:!0}),locale:B.varchar({length:10,notNull:!0,default:`_`}),data:B.jsonb({notNull:!0}),delta:B.jsonb(),status:B.varchar({length:20}),createdBy:B.varchar({length:255,pgName:`created_by`}),createdByName:B.varchar({length:255,pgName:`created_by_name`}),createdAt:B.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:B.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}function Ae(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 k({name:`@murumets-ee/content`,contributes:{"blocks.documentDraftStore":oe(),"blocks.documentVersionStore":ue(),"blocks.publishedPathResolver":we,"blocks.publishedPathsAmong":Te,"blocks.breadcrumbTrail":Ee,...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:S,toolkitPaths:g,toolkitPathPatterns:xe,toolkitRedirects:v},routes:[...x(),...b()],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:l,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:u,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=ke(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=Oe(i);H.register(t,n.table),s(e,{localeStatus:n})}r++}if(t){let t=`${e}_drafts`;if(!H.has(t)){let n=De(i);H.register(t,n.table),s(e,{drafts:n})}a++}}q(e.locales.map(e=>e.code)),_e();try{await be(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 Ce(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{K as n,Ae as t};
2
+ //# sourceMappingURL=plugin-ChCEq_Qy.mjs.map