You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Before contributing to anyone's code, be sure to study how the existing project code
is expressed. It's important that all code in a project closely matches each others.
❌ Do not use your own standards irrespective of the existing project's code. Even if they have not
explicitly set a standard.
Char Length
Code should be written like a book. A book has pages and each line of each page has a common max length.
Imagine a child's eye moving from left to right when reading a book. As that child reads while growing
up, their eyes move a lot less. Eventually a person moves from letter to letter, to word by word, to line
by line, and some can even do page by page (speed reading). This is because as a person reads more and
more they develop their sight peripherals, but there is a limit to how far peripherals can reach (less
than 180 degrees). Around when an individual's sight peripherals reach their limit is when the eyes need
to move. Eyes moving constantly without rest can cause eye stress. Unlike a book, there is no max length
a line of code could be and contributes to eye stress for everyone reading code not having a standard.
Every line of code should not exceed 80 characters.
If you are using VSCode
Go to File > Preferences > Settings.
Search for "rulers"
Click "Edit in settings.json"
Add the following to settings.json
"editor.rulers": [72,80,100,120],
To keep code readable and prevent horizontal scrolling, follow a consistent line breaking (drop down) style.
Only use /*...*/ for JSDoc-style comments. In Typescript there is no need to add @param to the JSDoc.
//✅ Good/** * Does the foobar */functionfooBar(foo: string){}//❌ Bad/** * Does the foobar * * @param string foo */functionfooBar(foo: string){}
Only use inline comments // inside of functions and blocks. For single line inline comments, do not add a space after //.
//This is a good comment// This is a bad comment//This is a long comment that describes// complicated logic. You can use spaces// so they know it's connected as one// comment block//These are directions// 1. Step 1// 2. Step 2//These are flows// - if good then send// - otherwise dont send
Spaces Between Data Block
A data block are non scalar data structure like arrays and objects. Data blocks should have a space in between the opening and closing where values are present. Consider the following examples.
Variables representing booleans, can use is, has, can, should, valid
Variables representing functions, follow function naming
Variables representing class definitions, follow class naming
Functions
Use camelCase
Use verbs or verb phrasing (verb followed by noun): get(), getUser(), submitForm(), resetTimer()
For React components, follow class naming
Classes & Components
Use PascalCase
Use nouns for values: User, EventEmitter, TaskQueue
Folders & Files
In general you should use kebab-case to name your folders and files. Files that export default a class or component should use PascalCase.
Item
Naming
Example
Files (JS/TS)
kebab-case
user-service.ts
Classes
PascalCase
User.ts
Components
PascalCase
UserCard.tsx
Folders
kebab-case
user-profile/
Class Index
PascalCase
User/index.ts
Component Index
PascalCase
UserCard/index.tsx
Imports
Organize imports by node modules like node:path, node:fs, packages (in node_modules) and local imports as well as type imports and actual imports. Consider the following.
When defining component prop arguments, it's important that it's easily readable. For complex props consider separating the type into a separate type definition. Consider the following.
Use separate markdown files for each major component or class (e.g., EventEmitter, ExpressEmitter).
Ensure each file follows a consistent structure with sections for features, public properties, methods, and examples.
A description should always follow a header. If you can determine a proper description, then talk about what the next sections are about. I you made the same mistake with #### methods. FYI i define headers as lines that start with a hashtag.
Method Documentation:
Document each method with a clear description, parameter table, and example usage.
Avoid using tables for return values; instead, describe the return type in the method description.
Parameter Tables:
Use tables to list parameters for each method, including type, default value (if applicable), and description.
Examples:
Provide practical examples for each method to illustrate usage.
Ensure examples are relevant and demonstrate typical use cases.
Public vs. Protected/Private:
Focus on documenting public properties and methods.
Avoid documenting protected or private members unless necessary for understanding public API behavior.
Just call public properties, properties in the documentation.
Type Parameters:
Clearly define type parameters for generic classes (e.g., ItemQueue<I>, TaskQueue<A>).
Provide examples that demonstrate how to declare and use these types.
Feedback Integration:
Continuously integrate user feedback to refine documentation.
Address specific critiques, such as missing methods or incorrect examples, promptly.
Consistency and Clarity:
Maintain consistency in formatting, terminology, and style across all documentation files.
Ensure clarity and accessibility for the target audience, including junior developers.
Code Char Length:
Code should be written like a book. A book has pages and each line of each page has a common max length. Imagine a child's eye moving from left to right when reading a book. As that child reads while growing up, their eyes move a lot less. Eventually a person moves from letter to letter, to word by word, to line by line, and some can even do page by page (speed reading). This is because as a person reads more and more they develop their sight peripherals, but there is a limit to how far peripherals can reach (less than 180 degrees). Around when an individual's sight peripherals reach their limit is when the eyes need to move. Eyes moving constantly without rest can cause eye stress. Unlike a book, there is no max length a line of code could be and contributes to eye stress for everyone reading code not having a standard.
Unless it cannot be avoided, every line of code should not exceed 80 characters.
The .idea file format is a declarative schema definition language designed to simplify application development by providing a single source of truth for data structures, relationships, and code generation. It enables developers to define their application's data model once and generate multiple outputs including database schemas, TypeScript interfaces, API documentation, forms, and more.
Key Benefits
Single Source of Truth: Define your data model once, use it everywhere
Type Safety: Generate type-safe code across multiple languages and frameworks
Rapid Development: Automatically generate boilerplate code, forms, and documentation
Consistency: Ensure consistent data structures across your entire application
Extensibility: Plugin system allows custom code generation for any target
Who Should Use This
Junior Developers: Easy-to-understand syntax with comprehensive examples
Senior Developers: Powerful features for complex applications
CTOs/Technical Leaders: Reduce development time and improve code consistency
Syntax Overview
The .idea file format uses a simplified syntax that eliminates the need for traditional separators like commas (,) and colons (:) found in JSON or JavaScript. The parser can logically determine separations, making the syntax cleaner and more readable.
Key Syntax Rules
No Separators Required: The parser intelligently determines where values begin and end
Double Quotes Only: All strings must use double quotes (") - single quotes are not supported
Logical Structure: The parser understands context and can differentiate between keys, values, and nested structures
// Strings - always use double quotesname"John Doe"description"A comprehensive user management system"// Numbers - no quotes neededage30price99.99count-5// Booleans - no quotes neededactivetrueverifiedfalse// Arrays - space-separated valuestags["admin""user""moderator"]numbers[12345]mixed["text"123true]// Objects - nested key-value pairsprofile{firstName"John"lastName"Doe"settings{theme"dark"notificationstrue}}
Comments
Comments in .idea files use the standard // syntax:
// This is a single-line commentmodelUser{idString @id// Inline commentnameString @required// Another commentemailString @unique}/* Multi-line comments are also supported for longer explanations*/
Data Types
The .idea format supports four primary data types that form the building blocks of your application schema.
Enum
Enums define a set of named constants with associated values, perfect for representing fixed sets of options like user roles, status values, or categories.
Syntax
enumEnumName{KEY1"Display Value 1"KEY2"Display Value 2"KEY3"Display Value 3"}
Structure
EnumName: The identifier used to reference this enum
KEY: The constant name (typically uppercase)
"Display Value": Human-readable label for the constant
Props are reusable property configurations that define common field behaviors, validation rules, and UI components. They promote consistency and reduce duplication across your schema.
PropName: The identifier used to reference this prop
property: Configuration key-value pairs
nested: Grouped configuration options
Example
propEmail{type"email"format"email"validation{requiredtruepattern"^[^\s@]+@[^\s@]+\.[^\s@]+$"}ui{component"EmailInput"placeholder"Enter your email address"icon"envelope"}}propPassword{type"password"validation{requiredtrueminLength8pattern"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]"}ui{component"PasswordInput"placeholder"Enter a secure password"showStrengthtrue}}propCurrency{type"number"format"currency"validation{min0precision2}ui{component"CurrencyInput"
symbol "$"locale"en-US"}}
Types define custom data structures with multiple columns, similar to objects or structs in programming languages. They're perfect for representing complex data that doesn't warrant a full model.
Models represent the core entities in your application, typically corresponding to database tables or API resources. They define the structure, relationships, and behavior of your data.
!: Optional non-mergeable indicator (prevents automatic merging when using use directives)
columnName: The field name
DataType: Built-in types (String, Number, Boolean, Date) or custom types/enums
@attribute: Attributes for validation, relationships, UI, etc.
Merging Behavior
By default, when importing schemas with use directives, models with the same name are automatically merged. The ! suffix prevents this behavior:
// base-schema.ideamodelUser{idString @idnameString @required}// extended-schema.ideause"./base-schema.idea"// This will merge with the imported User modelmodelUser{emailString @requiredcreatedDate @default("now()")}// This will NOT merge - it completely replaces the imported UsermodelUser!{idString @idusernameString @requiredpasswordString @required}
Attributes provide metadata and configuration for columns, types, and models. They define validation rules, UI components, relationships, and behavior. Attributes can be attached to any schema element and are processed by plugins according to their specific needs.
There are no reserved or pre-defined attributes in idea. You can define any arbitrary attributes in your schema. It's up to the plugins to recognize and process them.
Attribute Syntax
Attributes always start with the at symbol (@) followed by letters, numbers, and periods. They can be expressed in several forms:
// Simple boolean attribute (sets value to true)
@filterable// Function with single argument
@label("Name")// Function with multiple arguments
@is.cgt(3"Name should be more than 3 characters")// Function with object argument
@view.image({width100 height 100})// Nested attribute names using periods
@field.input(Email)
@validation.required
@ui.component("CustomInput")
Attribute Value Types
Attributes can hold different types of values:
// Boolean (implicit true)
@required
@unique
@filterable// String values
@label("User Name")
@placeholder("Enter your name")
@description("This field is required")// Number values
@min(0)
@max(100)
@precision(2)// Object values
@validation({requiredtrue minLength 3})
@ui({component"Input"placeholder"Enter text"})
@options({multipletruesearchable false })// Array values
@tags(["admin""user""guest"])
@options(["small""medium""large"])
@toolbar(["bold""italic""underline"])// Mixed arguments
@between(1100"Value must be between 1 and 100")
@pattern("^[a-zA-Z]+$""Only letters allowed")
Attribute Scope
Attributes can be applied to different schema elements:
Schema directives are special declarations that control how the schema is processed and what outputs are generated.
Use
The use directive imports definitions from other .idea files, enabling modular schema organization and reusability. When importing, data types with the same name are automatically merged unless the ! (non-mergeable) indicator is used.
Path: Relative or absolute path to the .idea file to import
Automatic Merging: Data types with matching names are merged by default
Merge Prevention: Use ! suffix to prevent merging
Example
shared/common.idea:
// Common types used across multiple schemastypeAddress{streetString @requiredcityString @requiredcountryString @default("US")}enumStatus{ACTIVE"Active"INACTIVE"Inactive"}propEmail{type"email"validation{requiredtrueformat"email"}}
user/user-schema.idea:
// Import common definitionsuse"../shared/common.idea"// Extend the Status enum (will merge with imported one)enumStatus{PENDING"Pending Approval"SUSPENDED"Temporarily Suspended"}// Use imported types and propsmodelUser{idString @id @default("nanoid()")emailString @field.input(Email)addressAddressstatusStatus @default("PENDING")}
Result after merging:
// The Status enum now contains all valuesenumStatus{ACTIVE"Active"// From common.ideaINACTIVE"Inactive"// From common.ideaPENDING"Pending Approval"// From user-schema.ideaSUSPENDED"Temporarily Suspended"// From user-schema.idea}
Preventing Merging with !
When you want to prevent automatic merging and keep definitions separate, use the ! suffix:
use"./base-schema.idea"// This will NOT merge with the imported UserRole// Instead, it will override it completelyenumUserRole!{GUEST"Guest User"MEMBER"Member"MODERATOR"Moderator"ADMIN"Administrator"}
Use Cases
Shared Types: Define common types once and reuse across multiple schemas
Modular Organization: Split large schemas into manageable, focused files
Team Collaboration: Different teams can work on separate schema files
Environment-Specific: Override certain definitions for different environments
Best Practices
// ✅ Good - organize by domainuse"./shared/common-types.idea"use"./auth/user-types.idea"use"./commerce/product-types.idea"// ✅ Good - clear file naminguse"./enums/status-enums.idea"use"./types/address-types.idea"use"./props/form-props.idea"// ❌ Avoid - unclear importsuse"./stuff.idea"use"./misc.idea"
Plugin
The plugin directive configures code generation plugins that process your schema and generate various outputs like TypeScript interfaces, database schemas, API documentation, and more.
Path: Relative or absolute path to the plugin file
Configuration Block: Key-value pairs that configure the plugin behavior
Nested Configuration: Support for complex configuration structures
Example
// TypeScript interface generationplugin"./plugins/typescript-generator.js"{output"./src/types/schema.ts"namespace"Schema"exportType"named"includeCommentstrueformatting{indent2semicolonstruetrailingCommastrue}}// Database schema generationplugin"./plugins/database-generator.js"{output"./database/schema.sql"dialect"postgresql"includeIndexestrueincludeForeignKeystruetablePrefix"app_"options{dropExistingfalseaddTimestampstruecharset"utf8mb4"}}// API documentation generationplugin"./plugins/openapi-generator.js"{output"./docs/api.yaml"version"1.0.0"title"My API Documentation"description"Comprehensive API documentation generated from schema"servers[{url "https://api.example.com/v1"description"Production server"}{url"https://staging-api.example.com/v1"description"Staging server"}]security{type"bearer"scheme"JWT"}}// Form generationplugin"./plugins/form-generator.js"{output"./src/components/forms/"framework"react"styling"tailwind"validation"zod"features{typescripttruestorybooktrueteststrue}components{inputWrapper"FormField"submitButton"SubmitButton"errorMessage"ErrorText"}}
Plugin Configuration Options
Common configuration patterns across different plugin types:
plugin"./plugins/my-plugin.js"{// Output configurationoutput"./generated/output.ts"outputDir"./generated/"// Format and style optionsformat"typescript"// or "javascript", "json", "yaml"style"camelCase"// or "snake_case", "kebab-case"// Feature flagsincludeCommentstruegenerateTestsfalseaddValidationtrue// Framework-specific optionsframework"react"// or "vue", "angular", "svelte"styling"tailwind"// or "bootstrap", "material", "custom"// Advanced configurationtemplates{model"./templates/model.hbs"enum"./templates/enum.hbs"}// Custom options (plugin-specific)customOptions{apiVersion"v1"includeMetadatatruecompressionLevel9}}
Multiple Plugin Execution
You can configure multiple plugins to generate different outputs from the same schema:
// Generate TypeScript typesplugin"./plugins/typescript.js"{output"./src/types/index.ts"}// Generate database schemaplugin"./plugins/database.js"{output"./database/schema.sql"dialect"postgresql"}// Generate API documentationplugin"./plugins/docs.js"{output"./docs/api.md"format"markdown"}// Generate form componentsplugin"./plugins/forms.js"{output"./src/forms/"framework"react"}// Generate validation schemasplugin"./plugins/validation.js"{output"./src/validation/index.ts"library"zod"}
Plugin Development
Plugins are JavaScript/TypeScript modules that export a default function:
// Example plugin structureimporttype{PluginProps}from'@stackpress/idea-transformer/types';interfaceMyPluginConfig{output: string;format?: 'typescript'|'javascript';includeComments?: boolean;}exportdefaultasyncfunctionmyPlugin(props: PluginProps<{config: MyPluginConfig}>){const{ config, schema, transformer, cwd }=props;// Validate configurationif(!config.output){thrownewError('Output path is required');}// Process schemaconstcontent=generateContent(schema,config);// Write outputconstoutputPath=awaittransformer.loader.absolute(config.output);awaitwriteFile(outputPath,content);console.log(`✅ Generated: ${outputPath}`);}
Processing Flow
The .idea file format follows a structured processing flow:
// Type generationplugin"./plugins/typescript-generator.js"{output"./src/types/schema.ts"namespace"Schema"}// Database schemaplugin"./plugins/database-generator.js"{output"./database/schema.sql"dialect"postgresql"}// API documentationplugin"./plugins/openapi-generator.js"{output"./docs/api.yaml"version"1.0.0"}// Form generationplugin"./plugins/form-generator.js"{output"./src/components/forms/"framework"react"styling"tailwind"}
// ❌ Invalid - Boolean can't have @minLengthmodelUser{activeBoolean @minLength(5)}// ✅ Valid - appropriate attributes for typemodelUser{activeBoolean @default(true)nameString @minLength(2) @maxLength(50)}
Error Prevention
1. Use TypeScript for Plugin Development
importtype{PluginProps,SchemaConfig}from'@stackpress/idea-transformer/types';exportdefaultasyncfunctionmyPlugin(props: PluginProps<{}>){// TypeScript will catch type errors at compile timeconst{ config, schema }=props;// Validate configurationif(!config.output){thrownewError('Output path is required');}// Process schema safelyif(schema.model){for(const[modelName,model]ofObject.entries(schema.model)){// Process each model}}}
2. Validate Schema Before Processing
// Always validate required fieldsmodelUser{idString @id @requiredemailString @required @uniquenameString @required}// Use appropriate data typesmodelProduct{priceNumber @min(0)// Not StringactiveBoolean// Not NumbercreatedDate @default("now()")// Not String}
# Parse schema to check for errors
npm run idea:parse schema.idea
# Transform schema to validate plugins
npm run idea:transform schema.idea
# Generate output to verify results
npm run idea:generate
Conclusion
The .idea file format provides a powerful, declarative approach to defining application schemas that can generate multiple outputs from a single source of truth. By following the patterns and best practices outlined in this specification, development teams can:
Key Advantages
Reduce Development Time: Generate boilerplate code, forms, documentation, and database schemas automatically
Improve Consistency: Ensure data structures remain consistent across frontend, backend, and database layers
Enhance Type Safety: Generate type-safe code for TypeScript, preventing runtime errors
Simplify Maintenance: Update schema once and regenerate all dependent code
Enable Rapid Prototyping: Quickly iterate on data models and see changes across the entire application
Getting Started
Define Your Schema: Start with a simple .idea file containing your core models
Add Plugins: Configure plugins to generate the outputs you need (TypeScript, SQL, forms, etc.)
Iterate and Refine: Add validation rules, relationships, and UI configurations as needed
Integrate with Build Process: Add schema generation to your CI/CD pipeline
Next Steps
Explore Plugin Ecosystem: Discover available plugins for your technology stack
Create Custom Plugins: Develop plugins for specific requirements not covered by existing ones
Join the Community: Contribute to the .idea ecosystem and share your experiences
Support and Resources
Documentation: Comprehensive guides for parser, transformer, and plugin development
Examples: Real-world schema examples for common application types
Community: Active community of developers using and contributing to the .idea ecosystem
The .idea file format represents a significant step forward in application development efficiency, providing the tools needed to build robust, type-safe applications with minimal boilerplate code. Whether you're a junior developer learning the ropes, a senior developer optimizing workflows, or a technical leader evaluating new tools, the .idea format offers clear benefits for modern application development.
Start small, think big, and let the .idea format transform how you build applications.
A TypeScript library for parsing .idea schema files into Abstract Syntax Trees (AST) and converting them to readable JSON configurations. This library is designed to help developers work with schema definitions in a structured and type-safe manner.
Installation
Install the package using npm:
npm install @stackpress/idea-parser
Quick Start
The library provides two main functions for parsing schema files:
Basic Usage
import{parse,final}from'@stackpress/idea-parser';// Parse a schema file into JSON (includes references)constschemaCode=`prop Text { type "text" }enum Roles { ADMIN "Admin" USER "User"}model User { id String @id name String @field.input(Text) role Roles}`;// Parse with references intactconstparsedSchema=parse(schemaCode);// Parse and clean up references (final version)constfinalSchema=final(schemaCode);
Difference Between parse and final
parse(code: string): Converts schema code to JSON while preserving prop and use references
final(code: string): Like parse but removes prop and use references for a clean final output
Core Concepts
Schema Structure
An .idea schema file can contain several types of declarations:
Plugins: External integrations and configurations
Use statements: Import other schema files
Props: Reusable property configurations
Enums: Enumerated value definitions
Types: Custom type definitions with columns
Models: Database model definitions
Processing Flow
The library follows this processing flow:
Raw Schema Code → SchemaTree → Compiler → JSON Output
Raw Code: Your .idea schema file content
SchemaTree: Parses the entire file into an Abstract Syntax Tree
Compiler: Converts AST tokens into structured JSON
JSON Output: Final configuration object
API Reference
Main Functions
parse(code: string)
Converts schema code into a JSON representation with references preserved.
Parameters:
code (string): The schema code to parse
Returns:
SchemaConfig: JSON object representing the parsed schema
Example:
import{parse}from'@stackpress/idea-parser';constresult=parse(`prop Text { type "text" }model User { name String @field.input(Text)}`);console.log(result);// Output includes prop references: { prop: { Text: { type: "text" }}, ... }
final(code: string)
Converts schema code into a clean JSON representation with references resolved and removed.
Parameters:
code (string): The schema code to parse
Returns:
FinalSchemaConfig: Clean JSON object without prop/use references
Example:
import{final}from'@stackpress/idea-parser';constresult=final(`prop Text { type "text" }model User { name String @field.input(Text)}`);console.log(result);// Output has resolved references: { model: { User: { ... }}}// No 'prop' section in output
Core Classes
Compiler: Static methods for converting AST tokens to JSON
Lexer: Tokenization and parsing utilities
SchemaTree: Main parser for complete schema files
Syntax Trees: Individual parsers for different schema elements
Tokens: AST token structures and type definitions
Exception Handling
The library uses a custom Exception class that extends the standard Exception class for better error reporting.
import{final}from'@stackpress/idea-parser';constschemaCode=`plugin "./database-plugin" { provider "postgresql" url env("DATABASE_URL")}prop Text { type "text" }prop Email { type "email" format "email" }enum UserRole { ADMIN "Administrator" USER "Regular User" GUEST "Guest User"}type Address { street String @field.input(Text) @is.required city String @field.input(Text) @is.required country String @field.select postal String @field.input(Text)}model User! { id String @id @default("nanoid()") email String @field.input(Email) @is.required @is.unique name String @field.input(Text) @is.required role UserRole @default("USER") address Address? active Boolean @default(true) created Date @default("now()") updated Date @default("updated()")}`;constresult=final(schemaCode);console.log(JSON.stringify(result,null,2));
Working with Individual Components
import{Compiler,EnumTree,ModelTree}from'@stackpress/idea-parser';// Parse individual enumconstenumCode=`enum Status { ACTIVE "Active" INACTIVE "Inactive" }`;constenumAST=EnumTree.parse(enumCode);const[enumName,enumConfig]=Compiler.enum(enumAST);// Parse individual modelconstmodelCode=`model User { id String @id name String }`;constmodelAST=ModelTree.parse(modelCode);const[modelName,modelConfig]=Compiler.model(modelAST);
Best Practices
1. Use Type Safety
The library is built with TypeScript and provides comprehensive type definitions:
Always wrap parsing operations in try-catch blocks:
import{parse,Exception}from'@stackpress/idea-parser';try{constresult=parse(schemaCode);// Process result}catch(error){if(errorinstanceofException){console.error('Schema parsing failed:',error.message);// Handle parsing error}else{console.error('Unexpected error:',error);// Handle other errors}}
3. Choose the Right Function
Use parse() when you need to preserve references for further processing
Use final() when you want a clean output for final consumption
4. Validate Schema Structure
Ensure your schema follows the expected structure:
// Good: Proper model structuremodelUser{idString @idnameString}// Bad: Missing required propertiesmodelUser{// Missing columns - will throw error}
5. Use Meaningful Names
Choose descriptive names for your schema elements:
// GoodenumUserStatus{ACTIVE"Active"SUSPENDED"Suspended"}propEmailInput{type"email"format"email"}// Less clearenumStatus{A"Active"S"Suspended"}propInput{type"email"}
Error Handling
Common errors and their solutions:
Invalid Schema Structure
Error: "Invalid Schema"
Solution: Ensure your schema follows the correct syntax and structure.
Missing Required Properties
Error: "Expecting a columns property"
Solution: Models and types must have a columns definition.
Duplicate Declarations
Error: "Duplicate [name]"
Solution: Each declaration name must be unique within the schema.
Unknown References
Error: "Unknown reference [name]"
Solution: Ensure all referenced props and types are defined before use.
Lexer
The Lexer class implements the Parser interface and provides tokenization and parsing utilities for schema code. It manages a dictionary of token definitions and handles the parsing process by matching patterns against the input code.
import{Lexer}from'@stackpress/idea-parser';
Properties
The following properties are available when instantiating a Lexer.
Property
Type
Description
dictionary
Record<string, Definition>
Shallow copy of all token definitions
index
number
Current parsing position in the code
Methods
The following methods are available when instantiating a Lexer.
Cloning the Lexer
The following example shows how to create an exact copy of the lexer's current state.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.load('enum Status { ACTIVE "Active" }');lexer.define('enum',enumReader);// Clone preserves code, index position, and all definitionsconstclonedLexer=lexer.clone();console.log(clonedLexer.index);// Same index as originalconsole.log(clonedLexer.dictionary);// Same definitions as original
Returns
A new Lexer instance with identical state to the original.
Defining Token Patterns
The following example shows how to register token definitions for parsing.
import{Lexer}from'@stackpress/idea-parser';importdefinitionsfrom'@stackpress/idea-parser/definitions';constlexer=newLexer();// Load all predefined token definitionsObject.keys(definitions).forEach((key)=>{lexer.define(key,definitions[key]);});// Define a custom tokenlexer.define('customKeyword',(code,start,lexer)=>{if(code.substring(start,start+6)==='custom'){return{type: 'Keyword',value: 'custom',start: start,end: start+6};}returnundefined;});
Parameters
Parameter
Type
Description
key
string
Unique identifier for the token definition
reader
Reader
Function that attempts to match and parse the token
Returns
Void (modifies the lexer's internal dictionary).
Expecting Specific Tokens
The following example shows how to require specific tokens at the current position.
import{Lexer}from'@stackpress/idea-parser';importdefinitions,{data}from'@stackpress/idea-parser/definitions';constlexer=newLexer();Object.keys(definitions).forEach((key)=>{lexer.define(key,definitions[key]);});// Parse a float literallexer.load('4.4');constfloatToken=lexer.expect('Float');console.log(floatToken.value);// 4.4console.log(floatToken.start);// 0console.log(floatToken.end);// 3// Parse any data type (scalar, object, or array)lexer.load('"hello world"');conststringToken=lexer.expect(data);console.log(stringToken.value);// 'hello world'// Expect one of multiple token typeslexer.load('true');constbooleanToken=lexer.expect(['Boolean','String','Integer']);console.log(booleanToken.value);// true
Parameters
Parameter
Type
Description
keys
string | string[]
Token definition key(s) to expect
Returns
The matched token object, or throws an exception if no match is found.
Getting Token Definitions
The following example shows how to retrieve registered token definitions.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.define('String',definitions['String']);constdefinition=lexer.get('String');if(definition){console.log('Definition key:',definition.key);// 'String'console.log('Reader function:',typeofdefinition.reader);// 'function'}else{console.log('Definition not found');}
Parameters
Parameter
Type
Description
key
string
The token definition key to retrieve
Returns
The Definition object if found, undefined otherwise.
Loading Code
The following example shows how to load code for parsing.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();// Load code from the beginninglexer.load('enum Status { ACTIVE "Active" }');console.log(lexer.index);// 0// Load code starting from a specific positionlexer.load('enum Status { ACTIVE "Active" }',5);console.log(lexer.index);// 5console.log(lexer.substring(5,11));// 'Status'
Parameters
Parameter
Type
Description
code
string
The source code to parse
index
number
Starting position in the code (default: 0)
Returns
The Lexer instance to allow method chaining.
Matching Tokens
The following example shows how to find the first matching token from available definitions.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.define('literal',(code,start)=>{if(code.startsWith('42',start)){return{type: 'Literal',value: 42, start,end: start+2};}returnundefined;});constcode='42';// Match against specific token typesconstmatch=lexer.match(code,0,['literal']);if(match){console.log('Matched:',match.type);// 'Literal'console.log('Value:',match.value);// 42}// Match against all available definitions (pass undefined for keys)constmatch2=lexer.match('42',0,undefined);if(match2){console.log('Matched:',match2.type);// 'Literal'}
Parameters
Parameter
Type
Description
code
string
The code to match against
start
number
Starting position for matching
keys
string[]
Optional array of definition keys to try (default: all definitions)
Returns
The first matching token object, or null if no match is found.
Testing for Next Tokens
The following example shows how to check if the next tokens match without consuming them.
import{Lexer}from'@stackpress/idea-parser';importdefinitionsfrom'@stackpress/idea-parser/definitions';constlexer=newLexer();Object.keys(definitions).forEach((key)=>{lexer.define(key,definitions[key]);});lexer.load('enum Status { ACTIVE "Active" }');// Test if next token matches (doesn't advance index)if(lexer.next('AnyIdentifier')){console.log('Next token is an identifier');console.log(lexer.index);// Still 0}// Test for multiple possible tokensif(lexer.next(['String','Integer','Boolean'])){console.log('Next token is a literal value');}
Parameters
Parameter
Type
Description
names
string | string[]
Token definition key(s) to test for
Returns
true if the next token(s) match, false otherwise. Does not advance the index.
Optional Token Parsing
The following example shows how to optionally parse tokens without throwing errors.
import{Lexer}from'@stackpress/idea-parser';importdefinitionsfrom'@stackpress/idea-parser/definitions';constlexer=newLexer();Object.keys(definitions).forEach((key)=>{lexer.define(key,definitions[key]);});lexer.load('/* some comment */ enum Status');// Try to parse optional whitespace/commentsconstcomment=lexer.optional('note');if(comment){console.log('Found comment:',comment.value);// '/* some comment */'}// Skip optional whitespacelexer.optional('whitespace');// Now parse the enum keywordconstenumToken=lexer.expect('AnyIdentifier');console.log(enumToken.name);// 'enum'
Parameters
Parameter
Type
Description
names
string | string[]
Token definition key(s) to try parsing
Returns
The matched token object if found, undefined otherwise.
Reading Ahead
The following example shows how to read the next available token.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.load('');// Empty string// Read the next token (tries all definitions)constnextToken=lexer.read();console.log(nextToken);// undefined (no tokens in empty string)lexer.load('42');lexer.define('Integer',definitions['Integer']);consttoken=lexer.read();if(token){console.log('Token type:',token.type);// 'Literal'console.log('Token value:',token.value);// 42}
Returns
The next available token object, or undefined if no token can be parsed.
Getting Substrings
The following example shows how to extract portions of the source code.
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.load('some code');// Extract specific portions of codeconstsubstring=lexer.substring(5,9);console.log(substring);// 'code'// Return empty string when start and end are the sameconstempty=lexer.substring(5,5);console.log(empty);// ''
Parameters
Parameter
Type
Description
start
number
Starting position in the code
end
number
Ending position in the code
Returns
The substring of code between start and end positions.
Finding Next Space
The following example shows how to find the next whitespace character (useful for error reporting).
import{Lexer}from'@stackpress/idea-parser';constlexer=newLexer();lexer.load('enum Status { ACTIVE "Active" }');// Find next space from current positionconstspaceIndex=lexer.nextSpace();console.log(spaceIndex);// 4 (position of space after 'enum')// If no space found, returns code lengthlexer.load('enumStatus');constendIndex=lexer.nextSpace();console.log(endIndex);// 10 (length of code)
Returns
The index of the next space character, or the code length if no space is found.
Parsing Complex Data Structures
The Lexer can parse complex nested data structures using the predefined token definitions:
Parsing Objects
import{Lexer}from'@stackpress/idea-parser';import{Compiler}from'@stackpress/idea-parser';importdefinitions,{data}from'@stackpress/idea-parser/definitions';constlexer=newLexer();Object.keys(definitions).forEach((key)=>{lexer.define(key,definitions[key]);});// Parse a simple objectlexer.load('{ foo "bar" bar 4.4 }');constobjectToken=lexer.expect('Object');constcompiled=Compiler.object(objectToken);console.log(compiled);// { foo: 'bar', bar: 4.4 }// Parse nested objectslexer.load('{ foo "bar" zoo { foo false bar null } }');constnestedToken=lexer.expect('Object');constnestedCompiled=Compiler.object(nestedToken);console.log(nestedCompiled.zoo.foo);// falseconsole.log(nestedCompiled.zoo.bar);// null
import{Exception}from'@stackpress/idea-parser';constlexer=newLexer();lexer.define('String',definitions['String']);lexer.load('42');// Number, not stringtry{lexer.expect('String');// Expecting string but found number}catch(error){if(errorinstanceofException){console.log(error.message);// "Unexpected 42 expecting String"console.log(error.start);// Position where error occurredconsole.log(error.end);// End position for error highlighting}}
Predefined Token Definitions
The library comes with comprehensive predefined token definitions:
data: Array of all data types [...scalar, 'Object', 'Array']
Usage with AST
The Lexer is typically used by AST classes for parsing specific language constructs:
import{Lexer,EnumTree}from'@stackpress/idea-parser';// AST classes configure lexers with appropriate definitionsconstlexer=newLexer();EnumTree.definitions(lexer);// Adds enum-specific token definitionslexer.load('enum Status { ACTIVE "Active" INACTIVE "Inactive" }');constenumTree=newEnumTree(lexer);constresult=enumTree.enum();// Parse enum using configured lexer
Advanced Usage Patterns
Backtracking with Clone
// Save current state for potential backtrackingconstcheckpoint=lexer.clone();try{// Try to parse complex structureconstresult=parseComplexStructure(lexer);returnresult;}catch(error){// Restore state and try alternative parsingconstrestoredLexer=checkpoint;returnparseAlternativeStructure(restoredLexer);}
Conditional Parsing
// Parse different types based on lookaheadif(lexer.next('AnyIdentifier')){constidentifier=lexer.expect('AnyIdentifier');if(identifier.name==='enum'){// Parse enum declaration}elseif(identifier.name==='model'){// Parse model declaration}}
Custom Reader Functions
Reader functions should follow this pattern:
importtype{Reader}from'@stackpress/idea-parser';constcustomReader: Reader=(code,start,lexer)=>{// Check if pattern matches at current positionif(!code.startsWith('custom',start)){returnundefined;// No match}// Calculate end positionconstend=start+6;// Return token objectreturn{type: 'CustomToken',value: 'custom',start: start,end: end};};// Register the custom readerlexer.define('custom',customReader);
Compiler
The Compiler class provides static methods for converting Abstract Syntax Tree (AST) tokens into structured JSON configurations. It serves as the bridge between parsed tokens and the final JSON output.
import{Compiler}from'@stackpress/idea-parser';
Static Methods
The following methods can be accessed directly from the Compiler class.
Converting Array Tokens
The following example shows how to compile array tokens into actual arrays.
import{Compiler}from'@stackpress/idea-parser';// Example array token from parsing ["value1", "value2", "value3"]constarrayToken={type: 'ArrayExpression',elements: [{type: 'Literal',value: 'value1'},{type: 'Literal',value: 'value2'},{type: 'Literal',value: 'value3'}]};constresult=Compiler.array(arrayToken);console.log(result);// ['value1', 'value2', 'value3']
Parameters
Parameter
Type
Description
token
ArrayToken
The array token to compile
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
An array containing the compiled elements.
Converting Data Tokens
The following example shows how to compile various data tokens into their actual values.
import{Compiler}from'@stackpress/idea-parser';// Compile different types of data tokensconstliteralResult=Compiler.data({type: 'Literal',value: 'hello'});console.log(literalResult);// 'hello'constobjectResult=Compiler.data({type: 'ObjectExpression',properties: [{key: {name: 'name'},value: {type: 'Literal',value: 'John'}}]});console.log(objectResult);// { name: 'John' }
Parameters
Parameter
Type
Description
token
DataToken
The data token to compile (can be object, array, literal, or identifier)
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
The compiled data value based on the token type.
Converting Enum Declarations
The following example shows how to compile enum declarations into JSON configurations.
import{Compiler}from'@stackpress/idea-parser';// Example enum token from parsing: enum Status { ACTIVE "Active" INACTIVE "Inactive" }constenumToken={kind: 'enum',declarations: [{id: {name: 'Status'},init: {properties: [{key: {name: 'ACTIVE'},value: {type: 'Literal',value: 'Active'}},{key: {name: 'INACTIVE'},value: {type: 'Literal',value: 'Inactive'}}]}}]};const[name,config]=Compiler.enum(enumToken);console.log(name);// 'Status'console.log(config);// { ACTIVE: 'Active', INACTIVE: 'Inactive' }
Parameters
Parameter
Type
Description
token
DeclarationToken
The enum declaration token to compile
Returns
A tuple containing the enum name and its configuration object.
Converting Schema to Final JSON
The following example shows how to compile a schema token into a final JSON configuration.
import{Compiler}from'@stackpress/idea-parser';// This method removes prop and use references for a clean final outputconstfinalSchema=Compiler.final(schemaToken);console.log(finalSchema);// Output will not contain 'prop' or 'use' sections
Parameters
Parameter
Type
Description
token
SchemaToken
The schema token to compile into final form
Returns
A FinalSchemaConfig object with references resolved and removed.
Converting Identifier Tokens
The following example shows how to resolve identifier tokens to their actual values.
import{Compiler}from'@stackpress/idea-parser';// With references providedconstreferences={MyProp: {type: 'text'}};constresult1=Compiler.identifier({name: 'MyProp'},references);console.log(result1);// { type: 'text' }// Without references (returns template string)constresult2=Compiler.identifier({name: 'MyProp'},false);console.log(result2);// '${MyProp}'// With empty references (throws error)try{Compiler.identifier({name: 'UnknownProp'},{});}catch(error){console.log(error.message);// 'Unknown reference UnknownProp'}
Parameters
Parameter
Type
Description
token
IdentifierToken
The identifier token to resolve
references
UseReferences
Reference map for resolving the identifier
Returns
The resolved value from references, a template string, or throws an error.
Converting Literal Tokens
The following example shows how to extract values from literal tokens.
The literal value (string, number, boolean, etc.).
Converting Model Declarations
The following example shows how to compile model declarations into JSON configurations.
import{Compiler}from'@stackpress/idea-parser';// Example model token from parsing: model User { id String @id name String }constmodelToken={kind: 'model',mutable: false,// model User! would be truedeclarations: [{id: {name: 'User'},init: {properties: [{key: {name: 'columns'},value: {type: 'ObjectExpression',properties: [{key: {name: 'id'},value: {type: 'ObjectExpression',properties: [{key: {name: 'type'},value: {type: 'Literal',value: 'String'}},{key: {name: 'attributes'},value: {/* attributes */}}]}}]}}]}}]};const[name,config]=Compiler.model(modelToken);console.log(name);// 'User'console.log(config.mutable);// falseconsole.log(config.columns);// Array of column configurations
Parameters
Parameter
Type
Description
token
DeclarationToken
The model declaration token to compile
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
A tuple containing the model name and its configuration object.
Converting Object Tokens
The following example shows how to compile object tokens into actual objects.
import{Compiler}from'@stackpress/idea-parser';// Example object token from parsing { name "John" age 30 }constobjectToken={type: 'ObjectExpression',properties: [{key: {name: 'name'},value: {type: 'Literal',value: 'John'}},{key: {name: 'age'},value: {type: 'Literal',value: 30}}]};constresult=Compiler.object(objectToken);console.log(result);// { name: 'John', age: 30 }
Parameters
Parameter
Type
Description
token
ObjectToken
The object token to compile
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
An object with compiled key-value pairs.
Converting Plugin Declarations
The following example shows how to compile plugin declarations into JSON configurations.
A tuple containing the plugin name and its configuration object.
Converting Prop Declarations
The following example shows how to compile prop declarations into JSON configurations.
import{Compiler}from'@stackpress/idea-parser';// Example prop token from parsing: prop Text { type "text" format "lowercase" }constpropToken={kind: 'prop',declarations: [{id: {name: 'Text'},init: {properties: [{key: {name: 'type'},value: {type: 'Literal',value: 'text'}},{key: {name: 'format'},value: {type: 'Literal',value: 'lowercase'}}]}}]};const[name,config]=Compiler.prop(propToken);console.log(name);// 'Text'console.log(config);// { type: 'text', format: 'lowercase' }
Parameters
Parameter
Type
Description
token
DeclarationToken
The prop declaration token to compile
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
A tuple containing the prop name and its configuration object.
Converting Schema Declarations
The following example shows how to compile complete schema tokens into JSON configurations.
import{Compiler}from'@stackpress/idea-parser';// Compile a complete schema with all declaration typesconstschemaConfig=Compiler.schema(schemaToken);console.log(schemaConfig);// Output contains: { enum: {...}, prop: {...}, type: {...}, model: {...}, plugin: {...}, use: [...] }// Compile with finalization (resolves references)constfinalizedConfig=Compiler.schema(schemaToken,true);console.log(finalizedConfig);// References are resolved in the output
Parameters
Parameter
Type
Description
token
SchemaToken
The schema token to compile
finalize
boolean
Whether to resolve references (default: false)
Returns
A SchemaConfig object containing all compiled declarations.
Converting Type Declarations
The following example shows how to compile type declarations into JSON configurations.
import{Compiler}from'@stackpress/idea-parser';// Example type token from parsing: type Address { street String city String }consttypeToken={kind: 'type',mutable: true,// type Address (mutable) vs type Address! (immutable)declarations: [{id: {name: 'Address'},init: {properties: [{key: {name: 'columns'},value: {type: 'ObjectExpression',properties: [{key: {name: 'street'},value: {type: 'ObjectExpression',properties: [{key: {name: 'type'},value: {type: 'Literal',value: 'String'}}]}}]}}]}}]};const[name,config]=Compiler.type(typeToken);console.log(name);// 'Address'console.log(config.mutable);// trueconsole.log(config.columns);// Array of column configurations
Parameters
Parameter
Type
Description
token
DeclarationToken
The type declaration token to compile
references
UseReferences
Reference map for resolving identifiers (default: false)
Returns
A tuple containing the type name and its configuration object.
Converting Use Declarations
The following example shows how to compile use (import) declarations.
import{Compiler}from'@stackpress/idea-parser';// Example use token from parsing: use "./another.idea"constuseToken={type: 'ImportDeclaration',source: {type: 'Literal',value: './another.idea'}};constimportPath=Compiler.use(useToken);console.log(importPath);// './another.idea'
Parameters
Parameter
Type
Description
token
ImportToken
The import declaration token to compile
Returns
The import path as a string.
Error Handling
The Compiler class throws Exception errors for various invalid conditions:
The Compiler is typically used in conjunction with AST classes:
import{Compiler,EnumTree,ModelTree,SchemaTree}from'@stackpress/idea-parser';// Parse and compile individual componentsconstenumAST=EnumTree.parse('enum Status { ACTIVE "Active" }');const[enumName,enumConfig]=Compiler.enum(enumAST);constmodelAST=ModelTree.parse('model User { id String @id }');const[modelName,modelConfig]=Compiler.model(modelAST);// Parse and compile complete schemaconstschemaAST=SchemaTree.parse(schemaCode);constschemaConfig=Compiler.schema(schemaAST);
Syntax Trees
The AST classes are responsible for parsing specific parts of schema code into Abstract Syntax Trees (ASTs). Each AST class handles a different type of declaration or construct in the schema language.
The following example shows how to configure a lexer for schema parsing.
import{SchemaTree,Lexer}from'@stackpress/idea-parser';constlexer=newLexer();SchemaTree.definitions(lexer);// Lexer now has definitions for all schema constructs:// enum, prop, type, model, plugin, use keywords and structures
Parameters
Parameter
Type
Description
lexer
Lexer
The lexer instance to configure
Returns
The configured lexer instance.
Parsing Complete Schemas
The following example shows how to parse a complete schema file.
import{SchemaTree}from'@stackpress/idea-parser';constschemaCode=`plugin "./database" { provider "postgresql"}enum Status { ACTIVE "Active" INACTIVE "Inactive"}prop Text { type "text" }model User { id String @id name String @field.input(Text) status Status}`;constast=SchemaTree.parse(schemaCode);console.log(ast.type);// 'Program'console.log(ast.kind);// 'schema'console.log(ast.body.length);// 4 (plugin, enum, prop, model)
Parameters
Parameter
Type
Description
code
string
The complete schema code to parse
Returns
A SchemaToken representing the entire parsed schema.
Methods
Parsing Schema Content
The following example shows how to parse schema content with custom starting position.
import{SchemaTree}from'@stackpress/idea-parser';consttree=newSchemaTree();constschemaCode='enum Status { ACTIVE "Active" }';constresult=tree.parse(schemaCode,0);console.log(result.body[0].kind);// 'enum'
Parameters
Parameter
Type
Description
code
string
The schema code to parse
start
number
Starting position in the code (default: 0)
Returns
A SchemaToken containing all parsed declarations.
EnumTree
Parses enum declarations into AST tokens.
Static Methods
Setting Up Enum Definitions
The following example shows how to configure a lexer for enum parsing.
import{EnumTree,Lexer}from'@stackpress/idea-parser';constlexer=newLexer();EnumTree.definitions(lexer);// Adds 'EnumWord' token definition for 'enum' keyword
Parsing Enum Declarations
The following example shows how to parse enum declarations based on the test fixtures.
A PropertyToken representing a single enum key-value pair.
ModelTree
Parses model declarations (extends TypeTree for shared functionality).
Static Methods
Parsing Model Declarations
The following example shows how to parse model declarations based on the test fixtures.
import{ModelTree}from'@stackpress/idea-parser';constmodelCode=`model User @label("User" "Users") { id String @label("ID") @id @default("nanoid(20)") username String @label("Username") @searchable @field.input(Text) @is.required password String @label("Password") @field.password @is.clt(80) @is.cgt(8) @is.required @list.hide @view.hide role Roles @label("Role") @filterable @field.select @list.text(Uppercase) @view.text(Uppercase) address Address? @label("Address") @list.hide age Number @label("Age") @unsigned @filterable @sortable @field.number(Age) @is.gt(0) @is.lt(150) active Boolean @label("Active") @default(true) @filterable @field.switch @list.yesno @view.yesno created Date @label("Created") @default("now()") @filterable @sortable @list.date(Pretty)}`;constast=ModelTree.parse(modelCode);console.log(ast.kind);// 'model'console.log(ast.mutable);// false (because of '!' modifier)console.log(ast.declarations[0].id.name);// 'User'
Parameters
Parameter
Type
Description
code
string
The model declaration code to parse
start
number
Starting position in the code (default: 0)
Returns
A DeclarationToken representing the parsed model.
Methods
Parsing Model Structure
The following example shows how to parse the model structure.
consttree=newModelTree();tree._lexer.load('model User { id String @id }');constmodelToken=tree.model();console.log(modelToken.kind);// 'model'console.log(modelToken.mutable);// false (immutable due to '!')
Returns
A DeclarationToken representing the model structure.
TypeTree
Parses type declarations.
Static Methods
Parsing Type Declarations
The following example shows how to parse type declarations.
import{TypeTree}from'@stackpress/idea-parser';consttypeCode=`type Address @label("Address" "Addresses") { street String @field.input @is.required city String @field.input @is.required country String @field.select postal String @field.input}`;constast=TypeTree.parse(typeCode);console.log(ast.kind);// 'type'console.log(ast.mutable);// true (mutable by default)console.log(ast.declarations[0].id.name);// 'Address'
Parameters
Parameter
Type
Description
code
string
The type declaration code to parse
start
number
Starting position in the code (default: 0)
Returns
A DeclarationToken representing the parsed type.
Methods
Parsing Type Structure
The following example shows how to parse the type structure.
consttree=newTypeTree();tree._lexer.load('type Address { street String city String }');consttypeToken=tree.type();console.log(typeToken.kind);// 'type'console.log(typeToken.mutable);// true (default for types)
Returns
A DeclarationToken representing the type structure.
Parsing Type Properties
The following example shows how type properties (columns) are parsed.
// Inside type parsingconstproperty=tree.property();console.log(property.key.name);// e.g., 'street'console.log(property.value);// Object containing type and attributes
Returns
A PropertyToken representing a type column definition.
Parsing Type Parameters
The following example shows how type parameters are parsed.
// For parsing generic type parametersconstparameter=tree.parameter();console.log(parameter.key.name);// Parameter nameconsole.log(parameter.value);// Parameter type/constraint
The following example shows how to parse prop declarations.
import{PropTree}from'@stackpress/idea-parser';constpropCode=`prop EmailInput { type "email" format "email" placeholder "Enter your email" required true}`;constast=PropTree.parse(propCode);console.log(ast.kind);// 'prop'console.log(ast.declarations[0].id.name);// 'EmailInput'
Parameters
Parameter
Type
Description
code
string
The prop declaration code to parse
start
number
Starting position in the code (default: 0)
Returns
A DeclarationToken representing the parsed prop.
Methods
Parsing Prop Structure
The following example shows how to parse the prop structure.
consttree=newPropTree();tree._lexer.load('prop Text { type "text" format "lowercase" }');constpropToken=tree.prop();console.log(propToken.kind);// 'prop'console.log(propToken.declarations[0].id.name);// 'Text'
Returns
A DeclarationToken representing the prop configuration.
PluginTree
Parses plugin declarations.
Static Methods
Parsing Plugin Declarations
The following example shows how to parse plugin declarations.
import{EnumTree,ModelTree,TypeTree}from'@stackpress/idea-parser';// Parse individual enumconstenumAST=EnumTree.parse(`enum Roles { ADMIN "Admin" MANAGER "Manager" USER "User"}`);// Parse individual modelconstmodelAST=ModelTree.parse(`model User { id String @id username String @is.required}`);// Parse individual typeconsttypeAST=TypeTree.parse(`type Address { street String city String}`);
Using with Compiler
import{EnumTree,Compiler}from'@stackpress/idea-parser';// Parse and compile in one stepconstenumAST=EnumTree.parse(`enum Status { ACTIVE "Active" INACTIVE "Inactive"}`);const[enumName,enumConfig]=Compiler.enum(enumAST);console.log(enumName);// 'Status'console.log(enumConfig);// { ACTIVE: 'Active', INACTIVE: 'Inactive' }
Custom AST Classes
You can extend AbstractTree to create custom parsers:
AST classes provide detailed error information when parsing fails:
Syntax Errors
import{SchemaTree,Exception}from'@stackpress/idea-parser';try{// Invalid syntax - missing closing braceSchemaTree.parse('enum Status { ACTIVE "Active"');}catch(error){if(errorinstanceofException){console.log('Parse error:',error.message);console.log('Position:',error.start,'-',error.end);}}
Unexpected Tokens
try{// Invalid - 'enum' keyword expected but found 'model'EnumTree.parse('model User { id String }');}catch(error){console.log('Expected enum but found model');}
Empty Input Handling
import{EnumTree}from'@stackpress/idea-parser';try{// Empty string will throw an errorEnumTree.parse('');}catch(error){console.log('Error:',error.message);// 'Unexpected end of input'}
Invalid Identifiers
import{ModelTree}from'@stackpress/idea-parser';try{// Invalid - model names must be capitalizedModelTree.parse('model user { id String }');}catch(error){console.log('Expected CapitalIdentifier but got something else');}
Integration with Main Functions
AST classes are used internally by the main parse and final functions:
// This is what happens internally:import{SchemaTree,Compiler}from'@stackpress/idea-parser';exportfunctionparse(code: string){constast=SchemaTree.parse(code);// Parse to ASTreturnCompiler.schema(ast);// Compile to JSON}exportfunctionfinal(code: string){constast=SchemaTree.parse(code);// Parse to ASTreturnCompiler.final(ast);// Compile and clean up}
Performance Considerations
Lexer Reuse
AST classes can share lexer instances for better performance:
import{Lexer,SchemaTree}from'@stackpress/idea-parser';// Create and configure lexer onceconstlexer=newLexer();SchemaTree.definitions(lexer);// Reuse for multiple parsesconsttree=newSchemaTree(lexer);constresult1=tree.parse(code1);constresult2=tree.parse(code2);constresult3=tree.parse(code3);
Cloning for Backtracking
AST classes use lexer cloning for safe parsing attempts:
// Inside tree parsing methodsconstcheckpoint=this._lexer.clone();try{// Try to parse optional structurereturnthis.parseOptionalStructure();}catch(error){// Restore lexer state and continuethis._lexer=checkpoint;returnthis.parseAlternativeStructure();}
Test-Driven Examples
Based on the test fixtures, here are real-world examples:
Enum with Multiple Values
constenumCode=`enum Roles { ADMIN "Admin" MANAGER "Manager" USER "User"}`;constast=EnumTree.parse(enumCode);// Produces a complete AST with all three enum values
Complex Model with Attributes
constmodelCode=`model User @label("User" "Users") { id String @label("ID") @id @default("nanoid(20)") username String @label("Username") @searchable @field.input(Text) @is.required password String @label("Password") @field.password @is.clt(80) @is.cgt(8) @is.required @list.hide @view.hide role Roles @label("Role") @filterable @field.select @list.text(Uppercase) @view.text(Uppercase) address Address? @label("Address") @list.hide age Number @label("Age") @unsigned @filterable @sortable @field.number(Age) @is.gt(0) @is.lt(150) balance Number[] @label("Balance") @filterable @sortable @field.number() @list.number() @view.number active Boolean @label("Active") @default(true) @filterable @field.switch @list.yesno @view.yesno created Date @label("Created") @default("now()") @filterable @sortable @list.date(Pretty) updated Date @label("Updated") @default("updated()") @filterable @sortable @list.date(Pretty) company Company? @label("My Company") }`;constast=ModelTree.parse(modelCode);// Produces a complete model AST with all columns and attributes
Token types define the Abstract Syntax Tree (AST) structures used by the idea parser to represent parsed schema code. These types form the foundation of the parsing system, providing type-safe representations of schema elements.
Used by all tree parsers (EnumTree, PropTree, TypeTree, ModelTree, PluginTree) to represent their respective declarations. The kind property determines how the Compiler processes the declaration.
Examples from Tests
The enum fixture shows a complete DeclarationToken with kind 'enum' containing the Roles enum definition.
DeclaratorToken
Represents the declarator part of a variable declaration, containing the identifier and initialization.
Initialization object containing the declaration body
Usage
Used within DeclarationTokens to separate the declaration name from its body. The id contains the name (e.g., "Roles", "User") and init contains the definition object.
ImportToken
Represents use statements for importing other schema files.
Used by SchemaTree as the root token representing the entire parsed schema file. The Compiler processes the body array to generate the final schema configuration.
Union Types
The following types provide flexible token handling for different contexts.
Token
Union type for all possible token types that can be returned by readers.
typeToken=DataToken|UnknownToken;
Usage
Used as the return type for lexer operations and reader functions. Allows handling both recognized data tokens and unknown tokens.
Tokens include position information for error reporting and validation:
// Position information for error highlightingconsttoken: IdentifierToken={type: 'Identifier',name: 'InvalidName',start: 10,end: 21};// Can be used to highlight errors in editorsconsterrorRange={start: token.start,end: token.end};
Integration with AST
AST classes generate specific token types:
EnumTree: Generates DeclarationToken with kind: 'enum'
PropTree: Generates DeclarationToken with kind: 'prop'
TypeTree: Generates DeclarationToken with kind: 'type'
ModelTree: Generates DeclarationToken with kind: 'model'
PluginTree: Generates DeclarationToken with kind: 'plugin'
UseTree: Generates ImportToken
SchemaTree: Generates SchemaToken containing all other tokens
Each AST class uses the Lexer to generate appropriate tokens, which are then processed by the Compiler to produce the final JSON configuration.
Exception
The Exception class extends the Exception class from @stackpress/lib to provide enhanced error handling specific to the idea parser library. It includes position information and better error reporting for parsing failures.
import{Exception}from'@stackpress/idea-parser';
Overview
Exception is a specialized error class that extends the base Exception class with additional functionality for parser-specific error handling. It automatically includes position information when parsing fails, making it easier to identify and fix syntax errors in schema files.
Exception includes position information to help locate errors in the source code:
import{EnumTree,Exception}from'@stackpress/idea-parser';try{// Missing closing braceEnumTree.parse('enum Status { ACTIVE "Active"');}catch(error){if(errorinstanceofException){console.log('Error message:',error.message);console.log('Error starts at character:',error.start);console.log('Error ends at character:',error.end);// Can be used for syntax highlighting in editorsconsterrorRange={start: error.start,end: error.end};}}
Common Error Scenarios
Syntax Errors
try{parse('enum Status { ACTIVE "Active"');// Missing closing brace}catch(error){console.log(error.message);// "Unexpected end of input expecting }"}
Invalid Token Types
try{parse('model user { id String }');// Invalid - should be capitalized}catch(error){console.log(error.message);// "Expected CapitalIdentifier but got something else"}
Unknown References
try{parse('model User { name String @field.input(UnknownProp) }');}catch(error){console.log(error.message);// "Unknown reference UnknownProp"}
Duplicate Declarations
try{parse(` enum Status { ACTIVE "Active" } enum Status { INACTIVE "Inactive" } `);}catch(error){console.log(error.message);// "Duplicate Status"}
Integration with AST
All AST classes throw Exception when parsing fails:
import{SchemaTree,EnumTree,ModelTree,Exception}from'@stackpress/idea-parser';// Any parsing operation can throw Exceptiontry{constschema=SchemaTree.parse(schemaCode);constenumAST=EnumTree.parse(enumCode);constmodelAST=ModelTree.parse(modelCode);}catch(error){if(errorinstanceofException){// Handle parser-specific errorsconsole.error('Parsing failed:',error.message);}else{// Handle other types of errorsconsole.error('Unexpected error:',error);}}
Error Recovery
While Exception indicates parsing failure, you can implement error recovery strategies:
import{parse,Exception}from'@stackpress/idea-parser';functionparseWithFallback(code: string,fallbackCode?: string){try{returnparse(code);}catch(error){if(errorinstanceofException&&fallbackCode){console.warn('Primary parsing failed, trying fallback:',error.message);returnparse(fallbackCode);}throwerror;// Re-throw if no fallback or different error type}}
Language Server Integration
Exception's position information makes it ideal for language server implementations:
This repository is a yarn workspace made of 3 packages, in the packages folder, and already published to NPM.
idea-parser
idea-transformer
idea
In general, with the exception of the root package.json, docs and .github, you should not edit files outside the packages folder without explicit permission.
The tasks asked by the user will always be regarding a specific package. You should not be editing files in multiple packages in the same task without explicit permission.
The following folders and files are found in the root of the project.
.context - This is documentation to be submitted to Context7 MCP. It's basically the same as the docs folder, but fixes the language in the code snippets.
docs - The official documentation of the project.
example - A working project example using idea to generate code
language - The source code for the language server already published to the VSCode marketplace. (currently using the typescript version of idea-parser)
packages - Published NPM packages; The source code for the relative work involved.
Please ignore the following folders:
.nyc_output
archives
coverage
In this project we use yarn to execute scripts. Please do not use npm.
We use package.json to call scripts from packages like so.
yarn parser [command] - shortbut for yarn --cwd packages/idea-parser
yarn transformer [command] - shortbut for yarn --cwd packages/idea-transformer
yarn idea [command] - shortbut for yarn --cwd packages/idea
yarn build - calls all the build commands in packages/idea-parser, packages/idea-transformer, packages/idea and example
yarn test - runs all the test commands in packages/idea-parser and packages/idea-transformer
yarn build - runs all the build commands in packages/idea-parser, packages/idea-transformer, packages/idea and example
When creating documentation please use the root docs folder. Do not add documentation in the packages folder.
In packages/idea-parser, there is a folder called old. This is the original working parser written in typescript. It exists only temporarily as a reference in case context for the rust version of the compiler is needed. Please do not update any files in packages/idea-parser/old.
Comprehensive API documentation for the @stackpress/idea-transformer library - a powerful schema transformation tool that processes .idea schema files and executes plugins to generate code and other outputs.
Overview
The idea-transformer library provides a complete solution for:
Schema Processing: Load and parse .idea schema files with support for imports and merging
Plugin System: Execute transformation plugins that generate code, documentation, or other outputs
CLI Integration: Command-line interface for processing schemas in build pipelines
Type Safety: Full TypeScript support with comprehensive type definitions
Quick Start
npm install @stackpress/idea-transformer
importTransformerfrom'@stackpress/idea-transformer';// Load and process a schemaconsttransformer=awaitTransformer.load('./schema.idea');constschema=awaittransformer.schema();awaittransformer.transform();
API Reference
Core Components
Component
Description
Transformer
Main class for loading and transforming schema files
Terminal
Command-line interface for schema processing
Plugin Types
Type definitions for creating plugins
Key Features
Schema Loading and Processing
Support for both .idea and .json schema files
Automatic dependency resolution with use directives
Intelligent schema merging based on mutability rules
Comprehensive error handling and validation
Plugin System
Type-safe plugin development with PluginProps and PluginWithCLIProps
Access to complete schema configuration and transformation context
CLI integration for interactive plugins
Flexible plugin configuration system
Command-Line Interface
Simple CLI for processing schemas in build pipelines
Configurable working directories and file extensions
The Transformer class is the core component of the idea-transformer library. It handles:
Loading schema files (both .idea and .json formats)
Processing and merging schema configurations from multiple files
Executing plugins defined in the schema
Managing file dependencies and imports
Static Methods
The following methods can be accessed directly from the Transformer class.
Loading a Transformer
The following example shows how to create a new Transformer instance.
importTransformerfrom'@stackpress/idea-transformer';// Load with default optionsconsttransformer=awaitTransformer.load('./schema.idea');// Load with custom optionsconsttransformer=awaitTransformer.load('./schema.idea',{cwd: '/custom/working/directory',fs: customFileSystem});
Parameters
Parameter
Type
Description
input
string
Path to the schema file to load
options
FileLoaderOptions
Optional configuration for file loading
Returns
A promise that resolves to a new Transformer instance configured with the specified input file and options.
Properties
The following properties are available when instantiating a Transformer.
Property
Type
Description
loader
FileLoader
File system loader for handling file operations
input
string
Absolute path to the input schema file
Methods
The following methods are available when instantiating a Transformer.
Loading Schema Configuration
The following example shows how to load and process the schema configuration.
consttransformer=awaitTransformer.load('./schema.idea');constschema=awaittransformer.schema();console.log(schema.model);// Access model definitionsconsole.log(schema.enum);// Access enum definitionsconsole.log(schema.type);// Access type definitionsconsole.log(schema.prop);// Access prop definitionsconsole.log(schema.plugin);// Access plugin configurations
Returns
A promise that resolves to a SchemaConfig object containing all processed schema definitions.
Features
File Format Support: Automatically detects and handles both .idea and .json schema files
Dependency Resolution: Processes use directives to import and merge external schema files
Schema Merging: Intelligently merges child schemas into parent schemas based on mutability rules
Caching: Caches the processed schema to avoid redundant processing
Schema Merging Rules
When processing use directives, the transformer applies these merging rules:
Props and Enums: Simple merge where parent takes precedence
Types and Models:
If parent doesn't exist or is immutable: child is added
If parent is mutable: attributes and columns are merged
Child columns are prepended to parent columns
Parent attributes take precedence over child attributes
Transforming with Plugins
The following example shows how to execute all plugins defined in the schema.
consttransformer=awaitTransformer.load('./schema.idea');// Transform with no additional contextawaittransformer.transform();// Transform with additional contextawaittransformer.transform({outputDir: './generated',debug: true});
Parameters
Parameter
Type
Description
extras
T
Optional additional context to pass to plugins
Returns
A promise that resolves when all plugins have been executed.
Plugin Execution Process
Validation: Ensures plugins are defined in the schema
Module Resolution: Resolves plugin file paths relative to the schema file
Dynamic Import: Loads plugin modules dynamically
Context Injection: Passes context including transformer, schema, config, and extras
Execution: Calls each plugin function with the injected context
Plugin Context
Each plugin receives a context object with the following properties:
{transformer: Transformer,// The transformer instanceconfig: PluginConfig,// Plugin-specific configurationschema: SchemaConfig,// Complete processed schemacwd: string,// Current working directory
...extras// Any additional context passed to transform()}
Usage Examples
Basic Schema Loading
importTransformerfrom'@stackpress/idea-transformer';consttransformer=awaitTransformer.load('./schema.idea');constschema=awaittransformer.schema();// Access different parts of the schemaconsole.log('Models:',Object.keys(schema.model||{}));console.log('Enums:',Object.keys(schema.enum||{}));console.log('Types:',Object.keys(schema.type||{}));
Working with Multiple Schema Files
// main.idea/*use "./shared/types.idea"use "./shared/enums.idea"model User { id String @id profile Profile // From shared/types.idea role UserRole // From shared/enums.idea}*/consttransformer=awaitTransformer.load('./main.idea');constschema=awaittransformer.schema();// The schema now includes definitions from all imported filesconsole.log(schema.type?.Profile);// Available from shared/types.ideaconsole.log(schema.enum?.UserRole);// Available from shared/enums.idea
Plugin Development and Execution
// schema.idea/*plugin "./plugins/generate-types.js" { output "./generated/types.ts" format "typescript"}*/// plugins/generate-types.jsexportdefaultfunctiongenerateTypes({ transformer, config, schema, cwd }){constoutputPath=config.output;constformat=config.format;// Generate TypeScript types based on schemaletcontent='';if(schema.model){for(const[name,model]ofObject.entries(schema.model)){content+=`export interface ${name} {\n`;for(constcolumnofmodel.columns){constoptional=column.required ? '' : '?';content+=` ${column.name}${optional}: ${column.type};\n`;}content+='}\n\n';}}// Write generated content to fileawaitwriteFile(path.resolve(cwd,outputPath),content);}// Execute the transformationconsttransformer=awaitTransformer.load('./schema.idea');awaittransformer.transform({timestamp: newDate().toISOString()});
import{NodeFS}from'@stackpress/lib';// Using custom file systemconstcustomFS=newNodeFS();consttransformer=awaitTransformer.load('./schema.idea',{fs: customFS,cwd: '/custom/working/directory'});
Error Scenarios
File Not Found
// Throws: "Input file /path/to/nonexistent.idea does not exist"consttransformer=awaitTransformer.load('./nonexistent.idea');awaittransformer.schema();// Error thrown here
No Plugins Defined
// If schema has no plugins definedconsttransformer=awaitTransformer.load('./schema-without-plugins.idea');awaittransformer.transform();// Throws: "No plugins defined in schema file"
Invalid Plugin Module
// If plugin file doesn't export a functionconsttransformer=awaitTransformer.load('./schema.idea');awaittransformer.transform();// Plugin is silently skipped if not a function
Best Practices
Schema Organization
// Organize schemas hierarchically// shared/base.idea - Common types and enums// modules/user.idea - User-specific models// main.idea - Main schema that imports othersuse"./shared/base.idea"use"./modules/user.idea"// Additional models specific to this schemamodelApplication{idString @idusersUser[]}
Plugin Development
// Always validate plugin configurationexportdefaultasyncfunctionmyPlugin({ config, schema, transformer, cwd }){// Validate required configurationif(!config.output){thrownewError('Plugin requires output configuration');}// Use transformer's file loader for consistent path resolutionconstoutputPath=awaittransformer.loader.absolute(config.output);// Process schema safelyconstmodels=schema.model||{};constenums=schema.enum||{};// Generate output...}
Error Recovery
// Implement graceful error handlingasyncfunctionprocessSchema(schemaPath){try{consttransformer=awaitTransformer.load(schemaPath);constschema=awaittransformer.schema();awaittransformer.transform();return{success: true, schema };}catch(error){console.error(`Failed to process ${schemaPath}:`,error.message);return{success: false,error: error.message};}}
Integration with Other Tools
Build Systems
// Integration with build toolsimportTransformerfrom'@stackpress/idea-transformer';exportasyncfunctionbuildSchemas(inputDir,outputDir){constschemaFiles=awaitglob(`${inputDir}/**/*.idea`);for(constschemaFileofschemaFiles){consttransformer=awaitTransformer.load(schemaFile);awaittransformer.transform({ outputDir });}}
Testing
// Testing schema transformationsimport{expect}from'chai';describe('Schema Transformation',()=>{it('should process schema correctly',async()=>{consttransformer=awaitTransformer.load('./test-schema.idea');constschema=awaittransformer.schema();expect(schema.model).to.have.property('User');expect(schema.model.User.columns).to.have.length.greaterThan(0);});});
Terminal
A command-line interface for processing schema files and executing transformations through terminal commands.
The Terminal class (exported as IdeaTerminal) extends the base Terminal class from @stackpress/lib to provide command-line functionality for the idea-transformer library. It handles:
Command-line argument parsing
Schema file transformation via CLI commands
Integration with the Transformer class for processing
Configurable working directories and file extensions
Static Methods
The following methods can be accessed directly from the Terminal class.
Loading a Terminal Instance
The following example shows how to create a new Terminal instance from command-line arguments.
importTerminalfrom'@stackpress/idea-transformer/Terminal';// Load with command-line argumentsconstargs=['transform','--input','./schema.idea'];constterminal=awaitTerminal.load(args);// Load with custom optionsconstterminal=awaitTerminal.load(args,{cwd: '/custom/working/directory',extname: '.schema',brand: '[MY-TOOL]'});
Parameters
Parameter
Type
Description
args
string[]
Command-line arguments array
options
TerminalOptions
Optional configuration for terminal behavior
Returns
A promise that resolves to a new Terminal instance configured with the specified arguments and options.
Properties
The following properties are available when instantiating a Terminal.
Property
Type
Description
cwd
string
Current working directory for file operations
extname
string
Default file extension for schema files (default: '.idea')
Methods
The following methods are available when instantiating a Terminal.
Running Terminal Commands
The Terminal automatically sets up event handlers for processing commands. The main command supported is transform.
importTerminalfrom'@stackpress/idea-transformer/Terminal';// Process a schema fileconstargs=['transform','--input','./schema.idea'];constterminal=awaitTerminal.load(args);awaitterminal.run();
Using Short Flag Syntax
// Using the short flag aliasconstargs=['transform','--i','./schema.idea'];constterminal=awaitTerminal.load(args);awaitterminal.run();
Custom Working Directory
// Set custom working directoryconstterminal=awaitTerminal.load(['transform','--i','./schema.idea'],{cwd: '/path/to/project'});awaitterminal.run();
Custom File Extension
// Use custom file extensionconstterminal=awaitTerminal.load(['transform','--i','./schema.custom'],{extname: '.custom'});awaitterminal.run();
Custom Brand/Label
// Use custom terminal brandconstterminal=awaitTerminal.load(['transform','--i','./schema.idea'],{brand: '[MY-SCHEMA-TOOL]'});awaitterminal.run();
Command-Line Integration
Direct Command-Line Usage
## Basic usage
node cli.js transform --input ./schema.idea
## Using short flag
node cli.js transform --i ./schema.idea
## With custom working directorycd /path/to/project && node cli.js transform --i ./schema.idea
The terminal processes the following flags in order of preference:
--input (full flag name)
--i (short alias)
Default file path if no flags provided
// These are equivalent:['transform','--input','./schema.idea']['transform','--i','./schema.idea']// Uses default path: ./schema.idea['transform']
Error Handling
Missing Schema File
try{constterminal=awaitTerminal.load(['transform','--i','./nonexistent.idea']);awaitterminal.run();}catch(error){console.error('File not found:',error.message);}
// If plugins fail during transformationtry{constterminal=awaitTerminal.load(['transform','--i','./schema.idea']);awaitterminal.run();}catch(error){console.error('Transformation failed:',error.message);}
import{glob}from'glob';asyncfunctionprocessAllSchemas(pattern: string){constschemaFiles=awaitglob(pattern);for(constschemaFileofschemaFiles){console.log(`Processing ${schemaFile}...`);constterminal=awaitTerminal.load(['transform','--i',schemaFile]);awaitterminal.run();console.log(`Completed ${schemaFile}`);}}// Process all .idea files in a directoryawaitprocessAllSchemas('./schemas/**/*.idea');
import{expect}from'chai';importTerminalfrom'@stackpress/idea-transformer/Terminal';describe('Terminal Tests',()=>{it('should process schema file',async()=>{constterminal=awaitTerminal.load(['transform','--i','./test-schema.idea'],{cwd: './test-fixtures'});expect(terminal.cwd).to.include('test-fixtures');// Run the terminal commandawaitterminal.run();// Verify output files were created// ... assertions here});it('should use default options',async()=>{constterminal=awaitTerminal.load(['transform']);expect(terminal.extname).to.equal('.idea');expect(terminal.cwd).to.be.a('string');});});
Integration Testing
importfsfrom'fs';importpathfrom'path';describe('Terminal Integration',()=>{it('should generate expected output files',async()=>{constoutputDir='./test-output';constschemaFile='./test-schema.idea';// Clean output directoryif(fs.existsSync(outputDir)){fs.rmSync(outputDir,{recursive: true});}// Run transformationconstterminal=awaitTerminal.load(['transform','--i',schemaFile]);awaitterminal.run();// Verify expected files were createdconstexpectedFiles=['types.ts','enums.ts','models.ts'];for(constfileofexpectedFiles){constfilePath=path.join(outputDir,file);expect(fs.existsSync(filePath)).to.be.true;}});});
Best Practices
Error Handling
// Always wrap terminal execution in try-catchasyncfunctionsafeTransform(schemaFile: string){try{constterminal=awaitTerminal.load(['transform','--i',schemaFile]);awaitterminal.run();console.log(`✅ Successfully processed ${schemaFile}`);}catch(error){console.error(`❌ Failed to process ${schemaFile}:`,error.message);throwerror;}}
Configuration Management
// Use configuration objects for reusable settingsconstdefaultConfig={cwd: process.cwd(),extname: '.idea',brand: '[SCHEMA-TOOL]'};asyncfunctioncreateTerminal(args: string[],config=defaultConfig){returnawaitTerminal.load(args,config);}
The plugin type system in idea-transformer provides type-safe interfaces for developing plugins that can process schema configurations and generate output files. These types ensure that plugins receive the correct context and maintain consistency across the plugin ecosystem.
Core Plugin Types
PluginProps
A generic type that defines the base structure for all plugin functions, providing access to essential transformation context.
importtype{PluginWithCLIProps}from'@stackpress/idea-transformer/types';importfsfrom'fs/promises';exportdefaultasyncfunctioncliPlugin(props: PluginWithCLIProps){const{ config, schema, transformer, cwd, cli }=props;// Access CLI propertiesconstworkingDir=cli.cwd;constfileExtension=cli.extname;// Use CLI for logging or user interactionconsole.log(`Processing schema in: ${workingDir}`);// Process based on CLI contextif(config.interactive){// Interactive mode logicconsole.log('Running in interactive mode...');}// Generate outputconstcontent=generateContent(schema,{ workingDir, fileExtension });// Write to fileconstoutputPath=path.resolve(workingDir,config.output);awaitfs.writeFile(outputPath,content,'utf8');console.log(`Generated: ${outputPath}`);}
Custom Plugin Props
You can extend the base plugin props with custom properties:
importtype{PluginProps}from'@stackpress/idea-transformer/types';// Define custom propsinterfaceCustomProps{timestamp: string;version: string;debug: boolean;}// Use custom props in pluginexportdefaultasyncfunctioncustomPlugin(props: PluginProps<CustomProps>){const{ config, schema, transformer, cwd, timestamp, version, debug }=props;if(debug){console.log(`Plugin executed at ${timestamp} for version ${version}`);}// Plugin implementation}// Usage with transformerconsttransformer=awaitTransformer.load('./schema.idea');awaittransformer.transform({timestamp: newDate().toISOString(),version: '1.0.0',debug: true});
Plugin Examples
TypeScript Interface Generator
importtype{PluginProps}from'@stackpress/idea-transformer/types';importfsfrom'fs/promises';importpathfrom'path';interfaceTypeGenConfig{output: string;namespace?: string;exportType?: 'named'|'default';}exportdefaultasyncfunctiongenerateInterfaces(props: PluginProps<{config: TypeGenConfig}>){const{ config, schema, transformer, cwd }=props;letcontent='';// Add namespace if specifiedif(config.namespace){content+=`export namespace ${config.namespace} {\n`;}// Generate interfaces from modelsif(schema.model){for(const[name,model]ofObject.entries(schema.model)){content+=generateInterface(name,model);}}// Generate types from type definitionsif(schema.type){for(const[name,type]ofObject.entries(schema.type)){content+=generateType(name,type);}}// Close namespaceif(config.namespace){content+='}\n';}// Write to output fileconstoutputPath=awaittransformer.loader.absolute(config.output);awaitfs.writeFile(outputPath,content,'utf8');}functiongenerateInterface(name: string,model: any): string{letcontent=`export interface ${name} {\n`;for(constcolumnofmodel.columns||[]){constoptional=column.required ? '' : '?';consttype=mapToTypeScript(column.type);content+=` ${column.name}${optional}: ${type};\n`;}content+='}\n\n';returncontent;}functiongenerateType(name: string,type: any): string{// Implementation for generating TypeScript typesreturn`export type ${name} = any; // TODO: Implement\n\n`;}functionmapToTypeScript(schemaType: string): string{consttypeMap: Record<string,string>={'String': 'string','Number': 'number','Boolean': 'boolean','Date': 'Date','JSON': 'any'};returntypeMap[schemaType]||'any';}
Enum Generator
importtype{PluginProps}from'@stackpress/idea-transformer/types';importfsfrom'fs/promises';exportdefaultasyncfunctiongenerateEnums(props: PluginProps<{}>){const{ config, schema, transformer }=props;if(!schema.enum){console.log('No enums found in schema');return;}letcontent='// Generated Enums\n\n';for(const[name,enumDef]ofObject.entries(schema.enum)){content+=`export enum ${name} {\n`;for(const[key,value]ofObject.entries(enumDef)){content+=` ${key} = "${value}",\n`;}content+='}\n\n';}constoutputPath=awaittransformer.loader.absolute(config.output);awaitfs.writeFile(outputPath,content,'utf8');console.log(`Generated enums: ${outputPath}`);}
CLI-Interactive Plugin
importtype{PluginWithCLIProps}from'@stackpress/idea-transformer/types';importfsfrom'fs/promises';importpathfrom'path';exportdefaultasyncfunctioninteractiveGenerator(props: PluginWithCLIProps){const{ config, schema, transformer, cli }=props;// Use CLI for interactive promptsconsole.log(`\n🚀 Interactive Generator`);console.log(`Working Directory: ${cli.cwd}`);console.log(`Schema Extension: ${cli.extname}`);// Process based on available schema elementsconsthasModels=schema.model&&Object.keys(schema.model).length>0;consthasEnums=schema.enum&&Object.keys(schema.enum).length>0;consthasTypes=schema.type&&Object.keys(schema.type).length>0;console.log(`\n📊 Schema Summary:`);console.log(` Models: ${hasModels ? Object.keys(schema.model!).length : 0}`);console.log(` Enums: ${hasEnums ? Object.keys(schema.enum!).length : 0}`);console.log(` Types: ${hasTypes ? Object.keys(schema.type!).length : 0}`);// Generate based on configurationconstoutputs: string[]=[];if(config.generateModels&&hasModels){constmodelContent=generateModels(schema.model!);constmodelPath=path.resolve(cli.cwd,'generated/models.ts');awaitfs.mkdir(path.dirname(modelPath),{recursive: true});awaitfs.writeFile(modelPath,modelContent,'utf8');outputs.push(modelPath);}if(config.generateEnums&&hasEnums){constenumContent=generateEnums(schema.enum!);constenumPath=path.resolve(cli.cwd,'generated/enums.ts');awaitfs.mkdir(path.dirname(enumPath),{recursive: true});awaitfs.writeFile(enumPath,enumContent,'utf8');outputs.push(enumPath);}// Report resultsconsole.log(`\n✅ Generated ${outputs.length} files:`);outputs.forEach(file=>console.log(` 📄 ${file}`));}functiongenerateModels(models: Record<string,any>): string{// Implementation for generating modelsreturn'// Generated models\n';}functiongenerateEnums(enums: Record<string,any>): string{// Implementation for generating enumsreturn'// Generated enums\n';}
// Always use proper typing for plugin propsimporttype{PluginProps,PluginWithCLIProps}from'@stackpress/idea-transformer/types';// Define custom config typesinterfaceMyPluginConfig{output: string;format: 'typescript'|'javascript';strict?: boolean;}// Use typed propsexportdefaultasyncfunctiontypedPlugin(props: PluginProps<{config: MyPluginConfig}>){const{ config }=props;// TypeScript will enforce config structureconstoutput: string=config.output;// ✅ Type-safeconstformat: 'typescript'|'javascript'=config.format;// ✅ Type-safeconststrict: boolean=config.strict??false;// ✅ Type-safe with default}
Configuration Validation
functionvalidateConfig(config: any): asserts config is MyPluginConfig{if(!config.output||typeofconfig.output!=='string'){thrownewError('Plugin requires "output" configuration as string');}if(!config.format||!['typescript','javascript'].includes(config.format)){thrownewError('Plugin requires "format" to be "typescript" or "javascript"');}}exportdefaultasyncfunctionvalidatedPlugin(props: PluginProps<{}>){validateConfig(props.config);// Now config is properly typedconst{ output, format }=props.config;}
File Operations
// Use transformer's file loader for consistent path resolutionexportdefaultasyncfunctionfilePlugin(props: PluginProps<{}>){const{ config, transformer }=props;// ✅ Use transformer.loader for path resolutionconstoutputPath=awaittransformer.loader.absolute(config.output);// ✅ Create directories if neededawaitfs.mkdir(path.dirname(outputPath),{recursive: true});// ✅ Write file with proper error handlingtry{awaitfs.writeFile(outputPath,content,'utf8');}catch(error){thrownewError(`Failed to write output file: ${error.message}`);}}
CLI Integration
// Use CLI props when availableexportdefaultasyncfunctionadaptivePlugin(props: PluginWithCLIProps){const{ cli, config }=props;// Adapt behavior based on CLI contextconstoutputDir=config.outputDir||path.join(cli.cwd,'generated');constverbose=config.verbose||false;if(verbose){console.log(`Generating files in: ${outputDir}`);console.log(`Working directory: ${cli.cwd}`);console.log(`File extension: ${cli.extname}`);}// Use CLI working directory for relative pathsconstabsoluteOutputDir=path.resolve(cli.cwd,outputDir);// Generate files...}
The plugin system provides a simple and elegant way to extend your application's functionality through modular components. Plugins can be easily toggled on or off, making your application highly configurable and maintainable.
How It Works
The plugin system follows a three-step process:
Create a plugin file that exports a default function
Register the plugin in your package.json
Bootstrap the plugins when starting your server
Creating a Plugin
A plugin is simply a TypeScript/JavaScript file that exports a default function. This function receives the server instance and can configure it as needed.
Locally: ./src/plugin.ts or ./plugins/my-plugin.js
In node_modules: some-package/plugin or @scope/package/plugin
Anywhere accessible: Any valid module path
Bootstrapping Plugins
The magic happens when you call server.bootstrap() in your server startup script:
importserverfrom'../server';asyncfunctionmain(){// Load all plugins from package.jsonawaitserver.bootstrap();// Start the server with plugins loadedserver.create().listen(3000,()=>{console.log('Server is running on port 3000');});}main().catch(console.error);
Bootstrap Process
Reads the plugins array from package.json
Imports each plugin module
Executes the plugin function with the server instance
Configures the server with all plugin modifications
Managing Plugins
Enabling a Plugin
Add the plugin path to the plugins array:
{
"plugins": [
"./src/plugin",
"./src/new-plugin"// ← Add this line
]
}
Disabling a Plugin
Remove or comment out the plugin path:
{
"plugins": [
"./src/plugin"// "./src/disabled-plugin" ← Comment out or remove
]
}
Plugin Order
Plugins are loaded in the order they appear in the array. If plugin order matters for your use case, arrange them accordingly.
Benefits
🔧 Modularity
Keep your application organized by separating concerns into focused plugins.
🎛️ Easy Configuration
Toggle features on/off by simply editing the plugins array.
📦 Reusability
Share plugins across projects or publish them as npm packages.
🚀 Clean Startup
The bootstrap process keeps your server startup code clean and simple.
🔄 Hot Swapping
Easily experiment with different plugin combinations during development.
Example Use Cases
Authentication Plugin: Handle user login/logout functionality
Logging Plugin: Add request/response logging
API Plugin: Add REST or GraphQL endpoints
Static Files Plugin: Serve static assets
Database Plugin: Configure database connections
Monitoring Plugin: Add health checks and metrics
Best Practices
Keep plugins focused: Each plugin should have a single responsibility
Use TypeScript: Leverage type safety for better development experience
Document your plugins: Include clear documentation for custom plugins
Test plugins independently: Write unit tests for plugin functionality
Handle errors gracefully: Ensure plugins don't crash the entire application
This plugin system makes your application highly modular and configurable while keeping the complexity hidden behind a simple, elegant interface.
Comprehensive API documentation for the Stackpress Library - a shared library used across Stackpress projects that standardizes type definitions and provides common utilities for developers.
Installation
npm install @stackpress/lib
API Reference
Data Structures
Data structures in programming are specialized formats for organizing, processing, storing, and retrieving data within a computer's memory.
Key Features:
Type-safe nested object manipulation
Path-based data access with dot notation
Built-in parsers for query strings, form data, and arguments
Chainable API for fluent data operations
Events
Event driven designed to support event chain reactions.
Key Features:
Priority-based event listeners
Type-safe event maps with TypeScript generics
Before/after hooks for event execution
Task queue management for event handling
Exception
Enhanced error handling with expressive error reporting and stack trace support. Provides better error information than standard JavaScript errors.
Key Features:
Template-based error messages
Validation error aggregation
Enhanced stack trace parsing
HTTP status code integration
Queues
Priority-based queue implementations for managing items and tasks with FIFO ordering. Includes both generic item queues and specialized task queues.
Most components work in both Node.js and browser environments:
✅ Nest - Full browser support
✅ EventEmitter - Full browser support
✅ Exception - Full browser support
✅ Queue - Full browser support
⚠️FileLoader/NodeFS - Node.js only
Events
From browser clicks to CLI commands to backend routing, Stackpress treats everything as an event that flows seamlessly through your stack.
Behind user experiences are a chain reaction of events.
At its core, all software exists for users to interact with it. These interactions—whether a mouse click in the browser, a command in the terminal, or a tap on a mobile app, are all actions. Actions are events. Software, in one way or another, is always waiting for certain actions to occur and then responding to them. This means every application has an inherent level of event-driven design (IBM Developer, Wikipedia).
Modern event-driven systems are valued for being responsive, loosely coupled, scalable, and resilient (Confluent, PubNub).
🔄 Responsive — React immediately when an event occurs, instead of waiting on rigid request/response cycles. This enables real-time behavior, such as updating a UI the moment data changes or triggering backend workflows instantly.
🧩 Loosely Coupled — Components don’t need direct knowledge of each other; they communicate through events. This reduces dependencies, making systems easier to maintain and extend.
📈 Scalable — Events are asynchronous, which means they can be queued, processed in parallel, and distributed across workers or servers. This makes handling high loads far simpler.
🛡️ Resilient — Failures are isolated. If one listener fails, the rest of the system continues. Recovery strategies like retries or fallbacks can be added without rewriting business logic.
Stackpress builds on these principles by making events not just a layer, but the foundation of software design. A user interaction starts at the edge (browser, CLI, mobile app), but in a fully event-driven design these same events can propagate through the backend—across servers, databases, sockets, etc. setting of a chain reaction of events where one event triggers actions, and those actions can emit more events in turn, and so on.
In Stackpress, even a router is a type of event emitter. By treating routes, sockets, and commands as events, Stackpress provides a unified, generically designed emitter that simplifies building across environments.
EventEmitter
A class that implements the observer pattern for handling events with priority levels and task queues.
A Map of event matches with their patterns and data.
Getting Task Queue
The following example shows how to get a task queue for a specific event.
constqueue=emitter.tasks('trigger something');console.log(queue.size);// Number of tasks for this event
Parameters
Parameter
Type
Description
event
N extends EventName<M>
The event name
Returns
A TaskQueue containing all tasks for the specified event.
Using Other Emitters
The following example shows how to merge listeners from another emitter.
constemitter1=newEventEmitter();constemitter2=newEventEmitter();emitter2.on('shared event',async()=>console.log('from emitter2'));emitter1.use(emitter2);// emitter1 now has emitter2's listeners
Parameters
Parameter
Type
Description
emitter
EventEmitter<M>
Another emitter to merge listeners from
Returns
The EventEmitter instance to allow method chaining.
Creating Task Queues
The following example shows how to create a new task queue (can be overridden in subclasses).
constqueue=emitter.makeQueue();
Returns
A new TaskQueue instance for managing event tasks.
Setting Hooks
The following example shows how to set before and after hooks for event execution.
The ExpressEmitter instance to allow method chaining.
Pattern Matching
The following example shows how to get all matching patterns for an event.
emitter.on('user *',handler1);emitter.on(':action user',handler2);emitter.on(/user(.+)/,handler3);constmatches=emitter.match('user login');// Returns Map with all matching patterns and their extracted data
Parameters
Parameter
Type
Description
event
string
The event name to match against patterns
Returns
A Map of event matches with their patterns, parameters, and arguments.
Using Other ExpressEmitters
The following example shows how to merge patterns and listeners from another emitter.
constemitter1=newExpressEmitter('/');constemitter2=newExpressEmitter('-');emitter2.on('api-*',handler);emitter2.on(':method-users',handler);emitter1.use(emitter2);// Merges expressions and listeners
Parameters
Parameter
Type
Description
emitter
EventEmitter<M>
Another emitter to merge patterns from
Returns
The ExpressEmitter instance to allow method chaining.
Pattern Syntax
ExpressEmitter supports several pattern matching syntaxes:
The following properties are available when instantiating a RouteEmitter.
Property
Type
Description
routes
Map<string, Route>
Map of event names to route definitions
separator
string
Pattern separator (always '/')
expressions
Map<string, EventExpression>
Map of event names to regex expressions (inherited)
Methods
The following methods are available when instantiating a RouteEmitter.
Defining Routes
The following example shows how to define HTTP-like routes.
constrouter=newRouteEmitter();// Basic routesrouter.route('GET','/users',async(req,res)=>{// Handle GET /users});router.route('POST','/users',async(req,res)=>{// Handle POST /users });// Routes with parametersrouter.route('GET','/users/:id',async(req,res)=>{constuserId=req.params.id;// Parameter extraction});// Wildcard routesrouter.route('GET','/api/*',async(req,res)=>{// Handle any GET /api/* route});// Any method routesrouter.route('ANY','/health',async(req,res)=>{// Handle any HTTP method to /health});
Parameters
Parameter
Type
Description
method
string
HTTP method (GET, POST, PUT, DELETE, ANY, etc.)
path
string
Route path with optional parameters
action
RouteAction<R, S>
Route handler function
priority
number
Priority level (default: 0)
Returns
The RouteEmitter instance to allow method chaining.
Using Other RouteEmitters
The following example shows how to merge routes from another router.
constapiRouter=newRouteEmitter();apiRouter.route('GET','/api/users',handler);apiRouter.route('POST','/api/users',handler);constmainRouter=newRouteEmitter();mainRouter.use(apiRouter);// Merges routes and listeners
Parameters
Parameter
Type
Description
emitter
EventEmitter<RouteMap<R, S>>
Another router to merge routes from
Returns
The RouteEmitter instance to allow method chaining.
// Single parameterrouter.route('GET','/users/:id',handler);// Matches: GET /users/123// Extracts: { id: '123' }// Multiple parameters router.route('GET','/users/:userId/posts/:postId',handler);// Matches: GET /users/123/posts/456// Extracts: { userId: '123', postId: '456' }
Wildcard Routes
// Single wildcardrouter.route('GET','/files/*',handler);// Matches: GET /files/document.pdf// Catch-all wildcardrouter.route('GET','/static/**',handler);// Matches: GET /static/css/main.css
Method Handling
// Specific methodsrouter.route('GET','/users',getUsers);router.route('POST','/users',createUser);router.route('PUT','/users/:id',updateUser);router.route('DELETE','/users/:id',deleteUser);// Any methodrouter.route('ANY','/health',healthCheck);
Event Generation
RouteEmitter automatically generates event names from routes:
router.route('GET','/users/:id',handler);// Generates event: 'GET /users/:id'// Can be emitted as: router.emit('GET /users/123', req, res)router.route('ANY','/api/*',handler);// Generates regex pattern for any method// Matches: 'GET /api/users', 'POST /api/data', etc.
Integration with Router
RouteEmitter is used internally by the Router class but can be used standalone:
constrouteEmitter=newRouteEmitter<MyRequest,MyResponse>();routeEmitter.route('GET','/api/:resource',async(req,res)=>{constresource=routeEmitter.event?.data.params.resource;// Handle API resource request});// Emit route events directlyawaitrouteEmitter.emit('GET /api/users',request,response);
Queue
Priority-based queue implementations for managing items and tasks with FIFO ordering.
When an event is triggered, Stackpress doesn’t just fire off listeners blindly. Instead, it organizes them into a TaskQueue, which then consumes items sequentially by priority.
Because events can be defined anywhere, event priority allows you to structure execution like a series of steps—making the flow of your application predictable and easy to follow.
Even better, the TaskQueue makes the EventEmitter a true plugin system: you can insert new code between existing listeners without rewriting or restructuring what’s already there. This means features, extensions, or third-party modules can seamlessly “hook into” the event pipeline without breaking your core logic.
Generic request wrapper that works with IncomingMessage and WHATWG (Fetch) Request.
Properties
The following properties are available when instantiating a Request.
Property
Type
Description
data
CallableNest
Combined data from query, post, and additional data
headers
CallableMap<string, string|string[]>
Request headers
query
CallableNest
URL query parameters
post
CallableNest
POST body data
session
CallableSession
Session data
url
URL
Request URL object
method
Method
HTTP method
body
Body|null
Raw request body
loaded
boolean
Whether the body has been loaded
mimetype
string
Request body MIME type
resource
R
Original request resource
type
string
Type of body content
loader
RequestLoader<R>
Body loader function
Methods
The following methods are available when instantiating a Request.
Loading Request Body
The following example shows how to load the request body asynchronously.
constreq=router.request({url: 'http://example.com/api',method: 'POST'});awaitreq.load();// Loads body using the configured loaderconsole.log(req.body);// Access the loaded body
Returns
The Request instance to allow method chaining.
Response
Generic response wrapper that works with ServerResponse and WHATWG (Fetch) Response.
Properties
The following properties are available when instantiating a Response.
Property
Type
Description
headers
CallableMap<string, string|string[]>
Response headers
session
CallableSession
Session data
errors
CallableNest
Validation errors
data
CallableNest
Response data
body
Body|null
Response body
code
number
HTTP status code
error
string|undefined
Error message
redirected
boolean
Whether response is a redirect
sent
boolean
Whether response has been sent
stack
Trace[]|undefined
Stack trace for errors
status
string
HTTP status message
total
number
Total count of results
mimetype
string|undefined
Response MIME type
resource
S
Original response resource
type
string
Type of body content
dispatcher
ResponseDispatcher<S>
Response dispatcher function
Methods
The following methods are available when instantiating a Response.
Setting JSON Response
The following example shows how to set a JSON response.
A StatusResponse object with all response details.
Router
Event-driven router that extends ExpressEmitter for handling HTTP-like routing.
Properties
The following properties are available when instantiating a Router.
Property
Type
Description
routes
Map<string, Route>
Map of event names to route definitions
Methods
The following methods are available when instantiating a Router.
Defining Routes
The following example shows how to define routes with different HTTP methods.
router.route('GET','/users/:id',async(req,res,ctx)=>{constuserId=req.data.get('id');res.setJSON({id: userId,name: 'John'});});router.route('POST','/users',async(req,res,ctx)=>{constuserData=req.data.get();// Create user logicres.setJSON(userData,201,'Created');});
Parameters
Parameter
Type
Description
method
string
HTTP method (GET, POST, PUT, DELETE, etc.)
path
string
Route path with optional parameters (:id)
action
RouterAction<R, S, X>
Route handler function
priority
number
Priority level (default: 0)
Returns
The Router instance to allow method chaining.
Emitting Route Events
The following example shows how to emit route events directly.
constreq=router.request({url: '/users/123'});constres=router.response();conststatus=awaitrouter.emit('GET /users/123',req,res);console.log(status.code);// 200, 404, etc.
Parameters
Parameter
Type
Description
event
string
Route event name (e.g., 'GET /users/123')
req
Request<R>
Request object
res
Response<S>
Response object
Returns
A promise that resolves to a Status object indicating success or failure.
Resolving Routes
The following example shows how to resolve routes and get response data.
// Resolve by method and pathconstresponse=awaitrouter.resolve('GET','/users/123',{headers: {'Authorization': 'Bearer token'}});// Resolve by event nameconstresponse=awaitrouter.resolve('user-created',userData);
Parameters
Parameter
Type
Description
methodOrEvent
string
HTTP method or event name
pathOrRequest
string|Request<R>|Record<string, any>
Route path or request data
requestOrResponse
Request<R>|Record<string, any>|Response<S>
Request or response object
response
Response<S>
Response object (optional)
Returns
A promise that resolves to a partial StatusResponse object.
Creating Request Objects
The following example shows how to create request objects.
The following example shows how to merge routes and listeners from another router.
constapiRouter=newRouter();apiRouter.route('GET','/api/users',handler);constmainRouter=newRouter();mainRouter.use(apiRouter);// Merges routes and listeners
Parameters
Parameter
Type
Description
emitter
EventEmitter<RouterMap<R, S, X>>
Another router or emitter to merge
Returns
The Router instance to allow method chaining.
Route Parameters
Routes support parameter extraction using colon notation:
router.route('GET','/users/:id/posts/:postId',(req,res)=>{constuserId=req.data.get('id');constpostId=req.data.get('postId');// Parameters are automatically added to req.data});
Cross-Platform Usage
The Router system is designed to work across different platforms:
Path to resolve (supports @/, ./, ../, and node_modules)
pwd
string
Working directory (default: current working directory)
Returns
A promise that resolves to the absolute path string.
Getting Base Paths
The following example shows how to remove file extensions from paths.
constbasePath=loader.basepath('/path/to/file.js');// Returns: '/path/to/file'constnoExt=loader.basepath('/path/to/file');// Returns: '/path/to/file' (unchanged if no extension)
Parameters
Parameter
Type
Description
pathname
string
Path to process
Returns
The path without the file extension.
Locating Node Modules
The following example shows how to locate node_modules directories.
// Find node_modules containing a specific packageconstmodulesPath=awaitloader.modules('@types/node');// Returns: '/project/node_modules'// Find node_modules from a specific directoryconstmodulesPath2=awaitloader.modules('lodash','/some/path');// Returns: '/some/path/node_modules' or traverses up
Parameters
Parameter
Type
Description
pathname
string
Package name to locate
pwd
string
Starting directory (default: current working directory)
meta
boolean
Use import.meta.resolve if available (default: true)
Returns
A promise that resolves to the node_modules directory path.
Locating Library Path
The following example shows how to locate the @stackpress/lib installation.
constlibPath=awaitloader.lib();// Returns: '/project/node_modules' (where @stackpress/lib is installed)constlibPath2=awaitloader.lib('/custom/path');// Returns: node_modules path containing @stackpress/lib from custom path
Parameters
Parameter
Type
Description
pwd
string
Starting directory (default: current working directory)
Returns
A promise that resolves to the node_modules directory containing @stackpress/lib.
Importing Files
The following example shows how to dynamically import various file types.
// Import JavaScript/TypeScript modulesconstmodule=awaitloader.import('./utils/helper.js');console.log(module.default);// Default exportconsole.log(module.namedExport);// Named export// Import JSON filesconstconfig=awaitloader.import('./config.json');console.log(config);// Parsed JSON object// Import with default extractionconstdefaultExport=awaitloader.import('./module.js',true);// Returns only the default export
Parameters
Parameter
Type
Description
pathname
string
Path to the file to import
getDefault
boolean
Return only the default export (default: false)
Returns
A promise that resolves to the imported module or JSON data.
Getting Relative Paths
The following example shows how to get relative paths between files.
// Get relative path from source to targetconstrelativePath=loader.relative('/project/src/components/Button.js','/project/src/utils/helper.js');// Returns: '../utils/helper'// Include file extensionconstrelativeWithExt=loader.relative('/project/src/components/Button.js','/project/src/utils/helper.js',true);// Returns: '../utils/helper.js'
Parameters
Parameter
Type
Description
pathname
string
Source file path
require
string
Target file path
withExtname
boolean
Include file extension (default: false)
Returns
The relative path from source to target.
Resolving Paths
The following example shows how to resolve and verify path existence.
// Resolve path (returns null if not found)constresolved=awaitloader.resolve('./config.json');// Returns: '/project/config.json' or null// Resolve with existence check (throws if not found)try{constresolved=awaitloader.resolve('./config.json',process.cwd(),true);console.log('File exists:',resolved);}catch(error){console.log('File not found');}// Resolve from specific directoryconstresolved2=awaitloader.resolve('@/src/index.ts','/custom/pwd');
Parameters
Parameter
Type
Description
pathname
string
Path to resolve
pwd
string
Working directory (default: current working directory)
exists
boolean
Throw error if path doesn't exist (default: false)
Returns
A promise that resolves to the absolute path or null if not found.
Resolving Files with Extensions
The following example shows how to resolve files with automatic extension detection.
// Try multiple extensionsconstfile=awaitloader.resolveFile('./module',['.js','.ts','.json']);// Tries: ./module.js, ./module.ts, ./module.json, ./module/index.js, etc.// Resolve with existence checktry{constfile=awaitloader.resolveFile('./module',['.js'],process.cwd(),true);console.log('Found file:',file);}catch(error){console.log('No file found with specified extensions');}
Parameters
Parameter
Type
Description
pathname
string
Path to resolve (without extension)
extnames
string[]
File extensions to try (default: ['.js', '.json'])
pwd
string
Working directory (default: current working directory)
exists
boolean
Throw error if file doesn't exist (default: false)
Returns
A promise that resolves to the resolved file path or null if not found.
Path Resolution Patterns
FileLoader supports several path resolution patterns:
Project Root Paths (@/)
// @ refers to the project root (cwd)awaitloader.absolute('@/src/index.ts');awaitloader.absolute('@/config/database.json');
Relative Paths
// Standard relative pathsawaitloader.absolute('./utils/helper.js');awaitloader.absolute('../shared/constants.ts');
Node Modules
// Automatically resolves from node_modulesawaitloader.absolute('lodash');awaitloader.absolute('@types/node');awaitloader.absolute('@company/private-package');
Absolute Paths
// Absolute paths are returned as-is (with symlink resolution)awaitloader.absolute('/usr/local/bin/node');awaitloader.absolute('C:\\Program Files\\Node\\node.exe');
File System Abstraction
FileLoader works with any FileSystem implementation:
Path separators: Automatically uses correct separators (/ vs \)
Symlinks: Resolves symbolic links to real paths
Case sensitivity: Respects filesystem case sensitivity rules
Module resolution: Works with both CommonJS and ES modules
Error Handling
FileLoader provides clear error messages for common issues:
try{awaitloader.modules('non-existent-package');}catch(error){// Error: Cannot find non-existent-package in any node_modules}try{awaitloader.resolve('missing-file.js',process.cwd(),true);}catch(error){// Error: Cannot resolve 'missing-file.js'}
Usage Examples
Loading Configuration Files
// Load config with fallbacksconstconfig=awaitloader.import('@/config.json').catch(()=>({}));// Load environment-specific configconstenv=process.env.NODE_ENV||'development';constenvConfig=awaitloader.import(`@/config/${env}.json`);
Module Resolution
// Resolve module paths for bundlingconstmodulePath=awaitloader.absolute('lodash');constrelativePath=loader.relative('./src/index.js',modulePath);// Find all modules in a directoryconstmodules=awaitloader.modules('.');
Dynamic Imports
// Dynamically import pluginsconstpluginPath=awaitloader.resolveFile(`./plugins/${name}`,['.js','.ts']);if(pluginPath){constplugin=awaitloader.import(pluginPath,true);// Use plugin.default}
## DataStructuresDatastructuresinprogrammingarespecializedformatsfororganizing,processing,storing,andretrievingdatawithinacomputer's memory. They define the relationships between data elements and the operations that can be performed on them. The choice of data structure significantly impacts the efficiency and performance of algorithms and overall program execution.
### NestHierarchicaldatamanagementutilitiesfornestedobjectsandarrays.```typescriptimport{nest,Nest,ReadonlyNest}from'@stackpress/lib';typeNestMap={database: Record<string,{host: string,port: number}>};constcallable=nest<NestMap>({database: {postgres: {host: 'localhost',port: 5432}}});constconfig=newNest<NestMap>({database: {postgres: {host: 'localhost',port: 5432}}});constreadonly=newReadonlyNest<NestMap>({database: {postgres: {host: 'localhost',port: 5432}}});
Properties
The following properties are available when instantiating a Nest.
Property
Type
Description
data
M
Raw nested data structure
size
number
Total number of top-level keys
withArgs
ArgString
Parser for terminal args
withFormData
FormData
Parser for multipart/form-data
withPath
PathString
Parser for path notations
withQuery
QueryString
Parser for query string
Methods
The following methods are available when instantiating a Nest.
Retrieving Data
The following example shows how to retrieve data from a nest.
Creates a callable Map instance that can be invoked as a function to get values.
import{map}from'@stackpress/lib';constuserMap=map<string,User>([['john',{name: 'John',age: 30}],['jane',{name: 'Jane',age: 25}]]);// Use as function to get valuesconstjohn=userMap('john');// { name: 'John', age: 30 }// Use as MapuserMap.set('bob',{name: 'Bob',age: 35});console.log(userMap.size);// 3
Properties
The following properties are available on a callable map.
Property
Type
Description
size
number
Number of key-value pairs in the map
Methods
The following methods are available on a callable map.
Direct Invocation
The following example shows how to use the map as a function.
constvalue=myMap('key');// Equivalent to myMap.get('key')
Parameters
Parameter
Type
Description
key
K
The key to retrieve the value for
Returns
The value associated with the key, or undefined if not found.
Setting Values
The following example shows how to set key-value pairs.
myMap.set('newKey','newValue');
Parameters
Parameter
Type
Description
key
K
The key to set
value
V
The value to associate with the key
Returns
The Map instance to allow method chaining.
Getting Values
The following example shows how to get values by key.
constvalue=myMap.get('key');
Parameters
Parameter
Type
Description
key
K
The key to retrieve the value for
Returns
The value associated with the key, or undefined if not found.
Checking Key Existence
The following example shows how to check if a key exists.
constexists=myMap.has('key');// true or false
Parameters
Parameter
Type
Description
key
K
The key to check for existence
Returns
true if the key exists, false otherwise.
Deleting Keys
The following example shows how to delete a key-value pair.
constdeleted=myMap.delete('key');// true if deleted, false if not found
Parameters
Parameter
Type
Description
key
K
The key to delete
Returns
true if the key was deleted, false if the key didn't exist.
Clearing All Data
The following example shows how to remove all key-value pairs.
myMap.clear();console.log(myMap.size);// 0
Returns
undefined
Iterating Over Entries
The following example shows how to iterate over all key-value pairs.
Creates a callable Set instance that can be invoked as a function to get values by index.
import{set}from'@stackpress/lib';consttags=set(['javascript','typescript','node.js']);// Use as function to get by indexconstfirstTag=tags(0);// 'javascript'constsecondTag=tags(1);// 'typescript'// Use as Settags.add('react');console.log(tags.size);// 4
Properties
The following properties are available on a callable set.
Property
Type
Description
size
number
Number of values in the set
Methods
The following methods are available on a callable set.
Direct Invocation
The following example shows how to use the set as a function to get values by index.
constvalue=mySet(0);// Get first itemconstvalue2=mySet(2);// Get third item
Parameters
Parameter
Type
Description
index
number
The index of the value to retrieve
Returns
The value at the specified index, or undefined if index is out of bounds.
Getting Values by Index
The following example shows how to get values by index using the index method.
constvalue=mySet.index(0);// Same as mySet(0)
Parameters
Parameter
Type
Description
index
number
The index of the value to retrieve
Returns
The value at the specified index, or undefined if index is out of bounds.
Adding Values
The following example shows how to add values to the set.
mySet.add('newValue');
Parameters
Parameter
Type
Description
value
V
The value to add to the set
Returns
The Set instance to allow method chaining.
Checking Value Existence
The following example shows how to check if a value exists.
constexists=mySet.has('value');// true or false
Parameters
Parameter
Type
Description
value
V
The value to check for existence
Returns
true if the value exists, false otherwise.
Deleting Values
The following example shows how to delete a value.
constdeleted=mySet.delete('value');// true if deleted, false if not found
Parameters
Parameter
Type
Description
value
V
The value to delete
Returns
true if the value was deleted, false if the value didn't exist.
Clearing All Data
The following example shows how to remove all values.
mySet.clear();console.log(mySet.size);// 0
Returns
undefined
Iterating Over Entries
The following example shows how to iterate over all value pairs.
for(const[value1,value2]ofmySet.entries()){console.log(value1,value2);// Both values are the same in a Set}
Returns
An iterator of [value, value] pairs.
Iterating Over Values
The following example shows how to iterate over all values.
Used by the Response class for successful API responses and data
retrieval operations.
StatusResponse
Union type combining error and success response structures for flexible
response handling.
constresponse: StatusResponse<User>={code: 200,status: 'OK',results: {id: 1,name: 'John'}};// Or for errorsconsterrorResponse: StatusResponse={code: 400,status: 'Bad Request',error: 'Validation failed'};
Usage
Used by router actions and event handlers that can return either success
or error responses. Distinguished from ResponseStatus by including
optional error and success fields.
Trace
Stack trace entry providing debugging information for error tracking.
Used by Request and Response classes to handle different body types
in a type-safe manner.
Headers
HTTP headers structure supporting both object and Map representations.
constheaders: Headers={'Content-Type': 'application/json','Authorization': 'Bearer token123'};// Or as MapconstheaderMap: Headers=newMap([['Content-Type','application/json'],['Set-Cookie',['session=abc','csrf=xyz']]]);
Usage
Used by Request and Response classes for HTTP header management
with support for multiple values per header.
Data
Generic data container supporting both Map and object representations.
constdata: Data={user: {id: 1,name: 'John'},settings: {theme: 'dark'}};// Or as MapconstdataMap: Data=newMap([['user',{id: 1,name: 'John'}],['settings',{theme: 'dark'}]]);
Usage
Used by Request class to store merged data from query parameters,
POST data, and additional context.
Query
URL query parameters supporting string, Map, or object representations.