@qkix/strapi-plugin-rewind 0.3.0 → 0.4.1

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"], E = [
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"], E = [
10
33
  "strapi_assignee",
11
34
  "locale",
12
35
  "localizations"
13
- ], k = (e, t) => e.getModel(t), x = (e) => e?.type === "relation" || e?.type === "media", F = (e) => e?.type === "relation" && String(e.relation ?? "").toLowerCase().startsWith("morph"), _ = (e, t) => Object.entries(k(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, t, n = !0) => {
16
- const r = k(e, t), o = n ? "select" : "fields";
17
- return Object.entries(r.attributes).reduce(
18
- (a, [s, i]) => {
19
- switch (i.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 (F(i)) 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, i.component, n),
32
- [o]: _(e, i.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: (i.components ?? []).reduce(
39
- (c, l) => (c[l] = {
40
- populate: $(e, l, n),
41
- [o]: _(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,131 +104,126 @@ const Q = ["id", "documentId", "locale"], E = [
50
104
  },
51
105
  {}
52
106
  );
53
- }, ee = (e, t) => {
54
- const n = {}, r = (a) => {
55
- if (n[a]) return;
56
- const s = k(e, a).attributes;
57
- n[a] = N(s, E);
58
- for (const i of Object.values(s))
59
- if (i.type === "component" && r(i.component), i.type === "dynamiczone")
60
- for (const c of i.components ?? []) r(c);
61
- }, o = k(e, t).attributes;
62
- for (const a of Object.values(o))
63
- if (a.type === "component" && r(a.component), a.type === "dynamiczone")
64
- for (const s of a.components ?? []) r(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: N(o, E),
120
+ attributes: N(o, q),
67
121
  components: n
68
122
  };
69
- }, N = (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 r = k(e, t).attributes, o = {}, a = {}, s = N(n, [...E, ...Q]);
77
- for (const [i, c] of Object.entries(s)) {
78
- const l = r[i];
79
- if (l && x(l)) {
80
- if (F(l)) continue;
81
- const d = l.type === "media" ? "plugin::upload.file" : l.target;
82
- a[i] = 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[i] = l ? H(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
- }, H = (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 r = (o) => j(e, t.component, o);
92
- return Array.isArray(n) ? n.map((o) => r(o)) : r(n);
157
+ const i = (o) => M(e, t.component, o);
158
+ return Array.isArray(n) ? n.map((o) => i(o)) : i(n);
93
159
  }
94
- return t.type === "dynamiczone" && Array.isArray(n) ? n.map((r) => {
95
- const o = r;
96
- return j(e, o.__component, o);
160
+ return t.type === "dynamiczone" && Array.isArray(n) ? n.map((i) => {
161
+ const o = i;
162
+ return M(e, o.__component, o);
97
163
  }) : n;
98
- }, j = (e, t, n) => {
164
+ }, M = (e, t, n) => {
99
165
  if (!n || !t) return n;
100
- const r = k(e, t).attributes, o = {};
101
- for (const [a, s] of Object.entries(n)) {
102
- if (a === "id") continue;
103
- const i = r[a];
104
- o[a] = i ? H(e, i, 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
- }, W = (e) => {
108
- const t = /* @__PURE__ */ new Map(), n = async (r) => {
109
- if (t.has(r)) return t.get(r);
110
- let o;
111
- try {
112
- o = (await e.plugin("content-manager").service("content-types").findConfiguration(e.contentTypes[r]))?.settings?.mainField;
113
- } catch {
114
- }
115
- return t.set(r, o), o;
116
- };
117
- return async (r, o) => {
118
- if (!o) return null;
119
- const s = [await n(r), "title", "name", "label"].filter(Boolean);
120
- for (const i of s) {
121
- const c = o[i];
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 = W(e), n = (i) => !!e.contentTypes[i]?.options?.draftAndPublish, r = (i) => !!(e.getModel(i)?.pluginOptions?.i18n?.localized ?? e.contentTypes[i]?.pluginOptions?.i18n?.localized), o = async (i, c, l) => {
129
- const d = r(i) && l.filter(Boolean).length > 0 ? { locale: { $in: l.filter(Boolean) } } : {};
130
- return e.db.query(i).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(i) ? { publishedAt: null } : {}
181
+ documentId: u,
182
+ ...g,
183
+ ...r(c) ? { publishedAt: null } : {}
135
184
  },
136
- populate: $(e, i)
185
+ populate: F(e, c, !0, await o(c))
137
186
  });
138
- }, a = async (i, c) => {
187
+ }, l = async (c, u) => {
139
188
  try {
140
- const l = e.plugin("content-manager").service("document-metadata"), d = await l.getMetadata(i, 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 (i, c, l) => e.db.query(v).findOne({
146
- where: { contentType: i, 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(i, c) {
158
- for (const l of i)
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(i, c) {
162
- const { uid: l, relatedDocumentId: d, locale: f, origin: b } = i, h = i.before ?? await o(l, d, [f]);
163
- for (const m of h) {
164
- const T = m.locale ?? null, { data: u, relations: y, schemaSnapshot: w } = ne(e, l, m), p = se({ data: u, relations: y });
165
- !oe.has(b) && (await s(l, d, T))?.contentHash === p || 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: T,
170
- status: await a(l, m),
171
- label: await t(l, u),
172
- data: u,
173
- relations: y,
174
- schemaSnapshot: w,
175
- contentHash: p,
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
  });
@@ -189,97 +238,97 @@ const Q = ["id", "documentId", "locale"], E = [
189
238
  "clone",
190
239
  "create",
191
240
  "update"
192
- ], M = /* @__PURE__ */ new WeakMap(), re = (e) => {
193
- let t = M.get(e);
194
- return t || (t = { intents: [], flushRegistered: !1 }, M.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
- const r = `${n.uid}:${n.relatedDocumentId}:${n.locale ?? ""}`, o = t.get(r);
247
+ const i = `${n.uid}:${n.relatedDocumentId}:${n.locale ?? ""}`, o = t.get(i);
199
248
  if (!o) {
200
- t.set(r, n);
249
+ t.set(i, n);
201
250
  continue;
202
251
  }
203
- const a = C.indexOf(n.origin) < C.indexOf(o.origin);
204
- t.set(r, {
252
+ const r = C.indexOf(n.origin) < C.indexOf(o.origin);
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
- }, K = new Z(), ce = (e) => K.run({ active: !0 }, e), le = () => K.getStore()?.active === !0, de = 200, ue = async (e, t) => {
213
- const n = e.db.query(t), r = await n.count({ where: { label: null } });
214
- if (r === 0) return 0;
215
- e.log.info(`[rewind] labelling ${r} older version(s)…`);
216
- const o = W(e);
217
- let a = 0;
261
+ }, X = new ee(), me = (e) => X.run({ active: !0 }, e), ge = () => X.getStore()?.active === !0, ye = 200, he = async (e, t) => {
262
+ const n = e.db.query(t), i = await n.count({ where: { label: null } });
263
+ if (i === 0) return 0;
264
+ e.log.info(`[rewind] labelling ${i} older version(s)…`);
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 i of s) {
226
- const c = await o(i.contentType, i.data);
274
+ for (const a of s) {
275
+ const l = await o(a.contentType, a.data);
227
276
  await n.update({
228
- where: { id: i.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
- }, U = { type: "core", key: "persisted_tables" }, fe = async (e, t) => {
235
- const n = await e.store.get(U) ?? [];
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
- ...U,
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);
254
- const n = () => e.plugin("rewind").service("snapshot"), r = (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"));
302
+ ), Ae(e, t);
303
+ const n = () => e.plugin("rewind").service("snapshot"), i = (o) => {
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 (!r(o)) return a();
261
- const s = o.contentType?.uid ?? o.uid, i = o.params?.locale ?? null, c = o.action === "discardDraft" && o.params?.documentId ? await n().readRows(s, o.params.documentId, [i]) : null, l = await a(), d = o.action === "create" || o.action === "clone" ? l?.documentId : o.params?.documentId;
262
- if (!d) return l;
263
- const f = ge(e);
264
- return await e.db.transaction(async ({ trx: b, onCommit: h }) => {
265
- const m = re(b);
266
- m.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: i,
317
+ relatedDocumentId: c,
318
+ locale: a,
270
319
  origin: o.action,
271
- before: c
272
- }), !m.flushRegistered && (m.flushRegistered = !0, h(() => {
273
- const T = m.intents.splice(0, m.intents.length);
274
- n().captureAll(ae(T), f).catch(
275
- (u) => e.log.error("[rewind] failed to record a version", u)
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,19 +338,19 @@ const Q = ["id", "documentId", "locale"], E = [
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() {
296
345
  const n = e.plugin("rewind").service("lock");
297
346
  if (await n.acquire("prune", 30 * 60 * 1e3))
298
347
  try {
299
- const r = await e.plugin("rewind").service("retention").prune();
300
- r.deleted > 0 && e.log.info(
301
- `[rewind] thinned ${r.deleted} version(s) across ${r.documents} document(s)`
348
+ const i = await e.plugin("rewind").service("retention").prune();
349
+ i.deleted > 0 && e.log.info(
350
+ `[rewind] thinned ${i.deleted} version(s) across ${i.documents} document(s)`
302
351
  );
303
- } catch (r) {
304
- e.log.error("[rewind] thinning failed", r);
352
+ } catch (i) {
353
+ e.log.error("[rewind] thinning failed", i);
305
354
  } finally {
306
355
  await n.release("prune");
307
356
  }
@@ -309,7 +358,7 @@ const Q = ["id", "documentId", "locale"], E = [
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"], E = [
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;
@@ -371,21 +420,21 @@ const Q = ["id", "documentId", "locale"], E = [
371
420
  throw new Error(
372
421
  '[rewind] config.contentTypes must be an array of content type uids, e.g. ["api::article.article"]'
373
422
  );
374
- const r = t.filter((o) => !o.startsWith("api::"));
375
- if (r.length > 0)
423
+ const i = t.filter((o) => !o.startsWith("api::"));
424
+ if (i.length > 0)
376
425
  throw new Error(
377
- `[rewind] config.contentTypes only supports api:: content types, received: ${r.join(", ")}`
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"], E = [
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"], E = [
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,34 +526,34 @@ const Q = ["id", "documentId", "locale"], E = [
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: r, locale: o, page: a = 1, pageSize: s = 10 } = t.query;
483
- if (!n || !r)
531
+ const { contentType: n, documentId: i, locale: o, page: r = 1, pageSize: s = 10 } = t.query;
532
+ if (!n || !i)
484
533
  return t.badRequest("contentType and documentId are required.");
485
- const i = {
534
+ const a = {
486
535
  contentType: n,
487
- relatedDocumentId: r,
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, f] = await Promise.all([
490
- e.db.query(v).findMany({
491
- select: qe,
492
- where: i,
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: i })
546
+ e.db.query(D).count({ where: a })
498
547
  ]);
499
548
  t.body = {
500
- data: await B(e, d),
501
- meta: { page: Number(a) || 1, pageSize: c, total: f }
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
- t.body = { data: (await B(e, [n]))[0] };
556
+ t.body = { data: (await U(e, [n]))[0] };
508
557
  },
509
558
  async diff(t) {
510
559
  const { against: n } = t.query;
@@ -514,9 +563,9 @@ const Q = ["id", "documentId", "locale"], E = [
514
563
  t.body = {
515
564
  data: await e.plugin("rewind").service("diff").between(Number(t.params.id), n ? Number(n) : void 0)
516
565
  };
517
- } catch (r) {
566
+ } catch (i) {
518
567
  return t.badRequest(
519
- r instanceof Error ? r.message : "Could not compare these versions."
568
+ i instanceof Error ? i.message : "Could not compare these versions."
520
569
  );
521
570
  }
522
571
  },
@@ -542,8 +591,8 @@ const Q = ["id", "documentId", "locale"], E = [
542
591
  t.body = {
543
592
  data: await e.plugin("rewind").service("pin").set(Number(t.params.id), n)
544
593
  };
545
- } catch (r) {
546
- return t.notFound(r instanceof Error ? r.message : "No such version.");
594
+ } catch (i) {
595
+ return t.notFound(i instanceof Error ? i.message : "No such version.");
547
596
  }
548
597
  },
549
598
  async restore(t) {
@@ -555,23 +604,23 @@ const Q = ["id", "documentId", "locale"], E = [
555
604
  return t.badRequest(n instanceof Error ? n.message : "Restore failed.");
556
605
  }
557
606
  }
558
- }), B = async (e, t) => {
559
- const n = [...new Set(t.map((a) => a.userId).filter(Boolean))];
560
- if (n.length === 0) return t.map((a) => ({ ...a, user: null }));
561
- const r = await e.db.query("admin::user").findMany({ select: ["id", "firstname", "lastname", "email"], where: { id: n } }), o = new Map(r.map((a) => [a.id, a]));
562
- return t.map((a) => {
563
- const s = o.get(a.userId);
607
+ }), U = async (e, t) => {
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);
564
613
  return {
565
- ...a,
614
+ ...r,
566
615
  user: s ? {
567
616
  id: s.id,
568
617
  name: [s.firstname, s.lastname].filter(Boolean).join(" ") || s.email
569
618
  } : null
570
619
  };
571
620
  });
572
- }, Re = {
573
- version: Ee
574
- }, $e = {
621
+ }, Me = {
622
+ version: je
623
+ }, Ce = {
575
624
  type: "admin",
576
625
  routes: [
577
626
  {
@@ -611,33 +660,33 @@ const Q = ["id", "documentId", "locale"], E = [
611
660
  config: { policies: ["admin::isAuthenticatedAdmin"] }
612
661
  }
613
662
  ]
614
- }, Ne = {
615
- admin: $e
616
- }, L = (e) => e.match(/\S+\s*/g) ?? [], z = 2500, Fe = (e, t) => {
617
- const n = L(e), r = L(t);
618
- if (n.length > z || r.length > z)
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)
619
668
  return [
620
669
  { op: "removed", value: e },
621
670
  { op: "added", value: t }
622
671
  ];
623
672
  const o = Array.from(
624
673
  { length: n.length + 1 },
625
- () => new Array(r.length + 1).fill(0)
674
+ () => new Array(i.length + 1).fill(0)
626
675
  );
627
- for (let l = n.length - 1; l >= 0; l -= 1)
628
- for (let d = r.length - 1; d >= 0; d -= 1)
629
- o[l][d] = n[l] === r[d] ? o[l + 1][d + 1] + 1 : Math.max(o[l + 1][d], o[l][d + 1]);
630
- const a = [], s = (l, d) => {
631
- const f = a[a.length - 1];
632
- f && f.op === l ? f.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 });
633
682
  };
634
- let i = 0, c = 0;
635
- for (; i < n.length && c < r.length; )
636
- n[i] === r[c] ? (s("equal", n[i]), i += 1, c += 1) : o[i + 1][c] >= o[i][c + 1] ? (s("removed", n[i]), i += 1) : (s("added", r[c]), c += 1);
637
- for (; i < n.length; ) s("removed", n[i++]);
638
- for (; c < r.length; ) s("added", r[c++]);
639
- return a;
640
- }, _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([
641
690
  "id",
642
691
  "__component",
643
692
  "type",
@@ -645,22 +694,22 @@ const Q = ["id", "documentId", "locale"], E = [
645
694
  "level",
646
695
  "language",
647
696
  "url"
648
- ]), 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]) => !_e.has(n)).map(([, n]) => R(n, t + 1)).filter(Boolean).join(" ") : "", P = (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) => {
649
698
  const n = [];
650
- return e.forEach((r, o) => {
651
- if (r.op !== "equal") {
652
- n.push(r);
699
+ return e.forEach((i, o) => {
700
+ if (i.op !== "equal") {
701
+ n.push(i);
653
702
  return;
654
703
  }
655
- const a = r.value.match(/\S+\s*/g) ?? [];
656
- if (a.length <= t * 2 + 1) {
657
- n.push(r);
704
+ const r = i.value.match(/\S+\s*/g) ?? [];
705
+ if (r.length <= t * 2 + 1) {
706
+ n.push(i);
658
707
  return;
659
708
  }
660
- const s = o === 0, i = o === e.length - 1, c = s ? [] : a.slice(0, t), l = i ? [] : a.slice(-t);
661
- 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("") });
662
711
  }), n;
663
- }, Me = /* @__PURE__ */ new Set([
712
+ }, Ye = /* @__PURE__ */ new Set([
664
713
  "string",
665
714
  "text",
666
715
  "integer",
@@ -675,18 +724,18 @@ const Q = ["id", "documentId", "locale"], E = [
675
724
  "uid",
676
725
  "email",
677
726
  "password"
678
- ]), 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, q = (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 }) => {
679
728
  const t = (o) => o && {
680
729
  id: o.id,
681
730
  label: o.label ?? null,
682
731
  createdAt: o.createdAt,
683
732
  origin: o.origin
684
733
  }, n = async (o) => {
685
- const a = await e.db.query(v).findOne({ where: { id: o } });
686
- if (!a) throw new Error(`No version with id ${o}`);
687
- return a;
688
- }, r = async (o) => {
689
- const [a] = await e.db.query(v).findMany({
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;
737
+ }, i = async (o) => {
738
+ const [r] = await e.db.query(D).findMany({
690
739
  where: {
691
740
  contentType: o.contentType,
692
741
  relatedDocumentId: o.relatedDocumentId,
@@ -696,7 +745,7 @@ const Q = ["id", "documentId", "locale"], E = [
696
745
  orderBy: { id: "desc" },
697
746
  limit: 1
698
747
  });
699
- return a ?? null;
748
+ return r ?? null;
700
749
  };
701
750
  return {
702
751
  /**
@@ -707,123 +756,123 @@ const Q = ["id", "documentId", "locale"], E = [
707
756
  * did this edit do?". The other question, "what would restoring this
708
757
  * change?", is what `restore.preview()` already answers.
709
758
  */
710
- async between(o, a) {
711
- const s = await n(o), i = a ? await n(a) : null;
712
- if (i && (i.contentType !== s.contentType || i.relatedDocumentId !== s.relatedDocumentId || i.locale !== s.locale))
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))
713
762
  throw new Error("Those two versions are not of the same document and locale.");
714
- let c, l;
715
- i ? (c = i.id < s.id ? i : s, l = i.id < s.id ? s : i) : (c = await r(s), l = s);
716
- const d = e.getModel(l.contentType)?.attributes, f = l.data ?? {}, b = c?.data ?? {}, h = l.relations ?? {}, m = c?.relations ?? {}, T = [];
717
- for (const u of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(f)])) {
718
- const y = b[u], w = f[u];
719
- if (JSON.stringify(y) === JSON.stringify(w)) continue;
720
- const p = d?.[u]?.type ?? "unknown", D = d?.[u]?.customField, A = y === void 0 ? "added" : w === void 0 ? "removed" : "changed";
721
- if (Me.has(p) && !Ue(p, y ?? w)) {
722
- T.push({ field: u, type: p, customField: D, kind: A, before: y, after: w });
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 });
723
772
  continue;
724
773
  }
725
- const I = P(R(y)), O = P(R(w));
774
+ const I = Y(R(h)), O = Y(R(b));
726
775
  if (I === O) {
727
- T.push({ field: u, type: p, customField: D, kind: A, before: void 0, after: void 0 });
776
+ T.push({ field: p, type: m, customField: v, kind: A, before: void 0, after: void 0 });
728
777
  continue;
729
778
  }
730
779
  T.push({
731
- field: u,
732
- type: p,
733
- customField: D,
780
+ field: p,
781
+ type: m,
782
+ customField: v,
734
783
  kind: A,
735
784
  // Collapsed here rather than in the panel: sending an entire article
736
785
  // to highlight one word wastes the payload as well as the reader's
737
786
  // attention.
738
- spans: Ce(Fe(I, O))
787
+ spans: Pe(Le(I, O))
739
788
  });
740
789
  }
741
- for (const u of /* @__PURE__ */ new Set([...Object.keys(m), ...Object.keys(h)])) {
742
- const y = m[u] ?? [], w = h[u] ?? [], p = new Set(y.map(q)), D = new Set(w.map(q)), A = w.filter((O) => !p.has(q(O))), I = y.filter((O) => !D.has(q(O)));
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)));
743
792
  A.length === 0 && I.length === 0 || T.push({
744
- field: u,
745
- type: d?.[u]?.type ?? "relation",
793
+ field: p,
794
+ type: c?.[p]?.type ?? "relation",
746
795
  kind: "changed",
747
796
  linked: A,
748
797
  unlinked: I
749
798
  });
750
799
  }
751
800
  return {
752
- from: c ? t(c) : null,
753
- to: t(l),
801
+ from: l ? t(l) : null,
802
+ to: t(d),
754
803
  changes: T,
755
804
  identical: T.length === 0
756
805
  };
757
806
  }
758
807
  };
759
- }, Le = ({ strapi: e }) => {
808
+ }, Ke = ({ strapi: e }) => {
760
809
  const t = (n) => ({
761
810
  type: "plugin",
762
811
  name: "rewind",
763
812
  key: `lock:${n}`
764
813
  });
765
814
  return {
766
- async acquire(n, r) {
815
+ async acquire(n, i) {
767
816
  const o = await e.store.get(t(n));
768
817
  return o && o.expires > Date.now() ? !1 : (await e.store.set({
769
818
  ...t(n),
770
- value: { expires: Date.now() + r }
819
+ value: { expires: Date.now() + i }
771
820
  }), !0);
772
821
  },
773
822
  async release(n) {
774
823
  await e.store.set({ ...t(n), value: { expires: 0 } });
775
824
  }
776
825
  };
777
- }, ze = ({ strapi: e }) => ({
826
+ }, Ge = ({ strapi: e }) => ({
778
827
  async set(t, n) {
779
828
  if (!Number.isInteger(t))
780
829
  throw new Error("A version id is required.");
781
- const r = await e.db.query(v).findOne({ where: { id: t }, select: ["id", "pinned"] });
782
- if (!r)
830
+ const i = await e.db.query(D).findOne({ where: { id: t }, select: ["id", "pinned"] });
831
+ if (!i)
783
832
  throw new Error(`No version with id ${t}`);
784
- return !!r.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 });
785
834
  }
786
- }), G = {
835
+ }), J = {
787
836
  keepAllDays: 7,
788
837
  dailyUntilDays: 30,
789
838
  maxAgeDays: 365,
790
839
  keepAnchors: !0
791
- }, Pe = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), V = 24 * 60 * 60 * 1e3, Ye = (e, t) => (t.getTime() - new Date(e).getTime()) / V, 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) => {
792
841
  const t = new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate())), n = t.getUTCDay() || 7;
793
842
  t.setUTCDate(t.getUTCDate() + 4 - n);
794
- const r = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - r.getTime()) / V + 1) / 7);
843
+ const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) / Z + 1) / 7);
795
844
  return `${t.getUTCFullYear()}-W${o}`;
796
- }, Ke = (e, t, n = G) => {
797
- const r = /* @__PURE__ */ new Map(), o = [], a = [...e].sort(
798
- (s, i) => new Date(i.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()
799
848
  );
800
- for (const s of a) {
801
- if (s.pinned || n.keepAnchors && Pe.has(s.origin)) continue;
802
- const i = Ye(s.createdAt, t);
803
- if (i <= n.keepAllDays) continue;
804
- if (i > 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) {
805
854
  o.push(s.id);
806
855
  continue;
807
856
  }
808
- const c = new Date(s.createdAt), l = i <= n.dailyUntilDays ? `d:${He(c)}` : `w:${We(c)}`;
809
- r.has(l) ? o.push(s.id) : r.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);
810
859
  }
811
860
  return o;
812
- }, Y = 500, Ge = ({ strapi: e }) => {
813
- const t = () => e.db.query(v), n = async (r) => {
861
+ }, H = 500, xe = ({ strapi: e }) => {
862
+ const t = () => e.db.query(D), n = async (i) => {
814
863
  const o = await t().findMany({
815
864
  select: ["contentType", "relatedDocumentId", "locale"],
816
- where: { createdAt: { $lt: r } }
817
- }), a = /* @__PURE__ */ new Map();
865
+ where: { createdAt: { $lt: i } }
866
+ }), r = /* @__PURE__ */ new Map();
818
867
  for (const s of o) {
819
- const i = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
820
- a.has(i) || a.set(i, {
868
+ const a = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
869
+ r.has(a) || r.set(a, {
821
870
  contentType: s.contentType,
822
871
  relatedDocumentId: s.relatedDocumentId,
823
872
  locale: s.locale ?? null
824
873
  });
825
874
  }
826
- return [...a.values()];
875
+ return [...r.values()];
827
876
  };
828
877
  return {
829
878
  /**
@@ -832,38 +881,43 @@ const Q = ["id", "documentId", "locale"], E = [
832
881
  * Returns what it did rather than logging and forgetting, so the cron can
833
882
  * report it and a test can assert on it.
834
883
  */
835
- async prune(r = /* @__PURE__ */ new Date()) {
836
- const o = e.config.get("plugin::rewind"), a = { ...G, ...o?.retention };
884
+ async prune(i = /* @__PURE__ */ new Date()) {
885
+ const o = e.config.get("plugin::rewind"), r = { ...J, ...o?.retention };
837
886
  if (o?.retention?.enabled === !1)
838
887
  return { scanned: 0, deleted: 0, documents: 0 };
839
- const s = new Date(r.getTime() - a.keepAllDays * 24 * 60 * 60 * 1e3), i = await n(s);
840
- let c = 0, l = 0;
841
- for (const d of i) {
842
- const f = 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({
843
892
  select: ["id", "createdAt", "origin", "pinned"],
844
893
  where: {
845
- contentType: d.contentType,
846
- relatedDocumentId: d.relatedDocumentId,
847
- locale: d.locale
894
+ contentType: c.contentType,
895
+ relatedDocumentId: c.relatedDocumentId,
896
+ locale: c.locale
848
897
  }
849
898
  });
850
- c += f.length;
851
- const b = Ke(f, r, a);
852
- for (let h = 0; h < b.length; h += Y) {
853
- const m = b.slice(h, h + Y);
854
- await t().deleteMany({ where: { id: { $in: m } } }), l += m.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;
855
904
  }
856
905
  }
857
- return { scanned: c, deleted: l, documents: i.length };
906
+ return { scanned: l, deleted: d, documents: a.length };
858
907
  }
859
908
  };
860
- }, 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 }) => {
861
915
  const t = (s) => e.getModel(s), n = async (s) => {
862
- const i = await e.db.query(v).findOne({ where: { id: s } });
863
- if (!i)
916
+ const a = await e.db.query(D).findOne({ where: { id: s } });
917
+ if (!a)
864
918
  throw new Error(`No version with id ${s}`);
865
- return i;
866
- }, r = (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({
867
921
  where: {
868
922
  documentId: s.documentId,
869
923
  ...s.locale ? { locale: s.locale } : {}
@@ -879,21 +933,21 @@ const Q = ["id", "documentId", "locale"], E = [
879
933
  * version of a document quietly rewrites a field in every other language.
880
934
  */
881
935
  async preview(s) {
882
- const i = await n(s), c = i.contentType, l = t(c).attributes, d = i.schemaSnapshot?.attributes ?? {}, f = i.data ?? {}, b = i.relations ?? {}, h = Object.keys(l).filter((g) => !E.includes(g)), m = Object.keys(d), T = m.filter((g) => !h.includes(g)), u = h.filter((g) => !m.includes(g)), y = m.filter((g) => h.includes(g)), w = y.filter((g) => F(l[g])), p = y.filter((g) => !w.includes(g)), D = o(c) ? p.filter((g) => !r(l[g])) : [], A = [], I = [];
883
- for (const [g, X] of Object.entries(b))
884
- if (p.includes(g))
885
- for (const S of X)
886
- await a(S) || (S.targetUid === "plugin::upload.file" ? I.push(S) : A.push(S));
887
- const O = 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() : [];
888
942
  return {
889
- fieldsToRestore: p.filter((g) => g in f),
890
- fieldsKeptAsIs: u,
943
+ fieldsToRestore: m.filter((w) => w in u),
944
+ fieldsKeptAsIs: p,
891
945
  fieldsDropped: T,
892
- crossLocaleFields: D,
893
- unsupportedFields: w,
946
+ crossLocaleFields: v,
947
+ unsupportedFields: b,
894
948
  brokenRelations: A,
895
949
  missingMedia: I,
896
- affectedLocales: O.map((g) => g.code)
950
+ affectedLocales: O.map((w) => w.code)
897
951
  };
898
952
  },
899
953
  /**
@@ -902,69 +956,71 @@ const Q = ["id", "documentId", "locale"], E = [
902
956
  * The document lands in "modified" and publishing stays a deliberate act -
903
957
  * restoring must never change what the public sees on its own.
904
958
  */
905
- async apply(s, i) {
906
- const c = await n(s), l = c.contentType, d = await this.preview(s), f = [];
959
+ async apply(s, a) {
960
+ const l = await n(s), d = l.contentType, c = await this.preview(s), u = [];
907
961
  await e.plugin("rewind").service("snapshot").capture(
908
962
  {
909
- uid: l,
910
- relatedDocumentId: c.relatedDocumentId,
911
- locale: c.locale,
963
+ uid: d,
964
+ relatedDocumentId: l.relatedDocumentId,
965
+ locale: l.locale,
912
966
  origin: "restore",
913
967
  before: null
914
968
  },
915
- i
969
+ a
916
970
  );
917
- const b = {}, h = c.data ?? {}, m = c.relations ?? {};
918
- for (const u of d.fieldsToRestore)
919
- b[u] = h[u];
971
+ const f = {}, g = l.data ?? {}, y = l.relations ?? {};
972
+ for (const p of c.fieldsToRestore)
973
+ f[p] = g[p];
920
974
  const T = new Set(
921
- [...d.brokenRelations, ...d.missingMedia].map(
922
- (u) => `${u.targetUid}:${u.documentId ?? u.id}`
975
+ [...c.brokenRelations, ...c.missingMedia].map(
976
+ (p) => `${p.targetUid}:${p.documentId ?? p.id}`
923
977
  )
924
978
  );
925
- for (const [u, y] of Object.entries(m)) {
926
- if (d.unsupportedFields.includes(u) || d.fieldsDropped.includes(u)) continue;
927
- const w = y.filter(
928
- (p) => !T.has(`${p.targetUid}:${p.documentId ?? p.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}`)
929
983
  );
930
- w.length !== y.length && f.push(
931
- `${y.length - w.length} target(s) of "${u}" no longer exist and were skipped.`
932
- ), b[u] = w.map(
933
- (p) => p.targetUid === "plugin::upload.file" ? p.id : p.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
934
990
  );
935
991
  }
936
- return d.fieldsDropped.length && f.push(
937
- `Skipped ${d.fieldsDropped.length} field(s) no longer in the model: ${d.fieldsDropped.join(", ")}.`
938
- ), d.crossLocaleFields.length && f.push(
939
- `${d.crossLocaleFields.join(", ")} are not localised, so they changed in every locale.`
940
- ), await ce(async () => {
941
- await e.documents(l).update({
942
- documentId: c.relatedDocumentId,
943
- 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,
944
1000
  // Explicit, though update() forces draft anyway: passing 'published'
945
1001
  // here would publish, and that must never happen by accident.
946
1002
  status: "draft",
947
- data: b
1003
+ data: f
948
1004
  });
949
- }), { documentId: c.relatedDocumentId, warnings: f };
1005
+ }), { documentId: l.relatedDocumentId, warnings: u };
950
1006
  }
951
1007
  };
952
- }, Xe = {
953
- snapshot: ie,
954
- restore: Ve,
955
- diff: Be,
956
- retention: Ge,
957
- lock: Le,
958
- pin: ze
959
- }, Qe = {
960
- bootstrap: me,
961
- destroy: Se,
962
- config: we,
963
- contentTypes: ke,
964
- controllers: Re,
965
- routes: Ne,
966
- 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
967
1023
  };
968
1024
  export {
969
- Qe as default
1025
+ it as default
970
1026
  };