hikoutei 0.10.23-dev → 0.10.25-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/Hikoutei.d.ts.map +1 -1
- package/dist/api/Hikoutei.js +8 -4
- package/dist/api/Hikoutei.js.map +1 -1
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/syncRuntime.d.ts +8 -1
- package/dist/api/syncRuntime.d.ts.map +1 -1
- package/dist/api/syncRuntime.js +5 -0
- package/dist/api/syncRuntime.js.map +1 -1
- package/dist/cli/adoptArgs.d.ts +8 -0
- package/dist/cli/adoptArgs.d.ts.map +1 -1
- package/dist/cli/adoptArgs.js +40 -12
- package/dist/cli/adoptArgs.js.map +1 -1
- package/dist/cli/adoptMain.d.ts +17 -0
- package/dist/cli/adoptMain.d.ts.map +1 -1
- package/dist/cli/adoptMain.js +76 -5
- package/dist/cli/adoptMain.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/infer.d.ts +118 -0
- package/dist/cli/infer.d.ts.map +1 -0
- package/dist/cli/infer.js +319 -0
- package/dist/cli/infer.js.map +1 -0
- package/dist/cli/inferArgs.d.ts +54 -0
- package/dist/cli/inferArgs.d.ts.map +1 -0
- package/dist/cli/inferArgs.js +123 -0
- package/dist/cli/inferArgs.js.map +1 -0
- package/dist/cli/inferMain.d.ts +66 -0
- package/dist/cli/inferMain.d.ts.map +1 -0
- package/dist/cli/inferMain.js +281 -0
- package/dist/cli/inferMain.js.map +1 -0
- package/dist/composition/localRuntime.d.ts.map +1 -1
- package/dist/composition/localRuntime.js +3 -1
- package/dist/composition/localRuntime.js.map +1 -1
- package/dist/contracts/api/descriptorFile.d.ts +116 -0
- package/dist/contracts/api/descriptorFile.d.ts.map +1 -0
- package/dist/contracts/api/descriptorFile.js +179 -0
- package/dist/contracts/api/descriptorFile.js.map +1 -0
- package/dist/sync-engine/api/entity.d.ts +1 -0
- package/dist/sync-engine/api/entity.d.ts.map +1 -1
- package/dist/sync-engine/api/entity.js +1 -0
- package/dist/sync-engine/api/entity.js.map +1 -1
- package/dist/sync-engine/api/hikouteiCore.d.ts +31 -1
- package/dist/sync-engine/api/hikouteiCore.d.ts.map +1 -1
- package/dist/sync-engine/api/hikouteiCore.js +29 -0
- package/dist/sync-engine/api/hikouteiCore.js.map +1 -1
- package/dist/sync-engine/sync/service/syncAutoStart.d.ts +8 -1
- package/dist/sync-engine/sync/service/syncAutoStart.d.ts.map +1 -1
- package/dist/sync-engine/sync/service/syncAutoStart.js +13 -6
- package/dist/sync-engine/sync/service/syncAutoStart.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure `hikoutei infer` core: raw grid cells in, descriptor block out.
|
|
3
|
+
*
|
|
4
|
+
* The flow is injectable and network-free: the production CLI reads one tab
|
|
5
|
+
* range through the Sheets API and hands the raw grid here; tests inject a
|
|
6
|
+
* fake grid. Cell normalization ports the provider's getValues semantics
|
|
7
|
+
* (formula cells resolve to their computed effective value, the canonical
|
|
8
|
+
* DATE_TIME number format marks dates, blank stays null) so Sheets-provided
|
|
9
|
+
* typed cell info wins over heuristics — there are no date-format guesses
|
|
10
|
+
* beyond the canonical format the provider itself writes.
|
|
11
|
+
*
|
|
12
|
+
* Source parity notes: the canonical date pattern is byte-identical to
|
|
13
|
+
* `GOOGLE_SHEETS_API_DATE_NUMBER_FORMAT` and the serial math matches
|
|
14
|
+
* `isoFromDateSerial` in the google-sheets-api provider's
|
|
15
|
+
* `model/valueNormalization.ts`. A local port (not an import) keeps the CLI
|
|
16
|
+
* runtime dependency-closed: `@hikoutei/sheets` is not a CLI dependency.
|
|
17
|
+
*
|
|
18
|
+
* Redaction contract: warnings carry COLUMN NAMES ONLY, never cell values.
|
|
19
|
+
*/
|
|
20
|
+
import { type HikouteiDescriptorFile } from "../sync-engine/api/entity.js";
|
|
21
|
+
/** Machine-readable CLI error prefix, shared with inferMain.ts. */
|
|
22
|
+
export declare const INFER_ERROR_PREFIX = "hikoutei-infer";
|
|
23
|
+
/** Closed set of scalar kinds the inference can emit. */
|
|
24
|
+
export type InferScalarType = "string" | "number" | "boolean" | "date";
|
|
25
|
+
/** One normalized cell: `null` is an empty cell. */
|
|
26
|
+
export type InferCell = null | {
|
|
27
|
+
readonly kind: "string";
|
|
28
|
+
readonly value: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: "number";
|
|
31
|
+
readonly value: number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: "boolean";
|
|
34
|
+
readonly value: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: "date";
|
|
37
|
+
readonly value: string;
|
|
38
|
+
};
|
|
39
|
+
/** Raw tab grid handed to the inference (raw Sheets grid cell objects). */
|
|
40
|
+
export interface InferTabGrid {
|
|
41
|
+
readonly tabName: string;
|
|
42
|
+
/** Raw grid cells of the header row (rowData values entries). */
|
|
43
|
+
readonly headerCells: readonly unknown[];
|
|
44
|
+
/** Raw grid cells of sampled data rows (rowData values entries per row). */
|
|
45
|
+
readonly dataCells: readonly (readonly unknown[])[];
|
|
46
|
+
}
|
|
47
|
+
/** Injectable tab sampler; production wraps the Sheets API, tests use fakes. */
|
|
48
|
+
export type InferTabReader = (input: {
|
|
49
|
+
readonly spreadsheetId: string;
|
|
50
|
+
readonly tabName: string;
|
|
51
|
+
readonly limit: number;
|
|
52
|
+
}) => Promise<InferTabGrid>;
|
|
53
|
+
/** One column-name-only inference warning. */
|
|
54
|
+
export interface InferWarning {
|
|
55
|
+
readonly column: string;
|
|
56
|
+
readonly reason: string;
|
|
57
|
+
}
|
|
58
|
+
/** One headed column of a successful inference (feeds `--emit`). */
|
|
59
|
+
export interface InferColumn {
|
|
60
|
+
/** Original sheet header, preserved for adopt's column mapping. */
|
|
61
|
+
readonly header: string;
|
|
62
|
+
/** camelCase entity property name derived from the header. */
|
|
63
|
+
readonly property: string;
|
|
64
|
+
/** Inferred scalar type (PK-normalized: a boolean/date PK reads string). */
|
|
65
|
+
readonly type: InferScalarType;
|
|
66
|
+
/** True for the single primary-key/business-key column. */
|
|
67
|
+
readonly primary: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** Successful inference: the TS block plus its summary. */
|
|
70
|
+
export interface InferDescriptorResult {
|
|
71
|
+
readonly entityName: string;
|
|
72
|
+
readonly tableName: string;
|
|
73
|
+
readonly tsBlock: string;
|
|
74
|
+
readonly summary: string;
|
|
75
|
+
readonly warnings: readonly InferWarning[];
|
|
76
|
+
readonly sampledRows: number;
|
|
77
|
+
readonly columnCount: number;
|
|
78
|
+
readonly distribution: Readonly<Record<InferScalarType, number>>;
|
|
79
|
+
readonly pkProperty: string;
|
|
80
|
+
/**
|
|
81
|
+
* Headed columns in sheet order. `--emit` builds the descriptor file
|
|
82
|
+
* from these, so the JSON file and the printed block always agree.
|
|
83
|
+
*/
|
|
84
|
+
readonly columns: readonly InferColumn[];
|
|
85
|
+
}
|
|
86
|
+
/** Coded failure thrown by the pure inference (mapped to stderr + exit 1/2). */
|
|
87
|
+
export declare class InferError extends Error {
|
|
88
|
+
readonly code: string;
|
|
89
|
+
constructor(code: string, message: string);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Normalizes one raw grid cell with getValues semantics: formula cells
|
|
93
|
+
* resolve to their computed effective value, error cells to their display
|
|
94
|
+
* string, literals keep their typed kind, blank cells become `null`.
|
|
95
|
+
*/
|
|
96
|
+
export declare function normalizeInferCell(cell: unknown): InferCell;
|
|
97
|
+
/**
|
|
98
|
+
* Infers the entity descriptor block from a raw tab grid.
|
|
99
|
+
*
|
|
100
|
+
* Throws `InferError`: `empty_tab` (no header row), `pk_not_found` (the
|
|
101
|
+
* `--pk` header names no sampled column). Warnings carry column names only.
|
|
102
|
+
*/
|
|
103
|
+
export declare function inferFromGrid(grid: InferTabGrid, options?: {
|
|
104
|
+
readonly pkHeader?: string;
|
|
105
|
+
}): InferDescriptorResult;
|
|
106
|
+
/**
|
|
107
|
+
* Builds the versioned descriptor file for an inference result.
|
|
108
|
+
*
|
|
109
|
+
* Pure and network-free: `--emit` serializes this file next to the printed
|
|
110
|
+
* block, and the file's `header` fields later feed `adopt --descriptor`
|
|
111
|
+
* without manual `--map` flags. Delegates to the contracts builder so the
|
|
112
|
+
* file envelope has one owner; the scalar core matches the printed block
|
|
113
|
+
* column-for-column (same `columns` source), so the two never disagree.
|
|
114
|
+
*/
|
|
115
|
+
export declare function inferResultToDescriptorFile(result: InferDescriptorResult): HikouteiDescriptorFile;
|
|
116
|
+
/** Serializes an inference result to the stable descriptor-file JSON text. */
|
|
117
|
+
export declare function serializeInferDescriptorFile(result: InferDescriptorResult): string;
|
|
118
|
+
//# sourceMappingURL=infer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../../../../../src/infer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure `hikoutei infer` core: raw grid cells in, descriptor block out.
|
|
3
|
+
*
|
|
4
|
+
* The flow is injectable and network-free: the production CLI reads one tab
|
|
5
|
+
* range through the Sheets API and hands the raw grid here; tests inject a
|
|
6
|
+
* fake grid. Cell normalization ports the provider's getValues semantics
|
|
7
|
+
* (formula cells resolve to their computed effective value, the canonical
|
|
8
|
+
* DATE_TIME number format marks dates, blank stays null) so Sheets-provided
|
|
9
|
+
* typed cell info wins over heuristics — there are no date-format guesses
|
|
10
|
+
* beyond the canonical format the provider itself writes.
|
|
11
|
+
*
|
|
12
|
+
* Source parity notes: the canonical date pattern is byte-identical to
|
|
13
|
+
* `GOOGLE_SHEETS_API_DATE_NUMBER_FORMAT` and the serial math matches
|
|
14
|
+
* `isoFromDateSerial` in the google-sheets-api provider's
|
|
15
|
+
* `model/valueNormalization.ts`. A local port (not an import) keeps the CLI
|
|
16
|
+
* runtime dependency-closed: `@hikoutei/sheets` is not a CLI dependency.
|
|
17
|
+
*
|
|
18
|
+
* Redaction contract: warnings carry COLUMN NAMES ONLY, never cell values.
|
|
19
|
+
*/
|
|
20
|
+
import { buildDescriptorFile, serializeDescriptorFile, } from "../sync-engine/api/entity.js";
|
|
21
|
+
/** Machine-readable CLI error prefix, shared with inferMain.ts. */
|
|
22
|
+
export const INFER_ERROR_PREFIX = "hikoutei-infer";
|
|
23
|
+
/** Coded failure thrown by the pure inference (mapped to stderr + exit 1/2). */
|
|
24
|
+
export class InferError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "InferError";
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Canonical UTC date pattern, byte-identical to the provider's own format. */
|
|
33
|
+
const INFER_CANONICAL_DATE_PATTERN = 'yyyy"-"mm"-"dd"T"hh:mm:ss.000"Z"';
|
|
34
|
+
function normalizedDatePattern(pattern) {
|
|
35
|
+
return pattern.replace(/["\s]/g, "");
|
|
36
|
+
}
|
|
37
|
+
const NORMALIZED_CANONICAL_DATE_PATTERN = normalizedDatePattern(INFER_CANONICAL_DATE_PATTERN);
|
|
38
|
+
/** True when a REST `CellFormat.numberFormat` object is the canonical date format. */
|
|
39
|
+
function isCanonicalDateFormat(format) {
|
|
40
|
+
if (format === null || typeof format !== "object" || Array.isArray(format))
|
|
41
|
+
return false;
|
|
42
|
+
const record = format;
|
|
43
|
+
if (record.type !== "DATE_TIME" || typeof record.pattern !== "string")
|
|
44
|
+
return false;
|
|
45
|
+
return normalizedDatePattern(record.pattern) === NORMALIZED_CANONICAL_DATE_PATTERN;
|
|
46
|
+
}
|
|
47
|
+
function isRecord(value) {
|
|
48
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
function malformedGrid() {
|
|
51
|
+
return new InferError("malformed_grid", "the sheet grid has an unsupported cell shape");
|
|
52
|
+
}
|
|
53
|
+
/** Prefers the user-entered number format, falls back to the effective one. */
|
|
54
|
+
function cellNumberFormat(cell) {
|
|
55
|
+
const entered = cell.userEnteredFormat;
|
|
56
|
+
if (isRecord(entered) && entered.numberFormat !== undefined)
|
|
57
|
+
return entered.numberFormat;
|
|
58
|
+
const effective = cell.effectiveFormat;
|
|
59
|
+
if (isRecord(effective) && effective.numberFormat !== undefined)
|
|
60
|
+
return effective.numberFormat;
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Display string of an error cell: formatted value, else the error message. */
|
|
64
|
+
function errorDisplayString(cell, owner) {
|
|
65
|
+
const formatted = cell.formattedValue;
|
|
66
|
+
if (typeof formatted === "string" && formatted.length > 0)
|
|
67
|
+
return formatted;
|
|
68
|
+
const error = owner.errorValue;
|
|
69
|
+
if (isRecord(error) && typeof error.message === "string" && error.message.length > 0) {
|
|
70
|
+
return error.message;
|
|
71
|
+
}
|
|
72
|
+
throw malformedGrid();
|
|
73
|
+
}
|
|
74
|
+
/** Normalizes one literal ExtendedValue; `null` is blank. */
|
|
75
|
+
function literalCell(value, format) {
|
|
76
|
+
if (value.stringValue !== undefined) {
|
|
77
|
+
if (typeof value.stringValue !== "string")
|
|
78
|
+
throw malformedGrid();
|
|
79
|
+
// An explicit empty string is blank (getValues semantics).
|
|
80
|
+
return value.stringValue.length === 0 ? null : { kind: "string", value: value.stringValue.normalize("NFC") };
|
|
81
|
+
}
|
|
82
|
+
if (value.boolValue !== undefined) {
|
|
83
|
+
if (typeof value.boolValue !== "boolean")
|
|
84
|
+
throw malformedGrid();
|
|
85
|
+
return { kind: "boolean", value: value.boolValue };
|
|
86
|
+
}
|
|
87
|
+
if (value.numberValue !== undefined) {
|
|
88
|
+
if (typeof value.numberValue !== "number" || !Number.isFinite(value.numberValue))
|
|
89
|
+
throw malformedGrid();
|
|
90
|
+
if (isCanonicalDateFormat(format)) {
|
|
91
|
+
return { kind: "date", value: isoFromDateSerial(value.numberValue) };
|
|
92
|
+
}
|
|
93
|
+
return { kind: "number", value: value.numberValue };
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(value).length === 0)
|
|
96
|
+
return null;
|
|
97
|
+
throw malformedGrid();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Normalizes one raw grid cell with getValues semantics: formula cells
|
|
101
|
+
* resolve to their computed effective value, error cells to their display
|
|
102
|
+
* string, literals keep their typed kind, blank cells become `null`.
|
|
103
|
+
*/
|
|
104
|
+
export function normalizeInferCell(cell) {
|
|
105
|
+
if (cell === null || cell === undefined)
|
|
106
|
+
return null;
|
|
107
|
+
if (!isRecord(cell))
|
|
108
|
+
throw malformedGrid();
|
|
109
|
+
const entered = isRecord(cell.userEnteredValue) ? cell.userEnteredValue : undefined;
|
|
110
|
+
const effective = isRecord(cell.effectiveValue) ? cell.effectiveValue : undefined;
|
|
111
|
+
const format = cellNumberFormat(cell);
|
|
112
|
+
if (entered !== undefined && entered.formulaValue !== undefined) {
|
|
113
|
+
if (effective === undefined)
|
|
114
|
+
throw malformedGrid();
|
|
115
|
+
if (effective.errorValue !== undefined) {
|
|
116
|
+
return { kind: "string", value: errorDisplayString(cell, effective) };
|
|
117
|
+
}
|
|
118
|
+
return literalCell(effective, format);
|
|
119
|
+
}
|
|
120
|
+
if (entered !== undefined && entered.errorValue !== undefined) {
|
|
121
|
+
return { kind: "string", value: errorDisplayString(cell, entered) };
|
|
122
|
+
}
|
|
123
|
+
if (entered === undefined)
|
|
124
|
+
return null;
|
|
125
|
+
return literalCell(entered, format);
|
|
126
|
+
}
|
|
127
|
+
/** Excel 1900-system serial (days since 1899-12-30 UTC) back to canonical ISO. */
|
|
128
|
+
function isoFromDateSerial(serial) {
|
|
129
|
+
return new Date(Math.round(Date.UTC(1899, 11, 30) + serial * 86_400_000)).toISOString();
|
|
130
|
+
}
|
|
131
|
+
/** Display text of one raw header cell; blank when empty. */
|
|
132
|
+
function headerTextOf(cell) {
|
|
133
|
+
if (!isRecord(cell))
|
|
134
|
+
return "";
|
|
135
|
+
const formatted = cell.formattedValue;
|
|
136
|
+
if (typeof formatted === "string")
|
|
137
|
+
return formatted.trim();
|
|
138
|
+
const entered = cell.userEnteredValue;
|
|
139
|
+
if (isRecord(entered) && typeof entered.stringValue === "string")
|
|
140
|
+
return entered.stringValue.trim();
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
const SQL_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
144
|
+
/** Splits display text into alphanumeric words. */
|
|
145
|
+
function wordsOf(text) {
|
|
146
|
+
return text.split(/[^A-Za-z0-9]+/).filter((word) => word !== "");
|
|
147
|
+
}
|
|
148
|
+
/** Makes a camelCase property name, suffixed on collision (`name`, `name2`, ...). */
|
|
149
|
+
function toPropertyName(header, columnIndex, used) {
|
|
150
|
+
const words = wordsOf(header);
|
|
151
|
+
let base = words.length === 0
|
|
152
|
+
? `field${columnIndex + 1}`
|
|
153
|
+
: words
|
|
154
|
+
.map((word, i) => (i === 0 ? word.charAt(0).toLowerCase() + word.slice(1) : word.charAt(0).toUpperCase() + word.slice(1)))
|
|
155
|
+
.join("");
|
|
156
|
+
base = base.replace(/^[^A-Za-z_]+/, "");
|
|
157
|
+
if (base === "" || !SQL_IDENTIFIER_PATTERN.test(base))
|
|
158
|
+
base = `field${columnIndex + 1}`;
|
|
159
|
+
let name = base;
|
|
160
|
+
let suffix = 2;
|
|
161
|
+
while (used.has(name)) {
|
|
162
|
+
name = `${base}${suffix}`;
|
|
163
|
+
suffix += 1;
|
|
164
|
+
}
|
|
165
|
+
used.add(name);
|
|
166
|
+
return name;
|
|
167
|
+
}
|
|
168
|
+
/** Derives the PascalCase entity name from the tab name. */
|
|
169
|
+
function toEntityName(tabName) {
|
|
170
|
+
const words = wordsOf(tabName);
|
|
171
|
+
if (words.length === 0)
|
|
172
|
+
return "InferredEntity";
|
|
173
|
+
const name = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
174
|
+
return SQL_IDENTIFIER_PATTERN.test(name) ? name : "InferredEntity";
|
|
175
|
+
}
|
|
176
|
+
/** Derives the snake_case table name from the tab name. */
|
|
177
|
+
function toTableName(tabName) {
|
|
178
|
+
const words = wordsOf(tabName);
|
|
179
|
+
if (words.length === 0)
|
|
180
|
+
return "inferred_entity";
|
|
181
|
+
return words.map((word) => word.toLowerCase()).join("_");
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Infers the entity descriptor block from a raw tab grid.
|
|
185
|
+
*
|
|
186
|
+
* Throws `InferError`: `empty_tab` (no header row), `pk_not_found` (the
|
|
187
|
+
* `--pk` header names no sampled column). Warnings carry column names only.
|
|
188
|
+
*/
|
|
189
|
+
export function inferFromGrid(grid, options = {}) {
|
|
190
|
+
const headers = grid.headerCells.map((cell) => headerTextOf(cell));
|
|
191
|
+
const usable = headers
|
|
192
|
+
.map((header, index) => ({ header, index }))
|
|
193
|
+
.filter((entry) => entry.header !== "");
|
|
194
|
+
if (usable.length === 0) {
|
|
195
|
+
throw new InferError("empty_tab", `Tab "${grid.tabName}" has no header row.`);
|
|
196
|
+
}
|
|
197
|
+
const rows = grid.dataCells.map((cells) => usable.map((entry) => normalizeInferCell(cells[entry.index])));
|
|
198
|
+
const nonEmptyRows = rows.filter((cells) => cells.some((cell) => cell !== null));
|
|
199
|
+
// Column kinds over the sampled non-empty cells; empty/mixed → string.
|
|
200
|
+
const warnings = [];
|
|
201
|
+
const types = new Map();
|
|
202
|
+
const distribution = { string: 0, number: 0, boolean: 0, date: 0 };
|
|
203
|
+
usable.forEach((entry, position) => {
|
|
204
|
+
const kinds = new Set();
|
|
205
|
+
for (const cells of nonEmptyRows) {
|
|
206
|
+
const cell = cells[position];
|
|
207
|
+
if (cell !== null && cell !== undefined)
|
|
208
|
+
kinds.add(cell.kind);
|
|
209
|
+
}
|
|
210
|
+
let type;
|
|
211
|
+
if (kinds.size === 0) {
|
|
212
|
+
type = "string";
|
|
213
|
+
warnings.push({ column: entry.header, reason: "empty column; defaulted to string" });
|
|
214
|
+
}
|
|
215
|
+
else if (kinds.size === 1) {
|
|
216
|
+
type = [...kinds][0];
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
type = "string";
|
|
220
|
+
warnings.push({ column: entry.header, reason: "mixed column types; defaulted to string" });
|
|
221
|
+
}
|
|
222
|
+
types.set(entry.index, type);
|
|
223
|
+
distribution[type] += 1;
|
|
224
|
+
});
|
|
225
|
+
// PK: override header, else the first column. v1 PKs are string|number, so
|
|
226
|
+
// a boolean/date PK column is emitted as string with a warning.
|
|
227
|
+
const pkHeader = options.pkHeader ?? usable[0].header;
|
|
228
|
+
const pkEntry = usable.find((entry) => entry.header === pkHeader);
|
|
229
|
+
if (pkEntry === undefined) {
|
|
230
|
+
throw new InferError("pk_not_found", options.pkHeader === undefined
|
|
231
|
+
? `Tab "${grid.tabName}" has no usable first column.`
|
|
232
|
+
: `PK header "${options.pkHeader}" names no column of tab "${grid.tabName}".`);
|
|
233
|
+
}
|
|
234
|
+
let pkType = types.get(pkEntry.index);
|
|
235
|
+
if (pkType !== "string" && pkType !== "number") {
|
|
236
|
+
warnings.push({ column: pkEntry.header, reason: `PK type ${pkType} is not a v1 business key; emitted as string` });
|
|
237
|
+
distribution[pkType] -= 1;
|
|
238
|
+
distribution.string += 1;
|
|
239
|
+
pkType = "string";
|
|
240
|
+
types.set(pkEntry.index, "string");
|
|
241
|
+
}
|
|
242
|
+
const entityName = toEntityName(grid.tabName);
|
|
243
|
+
const tableName = toTableName(grid.tabName);
|
|
244
|
+
const used = new Set();
|
|
245
|
+
const props = usable.map((entry) => ({
|
|
246
|
+
header: entry.header,
|
|
247
|
+
name: toPropertyName(entry.header, entry.index, used),
|
|
248
|
+
type: entry.index === pkEntry.index ? pkType : types.get(entry.index),
|
|
249
|
+
primary: entry.index === pkEntry.index,
|
|
250
|
+
}));
|
|
251
|
+
const lines = [
|
|
252
|
+
`import { defineTypedSheetsEntity } from "hikoutei";`,
|
|
253
|
+
``,
|
|
254
|
+
`export const ${entityName} = defineTypedSheetsEntity({`,
|
|
255
|
+
` name: "${entityName}",`,
|
|
256
|
+
` tableName: "${tableName}",`,
|
|
257
|
+
` properties: {`,
|
|
258
|
+
];
|
|
259
|
+
for (const prop of props) {
|
|
260
|
+
lines.push(` // ${JSON.stringify(prop.header.replace(/[\r\n]+/g, " "))}`);
|
|
261
|
+
lines.push(prop.primary
|
|
262
|
+
? ` ${prop.name}: { type: "${prop.type}", primary: true },`
|
|
263
|
+
: ` ${prop.name}: { type: "${prop.type}" },`);
|
|
264
|
+
}
|
|
265
|
+
lines.push(` },`);
|
|
266
|
+
lines.push(`});`);
|
|
267
|
+
const pkProp = props.find((prop) => prop.primary);
|
|
268
|
+
const summaryLines = [
|
|
269
|
+
`Tab "${grid.tabName}": ${nonEmptyRows.length} sampled rows, ${usable.length} columns.`,
|
|
270
|
+
`Types: string=${distribution.string}, number=${distribution.number}, boolean=${distribution.boolean}, date=${distribution.date}.`,
|
|
271
|
+
`PK: ${pkProp.name} (from header "${pkEntry.header}").`,
|
|
272
|
+
warnings.length === 0
|
|
273
|
+
? `Warnings: (none).`
|
|
274
|
+
: `Warnings:\n${warnings.map((warning) => `WARN "${warning.column}": ${warning.reason}.`).join("\n")}`,
|
|
275
|
+
];
|
|
276
|
+
return {
|
|
277
|
+
entityName,
|
|
278
|
+
tableName,
|
|
279
|
+
tsBlock: lines.join("\n"),
|
|
280
|
+
summary: summaryLines.join("\n"),
|
|
281
|
+
warnings,
|
|
282
|
+
sampledRows: nonEmptyRows.length,
|
|
283
|
+
columnCount: usable.length,
|
|
284
|
+
distribution,
|
|
285
|
+
pkProperty: pkProp.name,
|
|
286
|
+
columns: props.map((prop) => ({
|
|
287
|
+
header: prop.header,
|
|
288
|
+
property: prop.name,
|
|
289
|
+
type: prop.type,
|
|
290
|
+
primary: prop.primary,
|
|
291
|
+
})),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Builds the versioned descriptor file for an inference result.
|
|
296
|
+
*
|
|
297
|
+
* Pure and network-free: `--emit` serializes this file next to the printed
|
|
298
|
+
* block, and the file's `header` fields later feed `adopt --descriptor`
|
|
299
|
+
* without manual `--map` flags. Delegates to the contracts builder so the
|
|
300
|
+
* file envelope has one owner; the scalar core matches the printed block
|
|
301
|
+
* column-for-column (same `columns` source), so the two never disagree.
|
|
302
|
+
*/
|
|
303
|
+
export function inferResultToDescriptorFile(result) {
|
|
304
|
+
return buildDescriptorFile({
|
|
305
|
+
name: result.entityName,
|
|
306
|
+
tableName: result.tableName,
|
|
307
|
+
columns: result.columns.map((column) => ({
|
|
308
|
+
property: column.property,
|
|
309
|
+
header: column.header,
|
|
310
|
+
type: column.type,
|
|
311
|
+
primary: column.primary,
|
|
312
|
+
})),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/** Serializes an inference result to the stable descriptor-file JSON text. */
|
|
316
|
+
export function serializeInferDescriptorFile(result) {
|
|
317
|
+
return serializeDescriptorFile(inferResultToDescriptorFile(result));
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=infer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infer.js","sourceRoot":"","sources":["../../../../../../src/infer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure command-line parsing for `hikoutei infer`.
|
|
3
|
+
*
|
|
4
|
+
* This module owns flag syntax and defaults only; it never touches the
|
|
5
|
+
* filesystem, imports user modules, or contacts Google Sheets. The result is
|
|
6
|
+
* an explicit union so the CLI entry can branch on help / valid / invalid
|
|
7
|
+
* without inspecting message text.
|
|
8
|
+
*
|
|
9
|
+
* Infer (spike): read-only sampling of an existing spreadsheet tab to print
|
|
10
|
+
* a copy-pasteable `defineTypedSheetsEntity(...)` block. The flow never
|
|
11
|
+
* writes to the spreadsheet or provisions anything.
|
|
12
|
+
*/
|
|
13
|
+
import { type SetupFailure } from "./errors.js";
|
|
14
|
+
/** Default number of data rows sampled from the tab (header excluded). */
|
|
15
|
+
export declare const INFER_DEFAULT_LIMIT = 100;
|
|
16
|
+
/** Hard cap on sampled data rows so one run cannot page a whole spreadsheet. */
|
|
17
|
+
export declare const INFER_MAX_LIMIT = 1000;
|
|
18
|
+
/** Parsed and defaulted infer options handed to the infer flow. */
|
|
19
|
+
export interface InferOptions {
|
|
20
|
+
/** Spreadsheet URL; falls back to `HIKOUTEI_SYNC_SPREADSHEET_URL`. */
|
|
21
|
+
readonly spreadsheetUrl?: string;
|
|
22
|
+
/** Existing tab to sample (required). */
|
|
23
|
+
readonly tabName: string;
|
|
24
|
+
/** PK header override; defaults to the first column. */
|
|
25
|
+
readonly pkHeader?: string;
|
|
26
|
+
/** Data rows sampled below the header row (1..1000, default 100). */
|
|
27
|
+
readonly limit: number;
|
|
28
|
+
/** Service-account key file; falls back to `GOOGLE_APPLICATION_CREDENTIALS`. */
|
|
29
|
+
readonly credentialsPath?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Descriptor-file path written after the block is printed (`--emit`).
|
|
32
|
+
* The file carries the versioned JSON form of the same inference, so a
|
|
33
|
+
* later `createTypedSheets({ descriptors })` / `adopt --descriptor`
|
|
34
|
+
* reuses it without manual `--map` flags.
|
|
35
|
+
*/
|
|
36
|
+
readonly emitPath?: string;
|
|
37
|
+
/** Overwrite the `--emit` target when it already exists (`--force`). */
|
|
38
|
+
readonly force: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Discriminated result of `parseInferArgs`. */
|
|
41
|
+
export type InferArgsParseResult = {
|
|
42
|
+
readonly status: "help";
|
|
43
|
+
readonly helpText: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly status: "valid";
|
|
46
|
+
readonly options: InferOptions;
|
|
47
|
+
} | {
|
|
48
|
+
readonly status: "invalid";
|
|
49
|
+
readonly failure: SetupFailure;
|
|
50
|
+
};
|
|
51
|
+
export declare const INFER_HELP_TEXT: string;
|
|
52
|
+
/** Parses and defaults `hikoutei infer` arguments. */
|
|
53
|
+
export declare function parseInferArgs(argv: readonly string[]): InferArgsParseResult;
|
|
54
|
+
//# sourceMappingURL=inferArgs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inferArgs.d.ts","sourceRoot":"","sources":["../../../../../../src/inferArgs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAK9D,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,gFAAgF;AAChF,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,gDAAgD;AAChD,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAoBnE,eAAO,MAAM,eAAe,QAyBhB,CAAC;AAEb,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,oBAAoB,CAmE5E"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure command-line parsing for `hikoutei infer`.
|
|
3
|
+
*
|
|
4
|
+
* This module owns flag syntax and defaults only; it never touches the
|
|
5
|
+
* filesystem, imports user modules, or contacts Google Sheets. The result is
|
|
6
|
+
* an explicit union so the CLI entry can branch on help / valid / invalid
|
|
7
|
+
* without inspecting message text.
|
|
8
|
+
*
|
|
9
|
+
* Infer (spike): read-only sampling of an existing spreadsheet tab to print
|
|
10
|
+
* a copy-pasteable `defineTypedSheetsEntity(...)` block. The flow never
|
|
11
|
+
* writes to the spreadsheet or provisions anything.
|
|
12
|
+
*/
|
|
13
|
+
import { setupFailure } from "./errors.js";
|
|
14
|
+
/** Machine-readable code shared with the setup taxonomy for argument errors. */
|
|
15
|
+
const SETUP_ERROR_CODES_INVALID_ARGS = "invalid_args";
|
|
16
|
+
/** Default number of data rows sampled from the tab (header excluded). */
|
|
17
|
+
export const INFER_DEFAULT_LIMIT = 100;
|
|
18
|
+
/** Hard cap on sampled data rows so one run cannot page a whole spreadsheet. */
|
|
19
|
+
export const INFER_MAX_LIMIT = 1000;
|
|
20
|
+
const INFER_FLAGS = {
|
|
21
|
+
HELP: "--help",
|
|
22
|
+
HELP_SHORT: "-h",
|
|
23
|
+
SPREADSHEET: "--spreadsheet",
|
|
24
|
+
TAB: "--tab",
|
|
25
|
+
PK: "--pk",
|
|
26
|
+
LIMIT: "--limit",
|
|
27
|
+
CREDENTIALS: "--credentials",
|
|
28
|
+
EMIT: "--emit",
|
|
29
|
+
FORCE: "--force",
|
|
30
|
+
};
|
|
31
|
+
const KNOWN_FLAGS = new Set(Object.values(INFER_FLAGS));
|
|
32
|
+
const VALUE_FLAGS = new Set([
|
|
33
|
+
INFER_FLAGS.SPREADSHEET, INFER_FLAGS.TAB, INFER_FLAGS.PK,
|
|
34
|
+
INFER_FLAGS.LIMIT, INFER_FLAGS.CREDENTIALS, INFER_FLAGS.EMIT,
|
|
35
|
+
]);
|
|
36
|
+
export const INFER_HELP_TEXT = [
|
|
37
|
+
"hikoutei infer - sample an existing spreadsheet tab and print a",
|
|
38
|
+
"copy-pasteable defineTypedSheetsEntity(...) block. Read-only: the",
|
|
39
|
+
"spreadsheet is never written or provisioned.",
|
|
40
|
+
"",
|
|
41
|
+
"Usage: hikoutei infer --tab <TabName> [options]",
|
|
42
|
+
"",
|
|
43
|
+
"Required:",
|
|
44
|
+
" --tab <TabName> Existing tab to sample.",
|
|
45
|
+
"",
|
|
46
|
+
"Options:",
|
|
47
|
+
" --spreadsheet <url> Falls back to HIKOUTEI_SYNC_SPREADSHEET_URL.",
|
|
48
|
+
" --pk <header> PK column header (default: the first column).",
|
|
49
|
+
" --limit <n> Data rows sampled below the header",
|
|
50
|
+
" (default 100, max 1000).",
|
|
51
|
+
" --credentials <path> SA key file; falls back to",
|
|
52
|
+
" GOOGLE_APPLICATION_CREDENTIALS.",
|
|
53
|
+
" --emit <path> Write the versioned descriptor JSON file",
|
|
54
|
+
" after printing the block (parent dirs must",
|
|
55
|
+
" exist; refuses to overwrite unless --force).",
|
|
56
|
+
" --force Overwrite the --emit target when it exists.",
|
|
57
|
+
" -h, --help Show this help.",
|
|
58
|
+
"",
|
|
59
|
+
"Exit codes: 0 success, 1 runtime failure (tab not found, empty tab,",
|
|
60
|
+
"missing credentials), 2 argument errors.",
|
|
61
|
+
].join("\n");
|
|
62
|
+
/** Parses and defaults `hikoutei infer` arguments. */
|
|
63
|
+
export function parseInferArgs(argv) {
|
|
64
|
+
const values = new Map();
|
|
65
|
+
const flags = new Set();
|
|
66
|
+
let index = 0;
|
|
67
|
+
while (index < argv.length) {
|
|
68
|
+
const token = argv[index];
|
|
69
|
+
if (token === INFER_FLAGS.HELP || token === INFER_FLAGS.HELP_SHORT) {
|
|
70
|
+
return { status: "help", helpText: INFER_HELP_TEXT };
|
|
71
|
+
}
|
|
72
|
+
if (!KNOWN_FLAGS.has(token)) {
|
|
73
|
+
return { status: "invalid", failure: setupFailure(SETUP_ERROR_CODES_INVALID_ARGS, `unknown flag "${token}"`) };
|
|
74
|
+
}
|
|
75
|
+
if (VALUE_FLAGS.has(token)) {
|
|
76
|
+
const value = argv[index + 1];
|
|
77
|
+
if (value === undefined || value.startsWith("--")) {
|
|
78
|
+
return { status: "invalid", failure: setupFailure(SETUP_ERROR_CODES_INVALID_ARGS, `flag ${token} requires a value`) };
|
|
79
|
+
}
|
|
80
|
+
values.set(token, value);
|
|
81
|
+
index += 2;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
flags.add(token);
|
|
85
|
+
index += 1;
|
|
86
|
+
}
|
|
87
|
+
const tabName = values.get(INFER_FLAGS.TAB);
|
|
88
|
+
if (tabName === undefined || tabName.trim() === "") {
|
|
89
|
+
return { status: "invalid", failure: setupFailure(SETUP_ERROR_CODES_INVALID_ARGS, "missing required flag(s): --tab") };
|
|
90
|
+
}
|
|
91
|
+
const rawLimit = values.get(INFER_FLAGS.LIMIT) ?? String(INFER_DEFAULT_LIMIT);
|
|
92
|
+
const limit = Number(rawLimit);
|
|
93
|
+
if (!/^\d+$/.test(rawLimit) || !Number.isSafeInteger(limit) || limit < 1 || limit > INFER_MAX_LIMIT) {
|
|
94
|
+
return {
|
|
95
|
+
status: "invalid",
|
|
96
|
+
failure: setupFailure(SETUP_ERROR_CODES_INVALID_ARGS, `--limit must be an integer between 1 and ${INFER_MAX_LIMIT}, received "${rawLimit}"`),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const optional = (flag) => {
|
|
100
|
+
const value = values.get(flag);
|
|
101
|
+
return value === undefined || value.trim() === "" ? undefined : value;
|
|
102
|
+
};
|
|
103
|
+
const spreadsheetUrl = optional(INFER_FLAGS.SPREADSHEET);
|
|
104
|
+
const pkHeader = optional(INFER_FLAGS.PK);
|
|
105
|
+
const credentialsPath = optional(INFER_FLAGS.CREDENTIALS);
|
|
106
|
+
const rawEmit = values.get(INFER_FLAGS.EMIT);
|
|
107
|
+
if (rawEmit !== undefined && rawEmit.trim() === "") {
|
|
108
|
+
return { status: "invalid", failure: setupFailure(SETUP_ERROR_CODES_INVALID_ARGS, "flag --emit requires a non-empty path") };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status: "valid",
|
|
112
|
+
options: {
|
|
113
|
+
tabName,
|
|
114
|
+
limit,
|
|
115
|
+
force: flags.has(INFER_FLAGS.FORCE),
|
|
116
|
+
...(spreadsheetUrl === undefined ? {} : { spreadsheetUrl }),
|
|
117
|
+
...(pkHeader === undefined ? {} : { pkHeader }),
|
|
118
|
+
...(credentialsPath === undefined ? {} : { credentialsPath }),
|
|
119
|
+
...(rawEmit === undefined ? {} : { emitPath: rawEmit }),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=inferArgs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inferArgs.js","sourceRoot":"","sources":["../../../../../../src/inferArgs.ts"],"names":[],"mappings":"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"}
|