@drzl/validation-core 3.10.0 → 3.12.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/dist/index.cjs +17 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +16 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -84441,6 +84441,7 @@ ${codeblock}`, options7);
|
|
|
84441
84441
|
var index_exports2 = {};
|
|
84442
84442
|
__export(index_exports2, {
|
|
84443
84443
|
AFFIX_PROBE_TABLE: () => AFFIX_PROBE_TABLE,
|
|
84444
|
+
CODEPOINT_LENGTH: () => CODEPOINT_LENGTH,
|
|
84444
84445
|
COLUMN_FORMATS: () => COLUMN_FORMATS,
|
|
84445
84446
|
DEFAULT_IMPORT_EXTENSION: () => DEFAULT_IMPORT_EXTENSION,
|
|
84446
84447
|
DEFAULT_MODE_PREFIX: () => DEFAULT_MODE_PREFIX,
|
|
@@ -84472,6 +84473,7 @@ module.exports = __toCommonJS(index_exports2);
|
|
|
84472
84473
|
// src/checks.ts
|
|
84473
84474
|
var COMPARISON = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(>=|<=|<>|!=|>|<|=)\s*(.+?)\s*$/;
|
|
84474
84475
|
var IN_LIST = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+IN\s*\((.+)\)\s*$/i;
|
|
84476
|
+
var LENGTH_OF = /^\s*(?:length|char_length)\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s*(>=|<=|<>|!=|>|<|=)\s*(\d+)\s*$/i;
|
|
84475
84477
|
function splitTopLevelAnd(expr) {
|
|
84476
84478
|
const parts = [];
|
|
84477
84479
|
let depth = 0;
|
|
@@ -84592,6 +84594,7 @@ function parseCheck(expression, name) {
|
|
|
84592
84594
|
const checks = [];
|
|
84593
84595
|
const sets = [];
|
|
84594
84596
|
const rows = [];
|
|
84597
|
+
const lengths = [];
|
|
84595
84598
|
for (const part of parts) {
|
|
84596
84599
|
const parsed = parseCheck(part, name);
|
|
84597
84600
|
if (!parsed.ok)
|
|
@@ -84599,12 +84602,23 @@ function parseCheck(expression, name) {
|
|
|
84599
84602
|
checks.push(...parsed.checks);
|
|
84600
84603
|
if (parsed.sets) sets.push(...parsed.sets);
|
|
84601
84604
|
if (parsed.rows) rows.push(...parsed.rows);
|
|
84605
|
+
if (parsed.lengths) lengths.push(...parsed.lengths);
|
|
84602
84606
|
}
|
|
84603
84607
|
return {
|
|
84604
84608
|
ok: true,
|
|
84605
84609
|
checks,
|
|
84606
84610
|
...sets.length ? { sets } : {},
|
|
84607
|
-
...rows.length ? { rows } : {}
|
|
84611
|
+
...rows.length ? { rows } : {},
|
|
84612
|
+
...lengths.length ? { lengths } : {}
|
|
84613
|
+
};
|
|
84614
|
+
}
|
|
84615
|
+
const lengthOf = expr.match(LENGTH_OF);
|
|
84616
|
+
if (lengthOf) {
|
|
84617
|
+
const op4 = lengthOf[2] === "!=" ? "<>" : lengthOf[2];
|
|
84618
|
+
return {
|
|
84619
|
+
ok: true,
|
|
84620
|
+
checks: [],
|
|
84621
|
+
lengths: [{ column: lengthOf[1], operator: op4, value: lengthOf[3], name }]
|
|
84608
84622
|
};
|
|
84609
84623
|
}
|
|
84610
84624
|
const inList = expr.match(IN_LIST);
|
|
@@ -84825,6 +84839,7 @@ var COLUMN_FORMATS = {
|
|
|
84825
84839
|
// probes, `1_000` and `0xDEAD_beef` through to `1__0`, `_1`, `0x` and `1e+`.
|
|
84826
84840
|
numeric: "^\\s*([+-]?(0[xX][0-9a-fA-F](_?[0-9a-fA-F])*|0[oO][0-7](_?[0-7])*|0[bB][01](_?[01])*)|[+-]?(\\d(_?\\d)*(\\.(\\d(_?\\d)*)?)?|\\.\\d(_?\\d)*)([eE][+-]?\\d(_?\\d)*)?|[+-]?(NaN|Infinity))\\s*$"
|
|
84827
84841
|
};
|
|
84842
|
+
var CODEPOINT_LENGTH = "[...v].length";
|
|
84828
84843
|
function isIntegerColumn(c5) {
|
|
84829
84844
|
if (typeof c5.integer === "boolean") return c5.integer;
|
|
84830
84845
|
return c5.dbType === "INTEGER" || c5.min !== void 0 && c5.max !== void 0;
|
|
@@ -84867,6 +84882,7 @@ async function formatCode(code, filePath, fmt) {
|
|
|
84867
84882
|
// Annotate the CommonJS export names for ESM import in node:
|
|
84868
84883
|
0 && (module.exports = {
|
|
84869
84884
|
AFFIX_PROBE_TABLE,
|
|
84885
|
+
CODEPOINT_LENGTH,
|
|
84870
84886
|
COLUMN_FORMATS,
|
|
84871
84887
|
DEFAULT_IMPORT_EXTENSION,
|
|
84872
84888
|
DEFAULT_MODE_PREFIX,
|
package/dist/index.d.cts
CHANGED
|
@@ -230,12 +230,27 @@ interface RowCheck {
|
|
|
230
230
|
operator: ColumnCheck['operator'];
|
|
231
231
|
name?: string;
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* A constraint on a column's *character* count, from `CHECK (length(name) > 3)`.
|
|
235
|
+
*
|
|
236
|
+
* Kept apart from `ColumnCheck` because it is not a comparison of the value: it compares a count
|
|
237
|
+
* derived from it, and the count Postgres takes is code points rather than UTF-16 units. See
|
|
238
|
+
* `CODEPOINT_LENGTH` for why that distinction is load bearing.
|
|
239
|
+
*/
|
|
240
|
+
interface LengthCheck {
|
|
241
|
+
column: string;
|
|
242
|
+
operator: ColumnCheck['operator'];
|
|
243
|
+
/** Decimal, as text, matching how the other bounds are carried. */
|
|
244
|
+
value: string;
|
|
245
|
+
name?: string;
|
|
246
|
+
}
|
|
233
247
|
/** A check that was understood, or the reason it was not. */
|
|
234
248
|
type ParsedCheck = {
|
|
235
249
|
ok: true;
|
|
236
250
|
checks: ColumnCheck[];
|
|
237
251
|
sets?: ColumnSet[];
|
|
238
252
|
rows?: RowCheck[];
|
|
253
|
+
lengths?: LengthCheck[];
|
|
239
254
|
} | {
|
|
240
255
|
ok: false;
|
|
241
256
|
reason: string;
|
|
@@ -405,10 +420,32 @@ declare function isGeneratedColumn(c: Column, _primaryKeyColumns?: string[]): bo
|
|
|
405
420
|
* cidr parses as `inet` and then demands zero host bits, which no regex can state
|
|
406
421
|
*/
|
|
407
422
|
declare const COLUMN_FORMATS: Record<string, string>;
|
|
423
|
+
/**
|
|
424
|
+
* Why a character limit is not `.max(n)`.
|
|
425
|
+
*
|
|
426
|
+
* Postgres and MySQL count `varchar(n)` in **characters**; every JavaScript validator counts
|
|
427
|
+
* `.length`, which is UTF-16 code units. They agree until the text leaves the basic plane, and
|
|
428
|
+
* then they do not: `varchar(10)` accepts ten emoji, and `.max(10)` refuses eight of them.
|
|
429
|
+
*
|
|
430
|
+
* Verified against Postgres through PGlite, for `varchar(10)`:
|
|
431
|
+
*
|
|
432
|
+
* 3 emoji db accepts .max(10) accepts
|
|
433
|
+
* 8 emoji db accepts .max(10) REFUSES
|
|
434
|
+
* 10 emoji db accepts .max(10) REFUSES
|
|
435
|
+
* 11 emoji db refuses .max(10) refuses
|
|
436
|
+
*
|
|
437
|
+
* `[...v].length` counts code points, which is what the database counts, and matches on all four.
|
|
438
|
+
* Refusing a user's emoji is the failure mode this avoids, and it is the same rule applied
|
|
439
|
+
* everywhere else here: never reject what the database accepts.
|
|
440
|
+
*
|
|
441
|
+
* `@sinclair/typebox` and ArkType cannot express it. Both state a length declaratively with no
|
|
442
|
+
* predicate to hook, so they keep the UTF-16 form and are documented as approximate.
|
|
443
|
+
*/
|
|
444
|
+
declare const CODEPOINT_LENGTH = "[...v].length";
|
|
408
445
|
declare function isIntegerColumn(c: Column): boolean;
|
|
409
446
|
declare function insertColumns(table: Table): Column[];
|
|
410
447
|
declare function updateColumns(table: Table): Column[];
|
|
411
448
|
declare function selectColumns(table: Table): Column[];
|
|
412
449
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
413
450
|
|
|
414
|
-
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, COLUMN_FORMATS, type ColumnCheck, type ColumnSet, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, NAME_MODES, type NameMode, type ParsedCheck, type ResolvedAffix, type RowCheck, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, describeSet, formatCode, importSpecifier, insertColumns, isGeneratedColumn, isIntegerColumn, moduleFileName, moduleSpecifier, parseCheck, pascalCase, resolveAffix, resolveConfiguredImport, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
|
451
|
+
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, CODEPOINT_LENGTH, COLUMN_FORMATS, type ColumnCheck, type ColumnSet, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, type LengthCheck, NAME_MODES, type NameMode, type ParsedCheck, type ResolvedAffix, type RowCheck, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, describeSet, formatCode, importSpecifier, insertColumns, isGeneratedColumn, isIntegerColumn, moduleFileName, moduleSpecifier, parseCheck, pascalCase, resolveAffix, resolveConfiguredImport, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
package/dist/index.d.ts
CHANGED
|
@@ -230,12 +230,27 @@ interface RowCheck {
|
|
|
230
230
|
operator: ColumnCheck['operator'];
|
|
231
231
|
name?: string;
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* A constraint on a column's *character* count, from `CHECK (length(name) > 3)`.
|
|
235
|
+
*
|
|
236
|
+
* Kept apart from `ColumnCheck` because it is not a comparison of the value: it compares a count
|
|
237
|
+
* derived from it, and the count Postgres takes is code points rather than UTF-16 units. See
|
|
238
|
+
* `CODEPOINT_LENGTH` for why that distinction is load bearing.
|
|
239
|
+
*/
|
|
240
|
+
interface LengthCheck {
|
|
241
|
+
column: string;
|
|
242
|
+
operator: ColumnCheck['operator'];
|
|
243
|
+
/** Decimal, as text, matching how the other bounds are carried. */
|
|
244
|
+
value: string;
|
|
245
|
+
name?: string;
|
|
246
|
+
}
|
|
233
247
|
/** A check that was understood, or the reason it was not. */
|
|
234
248
|
type ParsedCheck = {
|
|
235
249
|
ok: true;
|
|
236
250
|
checks: ColumnCheck[];
|
|
237
251
|
sets?: ColumnSet[];
|
|
238
252
|
rows?: RowCheck[];
|
|
253
|
+
lengths?: LengthCheck[];
|
|
239
254
|
} | {
|
|
240
255
|
ok: false;
|
|
241
256
|
reason: string;
|
|
@@ -405,10 +420,32 @@ declare function isGeneratedColumn(c: Column, _primaryKeyColumns?: string[]): bo
|
|
|
405
420
|
* cidr parses as `inet` and then demands zero host bits, which no regex can state
|
|
406
421
|
*/
|
|
407
422
|
declare const COLUMN_FORMATS: Record<string, string>;
|
|
423
|
+
/**
|
|
424
|
+
* Why a character limit is not `.max(n)`.
|
|
425
|
+
*
|
|
426
|
+
* Postgres and MySQL count `varchar(n)` in **characters**; every JavaScript validator counts
|
|
427
|
+
* `.length`, which is UTF-16 code units. They agree until the text leaves the basic plane, and
|
|
428
|
+
* then they do not: `varchar(10)` accepts ten emoji, and `.max(10)` refuses eight of them.
|
|
429
|
+
*
|
|
430
|
+
* Verified against Postgres through PGlite, for `varchar(10)`:
|
|
431
|
+
*
|
|
432
|
+
* 3 emoji db accepts .max(10) accepts
|
|
433
|
+
* 8 emoji db accepts .max(10) REFUSES
|
|
434
|
+
* 10 emoji db accepts .max(10) REFUSES
|
|
435
|
+
* 11 emoji db refuses .max(10) refuses
|
|
436
|
+
*
|
|
437
|
+
* `[...v].length` counts code points, which is what the database counts, and matches on all four.
|
|
438
|
+
* Refusing a user's emoji is the failure mode this avoids, and it is the same rule applied
|
|
439
|
+
* everywhere else here: never reject what the database accepts.
|
|
440
|
+
*
|
|
441
|
+
* `@sinclair/typebox` and ArkType cannot express it. Both state a length declaratively with no
|
|
442
|
+
* predicate to hook, so they keep the UTF-16 form and are documented as approximate.
|
|
443
|
+
*/
|
|
444
|
+
declare const CODEPOINT_LENGTH = "[...v].length";
|
|
408
445
|
declare function isIntegerColumn(c: Column): boolean;
|
|
409
446
|
declare function insertColumns(table: Table): Column[];
|
|
410
447
|
declare function updateColumns(table: Table): Column[];
|
|
411
448
|
declare function selectColumns(table: Table): Column[];
|
|
412
449
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
413
450
|
|
|
414
|
-
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, COLUMN_FORMATS, type ColumnCheck, type ColumnSet, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, NAME_MODES, type NameMode, type ParsedCheck, type ResolvedAffix, type RowCheck, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, describeSet, formatCode, importSpecifier, insertColumns, isGeneratedColumn, isIntegerColumn, moduleFileName, moduleSpecifier, parseCheck, pascalCase, resolveAffix, resolveConfiguredImport, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
|
451
|
+
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, CODEPOINT_LENGTH, COLUMN_FORMATS, type ColumnCheck, type ColumnSet, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, type LengthCheck, NAME_MODES, type NameMode, type ParsedCheck, type ResolvedAffix, type RowCheck, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, describeSet, formatCode, importSpecifier, insertColumns, isGeneratedColumn, isIntegerColumn, moduleFileName, moduleSpecifier, parseCheck, pascalCase, resolveAffix, resolveConfiguredImport, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import "./chunk-ZZGILH5A.js";
|
|
|
3
3
|
// src/checks.ts
|
|
4
4
|
var COMPARISON = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(>=|<=|<>|!=|>|<|=)\s*(.+?)\s*$/;
|
|
5
5
|
var IN_LIST = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+IN\s*\((.+)\)\s*$/i;
|
|
6
|
+
var LENGTH_OF = /^\s*(?:length|char_length)\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s*(>=|<=|<>|!=|>|<|=)\s*(\d+)\s*$/i;
|
|
6
7
|
function splitTopLevelAnd(expr) {
|
|
7
8
|
const parts = [];
|
|
8
9
|
let depth = 0;
|
|
@@ -123,6 +124,7 @@ function parseCheck(expression, name) {
|
|
|
123
124
|
const checks = [];
|
|
124
125
|
const sets = [];
|
|
125
126
|
const rows = [];
|
|
127
|
+
const lengths = [];
|
|
126
128
|
for (const part of parts) {
|
|
127
129
|
const parsed = parseCheck(part, name);
|
|
128
130
|
if (!parsed.ok)
|
|
@@ -130,12 +132,23 @@ function parseCheck(expression, name) {
|
|
|
130
132
|
checks.push(...parsed.checks);
|
|
131
133
|
if (parsed.sets) sets.push(...parsed.sets);
|
|
132
134
|
if (parsed.rows) rows.push(...parsed.rows);
|
|
135
|
+
if (parsed.lengths) lengths.push(...parsed.lengths);
|
|
133
136
|
}
|
|
134
137
|
return {
|
|
135
138
|
ok: true,
|
|
136
139
|
checks,
|
|
137
140
|
...sets.length ? { sets } : {},
|
|
138
|
-
...rows.length ? { rows } : {}
|
|
141
|
+
...rows.length ? { rows } : {},
|
|
142
|
+
...lengths.length ? { lengths } : {}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const lengthOf = expr.match(LENGTH_OF);
|
|
146
|
+
if (lengthOf) {
|
|
147
|
+
const op2 = lengthOf[2] === "!=" ? "<>" : lengthOf[2];
|
|
148
|
+
return {
|
|
149
|
+
ok: true,
|
|
150
|
+
checks: [],
|
|
151
|
+
lengths: [{ column: lengthOf[1], operator: op2, value: lengthOf[3], name }]
|
|
139
152
|
};
|
|
140
153
|
}
|
|
141
154
|
const inList = expr.match(IN_LIST);
|
|
@@ -356,6 +369,7 @@ var COLUMN_FORMATS = {
|
|
|
356
369
|
// probes, `1_000` and `0xDEAD_beef` through to `1__0`, `_1`, `0x` and `1e+`.
|
|
357
370
|
numeric: "^\\s*([+-]?(0[xX][0-9a-fA-F](_?[0-9a-fA-F])*|0[oO][0-7](_?[0-7])*|0[bB][01](_?[01])*)|[+-]?(\\d(_?\\d)*(\\.(\\d(_?\\d)*)?)?|\\.\\d(_?\\d)*)([eE][+-]?\\d(_?\\d)*)?|[+-]?(NaN|Infinity))\\s*$"
|
|
358
371
|
};
|
|
372
|
+
var CODEPOINT_LENGTH = "[...v].length";
|
|
359
373
|
function isIntegerColumn(c) {
|
|
360
374
|
if (typeof c.integer === "boolean") return c.integer;
|
|
361
375
|
return c.dbType === "INTEGER" || c.min !== void 0 && c.max !== void 0;
|
|
@@ -397,6 +411,7 @@ async function formatCode(code, filePath, fmt) {
|
|
|
397
411
|
}
|
|
398
412
|
export {
|
|
399
413
|
AFFIX_PROBE_TABLE,
|
|
414
|
+
CODEPOINT_LENGTH,
|
|
400
415
|
COLUMN_FORMATS,
|
|
401
416
|
DEFAULT_IMPORT_EXTENSION,
|
|
402
417
|
DEFAULT_MODE_PREFIX,
|