@happyvertical/smrt-spreadsheets 0.0.0-stage → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +75 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +112 -2
- package/dist/chunks/plan-BAV2jEzl.js +429 -0
- package/dist/chunks/plan-BAV2jEzl.js.map +1 -0
- package/dist/import/checks.d.ts +52 -0
- package/dist/import/checks.d.ts.map +1 -0
- package/dist/import/coerce.d.ts +27 -0
- package/dist/import/coerce.d.ts.map +1 -0
- package/dist/import/errors.d.ts +16 -0
- package/dist/import/errors.d.ts.map +1 -0
- package/dist/import/evaluate.d.ts +45 -0
- package/dist/import/evaluate.d.ts.map +1 -0
- package/dist/import/findings.d.ts +33 -0
- package/dist/import/findings.d.ts.map +1 -0
- package/dist/import/index.d.ts +11 -0
- package/dist/import/index.d.ts.map +1 -0
- package/dist/import/index.js +975 -0
- package/dist/import/index.js.map +1 -0
- package/dist/import/limits.d.ts +19 -0
- package/dist/import/limits.d.ts.map +1 -0
- package/dist/import/load.d.ts +35 -0
- package/dist/import/load.d.ts.map +1 -0
- package/dist/import/plan.d.ts +81 -0
- package/dist/import/plan.d.ts.map +1 -0
- package/dist/import/run.d.ts +34 -0
- package/dist/import/run.d.ts.map +1 -0
- package/dist/import/target.d.ts +38 -0
- package/dist/import/target.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +0 -0
- package/dist/manifest.json +9 -0
- package/dist/smrt-knowledge.json +57 -0
- package/dist/smrt-knowledge.okf.md +46 -0
- package/dist/testing.d.ts +21 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +198 -0
- package/dist/testing.js.map +1 -0
- package/package.json +61 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-BAV2jEzl.js","names":[],"sources":["../../src/import/errors.ts","../../src/import/plan.ts"],"sourcesContent":["/** Typed refusals. Every one carries a stable `code`; none wraps an adapter error. */\n\nexport type ImportPlanErrorCode =\n | 'plan-not-data'\n | 'plan-too-large'\n | 'unsupported-version'\n | 'invalid-shape'\n | 'unknown-key'\n | 'invalid-key'\n | 'reserved-key'\n | 'duplicate-key'\n | 'limit-exceeded'\n | 'invalid-value'\n | 'invalid-source'\n | 'field-source'\n | 'no-row-source'\n | 'unknown-field'\n | 'unknown-table'\n | 'reference-mismatch'\n | 'reference-cycle'\n | 'invalid-expression'\n | 'expression-column'\n | 'expression-type';\n\n/** A plan was refused. `path` locates the offending value, e.g. `tables[0].fields[2].key`. */\nexport class ImportPlanError extends Error {\n readonly code: ImportPlanErrorCode;\n readonly path: string;\n constructor(code: ImportPlanErrorCode, path: string, message: string) {\n super(`${path || 'plan'}: ${message}`);\n this.name = 'ImportPlanError';\n this.code = code;\n this.path = path;\n }\n}\n\nexport type ImportRunErrorCode =\n | 'cells-over-limit'\n | 'rows-over-limit'\n | 'work-over-limit'\n | 'target-not-empty'\n | 'source-invalid';\n\n/** A workbook or target was refused before anything was written. */\nexport class ImportRunError extends Error {\n readonly code: ImportRunErrorCode;\n readonly detail?: Record<string, unknown>;\n constructor(\n code: ImportRunErrorCode,\n message: string,\n detail?: Record<string, unknown>,\n ) {\n super(message);\n this.name = 'ImportRunError';\n this.code = code;\n this.detail = detail;\n }\n}\n","import { range as parseA1Range } from '@happyvertical/spreadsheets';\nimport {\n type AggregateOperand,\n ExpressionError,\n renderExpression,\n} from '@happyvertical/sql/query';\nimport { ImportPlanError, type ImportPlanErrorCode } from './errors.js';\n\nexport type ImportFieldType =\n | 'text'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'date'\n | 'money'\n | 'choice'\n | 'reference';\n\nexport const IMPORT_FIELD_TYPES: readonly ImportFieldType[] = Object.freeze([\n 'text',\n 'number',\n 'integer',\n 'boolean',\n 'date',\n 'money',\n 'choice',\n 'reference',\n]);\n\nexport type { AggregateOperand };\n\nexport interface PlanReference {\n /** Key of the referenced table. */\n table: string;\n /** Field keys of the referenced table; must equal its `lookupKey`. */\n by: string[];\n /** 1-based sheet columns whose values are matched, in `by` order. */\n columns: number[];\n}\n\nexport interface PlanField {\n key: string;\n label: string;\n type: ImportFieldType;\n required?: boolean;\n /** 1-based sheet column the value comes from. */\n column?: number;\n /** SDK expression over sibling field keys; the field has no column. */\n calculated?: AggregateOperand;\n /** For calculated fields: the column holding the workbook's cached result. */\n cachedColumn?: number;\n reference?: PlanReference;\n choices?: string[];\n /** Stored as integer minor units: `value * 10^scale`. */\n money?: { currency: string; scale: number };\n}\n\nexport interface PlanTable {\n key: string;\n label: string;\n source: {\n sheet: string;\n /** A1 range holding the header row and the data rows. */\n range: string;\n /** Absolute sheet row of the header. */\n headerRow: number;\n /** Absolute sheet rows to leave out (totals, notes). */\n skipRows?: number[];\n /** Rows are the distinct combinations of the mapped columns. */\n distinct?: boolean;\n };\n fields: PlanField[];\n /** Field keys that identify a row (reference target, re-import match). */\n lookupKey?: string[];\n titleField?: string;\n}\n\n/** Opaque to this package; passed to the target as given. */\nexport interface PlanReport {\n key: string;\n label: string;\n spec: unknown;\n}\n\nexport interface ImportPlan {\n version: 1;\n tables: PlanTable[];\n reports?: PlanReport[];\n}\n\nexport interface PlanLimits {\n maxTables: number;\n maxFieldsPerTable: number;\n maxPlanBytes: number;\n maxReports: number;\n maxChoices: number;\n maxSkipRows: number;\n maxExpressionNodes: number;\n}\n\nexport const DEFAULT_PLAN_LIMITS: Readonly<PlanLimits> = Object.freeze({\n maxTables: 200,\n maxFieldsPerTable: 500,\n maxPlanBytes: 2 * 1024 * 1024,\n maxReports: 200,\n maxChoices: 1000,\n maxSkipRows: 100_000,\n maxExpressionNodes: 1024,\n});\n\nexport const PLAN_KEY_PATTERN = /^[a-z][a-z0-9_]{0,62}$/;\n\n/**\n * Keys that name something on every object. Refused so a key can never address\n * inherited state in a record built from a plan, whatever the target does with it.\n */\nconst RESERVED_KEYS: ReadonlySet<string> = new Set([\n ...Object.getOwnPropertyNames(Object.prototype),\n 'prototype',\n]);\n\nconst MAX_LABEL = 200;\nconst MAX_COLUMN = 16384;\nconst MAX_ROW = 1_048_576;\n\nfunction fail(code: ImportPlanErrorCode, path: string, message: string): never {\n throw new ImportPlanError(code, path, message);\n}\n\n/** Copies the input through JSON so only own, plain data survives. */\nfunction toPlainData(input: unknown, limits: PlanLimits): unknown {\n let text: string | undefined;\n try {\n text = JSON.stringify(input, (_key, value) => {\n const kind = typeof value;\n if (kind === 'function' || kind === 'symbol' || kind === 'bigint') {\n throw new TypeError(`a ${kind} is not plain data`);\n }\n if (typeof value === 'number' && !Number.isFinite(value)) {\n throw new TypeError('a non-finite number is not plain data');\n }\n return value;\n });\n } catch (error) {\n fail(\n 'plan-not-data',\n '',\n `a plan must be plain JSON data (${error instanceof Error ? error.message : 'unserializable'})`,\n );\n }\n if (text === undefined) {\n fail('plan-not-data', '', 'a plan must be plain JSON data');\n }\n if (text.length > limits.maxPlanBytes) {\n fail(\n 'plan-too-large',\n '',\n `plan is ${text.length} bytes; the limit is ${limits.maxPlanBytes}`,\n );\n }\n return JSON.parse(text);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction closedObject(\n value: unknown,\n path: string,\n allowed: readonly string[],\n): Record<string, unknown> {\n if (!isRecord(value)) fail('invalid-shape', path, 'expected an object');\n for (const key of Object.keys(value)) {\n if (!allowed.includes(key)) {\n fail('unknown-key', `${path}.${key}`, `unknown key \"${key}\"`);\n }\n }\n return value;\n}\n\nfunction arrayOf(value: unknown, path: string, max: number): unknown[] {\n if (!Array.isArray(value)) fail('invalid-shape', path, 'expected an array');\n if (value.length > max) {\n fail('limit-exceeded', path, `at most ${max} entries; got ${value.length}`);\n }\n return value;\n}\n\nfunction planKey(value: unknown, path: string): string {\n if (typeof value !== 'string' || !PLAN_KEY_PATTERN.test(value)) {\n fail('invalid-key', path, 'a key matches ^[a-z][a-z0-9_]{0,62}$');\n }\n if (RESERVED_KEYS.has(value)) {\n fail('reserved-key', path, `\"${value}\" is a reserved key`);\n }\n return value;\n}\n\nfunction label(value: unknown, path: string): string {\n if (\n typeof value !== 'string' ||\n value.trim().length === 0 ||\n value.length > MAX_LABEL\n ) {\n fail('invalid-value', path, `a non-empty string of at most ${MAX_LABEL}`);\n }\n return value;\n}\n\nfunction positiveInt(value: unknown, path: string, max: number): number {\n if (\n typeof value !== 'number' ||\n !Number.isSafeInteger(value) ||\n value < 1 ||\n value > max\n ) {\n fail('invalid-value', path, `an integer from 1 to ${max}`);\n }\n return value;\n}\n\nfunction optionalBoolean(value: unknown, path: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') fail('invalid-value', path, 'a boolean');\n return value;\n}\n\nfunction keyList(\n value: unknown,\n path: string,\n max: number,\n): { keys: string[]; paths: string[] } {\n const list = arrayOf(value, path, max);\n if (list.length === 0) fail('invalid-value', path, 'at least one key');\n const seen = new Set<string>();\n const keys = list.map((item, index) => {\n const key = planKey(item, `${path}[${index}]`);\n if (seen.has(key)) fail('duplicate-key', `${path}[${index}]`, `\"${key}\"`);\n seen.add(key);\n return key;\n });\n return { keys, paths: list.map((_, index) => `${path}[${index}]`) };\n}\n\nconst FIELD_KEYS = [\n 'key',\n 'label',\n 'type',\n 'required',\n 'column',\n 'calculated',\n 'cachedColumn',\n 'reference',\n 'choices',\n 'money',\n] as const;\n\nfunction validateField(\n value: unknown,\n path: string,\n limits: PlanLimits,\n): PlanField {\n const raw = closedObject(value, path, FIELD_KEYS);\n const key = planKey(raw.key, `${path}.key`);\n const fieldLabel = label(raw.label, `${path}.label`);\n if (\n typeof raw.type !== 'string' ||\n !IMPORT_FIELD_TYPES.includes(raw.type as ImportFieldType)\n ) {\n fail(\n 'invalid-value',\n `${path}.type`,\n `one of ${IMPORT_FIELD_TYPES.join(', ')}`,\n );\n }\n const type = raw.type as ImportFieldType;\n const field: PlanField = { key, label: fieldLabel, type };\n const required = optionalBoolean(raw.required, `${path}.required`);\n if (required !== undefined) field.required = required;\n\n const sources = (['column', 'calculated', 'reference'] as const).filter(\n (name) => raw[name] !== undefined,\n );\n if (sources.length !== 1) {\n fail(\n 'field-source',\n path,\n `exactly one of column, calculated or reference is required; got ${sources.length}`,\n );\n }\n const source = sources[0];\n\n if (source === 'column') {\n field.column = positiveInt(raw.column, `${path}.column`, MAX_COLUMN);\n }\n if (source === 'calculated') {\n field.calculated = raw.calculated as AggregateOperand;\n }\n if (raw.cachedColumn !== undefined) {\n if (source !== 'calculated') {\n fail(\n 'field-source',\n `${path}.cachedColumn`,\n 'cachedColumn belongs to calculated fields',\n );\n }\n field.cachedColumn = positiveInt(\n raw.cachedColumn,\n `${path}.cachedColumn`,\n MAX_COLUMN,\n );\n }\n if (source === 'reference') {\n if (type !== 'reference') {\n fail(\n 'field-source',\n `${path}.type`,\n 'a reference field has type reference',\n );\n }\n const ref = closedObject(raw.reference, `${path}.reference`, [\n 'table',\n 'by',\n 'columns',\n ]);\n const by = keyList(ref.by, `${path}.reference.by`, 16);\n const columnList = arrayOf(ref.columns, `${path}.reference.columns`, 16);\n if (columnList.length !== by.keys.length) {\n fail(\n 'reference-mismatch',\n `${path}.reference.columns`,\n 'columns must pair one-to-one with by',\n );\n }\n field.reference = {\n table: planKey(ref.table, `${path}.reference.table`),\n by: by.keys,\n columns: columnList.map((column, index) =>\n positiveInt(column, `${path}.reference.columns[${index}]`, MAX_COLUMN),\n ),\n };\n } else if (type === 'reference') {\n fail('field-source', `${path}.type`, 'type reference needs a reference');\n }\n\n if (type === 'choice') {\n if (raw.choices === undefined) {\n fail('invalid-value', `${path}.choices`, 'a choice field needs choices');\n }\n const list = arrayOf(raw.choices, `${path}.choices`, limits.maxChoices);\n if (list.length === 0) {\n fail('invalid-value', `${path}.choices`, 'a choice field needs choices');\n }\n const seen = new Set<string>();\n field.choices = list.map((item, index) => {\n if (\n typeof item !== 'string' ||\n item.length === 0 ||\n item.length > MAX_LABEL ||\n item !== item.trim()\n ) {\n fail(\n 'invalid-value',\n `${path}.choices[${index}]`,\n 'a trimmed, non-empty string',\n );\n }\n if (seen.has(item)) {\n fail('duplicate-key', `${path}.choices[${index}]`, `\"${item}\"`);\n }\n seen.add(item);\n return item;\n });\n } else if (raw.choices !== undefined) {\n fail('invalid-value', `${path}.choices`, 'choices belong to choice fields');\n }\n\n if (type === 'money') {\n const money = closedObject(raw.money, `${path}.money`, [\n 'currency',\n 'scale',\n ]);\n if (\n typeof money.currency !== 'string' ||\n !/^[A-Z]{3}$/.test(money.currency)\n ) {\n fail(\n 'invalid-value',\n `${path}.money.currency`,\n 'a three-letter upper-case currency code',\n );\n }\n if (\n typeof money.scale !== 'number' ||\n !Number.isInteger(money.scale) ||\n money.scale < 0 ||\n money.scale > 6\n ) {\n fail('invalid-value', `${path}.money.scale`, 'an integer from 0 to 6');\n }\n field.money = { currency: money.currency, scale: money.scale };\n } else if (raw.money !== undefined) {\n fail('invalid-value', `${path}.money`, 'money belongs to money fields');\n }\n return field;\n}\n\nconst TABLE_KEYS = [\n 'key',\n 'label',\n 'source',\n 'fields',\n 'lookupKey',\n 'titleField',\n] as const;\nconst SOURCE_KEYS = [\n 'sheet',\n 'range',\n 'headerRow',\n 'skipRows',\n 'distinct',\n] as const;\n\nfunction validateTable(\n value: unknown,\n path: string,\n limits: PlanLimits,\n): PlanTable {\n const raw = closedObject(value, path, TABLE_KEYS);\n const key = planKey(raw.key, `${path}.key`);\n const tableLabel = label(raw.label, `${path}.label`);\n\n const src = closedObject(raw.source, `${path}.source`, SOURCE_KEYS);\n if (\n typeof src.sheet !== 'string' ||\n src.sheet.length === 0 ||\n src.sheet.length > 255\n ) {\n fail('invalid-source', `${path}.source.sheet`, 'a sheet name');\n }\n if (typeof src.range !== 'string') {\n fail('invalid-source', `${path}.source.range`, 'an A1 range');\n }\n try {\n parseA1Range(src.range);\n } catch {\n fail('invalid-source', `${path}.source.range`, `\"${src.range}\" is not A1`);\n }\n const source: PlanTable['source'] = {\n sheet: src.sheet,\n range: src.range,\n headerRow: positiveInt(src.headerRow, `${path}.source.headerRow`, MAX_ROW),\n };\n if (src.skipRows !== undefined) {\n const rows = arrayOf(\n src.skipRows,\n `${path}.source.skipRows`,\n limits.maxSkipRows,\n );\n const seen = new Set<number>();\n source.skipRows = rows.map((row, index) => {\n const n = positiveInt(row, `${path}.source.skipRows[${index}]`, MAX_ROW);\n if (seen.has(n)) {\n fail('duplicate-key', `${path}.source.skipRows[${index}]`, `row ${n}`);\n }\n seen.add(n);\n return n;\n });\n }\n const distinct = optionalBoolean(src.distinct, `${path}.source.distinct`);\n if (distinct !== undefined) source.distinct = distinct;\n\n const fieldList = arrayOf(\n raw.fields,\n `${path}.fields`,\n limits.maxFieldsPerTable,\n );\n if (fieldList.length === 0) {\n fail('invalid-value', `${path}.fields`, 'a table needs at least one field');\n }\n const fields = fieldList.map((item, index) =>\n validateField(item, `${path}.fields[${index}]`, limits),\n );\n if (\n !fields.some(\n (field) =>\n field.column !== undefined ||\n field.reference !== undefined ||\n field.cachedColumn !== undefined,\n )\n ) {\n fail(\n 'no-row-source',\n `${path}.fields`,\n 'a table needs a column, reference column or cachedColumn to establish rows',\n );\n }\n const byKey = new Map<string, PlanField>();\n fields.forEach((field, index) => {\n if (byKey.has(field.key)) {\n fail('duplicate-key', `${path}.fields[${index}].key`, `\"${field.key}\"`);\n }\n byKey.set(field.key, field);\n });\n\n const table: PlanTable = { key, label: tableLabel, source, fields };\n if (raw.lookupKey !== undefined) {\n const list = keyList(raw.lookupKey, `${path}.lookupKey`, 16);\n list.keys.forEach((fieldKey, index) => {\n const field = byKey.get(fieldKey);\n if (!field) {\n fail('unknown-field', list.paths[index], `no field \"${fieldKey}\"`);\n }\n if (field.column === undefined) {\n fail(\n 'field-source',\n list.paths[index],\n 'a lookup key field takes its value from a column',\n );\n }\n });\n table.lookupKey = list.keys;\n }\n if (raw.titleField !== undefined) {\n const title = planKey(raw.titleField, `${path}.titleField`);\n if (!byKey.has(title)) {\n fail('unknown-field', `${path}.titleField`, `no field \"${title}\"`);\n }\n table.titleField = title;\n }\n return table;\n}\n\nfunction expressionColumns(\n root: unknown,\n path: string,\n limits: PlanLimits,\n): string[] {\n const found = new Set<string>();\n const stack: unknown[] = [root];\n let nodes = 0;\n while (stack.length > 0) {\n const node = stack.pop();\n if (Array.isArray(node)) {\n for (const item of node) stack.push(item);\n continue;\n }\n if (!isRecord(node)) continue;\n if (++nodes > limits.maxExpressionNodes) {\n fail(\n 'invalid-expression',\n path,\n `more than ${limits.maxExpressionNodes} nodes`,\n );\n }\n if (Object.hasOwn(node, 'col')) {\n if (Object.keys(node).length !== 1 || typeof node.col !== 'string') {\n fail('invalid-expression', path, 'a column node is { col: string }');\n }\n found.add(node.col);\n continue;\n }\n for (const item of Object.values(node)) stack.push(item);\n }\n return [...found];\n}\n\nconst EXPRESSION_COLUMN_TYPE: Record<\n ImportFieldType,\n 'number' | 'text' | 'boolean' | 'date' | 'unknown'\n> = {\n text: 'text',\n choice: 'text',\n number: 'number',\n integer: 'number',\n money: 'number',\n boolean: 'boolean',\n date: 'date',\n reference: 'unknown',\n};\n\n/** The result type an expression must have to be stored in a field of this type. */\nconst EXPECTED_RESULT: Record<ImportFieldType, readonly string[]> = {\n text: ['text'],\n choice: ['text'],\n number: ['number'],\n integer: ['number'],\n money: ['number'],\n boolean: ['boolean'],\n date: ['date'],\n reference: [],\n};\n\nfunction validateCalculated(\n table: PlanTable,\n tablePath: string,\n limits: PlanLimits,\n): void {\n const siblings = new Map(table.fields.map((field) => [field.key, field]));\n table.fields.forEach((field, index) => {\n if (field.calculated === undefined) return;\n const path = `${tablePath}.fields[${index}].calculated`;\n if (field.type === 'reference') {\n fail('field-source', path, 'a calculated field cannot be a reference');\n }\n for (const column of expressionColumns(field.calculated, path, limits)) {\n const sibling = siblings.get(column);\n if (!sibling) {\n fail(\n 'expression-column',\n path,\n `\"${column}\" is not a field of this table`,\n );\n }\n if (sibling.column === undefined) {\n fail(\n 'expression-column',\n path,\n `\"${column}\" is not a column field; a calculated field reads column fields only`,\n );\n }\n }\n try {\n const rendered = renderExpression(field.calculated, {\n dialect: 'sqlite',\n mode: 'ddl',\n resolveColumn: (name) => {\n const sibling = siblings.get(name);\n return {\n name,\n type: sibling ? EXPRESSION_COLUMN_TYPE[sibling.type] : 'unknown',\n };\n },\n });\n const result = rendered.resultType as string;\n if (\n result !== 'unknown' &&\n result !== 'null' &&\n !EXPECTED_RESULT[field.type].includes(result)\n ) {\n fail(\n 'expression-type',\n path,\n `expression yields ${result}, but the field is ${field.type}`,\n );\n }\n } catch (error) {\n if (error instanceof ImportPlanError) throw error;\n if (error instanceof ExpressionError) {\n fail('invalid-expression', path, `${error.code}: ${error.message}`);\n }\n fail(\n 'invalid-expression',\n path,\n error instanceof Error ? error.message : String(error),\n );\n }\n });\n}\n\nfunction validateReferences(plan: ImportPlan): void {\n const tables = new Map(plan.tables.map((table) => [table.key, table]));\n const edges = new Map<string, Set<string>>();\n plan.tables.forEach((table, tableIndex) => {\n const out = new Set<string>();\n edges.set(table.key, out);\n table.fields.forEach((field, fieldIndex) => {\n if (!field.reference) return;\n const path = `tables[${tableIndex}].fields[${fieldIndex}].reference`;\n const target = tables.get(field.reference.table);\n if (!target) {\n fail(\n 'unknown-table',\n `${path}.table`,\n `no table \"${field.reference.table}\"`,\n );\n }\n const lookup = target.lookupKey;\n if (\n !lookup ||\n lookup.length !== field.reference.by.length ||\n lookup.some((key, i) => key !== field.reference?.by[i])\n ) {\n fail(\n 'reference-mismatch',\n `${path}.by`,\n `must equal the lookupKey of \"${target.key}\"`,\n );\n }\n out.add(target.key);\n });\n });\n const state = new Map<string, 1 | 2>();\n const visit = (key: string, trail: string[]): void => {\n if (state.get(key) === 2) return;\n if (state.get(key) === 1) {\n const loop = [...trail.slice(trail.indexOf(key)), key].join(' -> ');\n fail('reference-cycle', 'tables', `references form a cycle: ${loop}`);\n }\n state.set(key, 1);\n for (const next of edges.get(key) ?? []) visit(next, [...trail, key]);\n state.set(key, 2);\n };\n for (const table of plan.tables) visit(table.key, []);\n}\n\n/**\n * Validates a plan and returns a clean copy. Throws {@link ImportPlanError}.\n * A plan is data: it names no function, path or SQL string, and an unknown key\n * anywhere refuses the whole plan.\n */\nexport function validatePlan(\n input: unknown,\n overrides: Partial<PlanLimits> = {},\n): ImportPlan {\n const limits: PlanLimits = { ...DEFAULT_PLAN_LIMITS, ...overrides };\n const data = toPlainData(input, limits);\n const raw = closedObject(data, '', ['version', 'tables', 'reports']);\n if (raw.version !== 1) {\n fail('unsupported-version', 'version', 'this package reads plan version 1');\n }\n const tableList = arrayOf(raw.tables, 'tables', limits.maxTables);\n if (tableList.length === 0) {\n fail('invalid-value', 'tables', 'a plan needs at least one table');\n }\n const tables = tableList.map((item, index) =>\n validateTable(item, `tables[${index}]`, limits),\n );\n const seen = new Set<string>();\n tables.forEach((table, index) => {\n if (seen.has(table.key)) {\n fail('duplicate-key', `tables[${index}].key`, `\"${table.key}\"`);\n }\n seen.add(table.key);\n });\n const plan: ImportPlan = { version: 1, tables };\n\n if (raw.reports !== undefined) {\n const list = arrayOf(raw.reports, 'reports', limits.maxReports);\n const keys = new Set<string>();\n plan.reports = list.map((item, index) => {\n const path = `reports[${index}]`;\n const report = closedObject(item, path, ['key', 'label', 'spec']);\n const key = planKey(report.key, `${path}.key`);\n if (keys.has(key)) fail('duplicate-key', `${path}.key`, `\"${key}\"`);\n keys.add(key);\n return {\n key,\n label: label(report.label, `${path}.label`),\n spec: report.spec,\n };\n });\n }\n\n validateReferences(plan);\n for (const [index, table] of tables.entries()) {\n validateCalculated(table, `tables[${index}]`, limits);\n }\n return plan;\n}\n\n/** Table keys with referenced tables before the tables that reference them. */\nexport function loadOrder(plan: ImportPlan): string[] {\n const order: string[] = [];\n const done = new Set<string>();\n const tables = new Map(plan.tables.map((table) => [table.key, table]));\n const visit = (key: string): void => {\n if (done.has(key)) return;\n done.add(key);\n const table = tables.get(key);\n if (!table) return;\n for (const field of table.fields) {\n if (field.reference) visit(field.reference.table);\n }\n order.push(key);\n };\n for (const table of plan.tables) visit(table.key);\n return order;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Workbook, type WorkbookProfile } from '@happyvertical/spreadsheets';
|
|
2
|
+
import { WorkBudget } from './evaluate.js';
|
|
3
|
+
import { type Finding } from './findings.js';
|
|
4
|
+
import { type ImportLimits } from './limits.js';
|
|
5
|
+
import type { ImportPlan } from './plan.js';
|
|
6
|
+
import type { ImportTarget } from './target.js';
|
|
7
|
+
/** Numbers a table summed per field, for the after-load comparison. */
|
|
8
|
+
export interface TableFacts {
|
|
9
|
+
/** Data rows examined. */
|
|
10
|
+
read: number;
|
|
11
|
+
/** Rows left out: skipRows and rows with every mapped cell blank. */
|
|
12
|
+
skipped: number;
|
|
13
|
+
/** Rows refused by an error finding. */
|
|
14
|
+
rejected: number;
|
|
15
|
+
/** Records that will be loaded. */
|
|
16
|
+
planned: number;
|
|
17
|
+
}
|
|
18
|
+
export interface ImportFacts {
|
|
19
|
+
tables: Record<string, TableFacts>;
|
|
20
|
+
/** table -> field -> sum. Money and integers in exact minor units/integers. */
|
|
21
|
+
sums: Record<string, Record<string, number>>;
|
|
22
|
+
}
|
|
23
|
+
export interface CheckResult {
|
|
24
|
+
/** No error findings. */
|
|
25
|
+
ok: boolean;
|
|
26
|
+
findings: Finding[];
|
|
27
|
+
}
|
|
28
|
+
export interface PlanCheckResult extends CheckResult {
|
|
29
|
+
facts: ImportFacts;
|
|
30
|
+
/** Work units this check spent against `maxWork`. */
|
|
31
|
+
work: number;
|
|
32
|
+
}
|
|
33
|
+
/** Neumaier-compensated sum: the error does not grow with the row count. */
|
|
34
|
+
export declare function compensatedSum(values: Iterable<number>): number;
|
|
35
|
+
/**
|
|
36
|
+
* Checks a plan against a workbook before anything is written. The plan must
|
|
37
|
+
* already be valid ({@link validatePlan}); `profile` is accepted for callers
|
|
38
|
+
* that hold a workbook profile and is not needed to decide anything here.
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkPlan(plan: ImportPlan, workbook: Workbook, _profile?: WorkbookProfile, limits?: Partial<ImportLimits>, budget?: WorkBudget): PlanCheckResult;
|
|
41
|
+
/**
|
|
42
|
+
* The most work `checkLoaded` can spend for these planned counts. `runImport`
|
|
43
|
+
* reserves it before its first call on the target, so verification can never
|
|
44
|
+
* exhaust the budget after something was written.
|
|
45
|
+
*/
|
|
46
|
+
export declare function verificationWork(plan: ImportPlan, facts: ImportFacts): number;
|
|
47
|
+
/**
|
|
48
|
+
* After a load: row counts, column sums and every calculated field with a
|
|
49
|
+
* cached column, read back from the target and compared with the workbook.
|
|
50
|
+
*/
|
|
51
|
+
export declare function checkLoaded(plan: ImportPlan, workbook: Workbook, target: ImportTarget, facts: ImportFacts, budget?: WorkBudget): Promise<CheckResult>;
|
|
52
|
+
//# sourceMappingURL=checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/import/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAKL,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,OAAO,EAA0B,MAAM,eAAe,CAAC;AACrE,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,yBAAyB;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,KAAK,EAAE,WAAW,CAAC;IACnB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;CACd;AAID,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAY/D;AA6GD;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,eAAe,EAC1B,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM,EAClC,MAAM,GAAE,UAEP,GACA,eAAe,CAiCjB;AAmGD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAY7E;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,UAAqD,GAC5D,OAAO,CAAC,WAAW,CAAC,CAwCtB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Cell } from '@happyvertical/spreadsheets';
|
|
2
|
+
import type { PlanField } from './plan.js';
|
|
3
|
+
export type CoerceFailureCode = 'cell-error' | 'not-number' | 'not-integer' | 'unsafe-integer' | 'unsafe-number' | 'not-boolean' | 'not-date' | 'not-money' | 'not-choice' | 'not-coercible';
|
|
4
|
+
export type CoerceResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
value: string | number | boolean | null;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
code: CoerceFailureCode;
|
|
10
|
+
detail: string;
|
|
11
|
+
};
|
|
12
|
+
/** The shortest decimal that round-trips, never in exponent form. */
|
|
13
|
+
export declare function plainDecimal(value: number): string;
|
|
14
|
+
export declare function isoDate(date: Date): string;
|
|
15
|
+
/** A short, bounded rendering of a cell for findings. */
|
|
16
|
+
export declare function describeCell(cell: Cell | undefined): string;
|
|
17
|
+
export declare function isBlankCell(cell: Cell | undefined, trim?: boolean): boolean;
|
|
18
|
+
/** `007` and `00.5` are codes, not numbers; `0`, `0.5` and `-0.5` are numbers. */
|
|
19
|
+
export declare function hasLeadingZeros(text: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Turns one workbook cell into the value a plan field stores, or refuses with
|
|
22
|
+
* a code. Nothing is guessed: a value that does not fit is refused, never
|
|
23
|
+
* rounded, truncated or reinterpreted. A caller reading many cells passes the
|
|
24
|
+
* field's choices as a set, so matching stays constant-time per cell.
|
|
25
|
+
*/
|
|
26
|
+
export declare function coerceCell(cell: Cell | undefined, field: PlanField, choiceSet?: ReadonlySet<string>): CoerceResult;
|
|
27
|
+
//# sourceMappingURL=coerce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../../src/import/coerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,gBAAgB,GAChB,eAAe,GACf,aAAa,GACb,UAAU,GACV,WAAW,GACX,YAAY,GACZ,eAAe,CAAC;AAEpB,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;CAAE,GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAO3D,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWlD;AAMD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE1C;AAED,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,CAY3D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,UAAO,GAAG,OAAO,CASxE;AAYD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAwDD;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,KAAK,EAAE,SAAS,EAChB,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,YAAY,CA8Fd"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Typed refusals. Every one carries a stable `code`; none wraps an adapter error. */
|
|
2
|
+
export type ImportPlanErrorCode = 'plan-not-data' | 'plan-too-large' | 'unsupported-version' | 'invalid-shape' | 'unknown-key' | 'invalid-key' | 'reserved-key' | 'duplicate-key' | 'limit-exceeded' | 'invalid-value' | 'invalid-source' | 'field-source' | 'no-row-source' | 'unknown-field' | 'unknown-table' | 'reference-mismatch' | 'reference-cycle' | 'invalid-expression' | 'expression-column' | 'expression-type';
|
|
3
|
+
/** A plan was refused. `path` locates the offending value, e.g. `tables[0].fields[2].key`. */
|
|
4
|
+
export declare class ImportPlanError extends Error {
|
|
5
|
+
readonly code: ImportPlanErrorCode;
|
|
6
|
+
readonly path: string;
|
|
7
|
+
constructor(code: ImportPlanErrorCode, path: string, message: string);
|
|
8
|
+
}
|
|
9
|
+
export type ImportRunErrorCode = 'cells-over-limit' | 'rows-over-limit' | 'work-over-limit' | 'target-not-empty' | 'source-invalid';
|
|
10
|
+
/** A workbook or target was refused before anything was written. */
|
|
11
|
+
export declare class ImportRunError extends Error {
|
|
12
|
+
readonly code: ImportRunErrorCode;
|
|
13
|
+
readonly detail?: Record<string, unknown>;
|
|
14
|
+
constructor(code: ImportRunErrorCode, message: string, detail?: Record<string, unknown>);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/import/errors.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAEtF,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,GACf,aAAa,GACb,aAAa,GACb,cAAc,GACd,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,eAAe,GACf,eAAe,GACf,oBAAoB,GACpB,iBAAiB,GACjB,oBAAoB,GACpB,mBAAmB,GACnB,iBAAiB,CAAC;AAEtB,8FAA8F;AAC9F,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAMrE;AAED,MAAM,MAAM,kBAAkB,GAC1B,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,CAAC;AAErB,oEAAoE;AACpE,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAExC,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAOnC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Sheet, type Workbook } from '@happyvertical/spreadsheets';
|
|
2
|
+
import type { FindingSink } from './findings.js';
|
|
3
|
+
import type { ImportPlan, PlanField, PlanTable } from './plan.js';
|
|
4
|
+
export interface PlannedRecord {
|
|
5
|
+
/** Absolute sheet row the record came from (first one, for distinct tables). */
|
|
6
|
+
sourceRow: number;
|
|
7
|
+
/** Column and typed values by field key. Calculated and reference fields are absent. */
|
|
8
|
+
values: Record<string, unknown>;
|
|
9
|
+
/** Resolved-by-key references; a blank reference is absent. */
|
|
10
|
+
references: Record<string, {
|
|
11
|
+
table: string;
|
|
12
|
+
key: unknown[];
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export interface EvaluatedTable {
|
|
16
|
+
table: PlanTable;
|
|
17
|
+
/** False when the source could not be read at all; `rows` is then empty. */
|
|
18
|
+
readable: boolean;
|
|
19
|
+
rows: PlannedRecord[];
|
|
20
|
+
/** Data rows examined. */
|
|
21
|
+
read: number;
|
|
22
|
+
/** Rows left out: skipRows and rows with every mapped cell blank. */
|
|
23
|
+
skipped: number;
|
|
24
|
+
/** Rows refused by a finding; they never appear in `rows`. */
|
|
25
|
+
rejected: number;
|
|
26
|
+
}
|
|
27
|
+
/** Highest occupied row of a sheet. Never cached: workbooks are mutable. */
|
|
28
|
+
export declare function lastOccupiedRow(sheet: Sheet): number;
|
|
29
|
+
/** Spends work units against one ceiling; shared by every step of one import. */
|
|
30
|
+
export declare class WorkBudget {
|
|
31
|
+
readonly maxWork: number;
|
|
32
|
+
used: number;
|
|
33
|
+
constructor(maxWork: number);
|
|
34
|
+
charge(units: number): void;
|
|
35
|
+
}
|
|
36
|
+
/** The sheet columns a field reads from. */
|
|
37
|
+
export declare function fieldColumns(field: PlanField): number[];
|
|
38
|
+
declare function keyText(values: readonly unknown[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Reads every table of the plan from the workbook, typing each cell. Findings
|
|
41
|
+
* go to the sink; refused rows are counted and dropped.
|
|
42
|
+
*/
|
|
43
|
+
export declare function evaluatePlan(plan: ImportPlan, workbook: Workbook, sink: FindingSink, budget?: WorkBudget): Map<string, EvaluatedTable>;
|
|
44
|
+
export { keyText };
|
|
45
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/import/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,QAAQ,EACd,MAAM,6BAA6B,CAAC;AAQrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAIpD;AAID,iFAAiF;AACjF,qBAAa,UAAU;IAGT,QAAQ,CAAC,OAAO,EAAE,MAAM;IAFpC,IAAI,SAAK;gBAEY,OAAO,EAAE,MAAM;IAEpC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAU5B;AA2DD,4CAA4C;AAC5C,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,CAMvD;AAsBD,iBAAS,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,UAA6C,GACpD,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO7B;AAoTD,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** What checks report. A finding is data: nothing here throws or writes. */
|
|
2
|
+
export type FindingCode = 'sheet-missing' | 'range-invalid' | 'header-outside-range' | 'column-outside-range' | 'coerce-failed' | 'required-blank' | 'digits-as-number' | 'lookup-key-not-unique' | 'lookup-key-blank' | 'reference-unresolved' | 'unmapped-column' | 'sum-unsafe' | 'row-count-mismatch' | 'sum-mismatch' | 'calculated-invalid' | 'calculated-mismatch';
|
|
3
|
+
export interface Finding {
|
|
4
|
+
code: FindingCode;
|
|
5
|
+
severity: 'error' | 'warning';
|
|
6
|
+
table?: string;
|
|
7
|
+
field?: string;
|
|
8
|
+
sheet?: string;
|
|
9
|
+
/** A1 address of the offending cell. */
|
|
10
|
+
cell?: string;
|
|
11
|
+
/** Absolute sheet row. */
|
|
12
|
+
row?: number;
|
|
13
|
+
/** 1-based sheet column, for findings about a whole column. */
|
|
14
|
+
column?: number;
|
|
15
|
+
expected?: string | number | boolean | null;
|
|
16
|
+
actual?: string | number | boolean | null;
|
|
17
|
+
/** Total findings of this code for this table and field, examples included. */
|
|
18
|
+
count: number;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export type FindingInput = Omit<Finding, 'count'>;
|
|
22
|
+
/** At most this many examples are kept per (code, table, field). */
|
|
23
|
+
export declare const MAX_EXAMPLES = 20;
|
|
24
|
+
/** Collects findings, keeping a bounded number of examples and a total. */
|
|
25
|
+
export declare class FindingSink {
|
|
26
|
+
private readonly groups;
|
|
27
|
+
add(finding: FindingInput): void;
|
|
28
|
+
/** Insertion order of first occurrence, then row order within a group. */
|
|
29
|
+
list(): Finding[];
|
|
30
|
+
hasErrors(): boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function hasErrors(findings: readonly Finding[]): boolean;
|
|
33
|
+
//# sourceMappingURL=findings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../src/import/findings.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAE5E,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,eAAe,GACf,sBAAsB,GACtB,sBAAsB,GACtB,eAAe,GACf,gBAAgB,GAChB,kBAAkB,GAClB,uBAAuB,GACvB,kBAAkB,GAClB,sBAAsB,GACtB,iBAAiB,GACjB,YAAY,GACZ,oBAAoB,GACpB,cAAc,GACd,oBAAoB,GACpB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC1C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAElD,oEAAoE;AACpE,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,2EAA2E;AAC3E,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGnB;IAEJ,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAWhC,0EAA0E;IAC1E,IAAI,IAAI,OAAO,EAAE;IAQjB,SAAS,IAAI,OAAO;CAMrB;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAE/D"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { type CheckResult, checkLoaded, checkPlan, compensatedSum, type ImportFacts, type PlanCheckResult, type TableFacts, } from './checks.js';
|
|
2
|
+
export { type CoerceFailureCode, type CoerceResult, coerceCell, } from './coerce.js';
|
|
3
|
+
export { ImportPlanError, type ImportPlanErrorCode, ImportRunError, type ImportRunErrorCode, } from './errors.js';
|
|
4
|
+
export { WorkBudget } from './evaluate.js';
|
|
5
|
+
export { type Finding, type FindingCode, MAX_EXAMPLES, } from './findings.js';
|
|
6
|
+
export { DEFAULT_IMPORT_LIMITS, DEFAULT_MAX_CELLS, DEFAULT_MAX_ROWS_PER_TABLE, DEFAULT_MAX_WORK, type ImportLimits, } from './limits.js';
|
|
7
|
+
export { admit, DEFAULT_BATCH_SIZE, type LoadBatch, type LoadPlan, type LoadRecord, planLoad, } from './load.js';
|
|
8
|
+
export { type AggregateOperand, DEFAULT_PLAN_LIMITS, IMPORT_FIELD_TYPES, type ImportFieldType, type ImportPlan, loadOrder, PLAN_KEY_PATTERN, type PlanField, type PlanLimits, type PlanReference, type PlanReport, type PlanTable, validatePlan, } from './plan.js';
|
|
9
|
+
export { type RunImportOptions, type RunImportResult, runImport, } from './run.js';
|
|
10
|
+
export type { ImportTarget, TargetRow, TargetStructure } from './target.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/import/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,WAAW,EACX,SAAS,EACT,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,cAAc,EACd,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,0BAA0B,EAC1B,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,EACL,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,SAAS,EACT,gBAAgB,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|