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