@@ -58,9 +58,109 @@ export interface FunctionTable {
5858 [ functionName : string ] : FunctionSignature ;
5959}
6060
61- export class Runtime {
61+ // Built-in function names for TypeScript 5.x template literal type checking
62+ export type BuiltInFunctionNames =
63+ | 'abs'
64+ | 'avg'
65+ | 'ceil'
66+ | 'contains'
67+ | 'ends_with'
68+ | 'find_first'
69+ | 'find_last'
70+ | 'floor'
71+ | 'from_items'
72+ | 'group_by'
73+ | 'items'
74+ | 'join'
75+ | 'keys'
76+ | 'length'
77+ | 'lower'
78+ | 'map'
79+ | 'max'
80+ | 'max_by'
81+ | 'merge'
82+ | 'min'
83+ | 'min_by'
84+ | 'not_null'
85+ | 'pad_left'
86+ | 'pad_right'
87+ | 'replace'
88+ | 'reverse'
89+ | 'sort'
90+ | 'sort_by'
91+ | 'split'
92+ | 'starts_with'
93+ | 'sum'
94+ | 'to_array'
95+ | 'to_number'
96+ | 'to_string'
97+ | 'type'
98+ | 'upper'
99+ | 'values'
100+ | 'zip' ;
101+
102+ // Registration options for enhanced registerFunction behavior
103+ export interface RegisterOptions {
104+ /**
105+ * Allow overriding existing functions. Default: false
106+ * When true, replaces existing function without error
107+ * When false, throws error if function already exists (backward compatible)
108+ */
109+ override ?: boolean ;
110+ /**
111+ * Emit warning when overriding existing functions. Default: false
112+ * Only applies when override is true
113+ */
114+ warn ?: boolean ;
115+ }
116+
117+ // Registration result for better error handling and introspection
118+ export type RegistrationResult =
119+ | { success : true ; message ?: string }
120+ | { success : false ; reason : 'already-exists' | 'invalid-signature' | 'invalid-name' ; message : string } ;
121+
122+ // Enhanced function registry interface for state management
123+ export interface FunctionRegistry {
124+ /**
125+ * Register a new function with optional override behavior
126+ */
127+ register < T extends string > (
128+ name : T extends BuiltInFunctionNames ? never : T ,
129+ func : RuntimeFunction < ( JSONValue | ExpressionNode ) [ ] , JSONValue > ,
130+ signature : InputSignature [ ] ,
131+ options ?: RegisterOptions ,
132+ ) : RegistrationResult ;
133+
134+ /**
135+ * Unregister a custom function (built-in functions cannot be unregistered)
136+ */
137+ unregister < T extends string > ( name : T extends BuiltInFunctionNames ? never : T ) : boolean ;
138+
139+ /**
140+ * Check if a function is registered
141+ */
142+ isRegistered ( name : string ) : boolean ;
143+
144+ /**
145+ * Get list of all registered function names
146+ */
147+ getRegistered ( ) : string [ ] ;
148+
149+ /**
150+ * Get list of custom (non-built-in) function names
151+ */
152+ getCustomFunctions ( ) : string [ ] ;
153+
154+ /**
155+ * Clear all custom functions (built-in functions remain)
156+ */
157+ clearCustomFunctions ( ) : void ;
158+ }
159+
160+ export class Runtime implements FunctionRegistry {
62161 _interpreter : TreeInterpreter ;
63162 _functionTable : FunctionTable ;
163+ private _customFunctions : Set < string > = new Set ( ) ;
64164 TYPE_NAME_TABLE : { [ InputArgument : number ] : string } = {
65165 [ InputArgument . TYPE_NUMBER ] : 'number' ,
66166 [ InputArgument . TYPE_ANY ] : 'any' ,
@@ -81,18 +181,139 @@ export class Runtime {
81181 this . _functionTable = this . functionTable ;
82182 }
83183
184+ /**
185+ * Enhanced registerFunction with backward compatibility and new options
186+ * @deprecated Use register() method for enhanced functionality
187+ */
84188 registerFunction (
85189 name : string ,
86190 customFunction : RuntimeFunction < ( JSONValue | ExpressionNode ) [ ] , JSONValue > ,
87191 signature : InputSignature [ ] ,
192+ options ?: RegisterOptions ,
88193 ) : void {
89- if ( name in this . _functionTable ) {
90- throw new Error ( `Function already defined: ${ name } ()` ) ;
194+ // For backward compatibility, we bypass the type checking here
195+ // The register method will still validate the function name at runtime
196+ const result = this . _registerInternal ( name , customFunction , signature , options ) ;
197+ if ( ! result . success ) {
198+ throw new Error ( result . message ) ;
91199 }
200+ }
201+
202+ /**
203+ * Internal registration method that bypasses TypeScript type checking
204+ */
205+ private _registerInternal (
206+ name : string ,
207+ customFunction : RuntimeFunction < ( JSONValue | ExpressionNode ) [ ] , JSONValue > ,
208+ signature : InputSignature [ ] ,
209+ options : RegisterOptions = { } ,
210+ ) : RegistrationResult {
211+ // Validate function name
212+ if ( ! name || typeof name !== 'string' || name . trim ( ) === '' ) {
213+ return {
214+ success : false ,
215+ reason : 'invalid-name' ,
216+ message : 'Function name must be a non-empty string' ,
217+ } ;
218+ }
219+
220+ // Validate signature
221+ try {
222+ this . validateInputSignatures ( name , signature ) ;
223+ } catch ( error ) {
224+ return {
225+ success : false ,
226+ reason : 'invalid-signature' ,
227+ message : error instanceof Error ? error . message : 'Invalid function signature' ,
228+ } ;
229+ }
230+
231+ const { override = false , warn = false } = options ;
232+ const exists = name in this . _functionTable ;
233+
234+ // Handle existing function
235+ if ( exists && ! override ) {
236+ return {
237+ success : false ,
238+ reason : 'already-exists' ,
239+ message : `Function already defined: ${ name } (). Use { override: true } to replace it.` ,
240+ } ;
241+ }
242+
243+ // Emit warning if requested
244+ if ( exists && override && warn ) {
245+ console . warn ( `Warning: Overriding existing function: ${ name } ()` ) ;
246+ }
247+
248+ // Register the function
92249 this . _functionTable [ name ] = {
93250 _func : customFunction . bind ( this ) ,
94251 _signature : signature ,
95252 } ;
253+
254+ // Track custom functions (exclude built-ins)
255+ this . _customFunctions . add ( name ) ;
256+
257+ const message = exists
258+ ? `Function ${ name } () overridden successfully`
259+ : `Function ${ name } () registered successfully` ;
260+ return { success : true , message } ;
261+ }
262+
263+ /**
264+ * Register a new function with enhanced options and type safety
265+ */
266+ register < T extends string > (
267+ name : T extends BuiltInFunctionNames ? never : T ,
268+ customFunction : RuntimeFunction < ( JSONValue | ExpressionNode ) [ ] , JSONValue > ,
269+ signature : InputSignature [ ] ,
270+ options : RegisterOptions = { } ,
271+ ) : RegistrationResult {
272+ return this . _registerInternal ( name , customFunction , signature , options ) ;
273+ }
274+
275+ /**
276+ * Unregister a custom function (built-in functions cannot be unregistered)
277+ */
278+ unregister < T extends string > ( name : T extends BuiltInFunctionNames ? never : T ) : boolean {
279+ if ( ! this . _customFunctions . has ( name ) ) {
280+ return false ; // Function doesn't exist or is built-in
281+ }
282+
283+ delete this . _functionTable [ name ] ;
284+ this . _customFunctions . delete ( name ) ;
285+ return true ;
286+ }
287+
288+ /**
289+ * Check if a function is registered
290+ */
291+ isRegistered ( name : string ) : boolean {
292+ return name in this . _functionTable ;
293+ }
294+
295+ /**
296+ * Get list of all registered function names
297+ */
298+ getRegistered ( ) : string [ ] {
299+ return Object . keys ( this . _functionTable ) ;
300+ }
301+
302+ /**
303+ * Get list of custom (non-built-in) function names
304+ */
305+ getCustomFunctions ( ) : string [ ] {
306+ return Array . from ( this . _customFunctions ) ;
307+ }
308+
309+ /**
310+ * Clear all custom functions (built-in functions remain)
311+ */
312+ clearCustomFunctions ( ) : void {
313+ for ( const name of this . _customFunctions ) {
314+ delete this . _functionTable [ name ] ;
315+ }
316+ this . _customFunctions . clear ( ) ;
96317 }
97318
98319 callFunction ( name : string , resolvedArgs : ( JSONValue | ExpressionNode ) [ ] ) : JSONValue {
0 commit comments