@murumets-ee/content 0.19.0 → 0.21.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,102 +1,101 @@
1
1
  import { t as BlockDefinition } from "../types-BCmX1nBL.mjs";
2
- import * as _$_murumets_ee_entity0 from "@murumets-ee/entity";
3
2
 
4
3
  //#region src/blocks/code.d.ts
5
4
  declare const CodeBlock: BlockDefinition<{
6
- readonly code: _$_murumets_ee_entity0.TextField & {
5
+ readonly code: import("@murumets-ee/entity").TextField & {
7
6
  readonly required: true;
8
7
  };
9
- readonly language: _$_murumets_ee_entity0.SelectField & {
8
+ readonly language: import("@murumets-ee/entity").SelectField & {
10
9
  options: readonly ["javascript", "typescript", "python", "html", "css", "json", "bash", "sql", "other"];
11
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
10
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
12
11
  }>;
13
12
  //#endregion
14
13
  //#region src/blocks/cta.d.ts
15
14
  declare const CTABlock: BlockDefinition<{
16
- readonly heading: _$_murumets_ee_entity0.TextField & {
15
+ readonly heading: import("@murumets-ee/entity").TextField & {
17
16
  readonly required: true;
18
17
  readonly translatable: true;
19
18
  };
20
- readonly description: _$_murumets_ee_entity0.TextField & {
19
+ readonly description: import("@murumets-ee/entity").TextField & {
21
20
  readonly translatable: true;
22
21
  };
23
- readonly buttonText: _$_murumets_ee_entity0.TextField & {
22
+ readonly buttonText: import("@murumets-ee/entity").TextField & {
24
23
  readonly required: true;
25
24
  readonly translatable: true;
26
25
  };
27
- readonly buttonLink: _$_murumets_ee_entity0.TextField & {
26
+ readonly buttonLink: import("@murumets-ee/entity").TextField & {
28
27
  readonly required: true;
29
28
  };
30
29
  }>;
31
30
  //#endregion
32
31
  //#region src/blocks/gallery.d.ts
33
32
  declare const GalleryBlock: BlockDefinition<{
34
- readonly images: _$_murumets_ee_entity0.ReferenceField & {
33
+ readonly images: import("@murumets-ee/entity").ReferenceField & {
35
34
  cardinality: "many";
36
- } & Partial<Omit<_$_murumets_ee_entity0.ReferenceField, "entity" | "cardinality">>;
37
- readonly columns: _$_murumets_ee_entity0.SelectField & {
35
+ } & Partial<Omit<import("@murumets-ee/entity").ReferenceField, "cardinality" | "entity">>;
36
+ readonly columns: import("@murumets-ee/entity").SelectField & {
38
37
  options: readonly ["2", "3", "4"];
39
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
38
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
40
39
  }>;
41
40
  //#endregion
42
41
  //#region src/blocks/hero.d.ts
43
42
  declare const HeroBlock: BlockDefinition<{
44
- readonly heading: _$_murumets_ee_entity0.TextField & {
43
+ readonly heading: import("@murumets-ee/entity").TextField & {
45
44
  readonly required: true;
46
45
  readonly translatable: true;
47
46
  };
48
- readonly subheading: _$_murumets_ee_entity0.TextField & {
47
+ readonly subheading: import("@murumets-ee/entity").TextField & {
49
48
  readonly translatable: true;
50
49
  };
51
- readonly background: _$_murumets_ee_entity0.MediaField & {
50
+ readonly background: import("@murumets-ee/entity").MediaField & {
52
51
  readonly accept: ["image/*"];
53
52
  };
54
- readonly alignment: _$_murumets_ee_entity0.SelectField & {
53
+ readonly alignment: import("@murumets-ee/entity").SelectField & {
55
54
  options: readonly ["left", "center", "right"];
56
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
55
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
57
56
  }>;
58
57
  //#endregion
59
58
  //#region src/blocks/image.d.ts
60
59
  declare const ImageBlock: BlockDefinition<{
61
- readonly image: _$_murumets_ee_entity0.MediaField & {
60
+ readonly image: import("@murumets-ee/entity").MediaField & {
62
61
  readonly accept: ["image/*"];
63
62
  readonly required: true;
64
63
  };
65
- readonly alt: _$_murumets_ee_entity0.TextField & {
64
+ readonly alt: import("@murumets-ee/entity").TextField & {
66
65
  readonly translatable: true;
67
66
  };
68
- readonly caption: _$_murumets_ee_entity0.TextField & {
67
+ readonly caption: import("@murumets-ee/entity").TextField & {
69
68
  readonly translatable: true;
70
69
  };
71
70
  }>;
72
71
  //#endregion
73
72
  //#region src/blocks/quote.d.ts
74
73
  declare const QuoteBlock: BlockDefinition<{
75
- readonly quote: _$_murumets_ee_entity0.TextField & {
74
+ readonly quote: import("@murumets-ee/entity").TextField & {
76
75
  readonly required: true;
77
76
  readonly translatable: true;
78
77
  };
79
- readonly attribution: _$_murumets_ee_entity0.TextField & {
78
+ readonly attribution: import("@murumets-ee/entity").TextField & {
80
79
  readonly translatable: true;
81
80
  };
82
81
  }>;
83
82
  //#endregion
84
83
  //#region src/blocks/richtext.d.ts
85
84
  declare const RichTextBlock: BlockDefinition<{
86
- readonly content: _$_murumets_ee_entity0.RichTextField & {
85
+ readonly content: import("@murumets-ee/entity").RichTextField & {
87
86
  readonly translatable: true;
88
87
  };
89
88
  }>;
90
89
  //#endregion
91
90
  //#region src/blocks/video.d.ts
92
91
  declare const VideoBlock: BlockDefinition<{
93
- readonly url: _$_murumets_ee_entity0.TextField & {
92
+ readonly url: import("@murumets-ee/entity").TextField & {
94
93
  readonly required: true;
95
94
  };
96
- readonly poster: _$_murumets_ee_entity0.MediaField & {
95
+ readonly poster: import("@murumets-ee/entity").MediaField & {
97
96
  readonly accept: ["image/*"];
98
97
  };
99
- readonly caption: _$_murumets_ee_entity0.TextField & {
98
+ readonly caption: import("@murumets-ee/entity").TextField & {
100
99
  readonly translatable: true;
101
100
  };
102
101
  }>;
@@ -116,81 +115,81 @@ declare const VideoBlock: BlockDefinition<{
116
115
  * })
117
116
  */
118
117
  declare const commonBlocks: readonly [BlockDefinition<{
119
- readonly content: _$_murumets_ee_entity0.RichTextField & {
118
+ readonly content: import("@murumets-ee/entity").RichTextField & {
120
119
  readonly translatable: true;
121
120
  };
122
121
  }>, BlockDefinition<{
123
- readonly image: _$_murumets_ee_entity0.MediaField & {
122
+ readonly image: import("@murumets-ee/entity").MediaField & {
124
123
  readonly accept: ["image/*"];
125
124
  readonly required: true;
126
125
  };
127
- readonly alt: _$_murumets_ee_entity0.TextField & {
126
+ readonly alt: import("@murumets-ee/entity").TextField & {
128
127
  readonly translatable: true;
129
128
  };
130
- readonly caption: _$_murumets_ee_entity0.TextField & {
129
+ readonly caption: import("@murumets-ee/entity").TextField & {
131
130
  readonly translatable: true;
132
131
  };
133
132
  }>, BlockDefinition<{
134
- readonly heading: _$_murumets_ee_entity0.TextField & {
133
+ readonly heading: import("@murumets-ee/entity").TextField & {
135
134
  readonly required: true;
136
135
  readonly translatable: true;
137
136
  };
138
- readonly subheading: _$_murumets_ee_entity0.TextField & {
137
+ readonly subheading: import("@murumets-ee/entity").TextField & {
139
138
  readonly translatable: true;
140
139
  };
141
- readonly background: _$_murumets_ee_entity0.MediaField & {
140
+ readonly background: import("@murumets-ee/entity").MediaField & {
142
141
  readonly accept: ["image/*"];
143
142
  };
144
- readonly alignment: _$_murumets_ee_entity0.SelectField & {
143
+ readonly alignment: import("@murumets-ee/entity").SelectField & {
145
144
  options: readonly ["left", "center", "right"];
146
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
145
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
147
146
  }>, BlockDefinition<{
148
- readonly images: _$_murumets_ee_entity0.ReferenceField & {
147
+ readonly images: import("@murumets-ee/entity").ReferenceField & {
149
148
  cardinality: "many";
150
- } & Partial<Omit<_$_murumets_ee_entity0.ReferenceField, "entity" | "cardinality">>;
151
- readonly columns: _$_murumets_ee_entity0.SelectField & {
149
+ } & Partial<Omit<import("@murumets-ee/entity").ReferenceField, "cardinality" | "entity">>;
150
+ readonly columns: import("@murumets-ee/entity").SelectField & {
152
151
  options: readonly ["2", "3", "4"];
153
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
152
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
154
153
  }>, BlockDefinition<{
155
- readonly heading: _$_murumets_ee_entity0.TextField & {
154
+ readonly heading: import("@murumets-ee/entity").TextField & {
156
155
  readonly required: true;
157
156
  readonly translatable: true;
158
157
  };
159
- readonly description: _$_murumets_ee_entity0.TextField & {
158
+ readonly description: import("@murumets-ee/entity").TextField & {
160
159
  readonly translatable: true;
161
160
  };
162
- readonly buttonText: _$_murumets_ee_entity0.TextField & {
161
+ readonly buttonText: import("@murumets-ee/entity").TextField & {
163
162
  readonly required: true;
164
163
  readonly translatable: true;
165
164
  };
166
- readonly buttonLink: _$_murumets_ee_entity0.TextField & {
165
+ readonly buttonLink: import("@murumets-ee/entity").TextField & {
167
166
  readonly required: true;
168
167
  };
169
168
  }>, BlockDefinition<{
170
- readonly url: _$_murumets_ee_entity0.TextField & {
169
+ readonly url: import("@murumets-ee/entity").TextField & {
171
170
  readonly required: true;
172
171
  };
173
- readonly poster: _$_murumets_ee_entity0.MediaField & {
172
+ readonly poster: import("@murumets-ee/entity").MediaField & {
174
173
  readonly accept: ["image/*"];
175
174
  };
176
- readonly caption: _$_murumets_ee_entity0.TextField & {
175
+ readonly caption: import("@murumets-ee/entity").TextField & {
177
176
  readonly translatable: true;
178
177
  };
179
178
  }>, BlockDefinition<{
180
- readonly quote: _$_murumets_ee_entity0.TextField & {
179
+ readonly quote: import("@murumets-ee/entity").TextField & {
181
180
  readonly required: true;
182
181
  readonly translatable: true;
183
182
  };
184
- readonly attribution: _$_murumets_ee_entity0.TextField & {
183
+ readonly attribution: import("@murumets-ee/entity").TextField & {
185
184
  readonly translatable: true;
186
185
  };
187
186
  }>, BlockDefinition<{
188
- readonly code: _$_murumets_ee_entity0.TextField & {
187
+ readonly code: import("@murumets-ee/entity").TextField & {
189
188
  readonly required: true;
190
189
  };
191
- readonly language: _$_murumets_ee_entity0.SelectField & {
190
+ readonly language: import("@murumets-ee/entity").SelectField & {
192
191
  options: readonly ["javascript", "typescript", "python", "html", "css", "json", "bash", "sql", "other"];
193
- } & Omit<Partial<_$_murumets_ee_entity0.SelectField>, "options">;
192
+ } & Omit<Partial<import("@murumets-ee/entity").SelectField>, "options">;
194
193
  }>];
195
194
  //#endregion
196
195
  export { CTABlock, CodeBlock, GalleryBlock, HeroBlock, ImageBlock, QuoteBlock, RichTextBlock, VideoBlock, commonBlocks };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/blocks/code.ts","../../src/blocks/cta.ts","../../src/blocks/gallery.ts","../../src/blocks/hero.ts","../../src/blocks/image.ts","../../src/blocks/quote.ts","../../src/blocks/richtext.ts","../../src/blocks/video.ts","../../src/blocks/index.ts"],"mappings":";;;;cAGa,SAAA;iBAmBX,sBAAA,CAAA,SAAA;IAAA;;;;;;;;cCnBW,QAAA;oBASX,sBAAA,CAAA,SAAA;IAAA;;;;;;;;;;;;;;;;cCTW,YAAA;mBAOX,sBAAA,CAAA,cAAA;;;;;;;;;cCPW,SAAA;oBASX,sBAAA,CAAA,SAAA;IAAA;;;;;;;;;;;;;;;cCTW,UAAA;kBAQX,sBAAA,CAAA,UAAA;IAAA;;;;;;;;;;;;cCRW,UAAA;kBAOX,sBAAA,CAAA,SAAA;IAAA;;;;;;;;;cCPW,aAAA,EAMX,eAAA;EAAA,kBAAA,sBAAA,CAAA,aAAA;IAAA;;;;;cCNW,UAAA;gBAQX,sBAAA,CAAA,SAAA;IAAA;;;;;;;;;;;;;;;;;;;;;;;;cCoBW,YAAA;oBASH,sBAAA,CAAA,aAAA;IAAA;;;kBATe,sBAAA,CAAA,UAAA;IAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/blocks/code.ts","../../src/blocks/cta.ts","../../src/blocks/gallery.ts","../../src/blocks/hero.ts","../../src/blocks/image.ts","../../src/blocks/quote.ts","../../src/blocks/richtext.ts","../../src/blocks/video.ts","../../src/blocks/index.ts"],"mappings":";;;cAGa,SAAA;;;;;;;;;;cCAA,QAAA,EASX,eAAA;EAAA;;;;;;;;;;;;;;;;;cCTW,YAAA;;;;;;;;;;cCAA,SAAA;;;;;;;;;;;;;;;;;cCAA,UAAA,EAQX,eAAA;EAAA;;;;;;;;;;;;;cCRW,UAAA,EAOX,eAAA;EAAA;;;;;;;;;;cCPW,aAAA,EAMX,eAAA;EAAA;;;;;;cCNW,UAAA,EAQX,eAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;cCoBW,YAAA,YASH,eAAA;EAAA;;;IATe,eAAA;EAAA"}
package/dist/client.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./versionable-B0bP2Gib.mjs";import{t}from"./errors-kkf8Ue1H.mjs";import{n,r}from"./plugin-yijaaFS7.mjs";import{t as i}from"./locale-utils-By7EdAT1.mjs";import"server-only";import{getApp as a}from"@murumets-ee/core";import{createAdminClient as o}from"@murumets-ee/core/clients";import{schemaRegistry as s}from"@murumets-ee/db";import{and as c,eq as l,inArray as u}from"drizzle-orm";function d(e){return e.match(/\S+|\s+/g)??[]}function f(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=d(e),r=d(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,u=[];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)u.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)))u.push({type:`add`,value:t}),l--;else if(e!==void 0)u.push({type:`remove`,value:e}),c--;else break}u.reverse();for(let e of u){let t=s[s.length-1];t&&t.type===e.type?t.value+=e.value:s.push({...e})}return s}function p(e){return e.replace(/<[^>]*>/g,` `).replace(/\s+/g,` `).trim()}function m(e){return e==null?``:e instanceof Date?e.toISOString():typeof e==`object`?JSON.stringify(e):String(e)}function ee(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:f(m(t),m(n))};case`richtext`:return{field:e,type:`richtext`,from:t,to:n,segments:f(p(m(t)),p(m(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`blocks`:return{field:e,type:`blocks`,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=ee(a,e[a],t[a],n?.[a]);r&&i.push(r)}return i}function g(e,t){let n=e??t;return typeof n==`number`?`number`:typeof n==`boolean`?`boolean`:n instanceof Date?`date`:typeof n==`string`?/^\d{4}-\d{2}-\d{2}/.test(n)?`date`:`text`:Array.isArray(n)?n.length>0&&typeof n[0]==`object`&&n[0]!==null&&`_block`in n[0]?`blocks`:`json`:typeof n==`object`?`json`:`unknown`}function _(e,t){if(typeof e==`string`||typeof t==`string`)return f(m(e),m(t))}function v(e){return typeof e==`object`&&!!e&&`_block`in e&&`_id`in e&&typeof e._block==`string`&&typeof e._id==`string`}function y(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let i of n){if(i===`_block`||i===`_id`)continue;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,t){let n=e.filter(v),r=t.filter(v),i=new Map,a=new Map;for(let e=0;e<n.length;e++){let t=n[e];t&&i.set(t._id,{block:t,index:e})}for(let e=0;e<r.length;e++){let t=r[e];t&&a.set(t._id,{block:t,index:e})}let o=[],s=new Set;for(let[e,{block:t,index:n}]of a){s.add(e);let r=i.get(e);if(!r){o.push({blockId:e,blockType:t._block,status:`added`,toIndex:n});continue}let a=r.block,c=r.index,l=y(a,t),u=c!==n;l.length===0&&!u?o.push({blockId:e,blockType:t._block,status:`unchanged`,fromIndex:c,toIndex:n}):l.length>0?o.push({blockId:e,blockType:t._block,status:`changed`,fromIndex:c,toIndex:n,propDiffs:l}):o.push({blockId:e,blockType:t._block,status:`moved`,fromIndex:c,toIndex:n})}for(let[e,{block:t,index:n}]of i)s.has(e)||o.push({blockId:e,blockType:t._block,status:`removed`,fromIndex:n});return o}function x(e,t){return{entity:e.entity,admin:e.admin,db:e.admin.getDb(),versionableOptions:t,logger:e.logger}}function S(e){let{drafts:t}=r(e.entity.name);return t?t.makeClient(e.db):null}function C(e){let{versions:t}=r(e.entity.name);return t?t.makeClient(e.db):null}function w(e){let{localeStatus:t}=r(e.entity.name);return t?t.makeClient(e.db):null}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}async function k(e,t,n){n?.locale!==void 0&&i(n.locale);let r=C(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 A(e,t,n,r=`_`){i(r);let a=C(e);return a?await a.findOne({entityId:t,version:n,locale:r})??null:null}async function j(e,t,n,r,a=`_`){i(a);let[o,s]=await Promise.all([A(e,t,n,a),A(e,t,r,a)]);if(!o||!s)return null;let c=e.entity.allFields,l=h(o.data,s.data,c);for(let e of l)e.type===`blocks`&&Array.isArray(e.from)&&Array.isArray(e.to)&&(e.blockDiffs=b(e.from,e.to));return{diffs:l,fromVersion:o,toVersion:s}}async function M(e,n,r){if(!C(e))throw new t(e.entity.name,`versions`);let i=await A(e,n,r);if(!i)throw Error(`Version ${r} not found for entity ${n}`);let a=i.data,o=E(e.entity,a);return e.admin.update(n,o)}async function N(e,t,n=`_`){if(i(n),!e.versionableOptions)return;let r=C(e);if(!r)return;let a=e.versionableOptions.maxPerDoc,o=await r.findMany({where:{entityId:t,locale:n},orderBy:[{column:`version`,dir:`desc`}],limit:a});if(o.length<a)return;let s=o.map(e=>e.id);await r.deleteMany({entityId:t,locale:n,id:{notIn:s}})}async function P(e,t,n,r,a,o=`_`,s){i(o);let c=C(e);if(!c)return;let[l]=await c.aggregate({select:{maxVersion:{fn:`max`,column:`version`}},where:{entityId:t,locale:o}}),u=Number(l?.maxVersion??0)+1,d=n.status??null;if(a){let e=await c.findOne({entityId:t,isAutosave:!0,version:u,locale:o});if(e)return await c.update({id:e.id},{data:n,delta:r,status:d,createdAt:new Date}),u}return await c.insert({entityId:t,version:u,locale:o,data:n,delta:r,status:d,createdBy:s?.id??null,createdByName:s?.name??null,isAutosave:a}),u}async function F(e,t,n,r,a,o){if(a!==void 0&&i(a),!e.versionableOptions)return;let s=a??`_`,c=await P(e,t,n,r?O(r,n):null,!1,s,o);c!==void 0&&s===`_`&&await I(e,t,c),await N(e,t,s)}async function I(e,t,n){let r=s.get(e.entity.name);r&&await e.db.update(r).set({_version:n}).where(l(r.id,t))}function L(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 R(e,n,r,a=`_`,o){i(a);let s=S(e);if(!s)throw new t(e.entity.name,`drafts`);let c=new Date,l=o?.id??``,u=o?.name??null;return L(await s.upsert({entityId:n,locale:a,data:r,createdBy:l,createdByName:u,createdAt:c,updatedAt:c},{target:[`entityId`,`locale`],set:{data:r,updatedAt:c}}))}async function z(e,t,n=`_`){i(n);let r=S(e);if(!r)return null;let a=await r.findOne({entityId:t,locale:n});return a?L(a):null}async function B(e,t,n=`_`){i(n);let r=S(e);return r?(await r.deleteMany({entityId:t,locale:n})).length>0:!1}async function V(e,n,r=`_`,a){if(i(r),!S(e))throw new t(e.entity.name,`drafts`);let o=await z(e,n,r);if(!o)throw Error(`No draft found for entity ${n} locale ${r}`);let s=null;e.versionableOptions&&(s=await e.admin.findById(n));let c=o.data,l=E(e.entity,c),u=await e.admin.update(n,l);return await B(e,n,r),e.versionableOptions&&await F(e,n,u,s,r,a),u}async function H(e,t,n=`_`){i(n);let r=S(e);return r?r.exists({entityId:t,locale:n}):!1}async function U(e,t=`_`){i(t);let n=S(e);return n?n.count({locale:t}):0}async function W(e,t,n=`_`){i(n);let r=new Set;if(t.length===0)return r;let a=S(e);if(!a)return r;let o=await a.findMany({where:{entityId:{in:t},locale:n}});for(let e of o)r.add(e.entityId);return r}async function G(e,t,r=`_`){i(r);let a=await z(e,t,r);if(!a)return null;let o=n().defaultLocale,s=r===`_`?{defaultLocale:o}:{locale:r,defaultLocale:o},c=await e.admin.findById(t,s);if(!c)return null;let l=e.entity.allFields,u=c,d=a.data,f={};for(let e of Object.keys(d))f[e]=u[e];let p=h(f,d,l);for(let e of p)e.type===`blocks`&&Array.isArray(e.from)&&Array.isArray(e.to)&&(e.blockDiffs=b(e.from,e.to));return{diffs:p,current:c,draft:a}}async function K(e,t,n){i(n.from);let r=Array.isArray(n.to)?n.to:[n.to];for(let e of r)i(e);let a=s.get(`${e.entity.name}_layout`);if(!a)return;let o=await e.db.select().from(a).where(c(l(a.entityId,t),l(a.locale,n.from))).orderBy(a.sortOrder);if(o.length!==0){for(let n of r){await e.db.delete(a).where(c(l(a.entityId,t),l(a.locale,n)));for(let t of o)await e.db.insert(a).values({entityId:t.entityId,fieldName:t.fieldName,blockType:t.blockType,sortOrder:t.sortOrder,data:t.data,locale:n})}e.logger?.debug({entityId:t,from:n.from,to:r},`Layout copied between locales`)}}async function q(e,t){let n=(await Y(e,[t])).get(t);return n?Object.values(n):[]}async function J(e,r,a,o){if(i(a),a===n().defaultLocale){let t={status:o};o===`published`&&(t.publishedAt=new Date),await e.admin.update(r,t);return}let s=w(e);if(!s)throw new t(e.entity.name,`localeStatus`);let c=o===`published`?new Date:null;await s.upsert({entityId:r,locale:a,status:o,publishedAt:c},{target:[`entityId`,`locale`],set:{status:o,publishedAt:c}})}async function Y(e,t){let n=new Map;if(t.length===0)return n;let r=w(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(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 Z(e,t){return(await Q(e,[t])).get(t)??{}}async function Q(e,t){let r=new Map;if(t.length===0)return r;let i=n(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return r;let o=s.get(`${e.entity.name}_translations`);if(!o)return r;let c=await e.db.select().from(o).where(u(o.entityId,t)),l=new Map;for(let e of c){let t=e,n=t.entityId,r=t.locale,i={};for(let e of a)i[e]=t[e];let o=l.get(n);o||(o=new Map,l.set(n,o)),o.set(r,i)}for(let e of t)r.set(e,X(a,l.get(e),i.locales,i.defaultLocale));return r}async function te(e,t){let r=n(),i=t?.locale??r.locales.find(e=>e.code!==r.defaultLocale)?.code;if(!i)return{total:0,complete:0,partial:0,missing:0};let a=await e.admin.findMany();if(a.length===0)return{total:0,complete:0,partial:0,missing:0};let o=await Q(e,a.map(e=>e.id)),s=0,c=0,l=0;for(let e of a){let t=o.get(e.id)?.[i];!t||t.translated===0?l++:t.complete?s++:c++}return{total:a.length,complete:s,partial:c,missing:l}}var $=class{ctx;constructor(t){let n=e(t.entity.behaviors);this.ctx=x(t,n)}async create(e){let t=await this.ctx.admin.create(e);return this.ctx.versionableOptions&&await P(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=O(n,r),i=await P(this.ctx,e,r,t,!1);if(i!==void 0)return await I(this.ctx,e,i),await N(this.ctx,e),{...r,_version:i};await N(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)}copyLayout(e,t){return K(this.ctx,e,t)}getVersions(e,t){return k(this.ctx,e,t)}getVersion(e,t,n){return A(this.ctx,e,t,n)}compareVersions(e,t,n,r){return j(this.ctx,e,t,n,r)}restoreVersion(e,t){return M(this.ctx,e,t)}pruneVersions(e,t){return N(this.ctx,e,t)}createVersionSnapshot(e,t,n,r,i){return F(this.ctx,e,t,n,r,i)}getTranslationStatus(e){return Z(this.ctx,e)}getTranslationStatusBatch(e){return Q(this.ctx,e)}getTranslationReport(e){return te(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 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 H(this.ctx,e,t)}countDrafts(e){return U(this.ctx,e)}hasDraftBatch(e,t){return W(this.ctx,e,t)}compareDraftWithCurrent(e,t){return G(this.ctx,e,t)}};function ne(e){return new $({admin:o(e),entity:e,logger:a().logger.child({content:e.name})})}export{$ as ContentClient,ne as createContentClient};
1
+ import{t as e}from"./versionable-B0bP2Gib.mjs";import{t}from"./errors-kkf8Ue1H.mjs";import{n,r}from"./plugin-DkmXJ7Qw.mjs";import{t as i}from"./locale-utils-BMRVJwvU.mjs";import"server-only";import{getApp as a}from"@murumets-ee/core";import{createAdminClient as o}from"@murumets-ee/core/clients";import{schemaRegistry as s}from"@murumets-ee/db";import{and as c,eq as l,inArray as u}from"drizzle-orm";function d(e){return e.match(/\S+|\s+/g)??[]}function f(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=d(e),r=d(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,u=[];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)u.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)))u.push({type:`add`,value:t}),l--;else if(e!==void 0)u.push({type:`remove`,value:e}),c--;else break}u.reverse();for(let e of u){let t=s[s.length-1];t&&t.type===e.type?t.value+=e.value:s.push({...e})}return s}function p(e){return e.replace(/<[^>]*>/g,` `).replace(/\s+/g,` `).trim()}function m(e){return e==null?``:e instanceof Date?e.toISOString():typeof e==`object`?JSON.stringify(e):String(e)}function ee(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:f(m(t),m(n))};case`richtext`:return{field:e,type:`richtext`,from:t,to:n,segments:f(p(m(t)),p(m(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`blocks`:return{field:e,type:`blocks`,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=ee(a,e[a],t[a],n?.[a]);r&&i.push(r)}return i}function g(e,t){let n=e??t;return typeof n==`number`?`number`:typeof n==`boolean`?`boolean`:n instanceof Date?`date`:typeof n==`string`?/^\d{4}-\d{2}-\d{2}/.test(n)?`date`:`text`:Array.isArray(n)?n.length>0&&typeof n[0]==`object`&&n[0]!==null&&`_block`in n[0]?`blocks`:`json`:typeof n==`object`?`json`:`unknown`}function _(e,t){if(typeof e==`string`||typeof t==`string`)return f(m(e),m(t))}function v(e){return typeof e==`object`&&!!e&&`_block`in e&&`_id`in e&&typeof e._block==`string`&&typeof e._id==`string`}function y(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let i of n){if(i===`_block`||i===`_id`)continue;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,t){let n=e.filter(v),r=t.filter(v),i=new Map,a=new Map;for(let e=0;e<n.length;e++){let t=n[e];t&&i.set(t._id,{block:t,index:e})}for(let e=0;e<r.length;e++){let t=r[e];t&&a.set(t._id,{block:t,index:e})}let o=[],s=new Set;for(let[e,{block:t,index:n}]of a){s.add(e);let r=i.get(e);if(!r){o.push({blockId:e,blockType:t._block,status:`added`,toIndex:n});continue}let a=r.block,c=r.index,l=y(a,t),u=c!==n;l.length===0&&!u?o.push({blockId:e,blockType:t._block,status:`unchanged`,fromIndex:c,toIndex:n}):l.length>0?o.push({blockId:e,blockType:t._block,status:`changed`,fromIndex:c,toIndex:n,propDiffs:l}):o.push({blockId:e,blockType:t._block,status:`moved`,fromIndex:c,toIndex:n})}for(let[e,{block:t,index:n}]of i)s.has(e)||o.push({blockId:e,blockType:t._block,status:`removed`,fromIndex:n});return o}function x(e,t){return{entity:e.entity,admin:e.admin,db:e.admin.getDb(),versionableOptions:t,logger:e.logger}}function S(e){let{drafts:t}=r(e.entity.name);return t?t.makeClient(e.db):null}function C(e){let{versions:t}=r(e.entity.name);return t?t.makeClient(e.db):null}function w(e){let{localeStatus:t}=r(e.entity.name);return t?t.makeClient(e.db):null}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}async function k(e,t,n){n?.locale!==void 0&&i(n.locale);let r=C(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 A(e,t,n,r=`_`){i(r);let a=C(e);return a?await a.findOne({entityId:t,version:n,locale:r})??null:null}async function j(e,t,n,r,a=`_`){i(a);let[o,s]=await Promise.all([A(e,t,n,a),A(e,t,r,a)]);if(!o||!s)return null;let c=e.entity.allFields,l=h(o.data,s.data,c);for(let e of l)e.type===`blocks`&&Array.isArray(e.from)&&Array.isArray(e.to)&&(e.blockDiffs=b(e.from,e.to));return{diffs:l,fromVersion:o,toVersion:s}}async function M(e,n,r){if(!C(e))throw new t(e.entity.name,`versions`);let i=await A(e,n,r);if(!i)throw Error(`Version ${r} not found for entity ${n}`);let a=i.data,o=E(e.entity,a);return e.admin.update(n,o)}async function N(e,t,n=`_`){if(i(n),!e.versionableOptions)return;let r=C(e);if(!r)return;let a=e.versionableOptions.maxPerDoc,o=await r.findMany({where:{entityId:t,locale:n},orderBy:[{column:`version`,dir:`desc`}],limit:a});if(o.length<a)return;let s=o.map(e=>e.id);await r.deleteMany({entityId:t,locale:n,id:{notIn:s}})}async function P(e,t,n,r,a,o=`_`,s){i(o);let c=C(e);if(!c)return;let[l]=await c.aggregate({select:{maxVersion:{fn:`max`,column:`version`}},where:{entityId:t,locale:o}}),u=Number(l?.maxVersion??0)+1,d=n.status??null;if(a){let e=await c.findOne({entityId:t,isAutosave:!0,version:u,locale:o});if(e)return await c.update({id:e.id},{data:n,delta:r,status:d,createdAt:new Date}),u}return await c.insert({entityId:t,version:u,locale:o,data:n,delta:r,status:d,createdBy:s?.id??null,createdByName:s?.name??null,isAutosave:a}),u}async function F(e,t,n,r,a,o){if(a!==void 0&&i(a),!e.versionableOptions)return;let s=a??`_`,c=await P(e,t,n,r?O(r,n):null,!1,s,o);c!==void 0&&s===`_`&&await I(e,t,c),await N(e,t,s)}async function I(e,t,n){let r=s.get(e.entity.name);r&&await e.db.update(r).set({_version:n}).where(l(r.id,t))}function L(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 R(e,n,r,a=`_`,o){i(a);let s=S(e);if(!s)throw new t(e.entity.name,`drafts`);let c=new Date,l=o?.id??``,u=o?.name??null;return L(await s.upsert({entityId:n,locale:a,data:r,createdBy:l,createdByName:u,createdAt:c,updatedAt:c},{target:[`entityId`,`locale`],set:{data:r,updatedAt:c}}))}async function z(e,t,n=`_`){i(n);let r=S(e);if(!r)return null;let a=await r.findOne({entityId:t,locale:n});return a?L(a):null}async function B(e,t,n=`_`){i(n);let r=S(e);return r?(await r.deleteMany({entityId:t,locale:n})).length>0:!1}async function V(e,n,r=`_`,a){if(i(r),!S(e))throw new t(e.entity.name,`drafts`);let o=await z(e,n,r);if(!o)throw Error(`No draft found for entity ${n} locale ${r}`);let s=null;e.versionableOptions&&(s=await e.admin.findById(n));let c=o.data,l=E(e.entity,c),u=await e.admin.update(n,l);return await B(e,n,r),e.versionableOptions&&await F(e,n,u,s,r,a),u}async function H(e,t,n=`_`){i(n);let r=S(e);return r?r.exists({entityId:t,locale:n}):!1}async function U(e,t=`_`){i(t);let n=S(e);return n?n.count({locale:t}):0}async function W(e,t,n=`_`){i(n);let r=new Set;if(t.length===0)return r;let a=S(e);if(!a)return r;let o=await a.findMany({where:{entityId:{in:t},locale:n}});for(let e of o)r.add(e.entityId);return r}async function G(e,t,r=`_`){i(r);let a=await z(e,t,r);if(!a)return null;let o=n().defaultLocale,s=r===`_`?{defaultLocale:o}:{locale:r,defaultLocale:o},c=await e.admin.findById(t,s);if(!c)return null;let l=e.entity.allFields,u=c,d=a.data,f={};for(let e of Object.keys(d))f[e]=u[e];let p=h(f,d,l);for(let e of p)e.type===`blocks`&&Array.isArray(e.from)&&Array.isArray(e.to)&&(e.blockDiffs=b(e.from,e.to));return{diffs:p,current:c,draft:a}}async function K(e,t,n){i(n.from);let r=Array.isArray(n.to)?n.to:[n.to];for(let e of r)i(e);let a=s.get(`${e.entity.name}_layout`);if(!a)return;let o=await e.db.select().from(a).where(c(l(a.entityId,t),l(a.locale,n.from))).orderBy(a.sortOrder);if(o.length!==0){for(let n of r){await e.db.delete(a).where(c(l(a.entityId,t),l(a.locale,n)));for(let t of o)await e.db.insert(a).values({entityId:t.entityId,fieldName:t.fieldName,blockType:t.blockType,sortOrder:t.sortOrder,data:t.data,locale:n})}e.logger?.debug({entityId:t,from:n.from,to:r},`Layout copied between locales`)}}async function q(e,t){let n=(await Y(e,[t])).get(t);return n?Object.values(n):[]}async function J(e,r,a,o){if(i(a),a===n().defaultLocale){let t={status:o};o===`published`&&(t.publishedAt=new Date),await e.admin.update(r,t);return}let s=w(e);if(!s)throw new t(e.entity.name,`localeStatus`);let c=o===`published`?new Date:null;await s.upsert({entityId:r,locale:a,status:o,publishedAt:c},{target:[`entityId`,`locale`],set:{status:o,publishedAt:c}})}async function Y(e,t){let n=new Map;if(t.length===0)return n;let r=w(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(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 Z(e,t){return(await Q(e,[t])).get(t)??{}}async function Q(e,t){let r=new Map;if(t.length===0)return r;let i=n(),a=Object.entries(e.entity.allFields).filter(([e,t])=>t.translatable).map(([e])=>e);if(a.length===0)return r;let o=s.get(`${e.entity.name}_translations`);if(!o)return r;let c=await e.db.select().from(o).where(u(o.entityId,t)),l=new Map;for(let e of c){let t=e,n=t.entityId,r=t.locale,i={};for(let e of a)i[e]=t[e];let o=l.get(n);o||(o=new Map,l.set(n,o)),o.set(r,i)}for(let e of t)r.set(e,X(a,l.get(e),i.locales,i.defaultLocale));return r}async function te(e,t){let r=n(),i=t?.locale??r.locales.find(e=>e.code!==r.defaultLocale)?.code;if(!i)return{total:0,complete:0,partial:0,missing:0};let a=await e.admin.findMany();if(a.length===0)return{total:0,complete:0,partial:0,missing:0};let o=await Q(e,a.map(e=>e.id)),s=0,c=0,l=0;for(let e of a){let t=o.get(e.id)?.[i];!t||t.translated===0?l++:t.complete?s++:c++}return{total:a.length,complete:s,partial:c,missing:l}}var $=class{ctx;constructor(t){let n=e(t.entity.behaviors);this.ctx=x(t,n)}async create(e){let t=await this.ctx.admin.create(e);return this.ctx.versionableOptions&&await P(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=O(n,r),i=await P(this.ctx,e,r,t,!1);if(i!==void 0)return await I(this.ctx,e,i),await N(this.ctx,e),{...r,_version:i};await N(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)}copyLayout(e,t){return K(this.ctx,e,t)}getVersions(e,t){return k(this.ctx,e,t)}getVersion(e,t,n){return A(this.ctx,e,t,n)}compareVersions(e,t,n,r){return j(this.ctx,e,t,n,r)}restoreVersion(e,t){return M(this.ctx,e,t)}pruneVersions(e,t){return N(this.ctx,e,t)}createVersionSnapshot(e,t,n,r,i){return F(this.ctx,e,t,n,r,i)}getTranslationStatus(e){return Z(this.ctx,e)}getTranslationStatusBatch(e){return Q(this.ctx,e)}getTranslationReport(e){return te(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 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 H(this.ctx,e,t)}countDrafts(e){return U(this.ctx,e)}hasDraftBatch(e,t){return W(this.ctx,e,t)}compareDraftWithCurrent(e,t){return G(this.ctx,e,t)}};function ne(e){return new $({admin:o(e),entity:e,logger:a().logger.child({content:e.name})})}export{$ as ContentClient,ne as createContentClient};
2
2
  //# sourceMappingURL=client.mjs.map
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { a as InferBlocksUnion, c as LockInfo, d as TranslationStatus, f as VersionEntry, i as InferBlockData, l as TranslationFieldStatus, n as ContentConfig, o as LocaleConfig, p as VersionableOptions, r as DraftEntry, t as BlockDefinition, u as TranslationReport } from "./types-BCmX1nBL.mjs";
2
2
  import { _ as workflowable, a as DiffSegment, c as defineBlock, d as WORKFLOW_STATES, f as WORKFLOW_TRANSITIONS, g as isAllowedTransition, h as WorkflowableFields, i as BlockDiffStatus, l as EntityCapability, m as WorkflowTransitionError, n as InvalidLocaleError, o as FieldDiff, p as WorkflowState, r as BlockDiffEntry, s as PropDiff, t as EntityCapabilityError, u as entitySupports, v as getVersionableOptions, y as versionable } from "./index-B4kqLg-c.mjs";
3
- export { BlockDefinition, BlockDiffEntry, BlockDiffStatus, ContentConfig, DiffSegment, DraftEntry, EntityCapability, EntityCapabilityError, FieldDiff, InferBlockData, InferBlocksUnion, InvalidLocaleError, LocaleConfig, LockInfo, PropDiff, TranslationFieldStatus, TranslationReport, TranslationStatus, VersionEntry, VersionableOptions, WORKFLOW_STATES, WORKFLOW_TRANSITIONS, WorkflowState, WorkflowTransitionError, WorkflowableFields, defineBlock, entitySupports, getVersionableOptions, isAllowedTransition, versionable, workflowable };
3
+ export { type BlockDefinition, type BlockDiffEntry, type BlockDiffStatus, type ContentConfig, type DiffSegment, type DraftEntry, type EntityCapability, EntityCapabilityError, type FieldDiff, type InferBlockData, type InferBlocksUnion, InvalidLocaleError, type LocaleConfig, type LockInfo, type PropDiff, type TranslationFieldStatus, type TranslationReport, type TranslationStatus, type VersionEntry, type VersionableOptions, WORKFLOW_STATES, WORKFLOW_TRANSITIONS, type WorkflowState, WorkflowTransitionError, type WorkflowableFields, defineBlock, entitySupports, getVersionableOptions, isAllowedTransition, versionable, workflowable };
@@ -1,2 +1,2 @@
1
- import{n as e}from"./errors-kkf8Ue1H.mjs";import{n as t}from"./plugin-yijaaFS7.mjs";function n(n){if(n===`_`)return;let r=t();for(let e of r.locales)if(e.code===n)return;throw new e(n,r.locales.map(e=>e.code))}export{n as t};
2
- //# sourceMappingURL=locale-utils-By7EdAT1.mjs.map
1
+ import{n as e}from"./errors-kkf8Ue1H.mjs";import{n as t}from"./plugin-DkmXJ7Qw.mjs";function n(n){if(n===`_`)return;let r=t();for(let e of r.locales)if(e.code===n)return;throw new e(n,r.locales.map(e=>e.code))}export{n as t};
2
+ //# sourceMappingURL=locale-utils-BMRVJwvU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"locale-utils-By7EdAT1.mjs","names":[],"sources":["../src/locale-utils.ts"],"sourcesContent":["/**\n * Locale validation helpers.\n *\n * Every ContentClient and LockService method that accepts a `locale`\n * parameter must run it through `validateLocale()` before passing it to\n * the database. Drizzle parameterizes queries so SQL injection isn't a\n * concern, but unvalidated locale strings:\n * - bypass the configured locale set (typo'd `'frr'` writes a row that\n * never resolves on read),\n * - silently truncate at the column's `varchar(10)` boundary (e.g.\n * `'pt-BR-traditional'` → `'pt-BR-trad'`, breaking later equality),\n * - pollute drafts/versions/locale_status tables with rows that no\n * UI ever queries.\n *\n * The `'_'` sentinel is allowed because it's the conventional value for\n * default-locale rows in versions/drafts (where every row needs a\n * non-null `locale` for unique-constraint composition).\n */\n\nimport { InvalidLocaleError } from './errors.js'\nimport { getContentConfig } from './plugin.js'\n\n/** The internal sentinel for \"default locale\" used in version/draft storage. */\nexport const DEFAULT_LOCALE_SENTINEL = '_'\n\n/**\n * Throw `InvalidLocaleError` if `locale` is neither the `'_'` sentinel nor\n * one of the configured plugin locale codes.\n */\nexport function validateLocale(locale: string): void {\n if (locale === DEFAULT_LOCALE_SENTINEL) return\n const config = getContentConfig()\n for (const l of config.locales) {\n if (l.code === locale) return\n }\n throw new InvalidLocaleError(\n locale,\n config.locales.map((l) => l.code),\n )\n}\n"],"mappings":"oFA6BA,SAAgB,EAAe,EAAsB,CACnD,GAAI,IAAA,IAAoC,OACxC,IAAM,EAAS,GAAkB,CACjC,IAAK,IAAM,KAAK,EAAO,QACrB,GAAI,EAAE,OAAS,EAAQ,OAEzB,MAAM,IAAI,EACR,EACA,EAAO,QAAQ,IAAK,GAAM,EAAE,KAAK,CAClC"}
1
+ {"version":3,"file":"locale-utils-BMRVJwvU.mjs","names":[],"sources":["../src/locale-utils.ts"],"sourcesContent":["/**\n * Locale validation helpers.\n *\n * Every ContentClient and LockService method that accepts a `locale`\n * parameter must run it through `validateLocale()` before passing it to\n * the database. Drizzle parameterizes queries so SQL injection isn't a\n * concern, but unvalidated locale strings:\n * - bypass the configured locale set (typo'd `'frr'` writes a row that\n * never resolves on read),\n * - silently truncate at the column's `varchar(10)` boundary (e.g.\n * `'pt-BR-traditional'` → `'pt-BR-trad'`, breaking later equality),\n * - pollute drafts/versions/locale_status tables with rows that no\n * UI ever queries.\n *\n * The `'_'` sentinel is allowed because it's the conventional value for\n * default-locale rows in versions/drafts (where every row needs a\n * non-null `locale` for unique-constraint composition).\n */\n\nimport { InvalidLocaleError } from './errors.js'\nimport { getContentConfig } from './plugin.js'\n\n/** The internal sentinel for \"default locale\" used in version/draft storage. */\nexport const DEFAULT_LOCALE_SENTINEL = '_'\n\n/**\n * Throw `InvalidLocaleError` if `locale` is neither the `'_'` sentinel nor\n * one of the configured plugin locale codes.\n */\nexport function validateLocale(locale: string): void {\n if (locale === DEFAULT_LOCALE_SENTINEL) return\n const config = getContentConfig()\n for (const l of config.locales) {\n if (l.code === locale) return\n }\n throw new InvalidLocaleError(\n locale,\n config.locales.map((l) => l.code),\n )\n}\n"],"mappings":"oFA6BA,SAAgB,EAAe,EAAsB,CACnD,GAAI,IAAA,IAAoC,OACxC,IAAM,EAAS,GAAkB,CACjC,IAAK,IAAM,KAAK,EAAO,QACrB,GAAI,EAAE,OAAS,EAAQ,OAEzB,MAAM,IAAI,EACR,EACA,EAAO,QAAQ,IAAK,GAAM,EAAE,KAAK,CAClC"}
@@ -1,9 +1,9 @@
1
1
  import { c as LockInfo } from "./types-BCmX1nBL.mjs";
2
- import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import { ToolkitApp } from "@murumets-ee/core";
3
3
 
4
4
  //#region src/lock-service.d.ts
5
5
  interface LockServiceConfig {
6
- db: PostgresJsDatabase;
6
+ app: ToolkitApp;
7
7
  }
8
8
  declare class LockService {
9
9
  private client;
@@ -1 +1 @@
1
- {"version":3,"file":"lock-service.d.mts","names":[],"sources":["../src/lock-service.ts"],"mappings":";;;;UAqBiB,iBAAA;EACf,EAAA,EAAI,kBAAA;AAAA;AAAA,cAGO,WAAA;EAAA,QACH,MAAA;cAEI,MAAA,EAAQ,iBAAA;EAqG0D;;;;;;;;;;;;;;;;;EAhFxE,WAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,IAAA;IAAQ,EAAA;IAAY,IAAA;EAAA,IACnB,OAAA;IAAU,QAAA;EAAA;IAAqB,QAAA;IAAiB,IAAA,EAAM,QAAA;EAAA;EA8CtD;;;;EALG,SAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,MAAA,WACC,OAAA;EAmBD;;;EAJI,WAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,MAAA,WACC,OAAA;EASwC;;;;EAArC,gBAAA,CAAiB,UAAA,UAAoB,QAAA,UAAkB,MAAA,WAAiB,OAAA;EAS5C;;;;EAA5B,OAAA,CAAQ,UAAA,UAAoB,QAAA,UAAkB,MAAA,WAAiB,OAAA,CAAQ,QAAA;AAAA"}
1
+ {"version":3,"file":"lock-service.d.mts","names":[],"sources":["../src/lock-service.ts"],"mappings":";;;;UAqBiB,iBAAA;EACf,GAAA,EAAK,UAAA;AAAA;AAAA,cAGM,WAAA;EAAA,QACH,MAAA;cAEI,MAAA,EAAQ,iBAAA;EAqG0D;;;;;;;;;;;;;;;;;EAhFxE,WAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,IAAA;IAAQ,EAAA;IAAY,IAAA;EAAA,IACnB,OAAA;IAAU,QAAA;EAAA;IAAqB,QAAA;IAAiB,IAAA,EAAM,QAAA;EAAA;EA8CtD;;;;EALG,SAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,MAAA,WACC,OAAA;EAmBD;;;EAJI,WAAA,CACJ,UAAA,UACA,QAAA,UACA,MAAA,UACA,MAAA,WACC,OAAA;EASwC;;;;EAArC,gBAAA,CAAiB,UAAA,UAAoB,QAAA,UAAkB,MAAA,WAAiB,OAAA;EAS5C;;;;EAA5B,OAAA,CAAQ,UAAA,UAAoB,QAAA,UAAkB,MAAA,WAAiB,OAAA,CAAQ,QAAA;AAAA"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"./locks-BRtmYqgS.mjs";import"server-only";var t=class{client;constructor(t){this.client=e.makeClient(t.db)}async acquireLock(e,t,n,r){let i=new Date,a=new Date(i.getTime()+300*1e3),o=await this.client.tryClaim({entityType:e,entityId:t,locale:n,lockedBy:r.id,lockedByName:r.name??null,lockedAt:i,expiresAt:a},{target:[`entityType`,`entityId`,`locale`],replaceWhen:{$or:[{expiresAt:{lt:i}},{lockedBy:r.id}]}});return o.acquired?{acquired:!0}:{acquired:!1,lock:{lockedBy:o.row.lockedBy,lockedByName:o.row.lockedByName??null,lockedAt:o.row.lockedAt,expiresAt:o.row.expiresAt}}}async renewLock(e,t,n,r){let i=new Date,a=new Date(i.getTime()+300*1e3);return await this.client.update({entityType:e,entityId:t,locale:n,lockedBy:r,expiresAt:{gte:i}},{expiresAt:a})!==null}async releaseLock(e,t,n,r){return await this.client.delete({entityType:e,entityId:t,locale:n,lockedBy:r})!==null}async forceReleaseLock(e,t,n){return await this.client.delete({entityType:e,entityId:t,locale:n})!==null}async getLock(e,t,n){let r=new Date,i=await this.client.findOne({entityType:e,entityId:t,locale:n,expiresAt:{gte:r}});return i?{lockedBy:i.lockedBy,lockedByName:i.lockedByName??null,lockedAt:i.lockedAt,expiresAt:i.expiresAt}:null}};export{t as LockService};
1
+ import{t as e}from"./locks-BRtmYqgS.mjs";import"server-only";var t=class{client;constructor(t){this.client=e.makeClient(t.app.db.readWrite)}async acquireLock(e,t,n,r){let i=new Date,a=new Date(i.getTime()+300*1e3),o=await this.client.tryClaim({entityType:e,entityId:t,locale:n,lockedBy:r.id,lockedByName:r.name??null,lockedAt:i,expiresAt:a},{target:[`entityType`,`entityId`,`locale`],replaceWhen:{$or:[{expiresAt:{lt:i}},{lockedBy:r.id}]}});return o.acquired?{acquired:!0}:{acquired:!1,lock:{lockedBy:o.row.lockedBy,lockedByName:o.row.lockedByName??null,lockedAt:o.row.lockedAt,expiresAt:o.row.expiresAt}}}async renewLock(e,t,n,r){let i=new Date,a=new Date(i.getTime()+300*1e3);return await this.client.update({entityType:e,entityId:t,locale:n,lockedBy:r,expiresAt:{gte:i}},{expiresAt:a})!==null}async releaseLock(e,t,n,r){return await this.client.delete({entityType:e,entityId:t,locale:n,lockedBy:r})!==null}async forceReleaseLock(e,t,n){return await this.client.delete({entityType:e,entityId:t,locale:n})!==null}async getLock(e,t,n){let r=new Date,i=await this.client.findOne({entityType:e,entityId:t,locale:n,expiresAt:{gte:r}});return i?{lockedBy:i.lockedBy,lockedByName:i.lockedByName??null,lockedAt:i.lockedAt,expiresAt:i.expiresAt}:null}};export{t as LockService};
2
2
  //# sourceMappingURL=lock-service.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"lock-service.mjs","names":[],"sources":["../src/lock-service.ts"],"sourcesContent":["/**\n * Content lock service — pessimistic locking for entity editing.\n *\n * Prevents concurrent edit conflicts by allowing only one editor at a time\n * per entity+locale combination. Locks expire after 5 minutes and must\n * be renewed via heartbeat.\n *\n * Uses TableClient for all DB operations — zero direct Drizzle queries.\n */\n\nimport 'server-only'\n\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { contentLocksTable } from './schema/locks.js'\nimport type { LockInfo } from './types.js'\n\n/** Lock duration in minutes */\nconst LOCK_DURATION_MINUTES = 5\n\ntype LockClient = ReturnType<typeof contentLocksTable.makeClient>\n\nexport interface LockServiceConfig {\n db: PostgresJsDatabase\n}\n\nexport class LockService {\n private client: LockClient\n\n constructor(config: LockServiceConfig) {\n this.client = contentLocksTable.makeClient(config.db)\n }\n\n /**\n * Acquire a lock on an entity+locale, atomically.\n *\n * Implemented as a single `INSERT … ON CONFLICT DO UPDATE … WITH SET WHERE`\n * via {@link TableClient.tryClaim} — no `deleteMany → findOne → insert`\n * race. Two concurrent acquires of the same free slot produce exactly\n * one `acquired: true` and one `acquired: false`; the loser sees the\n * winner's row.\n *\n * Replace semantics: take the slot if **any** of these is true:\n * - the slot is empty (INSERT path),\n * - the existing row is past its expiry (`expiresAt < now`),\n * - the existing row is held by the same user (re-entrant renew).\n *\n * Otherwise — fresh lock by another user — return `{ acquired: false,\n * lock }` so the caller can render \"X is editing\".\n */\n async acquireLock(\n entityType: string,\n entityId: string,\n locale: string,\n user: { id: string; name?: string },\n ): Promise<{ acquired: true } | { acquired: false; lock: LockInfo }> {\n const now = new Date()\n const expiresAt = new Date(now.getTime() + LOCK_DURATION_MINUTES * 60 * 1000)\n\n const result = await this.client.tryClaim(\n {\n entityType,\n entityId,\n locale,\n lockedBy: user.id,\n lockedByName: user.name ?? null,\n lockedAt: now,\n expiresAt,\n },\n {\n target: ['entityType', 'entityId', 'locale'],\n // Replace if expired OR if I already hold this slot. Using $or\n // lets re-entrancy and stale-takeover share one atomic statement.\n replaceWhen: {\n $or: [{ expiresAt: { lt: now } }, { lockedBy: user.id }],\n },\n },\n )\n\n if (result.acquired) return { acquired: true }\n\n return {\n acquired: false,\n lock: {\n lockedBy: result.row.lockedBy,\n lockedByName: result.row.lockedByName ?? null,\n lockedAt: result.row.lockedAt,\n expiresAt: result.row.expiresAt,\n },\n }\n }\n\n /**\n * Renew an existing lock (heartbeat).\n * Only succeeds if the caller holds the lock and it hasn't expired.\n */\n async renewLock(\n entityType: string,\n entityId: string,\n locale: string,\n userId: string,\n ): Promise<boolean> {\n const now = new Date()\n const expiresAt = new Date(now.getTime() + LOCK_DURATION_MINUTES * 60 * 1000)\n\n const updated = await this.client.update(\n { entityType, entityId, locale, lockedBy: userId, expiresAt: { gte: now } },\n { expiresAt },\n )\n\n return updated !== null\n }\n\n /**\n * Release a lock. Only the lock holder can release their own lock.\n */\n async releaseLock(\n entityType: string,\n entityId: string,\n locale: string,\n userId: string,\n ): Promise<boolean> {\n const deleted = await this.client.delete({ entityType, entityId, locale, lockedBy: userId })\n return deleted !== null\n }\n\n /**\n * Force-release a lock regardless of who holds it.\n * Admin-only operation — must be audit-logged by the caller.\n */\n async forceReleaseLock(entityType: string, entityId: string, locale: string): Promise<boolean> {\n const deleted = await this.client.delete({ entityType, entityId, locale })\n return deleted !== null\n }\n\n /**\n * Get the current lock for an entity+locale.\n * Returns null if no lock or lock has expired.\n */\n async getLock(entityType: string, entityId: string, locale: string): Promise<LockInfo | null> {\n const now = new Date()\n\n const row = await this.client.findOne({\n entityType,\n entityId,\n locale,\n expiresAt: { gte: now },\n })\n\n if (!row) return null\n\n return {\n lockedBy: row.lockedBy,\n lockedByName: row.lockedByName ?? null,\n lockedAt: row.lockedAt as Date,\n expiresAt: row.expiresAt as Date,\n }\n }\n}\n"],"mappings":"6DAyBA,IAAa,EAAb,KAAyB,CACvB,OAEA,YAAY,EAA2B,CACrC,KAAK,OAAS,EAAkB,WAAW,EAAO,GAAG,CAoBvD,MAAM,YACJ,EACA,EACA,EACA,EACmE,CACnE,IAAM,EAAM,IAAI,KACV,EAAY,IAAI,KAAK,EAAI,SAAS,CAAG,IAA6B,IAAK,CAEvE,EAAS,MAAM,KAAK,OAAO,SAC/B,CACE,aACA,WACA,SACA,SAAU,EAAK,GACf,aAAc,EAAK,MAAQ,KAC3B,SAAU,EACV,YACD,CACD,CACE,OAAQ,CAAC,aAAc,WAAY,SAAS,CAG5C,YAAa,CACX,IAAK,CAAC,CAAE,UAAW,CAAE,GAAI,EAAK,CAAE,CAAE,CAAE,SAAU,EAAK,GAAI,CAAC,CACzD,CACF,CACF,CAID,OAFI,EAAO,SAAiB,CAAE,SAAU,GAAM,CAEvC,CACL,SAAU,GACV,KAAM,CACJ,SAAU,EAAO,IAAI,SACrB,aAAc,EAAO,IAAI,cAAgB,KACzC,SAAU,EAAO,IAAI,SACrB,UAAW,EAAO,IAAI,UACvB,CACF,CAOH,MAAM,UACJ,EACA,EACA,EACA,EACkB,CAClB,IAAM,EAAM,IAAI,KACV,EAAY,IAAI,KAAK,EAAI,SAAS,CAAG,IAA6B,IAAK,CAO7E,OAAO,MALe,KAAK,OAAO,OAChC,CAAE,aAAY,WAAU,SAAQ,SAAU,EAAQ,UAAW,CAAE,IAAK,EAAK,CAAE,CAC3E,CAAE,YAAW,CACd,GAEkB,KAMrB,MAAM,YACJ,EACA,EACA,EACA,EACkB,CAElB,OAAO,MADe,KAAK,OAAO,OAAO,CAAE,aAAY,WAAU,SAAQ,SAAU,EAAQ,CAAC,GACzE,KAOrB,MAAM,iBAAiB,EAAoB,EAAkB,EAAkC,CAE7F,OAAO,MADe,KAAK,OAAO,OAAO,CAAE,aAAY,WAAU,SAAQ,CAAC,GACvD,KAOrB,MAAM,QAAQ,EAAoB,EAAkB,EAA0C,CAC5F,IAAM,EAAM,IAAI,KAEV,EAAM,MAAM,KAAK,OAAO,QAAQ,CACpC,aACA,WACA,SACA,UAAW,CAAE,IAAK,EAAK,CACxB,CAAC,CAIF,OAFK,EAEE,CACL,SAAU,EAAI,SACd,aAAc,EAAI,cAAgB,KAClC,SAAU,EAAI,SACd,UAAW,EAAI,UAChB,CAPgB"}
1
+ {"version":3,"file":"lock-service.mjs","names":[],"sources":["../src/lock-service.ts"],"sourcesContent":["/**\n * Content lock service — pessimistic locking for entity editing.\n *\n * Prevents concurrent edit conflicts by allowing only one editor at a time\n * per entity+locale combination. Locks expire after 5 minutes and must\n * be renewed via heartbeat.\n *\n * Uses TableClient for all DB operations — zero direct Drizzle queries.\n */\n\nimport 'server-only'\n\nimport type { ToolkitApp } from '@murumets-ee/core'\nimport { contentLocksTable } from './schema/locks.js'\nimport type { LockInfo } from './types.js'\n\n/** Lock duration in minutes */\nconst LOCK_DURATION_MINUTES = 5\n\ntype LockClient = ReturnType<typeof contentLocksTable.makeClient>\n\nexport interface LockServiceConfig {\n app: ToolkitApp\n}\n\nexport class LockService {\n private client: LockClient\n\n constructor(config: LockServiceConfig) {\n this.client = contentLocksTable.makeClient(config.app.db.readWrite)\n }\n\n /**\n * Acquire a lock on an entity+locale, atomically.\n *\n * Implemented as a single `INSERT … ON CONFLICT DO UPDATE … WITH SET WHERE`\n * via {@link TableClient.tryClaim} — no `deleteMany → findOne → insert`\n * race. Two concurrent acquires of the same free slot produce exactly\n * one `acquired: true` and one `acquired: false`; the loser sees the\n * winner's row.\n *\n * Replace semantics: take the slot if **any** of these is true:\n * - the slot is empty (INSERT path),\n * - the existing row is past its expiry (`expiresAt < now`),\n * - the existing row is held by the same user (re-entrant renew).\n *\n * Otherwise — fresh lock by another user — return `{ acquired: false,\n * lock }` so the caller can render \"X is editing\".\n */\n async acquireLock(\n entityType: string,\n entityId: string,\n locale: string,\n user: { id: string; name?: string },\n ): Promise<{ acquired: true } | { acquired: false; lock: LockInfo }> {\n const now = new Date()\n const expiresAt = new Date(now.getTime() + LOCK_DURATION_MINUTES * 60 * 1000)\n\n const result = await this.client.tryClaim(\n {\n entityType,\n entityId,\n locale,\n lockedBy: user.id,\n lockedByName: user.name ?? null,\n lockedAt: now,\n expiresAt,\n },\n {\n target: ['entityType', 'entityId', 'locale'],\n // Replace if expired OR if I already hold this slot. Using $or\n // lets re-entrancy and stale-takeover share one atomic statement.\n replaceWhen: {\n $or: [{ expiresAt: { lt: now } }, { lockedBy: user.id }],\n },\n },\n )\n\n if (result.acquired) return { acquired: true }\n\n return {\n acquired: false,\n lock: {\n lockedBy: result.row.lockedBy,\n lockedByName: result.row.lockedByName ?? null,\n lockedAt: result.row.lockedAt,\n expiresAt: result.row.expiresAt,\n },\n }\n }\n\n /**\n * Renew an existing lock (heartbeat).\n * Only succeeds if the caller holds the lock and it hasn't expired.\n */\n async renewLock(\n entityType: string,\n entityId: string,\n locale: string,\n userId: string,\n ): Promise<boolean> {\n const now = new Date()\n const expiresAt = new Date(now.getTime() + LOCK_DURATION_MINUTES * 60 * 1000)\n\n const updated = await this.client.update(\n { entityType, entityId, locale, lockedBy: userId, expiresAt: { gte: now } },\n { expiresAt },\n )\n\n return updated !== null\n }\n\n /**\n * Release a lock. Only the lock holder can release their own lock.\n */\n async releaseLock(\n entityType: string,\n entityId: string,\n locale: string,\n userId: string,\n ): Promise<boolean> {\n const deleted = await this.client.delete({ entityType, entityId, locale, lockedBy: userId })\n return deleted !== null\n }\n\n /**\n * Force-release a lock regardless of who holds it.\n * Admin-only operation — must be audit-logged by the caller.\n */\n async forceReleaseLock(entityType: string, entityId: string, locale: string): Promise<boolean> {\n const deleted = await this.client.delete({ entityType, entityId, locale })\n return deleted !== null\n }\n\n /**\n * Get the current lock for an entity+locale.\n * Returns null if no lock or lock has expired.\n */\n async getLock(entityType: string, entityId: string, locale: string): Promise<LockInfo | null> {\n const now = new Date()\n\n const row = await this.client.findOne({\n entityType,\n entityId,\n locale,\n expiresAt: { gte: now },\n })\n\n if (!row) return null\n\n return {\n lockedBy: row.lockedBy,\n lockedByName: row.lockedByName ?? null,\n lockedAt: row.lockedAt as Date,\n expiresAt: row.expiresAt as Date,\n }\n }\n}\n"],"mappings":"6DAyBA,IAAa,EAAb,KAAyB,CACvB,OAEA,YAAY,EAA2B,CACrC,KAAK,OAAS,EAAkB,WAAW,EAAO,IAAI,GAAG,UAAU,CAoBrE,MAAM,YACJ,EACA,EACA,EACA,EACmE,CACnE,IAAM,EAAM,IAAI,KACV,EAAY,IAAI,KAAK,EAAI,SAAS,CAAG,IAA6B,IAAK,CAEvE,EAAS,MAAM,KAAK,OAAO,SAC/B,CACE,aACA,WACA,SACA,SAAU,EAAK,GACf,aAAc,EAAK,MAAQ,KAC3B,SAAU,EACV,YACD,CACD,CACE,OAAQ,CAAC,aAAc,WAAY,SAAS,CAG5C,YAAa,CACX,IAAK,CAAC,CAAE,UAAW,CAAE,GAAI,EAAK,CAAE,CAAE,CAAE,SAAU,EAAK,GAAI,CAAC,CACzD,CACF,CACF,CAID,OAFI,EAAO,SAAiB,CAAE,SAAU,GAAM,CAEvC,CACL,SAAU,GACV,KAAM,CACJ,SAAU,EAAO,IAAI,SACrB,aAAc,EAAO,IAAI,cAAgB,KACzC,SAAU,EAAO,IAAI,SACrB,UAAW,EAAO,IAAI,UACvB,CACF,CAOH,MAAM,UACJ,EACA,EACA,EACA,EACkB,CAClB,IAAM,EAAM,IAAI,KACV,EAAY,IAAI,KAAK,EAAI,SAAS,CAAG,IAA6B,IAAK,CAO7E,OAAO,MALe,KAAK,OAAO,OAChC,CAAE,aAAY,WAAU,SAAQ,SAAU,EAAQ,UAAW,CAAE,IAAK,EAAK,CAAE,CAC3E,CAAE,YAAW,CACd,GAEkB,KAMrB,MAAM,YACJ,EACA,EACA,EACA,EACkB,CAElB,OAAO,MADe,KAAK,OAAO,OAAO,CAAE,aAAY,WAAU,SAAQ,SAAU,EAAQ,CAAC,GACzE,KAOrB,MAAM,iBAAiB,EAAoB,EAAkB,EAAkC,CAE7F,OAAO,MADe,KAAK,OAAO,OAAO,CAAE,aAAY,WAAU,SAAQ,CAAC,GACvD,KAOrB,MAAM,QAAQ,EAAoB,EAAkB,EAA0C,CAC5F,IAAM,EAAM,IAAI,KAEV,EAAM,MAAM,KAAK,OAAO,QAAQ,CACpC,aACA,WACA,SACA,UAAW,CAAE,IAAK,EAAK,CACxB,CAAC,CAIF,OAFK,EAEE,CACL,SAAU,EAAI,SACd,aAAc,EAAI,cAAgB,KAClC,SAAU,EAAI,SACd,UAAW,EAAI,UAChB,CAPgB"}
@@ -0,0 +1,2 @@
1
+ import{n as e}from"./locks-BRtmYqgS.mjs";import{generateLayoutSchema as t,generateLayoutTranslationSchema as n}from"@murumets-ee/entity";import{definePlugin as r}from"@murumets-ee/core";import{column as i,defineTable as a,schemaRegistry as o}from"@murumets-ee/db";const s=new Map;function c(e,t){let n=s.get(e)??{};s.set(e,{...n,...t})}function l(e){return s.get(e)??{}}function u(e){return a({name:`${e.name}_drafts`,columns:{id:i.uuid({primaryKey:!0,defaultRandom:!0}),entityId:i.uuid({notNull:!0,pgName:`entity_id`}),locale:i.varchar({length:10,notNull:!0,default:`_`}),data:i.jsonb({notNull:!0}),createdBy:i.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:i.varchar({length:255,pgName:`created_by_name`}),createdAt:i.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:i.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function d(e){return a({name:`${e.name}_locale_status`,columns:{id:i.uuid({primaryKey:!0,defaultRandom:!0}),entityId:i.uuid({notNull:!0,pgName:`entity_id`}),locale:i.varchar({length:10,notNull:!0}),status:i.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:i.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function f(e){return a({name:`${e.name}_versions`,columns:{id:i.uuid({primaryKey:!0,defaultRandom:!0}),entityId:i.uuid({notNull:!0,pgName:`entity_id`}),version:i.integer({notNull:!0}),locale:i.varchar({length:10,notNull:!0,default:`_`}),data:i.jsonb({notNull:!0}),delta:i.jsonb(),status:i.varchar({length:20}),createdBy:i.varchar({length:255,pgName:`created_by`}),createdByName:i.varchar({length:255,pgName:`created_by_name`}),createdAt:i.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:i.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}let p=null;function m(){if(!p)throw Error(`@murumets-ee/content plugin not initialized. Add content() to your plugins array.`);return p}function h(i){if(!i.locales||i.locales.length===0)throw Error(`content() requires at least one locale`);if(!i.defaultLocale)throw Error(`content() requires a defaultLocale`);if(!i.locales.some(e=>e.code===i.defaultLocale))throw Error(`defaultLocale "${i.defaultLocale}" not found in locales: [${i.locales.map(e=>e.code).join(`, `)}]`);return r({name:`@murumets-ee/content`,server:{init:async r=>{p=i;let a=0,s=0,l=0,m=0;o.has(`toolkit_content_locks`)||o.register(`toolkit_content_locks`,e);for(let[e,i]of r.entities){if(i.behaviors?.some(e=>e.name===`versionable`)){let t=`${e}_versions`;if(!o.has(t)){let n=f(i);o.register(t,n.table),c(e,{versions:n})}a++}let r=i.allFields;if(Object.values(r).some(e=>e.type===`blocks`)){let r=`${e}_layout`;if(!o.has(r)){let e=t(i);e&&o.register(r,e)}let a=`${e}_layout_translations`;if(!o.has(a)){let e=n(i);e&&o.register(a,e)}s++}let p=i.behaviors?.some(e=>e.name===`publishable`),h=Object.values(r).some(e=>e.translatable);if(p&&h){let t=`${e}_locale_status`;if(!o.has(t)){let n=d(i);o.register(t,n.table),c(e,{localeStatus:n})}l++}if(p){let t=`${e}_drafts`;if(!o.has(t)){let n=u(i);o.register(t,n.table),c(e,{drafts:n})}m++}}r.logger.info({locales:i.locales.map(e=>e.code),defaultLocale:i.defaultLocale,versionableEntities:a,blockEntities:s,localeStatusEntities:l,draftEntities:m},`Content plugin initialized`)}}})}export{m as n,l as r,h as t};
2
+ //# sourceMappingURL=plugin-DkmXJ7Qw.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-yijaaFS7.mjs","names":[],"sources":["../src/schema/registry.ts","../src/schema/drafts.ts","../src/schema/locale-status.ts","../src/schema/versions.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Per-entity content table registry.\n *\n * Maps an entity name to its content infrastructure tables — drafts,\n * versions, locale_status. Each entry stores the `DefinedTable` (from\n * `defineTable`), which exposes a typed `.makeClient(db)` for CRUD via\n * `TableClient` and a raw `.table` for legacy callers.\n *\n * The content plugin populates this registry at `init` time; ContentClient\n * reads from it lazily on first use. Tables that don't apply to an entity\n * (non-publishable entities have no drafts, etc.) are simply absent.\n */\n\nimport type { DraftsTable } from './drafts.js'\nimport type { LocaleStatusTable } from './locale-status.js'\nimport type { VersionsTable } from './versions.js'\n\n/**\n * The full set of optional content infra tables attached to an entity.\n * Every field is optional — entities without the relevant behavior don't\n * get the corresponding table.\n */\nexport interface ContentTables {\n /** `{entity}_drafts` — present when the entity has `publishable()`. */\n drafts?: DraftsTable\n /** `{entity}_versions` — present when the entity has `versionable()`. */\n versions?: VersionsTable\n /** `{entity}_locale_status` — present when the entity is publishable + has translatable fields. */\n localeStatus?: LocaleStatusTable\n}\n\n// Module-level map. Single source of truth for ContentClient lookups.\nconst tables = new Map<string, ContentTables>()\n\n/**\n * Register content tables for an entity. Called from `content().init()`.\n *\n * Subsequent calls for the same entity name MERGE the provided patch into\n * the existing entry. This lets the plugin add tables one at a time (drafts,\n * then versions, then locale_status) without the caller needing to build the\n * whole object up front.\n */\nexport function registerContentTables(entityName: string, patch: ContentTables): void {\n const existing = tables.get(entityName) ?? {}\n tables.set(entityName, { ...existing, ...patch })\n}\n\n/**\n * Get the content tables for an entity. Returns an empty object (rather\n * than undefined) when the entity has no registered tables — that\n * simplifies the common ContentClient lookup idiom:\n *\n * const { drafts } = getContentTables(entity.name)\n * if (!drafts) return null\n */\nexport function getContentTables(entityName: string): ContentTables {\n return tables.get(entityName) ?? {}\n}\n\n/**\n * Clear the registry. **Test-only.** Throws in production.\n */\nexport function clearContentTables(): void {\n if (process.env.NODE_ENV === 'production') {\n throw new Error(\n 'clearContentTables() is not allowed in production — this is a test-only utility.',\n )\n }\n tables.clear()\n}\n","/**\n * Draft overlay table schema generator\n *\n * Generates a {entity}_drafts table for entities with the publishable() behavior.\n * Stores a mutable work-in-progress snapshot that non-publishers edit instead of\n * writing directly to the live published row.\n *\n * One draft per entity+locale. Drafts are upserted, discarded, or promoted to live.\n *\n * Uses `defineTable` so ContentClient can operate on drafts through a typed\n * TableClient (see registry.ts). The migration pipeline uses the\n * entity-package generator (which emits FK references) — this one produces a\n * runtime-only table shape for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a publishable entity's drafts table.\n *\n * Schema:\n * {entity}_drafts (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * locale varchar(10) NOT NULL DEFAULT '_',\n * data jsonb NOT NULL, -- full entity snapshot\n * created_by varchar(255) NOT NULL, -- user ID (varchar, NOT uuid!)\n * created_by_name varchar(255),\n * created_at timestamptz NOT NULL DEFAULT now(),\n * updated_at timestamptz NOT NULL DEFAULT now(),\n * UNIQUE(entity_id, locale)\n * )\n */\nexport function generateDraftsTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_drafts`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n locale: column.varchar({ length: 10, notNull: true, default: '_' }),\n data: column.jsonb<Record<string, unknown>, true>({ notNull: true }),\n createdBy: column.varchar({ length: 255, notNull: true, pgName: 'created_by' }),\n createdByName: column.varchar({ length: 255, pgName: 'created_by_name' }),\n createdAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'created_at',\n }),\n updatedAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'updated_at',\n }),\n },\n unique: [{ on: ['entityId', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type DraftsTable = ReturnType<typeof generateDraftsTable>\n","/**\n * Per-locale publish status table schema generator\n *\n * Generates a {entity}_locale_status table for entities that have both\n * publishable() behavior AND translatable fields. This allows different\n * locales to have independent publish statuses.\n *\n * Fallback rule: no row for a locale = inherit base entity status.\n *\n * Uses `defineTable` so ContentClient can operate on rows through a typed\n * TableClient. The migration pipeline uses the entity-package generator\n * (which emits FK references); this one produces a runtime-only table shape\n * for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a publishable + translatable entity's\n * locale status table.\n *\n * Schema:\n * {entity}_locale_status (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * locale varchar(10) NOT NULL,\n * status varchar(20) NOT NULL DEFAULT 'draft',\n * published_at timestamptz,\n * UNIQUE(entity_id, locale)\n * )\n */\nexport function generateLocaleStatusTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_locale_status`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n locale: column.varchar({ length: 10, notNull: true }),\n status: column.varchar({ length: 20, notNull: true, default: 'draft' }),\n publishedAt: column.timestamp({ withTimezone: true, pgName: 'published_at' }),\n },\n unique: [{ on: ['entityId', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type LocaleStatusTable = ReturnType<typeof generateLocaleStatusTable>\n","/**\n * Version history table schema generator\n *\n * Generates a {entity}_versions table for entities with the versionable() behavior.\n * Each version stores a full snapshot + delta (changed fields only).\n *\n * Uses `defineTable` so ContentClient can operate on versions through a typed\n * TableClient. The migration pipeline uses the entity-package generator\n * (which emits FK references) — this one produces a runtime-only table shape\n * for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a versionable entity's versions table.\n *\n * Schema:\n * {entity}_versions (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * version integer NOT NULL,\n * locale varchar(10) NOT NULL DEFAULT '_', -- locale this snapshot belongs to\n * data jsonb NOT NULL, -- full snapshot (locale-merged view)\n * delta jsonb, -- only changed fields\n * status varchar(20), -- 'draft' | 'published' at time of save\n * created_by varchar(255),\n * created_by_name varchar(255),\n * created_at timestamptz NOT NULL DEFAULT now(),\n * is_autosave boolean NOT NULL DEFAULT false,\n * UNIQUE(entity_id, version, locale)\n * )\n *\n * Each locale has its own version sequence. The default locale uses '_'\n * as the locale value (distinguishable from actual locale codes).\n */\nexport function generateVersionsTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_versions`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n version: column.integer({ notNull: true }),\n locale: column.varchar({ length: 10, notNull: true, default: '_' }),\n data: column.jsonb<Record<string, unknown>, true>({ notNull: true }),\n delta: column.jsonb<Record<string, unknown>>(),\n status: column.varchar({ length: 20 }),\n createdBy: column.varchar({ length: 255, pgName: 'created_by' }),\n createdByName: column.varchar({ length: 255, pgName: 'created_by_name' }),\n createdAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'created_at',\n }),\n isAutosave: column.boolean({ notNull: true, default: false, pgName: 'is_autosave' }),\n },\n unique: [{ on: ['entityId', 'version', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type VersionsTable = ReturnType<typeof generateVersionsTable>\n","/**\n * Content plugin — registers locale configuration and version schemas.\n *\n * Usage:\n * import { content } from '@murumets-ee/content/plugin'\n *\n * export default defineLumiConfig({\n * plugins: [\n * content({\n * locales: [\n * { code: 'en', label: 'English' },\n * { code: 'de', label: 'German' },\n * ],\n * defaultLocale: 'en',\n * }),\n * ],\n * })\n */\n\nimport type { Plugin } from '@murumets-ee/core'\nimport { schemaRegistry } from '@murumets-ee/db'\nimport { generateLayoutSchema, generateLayoutTranslationSchema } from '@murumets-ee/entity'\nimport { generateDraftsTable } from './schema/drafts.js'\nimport { generateLocaleStatusTable } from './schema/locale-status.js'\nimport { toolkitContentLocks } from './schema/locks.js'\nimport { registerContentTables } from './schema/registry.js'\nimport { generateVersionsTable } from './schema/versions.js'\nimport type { ContentConfig } from './types.js'\n\nlet _contentConfig: ContentConfig | null = null\n\n/**\n * Get the content configuration. Throws if plugin not initialized.\n */\nexport function getContentConfig(): ContentConfig {\n if (!_contentConfig) {\n throw new Error(\n '@murumets-ee/content plugin not initialized. Add content() to your plugins array.',\n )\n }\n return _contentConfig\n}\n\n/**\n * Content plugin factory.\n */\nexport function content(config: ContentConfig): Plugin {\n // Validate config\n if (!config.locales || config.locales.length === 0) {\n throw new Error('content() requires at least one locale')\n }\n\n if (!config.defaultLocale) {\n throw new Error('content() requires a defaultLocale')\n }\n\n const defaultExists = config.locales.some((l) => l.code === config.defaultLocale)\n if (!defaultExists) {\n throw new Error(\n `defaultLocale \"${config.defaultLocale}\" not found in locales: [${config.locales.map((l) => l.code).join(', ')}]`,\n )\n }\n\n return {\n name: '@murumets-ee/content',\n server: {\n init: async (app) => {\n // Store config globally\n _contentConfig = config\n\n let versionableCount = 0\n let blocksCount = 0\n let localeStatusCount = 0\n let draftsCount = 0\n\n // Register shared locks table (once, outside entity loop)\n if (!schemaRegistry.has('toolkit_content_locks')) {\n schemaRegistry.register('toolkit_content_locks', toolkitContentLocks)\n }\n\n for (const [name, entity] of app.entities) {\n // Register version tables for versionable entities\n const hasVersionable = entity.behaviors?.some(\n (b: { name: string }) => b.name === 'versionable',\n )\n if (hasVersionable) {\n const versionsTableName = `${name}_versions`\n if (!schemaRegistry.has(versionsTableName)) {\n const versions = generateVersionsTable(entity)\n schemaRegistry.register(versionsTableName, versions.table)\n registerContentTables(name, { versions })\n }\n versionableCount++\n }\n\n // Register layout tables for entities with blocks fields\n const allFields = entity.allFields as Record<\n string,\n { type: string; translatable?: boolean }\n >\n const hasBlocks = Object.values(allFields).some((f) => f.type === 'blocks')\n if (hasBlocks) {\n const layoutTableName = `${name}_layout`\n if (!schemaRegistry.has(layoutTableName)) {\n const layoutSchema = generateLayoutSchema(entity)\n if (layoutSchema) {\n schemaRegistry.register(layoutTableName, layoutSchema)\n }\n }\n\n const layoutTransTableName = `${name}_layout_translations`\n if (!schemaRegistry.has(layoutTransTableName)) {\n const layoutTransSchema = generateLayoutTranslationSchema(entity)\n if (layoutTransSchema) {\n schemaRegistry.register(layoutTransTableName, layoutTransSchema)\n }\n }\n\n blocksCount++\n }\n\n // Register locale_status tables for publishable + translatable entities\n const isPublishable = entity.behaviors?.some(\n (b: { name: string }) => b.name === 'publishable',\n )\n const hasTranslatable = Object.values(allFields).some((f) => f.translatable)\n if (isPublishable && hasTranslatable) {\n const statusTableName = `${name}_locale_status`\n if (!schemaRegistry.has(statusTableName)) {\n const localeStatus = generateLocaleStatusTable(entity)\n schemaRegistry.register(statusTableName, localeStatus.table)\n registerContentTables(name, { localeStatus })\n }\n localeStatusCount++\n }\n\n // Register drafts table for publishable entities\n if (isPublishable) {\n const draftsTableName = `${name}_drafts`\n if (!schemaRegistry.has(draftsTableName)) {\n const drafts = generateDraftsTable(entity)\n schemaRegistry.register(draftsTableName, drafts.table)\n registerContentTables(name, { drafts })\n }\n draftsCount++\n }\n }\n\n app.logger.info(\n {\n locales: config.locales.map((l) => l.code),\n defaultLocale: config.defaultLocale,\n versionableEntities: versionableCount,\n blockEntities: blocksCount,\n localeStatusEntities: localeStatusCount,\n draftEntities: draftsCount,\n },\n 'Content plugin initialized',\n )\n },\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin-DkmXJ7Qw.mjs","names":[],"sources":["../src/schema/registry.ts","../src/schema/drafts.ts","../src/schema/locale-status.ts","../src/schema/versions.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Per-entity content table registry.\n *\n * Maps an entity name to its content infrastructure tables — drafts,\n * versions, locale_status. Each entry stores the `DefinedTable` (from\n * `defineTable`), which exposes a typed `.makeClient(db)` for CRUD via\n * `TableClient` and a raw `.table` for legacy callers.\n *\n * The content plugin populates this registry at `init` time; ContentClient\n * reads from it lazily on first use. Tables that don't apply to an entity\n * (non-publishable entities have no drafts, etc.) are simply absent.\n */\n\nimport type { DraftsTable } from './drafts.js'\nimport type { LocaleStatusTable } from './locale-status.js'\nimport type { VersionsTable } from './versions.js'\n\n/**\n * The full set of optional content infra tables attached to an entity.\n * Every field is optional — entities without the relevant behavior don't\n * get the corresponding table.\n */\nexport interface ContentTables {\n /** `{entity}_drafts` — present when the entity has `publishable()`. */\n drafts?: DraftsTable\n /** `{entity}_versions` — present when the entity has `versionable()`. */\n versions?: VersionsTable\n /** `{entity}_locale_status` — present when the entity is publishable + has translatable fields. */\n localeStatus?: LocaleStatusTable\n}\n\n// Module-level map. Single source of truth for ContentClient lookups.\nconst tables = new Map<string, ContentTables>()\n\n/**\n * Register content tables for an entity. Called from `content().init()`.\n *\n * Subsequent calls for the same entity name MERGE the provided patch into\n * the existing entry. This lets the plugin add tables one at a time (drafts,\n * then versions, then locale_status) without the caller needing to build the\n * whole object up front.\n */\nexport function registerContentTables(entityName: string, patch: ContentTables): void {\n const existing = tables.get(entityName) ?? {}\n tables.set(entityName, { ...existing, ...patch })\n}\n\n/**\n * Get the content tables for an entity. Returns an empty object (rather\n * than undefined) when the entity has no registered tables — that\n * simplifies the common ContentClient lookup idiom:\n *\n * const { drafts } = getContentTables(entity.name)\n * if (!drafts) return null\n */\nexport function getContentTables(entityName: string): ContentTables {\n return tables.get(entityName) ?? {}\n}\n\n/**\n * Clear the registry. **Test-only.** Throws in production.\n */\nexport function clearContentTables(): void {\n if (process.env.NODE_ENV === 'production') {\n throw new Error(\n 'clearContentTables() is not allowed in production — this is a test-only utility.',\n )\n }\n tables.clear()\n}\n","/**\n * Draft overlay table schema generator\n *\n * Generates a {entity}_drafts table for entities with the publishable() behavior.\n * Stores a mutable work-in-progress snapshot that non-publishers edit instead of\n * writing directly to the live published row.\n *\n * One draft per entity+locale. Drafts are upserted, discarded, or promoted to live.\n *\n * Uses `defineTable` so ContentClient can operate on drafts through a typed\n * TableClient (see registry.ts). The migration pipeline uses the\n * entity-package generator (which emits FK references) — this one produces a\n * runtime-only table shape for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a publishable entity's drafts table.\n *\n * Schema:\n * {entity}_drafts (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * locale varchar(10) NOT NULL DEFAULT '_',\n * data jsonb NOT NULL, -- full entity snapshot\n * created_by varchar(255) NOT NULL, -- user ID (varchar, NOT uuid!)\n * created_by_name varchar(255),\n * created_at timestamptz NOT NULL DEFAULT now(),\n * updated_at timestamptz NOT NULL DEFAULT now(),\n * UNIQUE(entity_id, locale)\n * )\n */\nexport function generateDraftsTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_drafts`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n locale: column.varchar({ length: 10, notNull: true, default: '_' }),\n data: column.jsonb<Record<string, unknown>, true>({ notNull: true }),\n createdBy: column.varchar({ length: 255, notNull: true, pgName: 'created_by' }),\n createdByName: column.varchar({ length: 255, pgName: 'created_by_name' }),\n createdAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'created_at',\n }),\n updatedAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'updated_at',\n }),\n },\n unique: [{ on: ['entityId', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type DraftsTable = ReturnType<typeof generateDraftsTable>\n","/**\n * Per-locale publish status table schema generator\n *\n * Generates a {entity}_locale_status table for entities that have both\n * publishable() behavior AND translatable fields. This allows different\n * locales to have independent publish statuses.\n *\n * Fallback rule: no row for a locale = inherit base entity status.\n *\n * Uses `defineTable` so ContentClient can operate on rows through a typed\n * TableClient. The migration pipeline uses the entity-package generator\n * (which emits FK references); this one produces a runtime-only table shape\n * for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a publishable + translatable entity's\n * locale status table.\n *\n * Schema:\n * {entity}_locale_status (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * locale varchar(10) NOT NULL,\n * status varchar(20) NOT NULL DEFAULT 'draft',\n * published_at timestamptz,\n * UNIQUE(entity_id, locale)\n * )\n */\nexport function generateLocaleStatusTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_locale_status`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n locale: column.varchar({ length: 10, notNull: true }),\n status: column.varchar({ length: 20, notNull: true, default: 'draft' }),\n publishedAt: column.timestamp({ withTimezone: true, pgName: 'published_at' }),\n },\n unique: [{ on: ['entityId', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type LocaleStatusTable = ReturnType<typeof generateLocaleStatusTable>\n","/**\n * Version history table schema generator\n *\n * Generates a {entity}_versions table for entities with the versionable() behavior.\n * Each version stores a full snapshot + delta (changed fields only).\n *\n * Uses `defineTable` so ContentClient can operate on versions through a typed\n * TableClient. The migration pipeline uses the entity-package generator\n * (which emits FK references) — this one produces a runtime-only table shape\n * for read/write via TableClient.\n */\n\nimport { column, defineTable } from '@murumets-ee/db'\nimport type { Entity } from '@murumets-ee/entity'\n\n/**\n * Generate a `DefinedTable` for a versionable entity's versions table.\n *\n * Schema:\n * {entity}_versions (\n * id uuid PK,\n * entity_id uuid NOT NULL,\n * version integer NOT NULL,\n * locale varchar(10) NOT NULL DEFAULT '_', -- locale this snapshot belongs to\n * data jsonb NOT NULL, -- full snapshot (locale-merged view)\n * delta jsonb, -- only changed fields\n * status varchar(20), -- 'draft' | 'published' at time of save\n * created_by varchar(255),\n * created_by_name varchar(255),\n * created_at timestamptz NOT NULL DEFAULT now(),\n * is_autosave boolean NOT NULL DEFAULT false,\n * UNIQUE(entity_id, version, locale)\n * )\n *\n * Each locale has its own version sequence. The default locale uses '_'\n * as the locale value (distinguishable from actual locale codes).\n */\nexport function generateVersionsTable(entity: Entity) {\n return defineTable({\n name: `${entity.name}_versions`,\n columns: {\n id: column.uuid({ primaryKey: true, defaultRandom: true }),\n entityId: column.uuid({ notNull: true, pgName: 'entity_id' }),\n version: column.integer({ notNull: true }),\n locale: column.varchar({ length: 10, notNull: true, default: '_' }),\n data: column.jsonb<Record<string, unknown>, true>({ notNull: true }),\n delta: column.jsonb<Record<string, unknown>>(),\n status: column.varchar({ length: 20 }),\n createdBy: column.varchar({ length: 255, pgName: 'created_by' }),\n createdByName: column.varchar({ length: 255, pgName: 'created_by_name' }),\n createdAt: column.timestamp({\n notNull: true,\n defaultNow: true,\n withTimezone: true,\n pgName: 'created_at',\n }),\n isAutosave: column.boolean({ notNull: true, default: false, pgName: 'is_autosave' }),\n },\n unique: [{ on: ['entityId', 'version', 'locale'] }],\n })\n}\n\n/** Public type export — the full DefinedTable returned by the generator. */\nexport type VersionsTable = ReturnType<typeof generateVersionsTable>\n","/**\n * Content plugin — registers locale configuration and version schemas.\n *\n * Usage:\n * import { content } from '@murumets-ee/content/plugin'\n *\n * export default defineLumiConfig({\n * plugins: [\n * content({\n * locales: [\n * { code: 'en', label: 'English' },\n * { code: 'de', label: 'German' },\n * ],\n * defaultLocale: 'en',\n * }),\n * ],\n * })\n */\n\nimport { definePlugin } from '@murumets-ee/core'\nimport { schemaRegistry } from '@murumets-ee/db'\nimport { generateLayoutSchema, generateLayoutTranslationSchema } from '@murumets-ee/entity'\nimport { generateDraftsTable } from './schema/drafts.js'\nimport { generateLocaleStatusTable } from './schema/locale-status.js'\nimport { toolkitContentLocks } from './schema/locks.js'\nimport { registerContentTables } from './schema/registry.js'\nimport { generateVersionsTable } from './schema/versions.js'\nimport type { ContentConfig } from './types.js'\n\nlet _contentConfig: ContentConfig | null = null\n\n/**\n * Get the content configuration. Throws if plugin not initialized.\n */\nexport function getContentConfig(): ContentConfig {\n if (!_contentConfig) {\n throw new Error(\n '@murumets-ee/content plugin not initialized. Add content() to your plugins array.',\n )\n }\n return _contentConfig\n}\n\n/**\n * Content plugin factory.\n */\nexport function content(config: ContentConfig) {\n // Validate config\n if (!config.locales || config.locales.length === 0) {\n throw new Error('content() requires at least one locale')\n }\n\n if (!config.defaultLocale) {\n throw new Error('content() requires a defaultLocale')\n }\n\n const defaultExists = config.locales.some((l) => l.code === config.defaultLocale)\n if (!defaultExists) {\n throw new Error(\n `defaultLocale \"${config.defaultLocale}\" not found in locales: [${config.locales.map((l) => l.code).join(', ')}]`,\n )\n }\n\n return definePlugin({\n name: '@murumets-ee/content',\n server: {\n init: async (app) => {\n // Store config globally\n _contentConfig = config\n\n let versionableCount = 0\n let blocksCount = 0\n let localeStatusCount = 0\n let draftsCount = 0\n\n // Register shared locks table (once, outside entity loop)\n if (!schemaRegistry.has('toolkit_content_locks')) {\n schemaRegistry.register('toolkit_content_locks', toolkitContentLocks)\n }\n\n for (const [name, entity] of app.entities) {\n // Register version tables for versionable entities\n const hasVersionable = entity.behaviors?.some(\n (b: { name: string }) => b.name === 'versionable',\n )\n if (hasVersionable) {\n const versionsTableName = `${name}_versions`\n if (!schemaRegistry.has(versionsTableName)) {\n const versions = generateVersionsTable(entity)\n schemaRegistry.register(versionsTableName, versions.table)\n registerContentTables(name, { versions })\n }\n versionableCount++\n }\n\n // Register layout tables for entities with blocks fields\n const allFields = entity.allFields as Record<\n string,\n { type: string; translatable?: boolean }\n >\n const hasBlocks = Object.values(allFields).some((f) => f.type === 'blocks')\n if (hasBlocks) {\n const layoutTableName = `${name}_layout`\n if (!schemaRegistry.has(layoutTableName)) {\n const layoutSchema = generateLayoutSchema(entity)\n if (layoutSchema) {\n schemaRegistry.register(layoutTableName, layoutSchema)\n }\n }\n\n const layoutTransTableName = `${name}_layout_translations`\n if (!schemaRegistry.has(layoutTransTableName)) {\n const layoutTransSchema = generateLayoutTranslationSchema(entity)\n if (layoutTransSchema) {\n schemaRegistry.register(layoutTransTableName, layoutTransSchema)\n }\n }\n\n blocksCount++\n }\n\n // Register locale_status tables for publishable + translatable entities\n const isPublishable = entity.behaviors?.some(\n (b: { name: string }) => b.name === 'publishable',\n )\n const hasTranslatable = Object.values(allFields).some((f) => f.translatable)\n if (isPublishable && hasTranslatable) {\n const statusTableName = `${name}_locale_status`\n if (!schemaRegistry.has(statusTableName)) {\n const localeStatus = generateLocaleStatusTable(entity)\n schemaRegistry.register(statusTableName, localeStatus.table)\n registerContentTables(name, { localeStatus })\n }\n localeStatusCount++\n }\n\n // Register drafts table for publishable entities\n if (isPublishable) {\n const draftsTableName = `${name}_drafts`\n if (!schemaRegistry.has(draftsTableName)) {\n const drafts = generateDraftsTable(entity)\n schemaRegistry.register(draftsTableName, drafts.table)\n registerContentTables(name, { drafts })\n }\n draftsCount++\n }\n }\n\n app.logger.info(\n {\n locales: config.locales.map((l) => l.code),\n defaultLocale: config.defaultLocale,\n versionableEntities: versionableCount,\n blockEntities: blocksCount,\n localeStatusEntities: localeStatusCount,\n draftEntities: draftsCount,\n },\n 'Content plugin initialized',\n )\n },\n },\n })\n}\n"],"mappings":"wQAgCA,MAAM,EAAS,IAAI,IAUnB,SAAgB,EAAsB,EAAoB,EAA4B,CACpF,IAAM,EAAW,EAAO,IAAI,EAAW,EAAI,EAAE,CAC7C,EAAO,IAAI,EAAY,CAAE,GAAG,EAAU,GAAG,EAAO,CAAC,CAWnD,SAAgB,EAAiB,EAAmC,CAClE,OAAO,EAAO,IAAI,EAAW,EAAI,EAAE,CCtBrC,SAAgB,EAAoB,EAAgB,CAClD,OAAO,EAAY,CACjB,KAAM,GAAG,EAAO,KAAK,SACrB,QAAS,CACP,GAAI,EAAO,KAAK,CAAE,WAAY,GAAM,cAAe,GAAM,CAAC,CAC1D,SAAU,EAAO,KAAK,CAAE,QAAS,GAAM,OAAQ,YAAa,CAAC,CAC7D,OAAQ,EAAO,QAAQ,CAAE,OAAQ,GAAI,QAAS,GAAM,QAAS,IAAK,CAAC,CACnE,KAAM,EAAO,MAAqC,CAAE,QAAS,GAAM,CAAC,CACpE,UAAW,EAAO,QAAQ,CAAE,OAAQ,IAAK,QAAS,GAAM,OAAQ,aAAc,CAAC,CAC/E,cAAe,EAAO,QAAQ,CAAE,OAAQ,IAAK,OAAQ,kBAAmB,CAAC,CACzE,UAAW,EAAO,UAAU,CAC1B,QAAS,GACT,WAAY,GACZ,aAAc,GACd,OAAQ,aACT,CAAC,CACF,UAAW,EAAO,UAAU,CAC1B,QAAS,GACT,WAAY,GACZ,aAAc,GACd,OAAQ,aACT,CAAC,CACH,CACD,OAAQ,CAAC,CAAE,GAAI,CAAC,WAAY,SAAS,CAAE,CAAC,CACzC,CAAC,CC1BJ,SAAgB,EAA0B,EAAgB,CACxD,OAAO,EAAY,CACjB,KAAM,GAAG,EAAO,KAAK,gBACrB,QAAS,CACP,GAAI,EAAO,KAAK,CAAE,WAAY,GAAM,cAAe,GAAM,CAAC,CAC1D,SAAU,EAAO,KAAK,CAAE,QAAS,GAAM,OAAQ,YAAa,CAAC,CAC7D,OAAQ,EAAO,QAAQ,CAAE,OAAQ,GAAI,QAAS,GAAM,CAAC,CACrD,OAAQ,EAAO,QAAQ,CAAE,OAAQ,GAAI,QAAS,GAAM,QAAS,QAAS,CAAC,CACvE,YAAa,EAAO,UAAU,CAAE,aAAc,GAAM,OAAQ,eAAgB,CAAC,CAC9E,CACD,OAAQ,CAAC,CAAE,GAAI,CAAC,WAAY,SAAS,CAAE,CAAC,CACzC,CAAC,CCNJ,SAAgB,EAAsB,EAAgB,CACpD,OAAO,EAAY,CACjB,KAAM,GAAG,EAAO,KAAK,WACrB,QAAS,CACP,GAAI,EAAO,KAAK,CAAE,WAAY,GAAM,cAAe,GAAM,CAAC,CAC1D,SAAU,EAAO,KAAK,CAAE,QAAS,GAAM,OAAQ,YAAa,CAAC,CAC7D,QAAS,EAAO,QAAQ,CAAE,QAAS,GAAM,CAAC,CAC1C,OAAQ,EAAO,QAAQ,CAAE,OAAQ,GAAI,QAAS,GAAM,QAAS,IAAK,CAAC,CACnE,KAAM,EAAO,MAAqC,CAAE,QAAS,GAAM,CAAC,CACpE,MAAO,EAAO,OAAgC,CAC9C,OAAQ,EAAO,QAAQ,CAAE,OAAQ,GAAI,CAAC,CACtC,UAAW,EAAO,QAAQ,CAAE,OAAQ,IAAK,OAAQ,aAAc,CAAC,CAChE,cAAe,EAAO,QAAQ,CAAE,OAAQ,IAAK,OAAQ,kBAAmB,CAAC,CACzE,UAAW,EAAO,UAAU,CAC1B,QAAS,GACT,WAAY,GACZ,aAAc,GACd,OAAQ,aACT,CAAC,CACF,WAAY,EAAO,QAAQ,CAAE,QAAS,GAAM,QAAS,GAAO,OAAQ,cAAe,CAAC,CACrF,CACD,OAAQ,CAAC,CAAE,GAAI,CAAC,WAAY,UAAW,SAAS,CAAE,CAAC,CACpD,CAAC,CC9BJ,IAAI,EAAuC,KAK3C,SAAgB,GAAkC,CAChD,GAAI,CAAC,EACH,MAAU,MACR,oFACD,CAEH,OAAO,EAMT,SAAgB,EAAQ,EAAuB,CAE7C,GAAI,CAAC,EAAO,SAAW,EAAO,QAAQ,SAAW,EAC/C,MAAU,MAAM,yCAAyC,CAG3D,GAAI,CAAC,EAAO,cACV,MAAU,MAAM,qCAAqC,CAIvD,GAAI,CADkB,EAAO,QAAQ,KAAM,GAAM,EAAE,OAAS,EAAO,cACjD,CAChB,MAAU,MACR,kBAAkB,EAAO,cAAc,2BAA2B,EAAO,QAAQ,IAAK,GAAM,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC,GAChH,CAGH,OAAO,EAAa,CAClB,KAAM,uBACN,OAAQ,CACN,KAAM,KAAO,IAAQ,CAEnB,EAAiB,EAEjB,IAAI,EAAmB,EACnB,EAAc,EACd,EAAoB,EACpB,EAAc,EAGb,EAAe,IAAI,wBAAwB,EAC9C,EAAe,SAAS,wBAAyB,EAAoB,CAGvE,IAAK,GAAM,CAAC,EAAM,KAAW,EAAI,SAAU,CAKzC,GAHuB,EAAO,WAAW,KACtC,GAAwB,EAAE,OAAS,cACrC,CACmB,CAClB,IAAM,EAAoB,GAAG,EAAK,WAClC,GAAI,CAAC,EAAe,IAAI,EAAkB,CAAE,CAC1C,IAAM,EAAW,EAAsB,EAAO,CAC9C,EAAe,SAAS,EAAmB,EAAS,MAAM,CAC1D,EAAsB,EAAM,CAAE,WAAU,CAAC,CAE3C,IAIF,IAAM,EAAY,EAAO,UAKzB,GADkB,OAAO,OAAO,EAAU,CAAC,KAAM,GAAM,EAAE,OAAS,SACrD,CAAE,CACb,IAAM,EAAkB,GAAG,EAAK,SAChC,GAAI,CAAC,EAAe,IAAI,EAAgB,CAAE,CACxC,IAAM,EAAe,EAAqB,EAAO,CAC7C,GACF,EAAe,SAAS,EAAiB,EAAa,CAI1D,IAAM,EAAuB,GAAG,EAAK,sBACrC,GAAI,CAAC,EAAe,IAAI,EAAqB,CAAE,CAC7C,IAAM,EAAoB,EAAgC,EAAO,CAC7D,GACF,EAAe,SAAS,EAAsB,EAAkB,CAIpE,IAIF,IAAM,EAAgB,EAAO,WAAW,KACrC,GAAwB,EAAE,OAAS,cACrC,CACK,EAAkB,OAAO,OAAO,EAAU,CAAC,KAAM,GAAM,EAAE,aAAa,CAC5E,GAAI,GAAiB,EAAiB,CACpC,IAAM,EAAkB,GAAG,EAAK,gBAChC,GAAI,CAAC,EAAe,IAAI,EAAgB,CAAE,CACxC,IAAM,EAAe,EAA0B,EAAO,CACtD,EAAe,SAAS,EAAiB,EAAa,MAAM,CAC5D,EAAsB,EAAM,CAAE,eAAc,CAAC,CAE/C,IAIF,GAAI,EAAe,CACjB,IAAM,EAAkB,GAAG,EAAK,SAChC,GAAI,CAAC,EAAe,IAAI,EAAgB,CAAE,CACxC,IAAM,EAAS,EAAoB,EAAO,CAC1C,EAAe,SAAS,EAAiB,EAAO,MAAM,CACtD,EAAsB,EAAM,CAAE,SAAQ,CAAC,CAEzC,KAIJ,EAAI,OAAO,KACT,CACE,QAAS,EAAO,QAAQ,IAAK,GAAM,EAAE,KAAK,CAC1C,cAAe,EAAO,cACtB,oBAAqB,EACrB,cAAe,EACf,qBAAsB,EACtB,cAAe,EAChB,CACD,6BACD,EAEJ,CACF,CAAC"}
package/dist/plugin.d.mts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { n as ContentConfig } from "./types-BCmX1nBL.mjs";
2
- import { Plugin } from "@murumets-ee/core";
3
2
 
4
3
  //#region src/plugin.d.ts
5
4
  /**
@@ -9,7 +8,10 @@ declare function getContentConfig(): ContentConfig;
9
8
  /**
10
9
  * Content plugin factory.
11
10
  */
12
- declare function content(config: ContentConfig): Plugin;
11
+ declare function content(config: ContentConfig): Pick<import("@murumets-ee/core").Plugin, "server" | "adminUi" | "postInstall"> & {
12
+ readonly name: "@murumets-ee/content";
13
+ readonly shared?: undefined;
14
+ };
13
15
  //#endregion
14
16
  export { content, getContentConfig };
15
17
  //# sourceMappingURL=plugin.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;;;;iBAkCgB,gBAAA,CAAA,GAAoB,aAAA;;;;iBAYpB,OAAA,CAAQ,MAAA,EAAQ,aAAA,GAAgB,MAAA"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;;;iBAkCgB,gBAAA,CAAA,GAAoB,aAAA;;;;iBAYpB,OAAA,CAAQ,MAAA,EAAQ,aAAA,GAAa,IAAA,6BAAA,MAAA;EAAA"}
package/dist/plugin.mjs CHANGED
@@ -1 +1 @@
1
- import{n as e,t}from"./plugin-yijaaFS7.mjs";export{t as content,e as getContentConfig};
1
+ import{n as e,t}from"./plugin-DkmXJ7Qw.mjs";export{t as content,e as getContentConfig};
@@ -1,2 +1,2 @@
1
- import{t as e}from"./locale-utils-By7EdAT1.mjs";import"server-only";import{createQueryClient as t}from"@murumets-ee/core/clients";import{schemaRegistry as n}from"@murumets-ee/db";import{eq as r}from"drizzle-orm";var i=class{entity;query;constructor(e){this.entity=e.entity,this.query=e.query}async findById(e,t){return this.query.findById(e,t)}async findBySlug(t,i){i?.locale!==void 0&&e(i.locale);let a=n.get(this.entity.name);return!a||!a.slug?null:(await this.query.findMany({where:r(a.slug,t),limit:1,locale:i?.locale}))[0]??null}async findMany(e){return this.query.findMany(e)}async count(e){return this.query.count(e)}};function a(e){return new i({query:t(e),entity:e})}export{i as ContentQueryClient,a as createContentQueryClient};
1
+ import{t as e}from"./locale-utils-BMRVJwvU.mjs";import"server-only";import{createQueryClient as t}from"@murumets-ee/core/clients";import{schemaRegistry as n}from"@murumets-ee/db";import{eq as r}from"drizzle-orm";var i=class{entity;query;constructor(e){this.entity=e.entity,this.query=e.query}async findById(e,t){return this.query.findById(e,t)}async findBySlug(t,i){i?.locale!==void 0&&e(i.locale);let a=n.get(this.entity.name);return!a||!a.slug?null:(await this.query.findMany({where:r(a.slug,t),limit:1,locale:i?.locale}))[0]??null}async findMany(e){return this.query.findMany(e)}async count(e){return this.query.count(e)}};function a(e){return new i({query:t(e),entity:e})}export{i as ContentQueryClient,a as createContentQueryClient};
2
2
  //# sourceMappingURL=query-client.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@murumets-ee/content",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -36,10 +36,10 @@
36
36
  "drizzle-orm": "^0.45.2",
37
37
  "server-only": "^0.0.1",
38
38
  "zod": "^3.24.1",
39
- "@murumets-ee/core": "0.19.0",
40
- "@murumets-ee/db": "0.19.0",
41
- "@murumets-ee/entity": "0.19.0",
42
- "@murumets-ee/logging": "0.19.0"
39
+ "@murumets-ee/db": "0.21.0",
40
+ "@murumets-ee/entity": "0.21.0",
41
+ "@murumets-ee/core": "0.21.0",
42
+ "@murumets-ee/logging": "0.21.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^20.19.40",
@@ -53,6 +53,7 @@
53
53
  "scripts": {
54
54
  "build": "tsdown",
55
55
  "dev": "tsdown --watch",
56
- "test": "vitest"
56
+ "test": "vitest run",
57
+ "test:watch": "vitest"
57
58
  }
58
59
  }
@@ -1,2 +0,0 @@
1
- import{n as e}from"./locks-BRtmYqgS.mjs";import{generateLayoutSchema as t,generateLayoutTranslationSchema as n}from"@murumets-ee/entity";import{column as r,defineTable as i,schemaRegistry as a}from"@murumets-ee/db";const o=new Map;function s(e,t){let n=o.get(e)??{};o.set(e,{...n,...t})}function c(e){return o.get(e)??{}}function l(e){return i({name:`${e.name}_drafts`,columns:{id:r.uuid({primaryKey:!0,defaultRandom:!0}),entityId:r.uuid({notNull:!0,pgName:`entity_id`}),locale:r.varchar({length:10,notNull:!0,default:`_`}),data:r.jsonb({notNull:!0}),createdBy:r.varchar({length:255,notNull:!0,pgName:`created_by`}),createdByName:r.varchar({length:255,pgName:`created_by_name`}),createdAt:r.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),updatedAt:r.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`updated_at`})},unique:[{on:[`entityId`,`locale`]}]})}function u(e){return i({name:`${e.name}_locale_status`,columns:{id:r.uuid({primaryKey:!0,defaultRandom:!0}),entityId:r.uuid({notNull:!0,pgName:`entity_id`}),locale:r.varchar({length:10,notNull:!0}),status:r.varchar({length:20,notNull:!0,default:`draft`}),publishedAt:r.timestamp({withTimezone:!0,pgName:`published_at`})},unique:[{on:[`entityId`,`locale`]}]})}function d(e){return i({name:`${e.name}_versions`,columns:{id:r.uuid({primaryKey:!0,defaultRandom:!0}),entityId:r.uuid({notNull:!0,pgName:`entity_id`}),version:r.integer({notNull:!0}),locale:r.varchar({length:10,notNull:!0,default:`_`}),data:r.jsonb({notNull:!0}),delta:r.jsonb(),status:r.varchar({length:20}),createdBy:r.varchar({length:255,pgName:`created_by`}),createdByName:r.varchar({length:255,pgName:`created_by_name`}),createdAt:r.timestamp({notNull:!0,defaultNow:!0,withTimezone:!0,pgName:`created_at`}),isAutosave:r.boolean({notNull:!0,default:!1,pgName:`is_autosave`})},unique:[{on:[`entityId`,`version`,`locale`]}]})}let f=null;function p(){if(!f)throw Error(`@murumets-ee/content plugin not initialized. Add content() to your plugins array.`);return f}function m(r){if(!r.locales||r.locales.length===0)throw Error(`content() requires at least one locale`);if(!r.defaultLocale)throw Error(`content() requires a defaultLocale`);if(!r.locales.some(e=>e.code===r.defaultLocale))throw Error(`defaultLocale "${r.defaultLocale}" not found in locales: [${r.locales.map(e=>e.code).join(`, `)}]`);return{name:`@murumets-ee/content`,server:{init:async i=>{f=r;let o=0,c=0,p=0,m=0;a.has(`toolkit_content_locks`)||a.register(`toolkit_content_locks`,e);for(let[e,r]of i.entities){if(r.behaviors?.some(e=>e.name===`versionable`)){let t=`${e}_versions`;if(!a.has(t)){let n=d(r);a.register(t,n.table),s(e,{versions:n})}o++}let i=r.allFields;if(Object.values(i).some(e=>e.type===`blocks`)){let i=`${e}_layout`;if(!a.has(i)){let e=t(r);e&&a.register(i,e)}let o=`${e}_layout_translations`;if(!a.has(o)){let e=n(r);e&&a.register(o,e)}c++}let f=r.behaviors?.some(e=>e.name===`publishable`),h=Object.values(i).some(e=>e.translatable);if(f&&h){let t=`${e}_locale_status`;if(!a.has(t)){let n=u(r);a.register(t,n.table),s(e,{localeStatus:n})}p++}if(f){let t=`${e}_drafts`;if(!a.has(t)){let n=l(r);a.register(t,n.table),s(e,{drafts:n})}m++}}i.logger.info({locales:r.locales.map(e=>e.code),defaultLocale:r.defaultLocale,versionableEntities:o,blockEntities:c,localeStatusEntities:p,draftEntities:m},`Content plugin initialized`)}}}}export{p as n,c as r,m as t};
2
- //# sourceMappingURL=plugin-yijaaFS7.mjs.map