@rebasepro/inference 0.17.3 → 0.18.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/LICENSE CHANGED
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
package/README.md CHANGED
@@ -8,6 +8,10 @@ Automatically infer Rebase collection property schemas from sample data — anal
8
8
  pnpm add @rebasepro/inference
9
9
  ```
10
10
 
11
+ ESM-only: `"type": "module"` with no CommonJS build, so it is loaded with
12
+ `import`. `require()` of it resolves only on Node 22.12+, which supports
13
+ `require(esm)`.
14
+
11
15
  ## What This Package Does
12
16
 
13
17
  `@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.
package/package.json CHANGED
@@ -1,7 +1,26 @@
1
1
  {
2
2
  "name": "@rebasepro/inference",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
+ "description": "Infer a Rebase collection schema from existing data.",
5
+ "keywords": [
6
+ "rebase",
7
+ "schema",
8
+ "inference",
9
+ "codegen"
10
+ ],
11
+ "homepage": "https://rebase.pro",
12
+ "bugs": {
13
+ "url": "https://github.com/rebasepro/rebase/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/rebasepro/rebase.git",
18
+ "directory": "packages/inference"
19
+ },
4
20
  "license": "MIT",
21
+ "engines": {
22
+ "node": ">=22.22.0"
23
+ },
5
24
  "type": "module",
6
25
  "publishConfig": {
7
26
  "access": "public"
@@ -9,10 +28,9 @@
9
28
  "main": "./dist/index.es.js",
10
29
  "module": "./dist/index.es.js",
11
30
  "types": "dist/index.d.ts",
12
- "source": "src/index.ts",
13
31
  "dependencies": {
14
- "@rebasepro/types": "0.17.3",
15
- "@rebasepro/utils": "0.17.3"
32
+ "@rebasepro/types": "0.18.0",
33
+ "@rebasepro/utils": "0.18.0"
16
34
  },
17
35
  "devDependencies": {
18
36
  "@types/jest": "^30.0.0",
@@ -26,24 +44,20 @@
26
44
  ".": {
27
45
  "types": "./dist/index.d.ts",
28
46
  "development": "./dist/index.es.js",
29
- "import": "./dist/index.es.js"
47
+ "import": "./dist/index.es.js",
48
+ "default": "./dist/index.es.js"
30
49
  },
31
50
  "./package.json": "./package.json"
32
51
  },
33
52
  "files": [
34
- "dist",
35
- "src"
53
+ "dist"
36
54
  ],
37
55
  "gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
38
- "repository": {
39
- "type": "git",
40
- "url": "https://github.com/rebasepro/rebase.git",
41
- "directory": "packages/inference"
42
- },
43
56
  "scripts": {
44
57
  "dev": "vite",
45
58
  "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json && node ../../tooling/scripts/add-dts-extensions.mjs dist && node ../../tooling/scripts/assert-build-output.mjs",
46
59
  "test": "jest --passWithNoTests",
60
+ "test:watch": "jest --watch",
47
61
  "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
48
62
  }
49
63
  }
@@ -1,18 +0,0 @@
1
- import { findCommonInitialStringInPath } from "../strings";
2
- import { InferencePropertyBuilderProps } from "../types";
3
- import { Property } from "@rebasepro/types";
4
-
5
- export function buildReferenceProperty({
6
- name,
7
- totalDocsCount,
8
- valuesResult
9
- }: InferencePropertyBuilderProps): Property {
10
-
11
- const property: Property = {
12
- name: name ?? "",
13
- type: "reference",
14
- path: findCommonInitialStringInPath(valuesResult) ?? "!!!FIX_ME!!!"
15
- };
16
-
17
- return property;
18
- }
@@ -1,118 +0,0 @@
1
- import { findCommonInitialStringInPath } from "../strings";
2
- import { extractEnumFromValues } from "../util";
3
- import { FileType, Property, StringProperty } from "@rebasepro/types";
4
- import { InferencePropertyBuilderProps, ValuesCountEntry } from "../types";
5
-
6
- const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif"];
7
- const AUDIO_EXTENSIONS = [".mp3", ".ogg", ".opus", ".aac"];
8
- const VIDEO_EXTENSIONS = [".avi", ".mp4"];
9
-
10
- 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])?)*$/;
11
-
12
- export function buildStringProperty({
13
- name,
14
- totalDocsCount,
15
- valuesResult
16
- }: InferencePropertyBuilderProps): Property {
17
-
18
- let stringProperty: Property = {
19
- name: name ?? "",
20
- type: "string"
21
-
22
- };
23
-
24
- if (valuesResult) {
25
-
26
- const totalEntriesCount = valuesResult.values.length;
27
- const totalValues = Array.from(valuesResult.valuesCount.keys()).length;
28
-
29
- const config: Partial<StringProperty> = {};
30
-
31
- const probablyAURL = valuesResult.values
32
- .filter((value) => typeof value === "string" &&
33
- value.toString().startsWith("http")).length > totalDocsCount / 3 * 2;
34
- if (probablyAURL) {
35
- config.url = true;
36
- }
37
-
38
- const probablyAnEmail = valuesResult.values
39
- .filter((value) => typeof value === "string" &&
40
- emailRegEx.test(value)).length > totalDocsCount / 3 * 2;
41
- if (probablyAnEmail) {
42
- config.email = true;
43
- }
44
-
45
- const probablyUserIds = valuesResult.values
46
- .filter((value) => typeof value === "string" && value.length === 28 && !value.includes(" "))
47
- .length > totalDocsCount / 3 * 2;
48
- // No `admin.readOnly` here. This package is core — the field does not exist in
49
- // `@rebasepro/types`, because `@rebasepro/cms-types` declares it — and a
50
- // guess about whether a form should be editable is presentation, not schema.
51
- // (It also would not have enforced anything: nothing on the server reads
52
- // `readOnly`.) The inferred `isId` below is the part that carries meaning.
53
-
54
- if (!probablyAnEmail &&
55
- !probablyAURL &&
56
- !probablyUserIds &&
57
- !probablyAURL &&
58
- totalValues < totalEntriesCount / 3
59
- ) {
60
- const enumValues = extractEnumFromValues(Array.from(valuesResult.valuesCount.keys()));
61
-
62
- if (Object.keys(enumValues).length > 1)
63
- config.enum = enumValues;
64
- }
65
-
66
- // regular string
67
- if (!probablyAnEmail &&
68
- !probablyAURL &&
69
- !probablyUserIds &&
70
- !probablyAURL &&
71
- !config.enum) {
72
- const fileType = probableFileType(valuesResult, totalDocsCount);
73
- if (fileType) {
74
- config.storage = {
75
- acceptedFiles: fileType as FileType[],
76
- storagePath: findCommonInitialStringInPath(valuesResult) ?? "/"
77
- };
78
- }
79
- }
80
-
81
- if (Object.keys(config).length > 0)
82
- stringProperty = {
83
- ...stringProperty,
84
- ...config
85
- } as StringProperty;
86
- }
87
-
88
- return stringProperty;
89
- }
90
-
91
- function probableFileType(valuesCount: ValuesCountEntry, totalDocsCount: number): false | FileType[] {
92
- const isImage = (value: string) => IMAGE_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
93
- const isAudio = (value: string) => AUDIO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
94
- const isVideo = (value: string) => VIDEO_EXTENSIONS.some((extension) => value.toString().endsWith(extension));
95
-
96
- const stringValues = valuesCount.values.filter((v): v is string => typeof v === "string");
97
-
98
- let imageCount = 0;
99
- let audioCount = 0;
100
- let videoCount = 0;
101
-
102
- for (const value of stringValues) {
103
- if (isImage(value)) imageCount++;
104
- else if (isAudio(value)) audioCount++;
105
- else if (isVideo(value)) videoCount++;
106
- }
107
-
108
- const totalMediaCount = imageCount + audioCount + videoCount;
109
- if (totalMediaCount > (totalDocsCount * 2) / 3) {
110
- const fileTypes: FileType[] = [];
111
- if (imageCount > 0) fileTypes.push("image/*");
112
- if (audioCount > 0) fileTypes.push("audio/*");
113
- if (videoCount > 0) fileTypes.push("video/*");
114
- return fileTypes.length > 0 ? fileTypes : false;
115
- }
116
-
117
- return false;
118
- }
@@ -1,18 +0,0 @@
1
- import { PropertyValidationSchema } from "@rebasepro/types";
2
- import { InferencePropertyBuilderProps } from "../types";
3
-
4
- export function buildValidation({
5
- totalDocsCount,
6
- valuesResult
7
- }: InferencePropertyBuilderProps): PropertyValidationSchema | undefined {
8
-
9
- if (valuesResult) {
10
- const totalEntriesCount = valuesResult.values.length;
11
- if (totalDocsCount === totalEntriesCount)
12
- return {
13
- required: true
14
- }
15
- }
16
-
17
- return undefined;
18
- }
@@ -1,424 +0,0 @@
1
- import {
2
- InferencePropertyBuilderProps,
3
- TypesCount,
4
- TypesCountRecord,
5
- ValuesCountEntry,
6
- ValuesCountRecord
7
- } from "./types";
8
- import { buildStringProperty } from "./builders/string_property_builder";
9
- import { buildValidation } from "./builders/validation_builder";
10
- import { buildReferenceProperty } from "./builders/reference_property_builder";
11
- import { extractEnumFromValues, mergeDeep, prettifyIdentifier, resolveEnumValues } from "./util";
12
- import { DataType, EnumValues, Properties, Property, StringProperty, Vector } from "@rebasepro/types";
13
-
14
- export type InferenceTypeBuilder = (value: unknown) => DataType;
15
-
16
- export async function buildEntityPropertiesFromData(
17
- data: object[],
18
- getType: InferenceTypeBuilder
19
- ): Promise<Properties> {
20
- const typesCount: TypesCountRecord = {};
21
- const valuesCount: ValuesCountRecord = {};
22
- if (data) {
23
- data.forEach((entry) => {
24
- if (entry) {
25
- Object.entries(entry).forEach(([key, value]) => {
26
- if (key.startsWith("_")) return; // Ignore properties starting with _
27
- increaseMapTypeCount(typesCount, key, value, getType);
28
- increaseValuesCount(valuesCount, key, value, getType);
29
- });
30
- }
31
- });
32
- }
33
- return buildPropertiesFromCount(data.length, typesCount, valuesCount);
34
- }
35
-
36
- export function buildPropertyFromData(
37
- data: unknown[],
38
- property: Property,
39
- getType: InferenceTypeBuilder
40
- ): Property {
41
- const typesCount = {};
42
- const valuesCount: ValuesCountRecord = {};
43
- if (data) {
44
- data.forEach((entry) => {
45
- increaseTypeCount(property.type, typesCount, entry, getType);
46
- increaseValuesCount(valuesCount, "inferred_prop", entry, getType);
47
- });
48
- }
49
- const enumValues = "enum" in property ? resolveEnumValues(property["enum"] as EnumValues) : undefined;
50
- if (enumValues) {
51
- const newEnumValues = extractEnumFromValues(Array.from(valuesCount["inferred_prop"].valuesCount.keys()));
52
- return {
53
- ...property,
54
- enum: [...newEnumValues, ...enumValues]
55
- } as StringProperty;
56
- }
57
- const generatedProperty = buildPropertyFromCount(
58
- "inferred_prop",
59
- data.length,
60
- property.type,
61
- typesCount,
62
- valuesCount["inferred_prop"]
63
- );
64
- return mergeDeep(generatedProperty, property);
65
- }
66
-
67
- export function buildPropertiesOrder(
68
- properties: Properties,
69
- propertiesOrder?: string[],
70
- priorityKeys?: string[]
71
- ): string[] {
72
- const lowerCasePriorityKeys = (priorityKeys ?? []).map((key) => key.toLowerCase());
73
-
74
- function propOrder(s: string) {
75
- const k = s.toLowerCase();
76
- if (lowerCasePriorityKeys.includes(k)) return 4;
77
- if (k === "title" || k === "name") return 3;
78
- if (k.includes("title") || k.includes("name")) return 2;
79
- if (k.includes("image") || k.includes("picture")) return 1;
80
- return 0;
81
- }
82
-
83
- function deriveOrder(keys: string[]): string[] {
84
- return [...keys]
85
- .sort() // alphabetically
86
- .sort((a, b) => propOrder(b) - propOrder(a));
87
- }
88
-
89
- // A caller-supplied order is an answer, not an input. It used to be handed
90
- // to the same alphabetical + priority sort as a derived order, which threw
91
- // it away entirely — and because `Array.prototype.sort` sorts in place, the
92
- // caller's own array was silently reordered as a side effect.
93
- if (propertiesOrder) {
94
- const placed = new Set(propertiesOrder);
95
- // Properties the caller did not place — a newly inferred column, say —
96
- // still have to appear, so they follow in derived order.
97
- const rest = Object.keys(properties).filter(key => !placed.has(key));
98
- return [...propertiesOrder, ...deriveOrder(rest)];
99
- }
100
-
101
- return deriveOrder(Object.keys(properties));
102
- }
103
-
104
- /**
105
- * @param type
106
- * @param typesCount
107
- * @param fieldValue
108
- * @param getType
109
- */
110
- function increaseTypeCount(
111
- type: DataType,
112
- typesCount: TypesCount,
113
- fieldValue: unknown,
114
- getType: InferenceTypeBuilder
115
- ) {
116
- if (type === "map") {
117
- if (fieldValue) {
118
- let mapTypesCount = typesCount[type];
119
- if (!mapTypesCount) {
120
- mapTypesCount = {};
121
- typesCount[type] = mapTypesCount;
122
- }
123
- Object.entries(fieldValue as Record<string, unknown>).forEach(([key, value]) => {
124
- increaseMapTypeCount(mapTypesCount as TypesCountRecord, key, value, getType);
125
- });
126
- }
127
- } else if (type === "array") {
128
- let arrayTypesCount = typesCount[type];
129
- if (!arrayTypesCount) {
130
- arrayTypesCount = {};
131
- typesCount[type] = arrayTypesCount;
132
- }
133
- if (fieldValue && Array.isArray(fieldValue) && fieldValue.length > 0) {
134
- const arrayType = getMostProbableTypeInArray(fieldValue, getType);
135
- if (arrayType === "map") {
136
- let mapTypesCount = arrayTypesCount[arrayType];
137
- if (!mapTypesCount) {
138
- mapTypesCount = {};
139
- }
140
- fieldValue.forEach((value) => {
141
- if (value && typeof value === "object" && !Array.isArray(value)) { // Ensure value is an object for Object.entries
142
- Object.entries(value).forEach(([key, v]) =>
143
- increaseMapTypeCount(mapTypesCount, key, v, getType)
144
- );
145
- }
146
- });
147
- arrayTypesCount[arrayType] = mapTypesCount;
148
- } else {
149
- if (!arrayTypesCount[arrayType]) arrayTypesCount[arrayType] = 1;
150
- else arrayTypesCount[arrayType] = Number(arrayTypesCount[arrayType]) + 1;
151
- }
152
- }
153
- } else {
154
- if (!typesCount[type]) typesCount[type] = 1;
155
- else typesCount[type] = Number(typesCount[type]) + 1;
156
- }
157
- }
158
-
159
- function increaseMapTypeCount(
160
- typesCountRecord: TypesCountRecord,
161
- key: string,
162
- fieldValue: unknown,
163
- getType: InferenceTypeBuilder
164
- ) {
165
- if (key.startsWith("_")) return; // Ignore properties starting with _
166
-
167
- let typesCount: TypesCount = typesCountRecord[key];
168
- if (!typesCount) {
169
- typesCount = {};
170
- typesCountRecord[key] = typesCount;
171
- }
172
-
173
- if (fieldValue != null) {
174
- // Check that fieldValue is not null or undefined before proceeding
175
- const type = getType(fieldValue);
176
- increaseTypeCount(type, typesCount, fieldValue, getType);
177
- }
178
- }
179
-
180
- function increaseValuesCount(
181
- typeValuesRecord: ValuesCountRecord,
182
- key: string,
183
- fieldValue: unknown,
184
- getType: InferenceTypeBuilder
185
- ) {
186
- if (key.startsWith("_")) return; // Ignore properties starting with _
187
-
188
- const type = getType(fieldValue);
189
-
190
- let valuesRecord: {
191
- values: unknown[];
192
- valuesCount: Map<unknown, number>;
193
- map?: ValuesCountRecord;
194
- } = typeValuesRecord[key];
195
-
196
- if (!valuesRecord) {
197
- valuesRecord = {
198
- values: [],
199
- valuesCount: new Map()
200
- };
201
- typeValuesRecord[key] = valuesRecord;
202
- }
203
-
204
- if (type === "map") {
205
- let mapValuesRecord: ValuesCountRecord | undefined = valuesRecord.map;
206
- if (!mapValuesRecord) {
207
- mapValuesRecord = {};
208
- valuesRecord.map = mapValuesRecord;
209
- }
210
- if (fieldValue)
211
- Object.entries(fieldValue as Record<string, unknown>).forEach(([subKey, value]) =>
212
- increaseValuesCount(mapValuesRecord as ValuesCountRecord, subKey, value, getType)
213
- );
214
- } else if (type === "array") {
215
- if (Array.isArray(fieldValue)) {
216
- fieldValue.forEach((value) => {
217
- valuesRecord.values.push(value);
218
- valuesRecord.valuesCount.set(value, (valuesRecord.valuesCount.get(value) ?? 0) + 1);
219
- });
220
- }
221
- } else {
222
- if (fieldValue !== null && fieldValue !== undefined) {
223
- valuesRecord.values.push(fieldValue);
224
- valuesRecord.valuesCount.set(fieldValue, (valuesRecord.valuesCount.get(fieldValue) ?? 0) + 1);
225
- }
226
- }
227
- }
228
-
229
- function getHighestTypesCount(typesCount: TypesCount): number {
230
- let highestCount = 0;
231
- Object.entries(typesCount).forEach(([type, count]) => {
232
- let countValue = 0;
233
- if (type === "map") {
234
- countValue = getHighestRecordCount(count as TypesCountRecord);
235
- } else if (type === "array") {
236
- countValue = getHighestTypesCount(count as TypesCount);
237
- } else {
238
- countValue = Number(count);
239
- }
240
- if (countValue > highestCount) {
241
- highestCount = countValue;
242
- }
243
- });
244
-
245
- return highestCount;
246
- }
247
-
248
- function getHighestRecordCount(record: TypesCountRecord): number {
249
- return Object.entries(record)
250
- .map(([key, typesCount]) => getHighestTypesCount(typesCount))
251
- .reduce((a, b) => Math.max(a, b), 0);
252
- }
253
-
254
- function getMostProbableType(typesCount: TypesCount): DataType {
255
- let highestCount = -1;
256
- let probableType: DataType = "string"; // default
257
- Object.entries(typesCount).forEach(([type, count]) => {
258
- let countValue;
259
- if (type === "map") {
260
- countValue = getHighestRecordCount(count as TypesCountRecord);
261
- } else if (type === "array") {
262
- countValue = getHighestTypesCount(count as TypesCount);
263
- } else {
264
- countValue = Number(count);
265
- }
266
- if (countValue > highestCount) {
267
- highestCount = countValue;
268
- probableType = type as DataType;
269
- }
270
- });
271
- return probableType;
272
- }
273
-
274
- function buildPropertyFromCount(
275
- key: string,
276
- totalDocsCount: number,
277
- mostProbableType: DataType,
278
- typesCount: TypesCount,
279
- valuesResult?: ValuesCountEntry
280
- ): Property {
281
- let title: string | undefined;
282
-
283
- if (key) {
284
- title = prettifyIdentifier(key);
285
- }
286
-
287
- let result: Property | undefined = undefined;
288
- if (mostProbableType === "map") {
289
- const highVariability = checkTypesCountHighVariability(typesCount);
290
- if (highVariability) {
291
- result = {
292
- type: "map",
293
- name: title ?? key ?? "",
294
- keyValue: true,
295
- properties: {}
296
- };
297
- }
298
- const properties = buildPropertiesFromCount(
299
- totalDocsCount,
300
- typesCount.map as TypesCountRecord,
301
- valuesResult ? valuesResult.mapValues : undefined
302
- );
303
- result = {
304
- type: "map",
305
- name: title ?? key ?? "",
306
- properties
307
- };
308
- } else if (mostProbableType === "array") {
309
- const arrayTypesCount = typesCount.array as TypesCount;
310
- const arrayMostProbableType = getMostProbableType(arrayTypesCount);
311
- const of = buildPropertyFromCount(
312
- key,
313
- totalDocsCount,
314
- arrayMostProbableType,
315
- arrayTypesCount,
316
- valuesResult
317
- );
318
- result = {
319
- type: "array",
320
- name: title ?? key ?? "",
321
- of
322
- };
323
- }
324
-
325
- if (!result) {
326
- const propertyProps: InferencePropertyBuilderProps = {
327
- name: key,
328
- totalDocsCount,
329
- valuesResult
330
- };
331
- if (mostProbableType === "string") {
332
- result = buildStringProperty(propertyProps);
333
- } else if (mostProbableType === "reference") {
334
- result = buildReferenceProperty(propertyProps);
335
- } else {
336
- result = {
337
- type: mostProbableType
338
- } as Property;
339
- }
340
-
341
- if (title) {
342
- result.name = title;
343
- }
344
-
345
- const validation = buildValidation(propertyProps);
346
- if (validation) {
347
- result.validation = validation;
348
- }
349
- }
350
-
351
- return result;
352
- }
353
-
354
- function buildPropertiesFromCount(
355
- totalDocsCount: number,
356
- typesCountRecord: TypesCountRecord,
357
- valuesCountRecord?: ValuesCountRecord
358
- ): Properties {
359
- const res: Properties = {};
360
- Object.entries(typesCountRecord).forEach(([key, typesCount]) => {
361
- const mostProbableType = getMostProbableType(typesCount);
362
- res[key] = buildPropertyFromCount(
363
- key,
364
- totalDocsCount,
365
- mostProbableType,
366
- typesCount,
367
- valuesCountRecord ? valuesCountRecord[key] : undefined
368
- );
369
- });
370
- return res;
371
- }
372
-
373
- function countMaxDocumentsUnder(typesCount: TypesCount) {
374
- let count = 0;
375
- Object.entries(typesCount).forEach(([type, value]) => {
376
- if (typeof value === "object") {
377
- count = Math.max(count, countMaxDocumentsUnder(value as TypesCountRecord));
378
- } else {
379
- count = Math.max(count, Number(value));
380
- }
381
- });
382
- return count;
383
- }
384
-
385
- function getMostProbableTypeInArray(
386
- array: unknown[],
387
- getType: InferenceTypeBuilder
388
- ): DataType {
389
- const typesCount: TypesCount = {};
390
- array.forEach((value) => {
391
- // A hole in the array says nothing about what the array holds, so it
392
- // gets no vote. This used to fall out of `getType` answering "map" for
393
- // null and `increaseTypeCount` then skipping falsy map values; now that
394
- // null infers as "string" the skip has to be explicit, or every array
395
- // with a gap in it would drift towards "string".
396
- if (value === null || value === undefined) return;
397
- increaseTypeCount(getType(value), typesCount, value, getType);
398
- });
399
- return getMostProbableType(typesCount);
400
- }
401
-
402
- function checkTypesCountHighVariability(typesCount: TypesCount) {
403
- const maxCount = countMaxDocumentsUnder(typesCount);
404
- let keysWithFewValues = 0;
405
- Object.entries(typesCount.map ?? {}).forEach(([key, value]) => {
406
- const count = countMaxDocumentsUnder(value);
407
- if (count < maxCount / 3) {
408
- keysWithFewValues++;
409
- }
410
- });
411
- return keysWithFewValues / Object.entries(typesCount.map ?? {}).length > 0.5;
412
- }
413
-
414
-
415
- export function inferTypeFromValue(value: unknown): DataType {
416
- if (value === null || value === undefined) return "string";
417
- if (value instanceof Vector || (value && typeof value === "object" && "__type" in value && (value as Record<string, unknown>).__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
- }
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from "./collection_builder";
2
- export * from "./util";
3
- export * from "./strings";