@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,145 @@
1
+ /** What a file is written as, and read back from. */
2
+ export type Format = 'json' | 'csv';
3
+ /** Which version of a document to move. */
4
+ export type Status = 'draft' | 'published';
5
+ /**
6
+ * A field, sorted into the one bucket that decides how it travels.
7
+ *
8
+ * The buckets are not cosmetic. A scalar is copied, a relation is a key that
9
+ * has to be resolved on the far side, a component is nested data with no
10
+ * identity of its own, and a media field is a pointer to a binary this plugin
11
+ * does not carry. Code that treats them alike is code that loses one of them.
12
+ */
13
+ export interface ScalarField {
14
+ name: string;
15
+ type: string;
16
+ }
17
+ export interface RelationField {
18
+ name: string;
19
+ target: string;
20
+ many: boolean;
21
+ }
22
+ export interface ComponentField {
23
+ name: string;
24
+ component: string;
25
+ repeatable: boolean;
26
+ }
27
+ export interface DynamicZoneField {
28
+ name: string;
29
+ components: string[];
30
+ }
31
+ export interface MediaField {
32
+ name: string;
33
+ many: boolean;
34
+ }
35
+ /** Everything Ferry needs to know about one content type, worked out once. */
36
+ export interface Plan {
37
+ uid: string;
38
+ kind: 'collectionType' | 'singleType';
39
+ draftAndPublish: boolean;
40
+ localized: boolean;
41
+ scalars: ScalarField[];
42
+ relations: RelationField[];
43
+ components: ComponentField[];
44
+ dynamicZones: DynamicZoneField[];
45
+ media: MediaField[];
46
+ /** Field names deliberately left out, and why - surfaced in the UI. */
47
+ skipped: Array<{
48
+ name: string;
49
+ reason: string;
50
+ }>;
51
+ }
52
+ export interface ExportOptions {
53
+ uid: string;
54
+ format: Format;
55
+ status: Status;
56
+ locale?: string;
57
+ /** Export exactly these documents. Used by the Content Manager list view. */
58
+ documentIds?: string[];
59
+ /** Content Manager filters, passed through untouched. */
60
+ filters?: Record<string, unknown>;
61
+ sort?: string;
62
+ limit?: number;
63
+ relations: boolean;
64
+ media: boolean;
65
+ }
66
+ /** A document as it appears in a file: plain data, keyed by documentId. */
67
+ export type ExportedDocument = Record<string, unknown> & {
68
+ documentId: string;
69
+ };
70
+ /**
71
+ * The file itself.
72
+ *
73
+ * Deliberately without a timestamp. An export is content, and content that
74
+ * changes on every run cannot be committed to a repository and diffed, which is
75
+ * the whole point of being able to read it. Git already knows when the file
76
+ * arrived.
77
+ */
78
+ export interface Archive {
79
+ /** Format version, so a future reader can refuse a file it cannot read. */
80
+ ferry: 1;
81
+ contentType: string;
82
+ status: Status;
83
+ locale?: string;
84
+ count: number;
85
+ documents: ExportedDocument[];
86
+ }
87
+ export type RowOutcome = 'create' | 'update' | 'skip' | 'error';
88
+ export interface RowReport {
89
+ /** 1-based position in the file, so a message can name a line. */
90
+ row: number;
91
+ documentId?: string;
92
+ outcome: RowOutcome;
93
+ message?: string;
94
+ }
95
+ export interface UnresolvedRelation {
96
+ row: number;
97
+ documentId?: string;
98
+ field: string;
99
+ target: string;
100
+ /** The keys that point at nothing, capped for display. */
101
+ keys: string[];
102
+ }
103
+ /**
104
+ * What a dry run says, and what an applied import reports afterwards.
105
+ *
106
+ * The same shape both times on purpose: the preview and the receipt are the
107
+ * same document, so what you approved is what you can check against.
108
+ */
109
+ export interface Report {
110
+ contentType: string;
111
+ /** False for a dry run - nothing was written. */
112
+ applied: boolean;
113
+ total: number;
114
+ created: number;
115
+ updated: number;
116
+ skipped: number;
117
+ errored: number;
118
+ rows: RowReport[];
119
+ unresolved: UnresolvedRelation[];
120
+ warnings: string[];
121
+ }
122
+ export interface ImportOptions {
123
+ uid: string;
124
+ status: Status;
125
+ locale?: string;
126
+ /** What to do when a document with this documentId is already there. */
127
+ onExisting: 'update' | 'skip';
128
+ /**
129
+ * What to do when a relation points at a document that is not in the file and
130
+ * not in the database. Dropping the field keeps the row; failing keeps the
131
+ * content honest. Neither is right for everyone, so it is a choice.
132
+ */
133
+ onMissingRelation: 'skip' | 'fail';
134
+ /** Publish what the file recorded as published. */
135
+ publish: boolean;
136
+ /**
137
+ * Keep going past a row that fails, and commit the rest.
138
+ *
139
+ * Off by default, which makes an import all or nothing: a run that hits an
140
+ * error rolls back and the database is exactly as it was. A half-applied
141
+ * import is the worst outcome available - worse than a failed one, because
142
+ * nobody knows which half - so opting into it has to be deliberate.
143
+ */
144
+ continueOnError: boolean;
145
+ }
@@ -0,0 +1,9 @@
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";
8
+ /** Strapi's own file model. Media fields are relations to this. */
9
+ export declare const UPLOAD_FILE = "plugin::upload.file";
package/package.json ADDED
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "@qkix/strapi-plugin-ferry",
3
+ "version": "0.1.0",
4
+ "description": "Import and export Strapi v5 content as JSON or CSV - relations, components and dynamic zones included, with a dry run before anything is written",
5
+ "strapi": {
6
+ "kind": "plugin",
7
+ "name": "ferry",
8
+ "displayName": "Ferry",
9
+ "description": "Import and export Strapi v5 content as JSON or CSV, with a dry run before anything is written"
10
+ },
11
+ "keywords": [
12
+ "strapi",
13
+ "strapi-plugin",
14
+ "strapi-v5",
15
+ "import",
16
+ "export",
17
+ "csv",
18
+ "json",
19
+ "migration",
20
+ "seed",
21
+ "backup",
22
+ "cms",
23
+ "headless-cms"
24
+ ],
25
+ "type": "commonjs",
26
+ "exports": {
27
+ "./package.json": "./package.json",
28
+ "./strapi-admin": {
29
+ "types": "./dist/admin/src/index.d.ts",
30
+ "source": "./admin/src/index.ts",
31
+ "import": "./dist/admin/index.mjs",
32
+ "require": "./dist/admin/index.js",
33
+ "default": "./dist/admin/index.js"
34
+ },
35
+ "./strapi-server": {
36
+ "types": "./dist/server/src/index.d.ts",
37
+ "source": "./server/src/index.ts",
38
+ "import": "./dist/server/index.mjs",
39
+ "require": "./dist/server/index.js",
40
+ "default": "./dist/server/index.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist"
45
+ ],
46
+ "devDependencies": {
47
+ "@strapi/sdk-plugin": "^5.3.2",
48
+ "@strapi/strapi": "^5.12.5",
49
+ "@strapi/typescript-utils": "^5.12.5",
50
+ "@types/react": "^18.3.8",
51
+ "@types/react-dom": "^18.3.0",
52
+ "eslint": "^10.8.1",
53
+ "prettier": "^3.5.3",
54
+ "react": "^18.3.1",
55
+ "react-dom": "^18.3.1",
56
+ "react-router-dom": "^6.30.0",
57
+ "styled-components": "^6.1.17",
58
+ "typescript": "^5.8.3",
59
+ "vitest": "^3.2.4",
60
+ "@qkix/eslint-config": "0.0.0",
61
+ "@qkix/tsconfig": "0.0.0",
62
+ "@qkix/strapi-test-harness": "0.0.0"
63
+ },
64
+ "peerDependencies": {
65
+ "@strapi/admin": "^5.0.0",
66
+ "@strapi/design-system": "^2.0.0",
67
+ "@strapi/icons": "^2.0.0",
68
+ "@strapi/strapi": "^5.0.0",
69
+ "react": "^17.0.0 || ^18.0.0",
70
+ "react-dom": "^17.0.0 || ^18.0.0",
71
+ "react-intl": "^6.0.0 || ^7.0.0",
72
+ "react-router-dom": "^6.0.0",
73
+ "styled-components": "^6.0.0"
74
+ },
75
+ "engines": {
76
+ "node": ">=20.0.0 <=24.x.x",
77
+ "npm": ">=6.0.0"
78
+ },
79
+ "license": "MIT",
80
+ "author": "qkix",
81
+ "repository": {
82
+ "type": "git",
83
+ "url": "git+https://github.com/qkix/strapi-plugins.git",
84
+ "directory": "packages/strapi-plugin-ferry"
85
+ },
86
+ "bugs": {
87
+ "url": "https://github.com/qkix/strapi-plugins/issues"
88
+ },
89
+ "homepage": "https://github.com/qkix/strapi-plugins#readme",
90
+ "nx": {
91
+ "tags": [
92
+ "scope:plugin"
93
+ ],
94
+ "targets": {
95
+ "test": {
96
+ "dependsOn": [
97
+ "build",
98
+ "^build"
99
+ ]
100
+ }
101
+ }
102
+ },
103
+ "publishConfig": {
104
+ "access": "public"
105
+ },
106
+ "scripts": {
107
+ "build": "rm -rf dist && strapi-plugin build --minify",
108
+ "clean": "rm -rf dist",
109
+ "lint": "eslint . && prettier --check .",
110
+ "format": "prettier --write .",
111
+ "test": "vitest run",
112
+ "test:watch": "vitest",
113
+ "verify": "strapi-plugin verify",
114
+ "watch": "strapi-plugin watch",
115
+ "watch:link": "strapi-plugin watch:link",
116
+ "typecheck": "tsc -p admin/tsconfig.json --noEmit && tsc -p server/tsconfig.json --noEmit"
117
+ }
118
+ }