@qkix/strapi-plugin-rewind 0.2.1 → 0.4.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,6 +1,29 @@
1
- import { createHash as J } from "node:crypto";
2
- import { AsyncLocalStorage as Z } from "node:async_hooks";
3
- const Q = ["id", "documentId", "locale"], S = [
1
+ import { createHash as x } from "node:crypto";
2
+ import { AsyncLocalStorage as ee } from "node:async_hooks";
3
+ const W = (e) => {
4
+ const t = /* @__PURE__ */ new Map();
5
+ return async (n) => {
6
+ if (t.has(n)) return t.get(n);
7
+ let i;
8
+ try {
9
+ i = (await e.plugin("content-manager").service("content-types").findConfiguration(e.contentTypes[n]))?.settings?.mainField;
10
+ } catch {
11
+ }
12
+ return t.set(n, i), i;
13
+ };
14
+ }, K = ["title", "name", "label"], G = (e) => {
15
+ const t = W(e);
16
+ return async (n, i) => {
17
+ if (!i) return null;
18
+ const r = [await t(n), ...K].filter(Boolean);
19
+ for (const s of r) {
20
+ const a = i[s];
21
+ if (typeof a == "string" && a.trim())
22
+ return a.trim().slice(0, 255);
23
+ }
24
+ return null;
25
+ };
26
+ }, te = ["id", "documentId", "locale"], q = [
4
27
  "createdAt",
5
28
  "updatedAt",
6
29
  "publishedAt",
@@ -10,35 +33,66 @@ const Q = ["id", "documentId", "locale"], S = [
10
33
  "strapi_assignee",
11
34
  "locale",
12
35
  "localizations"
13
- ], I = (e, t) => e.getModel(t), x = (e) => e?.type === "relation" || e?.type === "media", $ = (e) => e?.type === "relation" && String(e.relation ?? "").toLowerCase().startsWith("morph"), F = (e, t) => Object.entries(I(e, t).attributes).filter(
36
+ ], S = (e, t) => e.getModel(t), ne = (e) => e?.type === "relation" || e?.type === "media", $ = (e) => e?.type === "relation" && String(e.relation ?? "").toLowerCase().startsWith("morph"), _ = "plugin::upload.file", oe = /* @__PURE__ */ new Set(["string", "uid", "email", "text", "enumeration"]), se = async (e, t, n) => {
37
+ const i = /* @__PURE__ */ new Map(), o = async (r) => {
38
+ if (!r || i.has(r)) return;
39
+ let s;
40
+ try {
41
+ s = S(e, r).attributes;
42
+ } catch {
43
+ return;
44
+ }
45
+ const a = (d) => !!d && oe.has(s[d]?.type), l = [await n(r), ...K].find(a);
46
+ l && i.set(r, l);
47
+ };
48
+ for (const r of Object.values(S(e, t).attributes))
49
+ r?.type === "media" ? await o(_) : r?.type === "relation" && !$(r) && await o(r.target);
50
+ return i;
51
+ }, ie = (e) => Object.fromEntries(
52
+ Object.entries(e).map(([t, n]) => [
53
+ t,
54
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
55
+ n.map(({ label: i, ...o }) => o)
56
+ ])
57
+ ), j = (e, t) => Object.entries(S(e, t).attributes).filter(
14
58
  ([, n]) => !["relation", "media", "component", "dynamiczone"].includes(n.type)
15
- ).map(([n]) => n), E = (e, t, n = !0) => {
16
- const i = I(e, t), o = n ? "select" : "fields";
17
- return Object.entries(i.attributes).reduce(
18
- (a, [s, r]) => {
19
- switch (r.type) {
59
+ ).map(([n]) => n), F = (e, t, n = !0, i) => {
60
+ const o = S(e, t), r = n ? "select" : "fields", s = (a) => {
61
+ const l = i?.get(a);
62
+ return l ? [l] : [];
63
+ };
64
+ return Object.entries(o.attributes).reduce(
65
+ (a, [l, d]) => {
66
+ switch (d.type) {
20
67
  case "relation": {
21
- if ($(r)) break;
22
- a[s] = { [o]: ["documentId", "locale", "publishedAt"] };
68
+ if ($(d)) break;
69
+ a[l] = {
70
+ [r]: [
71
+ "documentId",
72
+ "locale",
73
+ "publishedAt",
74
+ ...s(d.target)
75
+ ]
76
+ };
23
77
  break;
24
78
  }
25
79
  case "media": {
26
- a[s] = { [o]: ["id"] };
80
+ a[l] = { [r]: ["id", ...s(_)] };
27
81
  break;
28
82
  }
29
83
  case "component": {
30
- a[s] = {
31
- populate: E(e, r.component, n),
32
- [o]: F(e, r.component)
84
+ a[l] = {
85
+ populate: F(e, d.component, n),
86
+ [r]: j(e, d.component)
33
87
  };
34
88
  break;
35
89
  }
36
90
  case "dynamiczone": {
37
- a[s] = {
38
- on: (r.components ?? []).reduce(
39
- (c, l) => (c[l] = {
40
- populate: E(e, l, n),
41
- [o]: F(e, l)
91
+ a[l] = {
92
+ on: (d.components ?? []).reduce(
93
+ (c, u) => (c[u] = {
94
+ populate: F(e, u, n),
95
+ [r]: j(e, u)
42
96
  }, c),
43
97
  {}
44
98
  )
@@ -50,138 +104,133 @@ const Q = ["id", "documentId", "locale"], S = [
50
104
  },
51
105
  {}
52
106
  );
53
- }, ee = (e, t) => {
54
- const n = {}, i = (a) => {
55
- if (n[a]) return;
56
- const s = I(e, a).attributes;
57
- n[a] = R(s, S);
58
- for (const r of Object.values(s))
59
- if (r.type === "component" && i(r.component), r.type === "dynamiczone")
60
- for (const c of r.components ?? []) i(c);
61
- }, o = I(e, t).attributes;
62
- for (const a of Object.values(o))
63
- if (a.type === "component" && i(a.component), a.type === "dynamiczone")
64
- for (const s of a.components ?? []) i(s);
107
+ }, re = (e, t) => {
108
+ const n = {}, i = (r) => {
109
+ if (n[r]) return;
110
+ const s = S(e, r).attributes;
111
+ n[r] = N(s, q);
112
+ for (const a of Object.values(s))
113
+ if (a.type === "component" && i(a.component), a.type === "dynamiczone")
114
+ for (const l of a.components ?? []) i(l);
115
+ }, o = S(e, t).attributes;
116
+ for (const r of Object.values(o))
117
+ if (r.type === "component" && i(r.component), r.type === "dynamiczone")
118
+ for (const s of r.components ?? []) i(s);
65
119
  return {
66
- attributes: R(o, S),
120
+ attributes: N(o, q),
67
121
  components: n
68
122
  };
69
- }, R = (e, t) => Object.fromEntries(Object.entries(e ?? {}).filter(([n]) => !t.includes(n))), te = (e, t, n) => (Array.isArray(e) ? e : [e]).filter((o) => !!o).map(
70
- (o) => n ? { id: o.id, targetUid: t } : {
71
- documentId: o.documentId,
72
- locale: o.locale ?? null,
73
- targetUid: t
74
- }
75
- ), ne = (e, t, n) => {
76
- const i = I(e, t).attributes, o = {}, a = {}, s = R(n, [...S, ...Q]);
77
- for (const [r, c] of Object.entries(s)) {
78
- const l = i[r];
79
- if (l && x(l)) {
80
- if ($(l)) continue;
81
- const d = l.type === "media" ? "plugin::upload.file" : l.target;
82
- a[r] = te(c, d, l.type === "media");
123
+ }, N = (e, t) => Object.fromEntries(Object.entries(e ?? {}).filter(([n]) => !t.includes(n))), ae = (e, t, n, i) => {
124
+ const o = Array.isArray(e) ? e : [e], r = (s) => {
125
+ const a = i ? s[i] : void 0;
126
+ return typeof a == "string" && a.trim() ? { label: a.trim().slice(0, 255) } : {};
127
+ };
128
+ return o.filter((s) => !!s).map(
129
+ (s) => n ? { id: s.id, targetUid: t, ...r(s) } : {
130
+ documentId: s.documentId,
131
+ locale: s.locale ?? null,
132
+ targetUid: t,
133
+ ...r(s)
134
+ }
135
+ );
136
+ }, ce = (e, t, n, i) => {
137
+ const o = S(e, t).attributes, r = {}, s = {}, a = N(n, [...q, ...te]);
138
+ for (const [l, d] of Object.entries(a)) {
139
+ const c = o[l];
140
+ if (c && ne(c)) {
141
+ if ($(c)) continue;
142
+ const u = c.type === "media" ? _ : c.target;
143
+ s[l] = ae(
144
+ d,
145
+ u,
146
+ c.type === "media",
147
+ i?.get(u)
148
+ );
83
149
  continue;
84
150
  }
85
- o[r] = l ? Y(e, l, c) : c;
151
+ r[l] = c ? V(e, c, d) : d;
86
152
  }
87
- return { data: o, relations: a, schemaSnapshot: ee(e, t) };
88
- }, Y = (e, t, n) => {
153
+ return { data: r, relations: s, schemaSnapshot: re(e, t) };
154
+ }, V = (e, t, n) => {
89
155
  if (n == null) return n;
90
156
  if (t.type === "component") {
91
- const i = (o) => _(e, t.component, o);
157
+ const i = (o) => M(e, t.component, o);
92
158
  return Array.isArray(n) ? n.map((o) => i(o)) : i(n);
93
159
  }
94
160
  return t.type === "dynamiczone" && Array.isArray(n) ? n.map((i) => {
95
161
  const o = i;
96
- return _(e, o.__component, o);
162
+ return M(e, o.__component, o);
97
163
  }) : n;
98
- }, _ = (e, t, n) => {
164
+ }, M = (e, t, n) => {
99
165
  if (!n || !t) return n;
100
- const i = I(e, t).attributes, o = {};
101
- for (const [a, s] of Object.entries(n)) {
102
- if (a === "id") continue;
103
- const r = i[a];
104
- o[a] = r ? Y(e, r, s) : s;
166
+ const i = S(e, t).attributes, o = {};
167
+ for (const [r, s] of Object.entries(n)) {
168
+ if (r === "id") continue;
169
+ const a = i[r];
170
+ o[r] = a ? V(e, a, s) : s;
105
171
  }
106
172
  return o;
107
- }, H = (e) => {
108
- const t = /* @__PURE__ */ new Map(), n = async (i) => {
109
- if (t.has(i)) return t.get(i);
110
- let o;
111
- try {
112
- o = (await e.plugin("content-manager").service("content-types").findConfiguration(e.contentTypes[i]))?.settings?.mainField;
113
- } catch {
114
- }
115
- return t.set(i, o), o;
116
- };
117
- return async (i, o) => {
118
- if (!o) return null;
119
- const s = [await n(i), "title", "name", "label"].filter(Boolean);
120
- for (const r of s) {
121
- const c = o[r];
122
- if (typeof c == "string" && c.trim())
123
- return c.trim().slice(0, 255);
124
- }
125
- return null;
126
- };
127
- }, v = "plugin::rewind.version", oe = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), se = (e) => J("sha256").update(JSON.stringify(e)).digest("hex"), ie = ({ strapi: e }) => {
128
- const t = H(e), n = (r) => !!e.contentTypes[r]?.options?.draftAndPublish, i = (r) => !!(e.getModel(r)?.pluginOptions?.i18n?.localized ?? e.contentTypes[r]?.pluginOptions?.i18n?.localized), o = async (r, c, l) => {
129
- const d = i(r) && l.filter(Boolean).length > 0 ? { locale: { $in: l.filter(Boolean) } } : {};
130
- return e.db.query(r).findMany({
173
+ }, D = "plugin::rewind.version", le = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), de = (e) => x("sha256").update(JSON.stringify(e)).digest("hex"), ue = ({ strapi: e }) => {
174
+ const t = G(e), n = W(e), i = /* @__PURE__ */ new Map(), o = (c) => {
175
+ let u = i.get(c);
176
+ return u || (u = se(e, c, n), i.set(c, u)), u;
177
+ }, r = (c) => !!e.contentTypes[c]?.options?.draftAndPublish, s = (c) => !!(e.getModel(c)?.pluginOptions?.i18n?.localized ?? e.contentTypes[c]?.pluginOptions?.i18n?.localized), a = async (c, u, f) => {
178
+ const g = s(c) && f.filter(Boolean).length > 0 ? { locale: { $in: f.filter(Boolean) } } : {};
179
+ return e.db.query(c).findMany({
131
180
  where: {
132
- documentId: c,
133
- ...d,
134
- ...n(r) ? { publishedAt: null } : {}
181
+ documentId: u,
182
+ ...g,
183
+ ...r(c) ? { publishedAt: null } : {}
135
184
  },
136
- populate: E(e, r)
185
+ populate: F(e, c, !0, await o(c))
137
186
  });
138
- }, a = async (r, c) => {
187
+ }, l = async (c, u) => {
139
188
  try {
140
- const l = e.plugin("content-manager").service("document-metadata"), d = await l.getMetadata(r, c);
141
- return l.getStatus(c, d.availableStatus);
189
+ const f = e.plugin("content-manager").service("document-metadata"), g = await f.getMetadata(c, u);
190
+ return f.getStatus(u, g.availableStatus);
142
191
  } catch {
143
192
  return "draft";
144
193
  }
145
- }, s = async (r, c, l) => e.db.query(v).findOne({
146
- where: { contentType: r, relatedDocumentId: c, locale: l },
194
+ }, d = async (c, u, f) => e.db.query(D).findOne({
195
+ where: { contentType: c, relatedDocumentId: u, locale: f },
147
196
  orderBy: { createdAt: "desc" }
148
197
  });
149
198
  return {
150
- readRows: o,
199
+ readRows: a,
151
200
  /**
152
201
  * Writes one version per affected locale, for each buffered intent.
153
202
  *
154
203
  * Called from the transaction's commit hook and never awaited by the write
155
204
  * path - a failure here must not be able to reach the editor's save.
156
205
  */
157
- async captureAll(r, c) {
158
- for (const l of r)
159
- await this.capture(l, c);
206
+ async captureAll(c, u) {
207
+ for (const f of c)
208
+ await this.capture(f, u);
160
209
  },
161
- async capture(r, c) {
162
- const { uid: l, relatedDocumentId: d, locale: p, origin: b } = r, m = r.before ?? await o(l, d, [p]);
163
- for (const u of m) {
164
- const h = u.locale ?? null, { data: f, relations: w, schemaSnapshot: T } = ne(e, l, u), g = se({ data: f, relations: w });
165
- !oe.has(b) && (await s(l, d, h))?.contentHash === g || await e.db.query(v).create({
210
+ async capture(c, u) {
211
+ const { uid: f, relatedDocumentId: g, locale: y, origin: T } = c, p = c.before ?? await a(f, g, [y]);
212
+ for (const h of p) {
213
+ const b = h.locale ?? null, { data: m, relations: v, schemaSnapshot: A } = ce(e, f, h, await o(f)), I = de({ data: m, relations: ie(v) });
214
+ !le.has(T) && (await d(f, g, b))?.contentHash === I || await e.db.query(D).create({
166
215
  data: {
167
- contentType: l,
168
- relatedDocumentId: d,
169
- locale: h,
170
- status: await a(l, u),
171
- label: await t(l, f),
172
- data: f,
173
- relations: w,
174
- schemaSnapshot: T,
175
- contentHash: g,
176
- origin: b,
177
- userId: c,
216
+ contentType: f,
217
+ relatedDocumentId: g,
218
+ locale: b,
219
+ status: await l(f, h),
220
+ label: await t(f, m),
221
+ data: m,
222
+ relations: v,
223
+ schemaSnapshot: A,
224
+ contentHash: I,
225
+ origin: T,
226
+ userId: u,
178
227
  pinned: !1
179
228
  }
180
229
  });
181
230
  }
182
231
  }
183
232
  };
184
- }, j = [
233
+ }, C = [
185
234
  "restore",
186
235
  "publish",
187
236
  "unpublish",
@@ -189,10 +238,10 @@ const Q = ["id", "documentId", "locale"], S = [
189
238
  "clone",
190
239
  "create",
191
240
  "update"
192
- ], C = /* @__PURE__ */ new WeakMap(), re = (e) => {
193
- let t = C.get(e);
194
- return t || (t = { intents: [], flushRegistered: !1 }, C.set(e, t)), t;
195
- }, ae = (e) => {
241
+ ], B = /* @__PURE__ */ new WeakMap(), fe = (e) => {
242
+ let t = B.get(e);
243
+ return t || (t = { intents: [], flushRegistered: !1 }, B.set(e, t)), t;
244
+ }, pe = (e) => {
196
245
  const t = /* @__PURE__ */ new Map();
197
246
  for (const n of e) {
198
247
  const i = `${n.uid}:${n.relatedDocumentId}:${n.locale ?? ""}`, o = t.get(i);
@@ -200,86 +249,86 @@ const Q = ["id", "documentId", "locale"], S = [
200
249
  t.set(i, n);
201
250
  continue;
202
251
  }
203
- const a = j.indexOf(n.origin) < j.indexOf(o.origin);
252
+ const r = C.indexOf(n.origin) < C.indexOf(o.origin);
204
253
  t.set(i, {
205
254
  // A winning `publish` still wants the rows an earlier `discardDraft`
206
255
  // captured - losing them would lose the only copy of that state.
207
- ...a ? n : o,
256
+ ...r ? n : o,
208
257
  before: o.before ?? n.before
209
258
  });
210
259
  }
211
260
  return [...t.values()];
212
- }, W = new Z(), ce = (e) => W.run({ active: !0 }, e), le = () => W.getStore()?.active === !0, de = 200, ue = async (e, t) => {
261
+ }, X = new ee(), me = (e) => X.run({ active: !0 }, e), ge = () => X.getStore()?.active === !0, ye = 200, he = async (e, t) => {
213
262
  const n = e.db.query(t), i = await n.count({ where: { label: null } });
214
263
  if (i === 0) return 0;
215
264
  e.log.info(`[rewind] labelling ${i} older version(s)…`);
216
- const o = H(e);
217
- let a = 0;
265
+ const o = G(e);
266
+ let r = 0;
218
267
  for (; ; ) {
219
268
  const s = await n.findMany({
220
269
  select: ["id", "contentType", "data"],
221
270
  where: { label: null },
222
- limit: de
271
+ limit: ye
223
272
  });
224
273
  if (s.length === 0) break;
225
- for (const r of s) {
226
- const c = await o(r.contentType, r.data);
274
+ for (const a of s) {
275
+ const l = await o(a.contentType, a.data);
227
276
  await n.update({
228
- where: { id: r.id },
229
- data: { label: c ?? "" }
230
- }), a += 1;
277
+ where: { id: a.id },
278
+ data: { label: l ?? "" }
279
+ }), r += 1;
231
280
  }
232
281
  }
233
- return e.log.info(`[rewind] labelled ${a} older version(s)`), a;
234
- }, M = { type: "core", key: "persisted_tables" }, fe = async (e, t) => {
235
- const n = await e.store.get(M) ?? [];
282
+ return e.log.info(`[rewind] labelled ${r} older version(s)`), r;
283
+ }, L = { type: "core", key: "persisted_tables" }, we = async (e, t) => {
284
+ const n = await e.store.get(L) ?? [];
236
285
  n.map((o) => typeof o == "string" ? o : o.name).includes(t) || await e.store.set({
237
- ...M,
286
+ ...L,
238
287
  value: [...n, { name: t }]
239
288
  });
240
- }, pe = [
289
+ }, be = [
241
290
  "create",
242
291
  "update",
243
292
  "clone",
244
293
  "publish",
245
294
  "unpublish",
246
295
  "discardDraft"
247
- ], me = async ({ strapi: e }) => {
296
+ ], Te = async ({ strapi: e }) => {
248
297
  const t = e.config.get("plugin::rewind");
249
- await fe(e, "rewind_versions"), await ye(e), ue(e, v).catch(
298
+ await we(e, "rewind_versions"), await De(e), he(e, D).catch(
250
299
  (o) => e.log.warn(
251
300
  `[rewind] could not label older versions: ${o instanceof Error ? o.message : String(o)}`
252
301
  )
253
- ), he(e, t);
302
+ ), Ae(e, t);
254
303
  const n = () => e.plugin("rewind").service("snapshot"), i = (o) => {
255
- if (!pe.includes(o.action)) return !1;
256
- const a = o.contentType?.uid ?? o.uid ?? "";
257
- return !(!a.startsWith("api::") || !t.contentTypes.includes(a) || le() || !t.trackApiWrites && !e.requestContext.get()?.request?.url?.startsWith("/content-manager"));
304
+ if (!be.includes(o.action)) return !1;
305
+ const r = o.contentType?.uid ?? o.uid ?? "";
306
+ return !(!r.startsWith("api::") || !t.contentTypes.includes(r) || ge() || !t.trackApiWrites && !e.requestContext.get()?.request?.url?.startsWith("/content-manager"));
258
307
  };
259
- e.documents.use(async (o, a) => {
260
- if (!i(o)) return a();
261
- const s = o.contentType?.uid ?? o.uid, r = o.params?.locale ?? null, c = o.action === "discardDraft" && o.params?.documentId ? await n().readRows(s, o.params.documentId, [r]) : null, l = await a(), d = o.action === "create" || o.action === "clone" ? l?.documentId : o.params?.documentId;
262
- if (!d) return l;
263
- const p = ge(e);
264
- return await e.db.transaction(async ({ trx: b, onCommit: m }) => {
265
- const u = re(b);
266
- u.intents.push({
308
+ e.documents.use(async (o, r) => {
309
+ if (!i(o)) return r();
310
+ const s = o.contentType?.uid ?? o.uid, a = o.params?.locale ?? null, l = o.action === "discardDraft" && o.params?.documentId ? await n().readRows(s, o.params.documentId, [a]) : null, d = await r(), c = o.action === "create" || o.action === "clone" ? d?.documentId : o.params?.documentId;
311
+ if (!c) return d;
312
+ const u = ve(e);
313
+ return await e.db.transaction(async ({ trx: f, onCommit: g }) => {
314
+ const y = fe(f);
315
+ y.intents.push({
267
316
  uid: s,
268
- relatedDocumentId: d,
269
- locale: r,
317
+ relatedDocumentId: c,
318
+ locale: a,
270
319
  origin: o.action,
271
- before: c
272
- }), !u.flushRegistered && (u.flushRegistered = !0, m(() => {
273
- const h = u.intents.splice(0, u.intents.length);
274
- n().captureAll(ae(h), p).catch(
275
- (f) => e.log.error("[rewind] failed to record a version", f)
320
+ before: l
321
+ }), !y.flushRegistered && (y.flushRegistered = !0, g(() => {
322
+ const T = y.intents.splice(0, y.intents.length);
323
+ n().captureAll(pe(T), u).catch(
324
+ (p) => e.log.error("[rewind] failed to record a version", p)
276
325
  );
277
326
  }));
278
- }), l;
327
+ }), d;
279
328
  });
280
- }, ge = (e) => e.requestContext.get()?.state?.user?.id ?? null, ye = async (e) => {
329
+ }, ve = (e) => e.requestContext.get()?.state?.user?.id ?? null, De = async (e) => {
281
330
  try {
282
- const { tableName: t } = e.db.metadata.get(v);
331
+ const { tableName: t } = e.db.metadata.get(D);
283
332
  await e.db.connection.raw(
284
333
  `CREATE INDEX IF NOT EXISTS idx_rewind_lookup
285
334
  ON ${t} (content_type, related_document_id, locale, created_at)`
@@ -289,7 +338,7 @@ const Q = ["id", "documentId", "locale"], S = [
289
338
  `[rewind] could not create the lookup index, queries will be slower: ${t instanceof Error ? t.message : String(t)}`
290
339
  );
291
340
  }
292
- }, he = (e, t) => {
341
+ }, Ae = (e, t) => {
293
342
  t.retention?.enabled !== !1 && e.cron.add({
294
343
  rewindPrune: {
295
344
  async task() {
@@ -309,7 +358,7 @@ const Q = ["id", "documentId", "locale"], S = [
309
358
  options: { rule: t.cron ?? "0 3 * * *" }
310
359
  }
311
360
  });
312
- }, we = {
361
+ }, Ie = {
313
362
  default: {
314
363
  /**
315
364
  * Content types to version, by uid. Empty means nothing is tracked.
@@ -360,10 +409,10 @@ const Q = ["id", "documentId", "locale"], S = [
360
409
  validator(e = {}) {
361
410
  const { contentTypes: t, retention: n } = e;
362
411
  if (n) {
363
- const { keepAllDays: o = 7, dailyUntilDays: a = 30, maxAgeDays: s = 365 } = n;
364
- if (!(o <= a && a <= s))
412
+ const { keepAllDays: o = 7, dailyUntilDays: r = 30, maxAgeDays: s = 365 } = n;
413
+ if (!(o <= r && r <= s))
365
414
  throw new Error(
366
- `[rewind] config.retention needs keepAllDays <= dailyUntilDays <= maxAgeDays (received ${o}, ${a}, ${s})`
415
+ `[rewind] config.retention needs keepAllDays <= dailyUntilDays <= maxAgeDays (received ${o}, ${r}, ${s})`
367
416
  );
368
417
  }
369
418
  if (t === void 0) return;
@@ -377,15 +426,15 @@ const Q = ["id", "documentId", "locale"], S = [
377
426
  `[rewind] config.contentTypes only supports api:: content types, received: ${i.join(", ")}`
378
427
  );
379
428
  }
380
- }, be = "collectionType", Te = "rewind_versions", ve = {
429
+ }, Oe = "collectionType", Se = "rewind_versions", ke = {
381
430
  singularName: "version",
382
431
  pluralName: "versions",
383
432
  displayName: "Rewind Version",
384
433
  description: "One snapshot of one document, in one locale, at one point in time."
385
- }, De = {
434
+ }, Ee = {
386
435
  draftAndPublish: !1,
387
436
  comment: "Managed by strapi-plugin-rewind. Do not edit manually."
388
- }, Ae = {
437
+ }, qe = {
389
438
  "content-manager": {
390
439
  visible: !1
391
440
  },
@@ -395,7 +444,7 @@ const Q = ["id", "documentId", "locale"], S = [
395
444
  i18n: {
396
445
  localized: !1
397
446
  }
398
- }, Ie = {
447
+ }, Re = {
399
448
  contentType: {
400
449
  type: "string",
401
450
  required: !0
@@ -454,18 +503,18 @@ const Q = ["id", "documentId", "locale"], S = [
454
503
  userId: {
455
504
  type: "integer"
456
505
  }
457
- }, Oe = {
458
- kind: be,
459
- collectionName: Te,
460
- info: ve,
461
- options: De,
462
- pluginOptions: Ae,
463
- attributes: Ie
464
- }, ke = {
465
- version: { schema: Oe }
466
- }, Se = ({ strapi: e }) => {
506
+ }, $e = {
507
+ kind: Oe,
508
+ collectionName: Se,
509
+ info: ke,
510
+ options: Ee,
511
+ pluginOptions: qe,
512
+ attributes: Re
513
+ }, Fe = {
514
+ version: { schema: $e }
515
+ }, Ne = ({ strapi: e }) => {
467
516
  e.cron.remove("rewindPrune");
468
- }, qe = [
517
+ }, _e = [
469
518
  "id",
470
519
  "contentType",
471
520
  "relatedDocumentId",
@@ -477,37 +526,39 @@ const Q = ["id", "documentId", "locale"], S = [
477
526
  "pinned",
478
527
  "contentHash",
479
528
  "createdAt"
480
- ], Ee = ({ strapi: e }) => ({
529
+ ], je = ({ strapi: e }) => ({
481
530
  async find(t) {
482
- const { contentType: n, documentId: i, locale: o, page: a = 1, pageSize: s = 10 } = t.query;
531
+ const { contentType: n, documentId: i, locale: o, page: r = 1, pageSize: s = 10 } = t.query;
483
532
  if (!n || !i)
484
533
  return t.badRequest("contentType and documentId are required.");
485
- const r = {
534
+ const a = {
486
535
  contentType: n,
487
536
  relatedDocumentId: i,
488
537
  ...o ? { locale: o } : {}
489
- }, c = Math.min(Number(s) || 10, 100), l = (Math.max(Number(a) || 1, 1) - 1) * c, [d, p] = await Promise.all([
490
- e.db.query(v).findMany({
491
- select: qe,
492
- where: r,
538
+ }, l = Math.min(Number(s) || 10, 100), d = (Math.max(Number(r) || 1, 1) - 1) * l, [c, u] = await Promise.all([
539
+ e.db.query(D).findMany({
540
+ select: _e,
541
+ where: a,
493
542
  orderBy: { createdAt: "desc" },
494
- limit: c,
495
- offset: l
543
+ limit: l,
544
+ offset: d
496
545
  }),
497
- e.db.query(v).count({ where: r })
546
+ e.db.query(D).count({ where: a })
498
547
  ]);
499
548
  t.body = {
500
- data: await U(e, d),
501
- meta: { page: Number(a) || 1, pageSize: c, total: p }
549
+ data: await U(e, c),
550
+ meta: { page: Number(r) || 1, pageSize: l, total: u }
502
551
  };
503
552
  },
504
553
  async findOne(t) {
505
- const n = await e.db.query(v).findOne({ where: { id: Number(t.params.id) } });
554
+ const n = await e.db.query(D).findOne({ where: { id: Number(t.params.id) } });
506
555
  if (!n) return t.notFound("No such version.");
507
556
  t.body = { data: (await U(e, [n]))[0] };
508
557
  },
509
558
  async diff(t) {
510
559
  const { against: n } = t.query;
560
+ if (n !== void 0 && !Number.isInteger(Number(n)))
561
+ return t.badRequest("against must be a version id.");
511
562
  try {
512
563
  t.body = {
513
564
  data: await e.plugin("rewind").service("diff").between(Number(t.params.id), n ? Number(n) : void 0)
@@ -554,22 +605,22 @@ const Q = ["id", "documentId", "locale"], S = [
554
605
  }
555
606
  }
556
607
  }), U = async (e, t) => {
557
- const n = [...new Set(t.map((a) => a.userId).filter(Boolean))];
558
- if (n.length === 0) return t.map((a) => ({ ...a, user: null }));
559
- const i = await e.db.query("admin::user").findMany({ select: ["id", "firstname", "lastname", "email"], where: { id: n } }), o = new Map(i.map((a) => [a.id, a]));
560
- return t.map((a) => {
561
- const s = o.get(a.userId);
608
+ const n = [...new Set(t.map((r) => r.userId).filter(Boolean))];
609
+ if (n.length === 0) return t.map((r) => ({ ...r, user: null }));
610
+ const i = await e.db.query("admin::user").findMany({ select: ["id", "firstname", "lastname", "email"], where: { id: n } }), o = new Map(i.map((r) => [r.id, r]));
611
+ return t.map((r) => {
612
+ const s = o.get(r.userId);
562
613
  return {
563
- ...a,
614
+ ...r,
564
615
  user: s ? {
565
616
  id: s.id,
566
617
  name: [s.firstname, s.lastname].filter(Boolean).join(" ") || s.email
567
618
  } : null
568
619
  };
569
620
  });
570
- }, Re = {
571
- version: Ee
572
- }, $e = {
621
+ }, Me = {
622
+ version: je
623
+ }, Ce = {
573
624
  type: "admin",
574
625
  routes: [
575
626
  {
@@ -609,11 +660,11 @@ const Q = ["id", "documentId", "locale"], S = [
609
660
  config: { policies: ["admin::isAuthenticatedAdmin"] }
610
661
  }
611
662
  ]
612
- }, Ne = {
613
- admin: $e
614
- }, B = (e) => e.match(/\S+\s*/g) ?? [], L = 2500, Fe = (e, t) => {
615
- const n = B(e), i = B(t);
616
- if (n.length > L || i.length > L)
663
+ }, Be = {
664
+ admin: Ce
665
+ }, z = (e) => e.match(/\S+\s*/g) ?? [], P = 2500, Le = (e, t) => {
666
+ const n = z(e), i = z(t);
667
+ if (n.length > P || i.length > P)
617
668
  return [
618
669
  { op: "removed", value: e },
619
670
  { op: "added", value: t }
@@ -622,20 +673,20 @@ const Q = ["id", "documentId", "locale"], S = [
622
673
  { length: n.length + 1 },
623
674
  () => new Array(i.length + 1).fill(0)
624
675
  );
625
- for (let l = n.length - 1; l >= 0; l -= 1)
626
- for (let d = i.length - 1; d >= 0; d -= 1)
627
- o[l][d] = n[l] === i[d] ? o[l + 1][d + 1] + 1 : Math.max(o[l + 1][d], o[l][d + 1]);
628
- const a = [], s = (l, d) => {
629
- const p = a[a.length - 1];
630
- p && p.op === l ? p.value += d : a.push({ op: l, value: d });
676
+ for (let d = n.length - 1; d >= 0; d -= 1)
677
+ for (let c = i.length - 1; c >= 0; c -= 1)
678
+ o[d][c] = n[d] === i[c] ? o[d + 1][c + 1] + 1 : Math.max(o[d + 1][c], o[d][c + 1]);
679
+ const r = [], s = (d, c) => {
680
+ const u = r[r.length - 1];
681
+ u && u.op === d ? u.value += c : r.push({ op: d, value: c });
631
682
  };
632
- let r = 0, c = 0;
633
- for (; r < n.length && c < i.length; )
634
- n[r] === i[c] ? (s("equal", n[r]), r += 1, c += 1) : o[r + 1][c] >= o[r][c + 1] ? (s("removed", n[r]), r += 1) : (s("added", i[c]), c += 1);
635
- for (; r < n.length; ) s("removed", n[r++]);
636
- for (; c < i.length; ) s("added", i[c++]);
637
- return a;
638
- }, _e = /* @__PURE__ */ new Set([
683
+ let a = 0, l = 0;
684
+ for (; a < n.length && l < i.length; )
685
+ n[a] === i[l] ? (s("equal", n[a]), a += 1, l += 1) : o[a + 1][l] >= o[a][l + 1] ? (s("removed", n[a]), a += 1) : (s("added", i[l]), l += 1);
686
+ for (; a < n.length; ) s("removed", n[a++]);
687
+ for (; l < i.length; ) s("added", i[l++]);
688
+ return r;
689
+ }, Ue = /* @__PURE__ */ new Set([
639
690
  "id",
640
691
  "__component",
641
692
  "type",
@@ -643,22 +694,22 @@ const Q = ["id", "documentId", "locale"], S = [
643
694
  "level",
644
695
  "language",
645
696
  "url"
646
- ]), q = (e, t = 0) => t > 12 || e == null ? "" : typeof e == "string" ? e : typeof e == "boolean" ? "" : typeof e == "number" ? String(e) : Array.isArray(e) ? e.map((n) => q(n, t + 1)).filter(Boolean).join(" ") : typeof e == "object" ? Object.entries(e).filter(([n]) => !_e.has(n)).map(([, n]) => q(n, t + 1)).filter(Boolean).join(" ") : "", z = (e) => e.replace(/\s+/g, " ").trim(), je = 6, Ce = (e, t = je) => {
697
+ ]), R = (e, t = 0) => t > 12 || e == null ? "" : typeof e == "string" ? e : typeof e == "boolean" ? "" : typeof e == "number" ? String(e) : Array.isArray(e) ? e.map((n) => R(n, t + 1)).filter(Boolean).join(" ") : typeof e == "object" ? Object.entries(e).filter(([n]) => !Ue.has(n)).map(([, n]) => R(n, t + 1)).filter(Boolean).join(" ") : "", Y = (e) => e.replace(/\s+/g, " ").trim(), ze = 6, Pe = (e, t = ze) => {
647
698
  const n = [];
648
699
  return e.forEach((i, o) => {
649
700
  if (i.op !== "equal") {
650
701
  n.push(i);
651
702
  return;
652
703
  }
653
- const a = i.value.match(/\S+\s*/g) ?? [];
654
- if (a.length <= t * 2 + 1) {
704
+ const r = i.value.match(/\S+\s*/g) ?? [];
705
+ if (r.length <= t * 2 + 1) {
655
706
  n.push(i);
656
707
  return;
657
708
  }
658
- const s = o === 0, r = o === e.length - 1, c = s ? [] : a.slice(0, t), l = r ? [] : a.slice(-t);
659
- c.length && n.push({ op: "equal", value: c.join("") }), n.push({ op: "equal", value: " … " }), l.length && n.push({ op: "equal", value: l.join("") });
709
+ const s = o === 0, a = o === e.length - 1, l = s ? [] : r.slice(0, t), d = a ? [] : r.slice(-t);
710
+ l.length && n.push({ op: "equal", value: l.join("") }), n.push({ op: "equal", value: " … " }), d.length && n.push({ op: "equal", value: d.join("") });
660
711
  }), n;
661
- }, Me = /* @__PURE__ */ new Set([
712
+ }, Ye = /* @__PURE__ */ new Set([
662
713
  "string",
663
714
  "text",
664
715
  "integer",
@@ -673,18 +724,18 @@ const Q = ["id", "documentId", "locale"], S = [
673
724
  "uid",
674
725
  "email",
675
726
  "password"
676
- ]), Ue = (e, t) => e === "text" || e === "richtext" || e === "blocks" || e === "json" || e === "customField" || e === "component" || e === "dynamiczone" || e === "string" && typeof t == "string" && t.length > 80, k = (e) => `${e.targetUid}:${e.documentId ?? e.id}`, Be = ({ strapi: e }) => {
727
+ ]), He = (e, t) => e === "text" || e === "richtext" || e === "blocks" || e === "json" || e === "customField" || e === "component" || e === "dynamiczone" || e === "string" && typeof t == "string" && t.length > 80, E = (e) => `${e.targetUid}:${e.documentId ?? e.id}`, We = ({ strapi: e }) => {
677
728
  const t = (o) => o && {
678
729
  id: o.id,
679
730
  label: o.label ?? null,
680
731
  createdAt: o.createdAt,
681
732
  origin: o.origin
682
733
  }, n = async (o) => {
683
- const a = await e.db.query(v).findOne({ where: { id: o } });
684
- if (!a) throw new Error(`No version with id ${o}`);
685
- return a;
734
+ const r = await e.db.query(D).findOne({ where: { id: o } });
735
+ if (!r) throw new Error(`No version with id ${o}`);
736
+ return r;
686
737
  }, i = async (o) => {
687
- const [a] = await e.db.query(v).findMany({
738
+ const [r] = await e.db.query(D).findMany({
688
739
  where: {
689
740
  contentType: o.contentType,
690
741
  relatedDocumentId: o.relatedDocumentId,
@@ -694,7 +745,7 @@ const Q = ["id", "documentId", "locale"], S = [
694
745
  orderBy: { id: "desc" },
695
746
  limit: 1
696
747
  });
697
- return a ?? null;
748
+ return r ?? null;
698
749
  };
699
750
  return {
700
751
  /**
@@ -705,51 +756,56 @@ const Q = ["id", "documentId", "locale"], S = [
705
756
  * did this edit do?". The other question, "what would restoring this
706
757
  * change?", is what `restore.preview()` already answers.
707
758
  */
708
- async between(o, a) {
709
- const s = await n(o), r = a ? await n(a) : await i(s), c = e.getModel(s.contentType)?.attributes, l = s.data ?? {}, d = r?.data ?? {}, p = s.relations ?? {}, b = r?.relations ?? {}, m = [];
710
- for (const u of /* @__PURE__ */ new Set([...Object.keys(d), ...Object.keys(l)])) {
711
- const h = d[u], f = l[u];
712
- if (JSON.stringify(h) === JSON.stringify(f)) continue;
713
- const w = c?.[u]?.type ?? "unknown", T = c?.[u]?.customField, g = h === void 0 ? "added" : f === void 0 ? "removed" : "changed";
714
- if (Me.has(w) && !Ue(w, h ?? f)) {
715
- m.push({ field: u, type: w, customField: T, kind: g, before: h, after: f });
759
+ async between(o, r) {
760
+ const s = await n(o), a = r ? await n(r) : null;
761
+ if (a && (a.contentType !== s.contentType || a.relatedDocumentId !== s.relatedDocumentId || a.locale !== s.locale))
762
+ throw new Error("Those two versions are not of the same document and locale.");
763
+ let l, d;
764
+ a ? (l = a.id < s.id ? a : s, d = a.id < s.id ? s : a) : (l = await i(s), d = s);
765
+ const c = e.getModel(d.contentType)?.attributes, u = d.data ?? {}, f = l?.data ?? {}, g = d.relations ?? {}, y = l?.relations ?? {}, T = [];
766
+ for (const p of /* @__PURE__ */ new Set([...Object.keys(f), ...Object.keys(u)])) {
767
+ const h = f[p], b = u[p];
768
+ if (JSON.stringify(h) === JSON.stringify(b)) continue;
769
+ const m = c?.[p]?.type ?? "unknown", v = c?.[p]?.customField, A = h === void 0 ? "added" : b === void 0 ? "removed" : "changed";
770
+ if (Ye.has(m) && !He(m, h ?? b)) {
771
+ T.push({ field: p, type: m, customField: v, kind: A, before: h, after: b });
716
772
  continue;
717
773
  }
718
- const D = z(q(h)), A = z(q(f));
719
- if (D === A) {
720
- m.push({ field: u, type: w, customField: T, kind: g, before: void 0, after: void 0 });
774
+ const I = Y(R(h)), O = Y(R(b));
775
+ if (I === O) {
776
+ T.push({ field: p, type: m, customField: v, kind: A, before: void 0, after: void 0 });
721
777
  continue;
722
778
  }
723
- m.push({
724
- field: u,
725
- type: w,
726
- customField: T,
727
- kind: g,
779
+ T.push({
780
+ field: p,
781
+ type: m,
782
+ customField: v,
783
+ kind: A,
728
784
  // Collapsed here rather than in the panel: sending an entire article
729
785
  // to highlight one word wastes the payload as well as the reader's
730
786
  // attention.
731
- spans: Ce(Fe(D, A))
787
+ spans: Pe(Le(I, O))
732
788
  });
733
789
  }
734
- for (const u of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(p)])) {
735
- const h = b[u] ?? [], f = p[u] ?? [], w = new Set(h.map(k)), T = new Set(f.map(k)), g = f.filter((A) => !w.has(k(A))), D = h.filter((A) => !T.has(k(A)));
736
- g.length === 0 && D.length === 0 || m.push({
737
- field: u,
738
- type: c?.[u]?.type ?? "relation",
790
+ for (const p of /* @__PURE__ */ new Set([...Object.keys(y), ...Object.keys(g)])) {
791
+ const h = y[p] ?? [], b = g[p] ?? [], m = new Set(h.map(E)), v = new Set(b.map(E)), A = b.filter((O) => !m.has(E(O))), I = h.filter((O) => !v.has(E(O)));
792
+ A.length === 0 && I.length === 0 || T.push({
793
+ field: p,
794
+ type: c?.[p]?.type ?? "relation",
739
795
  kind: "changed",
740
- linked: g,
741
- unlinked: D
796
+ linked: A,
797
+ unlinked: I
742
798
  });
743
799
  }
744
800
  return {
745
- from: r ? t(r) : null,
746
- to: t(s),
747
- changes: m,
748
- identical: m.length === 0
801
+ from: l ? t(l) : null,
802
+ to: t(d),
803
+ changes: T,
804
+ identical: T.length === 0
749
805
  };
750
806
  }
751
807
  };
752
- }, Le = ({ strapi: e }) => {
808
+ }, Ke = ({ strapi: e }) => {
753
809
  const t = (n) => ({
754
810
  type: "plugin",
755
811
  name: "rewind",
@@ -767,56 +823,56 @@ const Q = ["id", "documentId", "locale"], S = [
767
823
  await e.store.set({ ...t(n), value: { expires: 0 } });
768
824
  }
769
825
  };
770
- }, ze = ({ strapi: e }) => ({
826
+ }, Ge = ({ strapi: e }) => ({
771
827
  async set(t, n) {
772
828
  if (!Number.isInteger(t))
773
829
  throw new Error("A version id is required.");
774
- const i = await e.db.query(v).findOne({ where: { id: t }, select: ["id", "pinned"] });
830
+ const i = await e.db.query(D).findOne({ where: { id: t }, select: ["id", "pinned"] });
775
831
  if (!i)
776
832
  throw new Error(`No version with id ${t}`);
777
- return !!i.pinned === n ? { id: t, pinned: n } : (await e.db.query(v).update({ where: { id: t }, data: { pinned: n } }), { id: t, pinned: n });
833
+ return !!i.pinned === n ? { id: t, pinned: n } : (await e.db.query(D).update({ where: { id: t }, data: { pinned: n } }), { id: t, pinned: n });
778
834
  }
779
- }), K = {
835
+ }), J = {
780
836
  keepAllDays: 7,
781
837
  dailyUntilDays: 30,
782
838
  maxAgeDays: 365,
783
839
  keepAnchors: !0
784
- }, Pe = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), G = 24 * 60 * 60 * 1e3, Ye = (e, t) => (t.getTime() - new Date(e).getTime()) / G, He = (e) => e.toISOString().slice(0, 10), We = (e) => {
840
+ }, Ve = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), Z = 24 * 60 * 60 * 1e3, Xe = (e, t) => (t.getTime() - new Date(e).getTime()) / Z, Je = (e) => e.toISOString().slice(0, 10), Ze = (e) => {
785
841
  const t = new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate())), n = t.getUTCDay() || 7;
786
842
  t.setUTCDate(t.getUTCDate() + 4 - n);
787
- const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) / G + 1) / 7);
843
+ const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) / Z + 1) / 7);
788
844
  return `${t.getUTCFullYear()}-W${o}`;
789
- }, Ke = (e, t, n = K) => {
790
- const i = /* @__PURE__ */ new Map(), o = [], a = [...e].sort(
791
- (s, r) => new Date(r.createdAt).getTime() - new Date(s.createdAt).getTime()
845
+ }, Qe = (e, t, n = J) => {
846
+ const i = /* @__PURE__ */ new Map(), o = [], r = [...e].sort(
847
+ (s, a) => new Date(a.createdAt).getTime() - new Date(s.createdAt).getTime()
792
848
  );
793
- for (const s of a) {
794
- if (s.pinned || n.keepAnchors && Pe.has(s.origin)) continue;
795
- const r = Ye(s.createdAt, t);
796
- if (r <= n.keepAllDays) continue;
797
- if (r > n.maxAgeDays) {
849
+ for (const s of r) {
850
+ if (s.pinned || n.keepAnchors && Ve.has(s.origin)) continue;
851
+ const a = Xe(s.createdAt, t);
852
+ if (a <= n.keepAllDays) continue;
853
+ if (a > n.maxAgeDays) {
798
854
  o.push(s.id);
799
855
  continue;
800
856
  }
801
- const c = new Date(s.createdAt), l = r <= n.dailyUntilDays ? `d:${He(c)}` : `w:${We(c)}`;
802
- i.has(l) ? o.push(s.id) : i.set(l, s);
857
+ const l = new Date(s.createdAt), d = a <= n.dailyUntilDays ? `d:${Je(l)}` : `w:${Ze(l)}`;
858
+ i.has(d) ? o.push(s.id) : i.set(d, s);
803
859
  }
804
860
  return o;
805
- }, P = 500, Ge = ({ strapi: e }) => {
806
- const t = () => e.db.query(v), n = async (i) => {
861
+ }, H = 500, xe = ({ strapi: e }) => {
862
+ const t = () => e.db.query(D), n = async (i) => {
807
863
  const o = await t().findMany({
808
864
  select: ["contentType", "relatedDocumentId", "locale"],
809
865
  where: { createdAt: { $lt: i } }
810
- }), a = /* @__PURE__ */ new Map();
866
+ }), r = /* @__PURE__ */ new Map();
811
867
  for (const s of o) {
812
- const r = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
813
- a.has(r) || a.set(r, {
868
+ const a = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
869
+ r.has(a) || r.set(a, {
814
870
  contentType: s.contentType,
815
871
  relatedDocumentId: s.relatedDocumentId,
816
872
  locale: s.locale ?? null
817
873
  });
818
874
  }
819
- return [...a.values()];
875
+ return [...r.values()];
820
876
  };
821
877
  return {
822
878
  /**
@@ -826,37 +882,42 @@ const Q = ["id", "documentId", "locale"], S = [
826
882
  * report it and a test can assert on it.
827
883
  */
828
884
  async prune(i = /* @__PURE__ */ new Date()) {
829
- const o = e.config.get("plugin::rewind"), a = { ...K, ...o?.retention };
885
+ const o = e.config.get("plugin::rewind"), r = { ...J, ...o?.retention };
830
886
  if (o?.retention?.enabled === !1)
831
887
  return { scanned: 0, deleted: 0, documents: 0 };
832
- const s = new Date(i.getTime() - a.keepAllDays * 24 * 60 * 60 * 1e3), r = await n(s);
833
- let c = 0, l = 0;
834
- for (const d of r) {
835
- const p = await t().findMany({
888
+ const s = new Date(i.getTime() - r.keepAllDays * 24 * 60 * 60 * 1e3), a = await n(s);
889
+ let l = 0, d = 0;
890
+ for (const c of a) {
891
+ const u = await t().findMany({
836
892
  select: ["id", "createdAt", "origin", "pinned"],
837
893
  where: {
838
- contentType: d.contentType,
839
- relatedDocumentId: d.relatedDocumentId,
840
- locale: d.locale
894
+ contentType: c.contentType,
895
+ relatedDocumentId: c.relatedDocumentId,
896
+ locale: c.locale
841
897
  }
842
898
  });
843
- c += p.length;
844
- const b = Ke(p, i, a);
845
- for (let m = 0; m < b.length; m += P) {
846
- const u = b.slice(m, m + P);
847
- await t().deleteMany({ where: { id: { $in: u } } }), l += u.length;
899
+ l += u.length;
900
+ const f = Qe(u, i, r);
901
+ for (let g = 0; g < f.length; g += H) {
902
+ const y = f.slice(g, g + H);
903
+ await t().deleteMany({ where: { id: { $in: y } } }), d += y.length;
848
904
  }
849
905
  }
850
- return { scanned: c, deleted: l, documents: r.length };
906
+ return { scanned: l, deleted: d, documents: a.length };
851
907
  }
852
908
  };
853
- }, Ve = ({ strapi: e }) => {
909
+ }, et = (e) => {
910
+ const t = e.map((i) => i.label).filter(Boolean);
911
+ if (t.length === 0) return `${e.length} target(s)`;
912
+ const n = e.length - t.length;
913
+ return n ? `${t.join(", ")} and ${n} more` : t.join(", ");
914
+ }, tt = ({ strapi: e }) => {
854
915
  const t = (s) => e.getModel(s), n = async (s) => {
855
- const r = await e.db.query(v).findOne({ where: { id: s } });
856
- if (!r)
916
+ const a = await e.db.query(D).findOne({ where: { id: s } });
917
+ if (!a)
857
918
  throw new Error(`No version with id ${s}`);
858
- return r;
859
- }, i = (s) => s?.pluginOptions?.i18n?.localized === !0, o = (s) => e.contentTypes[s]?.pluginOptions?.i18n?.localized === !0, a = async (s) => s.targetUid === "plugin::upload.file" ? !!await e.db.query("plugin::upload.file").findOne({ where: { id: s.id } }) : !!await e.db.query(s.targetUid).findOne({
919
+ return a;
920
+ }, i = (s) => s?.pluginOptions?.i18n?.localized === !0, o = (s) => e.contentTypes[s]?.pluginOptions?.i18n?.localized === !0, r = async (s) => s.targetUid === "plugin::upload.file" ? !!await e.db.query("plugin::upload.file").findOne({ where: { id: s.id } }) : !!await e.db.query(s.targetUid).findOne({
860
921
  where: {
861
922
  documentId: s.documentId,
862
923
  ...s.locale ? { locale: s.locale } : {}
@@ -872,21 +933,21 @@ const Q = ["id", "documentId", "locale"], S = [
872
933
  * version of a document quietly rewrites a field in every other language.
873
934
  */
874
935
  async preview(s) {
875
- const r = await n(s), c = r.contentType, l = t(c).attributes, d = r.schemaSnapshot?.attributes ?? {}, p = r.data ?? {}, b = r.relations ?? {}, m = Object.keys(l).filter((y) => !S.includes(y)), u = Object.keys(d), h = u.filter((y) => !m.includes(y)), f = m.filter((y) => !u.includes(y)), w = u.filter((y) => m.includes(y)), T = w.filter((y) => $(l[y])), g = w.filter((y) => !T.includes(y)), D = o(c) ? g.filter((y) => !i(l[y])) : [], A = [], N = [];
876
- for (const [y, X] of Object.entries(b))
877
- if (g.includes(y))
878
- for (const O of X)
879
- await a(O) || (O.targetUid === "plugin::upload.file" ? N.push(O) : A.push(O));
880
- const V = D.length ? await e.plugin("i18n")?.service("locales").find() : [];
936
+ const a = await n(s), l = a.contentType, d = t(l).attributes, c = a.schemaSnapshot?.attributes ?? {}, u = a.data ?? {}, f = a.relations ?? {}, g = Object.keys(d).filter((w) => !q.includes(w)), y = Object.keys(c), T = y.filter((w) => !g.includes(w)), p = g.filter((w) => !y.includes(w)), h = y.filter((w) => g.includes(w)), b = h.filter((w) => $(d[w])), m = h.filter((w) => !b.includes(w)), v = o(l) ? m.filter((w) => !i(d[w])) : [], A = [], I = [];
937
+ for (const [w, Q] of Object.entries(f))
938
+ if (m.includes(w))
939
+ for (const k of Q)
940
+ await r(k) || (k.targetUid === "plugin::upload.file" ? I.push(k) : A.push(k));
941
+ const O = v.length ? await e.plugin("i18n")?.service("locales").find() : [];
881
942
  return {
882
- fieldsToRestore: g.filter((y) => y in p),
883
- fieldsKeptAsIs: f,
884
- fieldsDropped: h,
885
- crossLocaleFields: D,
886
- unsupportedFields: T,
943
+ fieldsToRestore: m.filter((w) => w in u),
944
+ fieldsKeptAsIs: p,
945
+ fieldsDropped: T,
946
+ crossLocaleFields: v,
947
+ unsupportedFields: b,
887
948
  brokenRelations: A,
888
- missingMedia: N,
889
- affectedLocales: V.map((y) => y.code)
949
+ missingMedia: I,
950
+ affectedLocales: O.map((w) => w.code)
890
951
  };
891
952
  },
892
953
  /**
@@ -895,69 +956,71 @@ const Q = ["id", "documentId", "locale"], S = [
895
956
  * The document lands in "modified" and publishing stays a deliberate act -
896
957
  * restoring must never change what the public sees on its own.
897
958
  */
898
- async apply(s, r) {
899
- const c = await n(s), l = c.contentType, d = await this.preview(s), p = [];
959
+ async apply(s, a) {
960
+ const l = await n(s), d = l.contentType, c = await this.preview(s), u = [];
900
961
  await e.plugin("rewind").service("snapshot").capture(
901
962
  {
902
- uid: l,
903
- relatedDocumentId: c.relatedDocumentId,
904
- locale: c.locale,
963
+ uid: d,
964
+ relatedDocumentId: l.relatedDocumentId,
965
+ locale: l.locale,
905
966
  origin: "restore",
906
967
  before: null
907
968
  },
908
- r
969
+ a
909
970
  );
910
- const b = {}, m = c.data ?? {}, u = c.relations ?? {};
911
- for (const f of d.fieldsToRestore)
912
- b[f] = m[f];
913
- const h = new Set(
914
- [...d.brokenRelations, ...d.missingMedia].map(
915
- (f) => `${f.targetUid}:${f.documentId ?? f.id}`
971
+ const f = {}, g = l.data ?? {}, y = l.relations ?? {};
972
+ for (const p of c.fieldsToRestore)
973
+ f[p] = g[p];
974
+ const T = new Set(
975
+ [...c.brokenRelations, ...c.missingMedia].map(
976
+ (p) => `${p.targetUid}:${p.documentId ?? p.id}`
916
977
  )
917
978
  );
918
- for (const [f, w] of Object.entries(u)) {
919
- if (d.unsupportedFields.includes(f) || d.fieldsDropped.includes(f)) continue;
920
- const T = w.filter(
921
- (g) => !h.has(`${g.targetUid}:${g.documentId ?? g.id}`)
979
+ for (const [p, h] of Object.entries(y)) {
980
+ if (c.unsupportedFields.includes(p) || c.fieldsDropped.includes(p)) continue;
981
+ const b = h.filter(
982
+ (m) => !T.has(`${m.targetUid}:${m.documentId ?? m.id}`)
922
983
  );
923
- T.length !== w.length && p.push(
924
- `${w.length - T.length} target(s) of "${f}" no longer exist and were skipped.`
925
- ), b[f] = T.map(
926
- (g) => g.targetUid === "plugin::upload.file" ? g.id : g.documentId
984
+ if (b.length !== h.length) {
985
+ const m = h.filter((v) => !b.includes(v));
986
+ u.push(`${et(m)} of "${p}" no longer exist and were skipped.`);
987
+ }
988
+ f[p] = b.map(
989
+ (m) => m.targetUid === "plugin::upload.file" ? m.id : m.documentId
927
990
  );
928
991
  }
929
- return d.fieldsDropped.length && p.push(
930
- `Skipped ${d.fieldsDropped.length} field(s) no longer in the model: ${d.fieldsDropped.join(", ")}.`
931
- ), d.crossLocaleFields.length && p.push(
932
- `${d.crossLocaleFields.join(", ")} are not localised, so they changed in every locale.`
933
- ), await ce(async () => {
934
- await e.documents(l).update({
935
- documentId: c.relatedDocumentId,
936
- locale: c.locale ?? void 0,
992
+ return c.fieldsDropped.length && u.push(
993
+ `Skipped ${c.fieldsDropped.length} field(s) no longer in the model: ${c.fieldsDropped.join(", ")}.`
994
+ ), c.crossLocaleFields.length && u.push(
995
+ `${c.crossLocaleFields.join(", ")} are not localised, so they changed in every locale.`
996
+ ), await me(async () => {
997
+ await e.documents(d).update({
998
+ documentId: l.relatedDocumentId,
999
+ locale: l.locale ?? void 0,
937
1000
  // Explicit, though update() forces draft anyway: passing 'published'
938
1001
  // here would publish, and that must never happen by accident.
939
1002
  status: "draft",
940
- data: b
1003
+ data: f
941
1004
  });
942
- }), { documentId: c.relatedDocumentId, warnings: p };
1005
+ }), { documentId: l.relatedDocumentId, warnings: u };
943
1006
  }
944
1007
  };
945
- }, Xe = {
946
- snapshot: ie,
947
- restore: Ve,
948
- diff: Be,
949
- retention: Ge,
950
- lock: Le,
951
- pin: ze
952
- }, Qe = {
953
- bootstrap: me,
954
- destroy: Se,
955
- config: we,
956
- contentTypes: ke,
957
- controllers: Re,
958
- routes: Ne,
959
- services: Xe
1008
+ }, nt = {
1009
+ snapshot: ue,
1010
+ restore: tt,
1011
+ diff: We,
1012
+ retention: xe,
1013
+ lock: Ke,
1014
+ pin: Ge
1015
+ }, it = {
1016
+ bootstrap: Te,
1017
+ destroy: Ne,
1018
+ config: Ie,
1019
+ contentTypes: Fe,
1020
+ controllers: Me,
1021
+ routes: Be,
1022
+ services: nt
960
1023
  };
961
1024
  export {
962
- Qe as default
1025
+ it as default
963
1026
  };