@strapi/data-transfer 4.7.0 → 4.7.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.
Files changed (33) hide show
  1. package/dist/encryption/decrypt.d.ts +3 -0
  2. package/dist/encryption/decrypt.js +39 -0
  3. package/dist/encryption/encrypt.d.ts +3 -0
  4. package/dist/encryption/encrypt.js +39 -0
  5. package/dist/encryption/index.d.ts +2 -0
  6. package/dist/encryption/index.js +19 -0
  7. package/dist/engine/index.d.ts +29 -0
  8. package/dist/engine/index.js +324 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +19 -0
  11. package/dist/providers/index.d.ts +4 -0
  12. package/dist/providers/index.js +23 -0
  13. package/dist/providers/local-file-destination-provider.d.ts +41 -0
  14. package/dist/providers/local-file-destination-provider.js +195 -0
  15. package/dist/providers/local-file-source-provider.d.ts +43 -0
  16. package/dist/providers/local-file-source-provider.js +162 -0
  17. package/dist/providers/local-strapi-destination-provider.d.ts +22 -0
  18. package/dist/providers/local-strapi-destination-provider.js +78 -0
  19. package/dist/providers/local-strapi-source-provider/configuration.d.ts +5 -0
  20. package/dist/providers/local-strapi-source-provider/configuration.js +37 -0
  21. package/dist/providers/local-strapi-source-provider/entities.d.ts +10 -0
  22. package/dist/providers/local-strapi-source-provider/entities.js +58 -0
  23. package/dist/providers/local-strapi-source-provider/index.d.ts +26 -0
  24. package/dist/providers/local-strapi-source-provider/index.js +83 -0
  25. package/dist/providers/local-strapi-source-provider/links/index.d.ts +5 -0
  26. package/dist/providers/local-strapi-source-provider/links/index.js +37 -0
  27. package/dist/providers/local-strapi-source-provider/links/utils.d.ts +27 -0
  28. package/dist/providers/local-strapi-source-provider/links/utils.js +155 -0
  29. package/dist/strategies/index.d.ts +7 -0
  30. package/dist/strategies/index.js +29 -0
  31. package/dist/utils.d.ts +10 -0
  32. package/dist/utils.js +90 -0
  33. package/package.json +4 -4
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLocalStrapiSourceProvider = void 0;
4
+ const stream_chain_1 = require("stream-chain");
5
+ const stream_1 = require("stream");
6
+ const entities_1 = require("./entities");
7
+ const links_1 = require("./links");
8
+ const configuration_1 = require("./configuration");
9
+ const utils_1 = require("../../utils");
10
+ const createLocalStrapiSourceProvider = (options) => {
11
+ return new LocalStrapiSourceProvider(options);
12
+ };
13
+ exports.createLocalStrapiSourceProvider = createLocalStrapiSourceProvider;
14
+ class LocalStrapiSourceProvider {
15
+ constructor(options) {
16
+ this.name = 'source::local-strapi';
17
+ this.type = 'source';
18
+ this.options = options;
19
+ }
20
+ async bootstrap() {
21
+ this.strapi = await this.options.getStrapi();
22
+ }
23
+ async close() {
24
+ const { autoDestroy } = this.options;
25
+ // Basically `!== false` but more deterministic
26
+ if (autoDestroy === undefined || autoDestroy === true) {
27
+ await this.strapi?.destroy();
28
+ }
29
+ }
30
+ getMetadata() {
31
+ const strapiVersion = strapi.config.get('info.strapi');
32
+ const createdAt = new Date().toISOString();
33
+ const plugins = Object.keys(strapi.plugins);
34
+ return {
35
+ createdAt,
36
+ strapi: {
37
+ version: strapiVersion,
38
+ plugins: plugins.map((name) => ({
39
+ name,
40
+ // TODO: Get the plugin actual version when it'll be available
41
+ version: strapiVersion,
42
+ })),
43
+ },
44
+ };
45
+ }
46
+ async streamEntities() {
47
+ if (!this.strapi) {
48
+ throw new Error('Not able to stream entities. Strapi instance not found');
49
+ }
50
+ return (0, stream_chain_1.chain)([
51
+ // Entities stream
52
+ (0, entities_1.createEntitiesStream)(this.strapi),
53
+ // Transform stream
54
+ (0, entities_1.createEntitiesTransformStream)(),
55
+ ]);
56
+ }
57
+ streamLinks() {
58
+ if (!this.strapi) {
59
+ throw new Error('Not able to stream links. Strapi instance not found');
60
+ }
61
+ return (0, links_1.createLinksStream)(this.strapi);
62
+ }
63
+ streamConfiguration() {
64
+ if (!this.strapi) {
65
+ throw new Error('Not able to stream configuration. Strapi instance not found');
66
+ }
67
+ return (0, configuration_1.createConfigurationStream)(strapi);
68
+ }
69
+ getSchemas() {
70
+ if (!this.strapi) {
71
+ throw new Error('Not able to get Schemas. Strapi instance not found');
72
+ }
73
+ const schemas = {
74
+ ...this.strapi.contentTypes,
75
+ ...this.strapi.components,
76
+ };
77
+ return (0, utils_1.mapSchemasValues)(schemas);
78
+ }
79
+ streamSchemas() {
80
+ return stream_1.Readable.from(Object.values(this.getSchemas()));
81
+ }
82
+ }
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import { Readable } from 'stream';
2
+ /**
3
+ * Create a Duplex instance which will stream all the links from a Strapi instance
4
+ */
5
+ export declare const createLinksStream: (strapi: Strapi.Strapi) => Readable;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLinksStream = void 0;
4
+ const stream_1 = require("stream");
5
+ const fp_1 = require("lodash/fp");
6
+ const utils_1 = require("./utils");
7
+ /**
8
+ * Create a Duplex instance which will stream all the links from a Strapi instance
9
+ */
10
+ const createLinksStream = (strapi) => {
11
+ const schemas = Object.values(strapi.contentTypes);
12
+ // Destroy the Duplex stream instance
13
+ const destroy = () => {
14
+ if (!stream.destroyed) {
15
+ stream.destroy();
16
+ }
17
+ };
18
+ // Async generator stream that returns every link from a Strapi instance
19
+ const stream = stream_1.Readable.from((async function* () {
20
+ for (const schema of schemas) {
21
+ const populate = (0, utils_1.getDeepPopulateQuery)(schema, strapi);
22
+ const query = { fields: ['id'], populate };
23
+ // TODO: Replace with the DB stream API
24
+ const results = await strapi.entityService.findMany(schema.uid, query);
25
+ for (const entity of (0, fp_1.castArray)(results)) {
26
+ const links = (0, utils_1.parseEntityLinks)(entity, populate, schema, strapi);
27
+ for (const link of links) {
28
+ yield link;
29
+ }
30
+ }
31
+ }
32
+ destroy();
33
+ })());
34
+ return stream;
35
+ };
36
+ exports.createLinksStream = createLinksStream;
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,27 @@
1
+ import type { RelationsType } from '@strapi/strapi';
2
+ import type { ILink } from '../../../../types';
3
+ /**
4
+ * Parse every links from an entity result (including nested components and dynamic zone levels)
5
+ */
6
+ export declare const parseEntityLinks: (entity: any, populate: any, schema: any, strapi: any) => any[];
7
+ /**
8
+ * Parse links contained in a relational attribute
9
+ */
10
+ export declare const parseRelationLinks: ({ entity, schema, fieldName, value }: any) => ILink[];
11
+ /**
12
+ * Get a deep populate query for a given schema
13
+ * It will populate first level for relations and media as well as
14
+ * first-level relations for nested components and dynamic zones' components
15
+ */
16
+ export declare const getDeepPopulateQuery: (schema: any, strapi: any) => {
17
+ [key: string]: any;
18
+ };
19
+ /**
20
+ * Domain util to create a link
21
+ * TODO: Move that to the domain layer when we'll update it
22
+ */
23
+ export declare const linkBuilder: <T extends ILink = ILink>(kind: T["kind"], relation: RelationsType) => {
24
+ left(type: string, ref: string | number, field: string, pos?: number | undefined): any;
25
+ right(type: string, ref: string | number, field?: string | undefined): any;
26
+ readonly value: ILink | null;
27
+ };
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.linkBuilder = exports.getDeepPopulateQuery = exports.parseRelationLinks = exports.parseEntityLinks = void 0;
4
+ const fp_1 = require("lodash/fp");
5
+ // TODO: Fix any typings when we'll have better Strapi types
6
+ /**
7
+ * Parse every links from an entity result (including nested components and dynamic zone levels)
8
+ */
9
+ const parseEntityLinks = (entity, populate, schema, strapi) => {
10
+ if (!entity) {
11
+ return [];
12
+ }
13
+ if (Array.isArray(entity)) {
14
+ return entity
15
+ .map((item) => (0, exports.parseEntityLinks)(item, populate, schema, strapi))
16
+ .reduce(fp_1.concat, [])
17
+ .flat();
18
+ }
19
+ const { attributes } = schema;
20
+ const links = [];
21
+ for (const key of Object.keys(populate)) {
22
+ const attribute = attributes[key];
23
+ const value = entity[key];
24
+ const subPopulate = populate[key];
25
+ // Ignore nil values (relations, components or dynamic zones not set)
26
+ if (!value) {
27
+ continue;
28
+ }
29
+ // Components
30
+ // Recurse to find relations
31
+ if (attribute.type === 'component') {
32
+ const componentSchema = strapi.components[attribute.component];
33
+ const componentLinks = (0, exports.parseEntityLinks)(value, subPopulate.populate, componentSchema, strapi);
34
+ links.push(...componentLinks);
35
+ }
36
+ // Dynamic Zones
37
+ // We need to extract links from each items in the DZ's components
38
+ if (attribute.type === 'dynamiczone') {
39
+ const dzLinks = value
40
+ .map(({ __component, ...item }) => (0, exports.parseEntityLinks)(item, subPopulate.populate, strapi.components[__component], strapi))
41
+ .reduce((acc, links) => acc.concat(...links), []);
42
+ links.push(...dzLinks);
43
+ }
44
+ // Relations
45
+ // If it's a regular relation, extract the links but do not recurse further
46
+ if (attribute.type === 'relation') {
47
+ const relationLinks = (0, exports.parseRelationLinks)({ entity, fieldName: key, value, schema });
48
+ links.push(...relationLinks);
49
+ }
50
+ }
51
+ return links;
52
+ };
53
+ exports.parseEntityLinks = parseEntityLinks;
54
+ /**
55
+ * Parse links contained in a relational attribute
56
+ */
57
+ const parseRelationLinks = ({ entity, schema, fieldName, value }) => {
58
+ const attribute = schema.attributes[fieldName];
59
+ const { relation, target } = attribute;
60
+ // Handle ToMany relations
61
+ if (Array.isArray(value)) {
62
+ return (value
63
+ // Get links from value
64
+ .map((item) => (0, exports.parseRelationLinks)({ entity, schema, fieldName, value: item }))
65
+ // Flatten the results, to make sure we're dealing with the right data structure
66
+ .flat()
67
+ // Update the pos with the relation index in the collection
68
+ .map((link, i) => (0, fp_1.set)('left.pos', i, link)));
69
+ }
70
+ const isMorphRelation = relation.startsWith('morph');
71
+ const isCircularRelation = !isMorphRelation && target === schema.uid;
72
+ const kind = isMorphRelation
73
+ ? // Polymorphic relations
74
+ 'relation.morph'
75
+ : isCircularRelation
76
+ ? // Self referencing relations
77
+ 'relation.circular'
78
+ : // Regular relations
79
+ 'relation.basic';
80
+ const link = (0, exports.linkBuilder)(kind, relation)
81
+ .left(schema.uid, entity.id, fieldName)
82
+ .right(target, value.id, attribute.inversedBy).value;
83
+ return link ? [link] : [];
84
+ };
85
+ exports.parseRelationLinks = parseRelationLinks;
86
+ /**
87
+ * Get a deep populate query for a given schema
88
+ * It will populate first level for relations and media as well as
89
+ * first-level relations for nested components and dynamic zones' components
90
+ */
91
+ const getDeepPopulateQuery = (schema, strapi) => {
92
+ const populate = {};
93
+ for (const [key, attribute] of Object.entries(schema.attributes)) {
94
+ const setPopulateKey = (value) => {
95
+ populate[key] = value;
96
+ };
97
+ // Owning side of a relation
98
+ if (attribute.type === 'relation' && !attribute.mappedBy) {
99
+ setPopulateKey({ fields: ['id'] });
100
+ }
101
+ // Media
102
+ if (attribute.type === 'media') {
103
+ setPopulateKey({ fields: ['id'] });
104
+ }
105
+ // Dynamic zone (nested structure)
106
+ if (attribute.type === 'dynamiczone') {
107
+ const subPopulate = {};
108
+ for (const component of attribute.components) {
109
+ const componentSchema = strapi.components[component];
110
+ const componentPopulate = (0, exports.getDeepPopulateQuery)(componentSchema, strapi);
111
+ // FIXME: Same problem as when trying to populate dynamic zones,
112
+ // we don't have a way to discriminate components queries (which
113
+ // can cause issue when components share same fields name)
114
+ Object.assign(subPopulate, componentPopulate);
115
+ }
116
+ if (!(0, fp_1.isEmpty)(subPopulate)) {
117
+ setPopulateKey({ fields: ['id'], populate: subPopulate });
118
+ }
119
+ }
120
+ // Component (nested structure)
121
+ if (attribute.type === 'component') {
122
+ const componentSchema = strapi.components[attribute.component];
123
+ const componentPopulate = (0, exports.getDeepPopulateQuery)(componentSchema, strapi);
124
+ if (!(0, fp_1.isEmpty)(componentPopulate)) {
125
+ setPopulateKey({ fields: ['id'], populate: componentPopulate });
126
+ }
127
+ }
128
+ }
129
+ return populate;
130
+ };
131
+ exports.getDeepPopulateQuery = getDeepPopulateQuery;
132
+ /**
133
+ * Domain util to create a link
134
+ * TODO: Move that to the domain layer when we'll update it
135
+ */
136
+ const linkBuilder = (kind, relation) => {
137
+ const link = {};
138
+ link.kind = kind;
139
+ link.relation = relation;
140
+ return {
141
+ left(type, ref, field, pos) {
142
+ link.left = { type, ref, field, pos };
143
+ return this;
144
+ },
145
+ right(type, ref, field) {
146
+ link.right = { type, ref, field };
147
+ return this;
148
+ },
149
+ get value() {
150
+ return link.left && link.right ? link : null;
151
+ },
152
+ };
153
+ };
154
+ exports.linkBuilder = linkBuilder;
155
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ import type { Diff } from '../../types';
2
+ declare const strategies: {
3
+ exact: (diffs: Diff[]) => Diff[];
4
+ strict: (diffs: Diff[]) => Diff[];
5
+ };
6
+ declare const compareSchemas: <T, P>(a: T, b: P, strategy: keyof typeof strategies) => Diff[];
7
+ export default compareSchemas;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ const strategies = {
5
+ // No diffs
6
+ exact: (diffs) => {
7
+ return diffs;
8
+ },
9
+ // Diffs allowed on specific attributes properties
10
+ strict: (diffs) => {
11
+ const isIgnorableDiff = ({ path }) => {
12
+ return (path.length === 3 &&
13
+ // Root property must be attributes
14
+ path[0] === 'attributes' &&
15
+ // Need a valid string attribute name
16
+ typeof path[1] === 'string' &&
17
+ // The diff must be on ignorable attribute properties
18
+ ['private', 'required', 'configurable'].includes(path[2]));
19
+ };
20
+ const shouldKeepDiff = (diff) => !isIgnorableDiff(diff);
21
+ return diffs.filter(shouldKeepDiff);
22
+ },
23
+ };
24
+ const compareSchemas = (a, b, strategy) => {
25
+ const diffs = (0, utils_1.jsonDiffs)(a, b);
26
+ return strategies[strategy](diffs);
27
+ };
28
+ exports.default = compareSchemas;
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ import type { Readable } from 'stream';
2
+ import type { Context, Diff } from '../types';
3
+ /**
4
+ * Collect every entity in a Readable stream
5
+ */
6
+ export declare const collect: <T = unknown>(stream: Readable) => Promise<T[]>;
7
+ export declare const jsonDiffs: (a: unknown, b: unknown, ctx?: Context) => Diff[];
8
+ export declare const mapSchemasValues: (schemas: any) => {
9
+ [x: string]: Partial<any>;
10
+ };
package/dist/utils.js ADDED
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapSchemasValues = exports.jsonDiffs = exports.collect = void 0;
4
+ const fp_1 = require("lodash/fp");
5
+ /**
6
+ * Collect every entity in a Readable stream
7
+ */
8
+ const collect = (stream) => {
9
+ const chunks = [];
10
+ return new Promise((resolve) => {
11
+ stream.on('data', (chunk) => chunks.push(chunk));
12
+ stream.on('end', () => {
13
+ stream.destroy();
14
+ resolve(chunks);
15
+ });
16
+ });
17
+ };
18
+ exports.collect = collect;
19
+ const createContext = () => ({ path: [] });
20
+ const jsonDiffs = (a, b, ctx = createContext()) => {
21
+ const diffs = [];
22
+ const { path } = ctx;
23
+ // Define helpers
24
+ const added = () => {
25
+ diffs.push({ kind: 'added', path, type: bType, value: b });
26
+ return diffs;
27
+ };
28
+ const deleted = () => {
29
+ diffs.push({ kind: 'deleted', path, type: aType, value: a });
30
+ return diffs;
31
+ };
32
+ const modified = () => {
33
+ diffs.push({
34
+ kind: 'modified',
35
+ path,
36
+ types: [aType, bType],
37
+ values: [a, b],
38
+ });
39
+ return diffs;
40
+ };
41
+ const aType = typeof a;
42
+ const bType = typeof b;
43
+ if (aType === 'undefined') {
44
+ return added();
45
+ }
46
+ if (bType === 'undefined') {
47
+ return deleted();
48
+ }
49
+ if ((0, fp_1.isArray)(a) && (0, fp_1.isArray)(b)) {
50
+ let k = 0;
51
+ for (const [aItem, bItem] of (0, fp_1.zip)(a, b)) {
52
+ const kCtx = { path: [...path, k.toString()] };
53
+ const kDiffs = (0, exports.jsonDiffs)(aItem, bItem, kCtx);
54
+ diffs.push(...kDiffs);
55
+ k++;
56
+ }
57
+ return diffs;
58
+ }
59
+ if ((0, fp_1.isObject)(a) && (0, fp_1.isObject)(b)) {
60
+ const keys = (0, fp_1.uniq)(Object.keys(a).concat(Object.keys(b)));
61
+ for (const key of keys) {
62
+ const aValue = a[key];
63
+ const bValue = b[key];
64
+ const nestedDiffs = (0, exports.jsonDiffs)(aValue, bValue, { path: [...path, key] });
65
+ diffs.push(...nestedDiffs);
66
+ }
67
+ return diffs;
68
+ }
69
+ if (!(0, fp_1.isEqual)(a, b)) {
70
+ modified();
71
+ }
72
+ return diffs;
73
+ };
74
+ exports.jsonDiffs = jsonDiffs;
75
+ const schemaSelectedKeys = [
76
+ 'collectionName',
77
+ 'info',
78
+ 'options',
79
+ 'pluginOptions',
80
+ 'attributes',
81
+ 'kind',
82
+ 'modelType',
83
+ 'modelName',
84
+ 'uid',
85
+ 'plugin',
86
+ 'globalId',
87
+ ];
88
+ const mapSchemasValues = (schemas) => (0, fp_1.mapValues)((0, fp_1.pick)(schemaSelectedKeys), schemas);
89
+ exports.mapSchemasValues = mapSchemasValues;
90
+ //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/data-transfer",
3
- "version": "4.7.0",
3
+ "version": "4.7.1",
4
4
  "description": "Data transfer capabilities for Strapi",
5
5
  "keywords": [
6
6
  "strapi",
@@ -39,8 +39,8 @@
39
39
  "lib": "./lib"
40
40
  },
41
41
  "dependencies": {
42
- "@strapi/logger": "4.7.0",
43
- "@strapi/strapi": "4.7.0",
42
+ "@strapi/logger": "4.7.1",
43
+ "@strapi/strapi": "4.7.1",
44
44
  "chalk": "4.1.2",
45
45
  "fs-extra": "10.0.0",
46
46
  "lodash": "4.17.21",
@@ -73,5 +73,5 @@
73
73
  "node": ">=14.19.1 <=18.x.x",
74
74
  "npm": ">=6.0.0"
75
75
  },
76
- "gitHead": "948dbb3121330ffd43f3a4f55522c797770e0b2a"
76
+ "gitHead": "0307fb4bf7b006c4cd902412967d3987d7810ed8"
77
77
  }