@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,30 @@
1
+ declare const _default: {
2
+ default: {
3
+ /**
4
+ * Content types Ferry will not carry, whatever the UI asks for.
5
+ *
6
+ * For anything that must not leave the environment it is in - a table of
7
+ * customer records, an integration's bookkeeping. The picker hides them and
8
+ * the endpoints refuse them, so a hidden thing cannot be exported by
9
+ * guessing its uid.
10
+ *
11
+ * ferry: {
12
+ * enabled: true,
13
+ * config: { exclude: ['api::invoice.invoice'] },
14
+ * }
15
+ */
16
+ exclude: string[];
17
+ /**
18
+ * The most documents one request will read out.
19
+ *
20
+ * An export holds its whole result in memory before writing it, so this is
21
+ * the honest limit rather than a policy: past it, use the filters.
22
+ */
23
+ maxExport: number;
24
+ };
25
+ validator(config: {
26
+ exclude?: unknown;
27
+ maxExport?: unknown;
28
+ }): void;
29
+ };
30
+ export default _default;
@@ -0,0 +1,12 @@
1
+ declare const _default: {
2
+ transfer: ({ strapi }: {
3
+ strapi: import("@strapi/types/dist/core").Strapi;
4
+ }) => {
5
+ catalogue(ctx: any): Promise<void>;
6
+ plan(ctx: any): Promise<void>;
7
+ export(ctx: any): Promise<void>;
8
+ preview(ctx: any): Promise<void>;
9
+ apply(ctx: any): Promise<void>;
10
+ };
11
+ };
12
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ declare const transfer: ({ strapi }: {
3
+ strapi: Core.Strapi;
4
+ }) => {
5
+ /** The content types Ferry can carry, for the picker. */
6
+ catalogue(ctx: any): Promise<void>;
7
+ /** What would travel, and what would not, before anyone commits to it. */
8
+ plan(ctx: any): Promise<void>;
9
+ export(ctx: any): Promise<void>;
10
+ /** The dry run. Reads everything, writes nothing. */
11
+ preview(ctx: any): Promise<void>;
12
+ apply(ctx: any): Promise<void>;
13
+ };
14
+ export default transfer;
@@ -0,0 +1,97 @@
1
+ declare const _default: {
2
+ register: ({ strapi }: {
3
+ strapi: import("@strapi/types/dist/core").Strapi;
4
+ }) => void;
5
+ config: {
6
+ default: {
7
+ exclude: string[];
8
+ maxExport: number;
9
+ };
10
+ validator(config: {
11
+ exclude?: unknown;
12
+ maxExport?: unknown;
13
+ }): void;
14
+ };
15
+ controllers: {
16
+ transfer: ({ strapi }: {
17
+ strapi: import("@strapi/types/dist/core").Strapi;
18
+ }) => {
19
+ catalogue(ctx: any): Promise<void>;
20
+ plan(ctx: any): Promise<void>;
21
+ export(ctx: any): Promise<void>;
22
+ preview(ctx: any): Promise<void>;
23
+ apply(ctx: any): Promise<void>;
24
+ };
25
+ };
26
+ policies: {
27
+ canExport: (policyContext: any) => any;
28
+ canImport: (policyContext: any) => any;
29
+ };
30
+ routes: {
31
+ admin: {
32
+ type: string;
33
+ routes: {
34
+ method: string;
35
+ path: string;
36
+ handler: string;
37
+ config: {
38
+ policies: string[];
39
+ };
40
+ }[];
41
+ };
42
+ };
43
+ services: {
44
+ schema: ({ strapi }: {
45
+ strapi: import("@strapi/types/dist/core").Strapi;
46
+ }) => {
47
+ model(uid: string): import("./services/schema").RawModel;
48
+ component(uid: string): import("./services/schema").RawModel;
49
+ plan(uid: string): import("./types").Plan;
50
+ componentPlan(uid: string): import("./types").Plan;
51
+ excluded(): Set<string>;
52
+ allowed(uid: string): boolean;
53
+ catalogue(): {
54
+ uid: string;
55
+ label: string;
56
+ kind: string;
57
+ count?: number;
58
+ }[];
59
+ };
60
+ exporter: ({ strapi }: {
61
+ strapi: import("@strapi/types/dist/core").Strapi;
62
+ }) => {
63
+ run(options: import("./types").ExportOptions): Promise<import("./services/export").ExportResult>;
64
+ readSingle(uid: string, query: Record<string, unknown>): Promise<any[]>;
65
+ };
66
+ importer: ({ strapi }: {
67
+ strapi: import("@strapi/types/dist/core").Strapi;
68
+ }) => {
69
+ preview(options: Partial<import("./types").ImportOptions> & {
70
+ uid: string;
71
+ }, parsed: import("./services/parse").Parsed): Promise<import("./types").Report>;
72
+ apply(options: Partial<import("./types").ImportOptions> & {
73
+ uid: string;
74
+ }, parsed: import("./services/parse").Parsed): Promise<import("./types").Report>;
75
+ run(options: Partial<import("./types").ImportOptions> & {
76
+ uid: string;
77
+ }, parsed: import("./services/parse").Parsed, apply: boolean): Promise<import("./types").Report>;
78
+ decide(entry: import("./services/import").Entry, plan: import("./types").Plan, existing: Set<string>, opts: import("./types").ImportOptions): "create" | "update" | "skip";
79
+ existingDocuments(plan: import("./types").Plan, opts: import("./types").ImportOptions, entries: import("./services/import").Entry[]): Promise<Set<string>>;
80
+ resolveRelations(plan: import("./types").Plan, opts: import("./types").ImportOptions, entries: import("./services/import").Entry[], report: import("./types").Report): Promise<Map<string, Set<string>>>;
81
+ resolveMedia(plan: import("./types").Plan, entries: import("./services/import").Entry[], report: import("./types").Report): Promise<Map<string, number>>;
82
+ content(entry: import("./services/import").Entry, plan: import("./types").Plan, media: Map<string, number>): Record<string, unknown>;
83
+ wiring(entry: import("./services/import").Entry, plan: import("./types").Plan, media: Map<string, number>, exists: (target: string, key: string) => boolean): Record<string, unknown>;
84
+ withMedia(data: Record<string, unknown>, plan: import("./types").Plan, media: Map<string, number>): Record<string, unknown>;
85
+ write(decided: {
86
+ entry: import("./services/import").Entry;
87
+ outcome: "create" | "update" | "skip";
88
+ }[], plan: import("./types").Plan, opts: import("./types").ImportOptions, context: {
89
+ media: Map<string, number>;
90
+ exists: (target: string, key: string) => boolean;
91
+ hasRelations: boolean;
92
+ }, report: import("./types").Report): Promise<void>;
93
+ singleTypeId(plan: import("./types").Plan, opts: import("./types").ImportOptions): Promise<string>;
94
+ };
95
+ };
96
+ };
97
+ export default _default;
@@ -0,0 +1,5 @@
1
+ declare const _default: {
2
+ canExport: (policyContext: any) => any;
3
+ canImport: (policyContext: any) => any;
4
+ };
5
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ /**
3
+ * Two permissions: taking content out, and putting content in.
4
+ *
5
+ * Kept apart because they are not the same risk. An export is a copy; an import
6
+ * replaces what is there. A role that may do the first has no automatic claim
7
+ * on the second.
8
+ */
9
+ declare const register: ({ strapi }: {
10
+ strapi: Core.Strapi;
11
+ }) => void;
12
+ export default register;
@@ -0,0 +1,12 @@
1
+ declare const _default: {
2
+ type: string;
3
+ routes: {
4
+ method: string;
5
+ path: string;
6
+ handler: string;
7
+ config: {
8
+ policies: string[];
9
+ };
10
+ }[];
11
+ };
12
+ export default _default;
@@ -0,0 +1,14 @@
1
+ declare const _default: {
2
+ admin: {
3
+ type: string;
4
+ routes: {
5
+ method: string;
6
+ path: string;
7
+ handler: string;
8
+ config: {
9
+ policies: string[];
10
+ };
11
+ }[];
12
+ };
13
+ };
14
+ export default _default;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * CSV, done properly, with no dependency.
3
+ *
4
+ * "Split on commas" handles the CSV nobody has, and the abandoned plugin in
5
+ * this niche has two open bug reports that read exactly like a split: a comma
6
+ * inside a title, a newline inside a body. RFC 4180 is small enough to
7
+ * implement once and be done with, so it is implemented here as a state machine
8
+ * over characters rather than as a regular expression that is nearly right.
9
+ */
10
+ declare const DELIMITERS: readonly [",", ";", "\t", "|"];
11
+ export type Delimiter = (typeof DELIMITERS)[number];
12
+ /**
13
+ * Guess the delimiter from the header line.
14
+ *
15
+ * A comma is the standard and a semicolon is what a spreadsheet saves in every
16
+ * locale that uses a comma as the decimal separator - which is most of Europe.
17
+ * Getting this wrong turns the whole file into one column, so it is worth the
18
+ * twenty lines: count candidates outside quotes and take the winner.
19
+ */
20
+ export declare const sniffDelimiter: (text: string) => Delimiter;
21
+ /**
22
+ * One value, as a cell.
23
+ *
24
+ * `null` and `undefined` both become empty, and that is a real loss of
25
+ * information the format cannot avoid: a CSV has no way to say "this field is
26
+ * null" as distinct from "this field is blank". The importer resolves the
27
+ * ambiguity in the safe direction by leaving blank cells alone rather than
28
+ * writing empty values over existing content. Anything structured is written as
29
+ * JSON, which survives the round trip and is at least readable in a cell.
30
+ */
31
+ export declare const cell: (value: unknown, delimiter: string) => string;
32
+ export interface EncodeOptions {
33
+ delimiter?: string;
34
+ /**
35
+ * Prefix the byte order mark. Excel opens a UTF-8 CSV as the local codepage
36
+ * without it, which turns every accented character into mojibake - so it is
37
+ * on by default and can be turned off for anything that is not a spreadsheet.
38
+ */
39
+ bom?: boolean;
40
+ /** Excel wants CRLF; everything else tolerates it. */
41
+ newline?: '\n' | '\r\n';
42
+ }
43
+ export declare const encode: (rows: Array<Record<string, unknown>>, columns: string[], options?: EncodeOptions) => string;
44
+ /**
45
+ * The parser.
46
+ *
47
+ * Character by character, tracking whether it is inside quotes, because that is
48
+ * the only thing that decides whether a delimiter or a newline is data or
49
+ * structure. A doubled quote inside a quoted field is one literal quote, which
50
+ * is the rule everyone forgets.
51
+ */
52
+ export declare const parse: (text: string, delimiter: string) => string[][];
53
+ export interface Decoded {
54
+ columns: string[];
55
+ /** Rows keyed by column name. Extra cells beyond the header are dropped. */
56
+ rows: Array<Record<string, string>>;
57
+ }
58
+ export declare const decode: (text: string, delimiter?: string) => Decoded;
59
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import type { ExportOptions } from '../types';
3
+ export interface ExportResult {
4
+ filename: string;
5
+ mime: string;
6
+ body: string;
7
+ count: number;
8
+ /** Things left out, said plainly rather than discovered later. */
9
+ warnings: string[];
10
+ }
11
+ export declare const DEFAULTS: Omit<ExportOptions, 'uid'>;
12
+ declare const exportService: ({ strapi }: {
13
+ strapi: Core.Strapi;
14
+ }) => {
15
+ /** Everything of one content type that matches, as a file. */
16
+ run(options: ExportOptions): Promise<ExportResult>;
17
+ /**
18
+ * A single type is one document, or none at all before anyone has saved it.
19
+ * The document service still answers `findFirst`, so the only difference
20
+ * from a collection is the shape of the answer.
21
+ */
22
+ readSingle(uid: string, query: Record<string, unknown>): Promise<any[]>;
23
+ };
24
+ export default exportService;
@@ -0,0 +1,78 @@
1
+ import type { Parsed } from './parse';
2
+ import type { Core } from '@strapi/strapi';
3
+ import type { ImportOptions, Plan, Report } from '../types';
4
+ export declare const DEFAULTS: Omit<ImportOptions, 'uid'>;
5
+ /**
6
+ * One row of the file, before anything has been decided about it.
7
+ *
8
+ * Exported because it turns up in the signatures of the service's own methods,
9
+ * and a name that a declaration file cannot reach fails the build rather than
10
+ * merely being inconvenient.
11
+ */
12
+ export interface Entry {
13
+ row: number;
14
+ documentId?: string;
15
+ data: Record<string, unknown>;
16
+ }
17
+ declare const importService: ({ strapi }: {
18
+ strapi: Core.Strapi;
19
+ }) => {
20
+ /**
21
+ * A dry run: the whole resolution, none of the writes.
22
+ *
23
+ * Same code path as the real thing, so what it promises is what happens.
24
+ */
25
+ preview(options: Partial<ImportOptions> & {
26
+ uid: string;
27
+ }, parsed: Parsed): Promise<Report>;
28
+ apply(options: Partial<ImportOptions> & {
29
+ uid: string;
30
+ }, parsed: Parsed): Promise<Report>;
31
+ run(options: Partial<ImportOptions> & {
32
+ uid: string;
33
+ }, parsed: Parsed, apply: boolean): Promise<Report>;
34
+ decide(entry: Entry, plan: Plan, existing: Set<string>, opts: ImportOptions): 'create' | 'update' | 'skip';
35
+ /** Which of the documents in the file are already here. */
36
+ existingDocuments(plan: Plan, opts: ImportOptions, entries: Entry[]): Promise<Set<string>>;
37
+ /**
38
+ * Which relation targets are really there.
39
+ *
40
+ * One query per target content type rather than one per key. A file of a
41
+ * thousand articles all pointing at the same twenty authors asks about
42
+ * twenty authors once, not a thousand times - and the abandoned plugin's
43
+ * habit of resolving inside the row loop is why importing anything sizeable
44
+ * takes minutes.
45
+ */
46
+ resolveRelations(plan: Plan, opts: ImportOptions, entries: Entry[], report: Report): Promise<Map<string, Set<string>>>;
47
+ /**
48
+ * Media, matched by hash.
49
+ *
50
+ * The hash is derived from the file's contents, so the same image uploaded
51
+ * to two environments matches even when the filenames and the ids differ.
52
+ * Ferry does not carry the bytes; when the upload is not there, the field is
53
+ * left unset and said so, rather than pointing at somebody else's file.
54
+ */
55
+ resolveMedia(plan: Plan, entries: Entry[], report: Report): Promise<Map<string, number>>;
56
+ /** The payload for the first pass: content, with nothing pointing outward. */
57
+ content(entry: Entry, plan: Plan, media: Map<string, number>): Record<string, unknown>;
58
+ /** The payload for the second pass: the same content, now wired up. */
59
+ wiring(entry: Entry, plan: Plan, media: Map<string, number>, exists: (target: string, key: string) => boolean): Record<string, unknown>;
60
+ withMedia(data: Record<string, unknown>, plan: Plan, media: Map<string, number>): Record<string, unknown>;
61
+ /**
62
+ * The writes, in two passes.
63
+ *
64
+ * Everything exists before anything points at it. There is no ordering of a
65
+ * single pass that works: two documents that refer to each other cannot both
66
+ * be written second.
67
+ */
68
+ write(decided: Array<{
69
+ entry: Entry;
70
+ outcome: 'create' | 'update' | 'skip';
71
+ }>, plan: Plan, opts: ImportOptions, context: {
72
+ media: Map<string, number>;
73
+ exists: (target: string, key: string) => boolean;
74
+ hasRelations: boolean;
75
+ }, report: Report): Promise<void>;
76
+ singleTypeId(plan: Plan, opts: ImportOptions): Promise<string | undefined>;
77
+ };
78
+ export default importService;
@@ -0,0 +1,54 @@
1
+ declare const _default: {
2
+ schema: ({ strapi }: {
3
+ strapi: import("@strapi/types/dist/core").Strapi;
4
+ }) => {
5
+ model(uid: string): import("./schema").RawModel;
6
+ component(uid: string): import("./schema").RawModel;
7
+ plan(uid: string): import("../types").Plan;
8
+ componentPlan(uid: string): import("../types").Plan;
9
+ excluded(): Set<string>;
10
+ allowed(uid: string): boolean;
11
+ catalogue(): {
12
+ uid: string;
13
+ label: string;
14
+ kind: string;
15
+ count?: number;
16
+ }[];
17
+ };
18
+ exporter: ({ strapi }: {
19
+ strapi: import("@strapi/types/dist/core").Strapi;
20
+ }) => {
21
+ run(options: import("../types").ExportOptions): Promise<import("./export").ExportResult>;
22
+ readSingle(uid: string, query: Record<string, unknown>): Promise<any[]>;
23
+ };
24
+ importer: ({ strapi }: {
25
+ strapi: import("@strapi/types/dist/core").Strapi;
26
+ }) => {
27
+ preview(options: Partial<import("../types").ImportOptions> & {
28
+ uid: string;
29
+ }, parsed: import("./parse").Parsed): Promise<import("../types").Report>;
30
+ apply(options: Partial<import("../types").ImportOptions> & {
31
+ uid: string;
32
+ }, parsed: import("./parse").Parsed): Promise<import("../types").Report>;
33
+ run(options: Partial<import("../types").ImportOptions> & {
34
+ uid: string;
35
+ }, parsed: import("./parse").Parsed, apply: boolean): Promise<import("../types").Report>;
36
+ decide(entry: import("./import").Entry, plan: import("../types").Plan, existing: Set<string>, opts: import("../types").ImportOptions): "create" | "update" | "skip";
37
+ existingDocuments(plan: import("../types").Plan, opts: import("../types").ImportOptions, entries: import("./import").Entry[]): Promise<Set<string>>;
38
+ resolveRelations(plan: import("../types").Plan, opts: import("../types").ImportOptions, entries: import("./import").Entry[], report: import("../types").Report): Promise<Map<string, Set<string>>>;
39
+ resolveMedia(plan: import("../types").Plan, entries: import("./import").Entry[], report: import("../types").Report): Promise<Map<string, number>>;
40
+ content(entry: import("./import").Entry, plan: import("../types").Plan, media: Map<string, number>): Record<string, unknown>;
41
+ wiring(entry: import("./import").Entry, plan: import("../types").Plan, media: Map<string, number>, exists: (target: string, key: string) => boolean): Record<string, unknown>;
42
+ withMedia(data: Record<string, unknown>, plan: import("../types").Plan, media: Map<string, number>): Record<string, unknown>;
43
+ write(decided: {
44
+ entry: import("./import").Entry;
45
+ outcome: "create" | "update" | "skip";
46
+ }[], plan: import("../types").Plan, opts: import("../types").ImportOptions, context: {
47
+ media: Map<string, number>;
48
+ exists: (target: string, key: string) => boolean;
49
+ hasRelations: boolean;
50
+ }, report: import("../types").Report): Promise<void>;
51
+ singleTypeId(plan: import("../types").Plan, opts: import("../types").ImportOptions): Promise<string>;
52
+ };
53
+ };
54
+ export default _default;
@@ -0,0 +1,38 @@
1
+ import type { Plan } from '../types';
2
+ /**
3
+ * Relations do not only live at the top of a document.
4
+ *
5
+ * A component can hold one, and a component inside a component inside a dynamic
6
+ * zone can hold one too. Strapi refuses the entire write when any of them
7
+ * points at something absent, so an importer that only looks at the document's
8
+ * own fields will fail on rows it never inspected and blame the row.
9
+ *
10
+ * These three walks are the same walk with different jobs: find the keys, take
11
+ * the relations out, put the resolvable ones back.
12
+ */
13
+ export interface FoundRelation {
14
+ /** Where it was, for a message a person can act on: `seo.author`. */
15
+ path: string;
16
+ target: string;
17
+ keys: string[];
18
+ }
19
+ type PlanFor = (componentUid: string) => Plan;
20
+ /** Every relation key in the document, however deep, with where it came from. */
21
+ export declare const collectRelations: (document: Record<string, unknown>, plan: Plan, planFor: PlanFor) => FoundRelation[];
22
+ /**
23
+ * A copy with every relation removed, at every depth.
24
+ *
25
+ * What the first pass writes. Every document in the file has to exist before
26
+ * any of them can point at another, so the first pass carries the content and
27
+ * the second pass carries the wiring.
28
+ */
29
+ export declare const stripRelations: (value: Record<string, unknown>, plan: Plan, planFor: PlanFor) => Record<string, unknown>;
30
+ /**
31
+ * A copy with the relations kept, minus the keys that lead nowhere.
32
+ *
33
+ * What the second pass writes. Dropping an unresolvable key rather than passing
34
+ * it on is the difference between a row that lands with one link missing and a
35
+ * row that Strapi refuses outright, taking the content with it.
36
+ */
37
+ export declare const filterRelations: (value: Record<string, unknown>, plan: Plan, planFor: PlanFor, exists: (target: string, key: string) => boolean) => Record<string, unknown>;
38
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { Format, Plan, Status } from '../types';
2
+ export interface Parsed {
3
+ /** What the file says it holds, when it says anything. */
4
+ contentType?: string;
5
+ status?: Status;
6
+ locale?: string;
7
+ documents: Array<Record<string, unknown>>;
8
+ warnings: string[];
9
+ }
10
+ /**
11
+ * A cell, back into the type the schema says it is.
12
+ *
13
+ * CSV has one type, and it is string. Everything a spreadsheet hands back has
14
+ * to be talked out of being text before Strapi will accept it, and the schema
15
+ * is the only thing that knows into what.
16
+ */
17
+ export declare const coerce: (raw: string, type: string) => unknown;
18
+ /**
19
+ * Read a file into documents, without deciding anything about them.
20
+ *
21
+ * Parsing and importing are kept apart so that a malformed file fails before
22
+ * the database is opened, and so the dry run and the real run read exactly the
23
+ * same thing.
24
+ */
25
+ export declare const parseFile: (text: string, format: Format, plan: Plan) => Parsed;
@@ -0,0 +1,20 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import type { Plan } from '../types';
3
+ /** Enough of a file to identify it on the far side without carrying the bytes. */
4
+ export declare const MEDIA_FIELDS: string[];
5
+ interface Options {
6
+ relations: boolean;
7
+ media: boolean;
8
+ }
9
+ /**
10
+ * The populate argument, built from the schema rather than guessed.
11
+ *
12
+ * This is the difference between an export that contains a document's
13
+ * components and one that contains `[object Object]`, and it is where a naive
14
+ * implementation reaches for `populate: '*'` - which goes exactly one level
15
+ * deep. One level is enough for a component and not enough for a component
16
+ * inside a component, so half of a real schema comes out empty and nobody
17
+ * notices until the import is missing content.
18
+ */
19
+ export declare const buildPopulate: (strapi: Core.Strapi, plan: Plan, options: Options, depth?: number) => Record<string, unknown>;
20
+ export {};
@@ -0,0 +1,81 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import type { Plan } from '../types';
3
+ export interface RawAttribute {
4
+ type: string;
5
+ relation?: string;
6
+ target?: string;
7
+ component?: string;
8
+ components?: string[];
9
+ repeatable?: boolean;
10
+ multiple?: boolean;
11
+ required?: boolean;
12
+ private?: boolean;
13
+ mappedBy?: string;
14
+ inversedBy?: string;
15
+ pluginOptions?: Record<string, any>;
16
+ }
17
+ export interface RawModel {
18
+ uid: string;
19
+ kind?: string;
20
+ modelType?: string;
21
+ options?: {
22
+ draftAndPublish?: boolean;
23
+ };
24
+ info?: {
25
+ displayName?: string;
26
+ singularName?: string;
27
+ };
28
+ attributes?: Record<string, RawAttribute>;
29
+ pluginOptions?: {
30
+ 'content-type-builder'?: {
31
+ visible?: boolean;
32
+ };
33
+ i18n?: {
34
+ localized?: boolean;
35
+ };
36
+ };
37
+ }
38
+ declare const schema: ({ strapi }: {
39
+ strapi: Core.Strapi;
40
+ }) => {
41
+ model(uid: string): RawModel | undefined;
42
+ component(uid: string): RawModel | undefined;
43
+ /**
44
+ * Sort one content type's attributes into how each has to be carried.
45
+ *
46
+ * Everything downstream - the populate call, the CSV header, the two import
47
+ * passes - reads this and nothing else, so there is exactly one place that
48
+ * decides what a field is.
49
+ */
50
+ plan(uid: string): Plan;
51
+ /**
52
+ * The same sorting for a component, which needs it too: components nest,
53
+ * and a component three levels down still has relations to resolve.
54
+ */
55
+ componentPlan(uid: string): Plan;
56
+ /** Uids the project has put out of bounds in its plugin config. */
57
+ excluded(): Set<string>;
58
+ /**
59
+ * Whether Ferry may touch this content type at all.
60
+ *
61
+ * Checked by the endpoints as well as the picker. A hidden thing that can
62
+ * still be fetched by typing its uid is not hidden, and the reason someone
63
+ * excludes a content type is usually that its contents must not leave.
64
+ */
65
+ allowed(uid: string): boolean;
66
+ /**
67
+ * What Ferry will carry, for the picker in the admin panel.
68
+ *
69
+ * Only the project's own types. Strapi's internal tables are real content
70
+ * types, but exporting the admin user table or the upload file index moves
71
+ * infrastructure rather than content, and importing it into another
72
+ * environment breaks that environment.
73
+ */
74
+ catalogue(): Array<{
75
+ uid: string;
76
+ label: string;
77
+ kind: string;
78
+ count?: number;
79
+ }>;
80
+ };
81
+ export default schema;
@@ -0,0 +1,14 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ import type { ExportedDocument, Plan } from '../types';
3
+ interface Options {
4
+ relations: boolean;
5
+ media: boolean;
6
+ }
7
+ export declare const makeSerialiser: (strapi: Core.Strapi) => {
8
+ document: (raw: any, plan: Plan, options: Options) => ExportedDocument;
9
+ componentValue: (value: any, plan: Plan, options: Options) => unknown;
10
+ zoneValue: (value: any, options: Options) => unknown[];
11
+ relationValue: (value: any, many: boolean) => unknown;
12
+ mediaValue: (value: any, many: boolean) => unknown;
13
+ };
14
+ export {};