chore: add dist files and .npmignore for npm distribution

- Include dist/ in git for npm package distribution
- Add .npmignore to exclude source files from npm package
- This ensures npm install from git works without requiring build

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
hyper 2026-03-31 14:55:46 +08:00
parent 7243a677fe
commit 5e93bb5a1d
10 changed files with 1678 additions and 3 deletions

6
.gitignore vendored
View File

@ -4,10 +4,10 @@ package-lock.json
pnpm-lock.yaml pnpm-lock.yaml
yarn.lock yarn.lock
# Build output # Build output (kept in git for npm distribution)
dist/ # dist/
# TypeScript cache # TypeScript Cache
*.tsbuildinfo *.tsbuildinfo
# IDE # IDE

47
.npmignore Normal file
View File

@ -0,0 +1,47 @@
# Source files
src/
# Config files
tsconfig.json
tsup.config.ts
# Dev dependencies config
.eslintrc*
.prettierrc*
jest.config.*
vitest.config.*
# Test files
*.test.ts
*.spec.ts
__tests__/
test/
tests/
# Docs
*.md
!README.md
# Git
.git/
.gitignore
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
# TypeScript cache
*.tsbuildinfo
# Local env
.env*

13
dist/csv-loader/loader.d.mts vendored Normal file
View File

@ -0,0 +1,13 @@
import { LoaderContext } from '@rspack/core';
interface CsvLoaderOptions {
delimiter?: string;
quote?: string;
escape?: string;
bom?: boolean;
comment?: string | false;
trim?: boolean;
}
declare function csvLoader(this: LoaderContext<CsvLoaderOptions>, content: string): string | Buffer;
export { type CsvLoaderOptions, csvLoader as default };

13
dist/csv-loader/loader.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { LoaderContext } from '@rspack/core';
interface CsvLoaderOptions {
delimiter?: string;
quote?: string;
escape?: string;
bom?: boolean;
comment?: string | false;
trim?: boolean;
}
declare function csvLoader(this: LoaderContext<CsvLoaderOptions>, content: string): string | Buffer;
export { type CsvLoaderOptions, csvLoader as default };

420
dist/csv-loader/loader.js vendored Normal file
View File

@ -0,0 +1,420 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/csv-loader/loader.ts
var loader_exports = {};
__export(loader_exports, {
default: () => csvLoader
});
module.exports = __toCommonJS(loader_exports);
var import_sync = require("csv-parse/sync");
// src/parser.ts
var ParseError = class extends Error {
constructor(message, position) {
super(position !== void 0 ? `${message} at position ${position}` : message);
this.position = position;
this.name = "ParseError";
}
};
var Parser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
match(str) {
return this.input.slice(this.pos, this.pos + str.length) === str;
}
consumeStr(str) {
if (this.match(str)) {
this.pos += str.length;
return true;
}
return false;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
parseSchema() {
this.skipWhitespace();
if (this.consumeStr("string")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
if (this.consumeStr("number")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "number" } };
}
return { type: "number" };
}
if (this.consumeStr("boolean")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "boolean" } };
}
return { type: "boolean" };
}
if (this.consumeStr("[")) {
const elements = [];
this.skipWhitespace();
if (this.peek() === "]") {
this.consume();
throw new ParseError("Empty array/tuple not allowed", this.pos);
}
elements.push(this.parseSchema());
this.skipWhitespace();
if (this.consumeStr(";")) {
const remainingElements = [];
while (true) {
this.skipWhitespace();
remainingElements.push(this.parseSchema());
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
elements.push(...remainingElements);
}
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "array", element: { type: "tuple", elements } };
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "tuple", elements };
}
let identifier = "";
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
identifier += this.consume();
}
if (identifier.length > 0) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
throw new ParseError(`Unexpected character: ${this.peek()}`, this.pos);
}
};
function parseSchema(schemaString) {
const parser = new Parser(schemaString.trim());
const schema = parser.parseSchema();
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after schema", parser.getPosition());
}
return schema;
}
// src/validator.ts
var ValueParser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
consumeStr(str) {
if (this.input.slice(this.pos, this.pos + str.length) === str) {
this.pos += str.length;
return true;
}
return false;
}
parseValue(schema, allowOmitBrackets = false) {
this.skipWhitespace();
switch (schema.type) {
case "string":
return this.parseStringValue();
case "number":
return this.parseNumberValue();
case "boolean":
return this.parseBooleanValue();
case "tuple":
return this.parseTupleValue(schema, allowOmitBrackets);
case "array":
return this.parseArrayValue(schema, allowOmitBrackets);
default:
throw new ParseError(`Unknown schema type: ${schema.type}`, this.pos);
}
}
parseStringValue() {
let result = "";
while (this.pos < this.input.length) {
const char = this.peek();
if (char === "\\") {
this.consume();
const nextChar = this.consume();
if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
result += nextChar;
} else {
result += "\\" + nextChar;
}
} else if (char === ";" || char === "]") {
break;
} else {
result += this.consume();
}
}
return result.trim();
}
parseNumberValue() {
let numStr = "";
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
numStr += this.consume();
}
const num = parseFloat(numStr);
if (isNaN(num)) {
throw new ParseError("Invalid number", this.pos - numStr.length);
}
return num;
}
parseBooleanValue() {
if (this.consumeStr("true")) {
return true;
}
if (this.consumeStr("false")) {
return false;
}
throw new ParseError("Expected true or false", this.pos);
}
parseTupleValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
if (this.peek() === "[") {
this.consume();
hasOpenBracket = true;
} else if (!allowOmitBrackets) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
for (let i = 0; i < schema.elements.length; i++) {
this.skipWhitespace();
result.push(this.parseValue(schema.elements[i], false));
this.skipWhitespace();
if (i < schema.elements.length - 1) {
if (!this.consumeStr(";")) {
throw new ParseError("Expected ;", this.pos);
}
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
parseArrayValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
if (this.peek() === "[") {
if (!elementIsTupleOrArray) {
this.consume();
hasOpenBracket = true;
} else if (this.input[this.pos + 1] === "[") {
this.consume();
hasOpenBracket = true;
}
}
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
while (true) {
this.skipWhitespace();
result.push(this.parseValue(schema.element, false));
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
};
function parseValue(schema, valueString) {
const parser = new ValueParser(valueString.trim());
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
const value = parser.parseValue(schema, allowOmitBrackets);
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after value", parser.getPosition());
}
return value;
}
function createValidator(schema) {
return function validate(value) {
switch (schema.type) {
case "string":
return typeof value === "string";
case "number":
return typeof value === "number" && !isNaN(value);
case "boolean":
return typeof value === "boolean";
case "tuple":
if (!Array.isArray(value)) return false;
if (value.length !== schema.elements.length) return false;
return schema.elements.every(
(elementSchema, index) => createValidator(elementSchema)(value[index])
);
case "array":
if (!Array.isArray(value)) return false;
return value.every((item) => createValidator(schema.element)(item));
default:
return false;
}
};
}
// src/csv-loader/loader.ts
function csvLoader(content) {
const options = this.getOptions();
const delimiter = options?.delimiter ?? ",";
const quote = options?.quote ?? '"';
const escape = options?.escape ?? "\\";
const bom = options?.bom ?? true;
const comment = options?.comment === false ? void 0 : options?.comment ?? "#";
const trim = options?.trim ?? true;
const records = (0, import_sync.parse)(content, {
delimiter,
quote,
escape,
bom,
comment,
trim,
relax_column_count: true
});
if (records.length < 2) {
throw new Error("CSV must have at least 2 rows: headers and schemas");
}
const headers = records[0];
const schemas = records[1];
if (headers.length !== schemas.length) {
throw new Error(
`Header count (${headers.length}) does not match schema count (${schemas.length})`
);
}
const propertyConfigs = headers.map((header, index) => {
const schemaString = schemas[index];
const schema = parseSchema(schemaString);
return {
name: header,
schema,
validator: createValidator(schema),
parser: (valueString) => parseValue(schema, valueString)
};
});
const dataRows = records.slice(2);
const objects = dataRows.map((row, rowIndex) => {
const obj = {};
propertyConfigs.forEach((config, colIndex) => {
const rawValue = row[colIndex] ?? "";
try {
const parsed = config.parser(rawValue);
if (!config.validator(parsed)) {
throw new Error(
`Validation failed for property "${config.name}" at row ${rowIndex + 3}: ${rawValue}`
);
}
obj[config.name] = parsed;
} catch (error) {
if (error instanceof Error) {
throw new Error(
`Failed to parse property "${config.name}" at row ${rowIndex + 3}, column ${colIndex + 1}: ${error.message}`
);
}
throw error;
}
});
return obj;
});
const json = JSON.stringify(objects, null, 2);
return `export default ${json};`;
}

399
dist/csv-loader/loader.mjs vendored Normal file
View File

@ -0,0 +1,399 @@
// src/csv-loader/loader.ts
import { parse } from "csv-parse/sync";
// src/parser.ts
var ParseError = class extends Error {
constructor(message, position) {
super(position !== void 0 ? `${message} at position ${position}` : message);
this.position = position;
this.name = "ParseError";
}
};
var Parser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
match(str) {
return this.input.slice(this.pos, this.pos + str.length) === str;
}
consumeStr(str) {
if (this.match(str)) {
this.pos += str.length;
return true;
}
return false;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
parseSchema() {
this.skipWhitespace();
if (this.consumeStr("string")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
if (this.consumeStr("number")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "number" } };
}
return { type: "number" };
}
if (this.consumeStr("boolean")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "boolean" } };
}
return { type: "boolean" };
}
if (this.consumeStr("[")) {
const elements = [];
this.skipWhitespace();
if (this.peek() === "]") {
this.consume();
throw new ParseError("Empty array/tuple not allowed", this.pos);
}
elements.push(this.parseSchema());
this.skipWhitespace();
if (this.consumeStr(";")) {
const remainingElements = [];
while (true) {
this.skipWhitespace();
remainingElements.push(this.parseSchema());
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
elements.push(...remainingElements);
}
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "array", element: { type: "tuple", elements } };
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "tuple", elements };
}
let identifier = "";
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
identifier += this.consume();
}
if (identifier.length > 0) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
throw new ParseError(`Unexpected character: ${this.peek()}`, this.pos);
}
};
function parseSchema(schemaString) {
const parser = new Parser(schemaString.trim());
const schema = parser.parseSchema();
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after schema", parser.getPosition());
}
return schema;
}
// src/validator.ts
var ValueParser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
consumeStr(str) {
if (this.input.slice(this.pos, this.pos + str.length) === str) {
this.pos += str.length;
return true;
}
return false;
}
parseValue(schema, allowOmitBrackets = false) {
this.skipWhitespace();
switch (schema.type) {
case "string":
return this.parseStringValue();
case "number":
return this.parseNumberValue();
case "boolean":
return this.parseBooleanValue();
case "tuple":
return this.parseTupleValue(schema, allowOmitBrackets);
case "array":
return this.parseArrayValue(schema, allowOmitBrackets);
default:
throw new ParseError(`Unknown schema type: ${schema.type}`, this.pos);
}
}
parseStringValue() {
let result = "";
while (this.pos < this.input.length) {
const char = this.peek();
if (char === "\\") {
this.consume();
const nextChar = this.consume();
if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
result += nextChar;
} else {
result += "\\" + nextChar;
}
} else if (char === ";" || char === "]") {
break;
} else {
result += this.consume();
}
}
return result.trim();
}
parseNumberValue() {
let numStr = "";
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
numStr += this.consume();
}
const num = parseFloat(numStr);
if (isNaN(num)) {
throw new ParseError("Invalid number", this.pos - numStr.length);
}
return num;
}
parseBooleanValue() {
if (this.consumeStr("true")) {
return true;
}
if (this.consumeStr("false")) {
return false;
}
throw new ParseError("Expected true or false", this.pos);
}
parseTupleValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
if (this.peek() === "[") {
this.consume();
hasOpenBracket = true;
} else if (!allowOmitBrackets) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
for (let i = 0; i < schema.elements.length; i++) {
this.skipWhitespace();
result.push(this.parseValue(schema.elements[i], false));
this.skipWhitespace();
if (i < schema.elements.length - 1) {
if (!this.consumeStr(";")) {
throw new ParseError("Expected ;", this.pos);
}
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
parseArrayValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
if (this.peek() === "[") {
if (!elementIsTupleOrArray) {
this.consume();
hasOpenBracket = true;
} else if (this.input[this.pos + 1] === "[") {
this.consume();
hasOpenBracket = true;
}
}
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
while (true) {
this.skipWhitespace();
result.push(this.parseValue(schema.element, false));
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
};
function parseValue(schema, valueString) {
const parser = new ValueParser(valueString.trim());
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
const value = parser.parseValue(schema, allowOmitBrackets);
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after value", parser.getPosition());
}
return value;
}
function createValidator(schema) {
return function validate(value) {
switch (schema.type) {
case "string":
return typeof value === "string";
case "number":
return typeof value === "number" && !isNaN(value);
case "boolean":
return typeof value === "boolean";
case "tuple":
if (!Array.isArray(value)) return false;
if (value.length !== schema.elements.length) return false;
return schema.elements.every(
(elementSchema, index) => createValidator(elementSchema)(value[index])
);
case "array":
if (!Array.isArray(value)) return false;
return value.every((item) => createValidator(schema.element)(item));
default:
return false;
}
};
}
// src/csv-loader/loader.ts
function csvLoader(content) {
const options = this.getOptions();
const delimiter = options?.delimiter ?? ",";
const quote = options?.quote ?? '"';
const escape = options?.escape ?? "\\";
const bom = options?.bom ?? true;
const comment = options?.comment === false ? void 0 : options?.comment ?? "#";
const trim = options?.trim ?? true;
const records = parse(content, {
delimiter,
quote,
escape,
bom,
comment,
trim,
relax_column_count: true
});
if (records.length < 2) {
throw new Error("CSV must have at least 2 rows: headers and schemas");
}
const headers = records[0];
const schemas = records[1];
if (headers.length !== schemas.length) {
throw new Error(
`Header count (${headers.length}) does not match schema count (${schemas.length})`
);
}
const propertyConfigs = headers.map((header, index) => {
const schemaString = schemas[index];
const schema = parseSchema(schemaString);
return {
name: header,
schema,
validator: createValidator(schema),
parser: (valueString) => parseValue(schema, valueString)
};
});
const dataRows = records.slice(2);
const objects = dataRows.map((row, rowIndex) => {
const obj = {};
propertyConfigs.forEach((config, colIndex) => {
const rawValue = row[colIndex] ?? "";
try {
const parsed = config.parser(rawValue);
if (!config.validator(parsed)) {
throw new Error(
`Validation failed for property "${config.name}" at row ${rowIndex + 3}: ${rawValue}`
);
}
obj[config.name] = parsed;
} catch (error) {
if (error instanceof Error) {
throw new Error(
`Failed to parse property "${config.name}" at row ${rowIndex + 3}, column ${colIndex + 1}: ${error.message}`
);
}
throw error;
}
});
return obj;
});
const json = JSON.stringify(objects, null, 2);
return `export default ${json};`;
}
export {
csvLoader as default
};

31
dist/index.d.mts vendored Normal file
View File

@ -0,0 +1,31 @@
type SchemaType = 'string' | 'number' | 'boolean';
interface PrimitiveSchema {
type: SchemaType;
}
interface TupleSchema {
type: 'tuple';
elements: Schema[];
}
interface ArraySchema {
type: 'array';
element: Schema;
}
type Schema = PrimitiveSchema | TupleSchema | ArraySchema;
interface ParsedSchema {
schema: Schema;
validator: (value: unknown) => boolean;
parse: (valueString: string) => unknown;
}
declare class ParseError extends Error {
position?: number | undefined;
constructor(message: string, position?: number | undefined);
}
declare function parseSchema(schemaString: string): Schema;
declare function parseValue(schema: Schema, valueString: string): unknown;
declare function createValidator(schema: Schema): (value: unknown) => boolean;
declare function defineSchema(schemaString: string): ParsedSchema;
export { type ArraySchema, ParseError, type ParsedSchema, type PrimitiveSchema, type Schema, type TupleSchema, createValidator, defineSchema, parseSchema, parseValue };

31
dist/index.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
type SchemaType = 'string' | 'number' | 'boolean';
interface PrimitiveSchema {
type: SchemaType;
}
interface TupleSchema {
type: 'tuple';
elements: Schema[];
}
interface ArraySchema {
type: 'array';
element: Schema;
}
type Schema = PrimitiveSchema | TupleSchema | ArraySchema;
interface ParsedSchema {
schema: Schema;
validator: (value: unknown) => boolean;
parse: (valueString: string) => unknown;
}
declare class ParseError extends Error {
position?: number | undefined;
constructor(message: string, position?: number | undefined);
}
declare function parseSchema(schemaString: string): Schema;
declare function parseValue(schema: Schema, valueString: string): unknown;
declare function createValidator(schema: Schema): (value: unknown) => boolean;
declare function defineSchema(schemaString: string): ParsedSchema;
export { type ArraySchema, ParseError, type ParsedSchema, type PrimitiveSchema, type Schema, type TupleSchema, createValidator, defineSchema, parseSchema, parseValue };

376
dist/index.js vendored Normal file
View File

@ -0,0 +1,376 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ParseError: () => ParseError,
createValidator: () => createValidator,
defineSchema: () => defineSchema,
parseSchema: () => parseSchema,
parseValue: () => parseValue
});
module.exports = __toCommonJS(index_exports);
// src/parser.ts
var ParseError = class extends Error {
constructor(message, position) {
super(position !== void 0 ? `${message} at position ${position}` : message);
this.position = position;
this.name = "ParseError";
}
};
var Parser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
match(str) {
return this.input.slice(this.pos, this.pos + str.length) === str;
}
consumeStr(str) {
if (this.match(str)) {
this.pos += str.length;
return true;
}
return false;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
parseSchema() {
this.skipWhitespace();
if (this.consumeStr("string")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
if (this.consumeStr("number")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "number" } };
}
return { type: "number" };
}
if (this.consumeStr("boolean")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "boolean" } };
}
return { type: "boolean" };
}
if (this.consumeStr("[")) {
const elements = [];
this.skipWhitespace();
if (this.peek() === "]") {
this.consume();
throw new ParseError("Empty array/tuple not allowed", this.pos);
}
elements.push(this.parseSchema());
this.skipWhitespace();
if (this.consumeStr(";")) {
const remainingElements = [];
while (true) {
this.skipWhitespace();
remainingElements.push(this.parseSchema());
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
elements.push(...remainingElements);
}
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "array", element: { type: "tuple", elements } };
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "tuple", elements };
}
let identifier = "";
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
identifier += this.consume();
}
if (identifier.length > 0) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
throw new ParseError(`Unexpected character: ${this.peek()}`, this.pos);
}
};
function parseSchema(schemaString) {
const parser = new Parser(schemaString.trim());
const schema = parser.parseSchema();
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after schema", parser.getPosition());
}
return schema;
}
// src/validator.ts
var ValueParser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
consumeStr(str) {
if (this.input.slice(this.pos, this.pos + str.length) === str) {
this.pos += str.length;
return true;
}
return false;
}
parseValue(schema, allowOmitBrackets = false) {
this.skipWhitespace();
switch (schema.type) {
case "string":
return this.parseStringValue();
case "number":
return this.parseNumberValue();
case "boolean":
return this.parseBooleanValue();
case "tuple":
return this.parseTupleValue(schema, allowOmitBrackets);
case "array":
return this.parseArrayValue(schema, allowOmitBrackets);
default:
throw new ParseError(`Unknown schema type: ${schema.type}`, this.pos);
}
}
parseStringValue() {
let result = "";
while (this.pos < this.input.length) {
const char = this.peek();
if (char === "\\") {
this.consume();
const nextChar = this.consume();
if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
result += nextChar;
} else {
result += "\\" + nextChar;
}
} else if (char === ";" || char === "]") {
break;
} else {
result += this.consume();
}
}
return result.trim();
}
parseNumberValue() {
let numStr = "";
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
numStr += this.consume();
}
const num = parseFloat(numStr);
if (isNaN(num)) {
throw new ParseError("Invalid number", this.pos - numStr.length);
}
return num;
}
parseBooleanValue() {
if (this.consumeStr("true")) {
return true;
}
if (this.consumeStr("false")) {
return false;
}
throw new ParseError("Expected true or false", this.pos);
}
parseTupleValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
if (this.peek() === "[") {
this.consume();
hasOpenBracket = true;
} else if (!allowOmitBrackets) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
for (let i = 0; i < schema.elements.length; i++) {
this.skipWhitespace();
result.push(this.parseValue(schema.elements[i], false));
this.skipWhitespace();
if (i < schema.elements.length - 1) {
if (!this.consumeStr(";")) {
throw new ParseError("Expected ;", this.pos);
}
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
parseArrayValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
if (this.peek() === "[") {
if (!elementIsTupleOrArray) {
this.consume();
hasOpenBracket = true;
} else if (this.input[this.pos + 1] === "[") {
this.consume();
hasOpenBracket = true;
}
}
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
while (true) {
this.skipWhitespace();
result.push(this.parseValue(schema.element, false));
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
};
function parseValue(schema, valueString) {
const parser = new ValueParser(valueString.trim());
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
const value = parser.parseValue(schema, allowOmitBrackets);
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after value", parser.getPosition());
}
return value;
}
function createValidator(schema) {
return function validate(value) {
switch (schema.type) {
case "string":
return typeof value === "string";
case "number":
return typeof value === "number" && !isNaN(value);
case "boolean":
return typeof value === "boolean";
case "tuple":
if (!Array.isArray(value)) return false;
if (value.length !== schema.elements.length) return false;
return schema.elements.every(
(elementSchema, index) => createValidator(elementSchema)(value[index])
);
case "array":
if (!Array.isArray(value)) return false;
return value.every((item) => createValidator(schema.element)(item));
default:
return false;
}
};
}
// src/index.ts
function defineSchema(schemaString) {
const schema = parseSchema(schemaString);
const validator = createValidator(schema);
return {
schema,
validator,
parse: (valueString) => parseValue(schema, valueString)
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ParseError,
createValidator,
defineSchema,
parseSchema,
parseValue
});

345
dist/index.mjs vendored Normal file
View File

@ -0,0 +1,345 @@
// src/parser.ts
var ParseError = class extends Error {
constructor(message, position) {
super(position !== void 0 ? `${message} at position ${position}` : message);
this.position = position;
this.name = "ParseError";
}
};
var Parser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
match(str) {
return this.input.slice(this.pos, this.pos + str.length) === str;
}
consumeStr(str) {
if (this.match(str)) {
this.pos += str.length;
return true;
}
return false;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
parseSchema() {
this.skipWhitespace();
if (this.consumeStr("string")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
if (this.consumeStr("number")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "number" } };
}
return { type: "number" };
}
if (this.consumeStr("boolean")) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "boolean" } };
}
return { type: "boolean" };
}
if (this.consumeStr("[")) {
const elements = [];
this.skipWhitespace();
if (this.peek() === "]") {
this.consume();
throw new ParseError("Empty array/tuple not allowed", this.pos);
}
elements.push(this.parseSchema());
this.skipWhitespace();
if (this.consumeStr(";")) {
const remainingElements = [];
while (true) {
this.skipWhitespace();
remainingElements.push(this.parseSchema());
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
elements.push(...remainingElements);
}
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "array", element: { type: "tuple", elements } };
}
if (elements.length === 1) {
return { type: "array", element: elements[0] };
}
return { type: "tuple", elements };
}
let identifier = "";
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
identifier += this.consume();
}
if (identifier.length > 0) {
if (this.consumeStr("[")) {
this.skipWhitespace();
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
return { type: "array", element: { type: "string" } };
}
return { type: "string" };
}
throw new ParseError(`Unexpected character: ${this.peek()}`, this.pos);
}
};
function parseSchema(schemaString) {
const parser = new Parser(schemaString.trim());
const schema = parser.parseSchema();
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after schema", parser.getPosition());
}
return schema;
}
// src/validator.ts
var ValueParser = class {
constructor(input) {
this.pos = 0;
this.input = input;
}
peek() {
return this.input[this.pos] || "";
}
consume() {
return this.input[this.pos++] || "";
}
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
consumeStr(str) {
if (this.input.slice(this.pos, this.pos + str.length) === str) {
this.pos += str.length;
return true;
}
return false;
}
parseValue(schema, allowOmitBrackets = false) {
this.skipWhitespace();
switch (schema.type) {
case "string":
return this.parseStringValue();
case "number":
return this.parseNumberValue();
case "boolean":
return this.parseBooleanValue();
case "tuple":
return this.parseTupleValue(schema, allowOmitBrackets);
case "array":
return this.parseArrayValue(schema, allowOmitBrackets);
default:
throw new ParseError(`Unknown schema type: ${schema.type}`, this.pos);
}
}
parseStringValue() {
let result = "";
while (this.pos < this.input.length) {
const char = this.peek();
if (char === "\\") {
this.consume();
const nextChar = this.consume();
if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
result += nextChar;
} else {
result += "\\" + nextChar;
}
} else if (char === ";" || char === "]") {
break;
} else {
result += this.consume();
}
}
return result.trim();
}
parseNumberValue() {
let numStr = "";
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
numStr += this.consume();
}
const num = parseFloat(numStr);
if (isNaN(num)) {
throw new ParseError("Invalid number", this.pos - numStr.length);
}
return num;
}
parseBooleanValue() {
if (this.consumeStr("true")) {
return true;
}
if (this.consumeStr("false")) {
return false;
}
throw new ParseError("Expected true or false", this.pos);
}
parseTupleValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
if (this.peek() === "[") {
this.consume();
hasOpenBracket = true;
} else if (!allowOmitBrackets) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
for (let i = 0; i < schema.elements.length; i++) {
this.skipWhitespace();
result.push(this.parseValue(schema.elements[i], false));
this.skipWhitespace();
if (i < schema.elements.length - 1) {
if (!this.consumeStr(";")) {
throw new ParseError("Expected ;", this.pos);
}
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
parseArrayValue(schema, allowOmitBrackets) {
let hasOpenBracket = false;
const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
if (this.peek() === "[") {
if (!elementIsTupleOrArray) {
this.consume();
hasOpenBracket = true;
} else if (this.input[this.pos + 1] === "[") {
this.consume();
hasOpenBracket = true;
}
}
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
throw new ParseError("Expected [", this.pos);
}
this.skipWhitespace();
if (this.peek() === "]" && hasOpenBracket) {
this.consume();
return [];
}
const result = [];
while (true) {
this.skipWhitespace();
result.push(this.parseValue(schema.element, false));
this.skipWhitespace();
if (!this.consumeStr(";")) {
break;
}
}
this.skipWhitespace();
if (hasOpenBracket) {
if (!this.consumeStr("]")) {
throw new ParseError("Expected ]", this.pos);
}
}
return result;
}
getPosition() {
return this.pos;
}
getInputLength() {
return this.input.length;
}
};
function parseValue(schema, valueString) {
const parser = new ValueParser(valueString.trim());
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
const value = parser.parseValue(schema, allowOmitBrackets);
if (parser.getPosition() < parser.getInputLength()) {
throw new ParseError("Unexpected input after value", parser.getPosition());
}
return value;
}
function createValidator(schema) {
return function validate(value) {
switch (schema.type) {
case "string":
return typeof value === "string";
case "number":
return typeof value === "number" && !isNaN(value);
case "boolean":
return typeof value === "boolean";
case "tuple":
if (!Array.isArray(value)) return false;
if (value.length !== schema.elements.length) return false;
return schema.elements.every(
(elementSchema, index) => createValidator(elementSchema)(value[index])
);
case "array":
if (!Array.isArray(value)) return false;
return value.every((item) => createValidator(schema.element)(item));
default:
return false;
}
};
}
// src/index.ts
function defineSchema(schemaString) {
const schema = parseSchema(schemaString);
const validator = createValidator(schema);
return {
schema,
validator,
parse: (valueString) => parseValue(schema, valueString)
};
}
export {
ParseError,
createValidator,
defineSchema,
parseSchema,
parseValue
};