@rebasepro/inference 0.0.1-canary.4829d6e

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rebase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @rebasepro/inference
2
+
3
+ Automatically infer Rebase collection property schemas from sample data — analyzes field types, detects enums, references, validation rules, and nested structures.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/inference
9
+ ```
10
+
11
+ ## What This Package Does
12
+
13
+ `@rebasepro/inference` examines arrays of data objects and produces `Properties` definitions compatible with the Rebase collection schema. It uses statistical analysis to determine the most probable type for each field, detect enum values, identify reference patterns, and build nested map/array structures. Used by the Rebase introspection pipeline and data import tools.
14
+
15
+ ## Key Exports
16
+
17
+ ### Collection Builder
18
+
19
+ | Export | Type | Description |
20
+ |---|---|---|
21
+ | `buildSnapshotPropertiesFromData` | `(data: object[], getType: InferenceTypeBuilder) => Promise<Properties>` | Main entry — infer full property schema from data |
22
+ | `buildPropertyFromData` | `(data: unknown[], property: Property, getType: InferenceTypeBuilder) => Property` | Refine an existing property with new sample data |
23
+ | `buildPropertiesOrder` | `(properties: Properties, propertiesOrder?: string[], priorityKeys?: string[]) => string[]` | Sort property keys (title/name first, then images, then alphabetical) |
24
+ | `inferTypeFromValue` | `(value: unknown) => DataType` | Default type inference: string, number, boolean, array, map, vector |
25
+ | `InferenceTypeBuilder` | Type | `(value: unknown) => DataType` — pluggable type detector |
26
+
27
+ ### String Utilities
28
+
29
+ | Export | Description |
30
+ |---|---|
31
+ | `parseReferenceString` | Parse `"path/snapshotId"` or `"db:::path/snapshotId"` format |
32
+ | `looksLikeReference` | Check if a string looks like a document reference |
33
+ | `findCommonInitialStringInPath` | Find shared collection path prefix in sample values |
34
+ | `removeInitialAndTrailingSlashes` | Path cleanup |
35
+ | `removeInitialSlash` / `removeTrailingSlash` | Path cleanup |
36
+
37
+ ### General Utilities (re-exported from `@rebasepro/utils`)
38
+
39
+ | Export | Description |
40
+ |---|---|
41
+ | `extractEnumFromValues` | Extract enum entries from sample string values |
42
+ | `resolveEnumValues` | Normalize `EnumValues` to `EnumValueConfig[]` |
43
+ | `prettifyIdentifier` | `"snake_case"` → `"Snake Case"` |
44
+ | `unslugify` | `"my-slug"` → `"My Slug"` |
45
+ | `mergeDeep` | Deep merge objects |
46
+
47
+ ## Quick Start
48
+
49
+ ```typescript
50
+ import {
51
+ buildSnapshotPropertiesFromData,
52
+ inferTypeFromValue
53
+ } from "@rebasepro/inference";
54
+
55
+ const sampleData = [
56
+ { name: "Alice", age: 30, active: true, role: "admin" },
57
+ { name: "Bob", age: 25, active: false, role: "editor" },
58
+ { name: "Carol", age: 28, active: true, role: "admin" },
59
+ ];
60
+
61
+ const properties = await buildSnapshotPropertiesFromData(
62
+ sampleData,
63
+ inferTypeFromValue
64
+ );
65
+ // properties = {
66
+ // name: { type: "string", name: "Name", ... },
67
+ // age: { type: "number", name: "Age", ... },
68
+ // active: { type: "boolean", name: "Active", ... },
69
+ // role: { type: "string", name: "Role", enum: [...], ... }
70
+ // }
71
+ ```
72
+
73
+ ## Related Packages
74
+
75
+ - `@rebasepro/types` — Provides `Property`, `DataType`, `Properties`, and related types
76
+ - `@rebasepro/utils` — General utilities re-exported by this package
@@ -0,0 +1,3 @@
1
+ import { InferencePropertyBuilderProps } from "../types";
2
+ import { Property } from "@rebasepro/types";
3
+ export declare function buildReferenceProperty({ name, totalDocsCount, valuesResult }: InferencePropertyBuilderProps): Property;
@@ -0,0 +1,3 @@
1
+ import { Property } from "@rebasepro/types";
2
+ import { InferencePropertyBuilderProps } from "../types";
3
+ export declare function buildStringProperty({ name, totalDocsCount, valuesResult }: InferencePropertyBuilderProps): Property;
@@ -0,0 +1,3 @@
1
+ import { PropertyValidationSchema } from "@rebasepro/types";
2
+ import { InferencePropertyBuilderProps } from "../types";
3
+ export declare function buildValidation({ totalDocsCount, valuesResult }: InferencePropertyBuilderProps): PropertyValidationSchema | undefined;
@@ -0,0 +1,6 @@
1
+ import { DataType, Properties, Property } from "@rebasepro/types";
2
+ export type InferenceTypeBuilder = (value: unknown) => DataType;
3
+ export declare function buildEntityPropertiesFromData(data: object[], getType: InferenceTypeBuilder): Promise<Properties>;
4
+ export declare function buildPropertyFromData(data: unknown[], property: Property, getType: InferenceTypeBuilder): Property;
5
+ export declare function buildPropertiesOrder(properties: Properties, propertiesOrder?: string[], priorityKeys?: string[]): string[];
6
+ export declare function inferTypeFromValue(value: unknown): DataType;
@@ -0,0 +1,3 @@
1
+ export * from "./collection_builder";
2
+ export * from "./util";
3
+ export * from "./strings";
@@ -0,0 +1,422 @@
1
+ import { isObject, isPlainObject, mergeDeep, prettifyIdentifier, unslugify, unslugify as unslugify$1 } from "@rebasepro/utils";
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
+ */
10
+ function parseReferenceString(value) {
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
+ };
24
+ }
25
+ /**
26
+ * Check if a string value looks like a reference
27
+ */
28
+ function looksLikeReference(value) {
29
+ if (typeof value !== "string") return false;
30
+ return parseReferenceString(value) !== null;
31
+ }
32
+ function findCommonInitialStringInPath(valuesCount) {
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;
57
+ }
58
+ function removeInitialAndTrailingSlashes(s) {
59
+ return removeInitialSlash(removeTrailingSlash(s));
60
+ }
61
+ function removeInitialSlash(s) {
62
+ if (s.startsWith("/")) return s.slice(1);
63
+ else return s;
64
+ }
65
+ function removeTrailingSlash(s) {
66
+ if (s.endsWith("/")) return s.slice(0, -1);
67
+ else return s;
68
+ }
69
+ //#endregion
70
+ //#region src/util.ts
71
+ /**
72
+ * Extract enum values from a list of sample values.
73
+ * This is inference-specific logic (not a general utility).
74
+ */
75
+ function extractEnumFromValues(values) {
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;
86
+ }
87
+ function resolveEnumValues(input) {
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;
94
+ }
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;
148
+ }
149
+ function probableFileType(valuesCount, totalDocsCount) {
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;
168
+ }
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
+ }
175
+ }
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
+ };
184
+ }
185
+ //#endregion
186
+ //#region src/collection_builder.ts
187
+ async function buildEntityPropertiesFromData(data, getType) {
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);
198
+ }
199
+ function buildPropertyFromData(data, property, getType) {
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);
215
+ }
216
+ function buildPropertiesOrder(properties, propertiesOrder, priorityKeys) {
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;
232
+ }
233
+ /**
234
+ * @param type
235
+ * @param typesCount
236
+ * @param fieldValue
237
+ * @param getType
238
+ */
239
+ function increaseTypeCount(type, typesCount, fieldValue, getType) {
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;
271
+ }
272
+ function increaseMapTypeCount(typesCountRecord, key, fieldValue, getType) {
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);
280
+ }
281
+ function increaseValuesCount(typeValuesRecord, key, fieldValue, getType) {
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
+ }
308
+ }
309
+ function getHighestTypesCount(typesCount) {
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;
319
+ }
320
+ function getHighestRecordCount(record) {
321
+ return Object.entries(record).map(([key, typesCount]) => getHighestTypesCount(typesCount)).reduce((a, b) => Math.max(a, b), 0);
322
+ }
323
+ function getMostProbableType(typesCount) {
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;
337
+ }
338
+ function buildPropertyFromCount(key, totalDocsCount, mostProbableType, typesCount, valuesResult) {
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;
378
+ }
379
+ function buildPropertiesFromCount(totalDocsCount, typesCountRecord, valuesCountRecord) {
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;
385
+ }
386
+ function countMaxDocumentsUnder(typesCount) {
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;
393
+ }
394
+ function getMostProbableTypeInArray(array, getType) {
395
+ const typesCount = {};
396
+ array.forEach((value) => {
397
+ increaseTypeCount(getType(value), typesCount, value, getType);
398
+ });
399
+ return getMostProbableType(typesCount);
400
+ }
401
+ function checkTypesCountHighVariability(typesCount) {
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;
408
+ }
409
+ function inferTypeFromValue(value) {
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";
418
+ }
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../src/strings.ts","../src/util.ts","../src/builders/string_property_builder.ts","../src/builders/validation_builder.ts","../src/builders/reference_property_builder.ts","../src/collection_builder.ts"],"sourcesContent":["import { ValuesCountEntry } from \"./types\";\n\n/**\n * Parse a reference string value which can be in the format:\n * - Simple: \"path/entityId\"\n * - With database: \"database_name:::path/entityId\"\n * Returns the path and database (undefined if not specified or if \"(default)\")\n */\nexport function parseReferenceString(value: string): { path: string; database?: string } | null {\n if (!value) return null;\n\n let database: string | undefined = undefined;\n let fullPath = value;\n\n // Parse the new format: database_name:::path/entityId\n if (value.includes(\":::\")) {\n const [dbName, pathPart] = value.split(\":::\");\n if (dbName && dbName !== \"(default)\") {\n database = dbName;\n }\n fullPath = pathPart;\n }\n\n // Check if it looks like a path (contains at least one slash)\n if (!fullPath || !fullPath.includes(\"/\")) {\n return null;\n }\n\n // Extract the collection path (everything before the last slash)\n const path = fullPath.substring(0, fullPath.lastIndexOf(\"/\"));\n\n return { path,\ndatabase };\n}\n\n/**\n * Check if a string value looks like a reference\n */\nexport function looksLikeReference(value: unknown): boolean {\n if (typeof value !== \"string\") return false;\n return parseReferenceString(value) !== null;\n}\n\nexport function findCommonInitialStringInPath(valuesCount?: ValuesCountEntry) {\n\n if (!valuesCount) return undefined;\n\n function getPath(value: unknown): string | undefined {\n let pathString: string | undefined;\n\n if (typeof value === \"string\") {\n pathString = value;\n } else if (value && typeof value === \"object\" && \"slug\" in value && typeof (value as Record<string, unknown>).slug === \"string\") {\n pathString = (value as Record<string, unknown>).slug as string;\n } else {\n console.warn(\"findCommonInitialStringInPath: value is not a string or document with path\", value);\n return undefined;\n }\n\n if (!pathString) return undefined;\n\n // Parse the new format: database_name:::path/entityId\n // Extract just the path portion for comparison\n if (pathString.includes(\":::\")) {\n const [, pathPart] = pathString.split(\":::\");\n pathString = pathPart;\n }\n\n return pathString;\n }\n\n const strings: string[] = valuesCount.values.map((v) => getPath(v)).filter(v => !!v) as string[];\n const pathWithSlash = strings.find((s) => s.includes(\"/\"));\n if (!pathWithSlash)\n return undefined;\n\n const searchedPath = pathWithSlash.substring(0, pathWithSlash.lastIndexOf(\"/\"));\n\n const yep = valuesCount.values\n .filter((value) => {\n const path = getPath(value);\n if (!path) return false;\n return path.startsWith(searchedPath)\n }).length > valuesCount.values.length / 3 * 2;\n\n return yep ? searchedPath : undefined;\n\n}\n\nexport function removeInitialAndTrailingSlashes(s: string): string {\n return removeInitialSlash(removeTrailingSlash(s));\n}\n\nexport function removeInitialSlash(s: string) {\n if (s.startsWith(\"/\"))\n return s.slice(1);\n else return s;\n}\n\nexport function removeTrailingSlash(s: string) {\n if (s.endsWith(\"/\"))\n return s.slice(0, -1);\n else return s;\n}\n","import { EnumValueConfig, EnumValues } from \"@rebasepro/types\";\nimport { unslugify } from \"@rebasepro/utils\";\n\n// Canonical utility functions — single source of truth in @rebasepro/utils\nexport { unslugify, prettifyIdentifier, isObject, isPlainObject, mergeDeep } from \"@rebasepro/utils\";\n\n/**\n * Extract enum values from a list of sample values.\n * This is inference-specific logic (not a general utility).\n */\nexport function extractEnumFromValues(values: unknown[]) {\n if (!Array.isArray(values)) {\n return [];\n }\n const enumValues = values\n .map((value) => {\n if (typeof value === \"string\") {\n return ({\n id: value,\n label: unslugify(value)\n });\n } else\n return null;\n }).filter(Boolean) as Array<{ id: string, label: string }>;\n enumValues.sort((a, b) => a.label.localeCompare(b.label));\n return enumValues;\n}\n\nexport function resolveEnumValues(input: EnumValues): EnumValueConfig[] | undefined {\n // Check Array.isArray first since typeof [] === \"object\" is true in JavaScript\n if (Array.isArray(input)) {\n return input as EnumValueConfig[];\n } else if (typeof input === \"object\" && input !== null) {\n return Object.entries(input).map(([id, value]) =>\n (typeof value === \"string\"\n ? {\n id,\n label: value\n }\n : value));\n } else {\n return undefined;\n }\n}\n","import { findCommonInitialStringInPath } from \"../strings\";\nimport { extractEnumFromValues } from \"../util\";\nimport { FileType, Property, StringProperty } from \"@rebasepro/types\";\nimport { InferencePropertyBuilderProps, ValuesCountEntry } from \"../types\";\n\nconst IMAGE_EXTENSIONS = [\".jpg\", \".jpeg\", \".png\", \".webp\", \".gif\", \".avif\"];\nconst AUDIO_EXTENSIONS = [\".mp3\", \".ogg\", \".opus\", \".aac\"];\nconst VIDEO_EXTENSIONS = [\".avi\", \".mp4\"];\n\nconst 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])?)*$/;\n\nexport function buildStringProperty({\n name,\n totalDocsCount,\n valuesResult\n}: InferencePropertyBuilderProps): Property {\n\n let stringProperty: Property = {\n name: name ?? \"\",\n type: \"string\"\n\n };\n\n if (valuesResult) {\n\n const totalEntriesCount = valuesResult.values.length;\n const totalValues = Array.from(valuesResult.valuesCount.keys()).length;\n\n const config: Partial<StringProperty> = {};\n\n const probablyAURL = valuesResult.values\n .filter((value) => typeof value === \"string\" &&\n value.toString().startsWith(\"http\")).length > totalDocsCount / 3 * 2;\n if (probablyAURL) {\n config.ui = { url: true };\n }\n\n const probablyAnEmail = valuesResult.values\n .filter((value) => typeof value === \"string\" &&\n emailRegEx.test(value)).length > totalDocsCount / 3 * 2;\n if (probablyAnEmail) {\n config.email = true;\n }\n\n const probablyUserIds = valuesResult.values\n .filter((value) => typeof value === \"string\" && value.length === 28 && !value.includes(\" \"))\n .length > totalDocsCount / 3 * 2;\n if (probablyUserIds)\n config.ui = { ...config.ui,\nreadOnly: true };\n\n if (!probablyAnEmail &&\n !probablyAURL &&\n !probablyUserIds &&\n !probablyAURL &&\n totalValues < totalEntriesCount / 3\n ) {\n const enumValues = extractEnumFromValues(Array.from(valuesResult.valuesCount.keys()));\n\n if (Object.keys(enumValues).length > 1)\n config.enum = enumValues;\n }\n\n // regular string\n if (!probablyAnEmail &&\n !probablyAURL &&\n !probablyUserIds &&\n !probablyAURL &&\n !config.enum) {\n const fileType = probableFileType(valuesResult, totalDocsCount);\n if (fileType) {\n config.storage = {\n acceptedFiles: fileType as FileType[],\n storagePath: findCommonInitialStringInPath(valuesResult) ?? \"/\"\n };\n }\n }\n\n if (Object.keys(config).length > 0)\n stringProperty = {\n ...stringProperty,\n ...config\n } as StringProperty;\n }\n\n return stringProperty;\n}\n\nfunction probableFileType(valuesCount: ValuesCountEntry, totalDocsCount: number): false | FileType[] {\n const isImage = (value: string) => IMAGE_EXTENSIONS.some((extension) => value.toString().endsWith(extension));\n const isAudio = (value: string) => AUDIO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));\n const isVideo = (value: string) => VIDEO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));\n\n const stringValues = valuesCount.values.filter((v): v is string => typeof v === \"string\");\n\n let imageCount = 0;\n let audioCount = 0;\n let videoCount = 0;\n\n for (const value of stringValues) {\n if (isImage(value)) imageCount++;\n else if (isAudio(value)) audioCount++;\n else if (isVideo(value)) videoCount++;\n }\n\n const totalMediaCount = imageCount + audioCount + videoCount;\n if (totalMediaCount > (totalDocsCount * 2) / 3) {\n const fileTypes: FileType[] = [];\n if (imageCount > 0) fileTypes.push(\"image/*\");\n if (audioCount > 0) fileTypes.push(\"audio/*\");\n if (videoCount > 0) fileTypes.push(\"video/*\");\n return fileTypes.length > 0 ? fileTypes : false;\n }\n\n return false;\n}\n","import { PropertyValidationSchema } from \"@rebasepro/types\";\nimport { InferencePropertyBuilderProps } from \"../types\";\n\nexport function buildValidation({\n totalDocsCount,\n valuesResult\n}: InferencePropertyBuilderProps): PropertyValidationSchema | undefined {\n\n if (valuesResult) {\n const totalEntriesCount = valuesResult.values.length;\n if (totalDocsCount === totalEntriesCount)\n return {\n required: true\n }\n }\n\n return undefined;\n}\n","import { findCommonInitialStringInPath } from \"../strings\";\nimport { InferencePropertyBuilderProps } from \"../types\";\nimport { Property } from \"@rebasepro/types\";\n\nexport function buildReferenceProperty({\n name,\n totalDocsCount,\n valuesResult\n}: InferencePropertyBuilderProps): Property {\n\n const property: Property = {\n name: name ?? \"\",\n type: \"reference\",\n path: findCommonInitialStringInPath(valuesResult) ?? \"!!!FIX_ME!!!\"\n };\n\n return property;\n}\n","import {\n InferencePropertyBuilderProps,\n TypesCount,\n TypesCountRecord,\n ValuesCountEntry,\n ValuesCountRecord\n} from \"./types\";\nimport { buildStringProperty } from \"./builders/string_property_builder\";\nimport { buildValidation } from \"./builders/validation_builder\";\nimport { buildReferenceProperty } from \"./builders/reference_property_builder\";\nimport { extractEnumFromValues, mergeDeep, prettifyIdentifier, resolveEnumValues } from \"./util\";\nimport { DataType, EnumValues, Properties, Property, StringProperty, Vector } from \"@rebasepro/types\";\n\nexport type InferenceTypeBuilder = (value: unknown) => DataType;\n\nexport async function buildEntityPropertiesFromData(\n data: object[],\n getType: InferenceTypeBuilder\n): Promise<Properties> {\n const typesCount: TypesCountRecord = {};\n const valuesCount: ValuesCountRecord = {};\n if (data) {\n data.forEach((entry) => {\n if (entry) {\n Object.entries(entry).forEach(([key, value]) => {\n if (key.startsWith(\"_\")) return; // Ignore properties starting with _\n increaseMapTypeCount(typesCount, key, value, getType);\n increaseValuesCount(valuesCount, key, value, getType);\n });\n }\n });\n }\n return buildPropertiesFromCount(data.length, typesCount, valuesCount);\n}\n\nexport function buildPropertyFromData(\n data: unknown[],\n property: Property,\n getType: InferenceTypeBuilder\n): Property {\n const typesCount = {};\n const valuesCount: ValuesCountRecord = {};\n if (data) {\n data.forEach((entry) => {\n increaseTypeCount(property.type, typesCount, entry, getType);\n increaseValuesCount(valuesCount, \"inferred_prop\", entry, getType);\n });\n }\n const enumValues = \"enum\" in property ? resolveEnumValues(property[\"enum\"] as EnumValues) : undefined;\n if (enumValues) {\n const newEnumValues = extractEnumFromValues(Array.from(valuesCount[\"inferred_prop\"].valuesCount.keys()));\n return {\n ...property,\n enum: [...newEnumValues, ...enumValues]\n } as StringProperty;\n }\n const generatedProperty = buildPropertyFromCount(\n \"inferred_prop\",\n data.length,\n property.type,\n typesCount,\n valuesCount[\"inferred_prop\"]\n );\n return mergeDeep(generatedProperty, property);\n}\n\nexport function buildPropertiesOrder(\n properties: Properties,\n propertiesOrder?: string[],\n priorityKeys?: string[]\n): string[] {\n const lowerCasePriorityKeys = (priorityKeys ?? []).map((key) => key.toLowerCase());\n\n function propOrder(s: string) {\n const k = s.toLowerCase();\n if (lowerCasePriorityKeys.includes(k)) return 4;\n if (k === \"title\" || k === \"name\") return 3;\n if (k.includes(\"title\") || k.includes(\"name\")) return 2;\n if (k.includes(\"image\") || k.includes(\"picture\")) return 1;\n return 0;\n }\n\n const keys = propertiesOrder ?? Object.keys(properties);\n keys.sort(); // alphabetically\n keys.sort((a, b) => {\n return propOrder(b) - propOrder(a);\n });\n return keys;\n}\n\n/**\n * @param type\n * @param typesCount\n * @param fieldValue\n * @param getType\n */\nfunction increaseTypeCount(\n type: DataType,\n typesCount: TypesCount,\n fieldValue: unknown,\n getType: InferenceTypeBuilder\n) {\n if (type === \"map\") {\n if (fieldValue) {\n let mapTypesCount = typesCount[type];\n if (!mapTypesCount) {\n mapTypesCount = {};\n typesCount[type] = mapTypesCount;\n }\n Object.entries(fieldValue as Record<string, unknown>).forEach(([key, value]) => {\n increaseMapTypeCount(mapTypesCount as TypesCountRecord, key, value, getType);\n });\n }\n } else if (type === \"array\") {\n let arrayTypesCount = typesCount[type];\n if (!arrayTypesCount) {\n arrayTypesCount = {};\n typesCount[type] = arrayTypesCount;\n }\n if (fieldValue && Array.isArray(fieldValue) && fieldValue.length > 0) {\n const arrayType = getMostProbableTypeInArray(fieldValue, getType);\n if (arrayType === \"map\") {\n let mapTypesCount = arrayTypesCount[arrayType];\n if (!mapTypesCount) {\n mapTypesCount = {};\n }\n fieldValue.forEach((value) => {\n if (value && typeof value === \"object\" && !Array.isArray(value)) { // Ensure value is an object for Object.entries\n Object.entries(value).forEach(([key, v]) =>\n increaseMapTypeCount(mapTypesCount, key, v, getType)\n );\n }\n });\n arrayTypesCount[arrayType] = mapTypesCount;\n } else {\n if (!arrayTypesCount[arrayType]) arrayTypesCount[arrayType] = 1;\n else arrayTypesCount[arrayType] = Number(arrayTypesCount[arrayType]) + 1;\n }\n }\n } else {\n if (!typesCount[type]) typesCount[type] = 1;\n else typesCount[type] = Number(typesCount[type]) + 1;\n }\n}\n\nfunction increaseMapTypeCount(\n typesCountRecord: TypesCountRecord,\n key: string,\n fieldValue: unknown,\n getType: InferenceTypeBuilder\n) {\n if (key.startsWith(\"_\")) return; // Ignore properties starting with _\n\n let typesCount: TypesCount = typesCountRecord[key];\n if (!typesCount) {\n typesCount = {};\n typesCountRecord[key] = typesCount;\n }\n\n if (fieldValue != null) {\n // Check that fieldValue is not null or undefined before proceeding\n const type = getType(fieldValue);\n increaseTypeCount(type, typesCount, fieldValue, getType);\n }\n}\n\nfunction increaseValuesCount(\n typeValuesRecord: ValuesCountRecord,\n key: string,\n fieldValue: unknown,\n getType: InferenceTypeBuilder\n) {\n if (key.startsWith(\"_\")) return; // Ignore properties starting with _\n\n const type = getType(fieldValue);\n\n let valuesRecord: {\n values: unknown[];\n valuesCount: Map<unknown, number>;\n map?: ValuesCountRecord;\n } = typeValuesRecord[key];\n\n if (!valuesRecord) {\n valuesRecord = {\n values: [],\n valuesCount: new Map()\n };\n typeValuesRecord[key] = valuesRecord;\n }\n\n if (type === \"map\") {\n let mapValuesRecord: ValuesCountRecord | undefined = valuesRecord.map;\n if (!mapValuesRecord) {\n mapValuesRecord = {};\n valuesRecord.map = mapValuesRecord;\n }\n if (fieldValue)\n Object.entries(fieldValue as Record<string, unknown>).forEach(([subKey, value]) =>\n increaseValuesCount(mapValuesRecord as ValuesCountRecord, subKey, value, getType)\n );\n } else if (type === \"array\") {\n if (Array.isArray(fieldValue)) {\n fieldValue.forEach((value) => {\n valuesRecord.values.push(value);\n valuesRecord.valuesCount.set(value, (valuesRecord.valuesCount.get(value) ?? 0) + 1);\n });\n }\n } else {\n if (fieldValue !== null && fieldValue !== undefined) {\n valuesRecord.values.push(fieldValue);\n valuesRecord.valuesCount.set(fieldValue, (valuesRecord.valuesCount.get(fieldValue) ?? 0) + 1);\n }\n }\n}\n\nfunction getHighestTypesCount(typesCount: TypesCount): number {\n let highestCount = 0;\n Object.entries(typesCount).forEach(([type, count]) => {\n let countValue = 0;\n if (type === \"map\") {\n countValue = getHighestRecordCount(count as TypesCountRecord);\n } else if (type === \"array\") {\n countValue = getHighestTypesCount(count as TypesCount);\n } else {\n countValue = Number(count);\n }\n if (countValue > highestCount) {\n highestCount = countValue;\n }\n });\n\n return highestCount;\n}\n\nfunction getHighestRecordCount(record: TypesCountRecord): number {\n return Object.entries(record)\n .map(([key, typesCount]) => getHighestTypesCount(typesCount))\n .reduce((a, b) => Math.max(a, b), 0);\n}\n\nfunction getMostProbableType(typesCount: TypesCount): DataType {\n let highestCount = -1;\n let probableType: DataType = \"string\"; // default\n Object.entries(typesCount).forEach(([type, count]) => {\n let countValue;\n if (type === \"map\") {\n countValue = getHighestRecordCount(count as TypesCountRecord);\n } else if (type === \"array\") {\n countValue = getHighestTypesCount(count as TypesCount);\n } else {\n countValue = Number(count);\n }\n if (countValue > highestCount) {\n highestCount = countValue;\n probableType = type as DataType;\n }\n });\n return probableType;\n}\n\nfunction buildPropertyFromCount(\n key: string,\n totalDocsCount: number,\n mostProbableType: DataType,\n typesCount: TypesCount,\n valuesResult?: ValuesCountEntry\n): Property {\n let title: string | undefined;\n\n if (key) {\n title = prettifyIdentifier(key);\n }\n\n let result: Property | undefined = undefined;\n if (mostProbableType === \"map\") {\n const highVariability = checkTypesCountHighVariability(typesCount);\n if (highVariability) {\n result = {\n type: \"map\",\n name: title ?? key ?? \"\",\n keyValue: true,\n properties: {}\n };\n }\n const properties = buildPropertiesFromCount(\n totalDocsCount,\n typesCount.map as TypesCountRecord,\n valuesResult ? valuesResult.mapValues : undefined\n );\n result = {\n type: \"map\",\n name: title ?? key ?? \"\",\n properties\n };\n } else if (mostProbableType === \"array\") {\n const arrayTypesCount = typesCount.array as TypesCount;\n const arrayMostProbableType = getMostProbableType(arrayTypesCount);\n const of = buildPropertyFromCount(\n key,\n totalDocsCount,\n arrayMostProbableType,\n arrayTypesCount,\n valuesResult\n );\n result = {\n type: \"array\",\n name: title ?? key ?? \"\",\n of\n };\n }\n\n if (!result) {\n const propertyProps: InferencePropertyBuilderProps = {\n name: key,\n totalDocsCount,\n valuesResult\n };\n if (mostProbableType === \"string\") {\n result = buildStringProperty(propertyProps);\n } else if (mostProbableType === \"reference\") {\n result = buildReferenceProperty(propertyProps);\n } else {\n result = {\n type: mostProbableType\n } as Property;\n }\n\n if (title) {\n result.name = title;\n }\n\n const validation = buildValidation(propertyProps);\n if (validation) {\n result.validation = validation;\n }\n }\n\n return result;\n}\n\nfunction buildPropertiesFromCount(\n totalDocsCount: number,\n typesCountRecord: TypesCountRecord,\n valuesCountRecord?: ValuesCountRecord\n): Properties {\n const res: Properties = {};\n Object.entries(typesCountRecord).forEach(([key, typesCount]) => {\n const mostProbableType = getMostProbableType(typesCount);\n res[key] = buildPropertyFromCount(\n key,\n totalDocsCount,\n mostProbableType,\n typesCount,\n valuesCountRecord ? valuesCountRecord[key] : undefined\n );\n });\n return res;\n}\n\nfunction countMaxDocumentsUnder(typesCount: TypesCount) {\n let count = 0;\n Object.entries(typesCount).forEach(([type, value]) => {\n if (typeof value === \"object\") {\n count = Math.max(count, countMaxDocumentsUnder(value as TypesCountRecord));\n } else {\n count = Math.max(count, Number(value));\n }\n });\n return count;\n}\n\nfunction getMostProbableTypeInArray(\n array: unknown[],\n getType: InferenceTypeBuilder\n): DataType {\n const typesCount: TypesCount = {};\n array.forEach((value) => {\n increaseTypeCount(getType(value), typesCount, value, getType);\n });\n return getMostProbableType(typesCount);\n}\n\nfunction checkTypesCountHighVariability(typesCount: TypesCount) {\n const maxCount = countMaxDocumentsUnder(typesCount);\n let keysWithFewValues = 0;\n Object.entries(typesCount.map ?? {}).forEach(([key, value]) => {\n const count = countMaxDocumentsUnder(value);\n if (count < maxCount / 3) {\n keysWithFewValues++;\n }\n });\n return keysWithFewValues / Object.entries(typesCount.map ?? {}).length > 0.5;\n}\n\n\nexport function inferTypeFromValue(value: unknown): DataType {\n if (value === null || value === undefined) return \"string\";\n if (value instanceof Vector || (value && typeof value === \"object\" && \"__type\" in value && (value as Record<string, unknown>).__type === \"Vector\")) return \"vector\";\n if (typeof value === \"string\") return \"string\";\n if (typeof value === \"number\") return \"number\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"object\") return \"map\";\n return \"string\";\n}\n"],"mappings":"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"}