@qkix/strapi-plugin-ferry 0.1.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.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/dist/_chunks/TransferPage-Bhp6vkZE.js +1 -0
  4. package/dist/_chunks/TransferPage-c8hdFQDQ.mjs +398 -0
  5. package/dist/admin/index.js +1 -0
  6. package/dist/admin/index.mjs +43 -0
  7. package/dist/admin/src/api.d.ts +114 -0
  8. package/dist/admin/src/components/ExportPanel.d.ts +7 -0
  9. package/dist/admin/src/components/ImportPanel.d.ts +15 -0
  10. package/dist/admin/src/components/ReportView.d.ts +6 -0
  11. package/dist/admin/src/index.d.ts +11 -0
  12. package/dist/admin/src/pages/TransferPage.d.ts +10 -0
  13. package/dist/admin/src/pluginId.d.ts +7 -0
  14. package/dist/server/index.js +6 -0
  15. package/dist/server/index.mjs +996 -0
  16. package/dist/server/src/config/index.d.ts +30 -0
  17. package/dist/server/src/controllers/index.d.ts +12 -0
  18. package/dist/server/src/controllers/transfer.d.ts +14 -0
  19. package/dist/server/src/index.d.ts +97 -0
  20. package/dist/server/src/policies/index.d.ts +5 -0
  21. package/dist/server/src/register.d.ts +12 -0
  22. package/dist/server/src/routes/admin.d.ts +12 -0
  23. package/dist/server/src/routes/index.d.ts +14 -0
  24. package/dist/server/src/services/csv.d.ts +59 -0
  25. package/dist/server/src/services/export.d.ts +24 -0
  26. package/dist/server/src/services/import.d.ts +78 -0
  27. package/dist/server/src/services/index.d.ts +54 -0
  28. package/dist/server/src/services/nested.d.ts +38 -0
  29. package/dist/server/src/services/parse.d.ts +25 -0
  30. package/dist/server/src/services/populate.d.ts +20 -0
  31. package/dist/server/src/services/schema.d.ts +81 -0
  32. package/dist/server/src/services/serialise.d.ts +14 -0
  33. package/dist/server/src/types.d.ts +145 -0
  34. package/dist/server/src/uids.d.ts +9 -0
  35. package/package.json +118 -0
@@ -0,0 +1,398 @@
1
+ import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
+ import * as d from "react";
3
+ import { useFetchClient as F, Page as N, Layouts as U } from "@strapi/admin/strapi-admin";
4
+ import { Flex as s, Typography as i, Field as p, SingleSelect as k, SingleSelectOption as v, Box as m, Checkbox as D, Button as B, Badge as P, Loader as V } from "@strapi/design-system";
5
+ import { Download as A, Upload as J } from "@strapi/icons";
6
+ import { PLUGIN_ID as O } from "../admin/index.mjs";
7
+ const j = {
8
+ contentTypes: `/${O}/content-types`,
9
+ plan: (n) => `/${O}/plan/${n}`,
10
+ export: `/${O}/export`,
11
+ preview: `/${O}/import/preview`,
12
+ apply: `/${O}/import`
13
+ }, Z = {
14
+ format: "json",
15
+ status: "draft",
16
+ relations: !0,
17
+ media: !0
18
+ }, _ = {
19
+ status: "draft",
20
+ onExisting: "update",
21
+ onMissingRelation: "skip",
22
+ publish: !0,
23
+ continueOnError: !1
24
+ }, z = (n, h, r) => {
25
+ const c = URL.createObjectURL(new Blob([n], { type: r })), u = document.createElement("a");
26
+ u.href = c, u.download = h, document.body.appendChild(u), u.click(), u.remove(), URL.revokeObjectURL(c);
27
+ }, G = (n) => [
28
+ n.created > 0 ? `${n.created} to create` : null,
29
+ n.updated > 0 ? `${n.updated} to update` : null,
30
+ n.skipped > 0 ? `${n.skipped} skipped` : null,
31
+ n.errored > 0 ? `${n.errored} failed` : null
32
+ ].filter(Boolean).join(", ") || "nothing to do", H = ({ contentTypes: n }) => {
33
+ const { get: h, post: r } = F(), [c, u] = d.useState(n[0]?.uid ?? ""), [g, f] = d.useState(Z), [o, b] = d.useState(null), [w, S] = d.useState(!1), [T, R] = d.useState([]), [I, E] = d.useState(null), [y, L] = d.useState(null);
34
+ d.useEffect(() => {
35
+ if (!c) return;
36
+ let a = !0;
37
+ return h(j.plan(c)).then(({ data: $ }) => a && b($)).catch(() => a && b(null)), () => {
38
+ a = !1;
39
+ };
40
+ }, [h, c]);
41
+ const l = (a) => f(($) => ({ ...$, ...a })), C = async () => {
42
+ S(!0), E(null), R([]), L(null);
43
+ try {
44
+ const { data: a } = await r(j.export, {
45
+ uid: c,
46
+ ...g,
47
+ envelope: !0
48
+ });
49
+ z(a.body, a.filename, a.mime), R(a.warnings ?? []), L(`${a.filename} (${a.count} document${a.count === 1 ? "" : "s"})`);
50
+ } catch (a) {
51
+ E(a?.response?.data?.error?.message ?? "The export failed.");
52
+ } finally {
53
+ S(!1);
54
+ }
55
+ }, x = o ? [
56
+ `${o.scalars.length} field(s)`,
57
+ g.relations && o.relations.length > 0 ? `${o.relations.length} relation(s)` : null,
58
+ o.components.length > 0 ? `${o.components.length} component(s)` : null,
59
+ o.dynamicZones.length > 0 ? `${o.dynamicZones.length} dynamic zone(s)` : null,
60
+ g.media && o.media.length > 0 ? `${o.media.length} media field(s)` : null
61
+ ].filter(Boolean) : [];
62
+ return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
63
+ /* @__PURE__ */ e(i, { variant: "delta", children: "Export" }),
64
+ /* @__PURE__ */ t(p.Root, { name: "export-uid", children: [
65
+ /* @__PURE__ */ e(p.Label, { children: "Content type" }),
66
+ /* @__PURE__ */ e(k, { value: c, onChange: (a) => u(String(a)), children: n.map((a) => /* @__PURE__ */ e(v, { value: a.uid, children: a.label }, a.uid)) })
67
+ ] }),
68
+ /* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
69
+ /* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "export-format", children: [
70
+ /* @__PURE__ */ e(p.Label, { children: "Format" }),
71
+ /* @__PURE__ */ t(
72
+ k,
73
+ {
74
+ value: g.format,
75
+ onChange: (a) => l({ format: a }),
76
+ children: [
77
+ /* @__PURE__ */ e(v, { value: "json", children: "JSON - everything" }),
78
+ /* @__PURE__ */ e(v, { value: "csv", children: "CSV - flat fields" })
79
+ ]
80
+ }
81
+ )
82
+ ] }) }),
83
+ /* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "export-status", children: [
84
+ /* @__PURE__ */ e(p.Label, { children: "Version" }),
85
+ /* @__PURE__ */ t(
86
+ k,
87
+ {
88
+ value: g.status,
89
+ onChange: (a) => l({ status: a }),
90
+ disabled: o ? !o.draftAndPublish : !1,
91
+ children: [
92
+ /* @__PURE__ */ e(v, { value: "draft", children: "Draft" }),
93
+ /* @__PURE__ */ e(v, { value: "published", children: "Published" })
94
+ ]
95
+ }
96
+ )
97
+ ] }) })
98
+ ] }),
99
+ /* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", children: [
100
+ /* @__PURE__ */ e(
101
+ D,
102
+ {
103
+ checked: g.relations,
104
+ onCheckedChange: (a) => l({ relations: !!a }),
105
+ children: "Relations"
106
+ }
107
+ ),
108
+ /* @__PURE__ */ e(
109
+ D,
110
+ {
111
+ checked: g.media,
112
+ onCheckedChange: (a) => l({ media: !!a }),
113
+ children: "Media references"
114
+ }
115
+ )
116
+ ] }),
117
+ x.length > 0 && /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: [
118
+ "Carries ",
119
+ x.join(", "),
120
+ ".",
121
+ g.format === "csv" && (o?.components.length || o?.dynamicZones.length) ? " CSV leaves components and dynamic zones behind - use JSON to keep them." : ""
122
+ ] }),
123
+ /* @__PURE__ */ e(m, { children: /* @__PURE__ */ e(B, { startIcon: /* @__PURE__ */ e(A, {}), onClick: C, loading: w, disabled: !c, children: "Export" }) }),
124
+ y && /* @__PURE__ */ t(i, { variant: "pi", textColor: "success600", children: [
125
+ "Saved ",
126
+ y,
127
+ "."
128
+ ] }),
129
+ T.map((a, $) => /* @__PURE__ */ e(i, { variant: "pi", textColor: "warning700", children: a }, $)),
130
+ I && /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: I })
131
+ ] });
132
+ }, W = ({ label: n, value: h, tone: r }) => /* @__PURE__ */ t(s, { direction: "column", gap: 1, alignItems: "flex-start", children: [
133
+ /* @__PURE__ */ e(i, { variant: "beta", textColor: h > 0 ? r : "neutral500", children: h }),
134
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: n })
135
+ ] }), K = ({ report: n }) => {
136
+ const h = n.rows.filter((r) => r.outcome === "error");
137
+ return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
138
+ /* @__PURE__ */ t(
139
+ m,
140
+ {
141
+ padding: 4,
142
+ hasRadius: !0,
143
+ background: "neutral0",
144
+ borderColor: "neutral200",
145
+ borderWidth: "1px",
146
+ borderStyle: "solid",
147
+ children: [
148
+ /* @__PURE__ */ t(s, { justifyContent: "space-between", alignItems: "center", paddingBottom: 3, children: [
149
+ /* @__PURE__ */ e(i, { variant: "delta", children: n.applied ? "Imported" : "Dry run - nothing written yet" }),
150
+ /* @__PURE__ */ e(P, { children: G(n) })
151
+ ] }),
152
+ /* @__PURE__ */ t(s, { gap: 8, wrap: "wrap", children: [
153
+ /* @__PURE__ */ e(W, { label: "in the file", value: n.total, tone: "neutral800" }),
154
+ /* @__PURE__ */ e(W, { label: "to create", value: n.created, tone: "success600" }),
155
+ /* @__PURE__ */ e(W, { label: "to update", value: n.updated, tone: "primary600" }),
156
+ /* @__PURE__ */ e(W, { label: "skipped", value: n.skipped, tone: "neutral600" }),
157
+ /* @__PURE__ */ e(W, { label: "failed", value: n.errored, tone: "danger600" })
158
+ ] })
159
+ ]
160
+ }
161
+ ),
162
+ n.warnings.map((r, c) => /* @__PURE__ */ e(
163
+ m,
164
+ {
165
+ padding: 3,
166
+ hasRadius: !0,
167
+ background: "warning100",
168
+ borderColor: "warning200",
169
+ borderWidth: "1px",
170
+ borderStyle: "solid",
171
+ children: /* @__PURE__ */ e(i, { variant: "pi", textColor: "warning700", children: r })
172
+ },
173
+ c
174
+ )),
175
+ n.unresolved.length > 0 && /* @__PURE__ */ t(
176
+ m,
177
+ {
178
+ padding: 4,
179
+ hasRadius: !0,
180
+ background: "neutral0",
181
+ borderColor: "warning200",
182
+ borderWidth: "1px",
183
+ borderStyle: "solid",
184
+ children: [
185
+ /* @__PURE__ */ e(i, { variant: "delta", children: "Relations that point at nothing" }),
186
+ /* @__PURE__ */ e(m, { paddingTop: 1, paddingBottom: 3, children: /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: 'These targets are neither in the file nor in this environment. With "drop the link" the rows still import without them; with "refuse the import" nothing is written at all.' }) }),
187
+ /* @__PURE__ */ t(s, { direction: "column", gap: 2, alignItems: "stretch", children: [
188
+ n.unresolved.slice(0, 25).map((r, c) => /* @__PURE__ */ t(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
189
+ /* @__PURE__ */ t(P, { children: [
190
+ "row ",
191
+ r.row
192
+ ] }),
193
+ /* @__PURE__ */ e(i, { variant: "omega", fontWeight: "bold", children: r.field }),
194
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: r.target }),
195
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: r.keys.join(", ") })
196
+ ] }, c)),
197
+ n.unresolved.length > 25 && /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: [
198
+ "and ",
199
+ n.unresolved.length - 25,
200
+ " more"
201
+ ] })
202
+ ] })
203
+ ]
204
+ }
205
+ ),
206
+ h.length > 0 && /* @__PURE__ */ t(
207
+ m,
208
+ {
209
+ padding: 4,
210
+ hasRadius: !0,
211
+ background: "neutral0",
212
+ borderColor: "danger200",
213
+ borderWidth: "1px",
214
+ borderStyle: "solid",
215
+ children: [
216
+ /* @__PURE__ */ e(i, { variant: "delta", textColor: "danger600", children: "Rows that failed" }),
217
+ /* @__PURE__ */ e(m, { paddingTop: 2, children: /* @__PURE__ */ e(s, { direction: "column", gap: 2, alignItems: "stretch", children: h.slice(0, 25).map((r) => /* @__PURE__ */ t(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
218
+ /* @__PURE__ */ t(P, { children: [
219
+ "row ",
220
+ r.row
221
+ ] }),
222
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: r.documentId ?? "no documentId" }),
223
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: r.message })
224
+ ] }, r.row)) }) })
225
+ ]
226
+ }
227
+ )
228
+ ] });
229
+ }, X = ({ contentTypes: n }) => {
230
+ const { post: h } = F(), [r, c] = d.useState(n[0]?.uid ?? ""), [u, g] = d.useState(_), [f, o] = d.useState(null), [b, w] = d.useState(null), [S, T] = d.useState(!1), [R, I] = d.useState(null), E = d.useRef(null), y = (l) => {
231
+ g((C) => ({ ...C, ...l })), w(null);
232
+ }, L = async (l) => {
233
+ if (!f) return;
234
+ T(!0), I(null);
235
+ const C = new FormData();
236
+ C.append("file", f), C.append("uid", r), Object.entries(u).forEach(([x, a]) => C.append(x, String(a)));
237
+ try {
238
+ const { data: x } = await h(l ? j.apply : j.preview, C);
239
+ w(x);
240
+ } catch (x) {
241
+ I(x?.response?.data?.error?.message ?? "The file could not be read."), w(null);
242
+ } finally {
243
+ T(!1);
244
+ }
245
+ };
246
+ return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
247
+ /* @__PURE__ */ e(i, { variant: "delta", children: "Import" }),
248
+ /* @__PURE__ */ t(p.Root, { name: "import-uid", children: [
249
+ /* @__PURE__ */ e(p.Label, { children: "Content type" }),
250
+ /* @__PURE__ */ e(
251
+ k,
252
+ {
253
+ value: r,
254
+ onChange: (l) => {
255
+ c(String(l)), w(null);
256
+ },
257
+ children: n.map((l) => /* @__PURE__ */ e(v, { value: l.uid, children: l.label }, l.uid))
258
+ }
259
+ )
260
+ ] }),
261
+ /* @__PURE__ */ t(p.Root, { name: "import-file", children: [
262
+ /* @__PURE__ */ e(p.Label, { children: "File" }),
263
+ /* @__PURE__ */ t(s, { gap: 2, alignItems: "center", children: [
264
+ /* @__PURE__ */ e(B, { variant: "secondary", onClick: () => E.current?.click(), children: "Choose a file" }),
265
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: f ? "neutral800" : "neutral500", children: f ? f.name : "JSON or CSV" })
266
+ ] }),
267
+ /* @__PURE__ */ e(
268
+ "input",
269
+ {
270
+ ref: E,
271
+ type: "file",
272
+ accept: ".json,.csv,application/json,text/csv",
273
+ style: { display: "none" },
274
+ onChange: (l) => {
275
+ o(l.target.files?.[0] ?? null), w(null);
276
+ }
277
+ }
278
+ )
279
+ ] }),
280
+ /* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
281
+ /* @__PURE__ */ e(m, { minWidth: "200px", children: /* @__PURE__ */ t(p.Root, { name: "import-existing", children: [
282
+ /* @__PURE__ */ e(p.Label, { children: "When it is already there" }),
283
+ /* @__PURE__ */ t(
284
+ k,
285
+ {
286
+ value: u.onExisting,
287
+ onChange: (l) => y({ onExisting: l === "skip" ? "skip" : "update" }),
288
+ children: [
289
+ /* @__PURE__ */ e(v, { value: "update", children: "Update it" }),
290
+ /* @__PURE__ */ e(v, { value: "skip", children: "Leave it alone" })
291
+ ]
292
+ }
293
+ )
294
+ ] }) }),
295
+ /* @__PURE__ */ e(m, { minWidth: "240px", children: /* @__PURE__ */ t(p.Root, { name: "import-missing", children: [
296
+ /* @__PURE__ */ e(p.Label, { children: "When a relation points at nothing" }),
297
+ /* @__PURE__ */ t(
298
+ k,
299
+ {
300
+ value: u.onMissingRelation,
301
+ onChange: (l) => y({ onMissingRelation: l === "fail" ? "fail" : "skip" }),
302
+ children: [
303
+ /* @__PURE__ */ e(v, { value: "skip", children: "Drop the link, keep the row" }),
304
+ /* @__PURE__ */ e(v, { value: "fail", children: "Refuse the import" })
305
+ ]
306
+ }
307
+ )
308
+ ] }) }),
309
+ /* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "import-status", children: [
310
+ /* @__PURE__ */ e(p.Label, { children: "Import as" }),
311
+ /* @__PURE__ */ t(
312
+ k,
313
+ {
314
+ value: u.status,
315
+ onChange: (l) => y({ status: l }),
316
+ children: [
317
+ /* @__PURE__ */ e(v, { value: "draft", children: "Draft" }),
318
+ /* @__PURE__ */ e(v, { value: "published", children: "Published" })
319
+ ]
320
+ }
321
+ )
322
+ ] }) })
323
+ ] }),
324
+ /* @__PURE__ */ e(
325
+ D,
326
+ {
327
+ checked: u.continueOnError,
328
+ onCheckedChange: (l) => y({ continueOnError: !!l }),
329
+ children: "Keep going past a row that fails"
330
+ }
331
+ ),
332
+ /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: u.continueOnError ? "Rows that fail are reported and the rest are committed." : "All or nothing: if any row fails, the whole import is rolled back and nothing changes." }),
333
+ /* @__PURE__ */ t(s, { gap: 2, children: [
334
+ /* @__PURE__ */ e(
335
+ B,
336
+ {
337
+ variant: "secondary",
338
+ startIcon: /* @__PURE__ */ e(J, {}),
339
+ onClick: () => L(!1),
340
+ loading: S,
341
+ disabled: !f || !r,
342
+ children: "Dry run"
343
+ }
344
+ ),
345
+ /* @__PURE__ */ e(
346
+ B,
347
+ {
348
+ onClick: () => L(!0),
349
+ loading: S,
350
+ disabled: !f || !r || !b || b.applied,
351
+ children: "Import for real"
352
+ }
353
+ )
354
+ ] }),
355
+ !b && f && /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: "Run the dry run first. Nothing is written until you have seen what it would do." }),
356
+ R && /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: R }),
357
+ b && /* @__PURE__ */ e(K, { report: b })
358
+ ] });
359
+ }, M = ({ children: n }) => /* @__PURE__ */ e(
360
+ m,
361
+ {
362
+ padding: 6,
363
+ hasRadius: !0,
364
+ background: "neutral0",
365
+ borderColor: "neutral200",
366
+ borderWidth: "1px",
367
+ borderStyle: "solid",
368
+ flex: "1",
369
+ minWidth: "380px",
370
+ children: n
371
+ }
372
+ ), te = () => {
373
+ const { get: n } = F(), [h, r] = d.useState([]), [c, u] = d.useState(!0), [g, f] = d.useState(null);
374
+ return d.useEffect(() => {
375
+ let o = !0;
376
+ return n(j.contentTypes).then(({ data: b }) => o && r(b.contentTypes)).catch(() => o && f("Could not read the list of content types.")).finally(() => o && u(!1)), () => {
377
+ o = !1;
378
+ };
379
+ }, [n]), /* @__PURE__ */ t(N.Main, { children: [
380
+ /* @__PURE__ */ e(
381
+ U.Header,
382
+ {
383
+ title: "Ferry",
384
+ subtitle: "Move content in and out as JSON or CSV, with relations and components intact"
385
+ }
386
+ ),
387
+ /* @__PURE__ */ e(U.Content, { children: c ? /* @__PURE__ */ e(s, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ e(V, { small: !0, children: "Reading the schema" }) }) : g ? /* @__PURE__ */ e(i, { textColor: "danger600", children: g }) : h.length === 0 ? /* @__PURE__ */ t(s, { direction: "column", gap: 2, padding: 10, alignItems: "center", children: [
388
+ /* @__PURE__ */ e(i, { variant: "delta", children: "Nothing to carry yet" }),
389
+ /* @__PURE__ */ e(i, { textColor: "neutral600", children: "Create a content type, and it will show up here." })
390
+ ] }) : /* @__PURE__ */ t(s, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
391
+ /* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(H, { contentTypes: h }) }),
392
+ /* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(X, { contentTypes: h }) })
393
+ ] }) })
394
+ ] });
395
+ };
396
+ export {
397
+ te as TransferPage
398
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const o=require("@strapi/icons"),a=(r,e,i)=>{const t=r[e];return t?typeof t=="function"?t():Promise.resolve(t):new Promise((l,s)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(s.bind(null,new Error("Unknown variable dynamic import: "+e+(e.split("/").length!==i?". Note that variables only represent file names one level deep.":""))))})},n="ferry",u={register(r){r.registerPlugin({id:n,name:n,isReady:!0}),r.addMenuLink({to:`/plugins/${n}`,icon:o.ArrowsHorizontal,intlLabel:{id:`${n}.menu.label`,defaultMessage:"Ferry"},Component:async()=>{const{TransferPage:e}=await Promise.resolve().then(()=>require("../_chunks/TransferPage-Bhp6vkZE.js"));return{default:e}},permissions:[{action:`plugin::${n}.export`,subject:null}]})},async registerTrads({locales:r}){return Promise.all(r.map(async e=>{try{const{default:i}=await a(Object.assign({}),`./translations/${e}.json`,3);return{data:i,locale:e}}catch{return{data:{},locale:e}}}))}};exports.PLUGIN_ID=n;exports.default=u;
@@ -0,0 +1,43 @@
1
+ import { ArrowsHorizontal as s } from "@strapi/icons";
2
+ const o = (n, e, i) => {
3
+ const t = n[e];
4
+ return t ? typeof t == "function" ? t() : Promise.resolve(t) : new Promise((u, a) => {
5
+ (typeof queueMicrotask == "function" ? queueMicrotask : setTimeout)(
6
+ a.bind(
7
+ null,
8
+ new Error(
9
+ "Unknown variable dynamic import: " + e + (e.split("/").length !== i ? ". Note that variables only represent file names one level deep." : "")
10
+ )
11
+ )
12
+ );
13
+ });
14
+ }, r = "ferry", c = {
15
+ register(n) {
16
+ n.registerPlugin({ id: r, name: r, isReady: !0 }), n.addMenuLink({
17
+ to: `/plugins/${r}`,
18
+ icon: s,
19
+ intlLabel: { id: `${r}.menu.label`, defaultMessage: "Ferry" },
20
+ Component: async () => {
21
+ const { TransferPage: e } = await import("../_chunks/TransferPage-c8hdFQDQ.mjs");
22
+ return { default: e };
23
+ },
24
+ permissions: [{ action: `plugin::${r}.export`, subject: null }]
25
+ });
26
+ },
27
+ async registerTrads({ locales: n }) {
28
+ return Promise.all(
29
+ n.map(async (e) => {
30
+ try {
31
+ const { default: i } = await o(/* @__PURE__ */ Object.assign({}), `./translations/${e}.json`, 3);
32
+ return { data: i, locale: e };
33
+ } catch {
34
+ return { data: {}, locale: e };
35
+ }
36
+ })
37
+ );
38
+ }
39
+ };
40
+ export {
41
+ r as PLUGIN_ID,
42
+ c as default
43
+ };
@@ -0,0 +1,114 @@
1
+ export type Format = 'json' | 'csv';
2
+ export type Status = 'draft' | 'published';
3
+ export interface CatalogueEntry {
4
+ uid: string;
5
+ label: string;
6
+ kind: 'collectionType' | 'singleType';
7
+ }
8
+ export interface FieldPlan {
9
+ uid: string;
10
+ kind: 'collectionType' | 'singleType';
11
+ draftAndPublish: boolean;
12
+ localized: boolean;
13
+ scalars: Array<{
14
+ name: string;
15
+ type: string;
16
+ }>;
17
+ relations: Array<{
18
+ name: string;
19
+ target: string;
20
+ many: boolean;
21
+ }>;
22
+ components: Array<{
23
+ name: string;
24
+ component: string;
25
+ repeatable: boolean;
26
+ }>;
27
+ dynamicZones: Array<{
28
+ name: string;
29
+ components: string[];
30
+ }>;
31
+ media: Array<{
32
+ name: string;
33
+ many: boolean;
34
+ }>;
35
+ skipped: Array<{
36
+ name: string;
37
+ reason: string;
38
+ }>;
39
+ }
40
+ export interface RowReport {
41
+ row: number;
42
+ documentId?: string;
43
+ outcome: 'create' | 'update' | 'skip' | 'error';
44
+ message?: string;
45
+ }
46
+ export interface UnresolvedRelation {
47
+ row: number;
48
+ documentId?: string;
49
+ field: string;
50
+ target: string;
51
+ keys: string[];
52
+ }
53
+ export interface Report {
54
+ contentType: string;
55
+ applied: boolean;
56
+ total: number;
57
+ created: number;
58
+ updated: number;
59
+ skipped: number;
60
+ errored: number;
61
+ rows: RowReport[];
62
+ unresolved: UnresolvedRelation[];
63
+ warnings: string[];
64
+ }
65
+ export declare const routes: {
66
+ contentTypes: string;
67
+ plan: (uid: string) => string;
68
+ export: string;
69
+ preview: string;
70
+ apply: string;
71
+ };
72
+ export interface ExportRequest {
73
+ uid: string;
74
+ format: Format;
75
+ status: Status;
76
+ relations: boolean;
77
+ media: boolean;
78
+ documentIds?: string[];
79
+ }
80
+ export interface ImportRequest {
81
+ uid: string;
82
+ status: Status;
83
+ onExisting: 'update' | 'skip';
84
+ onMissingRelation: 'skip' | 'fail';
85
+ publish: boolean;
86
+ continueOnError: boolean;
87
+ }
88
+ export declare const DEFAULT_EXPORT: Omit<ExportRequest, 'uid'>;
89
+ export declare const DEFAULT_IMPORT: Omit<ImportRequest, 'uid'>;
90
+ /**
91
+ * The export, as JSON with the file inside it.
92
+ *
93
+ * Asked for with `envelope: true`. The alternative is a real download, which is
94
+ * what a curl user wants and what the admin panel cannot use: Strapi's fetch
95
+ * client parses every response as JSON whatever `responseType` it is given.
96
+ */
97
+ export interface ExportEnvelope {
98
+ filename: string;
99
+ mime: string;
100
+ body: string;
101
+ count: number;
102
+ warnings: string[];
103
+ }
104
+ /**
105
+ * Hands the browser a file it already has.
106
+ *
107
+ * Not a link to the export route: an admin endpoint needs the session's
108
+ * Authorization header, and a plain navigation cannot send one - which is the
109
+ * single most reported fault against the plugin this replaces, where four of
110
+ * eleven open issues are a 401 on download.
111
+ */
112
+ export declare const saveFile: (body: BlobPart, filename: string, mime: string) => void;
113
+ /** A summary line for the report, in the order that matters when scanning it. */
114
+ export declare const summarise: (report: Report) => string;
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import type { CatalogueEntry } from '../api';
3
+ interface Props {
4
+ contentTypes: CatalogueEntry[];
5
+ }
6
+ export declare const ExportPanel: ({ contentTypes }: Props) => React.JSX.Element;
7
+ export default ExportPanel;
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import type { CatalogueEntry } from '../api';
3
+ interface Props {
4
+ contentTypes: CatalogueEntry[];
5
+ }
6
+ /**
7
+ * The import, in two deliberate steps.
8
+ *
9
+ * The file is read and resolved against the database first, and the button that
10
+ * writes only appears once there is a report to read. It is the same request
11
+ * either way, with one flag changed, so the preview cannot drift from what the
12
+ * import then does.
13
+ */
14
+ export declare const ImportPanel: ({ contentTypes }: Props) => React.JSX.Element;
15
+ export default ImportPanel;
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ import type { Report } from '../api';
3
+ export declare const ReportView: ({ report }: {
4
+ report: Report;
5
+ }) => React.JSX.Element;
6
+ export default ReportView;
@@ -0,0 +1,11 @@
1
+ declare const _default: {
2
+ register(app: any): void;
3
+ registerTrads({ locales }: {
4
+ locales: string[];
5
+ }): Promise<{
6
+ data: any;
7
+ locale: string;
8
+ }[]>;
9
+ };
10
+ export default _default;
11
+ export { PLUGIN_ID } from './pluginId';
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Both directions on one page.
4
+ *
5
+ * Not tabs: taking a copy out of one environment and putting it into another is
6
+ * one job done twice, and hiding half of it behind a tab makes the second half
7
+ * feel like a different tool.
8
+ */
9
+ export declare const TransferPage: () => React.JSX.Element;
10
+ export default TransferPage;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The plugin id, in one place.
3
+ *
4
+ * It appears in route paths, permission names and the admin panel's URL, so
5
+ * changing it later breaks installs rather than merely renaming a thing.
6
+ */
7
+ export declare const PLUGIN_ID = "ferry";