@rebasepro/inference 0.24.0 → 0.24.1-canary.g56af4a4
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/README.md +13 -8
- package/dist/collection_builder.d.ts +9 -1
- package/dist/index.es.js +59 -16
- package/dist/index.es.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @rebasepro/inference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Infer Rebase collection property schemas from sample data: field types, enums, `required`, `email` / `url`, storage fields, and nested maps and arrays.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ of it resolves too: Node has supported `require(esm)` since 22.12.
|
|
|
14
14
|
|
|
15
15
|
## What This Package Does
|
|
16
16
|
|
|
17
|
-
`@rebasepro/inference` examines arrays of data objects and produces `Properties` definitions compatible with the Rebase collection schema. It
|
|
17
|
+
`@rebasepro/inference` examines arrays of data objects and produces `Properties` definitions compatible with the Rebase collection schema. It counts the types each field holds to pick one: a column that mixes numbers with text that is not exactly a number is a string. It also detects enum values and builds nested map/array structures. A `"reference"` property is built only when your `getType` returns `"reference"`; the default `inferTypeFromValue` never does. The admin's "create a collection from a file" import is its consumer in Rebase; database introspection does not use it.
|
|
18
18
|
|
|
19
19
|
## Key Exports
|
|
20
20
|
|
|
@@ -22,10 +22,10 @@ of it resolves too: Node has supported `require(esm)` since 22.12.
|
|
|
22
22
|
|
|
23
23
|
| Export | Type | Description |
|
|
24
24
|
|---|---|---|
|
|
25
|
-
| `buildEntityPropertiesFromData` | `(data: object[], getType: InferenceTypeBuilder) => Promise<Properties>` | Main entry — infer full property schema from data |
|
|
25
|
+
| `buildEntityPropertiesFromData` | `(data: object[], getType: InferenceTypeBuilder, options?: { ignoreKey?: (key: string) => boolean }) => Promise<Properties>` | Main entry — infer full property schema from data. `ignoreKey` leaves top-level keys out (none by default) |
|
|
26
26
|
| `buildPropertyFromData` | `(data: unknown[], property: Property, getType: InferenceTypeBuilder) => Property` | Refine an existing property with new sample data |
|
|
27
27
|
| `buildPropertiesOrder` | `(properties: Properties, propertiesOrder?: string[], priorityKeys?: string[]) => string[]` | Sort property keys (title/name first, then images, then alphabetical) |
|
|
28
|
-
| `inferTypeFromValue` | `(value: unknown) => DataType` | Default type inference: string, number, boolean, array, map, vector |
|
|
28
|
+
| `inferTypeFromValue` | `(value: unknown) => DataType` | Default type inference: string, number, boolean, date, array, map, vector |
|
|
29
29
|
| `InferenceTypeBuilder` | Type | `(value: unknown) => DataType` — pluggable type detector |
|
|
30
30
|
|
|
31
31
|
### String Utilities
|
|
@@ -60,6 +60,10 @@ const sampleData = [
|
|
|
60
60
|
{ name: "Alice", age: 30, active: true, role: "admin" },
|
|
61
61
|
{ name: "Bob", age: 25, active: false, role: "editor" },
|
|
62
62
|
{ name: "Carol", age: 28, active: true, role: "admin" },
|
|
63
|
+
{ name: "Dan", age: 41, active: true, role: "editor" },
|
|
64
|
+
{ name: "Eve", age: 35, active: false, role: "admin" },
|
|
65
|
+
{ name: "Frank", age: 52, active: true, role: "editor" },
|
|
66
|
+
{ name: "Grace", age: 23, active: true, role: "admin" },
|
|
63
67
|
];
|
|
64
68
|
|
|
65
69
|
const properties = await buildEntityPropertiesFromData(
|
|
@@ -67,11 +71,12 @@ const properties = await buildEntityPropertiesFromData(
|
|
|
67
71
|
inferTypeFromValue
|
|
68
72
|
);
|
|
69
73
|
// properties = {
|
|
70
|
-
// name: { type: "string", name: "Name",
|
|
71
|
-
// age: { type: "number", name: "Age",
|
|
72
|
-
// active: { type: "boolean", name: "Active",
|
|
73
|
-
// role: { type: "string", name: "Role", enum: [...],
|
|
74
|
+
// name: { type: "string", name: "Name", validation: { required: true } },
|
|
75
|
+
// age: { type: "number", name: "Age", validation: { required: true } },
|
|
76
|
+
// active: { type: "boolean", name: "Active", validation: { required: true } },
|
|
77
|
+
// role: { type: "string", name: "Role", enum: [...], validation: { required: true } }
|
|
74
78
|
// }
|
|
79
|
+
// An enum is inferred when a column has fewer distinct values than a third of its values.
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
## Related Packages
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { DataType, Properties, Property } from "@rebasepro/types";
|
|
2
2
|
export type InferenceTypeBuilder = (value: unknown) => DataType;
|
|
3
|
-
export
|
|
3
|
+
export type BuildEntityPropertiesOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Top-level keys to leave out of the inferred properties — Firestore
|
|
6
|
+
* metadata such as `__name__`, say. Nothing is left out by default: a
|
|
7
|
+
* column is data, `_id` in a Mongo export included.
|
|
8
|
+
*/
|
|
9
|
+
ignoreKey?: (key: string) => boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function buildEntityPropertiesFromData(data: object[], getType: InferenceTypeBuilder, options?: BuildEntityPropertiesOptions): Promise<Properties>;
|
|
4
12
|
export declare function buildPropertyFromData(data: unknown[], property: Property, getType: InferenceTypeBuilder): Property;
|
|
5
13
|
export declare function buildPropertiesOrder(properties: Properties, propertiesOrder?: string[], priorityKeys?: string[]): string[];
|
|
6
14
|
export declare function inferTypeFromValue(value: unknown): DataType;
|
package/dist/index.es.js
CHANGED
|
@@ -74,13 +74,18 @@ function removeTrailingSlash(s) {
|
|
|
74
74
|
*/
|
|
75
75
|
function extractEnumFromValues(values) {
|
|
76
76
|
if (!Array.isArray(values)) return [];
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
const seen = /* @__PURE__ */ new Set();
|
|
78
|
+
const enumValues = [];
|
|
79
|
+
for (const value of values) {
|
|
80
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") continue;
|
|
81
|
+
const id = String(value);
|
|
82
|
+
if (seen.has(id)) continue;
|
|
83
|
+
seen.add(id);
|
|
84
|
+
enumValues.push({
|
|
85
|
+
id,
|
|
86
|
+
label: typeof value === "string" ? unslugify$1(value) : id
|
|
87
|
+
});
|
|
88
|
+
}
|
|
84
89
|
enumValues.sort((a, b) => a.label.localeCompare(b.label));
|
|
85
90
|
return enumValues;
|
|
86
91
|
}
|
|
@@ -110,6 +115,14 @@ var AUDIO_EXTENSIONS = [
|
|
|
110
115
|
];
|
|
111
116
|
var VIDEO_EXTENSIONS = [".avi", ".mp4"];
|
|
112
117
|
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])?)*$/;
|
|
118
|
+
var EMAIL_SHAPE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
119
|
+
function isAbsoluteUrl(value) {
|
|
120
|
+
try {
|
|
121
|
+
return Boolean(new URL(value).protocol);
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
113
126
|
function buildStringProperty({ name, totalDocsCount, valuesResult }) {
|
|
114
127
|
let stringProperty = {
|
|
115
128
|
name: name ?? "",
|
|
@@ -119,9 +132,11 @@ function buildStringProperty({ name, totalDocsCount, valuesResult }) {
|
|
|
119
132
|
const totalEntriesCount = valuesResult.values.length;
|
|
120
133
|
const totalValues = Array.from(valuesResult.valuesCount.keys()).length;
|
|
121
134
|
const config = {};
|
|
122
|
-
const
|
|
135
|
+
const nonBlank = valuesResult.values.filter((value) => value !== "" && value !== null && value !== void 0);
|
|
136
|
+
const everyValue = (test) => nonBlank.length > 0 && nonBlank.every((value) => typeof value === "string" && test(value));
|
|
137
|
+
const probablyAURL = everyValue((value) => value.startsWith("http") && isAbsoluteUrl(value));
|
|
123
138
|
if (probablyAURL) config.url = true;
|
|
124
|
-
const probablyAnEmail =
|
|
139
|
+
const probablyAnEmail = everyValue((value) => emailRegEx.test(value) && EMAIL_SHAPE.test(value));
|
|
125
140
|
if (probablyAnEmail) config.email = true;
|
|
126
141
|
const probablyUserIds = valuesResult.values.filter((value) => typeof value === "string" && value.length === 28 && !value.includes(" ")).length > totalDocsCount / 3 * 2;
|
|
127
142
|
if (!probablyAnEmail && !probablyAURL && !probablyUserIds && !probablyAURL && totalValues < totalEntriesCount / 3) {
|
|
@@ -228,12 +243,12 @@ function readNumberExactly(text) {
|
|
|
228
243
|
function ownEntry(record, key) {
|
|
229
244
|
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
230
245
|
}
|
|
231
|
-
async function buildEntityPropertiesFromData(data, getType) {
|
|
246
|
+
async function buildEntityPropertiesFromData(data, getType, options = {}) {
|
|
232
247
|
const typesCount = {};
|
|
233
248
|
const valuesCount = {};
|
|
234
249
|
if (data) data.forEach((entry) => {
|
|
235
250
|
if (entry) Object.entries(entry).forEach(([key, value]) => {
|
|
236
|
-
if (
|
|
251
|
+
if (options.ignoreKey?.(key)) return;
|
|
237
252
|
increaseMapTypeCount(typesCount, key, value, getType);
|
|
238
253
|
increaseValuesCount(valuesCount, key, value, getType);
|
|
239
254
|
});
|
|
@@ -326,7 +341,6 @@ function isBlank(value) {
|
|
|
326
341
|
return value === null || value === void 0 || value === "";
|
|
327
342
|
}
|
|
328
343
|
function increaseMapTypeCount(typesCountRecord, key, fieldValue, getType) {
|
|
329
|
-
if (key.startsWith("_")) return;
|
|
330
344
|
let typesCount = ownEntry(typesCountRecord, key);
|
|
331
345
|
if (!typesCount) {
|
|
332
346
|
typesCount = {};
|
|
@@ -335,7 +349,6 @@ function increaseMapTypeCount(typesCountRecord, key, fieldValue, getType) {
|
|
|
335
349
|
if (!isBlank(fieldValue)) increaseTypeCount(getType(fieldValue), typesCount, fieldValue, getType);
|
|
336
350
|
}
|
|
337
351
|
function increaseValuesCount(typeValuesRecord, key, fieldValue, getType) {
|
|
338
|
-
if (key.startsWith("_")) return;
|
|
339
352
|
const type = getType(fieldValue);
|
|
340
353
|
const valuesRecord = ownEntry(typeValuesRecord, key) ?? {
|
|
341
354
|
values: [],
|
|
@@ -343,10 +356,10 @@ function increaseValuesCount(typeValuesRecord, key, fieldValue, getType) {
|
|
|
343
356
|
};
|
|
344
357
|
typeValuesRecord[key] = valuesRecord;
|
|
345
358
|
if (type === "map") {
|
|
346
|
-
let mapValuesRecord = valuesRecord.
|
|
359
|
+
let mapValuesRecord = valuesRecord.mapValues;
|
|
347
360
|
if (!mapValuesRecord) {
|
|
348
361
|
mapValuesRecord = {};
|
|
349
|
-
valuesRecord.
|
|
362
|
+
valuesRecord.mapValues = mapValuesRecord;
|
|
350
363
|
}
|
|
351
364
|
if (fieldValue) Object.entries(fieldValue).forEach(([subKey, value]) => increaseValuesCount(mapValuesRecord, subKey, value, getType));
|
|
352
365
|
} else if (type === "array") {
|
|
@@ -447,7 +460,8 @@ function buildPropertyFromCount(key, totalDocsCount, mostProbableType, typesCoun
|
|
|
447
460
|
totalDocsCount,
|
|
448
461
|
valuesResult
|
|
449
462
|
};
|
|
450
|
-
if (mostProbableType === "string") result =
|
|
463
|
+
if ((mostProbableType === "number" || mostProbableType === "string") && isEpochColumn(key, valuesResult?.values)) result = { type: "date" };
|
|
464
|
+
else if (mostProbableType === "string") result = buildStringProperty(propertyProps);
|
|
451
465
|
else if (mostProbableType === "reference") result = buildReferenceProperty(propertyProps);
|
|
452
466
|
else result = { type: mostProbableType };
|
|
453
467
|
if (title) result.name = title;
|
|
@@ -456,6 +470,34 @@ function buildPropertyFromCount(key, totalDocsCount, mostProbableType, typesCoun
|
|
|
456
470
|
}
|
|
457
471
|
return result;
|
|
458
472
|
}
|
|
473
|
+
/** The words that end a column name holding a moment: `created_at`, `updatedOn`, `birth_date`, `timestamp`. */
|
|
474
|
+
var MOMENT_NAME_ENDINGS = /* @__PURE__ */ new Set([
|
|
475
|
+
"at",
|
|
476
|
+
"on",
|
|
477
|
+
"date",
|
|
478
|
+
"time",
|
|
479
|
+
"datetime",
|
|
480
|
+
"timestamp",
|
|
481
|
+
"ts"
|
|
482
|
+
]);
|
|
483
|
+
/** An epoch in seconds from 2001-09-09 to 2103, or in milliseconds over the same span. */
|
|
484
|
+
function isEpochNumber(value) {
|
|
485
|
+
return Number.isInteger(value) && (value >= 1e9 && value < 42e8 || value >= 0xe8d4a51000 && value < 42e11);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Whether a column holds epoch timestamps: its name ends in a word that says
|
|
489
|
+
* it holds a moment, and every value it has is a whole number of seconds or
|
|
490
|
+
* milliseconds since 1970 (written as a number, or as the digits a CSV
|
|
491
|
+
* writes) in the range a timestamp of this century falls in. Both, because
|
|
492
|
+
* either alone is an order number or a `response_time` in milliseconds. The
|
|
493
|
+
* import reads such a value into a date as soon as the column is one.
|
|
494
|
+
*/
|
|
495
|
+
function isEpochColumn(key, values) {
|
|
496
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
497
|
+
if (words.length === 0 || !MOMENT_NAME_ENDINGS.has(words[words.length - 1])) return false;
|
|
498
|
+
const present = (values ?? []).filter((value) => !isBlank(value));
|
|
499
|
+
return present.length > 0 && present.every((value) => typeof value === "number" ? isEpochNumber(value) : typeof value === "string" && /^\d+$/.test(value.trim()) && isEpochNumber(Number(value.trim())));
|
|
500
|
+
}
|
|
459
501
|
function buildPropertiesFromCount(totalDocsCount, typesCountRecord, valuesCountRecord) {
|
|
460
502
|
const res = {};
|
|
461
503
|
Object.entries(typesCountRecord).forEach(([key, typesCount]) => {
|
|
@@ -495,6 +537,7 @@ function inferTypeFromValue(value) {
|
|
|
495
537
|
if (typeof value === "string") return "string";
|
|
496
538
|
if (typeof value === "number") return "number";
|
|
497
539
|
if (typeof value === "boolean") return "boolean";
|
|
540
|
+
if (value instanceof Date) return "date";
|
|
498
541
|
if (Array.isArray(value)) return "array";
|
|
499
542
|
if (typeof value === "object") return "map";
|
|
500
543
|
return "string";
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +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/numbers.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.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 // No `admin.readOnly` here. This package is core — the field does not exist in\n // `@rebasepro/types`, because `@rebasepro/cms-types` declares it — and a\n // guess about whether a form should be editable is presentation, not schema.\n // (It also would not have enforced anything: nothing on the server reads\n // `readOnly`.) The inferred `isId` below is the part that carries meaning.\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","/**\n * A double holds 15 significant decimal digits exactly; a 16th may already be\n * rounded (`9007199254740993` reads back as …992).\n */\nconst MAX_EXACT_SIGNIFICANT_DIGITS = 15;\n\n/**\n * JSON's number grammar: an optional minus, an integer part with no leading\n * zero, an optional fraction and an optional exponent. No `+`, no thousands\n * separator, no currency or percent sign, no hexadecimal, no `Infinity`.\n */\nconst NUMBER_SPELLING = /^-?(0|[1-9]\\d*)(\\.(\\d+))?([eE][+-]?\\d+)?$/;\n\n/**\n * The number a text spells, when it spells one that a number gives back with\n * nothing lost — otherwise `undefined`.\n *\n * This is the one answer to \"is this text a number\" for both the inference\n * that picks a column's type and the import that converts its cells, so the\n * two cannot disagree. It refuses what `Number()` would forgive and silently\n * change: a leading zero (`02134` is a zip code, `00123` a code — as numbers\n * they lose the zero), more significant digits than a double holds exactly (a\n * 20-digit SKU), and anything that is not a plain decimal (`1,234`, `$1.00`,\n * `12%`, `+15551234567`, `N/A`). Formatting a number does not carry —\n * `10.00` and `1e3` — is allowed: their value is exact.\n */\nexport function readNumberExactly(text: string): number | undefined {\n const trimmed = text.trim();\n const match = NUMBER_SPELLING.exec(trimmed);\n if (!match) return undefined;\n const integerPart = match[1];\n const fraction = (match[3] ?? \"\").replace(/0+$/, \"\");\n const significant = (integerPart + fraction).replace(/^0+/, \"\");\n if (significant.length > MAX_EXACT_SIGNIFICANT_DIGITS) return undefined;\n const value = Number(trimmed);\n // `1e400` overflows to Infinity and `1e-400` underflows to 0.\n if (!Number.isFinite(value) || (value === 0 && significant !== \"\")) return undefined;\n return value;\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 { readNumberExactly } from \"./numbers\";\nimport { DataType, EnumValues, Properties, Property, StringProperty, Vector } from \"@rebasepro/types\";\n\nexport type InferenceTypeBuilder = (value: unknown) => DataType;\n\n/**\n * The entry a count record holds for a column, if it holds one.\n *\n * The records are keyed by column names out of the data, and a plain object\n * answers `record[\"constructor\"]` or `record[\"toString\"]` with what it\n * inherits: the count was written onto the global `Object`, and the values\n * list was `Object.values`, so importing a file with a `constructor` column\n * threw. Only an own entry is one this record counted.\n */\nfunction ownEntry<T>(record: Record<string, T>, key: string): T | undefined {\n return Object.hasOwn(record, key) ? record[key] : undefined;\n}\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 function deriveOrder(keys: string[]): string[] {\n return [...keys]\n .sort() // alphabetically\n .sort((a, b) => propOrder(b) - propOrder(a));\n }\n\n // A caller-supplied order is an answer, not an input. It used to be handed\n // to the same alphabetical + priority sort as a derived order, which threw\n // it away entirely — and because `Array.prototype.sort` sorts in place, the\n // caller's own array was silently reordered as a side effect.\n if (propertiesOrder) {\n const placed = new Set(propertiesOrder);\n // Properties the caller did not place — a newly inferred column, say —\n // still have to appear, so they follow in derived order.\n const rest = Object.keys(properties).filter(key => !placed.has(key));\n return [...propertiesOrder, ...deriveOrder(rest)];\n }\n\n return deriveOrder(Object.keys(properties));\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\n/**\n * A cell nobody filled in. A CSV reader hands over `\"\"` for it where a\n * spreadsheet reader hands over nothing, and the import writes neither — so it\n * is no value here either: it casts no vote for the column's type and does not\n * count towards `required` or an enum.\n */\nfunction isBlank(value: unknown): boolean {\n return value === null || value === undefined || value === \"\";\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 | undefined = ownEntry(typesCountRecord, key);\n if (!typesCount) {\n typesCount = {};\n typesCountRecord[key] = typesCount;\n }\n\n if (!isBlank(fieldValue)) {\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 const valuesRecord: {\n values: unknown[];\n valuesCount: Map<unknown, number>;\n map?: ValuesCountRecord;\n } = ownEntry(typeValuesRecord, key) ?? {\n values: [],\n valuesCount: new Map()\n };\n typeValuesRecord[key] = valuesRecord;\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 if (isBlank(value)) return;\n valuesRecord.values.push(value);\n valuesRecord.valuesCount.set(value, (valuesRecord.valuesCount.get(value) ?? 0) + 1);\n });\n }\n } else {\n if (!isBlank(fieldValue)) {\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\n/**\n * The types a cell can be converted between: the import turns any of them into\n * text without loss, and text back into one of them only when the text spells\n * it.\n */\nconst SCALAR_TYPES: ReadonlySet<string> = new Set([\"string\", \"number\", \"boolean\", \"date\"]);\n\n/**\n * A column's type, from the votes its values cast.\n *\n * When the votes mix scalar types — numbers beside text, booleans beside\n * numbers — the majority is not an answer: converting the minority to it\n * changes them (`02134` → 2134, a 20-digit SKU rounded, `N/A` → nothing,\n * 2 → `false`). Such a column is text, which holds every one of them as\n * written. The one exception is numbers beside text that spells numbers\n * exactly (`10.00`, `1e3`): that column is a number, and nothing is lost.\n *\n * @param values every non-blank value the votes were cast for, when known;\n * without them, a mix of numbers and text is text.\n */\nfunction getMostProbableType(typesCount: TypesCount, values?: unknown[]): DataType {\n const scalarTypes = Object.entries(typesCount)\n .filter(([type, count]) => SCALAR_TYPES.has(type) && Number(count) > 0)\n .map(([type]) => type);\n if (scalarTypes.length > 1) {\n const numbersBesideNumericText = scalarTypes.every(type => type === \"number\" || type === \"string\")\n && values !== undefined\n && values.every(value => typeof value !== \"string\" || readNumberExactly(value) !== undefined);\n return numbersBesideNumericText ? \"number\" : \"string\";\n }\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 // The values recorded for an array column are its items.\n const arrayMostProbableType = getMostProbableType(arrayTypesCount, valuesResult?.values);\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 valuesEntry = valuesCountRecord ? ownEntry(valuesCountRecord, key) : undefined;\n const mostProbableType = getMostProbableType(typesCount, valuesEntry?.values);\n res[key] = buildPropertyFromCount(\n key,\n totalDocsCount,\n mostProbableType,\n typesCount,\n valuesEntry\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 // A hole in the array says nothing about what the array holds, so it\n // gets no vote. This used to fall out of `getType` answering \"map\" for\n // null and `increaseTypeCount` then skipping falsy map values; now that\n // null infers as \"string\" the skip has to be explicit, or every array\n // with a gap in it would drift towards \"string\".\n if (isBlank(value)) return;\n increaseTypeCount(getType(value), typesCount, value, getType);\n });\n return getMostProbableType(typesCount, array.filter(value => !isBlank(value)));\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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"}
|
|
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/numbers.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 // A number or a boolean in a text column is one of its options too (`10`\n // and `12` beside `S`, `M`, `L`): left out, the enum refused the rows it\n // was inferred from. Its id is its text, which is what the import writes.\n const seen = new Set<string>();\n const enumValues: Array<{ id: string, label: string }> = [];\n for (const value of values) {\n if (typeof value !== \"string\" && typeof value !== \"number\" && typeof value !== \"boolean\") continue;\n const id = String(value);\n if (seen.has(id)) continue;\n seen.add(id);\n enumValues.push({ id, label: typeof value === \"string\" ? unslugify(value) : id });\n }\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\n// The shape the server checks an `email` property against\n// (`write-validation.ts`): the regex above alone accepts `a@localhost`.\nconst EMAIL_SHAPE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nfunction isAbsoluteUrl(value: string): boolean {\n try {\n return Boolean(new URL(value).protocol);\n } catch {\n return false;\n }\n}\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 // `url` and `email` are constraints a write is checked against, so\n // they hold only when every value the column has meets them: set at\n // two thirds, the import refused the rest of the rows it was inferred\n // from. A blank is no value.\n const nonBlank = valuesResult.values.filter((value) => value !== \"\" && value !== null && value !== undefined);\n const everyValue = (test: (value: string) => boolean) =>\n nonBlank.length > 0 && nonBlank.every((value) => typeof value === \"string\" && test(value));\n\n const probablyAURL = everyValue((value) => value.startsWith(\"http\") && isAbsoluteUrl(value));\n if (probablyAURL) {\n config.url = true;\n }\n\n const probablyAnEmail = everyValue((value) => emailRegEx.test(value) && EMAIL_SHAPE.test(value));\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 // No `admin.readOnly` here. This package is core — the field does not exist in\n // `@rebasepro/types`, because `@rebasepro/cms-types` declares it — and a\n // guess about whether a form should be editable is presentation, not schema.\n // (It also would not have enforced anything: nothing on the server reads\n // `readOnly`.) The inferred `isId` below is the part that carries meaning.\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","/**\n * A double holds 15 significant decimal digits exactly; a 16th may already be\n * rounded (`9007199254740993` reads back as …992).\n */\nconst MAX_EXACT_SIGNIFICANT_DIGITS = 15;\n\n/**\n * JSON's number grammar: an optional minus, an integer part with no leading\n * zero, an optional fraction and an optional exponent. No `+`, no thousands\n * separator, no currency or percent sign, no hexadecimal, no `Infinity`.\n */\nconst NUMBER_SPELLING = /^-?(0|[1-9]\\d*)(\\.(\\d+))?([eE][+-]?\\d+)?$/;\n\n/**\n * The number a text spells, when it spells one that a number gives back with\n * nothing lost — otherwise `undefined`.\n *\n * This is the one answer to \"is this text a number\" for both the inference\n * that picks a column's type and the import that converts its cells, so the\n * two cannot disagree. It refuses what `Number()` would forgive and silently\n * change: a leading zero (`02134` is a zip code, `00123` a code — as numbers\n * they lose the zero), more significant digits than a double holds exactly (a\n * 20-digit SKU), and anything that is not a plain decimal (`1,234`, `$1.00`,\n * `12%`, `+15551234567`, `N/A`). Formatting a number does not carry —\n * `10.00` and `1e3` — is allowed: their value is exact.\n */\nexport function readNumberExactly(text: string): number | undefined {\n const trimmed = text.trim();\n const match = NUMBER_SPELLING.exec(trimmed);\n if (!match) return undefined;\n const integerPart = match[1];\n const fraction = (match[3] ?? \"\").replace(/0+$/, \"\");\n const significant = (integerPart + fraction).replace(/^0+/, \"\");\n if (significant.length > MAX_EXACT_SIGNIFICANT_DIGITS) return undefined;\n const value = Number(trimmed);\n // `1e400` overflows to Infinity and `1e-400` underflows to 0.\n if (!Number.isFinite(value) || (value === 0 && significant !== \"\")) return undefined;\n return value;\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 { readNumberExactly } from \"./numbers\";\nimport { DataType, EnumValues, Properties, Property, StringProperty, Vector } from \"@rebasepro/types\";\n\nexport type InferenceTypeBuilder = (value: unknown) => DataType;\n\n/**\n * The entry a count record holds for a column, if it holds one.\n *\n * The records are keyed by column names out of the data, and a plain object\n * answers `record[\"constructor\"]` or `record[\"toString\"]` with what it\n * inherits: the count was written onto the global `Object`, and the values\n * list was `Object.values`, so importing a file with a `constructor` column\n * threw. Only an own entry is one this record counted.\n */\nfunction ownEntry<T>(record: Record<string, T>, key: string): T | undefined {\n return Object.hasOwn(record, key) ? record[key] : undefined;\n}\n\nexport type BuildEntityPropertiesOptions = {\n /**\n * Top-level keys to leave out of the inferred properties — Firestore\n * metadata such as `__name__`, say. Nothing is left out by default: a\n * column is data, `_id` in a Mongo export included.\n */\n ignoreKey?: (key: string) => boolean;\n};\n\nexport async function buildEntityPropertiesFromData(\n data: object[],\n getType: InferenceTypeBuilder,\n options: BuildEntityPropertiesOptions = {}\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 (options.ignoreKey?.(key)) return;\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 function deriveOrder(keys: string[]): string[] {\n return [...keys]\n .sort() // alphabetically\n .sort((a, b) => propOrder(b) - propOrder(a));\n }\n\n // A caller-supplied order is an answer, not an input. It used to be handed\n // to the same alphabetical + priority sort as a derived order, which threw\n // it away entirely — and because `Array.prototype.sort` sorts in place, the\n // caller's own array was silently reordered as a side effect.\n if (propertiesOrder) {\n const placed = new Set(propertiesOrder);\n // Properties the caller did not place — a newly inferred column, say —\n // still have to appear, so they follow in derived order.\n const rest = Object.keys(properties).filter(key => !placed.has(key));\n return [...propertiesOrder, ...deriveOrder(rest)];\n }\n\n return deriveOrder(Object.keys(properties));\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\n/**\n * A cell nobody filled in. A CSV reader hands over `\"\"` for it where a\n * spreadsheet reader hands over nothing, and the import writes neither — so it\n * is no value here either: it casts no vote for the column's type and does not\n * count towards `required` or an enum.\n */\nfunction isBlank(value: unknown): boolean {\n return value === null || value === undefined || value === \"\";\n}\n\nfunction increaseMapTypeCount(\n typesCountRecord: TypesCountRecord,\n key: string,\n fieldValue: unknown,\n getType: InferenceTypeBuilder\n) {\n let typesCount: TypesCount | undefined = ownEntry(typesCountRecord, key);\n if (!typesCount) {\n typesCount = {};\n typesCountRecord[key] = typesCount;\n }\n\n if (!isBlank(fieldValue)) {\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 const type = getType(fieldValue);\n\n const valuesRecord: ValuesCountEntry = ownEntry(typeValuesRecord, key) ?? {\n values: [],\n valuesCount: new Map()\n };\n typeValuesRecord[key] = valuesRecord;\n\n if (type === \"map\") {\n // Under `mapValues`, the field `buildPropertyFromCount` reads. Stored\n // under `map`, a nested field's values were never seen: no enum, no\n // `required`, and no number-from-text decision for it.\n let mapValuesRecord: ValuesCountRecord | undefined = valuesRecord.mapValues;\n if (!mapValuesRecord) {\n mapValuesRecord = {};\n valuesRecord.mapValues = 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 if (isBlank(value)) return;\n valuesRecord.values.push(value);\n valuesRecord.valuesCount.set(value, (valuesRecord.valuesCount.get(value) ?? 0) + 1);\n });\n }\n } else {\n if (!isBlank(fieldValue)) {\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\n/**\n * The types a cell can be converted between: the import turns any of them into\n * text without loss, and text back into one of them only when the text spells\n * it.\n */\nconst SCALAR_TYPES: ReadonlySet<string> = new Set([\"string\", \"number\", \"boolean\", \"date\"]);\n\n/**\n * A column's type, from the votes its values cast.\n *\n * When the votes mix scalar types — numbers beside text, booleans beside\n * numbers — the majority is not an answer: converting the minority to it\n * changes them (`02134` → 2134, a 20-digit SKU rounded, `N/A` → nothing,\n * 2 → `false`). Such a column is text, which holds every one of them as\n * written. The one exception is numbers beside text that spells numbers\n * exactly (`10.00`, `1e3`): that column is a number, and nothing is lost.\n *\n * @param values every non-blank value the votes were cast for, when known;\n * without them, a mix of numbers and text is text.\n */\nfunction getMostProbableType(typesCount: TypesCount, values?: unknown[]): DataType {\n const scalarTypes = Object.entries(typesCount)\n .filter(([type, count]) => SCALAR_TYPES.has(type) && Number(count) > 0)\n .map(([type]) => type);\n if (scalarTypes.length > 1) {\n const numbersBesideNumericText = scalarTypes.every(type => type === \"number\" || type === \"string\")\n && values !== undefined\n && values.every(value => typeof value !== \"string\" || readNumberExactly(value) !== undefined);\n return numbersBesideNumericText ? \"number\" : \"string\";\n }\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 // The values recorded for an array column are its items.\n const arrayMostProbableType = getMostProbableType(arrayTypesCount, valuesResult?.values);\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 === \"number\" || mostProbableType === \"string\") && isEpochColumn(key, valuesResult?.values)) {\n result = { type: \"date\" } as Property;\n } else 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\n/** The words that end a column name holding a moment: `created_at`, `updatedOn`, `birth_date`, `timestamp`. */\nconst MOMENT_NAME_ENDINGS = new Set([\"at\", \"on\", \"date\", \"time\", \"datetime\", \"timestamp\", \"ts\"]);\n\n/** An epoch in seconds from 2001-09-09 to 2103, or in milliseconds over the same span. */\nfunction isEpochNumber(value: number): boolean {\n return Number.isInteger(value)\n && ((value >= 1e9 && value < 4.2e9) || (value >= 1e12 && value < 4.2e12));\n}\n\n/**\n * Whether a column holds epoch timestamps: its name ends in a word that says\n * it holds a moment, and every value it has is a whole number of seconds or\n * milliseconds since 1970 (written as a number, or as the digits a CSV\n * writes) in the range a timestamp of this century falls in. Both, because\n * either alone is an order number or a `response_time` in milliseconds. The\n * import reads such a value into a date as soon as the column is one.\n */\nfunction isEpochColumn(key: string, values: unknown[] | undefined): boolean {\n const words = key.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);\n if (words.length === 0 || !MOMENT_NAME_ENDINGS.has(words[words.length - 1])) return false;\n const present = (values ?? []).filter(value => !isBlank(value));\n return present.length > 0 && present.every(value =>\n typeof value === \"number\"\n ? isEpochNumber(value)\n : typeof value === \"string\" && /^\\d+$/.test(value.trim()) && isEpochNumber(Number(value.trim())));\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 valuesEntry = valuesCountRecord ? ownEntry(valuesCountRecord, key) : undefined;\n const mostProbableType = getMostProbableType(typesCount, valuesEntry?.values);\n res[key] = buildPropertyFromCount(\n key,\n totalDocsCount,\n mostProbableType,\n typesCount,\n valuesEntry\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 // A hole in the array says nothing about what the array holds, so it\n // gets no vote. This used to fall out of `getType` answering \"map\" for\n // null and `increaseTypeCount` then skipping falsy map values; now that\n // null infers as \"string\" the skip has to be explicit, or every array\n // with a gap in it would drift towards \"string\".\n if (isBlank(value)) return;\n increaseTypeCount(getType(value), typesCount, value, getType);\n });\n return getMostProbableType(typesCount, array.filter(value => !isBlank(value)));\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 // A Date is an object, so it read as a map — while the CMS's own detector\n // (`getInferenceType`) calls it a date. The two defaults agree now.\n if (value instanceof Date) return \"date\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"object\") return \"map\";\n return \"string\";\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/inference",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.1-canary.g56af4a4",
|
|
4
4
|
"description": "Infer a Rebase collection schema from existing data.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rebase",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"module": "./dist/index.es.js",
|
|
30
30
|
"types": "dist/index.d.ts",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@rebasepro/types": "0.24.
|
|
33
|
-
"@rebasepro/utils": "0.24.
|
|
32
|
+
"@rebasepro/types": "0.24.1-canary.g56af4a4",
|
|
33
|
+
"@rebasepro/utils": "0.24.1-canary.g56af4a4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/jest": "^30.0.0",
|