🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@wiredwp/robinpath

Package Overview
Dependencies
Maintainers
2
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@wiredwp/robinpath - npm Package Compare versions

Comparing version
0.30.0
to
0.30.1
+45
dist/classes/ASTSerializer.d.ts
import { Environment } from '../types/Environment.type';
import { Statement, Arg } from '../types/Ast.type';
import { Value } from '../utils';
export declare class ASTSerializer {
private environment;
constructor(environment: Environment);
/**
* Find the module name for a given function name
* Returns the module name if found, null otherwise
*/
findModuleName(functionName: string, currentModuleContext?: string | null): string | null;
/**
* Get a simplified tree structure of the AST for navigation
*/
getStructure(statements: Statement[], nodeKeyPrefix?: string): any[];
/**
* Generate a human-readable label for a statement
*/
private getNodeLabel;
/**
* Serialize a statement to a JSON-serializable object
* @param stmt The statement to serialize
* @param currentModuleContext Optional module context from "use" command
* @param lastValue Optional last value (execution state) - can be a Value or a state object with lastValue and beforeValue
* @param nodeKey Optional key for the node
*/
serializeStatement(stmt: Statement, currentModuleContext?: string | null, lastValue?: Value | {
lastValue: Value;
beforeValue: Value;
} | null, nodeKey?: string): any;
/**
* Serialize an argument to a JSON-serializable object
*/
serializeArg(arg: Arg, currentModuleContext?: string | null, nodeKey?: string): any;
}
import { TokenStream } from '../classes/TokenStream';
import { CellBlock } from '../types/Ast.type';
/**
* Parse a cell block from the current position
*
* @param stream - Token stream positioned at the cell open fence
* @param source - Full source code string
* @param parseStatement - Function to parse statements (for code cells)
* @returns CellBlock if parsed, null otherwise
*/
export declare function parseCellBlock(stream: TokenStream, source: string, _parseStatement: (stream: TokenStream) => any): Promise<CellBlock | null>;
import { TokenStream } from '../classes/TokenStream';
import { ChunkMarkerStatement } from '../types/Ast.type';
/**
* Parse a chunk marker from the current line
*
* Syntax: --- chunk:<id> [key:value ...] ---
*
* @param stream - Token stream positioned at the start of a potential chunk marker
* @param source - Full source code string for extracting raw line
* @returns ChunkMarkerStatement if parsed, null otherwise
*/
export declare function parseChunkMarker(stream: TokenStream, source: string): ChunkMarkerStatement | null;
/**
* Fence Classifier - Classifies fence lines with priority order
*
* Priority order (to avoid ambiguity):
* 1. ---cell ...--- (cell open)
* 2. ---end--- (cell close)
* 3. --- chunk:... --- (chunk marker)
* 4. bare --- (prompt fence open/close)
*/
export type FenceClassification = {
kind: 'cell_open';
cellType: string;
meta: Record<string, string>;
} | {
kind: 'cell_end';
} | {
kind: 'chunk_marker';
id: string;
meta: Record<string, string>;
} | {
kind: 'prompt_fence';
} | null;
/**
* Classify a fence line according to priority order
*
* @param line - Raw line string to classify
* @returns Classification result or null if not a fence line
*/
export declare function classifyFenceLine(line: string): FenceClassification;
import { Token } from '../classes/Lexer';
import { CodePosition } from '../types/Ast.type';
import { TokenStream } from '../classes/TokenStream';
/**
* Create CodePosition from start and end tokens
* @param startToken - The token where the node starts
* @param endToken - The token where the node ends
* @returns 0-indexed CodePosition
*/
export declare function createCodePosition(startToken: Token, endToken: Token): CodePosition;
/**
* Skip whitespace and comments on the current line
* @param stream - TokenStream to skip in
*/
export declare function skipWhitespaceOnLine(stream: TokenStream): void;
import { TokenStream } from '../classes/TokenStream';
import { PromptBlockStatement } from '../types/Ast.type';
/**
* Parse a prompt block from the current position
*
* @param stream - Token stream positioned at the opening prompt fence
* @param source - Full source code string
* @returns PromptBlockStatement if parsed, null otherwise
*/
export declare function parsePromptBlock(stream: TokenStream, source: string): PromptBlockStatement | null;
import { Value } from '../utils';
import { DefineFunction, OnBlock, Arg } from './Ast.type';
export type BuiltinCallback = (callbackArgs: Value[]) => Promise<Value> | Value | null;
export type BuiltinHandler = (args: Value[], callback?: BuiltinCallback | null) => Promise<Value> | Value | null;
export type DecoratorHandler = (targetName: string, func: DefineFunction | null, originalArgs: Value[], decoratorArgs: Value[], originalDecoratorArgs?: Arg[]) => Promise<Value[] | Value | null | undefined>;
export type ParseDecoratorHandler = (targetName: string, func: DefineFunction | null, decoratorArgs: Arg[], environment: Environment) => Promise<void> | void;
export interface Environment {
variables: Map<string, Value>;
functions: Map<string, DefineFunction>;
builtins: Map<string, BuiltinHandler>;
decorators: Map<string, DecoratorHandler>;
parseDecorators: Map<string, ParseDecoratorHandler>;
metadata: Map<string, FunctionMetadata>;
moduleMetadata: Map<string, ModuleMetadata>;
currentModule: string | null;
variableMetadata: Map<string, Map<string, Value>>;
functionMetadata: Map<string, Map<string, Value>>;
constants: Set<string>;
eventHandlers: Map<string, OnBlock[]>;
}
export interface Frame {
locals: Map<string, Value>;
lastValue: Value;
isFunctionFrame?: boolean;
forgotten?: Set<string>;
isIsolatedScope?: boolean;
}
export type DataType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null' | 'any';
export type FormInputType = 'text' | 'number' | 'textarea' | 'select' | 'checkbox' | 'radio' | 'date' | 'datetime' | 'file' | 'json' | 'code' | 'varname';
export interface ParameterMetadata {
name: string;
label?: string;
dataType: DataType;
description: string;
formInputType: FormInputType;
required?: boolean;
defaultValue?: Value;
children?: ParameterMetadata;
}
export interface FunctionMetadata {
description: string;
parameters: ParameterMetadata[];
returnType: DataType;
returnDescription: string;
example?: string;
}
export interface ModuleMetadata {
description: string;
methods: string[];
author?: string;
category?: string;
doc_url?: string;
}
+161
-2

@@ -1,4 +0,163 @@

import { Statement } from '../../types/Ast.type';
import { Statement, CommentWithPosition } from '../../types/Ast.type';
import { Writer } from './Writer';
import { LineIndex } from './LineIndex';
import { Patch, CommentLayout, PrintContext } from './types';
export type { CommentWithPosition, LineIndex, PrintContext };
export { Writer };
/**
* CommentLayout - Normalize comment ownership
*
* Decides, per statement:
* - leadingComments: Comment[] (non-inline)
* - inlineComment: Comment | null
* - leadingGapLines: number (blank lines between last leading comment and statement)
* - trailingBlankLinesAfterStandaloneComment: number (only for standalone comment nodes)
*/
export declare class CommentLayoutNormalizer {
/**
* Normalize comment layout for a statement
*/
static normalize(node: Statement, lineIndex: LineIndex): CommentLayout;
}
/**
* PatchApplier - Apply patches to source code
*
* Sort patches descending (as you do now) and apply.
* Optional: validate overlaps and throw in dev mode.
*/
export declare class PatchApplier {
/**
* Apply patches to source code
* Patches are sorted descending by startOffset and applied from end to start
* to prevent character position shifts from affecting subsequent replacements
*/
static apply(originalScript: string, patches: Patch[]): string;
/**
* Validate that patches don't overlap
* @internal This method is kept for potential future use or manual invocation
*/
static validatePatches(patches: Patch[]): void;
}
/**
* PatchPlanner - Collect edit operations
*
* Produces Patch[] = { startOffset, endOffset, replacement }.
* Responsible for "range selection" (including blank lines, inline comment removal, etc.)
* Must guarantee patches don't overlap (or resolve overlaps deterministically).
*/
export declare class PatchPlanner {
private lineIndex;
private patches;
private originalScript;
private originalAST;
/**
* Enable defensive validation of extracted code.
* When true, extracted code is validated before use and falls back to regeneration if invalid.
* Set via ROBINPATH_DEBUG environment variable or constructor option.
*/
private debugMode;
constructor(originalScript: string, options?: {
debugMode?: boolean;
});
/**
* Plan patches for all nodes in the AST
* This includes patches for:
* - Updated nodes (in modified AST)
* - New nodes (in modified AST but not in original)
* - Deleted nodes (in original AST but not in modified)
*/
planPatches(ast: Statement[]): Promise<Patch[]>;
/**
* Plan a patch for a deleted node
*/
private planPatchForDeletedNode;
/**
* Plan a patch for a single node
*/
private planPatchForNode;
/**
* Plan a patch for a new node (insertion)
*/
private planPatchForNewNode;
/**
* Plan patch for leading comments (non-overlapping case)
*/
private planPatchForLeadingComments;
/**
* Generate replacement without leading comments (for non-overlapping case)
*/
private generateReplacementWithoutLeadingComments;
/**
* Plan patch for a standalone comment node
*/
private planPatchForCommentNode;
/**
* Find the stop row for comment blank line inclusion
*/
private findStopRowForComment;
/**
* Plan patch to remove existing comments
*/
private planPatchToRemoveComments;
/**
* Compute the range for a statement region
*/
private computeStatementRange;
/**
* Generate replacement text for a node
*/
private generateReplacement;
/**
* Preserve blank lines that were included in the range after the statement
*/
private preserveBlankLinesInRange;
/**
* Extract original code from the script if the node hasn't changed
* Returns null if the node has changed and needs regeneration
*
* This preserves all original formatting including:
* - Indentation (spaces and tabs)
* - Spacing within statements
* - Blank lines
*/
private extractOriginalCode;
/**
* Validate extracted code by checking if it parses correctly and matches expected type.
* Used in debug mode to detect extraction corruption.
*/
private validateExtractedCode;
/**
* Find the original node that corresponds to the modified node
*/
private findOriginalNode;
/**
* Compare two nodes to see if they're equal (ignoring codePos and metadata)
*/
private nodesAreEqual;
/**
* Generate code with preserved indentation and spacing from original
* This is used when we must regenerate code but want to preserve formatting
*/
private generateCodeWithPreservedIndentation;
}
/**
* ASTToCodeConverter - Converts AST nodes back to source code
*
* This class handles the conversion of AST (Abstract Syntax Tree) nodes
* back into RobinPath source code strings. It provides methods for:
* - Updating source code based on AST changes
* - Reconstructing code from individual AST nodes
* - Handling comments, indentation, and code positioning
*/
export declare class ASTToCodeConverter {
/**
* Validate that AST node positions are within bounds of the source code.
* This helps detect stale AST data from external source modifications.
*
* @param source The current source code
* @param ast The AST to validate
* @returns Object with isValid flag and optional error details
*/
private validateASTPositions;
/**
* Update source code based on AST changes

@@ -11,3 +170,3 @@ * Uses precise character-level positions (codePos.startRow/startCol/endRow/endCol) to update code

*/
updateCodeFromAST(originalScript: string, ast: Statement[]): string;
updateCodeFromAST(originalScript: string, ast: Statement[]): Promise<string>;
/**

@@ -14,0 +173,0 @@ * Reconstruct code string from an AST node

+3
-6
/**
* Code Converter Module - Exports
*/
export { ASTToCodeConverter } from './ASTToCodeConverter';
export { ASTToCodeConverter, PatchPlanner, PatchApplier, CommentLayoutNormalizer } from './ASTToCodeConverter';
export { Writer } from './Writer';
export { LineIndexImpl } from './LineIndex';
export { Writer } from './Writer';
export { Printer } from './Printer';
export { PatchPlanner } from './PatchPlanner';
export { PatchApplier } from './PatchApplier';
export { CommentLayoutNormalizer } from './CommentLayout';
export type { Patch, CommentLayout, PrintContext, LineIndex } from './types';
export type { Patch, CommentLayout, PrintContext, LineIndex, CommentWithPosition } from './types';

@@ -1,2 +0,21 @@

import { LineIndex } from './types';
/**
* LineIndex - Fast row/col → offset conversion
*/
/**
* LineIndex interface for fast row/col → offset conversion
*/
export interface LineIndex {
offsetAt(row: number, col: number, exclusive?: boolean): number;
lineEndOffset(row: number): number;
hasNewline(row: number): boolean;
getLine(row: number): string;
getLines(): string[];
lineCount(): number;
}
/**
* LineIndex implementation - Fast row/col → offset conversion
*
* Build once per originalScript. Provides O(1) offset conversion
* by pre-computing line-start offsets.
*/
export declare class LineIndexImpl implements LineIndex {

@@ -3,0 +22,0 @@ private readonly lineStartOffsets;

@@ -1,5 +0,13 @@

import { Statement, CommentWithPosition } from '../../types/Ast.type';
import { PrintContext, CommentWithPosition } from './types';
import { Value } from '../../utils/types';
import { PrintContext } from './types';
import { Statement } from '../../types/Ast.type';
import { Writer } from './Writer';
/**
* Printer class - AST → string conversion
*
* Pure function(s), no access to originalScript.
* Uses a Writer to avoid heavy string concatenations.
*/
export declare class Printer {
private static printersRegistry;
/**

@@ -14,6 +22,2 @@ * Print a statement node to code

/**
* Print an argument
*/
static printArg(arg: any, ctx: PrintContext): string | null;
/**
* Get value type

@@ -26,2 +30,90 @@ */

static convertValueType(value: Value, targetType: 'string' | 'number' | 'boolean' | 'null' | 'object' | 'array'): Value | null;
/**
* Print argument/expression code
*/
static printArg(arg: any, _ctx: PrintContext): string | null;
/**
* Print a variable reference
*/
static printVarRef(name: string, path?: any[]): string;
/**
* Print an into target
*/
static printIntoTarget(targetName: string, targetPath?: any[]): string;
/**
* Print assignment node
*/
static printAssignment(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print cell block node
*/
static printCellBlock(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print chunk marker node
*/
static printChunkMarker(node: any, writer: Writer, _ctx: PrintContext): void;
/**
* Print command node
*/
static printCommand(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print a comment node
*/
static printCommentNode(node: any, writer: Writer, _ctx: PrintContext): void;
/**
* Get leading comments from a statement node.
*/
private static getLeadingComments;
/**
* Get inline comment from a statement node.
*/
static getInlineComment(stmt: any): CommentWithPosition | null;
/**
* Format an inline comment as a string to append to a line.
*/
static formatInlineComment(comment: CommentWithPosition | null): string;
/**
* Emit decorators for a node if they exist.
*/
private static emitDecorators;
/**
* Emit leading comments for a statement, preserving blank lines between them.
*/
static emitLeadingComments(stmt: any, writer: Writer, _ctx: PrintContext, indentLevel: number): boolean;
/**
* Check if there's a blank line gap between the last comment and a statement.
*/
static emitBlankLineAfterComments(stmt: any, writer: Writer): void;
/**
* Check if there's a blank line gap between two statements.
*/
static emitBlankLineBetweenStatements(prevStmt: any, currentStmt: any, writer: Writer): void;
/**
* Print define (function definition) node
*/
static printDefine(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print do block node
*/
static printDo(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print for loop node
*/
static printForLoop(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print ifBlock node
*/
static printIfBlock(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print on block node
*/
static printOnBlock(node: any, writer: Writer, ctx: PrintContext): void;
/**
* Print prompt block node
*/
static printPromptBlock(node: any, writer: Writer, _ctx: PrintContext): void;
/**
* Print together block node
*/
static printTogether(node: any, writer: Writer, ctx: PrintContext): void;
}
import { CommentWithPosition } from '../../types/Ast.type';
export type { CommentWithPosition };
import { LineIndex } from './LineIndex';
/**

@@ -26,13 +26,5 @@ * Represents a patch operation to apply to source code

lineIndex: LineIndex;
originalScript?: string;
allowExtractOriginalCode?: boolean;
}
/**
* LineIndex interface for fast row/col → offset conversion
*/
export interface LineIndex {
offsetAt(row: number, col: number, exclusive?: boolean): number;
lineEndOffset(row: number): number;
hasNewline(row: number): boolean;
getLine(row: number): string;
getLines(): string[];
lineCount(): number;
}
export type { CommentWithPosition, LineIndex };

@@ -23,10 +23,2 @@ /**

/**
* Increase indentation by one level
*/
indentIncrement(): void;
/**
* Decrease indentation by one level
*/
indentDecrement(): void;
/**
* Push text with current indentation

@@ -33,0 +25,0 @@ */

import { Value } from '../utils';
export interface ExecutionStepInfo {
nodeKey: string;
variables: Record<string, Value>;
result: Value;
timestamp: number;
}
export interface ExecutionLog {
message: string;
nodeKey?: string;
timestamp: number;
source?: 'log' | 'say' | string;
}
export type LogCallback = (log: ExecutionLog) => void;
export declare class ExecutionStateTracker {
private state;
setState(index: number, state: {
lastValue: Value;
beforeValue: Value;
}): void;
getState(index: number): {
lastValue: Value;
beforeValue: Value;
} | undefined;
private steps;
private logs;
private nodeStates;
private indexedStates;
private onLog;
setLogCallback(callback: LogCallback | null): void;
addStep(step: ExecutionStepInfo): void;
addLog(log: ExecutionLog): void;
getSteps(): ExecutionStepInfo[];
getLogs(): ExecutionLog[];
getNodeState(nodeKey: string): ExecutionStepInfo | undefined;
getState(index: number): any;
setState(index: number, state: any): void;
clear(): void;
}

@@ -10,2 +10,5 @@ import { Value } from '../utils';

private sourceCode;
private recursionDepth;
private currentStatement;
private static readonly MAX_RECURSION_DEPTH;
/**

@@ -18,5 +21,24 @@ * Debug mode flag - set to true to enable logging

getCurrentFrame(frameOverride?: Frame): Frame;
getCurrentStatement(): Statement | null;
getEnvironment(): Environment;
getCallStack(): Frame[];
/**
* Compute Levenshtein distance between two strings
*/
private levenshteinDistance;
/**
* Find similar function names for "did you mean?" suggestions
*/
private findSimilarFunctions;
/**
* Format "Unknown function" error with suggestions
*/
private unknownFunctionError;
/**
* Creates a new Executor instance that shares the same environment and call stack,
* but has its own call stack array to allow parallel execution without stack corruption.
* The frames themselves (locals Maps) are shared.
*/
spawnChild(): Executor;
/**
* Execute an event handler with the provided arguments

@@ -37,2 +59,6 @@ * Arguments are available as $1, $2, $3, etc. in the handler body

/**
* Get all variables visible in the current frame
*/
private getVariableStateInternal;
/**
* Reconstructs the original input string from an Arg object.

@@ -166,2 +192,6 @@ * This is useful for commands that need to preserve the original input

private setVariableAtPath;
/**
* Resolve a dynamic key segment to its actual key value
*/
private resolveDynamicKey;
}

@@ -11,4 +11,7 @@ /**

export { ExecutionStateTracker } from './ExecutionStateTracker';
export type { ExecutionLog, ExecutionStepInfo, LogCallback } from './ExecutionStateTracker';
export { ReturnException, BreakException, EndException } from './exceptions';
export { RobinPathThread } from './RobinPathThread';
export { ASTToCodeConverter } from './code-converter';
export { ASTSerializer } from './ASTSerializer';
export { ASTToCodeConverter, Printer, LineIndexImpl } from './code-converter';
export type { PrintContext, LineIndex } from './code-converter';

@@ -60,2 +60,3 @@ /**

value?: any;
isContinuation?: boolean;
}

@@ -62,0 +63,0 @@ /**

@@ -1,8 +0,16 @@

import { Statement, DefineFunction, OnBlock } from '../types/Ast.type';
import { Statement, CodePosition, DefineFunction, OnBlock } from '../types/Ast.type';
import { Environment } from '../index';
export interface ExtractedVariable {
name: string;
description?: string;
initialValue?: any;
codePos: CodePosition;
}
export declare class Parser {
private tokens;
private stream;
private source;
private extractedFunctions;
private extractedEventHandlers;
private extractedVariables;
private environment;

@@ -32,10 +40,6 @@ private decoratorBuffer;

/**
* Parse a statement from a stream (for use in DefineParser)
* Parse a statement from a stream with hierarchical nodeKey support
*/
private parseStatementFromStream;
/**
* Parse a comment from a stream (for use in DefineParser)
*/
private parseCommentFromStream;
/**
* Get extracted function definitions (def/enddef blocks)

@@ -49,19 +53,21 @@ */

/**
* Parse a single statement
* Get extracted variables (from assignments)
*/
private parseStatement;
getExtractedVariables(): ExtractedVariable[];
/**
* Attach inline comments to a statement if they exist on the same line
* Track a variable from an assignment statement
*/
private attachInlineComments;
private trackVariable;
/**
* Execute parse-time decorators during parsing
* Parse decorators inject metadata into AST nodes
* Parse a single statement
*/
private executeParseDecorators;
private parseStatement;
private parseCommentFromStream;
private attachInlineComments;
private countTrailingBlankLines;
/**
* Serialize AST to a JSON-serializable format
* Handles circular references and nested structures properly
* Recursively assign nodeKeys to body statements
* This ensures all nested statements have hierarchical nodeKeys for execution tracking
*/
private serializeAST;
private assignBodyNodeKeys;
}

@@ -8,2 +8,3 @@ import { Value } from '../utils';

private parent;
private serializer;
constructor(baseEnvironment: Environment, id: string, parent?: RobinPath);

@@ -40,2 +41,6 @@ /**

/**
* Get all variables in this thread as a plain object
*/
getVariableState(): Record<string, Value>;
/**
* Get the current module name (set by "use" command)

@@ -103,18 +108,2 @@ * Returns null if no module is currently in use

/**
* Find the module name for a given function name
* Returns the module name if found, null otherwise
*/
private findModuleName;
/**
* Determine the type of a value
* @param value The value to check
* @returns The type string: 'string', 'number', 'boolean', 'null', 'object', or 'array'
*/
private getValueType;
private serializeStatement;
/**
* Serialize an argument to JSON
*/
private serializeArg;
/**
* Get all available commands, modules, and functions for this thread

@@ -146,2 +135,4 @@ * Includes thread-local user-defined functions in addition to shared builtins and modules

description: string;
author?: string;
category?: string;
}>;

@@ -148,0 +139,0 @@ moduleFunctions: Array<{

@@ -22,2 +22,5 @@ import { TokenKind, Token } from './Lexer';

private consecutiveNextCalls;
private positionHistory;
private operationsSinceLastAdvance;
private lastPositionValue;
/**

@@ -28,2 +31,7 @@ * Maximum number of consecutive next() calls allowed at the same position before throwing

/**
* Maximum number of operations without position advancement before detecting stuck
* Only counts operations that should advance (next, match, expect, etc.)
*/
static readonly MAX_OPERATIONS_WITHOUT_ADVANCE = 100;
/**
* Debug mode flag - set to true to enable logging

@@ -48,2 +56,6 @@ * Can be controlled via VITE_DEBUG environment variable or set programmatically

/**
* Reset stuck detection counters (useful when manually advancing position)
*/
resetStuckDetection(): void;
/**
* Set the current position directly

@@ -71,2 +83,8 @@ * @param position - New position

/**
* Check if the stream appears to be stuck (no position advancement)
* @param operation - Name of the operation being performed
* @throws Error if stuck condition detected
*/
private checkStuckCondition;
/**
* Check if we're at the end of the token stream

@@ -73,0 +91,0 @@ * @returns True if at EOF or beyond

import { extractNamedArgs, Value, AttributePathSegment } from './utils';
import { RobinPathThread } from './classes';
import { RobinPathThread, ExecutionStateTracker, ExecutionLog } from './classes';
import { Statement, Arg, DefineFunction, OnBlock } from './types/Ast.type';

@@ -9,2 +9,3 @@ /**

*/
export declare const ROBINPATH_VERSION = "0.30.0";
export interface ModuleAdapter {

@@ -17,4 +18,8 @@ name: string;

}
export { Parser, Printer, LineIndexImpl } from './classes';
export type { PrintContext, LineIndex } from './classes';
export { formatErrorWithContext, createErrorWithContext } from './utils';
export type { ExecutionLog, ExecutionStepInfo, LogCallback } from './classes';
export type { Value, AttributePathSegment };
export type { Statement, Arg, CommandCall, Assignment, ShorthandAssignment, InlineIf, IfBlock, IfTrue, IfFalse, DefineFunction, ScopeBlock, TogetherBlock, ForLoop, ReturnStatement, BreakStatement, OnBlock, CommentStatement, CommentWithPosition, CodePosition, DecoratorCall } from './types/Ast.type';
export type { Statement, Arg, CommandCall, Assignment, ShorthandAssignment, InlineIf, IfBlock, IfTrue, IfFalse, DefineFunction, ScopeBlock, TogetherBlock, ForLoop, ReturnStatement, BreakStatement, OnBlock, CommentStatement, ChunkMarkerStatement, CellBlock, PromptBlockStatement, CommentWithPosition, CodePosition, DecoratorCall, } from './types/Ast.type';
export type BuiltinCallback = (callbackArgs: Value[]) => Promise<Value> | Value | null;

@@ -37,2 +42,3 @@ export type BuiltinHandler = (args: Value[], callback?: BuiltinCallback | null) => Promise<Value> | Value | null;

eventHandlers: Map<string, OnBlock[]>;
stateTracker?: ExecutionStateTracker;
}

@@ -47,4 +53,4 @@ export interface Frame {

export { extractNamedArgs };
export type DataType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null' | 'any';
export type FormInputType = 'text' | 'number' | 'textarea' | 'select' | 'checkbox' | 'radio' | 'date' | 'datetime' | 'file' | 'json' | 'code';
export type DataType = "string" | "number" | "boolean" | "object" | "array" | "null" | "any";
export type FormInputType = "text" | "number" | "textarea" | "select" | "checkbox" | "radio" | "date" | "datetime" | "file" | "json" | "code" | "varname";
export interface ParameterMetadata {

@@ -58,2 +64,3 @@ name: string;

defaultValue?: Value;
allowedTypes?: string[];
children?: ParameterMetadata;

@@ -71,7 +78,12 @@ }

methods: string[];
author?: string;
category?: string;
doc_url?: string;
}
export declare class RobinPath {
private static versionLogged;
private environment;
private persistentExecutor;
private lastExecutor;
private activeExecutor;
private threads;

@@ -82,2 +94,4 @@ private currentThread;

private astToCodeConverter;
private serializer;
private stateTracker;
constructor(options?: {

@@ -87,2 +101,19 @@ threadControl?: boolean;

/**
* Get execution steps from the last execution
*/
getExecutionSteps(): import('./classes').ExecutionStepInfo[];
/**
* Get execution logs from the last execution
*/
getExecutionLogs(): ExecutionLog[];
/**
* Set a callback for real-time log notifications
* The callback is called immediately when a log is added during execution
*/
setLogCallback(callback: ((log: ExecutionLog) => void) | null): void;
/**
* Clear execution state
*/
clearExecutionState(): void;
/**
* Native modules registry

@@ -265,2 +296,4 @@ * Add new modules here to auto-load them

description: string;
author?: string;
category?: string;
}>;

@@ -285,3 +318,3 @@ moduleFunctions: Array<{

needsMore: boolean;
waitingFor?: 'endif' | 'enddef' | 'endfor' | 'enddo' | 'endon' | 'subexpr' | 'paren' | 'object' | 'array';
waitingFor?: "endif" | "enddef" | "endfor" | "enddo" | "endon" | "subexpr" | "paren" | "object" | "array";
}>;

@@ -303,21 +336,24 @@ /**

/**
* Find the module name for a given function name
* Returns the module name if found, null otherwise
* @param functionName The function name to look up
* @param currentModuleContext Optional module context from "use" command (for getAST)
*/
private findModuleName;
/**
* Serialize a statement to JSON without execution state
* Serialize a statement to JSON
* @param stmt The statement to serialize
* @param currentModuleContext Optional module context from "use" command (for getAST)
* @param currentModuleContext Optional module context
* @param lastValue Optional execution state
* @param nodeKey Optional node key
*/
private serializeStatement;
/**
* Determine the type of a value
* @param value The value to check
* @returns The type string: 'string', 'number', 'boolean', 'null', 'object', or 'array'
* Get the complete structure of the script in a tree format
* Includes main body, functions, event handlers, and variables
*/
private getValueType;
getScriptStructure(script: string): Promise<any>;
/**
* Get extracted variables from the script (parsed, not executed)
* Returns variable names with optional description and initial value
*/
getExtractedVariables(script: string): Promise<Array<{
name: string;
description?: string;
initialValue?: any;
}>>;
/**
* Update source code based on AST changes

@@ -330,7 +366,10 @@ * Uses precise character-level positions (codePos.startRow/startCol/endRow/endCol) to update code

*/
updateCodeFromAST(originalScript: string, ast: any[]): string;
updateCodeFromAST(originalScript: string, ast: any[]): Promise<string>;
/**
* Serialize an argument to JSON
* Reconstruct code from an AST node
* @param node The AST node (serialized)
* @param indentLevel Indentation level for nested code
* @returns Reconstructed code string, or null if cannot be reconstructed
*/
private serializeArg;
reconstructCodeFromASTNode(node: any, indentLevel?: number): string | null;
/**

@@ -442,2 +481,6 @@ * Execute a RobinPath script

/**
* Get all variables as a plain object
*/
getVariableState(): Record<string, Value>;
/**
* Get the next statement index that would execute after a given statement.

@@ -452,5 +495,5 @@ * This method analyzes the AST structure to determine execution flow.

getNextStatementIndex(statements: Statement[], currentIndex: number, context?: {
ifBlockBranch?: 'then' | 'elseif' | 'else' | null;
ifBlockBranch?: "then" | "elseif" | "else" | null;
forLoopIteration?: number;
}): number;
}

@@ -25,6 +25,14 @@ import { TokenStream } from '../classes/TokenStream';

/**
* Parse a simple fallback value (string, number, boolean, null)
*/
private static parseFallbackValue;
/**
* Parse the value part of an assignment
* Returns the assignment data and the end token
*/
/**
* Check if there's a binary operator ahead that would indicate we need parseExpression
*/
private static isBinaryOperatorAhead;
private static parseAssignmentValue;
}

@@ -50,10 +50,8 @@ import { TokenStream } from '../classes/TokenStream';

* Parse "into $var" syntax
* Handles "into" on the same line or next line (for multiline calls)
*/
private static parseInto;
/**
* Parse callback block (with only - do blocks are standalone statements, not callbacks)
* Only accepts callbacks on the same line as the command (does not skip newlines)
* Parse callback block (with only)
*/
private static parseCallback;
}
import { TokenStream } from '../classes/TokenStream';
import { DefineFunction, Statement, DecoratorCall } from '../types/Ast.type';
import { Statement, DefineFunction, DecoratorCall } from '../types/Ast.type';
import { Environment } from '../index';

@@ -16,3 +16,4 @@ export declare class DefineParser {

*/
static parse(stream: TokenStream, parseStatement: (stream: TokenStream) => Statement | null, parseComment: (stream: TokenStream) => Statement | null, decorators?: DecoratorCall[], environment?: Environment | null): Promise<DefineFunction>;
static parse(stream: TokenStream, parseStatement: (stream: TokenStream) => Statement | null, _parseComment: (stream: TokenStream) => Statement | null, // eslint-disable-line @typescript-eslint/no-unused-vars
decorators?: DecoratorCall[], environment?: Environment | null): Promise<DefineFunction>;
}
import { TokenStream } from '../classes/TokenStream';
import { OnBlock, Statement, DecoratorCall } from '../types/Ast.type';
import { Statement, OnBlock, DecoratorCall } from '../types/Ast.type';
import { Environment } from '../index';

@@ -4,0 +4,0 @@ export declare class EventParser {

@@ -23,3 +23,3 @@ import { TokenStream } from '../classes/TokenStream';

*/
static parse(stream: TokenStream, parseStatement: (stream: TokenStream) => Statement | null, parseComment: (stream: TokenStream) => Statement | null, decorators?: DecoratorCall[]): ScopeBlock;
static parse(stream: TokenStream, parseStatement: (stream: TokenStream) => Statement | null, _parseComment: (stream: TokenStream) => Statement | null, decorators?: DecoratorCall[]): ScopeBlock;
}

@@ -17,6 +17,6 @@ import { TokenStream } from '../classes/TokenStream';

* Parse a subexpression from TokenStream
* Expects stream to be positioned at the '$' token
* Expects stream to be positioned at the '$(' token
* Syntax: $( ... )
*
* @param stream - TokenStream positioned at the '$' token
* @param stream - TokenStream positioned at the '$(' token
* @param context - Context with helper methods

@@ -29,3 +29,2 @@ * @returns Parsed SubexpressionExpression

* Subexpressions must start with SUBEXPRESSION_OPEN ($()
* Regular parentheses ( ) are handled by other parsers
*

@@ -32,0 +31,0 @@ * @param stream - TokenStream to check

@@ -24,4 +24,24 @@ import { Value, AttributePathSegment } from '../utils/types';

type: 'comment';
nodeKey?: string;
comments: CommentWithPosition[];
lineNumber: number;
/**
* Number of blank lines after this statement (for preserving formatting).
*
* **Semantics:**
* - `undefined` or `0`: No trailing blank lines (statement immediately followed by next)
* - `1`: One blank line after statement (two consecutive newlines: `\n\n`)
* - `2`: Two blank lines after statement (three consecutive newlines: `\n\n\n`)
* - ... and so on
*
* **For the LAST statement in a file:**
* - `undefined` or `0`: File does NOT end with a newline
* - `1`: File ends with exactly one newline (no blank line at EOF)
* - `2`: File ends with one blank line (two newlines at EOF)
* - ... and so on
*
* **Note:** The statement's own ending newline is NOT counted in trailingBlankLines.
* Each statement implicitly ends with `\n`. This field only tracks EXTRA blank lines.
*/
trailingBlankLines?: number;
}

@@ -177,5 +197,7 @@ /**

type: 'command';
nodeKey?: string;
name: string;
args: Arg[];
syntaxType?: 'space' | 'parentheses' | 'named-parentheses' | 'multiline-parentheses';
isTaggedTemplate?: boolean;
decorators?: DecoratorCall[];

@@ -188,2 +210,3 @@ into?: {

comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -196,2 +219,3 @@ }

type: 'assignment';
nodeKey?: string;
targetName: string;

@@ -203,4 +227,12 @@ targetPath?: AttributePathSegment[];

isLastValue?: boolean;
isSet?: boolean;
isVar?: boolean;
isConst?: boolean;
hasAs?: boolean;
isImplicit?: boolean;
fallbackValue?: Value;
fallbackValueType?: 'string' | 'number' | 'boolean' | 'null';
comments?: CommentWithPosition[];
decorators?: DecoratorCall[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -213,4 +245,6 @@ }

type: 'shorthand';
nodeKey?: string;
targetName: string;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -223,2 +257,3 @@ }

type: 'inlineIf';
nodeKey?: string;
condition: Expression;

@@ -228,2 +263,3 @@ command: Statement;

comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -236,11 +272,17 @@ }

type: 'ifBlock';
nodeKey?: string;
condition: Expression;
thenBranch: Statement[];
elseBranch?: Statement[];
elseHasThen?: boolean;
elseKeywordPos?: CodePosition;
elseifBranches?: Array<{
condition: Expression;
body: Statement[];
hasThen?: boolean;
keywordPos?: CodePosition;
}>;
decorators?: DecoratorCall[];
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -253,4 +295,6 @@ }

type: 'ifTrue';
nodeKey?: string;
command: Statement;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -263,4 +307,6 @@ }

type: 'ifFalse';
nodeKey?: string;
command: Statement;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -273,2 +319,3 @@ }

type: 'define';
nodeKey?: string;
name: string;

@@ -279,2 +326,3 @@ paramNames: string[];

comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -287,2 +335,3 @@ }

type: 'do';
nodeKey?: string;
paramNames?: string[];

@@ -296,2 +345,3 @@ body: Statement[];

comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -304,5 +354,7 @@ }

type: 'together';
nodeKey?: string;
blocks: ScopeBlock[];
decorators?: DecoratorCall[];
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -315,7 +367,13 @@ }

type: 'forLoop';
nodeKey?: string;
varName: string;
iterable: Expression;
iterable?: Expression;
from?: Expression;
to?: Expression;
step?: Expression;
keyVarName?: string;
body: Statement[];
decorators?: DecoratorCall[];
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -328,4 +386,6 @@ }

type: 'return';
nodeKey?: string;
value?: Arg;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -338,3 +398,5 @@ }

type: 'break';
nodeKey?: string;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -347,3 +409,5 @@ }

type: 'continue';
nodeKey?: string;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;

@@ -356,2 +420,3 @@ }

type: 'onBlock';
nodeKey?: string;
eventName: string;

@@ -361,7 +426,52 @@ body: Statement[];

comments?: CommentWithPosition[];
trailingBlankLines?: number;
codePos: CodePosition;
}
/**
* Represents a cell block (---cell <cellType> <meta...>--- ... ---end---)
*/
export interface CellBlock {
type: 'cell';
nodeKey?: string;
cellType: string;
meta: Record<string, string>;
rawBody: string;
body?: Statement[];
headerPos: CodePosition;
bodyPos: CodePosition;
codePos: CodePosition;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
}
/**
* Represents a prompt block statement (--- ... ---)
*/
export interface PromptBlockStatement {
type: 'prompt_block';
nodeKey?: string;
rawText: string;
fence: '---';
codePos: CodePosition;
openPos: CodePosition;
bodyPos: CodePosition;
closePos: CodePosition;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
}
/**
* Represents a chunk marker statement (--- chunk:<id> ---)
*/
export interface ChunkMarkerStatement {
type: 'chunk_marker';
nodeKey?: string;
id: string;
meta?: Record<string, string>;
codePos: CodePosition;
raw?: string;
comments?: CommentWithPosition[];
trailingBlankLines?: number;
}
/**
* Union type representing all possible statement types in the AST
*/
export type Statement = CommandCall | Assignment | ShorthandAssignment | InlineIf | IfBlock | IfTrue | IfFalse | DefineFunction | ScopeBlock | TogetherBlock | ForLoop | ReturnStatement | BreakStatement | ContinueStatement | OnBlock | CommentStatement;
export type Statement = CommandCall | Assignment | ShorthandAssignment | InlineIf | IfBlock | IfTrue | IfFalse | DefineFunction | ScopeBlock | TogetherBlock | ForLoop | ReturnStatement | BreakStatement | ContinueStatement | OnBlock | CommentStatement | ChunkMarkerStatement | CellBlock | PromptBlockStatement;

@@ -32,4 +32,11 @@ import { AttributePathSegment } from './types';

};
/**
* Parse a dynamic key from inside brackets: [$varName] or [$varName.property]
* @param str The full string being parsed
* @param pos Position right after the '$' character
* @returns The parsed segment and the position after the closing ']'
*/
private static parseDynamicKey;
static isLastValue(token: string): boolean;
static isPositionalParam(token: string): boolean;
}

@@ -11,2 +11,6 @@ /**

index: number;
} | {
type: 'dynamicKey';
variable: string;
varPath?: AttributePathSegment[];
};

@@ -15,1 +15,12 @@ import { Value } from './types';

export declare function isTruthy(val: Value): boolean;
/**
* Get the type of a value
*/
export declare function getValueType(value: Value): 'string' | 'number' | 'boolean' | 'null' | 'object' | 'array';
/**
* Attempt to convert a value to a different type
* @param value The value to convert
* @param targetType The target type to convert to
* @returns The converted value, or null if conversion fails
*/
export declare function convertValueType(value: Value, targetType: 'string' | 'number' | 'boolean' | 'null' | 'object' | 'array'): Value | null;
{
"name": "@wiredwp/robinpath",
"description": "RobinPath - A lightweight, fast, and easy-to-use scripting language for automation and data processing.",
"version": "0.30.0",
"version": "0.30.1",
"author": "Ryan Lee <ryan@wiredwp.com>",

@@ -24,3 +24,7 @@ "type": "module",

"test": "npm run build && node test/run.js",
"test:coverage": "npm run build && c8 node test/run.js",
"test:coverage:report": "c8 report --reporter=html --reporter=text",
"test:watch": "nodemon --watch src --watch test --ext ts,js,robin --exec 'npm run test'",
"test-perform": "npm run build && node test/run-performance.js",
"test-code": "npm run build && node test/test-code.js",
"cli": "npm run build && node bin/robinpath.js"

@@ -38,2 +42,4 @@ },

"devDependencies": {
"c8": "^10.1.3",
"nodemon": "^3.1.9",
"typescript": "~5.9.3",

@@ -40,0 +46,0 @@ "vite": "^7.2.4",

@@ -655,13 +655,22 @@ # RobinPath

```robinpath
for $i in range 1 5
# Iterate over a range
for $i from 1 to 5
log "Iteration:" $i
endfor
```
**For loop with array:**
```robinpath
$numbers = range 10 12
for $num in $numbers
log "Number:" $num
# Iterate with custom step and key (index)
for $a from 0 to 10 step 2 key $index
log "value: " + $a + ", index: " + $index
endfor
# Range parameters can be in any order
for $x by 5 to 100 from 0
log $x
endfor
# Iterate over an array
$numbers = [10, 11, 12]
for $num in $numbers key $i
log "Number " + $i + ": " + $num
endfor
```

@@ -668,0 +677,0 @@

/**
* ASTToCodeConverter - Converts AST nodes back to source code
*
* This class handles the conversion of AST (Abstract Syntax Tree) nodes
* back into RobinPath source code strings. It provides methods for:
* - Updating source code based on AST changes
* - Reconstructing code from individual AST nodes
* - Handling comments, indentation, and code positioning
*/
export declare class ASTToCodeConverter {
/**
* Update source code based on AST changes
* Uses precise character-level positions (codePos.startRow/startCol/endRow/endCol) to update code
* Nested nodes are reconstructed as part of their parent's code
* @param originalScript The original source code
* @param ast The modified AST array (top-level nodes only)
* @returns Updated source code
*/
updateCodeFromAST(originalScript: string, ast: any[]): string;
/**
* Reconstruct code string from an AST node
* @param node The AST node (serialized)
* @param indentLevel Indentation level for nested code
* @returns Reconstructed code string, or null if cannot be reconstructed
*/
private reconstructCodeFromASTNode;
/**
* Reconstruct code string from an Arg object
*/
private reconstructArgCode;
/**
* Reconstruct comment code from a CommentWithPosition object
*/
private reconstructCommentCode;
/**
* Convert row/column position to character offset in script
* @param script The script string
* @param row Zero-based row number
* @param col Zero-based column number
* @param exclusive If true, return offset one past the column (for end positions)
* @returns Character offset in the script string
*/
private rowColToCharOffset;
/**
* Get the type of a value
*/
private getValueType;
/**
* Attempt to convert a value to a different type
* @param value The value to convert
* @param targetType The target type to convert to
* @returns The converted value, or null if conversion fails
*/
private convertValueType;
}
import { Statement } from '../../types/Ast.type';
import { CommentLayout as CommentLayoutType, LineIndex } from './types';
export declare class CommentLayoutNormalizer {
/**
* Normalize comment layout for a statement
*/
static normalize(node: Statement, lineIndex: LineIndex): CommentLayoutType;
}
import { Patch } from './types';
export declare class PatchApplier {
/**
* Apply patches to source code
* Patches are sorted descending by startOffset and applied from end to start
* to prevent character position shifts from affecting subsequent replacements
*/
static apply(originalScript: string, patches: Patch[]): string;
/**
* Validate that patches don't overlap
* @internal This method is kept for potential future use or manual invocation
*/
static validatePatches(patches: Patch[]): void;
}
import { Statement } from '../../types/Ast.type';
import { Patch } from './types';
export declare class PatchPlanner {
private lineIndex;
private patches;
constructor(originalScript: string);
/**
* Plan patches for all nodes in the AST
*/
planPatches(ast: Statement[]): Patch[];
/**
* Plan a patch for a single node
*/
private planPatchForNode;
/**
* Plan patch for leading comments (non-overlapping case)
*/
private planPatchForLeadingComments;
/**
* Generate replacement without leading comments (for non-overlapping case)
*/
private generateReplacementWithoutLeadingComments;
/**
* Plan patch for a standalone comment node
*/
private planPatchForCommentNode;
/**
* Find the stop row for comment blank line inclusion
*/
private findStopRowForComment;
/**
* Plan patch to remove existing comments
*/
private planPatchToRemoveComments;
/**
* Compute the range for a statement region
*/
private computeStatementRange;
/**
* Generate replacement text for a node
*/
private generateReplacement;
}
import { PrintContext } from '../types';
export declare function printArg(arg: any, _ctx: PrintContext): string | null;
/**
* Print a variable reference
*/
export declare function printVarRef(name: string, path?: any[]): string;
/**
* Print an into target
*/
export declare function printIntoTarget(targetName: string, targetPath?: any[]): string;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printAssignment(node: any, writer: Writer, ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printCommand(node: any, writer: Writer, ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printComment(node: any, writer: Writer, _ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printDefine(node: any, writer: Writer, ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printDo(node: any, writer: Writer, ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printForLoop(node: any, writer: Writer, ctx: PrintContext): void;
import { PrintContext } from '../types';
import { Writer } from '../Writer';
export declare function printIfBlock(node: any, writer: Writer, ctx: PrintContext): void;

Sorry, the diff of this file is too big to display