@rebasepro/schema-inference 0.5.0 → 0.6.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.
- package/dist/index.es.js +370 -473
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.cjs +469 -540
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +9 -9
- package/src/builders/string_property_builder.ts +2 -1
package/dist/index.es.js
CHANGED
|
@@ -1,525 +1,422 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isObject, isPlainObject, mergeDeep as mergeDeep2, prettifyIdentifier as prettifyIdentifier2, unslugify as unslugify2 } from "@rebasepro/utils";
|
|
1
|
+
import { isObject, isPlainObject, mergeDeep, prettifyIdentifier, unslugify, unslugify as unslugify$1 } from "@rebasepro/utils";
|
|
3
2
|
import { Vector } from "@rebasepro/types";
|
|
3
|
+
//#region src/strings.ts
|
|
4
|
+
/**
|
|
5
|
+
* Parse a reference string value which can be in the format:
|
|
6
|
+
* - Simple: "path/entityId"
|
|
7
|
+
* - With database: "database_name:::path/entityId"
|
|
8
|
+
* Returns the path and database (undefined if not specified or if "(default)")
|
|
9
|
+
*/
|
|
4
10
|
function parseReferenceString(value) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const path = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
19
|
-
return {
|
|
20
|
-
path,
|
|
21
|
-
database
|
|
22
|
-
};
|
|
11
|
+
if (!value) return null;
|
|
12
|
+
let database = void 0;
|
|
13
|
+
let fullPath = value;
|
|
14
|
+
if (value.includes(":::")) {
|
|
15
|
+
const [dbName, pathPart] = value.split(":::");
|
|
16
|
+
if (dbName && dbName !== "(default)") database = dbName;
|
|
17
|
+
fullPath = pathPart;
|
|
18
|
+
}
|
|
19
|
+
if (!fullPath || !fullPath.includes("/")) return null;
|
|
20
|
+
return {
|
|
21
|
+
path: fullPath.substring(0, fullPath.lastIndexOf("/")),
|
|
22
|
+
database
|
|
23
|
+
};
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a string value looks like a reference
|
|
27
|
+
*/
|
|
24
28
|
function looksLikeReference(value) {
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
if (typeof value !== "string") return false;
|
|
30
|
+
return parseReferenceString(value) !== null;
|
|
27
31
|
}
|
|
28
32
|
function findCommonInitialStringInPath(valuesCount) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const path = getPath(value);
|
|
54
|
-
if (!path) return false;
|
|
55
|
-
return path.startsWith(searchedPath);
|
|
56
|
-
}).length > valuesCount.values.length / 3 * 2;
|
|
57
|
-
return yep ? searchedPath : void 0;
|
|
33
|
+
if (!valuesCount) return void 0;
|
|
34
|
+
function getPath(value) {
|
|
35
|
+
let pathString;
|
|
36
|
+
if (typeof value === "string") pathString = value;
|
|
37
|
+
else if (value && typeof value === "object" && "slug" in value && typeof value.slug === "string") pathString = value.slug;
|
|
38
|
+
else {
|
|
39
|
+
console.warn("findCommonInitialStringInPath: value is not a string or document with path", value);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!pathString) return void 0;
|
|
43
|
+
if (pathString.includes(":::")) {
|
|
44
|
+
const [, pathPart] = pathString.split(":::");
|
|
45
|
+
pathString = pathPart;
|
|
46
|
+
}
|
|
47
|
+
return pathString;
|
|
48
|
+
}
|
|
49
|
+
const pathWithSlash = valuesCount.values.map((v) => getPath(v)).filter((v) => !!v).find((s) => s.includes("/"));
|
|
50
|
+
if (!pathWithSlash) return void 0;
|
|
51
|
+
const searchedPath = pathWithSlash.substring(0, pathWithSlash.lastIndexOf("/"));
|
|
52
|
+
return valuesCount.values.filter((value) => {
|
|
53
|
+
const path = getPath(value);
|
|
54
|
+
if (!path) return false;
|
|
55
|
+
return path.startsWith(searchedPath);
|
|
56
|
+
}).length > valuesCount.values.length / 3 * 2 ? searchedPath : void 0;
|
|
58
57
|
}
|
|
59
58
|
function removeInitialAndTrailingSlashes(s) {
|
|
60
|
-
|
|
59
|
+
return removeInitialSlash(removeTrailingSlash(s));
|
|
61
60
|
}
|
|
62
61
|
function removeInitialSlash(s) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
else return s;
|
|
62
|
+
if (s.startsWith("/")) return s.slice(1);
|
|
63
|
+
else return s;
|
|
66
64
|
}
|
|
67
65
|
function removeTrailingSlash(s) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
else return s;
|
|
66
|
+
if (s.endsWith("/")) return s.slice(0, -1);
|
|
67
|
+
else return s;
|
|
71
68
|
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/util.ts
|
|
71
|
+
/**
|
|
72
|
+
* Extract enum values from a list of sample values.
|
|
73
|
+
* This is schema-inference-specific logic (not a general utility).
|
|
74
|
+
*/
|
|
72
75
|
function extractEnumFromValues(values) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return null;
|
|
84
|
-
}).filter(Boolean);
|
|
85
|
-
enumValues.sort((a, b) => a.label.localeCompare(b.label));
|
|
86
|
-
return enumValues;
|
|
76
|
+
if (!Array.isArray(values)) return [];
|
|
77
|
+
const enumValues = values.map((value) => {
|
|
78
|
+
if (typeof value === "string") return {
|
|
79
|
+
id: value,
|
|
80
|
+
label: unslugify$1(value)
|
|
81
|
+
};
|
|
82
|
+
else return null;
|
|
83
|
+
}).filter(Boolean);
|
|
84
|
+
enumValues.sort((a, b) => a.label.localeCompare(b.label));
|
|
85
|
+
return enumValues;
|
|
87
86
|
}
|
|
88
87
|
function resolveEnumValues(input) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
} : value);
|
|
96
|
-
} else {
|
|
97
|
-
return void 0;
|
|
98
|
-
}
|
|
88
|
+
if (Array.isArray(input)) return input;
|
|
89
|
+
else if (typeof input === "object" && input !== null) return Object.entries(input).map(([id, value]) => typeof value === "string" ? {
|
|
90
|
+
id,
|
|
91
|
+
label: value
|
|
92
|
+
} : value);
|
|
93
|
+
else return;
|
|
99
94
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/builders/string_property_builder.ts
|
|
97
|
+
var IMAGE_EXTENSIONS = [
|
|
98
|
+
".jpg",
|
|
99
|
+
".jpeg",
|
|
100
|
+
".png",
|
|
101
|
+
".webp",
|
|
102
|
+
".gif",
|
|
103
|
+
".avif"
|
|
104
|
+
];
|
|
105
|
+
var AUDIO_EXTENSIONS = [
|
|
106
|
+
".mp3",
|
|
107
|
+
".ogg",
|
|
108
|
+
".opus",
|
|
109
|
+
".aac"
|
|
110
|
+
];
|
|
111
|
+
var VIDEO_EXTENSIONS = [".avi", ".mp4"];
|
|
112
|
+
var emailRegEx = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
113
|
+
function buildStringProperty({ name, totalDocsCount, valuesResult }) {
|
|
114
|
+
let stringProperty = {
|
|
115
|
+
name: name ?? "",
|
|
116
|
+
type: "string"
|
|
117
|
+
};
|
|
118
|
+
if (valuesResult) {
|
|
119
|
+
const totalEntriesCount = valuesResult.values.length;
|
|
120
|
+
const totalValues = Array.from(valuesResult.valuesCount.keys()).length;
|
|
121
|
+
const config = {};
|
|
122
|
+
const probablyAURL = valuesResult.values.filter((value) => typeof value === "string" && value.toString().startsWith("http")).length > totalDocsCount / 3 * 2;
|
|
123
|
+
if (probablyAURL) config.ui = { url: true };
|
|
124
|
+
const probablyAnEmail = valuesResult.values.filter((value) => typeof value === "string" && emailRegEx.test(value)).length > totalDocsCount / 3 * 2;
|
|
125
|
+
if (probablyAnEmail) config.email = true;
|
|
126
|
+
const probablyUserIds = valuesResult.values.filter((value) => typeof value === "string" && value.length === 28 && !value.includes(" ")).length > totalDocsCount / 3 * 2;
|
|
127
|
+
if (probablyUserIds) config.ui = {
|
|
128
|
+
...config.ui,
|
|
129
|
+
readOnly: true
|
|
130
|
+
};
|
|
131
|
+
if (!probablyAnEmail && !probablyAURL && !probablyUserIds && !probablyAURL && totalValues < totalEntriesCount / 3) {
|
|
132
|
+
const enumValues = extractEnumFromValues(Array.from(valuesResult.valuesCount.keys()));
|
|
133
|
+
if (Object.keys(enumValues).length > 1) config.enum = enumValues;
|
|
134
|
+
}
|
|
135
|
+
if (!probablyAnEmail && !probablyAURL && !probablyUserIds && !probablyAURL && !config.enum) {
|
|
136
|
+
const fileType = probableFileType(valuesResult, totalDocsCount);
|
|
137
|
+
if (fileType) config.storage = {
|
|
138
|
+
acceptedFiles: fileType,
|
|
139
|
+
storagePath: findCommonInitialStringInPath(valuesResult) ?? "/"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (Object.keys(config).length > 0) stringProperty = {
|
|
143
|
+
...stringProperty,
|
|
144
|
+
...config
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return stringProperty;
|
|
149
148
|
}
|
|
150
149
|
function probableFileType(valuesCount, totalDocsCount) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return fileTypes.length > 0 ? fileTypes : false;
|
|
170
|
-
}
|
|
171
|
-
return false;
|
|
150
|
+
const isImage = (value) => IMAGE_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
|
|
151
|
+
const isAudio = (value) => AUDIO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
|
|
152
|
+
const isVideo = (value) => VIDEO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
|
|
153
|
+
const stringValues = valuesCount.values.filter((v) => typeof v === "string");
|
|
154
|
+
let imageCount = 0;
|
|
155
|
+
let audioCount = 0;
|
|
156
|
+
let videoCount = 0;
|
|
157
|
+
for (const value of stringValues) if (isImage(value)) imageCount++;
|
|
158
|
+
else if (isAudio(value)) audioCount++;
|
|
159
|
+
else if (isVideo(value)) videoCount++;
|
|
160
|
+
if (imageCount + audioCount + videoCount > totalDocsCount * 2 / 3) {
|
|
161
|
+
const fileTypes = [];
|
|
162
|
+
if (imageCount > 0) fileTypes.push("image/*");
|
|
163
|
+
if (audioCount > 0) fileTypes.push("audio/*");
|
|
164
|
+
if (videoCount > 0) fileTypes.push("video/*");
|
|
165
|
+
return fileTypes.length > 0 ? fileTypes : false;
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
172
168
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (totalDocsCount === totalEntriesCount)
|
|
180
|
-
return {
|
|
181
|
-
required: true
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
return void 0;
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/builders/validation_builder.ts
|
|
171
|
+
function buildValidation({ totalDocsCount, valuesResult }) {
|
|
172
|
+
if (valuesResult) {
|
|
173
|
+
if (totalDocsCount === valuesResult.values.length) return { required: true };
|
|
174
|
+
}
|
|
185
175
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
path: findCommonInitialStringInPath(valuesResult) ?? "!!!FIX_ME!!!"
|
|
195
|
-
};
|
|
196
|
-
return property;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/builders/reference_property_builder.ts
|
|
178
|
+
function buildReferenceProperty({ name, totalDocsCount, valuesResult }) {
|
|
179
|
+
return {
|
|
180
|
+
name: name ?? "",
|
|
181
|
+
type: "reference",
|
|
182
|
+
path: findCommonInitialStringInPath(valuesResult) ?? "!!!FIX_ME!!!"
|
|
183
|
+
};
|
|
197
184
|
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/collection_builder.ts
|
|
198
187
|
async function buildEntityPropertiesFromData(data, getType) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return buildPropertiesFromCount(data.length, typesCount, valuesCount);
|
|
188
|
+
const typesCount = {};
|
|
189
|
+
const valuesCount = {};
|
|
190
|
+
if (data) data.forEach((entry) => {
|
|
191
|
+
if (entry) Object.entries(entry).forEach(([key, value]) => {
|
|
192
|
+
if (key.startsWith("_")) return;
|
|
193
|
+
increaseMapTypeCount(typesCount, key, value, getType);
|
|
194
|
+
increaseValuesCount(valuesCount, key, value, getType);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
return buildPropertiesFromCount(data.length, typesCount, valuesCount);
|
|
213
198
|
}
|
|
214
199
|
function buildPropertyFromData(data, property, getType) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
const generatedProperty = buildPropertyFromCount(
|
|
232
|
-
"inferred_prop",
|
|
233
|
-
data.length,
|
|
234
|
-
property.type,
|
|
235
|
-
typesCount,
|
|
236
|
-
valuesCount["inferred_prop"]
|
|
237
|
-
);
|
|
238
|
-
return mergeDeep(generatedProperty, property);
|
|
200
|
+
const typesCount = {};
|
|
201
|
+
const valuesCount = {};
|
|
202
|
+
if (data) data.forEach((entry) => {
|
|
203
|
+
increaseTypeCount(property.type, typesCount, entry, getType);
|
|
204
|
+
increaseValuesCount(valuesCount, "inferred_prop", entry, getType);
|
|
205
|
+
});
|
|
206
|
+
const enumValues = "enum" in property ? resolveEnumValues(property["enum"]) : void 0;
|
|
207
|
+
if (enumValues) {
|
|
208
|
+
const newEnumValues = extractEnumFromValues(Array.from(valuesCount["inferred_prop"].valuesCount.keys()));
|
|
209
|
+
return {
|
|
210
|
+
...property,
|
|
211
|
+
enum: [...newEnumValues, ...enumValues]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return mergeDeep(buildPropertyFromCount("inferred_prop", data.length, property.type, typesCount, valuesCount["inferred_prop"]), property);
|
|
239
215
|
}
|
|
240
216
|
function buildPropertiesOrder(properties, propertiesOrder, priorityKeys) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
217
|
+
const lowerCasePriorityKeys = (priorityKeys ?? []).map((key) => key.toLowerCase());
|
|
218
|
+
function propOrder(s) {
|
|
219
|
+
const k = s.toLowerCase();
|
|
220
|
+
if (lowerCasePriorityKeys.includes(k)) return 4;
|
|
221
|
+
if (k === "title" || k === "name") return 3;
|
|
222
|
+
if (k.includes("title") || k.includes("name")) return 2;
|
|
223
|
+
if (k.includes("image") || k.includes("picture")) return 1;
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
const keys = propertiesOrder ?? Object.keys(properties);
|
|
227
|
+
keys.sort();
|
|
228
|
+
keys.sort((a, b) => {
|
|
229
|
+
return propOrder(b) - propOrder(a);
|
|
230
|
+
});
|
|
231
|
+
return keys;
|
|
256
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* @param type
|
|
235
|
+
* @param typesCount
|
|
236
|
+
* @param fieldValue
|
|
237
|
+
* @param getType
|
|
238
|
+
*/
|
|
257
239
|
function increaseTypeCount(type, typesCount, fieldValue, getType) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
arrayTypesCount[arrayType] = mapTypesCount;
|
|
290
|
-
} else {
|
|
291
|
-
if (!arrayTypesCount[arrayType]) arrayTypesCount[arrayType] = 1;
|
|
292
|
-
else arrayTypesCount[arrayType] = Number(arrayTypesCount[arrayType]) + 1;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
} else {
|
|
296
|
-
if (!typesCount[type]) typesCount[type] = 1;
|
|
297
|
-
else typesCount[type] = Number(typesCount[type]) + 1;
|
|
298
|
-
}
|
|
240
|
+
if (type === "map") {
|
|
241
|
+
if (fieldValue) {
|
|
242
|
+
let mapTypesCount = typesCount[type];
|
|
243
|
+
if (!mapTypesCount) {
|
|
244
|
+
mapTypesCount = {};
|
|
245
|
+
typesCount[type] = mapTypesCount;
|
|
246
|
+
}
|
|
247
|
+
Object.entries(fieldValue).forEach(([key, value]) => {
|
|
248
|
+
increaseMapTypeCount(mapTypesCount, key, value, getType);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
} else if (type === "array") {
|
|
252
|
+
let arrayTypesCount = typesCount[type];
|
|
253
|
+
if (!arrayTypesCount) {
|
|
254
|
+
arrayTypesCount = {};
|
|
255
|
+
typesCount[type] = arrayTypesCount;
|
|
256
|
+
}
|
|
257
|
+
if (fieldValue && Array.isArray(fieldValue) && fieldValue.length > 0) {
|
|
258
|
+
const arrayType = getMostProbableTypeInArray(fieldValue, getType);
|
|
259
|
+
if (arrayType === "map") {
|
|
260
|
+
let mapTypesCount = arrayTypesCount[arrayType];
|
|
261
|
+
if (!mapTypesCount) mapTypesCount = {};
|
|
262
|
+
fieldValue.forEach((value) => {
|
|
263
|
+
if (value && typeof value === "object" && !Array.isArray(value)) Object.entries(value).forEach(([key, v]) => increaseMapTypeCount(mapTypesCount, key, v, getType));
|
|
264
|
+
});
|
|
265
|
+
arrayTypesCount[arrayType] = mapTypesCount;
|
|
266
|
+
} else if (!arrayTypesCount[arrayType]) arrayTypesCount[arrayType] = 1;
|
|
267
|
+
else arrayTypesCount[arrayType] = Number(arrayTypesCount[arrayType]) + 1;
|
|
268
|
+
}
|
|
269
|
+
} else if (!typesCount[type]) typesCount[type] = 1;
|
|
270
|
+
else typesCount[type] = Number(typesCount[type]) + 1;
|
|
299
271
|
}
|
|
300
272
|
function increaseMapTypeCount(typesCountRecord, key, fieldValue, getType) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const type = getType(fieldValue);
|
|
309
|
-
increaseTypeCount(type, typesCount, fieldValue, getType);
|
|
310
|
-
}
|
|
273
|
+
if (key.startsWith("_")) return;
|
|
274
|
+
let typesCount = typesCountRecord[key];
|
|
275
|
+
if (!typesCount) {
|
|
276
|
+
typesCount = {};
|
|
277
|
+
typesCountRecord[key] = typesCount;
|
|
278
|
+
}
|
|
279
|
+
if (fieldValue != null) increaseTypeCount(getType(fieldValue), typesCount, fieldValue, getType);
|
|
311
280
|
}
|
|
312
281
|
function increaseValuesCount(typeValuesRecord, key, fieldValue, getType) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
} else {
|
|
341
|
-
if (fieldValue !== null && fieldValue !== void 0) {
|
|
342
|
-
valuesRecord.values.push(fieldValue);
|
|
343
|
-
valuesRecord.valuesCount.set(fieldValue, (valuesRecord.valuesCount.get(fieldValue) ?? 0) + 1);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
282
|
+
if (key.startsWith("_")) return;
|
|
283
|
+
const type = getType(fieldValue);
|
|
284
|
+
let valuesRecord = typeValuesRecord[key];
|
|
285
|
+
if (!valuesRecord) {
|
|
286
|
+
valuesRecord = {
|
|
287
|
+
values: [],
|
|
288
|
+
valuesCount: /* @__PURE__ */ new Map()
|
|
289
|
+
};
|
|
290
|
+
typeValuesRecord[key] = valuesRecord;
|
|
291
|
+
}
|
|
292
|
+
if (type === "map") {
|
|
293
|
+
let mapValuesRecord = valuesRecord.map;
|
|
294
|
+
if (!mapValuesRecord) {
|
|
295
|
+
mapValuesRecord = {};
|
|
296
|
+
valuesRecord.map = mapValuesRecord;
|
|
297
|
+
}
|
|
298
|
+
if (fieldValue) Object.entries(fieldValue).forEach(([subKey, value]) => increaseValuesCount(mapValuesRecord, subKey, value, getType));
|
|
299
|
+
} else if (type === "array") {
|
|
300
|
+
if (Array.isArray(fieldValue)) fieldValue.forEach((value) => {
|
|
301
|
+
valuesRecord.values.push(value);
|
|
302
|
+
valuesRecord.valuesCount.set(value, (valuesRecord.valuesCount.get(value) ?? 0) + 1);
|
|
303
|
+
});
|
|
304
|
+
} else if (fieldValue !== null && fieldValue !== void 0) {
|
|
305
|
+
valuesRecord.values.push(fieldValue);
|
|
306
|
+
valuesRecord.valuesCount.set(fieldValue, (valuesRecord.valuesCount.get(fieldValue) ?? 0) + 1);
|
|
307
|
+
}
|
|
346
308
|
}
|
|
347
309
|
function getHighestTypesCount(typesCount) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
if (countValue > highestCount) {
|
|
359
|
-
highestCount = countValue;
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
return highestCount;
|
|
310
|
+
let highestCount = 0;
|
|
311
|
+
Object.entries(typesCount).forEach(([type, count]) => {
|
|
312
|
+
let countValue = 0;
|
|
313
|
+
if (type === "map") countValue = getHighestRecordCount(count);
|
|
314
|
+
else if (type === "array") countValue = getHighestTypesCount(count);
|
|
315
|
+
else countValue = Number(count);
|
|
316
|
+
if (countValue > highestCount) highestCount = countValue;
|
|
317
|
+
});
|
|
318
|
+
return highestCount;
|
|
363
319
|
}
|
|
364
320
|
function getHighestRecordCount(record) {
|
|
365
|
-
|
|
321
|
+
return Object.entries(record).map(([key, typesCount]) => getHighestTypesCount(typesCount)).reduce((a, b) => Math.max(a, b), 0);
|
|
366
322
|
}
|
|
367
323
|
function getMostProbableType(typesCount) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
probableType = type;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
return probableType;
|
|
324
|
+
let highestCount = -1;
|
|
325
|
+
let probableType = "string";
|
|
326
|
+
Object.entries(typesCount).forEach(([type, count]) => {
|
|
327
|
+
let countValue;
|
|
328
|
+
if (type === "map") countValue = getHighestRecordCount(count);
|
|
329
|
+
else if (type === "array") countValue = getHighestTypesCount(count);
|
|
330
|
+
else countValue = Number(count);
|
|
331
|
+
if (countValue > highestCount) {
|
|
332
|
+
highestCount = countValue;
|
|
333
|
+
probableType = type;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
return probableType;
|
|
385
337
|
}
|
|
386
338
|
function buildPropertyFromCount(key, totalDocsCount, mostProbableType, typesCount, valuesResult) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
if (!result) {
|
|
429
|
-
const propertyProps = {
|
|
430
|
-
name: key,
|
|
431
|
-
totalDocsCount,
|
|
432
|
-
valuesResult
|
|
433
|
-
};
|
|
434
|
-
if (mostProbableType === "string") {
|
|
435
|
-
result = buildStringProperty(propertyProps);
|
|
436
|
-
} else if (mostProbableType === "reference") {
|
|
437
|
-
result = buildReferenceProperty(propertyProps);
|
|
438
|
-
} else {
|
|
439
|
-
result = {
|
|
440
|
-
type: mostProbableType
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
if (title) {
|
|
444
|
-
result.name = title;
|
|
445
|
-
}
|
|
446
|
-
const validation = buildValidation(propertyProps);
|
|
447
|
-
if (validation) {
|
|
448
|
-
result.validation = validation;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return result;
|
|
339
|
+
let title;
|
|
340
|
+
if (key) title = prettifyIdentifier(key);
|
|
341
|
+
let result = void 0;
|
|
342
|
+
if (mostProbableType === "map") {
|
|
343
|
+
if (checkTypesCountHighVariability(typesCount)) result = {
|
|
344
|
+
type: "map",
|
|
345
|
+
name: title ?? key ?? "",
|
|
346
|
+
keyValue: true,
|
|
347
|
+
properties: {}
|
|
348
|
+
};
|
|
349
|
+
const properties = buildPropertiesFromCount(totalDocsCount, typesCount.map, valuesResult ? valuesResult.mapValues : void 0);
|
|
350
|
+
result = {
|
|
351
|
+
type: "map",
|
|
352
|
+
name: title ?? key ?? "",
|
|
353
|
+
properties
|
|
354
|
+
};
|
|
355
|
+
} else if (mostProbableType === "array") {
|
|
356
|
+
const arrayTypesCount = typesCount.array;
|
|
357
|
+
const of = buildPropertyFromCount(key, totalDocsCount, getMostProbableType(arrayTypesCount), arrayTypesCount, valuesResult);
|
|
358
|
+
result = {
|
|
359
|
+
type: "array",
|
|
360
|
+
name: title ?? key ?? "",
|
|
361
|
+
of
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (!result) {
|
|
365
|
+
const propertyProps = {
|
|
366
|
+
name: key,
|
|
367
|
+
totalDocsCount,
|
|
368
|
+
valuesResult
|
|
369
|
+
};
|
|
370
|
+
if (mostProbableType === "string") result = buildStringProperty(propertyProps);
|
|
371
|
+
else if (mostProbableType === "reference") result = buildReferenceProperty(propertyProps);
|
|
372
|
+
else result = { type: mostProbableType };
|
|
373
|
+
if (title) result.name = title;
|
|
374
|
+
const validation = buildValidation(propertyProps);
|
|
375
|
+
if (validation) result.validation = validation;
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
452
378
|
}
|
|
453
379
|
function buildPropertiesFromCount(totalDocsCount, typesCountRecord, valuesCountRecord) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
totalDocsCount,
|
|
460
|
-
mostProbableType,
|
|
461
|
-
typesCount,
|
|
462
|
-
valuesCountRecord ? valuesCountRecord[key] : void 0
|
|
463
|
-
);
|
|
464
|
-
});
|
|
465
|
-
return res;
|
|
380
|
+
const res = {};
|
|
381
|
+
Object.entries(typesCountRecord).forEach(([key, typesCount]) => {
|
|
382
|
+
res[key] = buildPropertyFromCount(key, totalDocsCount, getMostProbableType(typesCount), typesCount, valuesCountRecord ? valuesCountRecord[key] : void 0);
|
|
383
|
+
});
|
|
384
|
+
return res;
|
|
466
385
|
}
|
|
467
386
|
function countMaxDocumentsUnder(typesCount) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
return count;
|
|
387
|
+
let count = 0;
|
|
388
|
+
Object.entries(typesCount).forEach(([type, value]) => {
|
|
389
|
+
if (typeof value === "object") count = Math.max(count, countMaxDocumentsUnder(value));
|
|
390
|
+
else count = Math.max(count, Number(value));
|
|
391
|
+
});
|
|
392
|
+
return count;
|
|
477
393
|
}
|
|
478
394
|
function getMostProbableTypeInArray(array, getType) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
395
|
+
const typesCount = {};
|
|
396
|
+
array.forEach((value) => {
|
|
397
|
+
increaseTypeCount(getType(value), typesCount, value, getType);
|
|
398
|
+
});
|
|
399
|
+
return getMostProbableType(typesCount);
|
|
484
400
|
}
|
|
485
401
|
function checkTypesCountHighVariability(typesCount) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
return keysWithFewValues / Object.entries(typesCount.map ?? {}).length > 0.5;
|
|
402
|
+
const maxCount = countMaxDocumentsUnder(typesCount);
|
|
403
|
+
let keysWithFewValues = 0;
|
|
404
|
+
Object.entries(typesCount.map ?? {}).forEach(([key, value]) => {
|
|
405
|
+
if (countMaxDocumentsUnder(value) < maxCount / 3) keysWithFewValues++;
|
|
406
|
+
});
|
|
407
|
+
return keysWithFewValues / Object.entries(typesCount.map ?? {}).length > .5;
|
|
495
408
|
}
|
|
496
409
|
function inferTypeFromValue(value) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
410
|
+
if (value === null || value === void 0) return "string";
|
|
411
|
+
if (value instanceof Vector || value && typeof value === "object" && "__type" in value && value.__type === "Vector") return "vector";
|
|
412
|
+
if (typeof value === "string") return "string";
|
|
413
|
+
if (typeof value === "number") return "number";
|
|
414
|
+
if (typeof value === "boolean") return "boolean";
|
|
415
|
+
if (Array.isArray(value)) return "array";
|
|
416
|
+
if (typeof value === "object") return "map";
|
|
417
|
+
return "string";
|
|
505
418
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
extractEnumFromValues,
|
|
511
|
-
findCommonInitialStringInPath,
|
|
512
|
-
inferTypeFromValue,
|
|
513
|
-
isObject,
|
|
514
|
-
isPlainObject,
|
|
515
|
-
looksLikeReference,
|
|
516
|
-
mergeDeep2 as mergeDeep,
|
|
517
|
-
parseReferenceString,
|
|
518
|
-
prettifyIdentifier2 as prettifyIdentifier,
|
|
519
|
-
removeInitialAndTrailingSlashes,
|
|
520
|
-
removeInitialSlash,
|
|
521
|
-
removeTrailingSlash,
|
|
522
|
-
resolveEnumValues,
|
|
523
|
-
unslugify2 as unslugify
|
|
524
|
-
};
|
|
525
|
-
//# sourceMappingURL=index.es.js.map
|
|
419
|
+
//#endregion
|
|
420
|
+
export { buildEntityPropertiesFromData, buildPropertiesOrder, buildPropertyFromData, extractEnumFromValues, findCommonInitialStringInPath, inferTypeFromValue, isObject, isPlainObject, looksLikeReference, mergeDeep, parseReferenceString, prettifyIdentifier, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveEnumValues, unslugify };
|
|
421
|
+
|
|
422
|
+
//# sourceMappingURL=index.es.js.map
|