feat: context-aware functions and generic context typing#40
Open
danfry1 wants to merge 3 commits into
Open
Conversation
Adds addContextFunction(name, fn) so registered functions can read the evaluation context as their first parameter, alongside the existing addFunction for pure functions. Makes bonsai<TCtx>() generic over context type, propagating end-to-end through evaluate, evaluateSync, compile, addContextFunction, and BonsaiPlugin. The context type generic is constrained to BonsaiContext (object) so primitive generics are rejected at compile time. EvaluationContextArgs makes the context argument required at call sites when TCtx has required fields and optional otherwise, catching missing-context bugs without breaking the untyped default path. Plugins typed against a narrower context apply cleanly to wider-context instances; mismatched plugins are rejected at compile time. Context functions receive a shallow-frozen copy of the context lazily created once per top-level evaluation and shared across all context-function calls within that evaluation. Pure-function call sites are unaffected; benchmarks show no regression. Closes #33. Co-authored-by: jaenyf <95911656+jaenyf@users.noreply.github.com>
CI activates oxlint-tsgolint (already in bun.lock as an optional peer of oxlint), which adds 8 extra rules that were not running locally. This commit clears the resulting violations: 4 mine and 3 pre-existing across the codebase, plus the matching u-flag regex warnings. - Drop unused type imports (ValidationResult, TransformFn, FunctionFn) from src/index.ts; they were already re-exported via export type. - Rename _shared -> sharedInstance in src/index.ts. - Rename __dirname -> scriptDir in scripts/check-package.ts. - Rename _typed / _untyped -> typed / untyped in the type tests. - Add the u flag to every regex literal across src, scripts, and tests.
Updates the website docs, landing page, and LLM-readable docs for the new addContextFunction API and bonsai<TCtx>() context-type generic. - New "Context-aware functions" section in docs.html with usage, frozen context semantics, namespace behavior, and the isContextFunction introspection helper. - Typed-generics section expanded with bonsai<TCtx>() examples and a note that required context fields make the context argument required at call sites. - Instance Methods table gains addContextFunction and isContextFunction with updated descriptions for the shared pure/context namespace. - Common Tasks card surfaces addContextFunction alongside addFunction. - llms.txt and llms-full.txt mirror the new API for LLM consumers. - Landing page feature card mentions context-aware functions. Playground is unchanged. It evaluates expressions against ad-hoc context only, so no registration UI is involved.
|
Hi Dan, many thanks ! This implementation is far better than my quick one.
Appart from this, my current usage scenario should be fulfilled with this current implementation and I should be happy with it. So thanks again. Have a lovely rest of Ascension Day |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #33.
Two coordinated additions, shipped together so the typed API lands polished from day one:
addContextFunction(name, fn)— register a function that reads the evaluation context as its first parameter, alongside the existingaddFunctionfor pure functions.bonsai<TCtx>()— make the factory generic over context type, with end-to-end type safety throughevaluate,evaluateSync,compile,addContextFunction, andBonsaiPlugin.Design highlights
thisbinding. Context flows in as a regular first parameter; arrow functions work without ceremony.addFunction) and context-aware (addContextFunction) functions share one namespace. Last registration wins.isContextFunction(name)introspects the kind.BonsaiContextconstraint.TCtx extends object, sobonsai<number>()is a compile error.EvaluationContextArgs. WhenTCtxhas required fields, thecontextargument is required at call sites; when it has only optional fields (or is the default),contextstays optional. Catches missing-context bugs at compile time without breaking the untyped default path.use<TPluginCtx>(plugin: TCtx extends TPluginCtx ? BonsaiPlugin<TPluginCtx> : never)lets plugins typed against a minimal context apply cleanly to instances with a wider context shape, and rejects mismatched plugins at compile time.resolveCallablereturns{ kind: 'pure' | 'context', fn }. Both sync and async evaluator paths share the same call-site logic so they cannot diverge.Backward compatibility
Fully backward compatible. Every existing call site keeps working:
bonsai()(untyped) defaultsTCtxtoRecord<string, unknown>andcontextstays optional everywhere.addFunction,addTransform,evaluate,evaluateSync,compile,BonsaiPlugin,CompiledExpressionkeep their existing public shapes via defaulted generics.Performance
Bundled three function-related registry maps into a single cached
Bindingssnapshot to keep the evaluator signature compact. The snapshot is rebuilt only when a registration changes, so the hot path pays zero per-call cost.Measured on this branch (Apple Silicon):
id(1)hasPermission("write")with 4-field contextevaluateSyncliteral (full path)evaluateSyncproperty accessThe lazy freeze means pure-function-only evaluations never pay the shallow-copy cost.
Tests
41 new tests covering:
hasFunction,removeFunction,listFunctions,isContextFunction)@ts-expect-errorfor wrong shapes, required-context enforcement, non-object generic rejection, plugin-context narrowing)Credits
Thanks to @jaenyf for raising #33 and contributing PR #34.
Test plan
bun run typecheckcleanbun run test— 816 passing (was 796 baseline)bun run lint— 0 warningsbun run buildcleanbun run bench— no regression on existing benchmarksbenchmarks/context-functions.bench.tsdocuments the feature's perf envelope