Skip to content

fix: draft() can't resolve TypeScript helper functions defined in logic/logic.ts (#147)#150

Open
mttrbrts wants to merge 1 commit into
mainfrom
fix/147-draft-logic-helpers
Open

fix: draft() can't resolve TypeScript helper functions defined in logic/logic.ts (#147)#150
mttrbrts wants to merge 1 commit into
mainfrom
fix/147-draft-logic-helpers

Conversation

@mttrbrts
Copy link
Copy Markdown
Member

Summary

Fixes #147.

TemplateArchiveProcessor.draft() was compiling each inline formula
({{% expr %}}) with a TypeScript context that did not include
the template's user code from logic/logic.ts. As a result, helper
functions like monthlyPaymentFormula(...) produced
Cannot find name 'monthlyPaymentFormula' (TS2304) and drafting failed
on otherwise valid templates (repro: cicero-template-library PR #484
src/fixed-interests).

trigger() and init() were already loading logic/logic.ts via
TypeScriptToJavaScriptCompiler. They just didn't share that work with
draft().

Fix

  • Extracted the existing trigger/init compilation into a shared helper
    compileUserLogic (src/UserLogic.ts). It returns the compiled JS
    along with the top-level identifiers (function, class,
    const/let/var) it declares and a copy of that JS with import/
    export syntax stripped so it can be spliced into a new Function
    body.
  • TypeScriptCompilationContext now emits declare const <name>: any;
    for each declared identifier, so user helpers type-check inside
    formula expressions.
  • TemplateMarkInterpreter accepts a CompiledUserLogic and threads
    the runtime prelude through to the JS formula evaluator, so the same
    identifiers are real values at evaluation time.
  • TemplateArchiveProcessor:
    • draft() now calls compileLogic() and passes the result into the
      interpreter.
    • trigger() and init() were refactored to call the same
      compileLogic() helper, so all three code paths now load logic via
      the same path.

Repro / test

Added test/archives/formula-uses-logic-helper: a minimal template
fixture whose grammar contains {{% return calc(penaltyPercentage); %}}
where calc is a helper function declared in logic/logic.ts.

The new test in test/TemplateArchiveProcessor.test.ts:

  • Fails on main with TS2304: Cannot find name 'calc'.
  • Passes with this change (calc(10.5) === 26.25).

Test plan

  • Reproduce TS2304 with a minimal fixture before the fix.
  • New unit test passes with the fix applied.
  • Existing should draft a template still passes (template with
    logic.ts but no helper-calling formula).
  • Existing should trigger a template / should init a template
    still pass after refactor to shared helper.
  • npx eslint src/ test/ — no new errors.
  • CI to run the full suite. (Network-restricted local environment
    blocks models.accordproject.org, so some unrelated tests fail
    locally on main too.)

Generated by Claude Code

…ic/logic.ts (#147)

TemplateArchiveProcessor.draft() compiled inline formulas (`{{% expr %}}`)
with a TypeScript context that did not include the template's user code
from `logic/logic.ts`. Helper functions like `monthlyPaymentFormula(...)`
therefore produced `Cannot find name 'monthlyPaymentFormula'` (TS2304).

trigger() and init() already loaded logic/logic.ts via the
TypeScriptToJavaScriptCompiler. This change:

- Extracts that compilation into a shared `compileUserLogic` helper
  (src/UserLogic.ts) which returns the compiled JS along with the
  top-level identifiers it declares.
- Adds `declare const <name>: any;` stubs to the formula compile
  context for each of those identifiers, so user helpers resolve at
  compile time.
- Splices the stripped logic.ts JS into the formula function body at
  runtime, so the same identifiers are real values at evaluation time.
- Refactors trigger() and init() to use the new shared helper, so
  draft() and trigger()/init() now use the same logic-loading path.

Adds a fixture (test/archives/formula-uses-logic-helper) and a unit
test that confirms draft() can call helpers from logic/logic.ts.

Signed-off-by: Claude <noreply@anthropic.com>
@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 25955964043

Coverage increased (+0.6%) to 64.788%

Details

  • Coverage increased (+0.6%) from the base build.
  • Patch coverage: 4 uncovered changes across 1 file (70 of 74 lines covered, 94.59%).
  • 2 coverage regressions across 1 file.

Uncovered Changes

File Changed Covered %
src/TemplateArchiveProcessor.ts 29 25 86.21%

Coverage Regressions

2 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
src/TemplateArchiveProcessor.ts 2 78.48%

Coverage Stats

Coverage Status
Relevant Lines: 1154
Covered Lines: 743
Line Coverage: 64.38%
Relevant Branches: 567
Covered Branches: 372
Branch Coverage: 65.61%
Branches in Coverage %: Yes
Coverage Strength: 5781.61 hits per line

💛 - Coveralls

@mttrbrts mttrbrts marked this pull request as ready for review May 25, 2026 18:57
@mttrbrts mttrbrts requested review from a team and Copilot May 25, 2026 18:57
@mttrbrts mttrbrts requested review from Copilot and removed request for Copilot May 25, 2026 18:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes issue #147 by ensuring draft() compiles inline formula expressions with awareness of helper symbols declared in a template’s logic/logic.ts, and makes those helpers available at runtime during formula evaluation.

Changes:

  • Extract user-logic compilation into a shared helper (compileUserLogic) and reuse it from draft(), trigger(), and init().
  • Extend the TypeScript compilation context for inline formulas to include ambient declarations for user-logic top-level identifiers.
  • Thread a stripped “user logic prelude” into the formula evaluator so helpers exist at evaluation time, and add a regression test fixture + unit test.

Reviewed changes

Copilot reviewed 17 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/TemplateArchiveProcessor.test.ts Adds a regression test that drafts a template whose formula calls a helper from logic/logic.ts.
test/archives/formula-uses-logic-helper/text/grammar.tem.md New fixture grammar containing a formula that calls calc(...).
test/archives/formula-uses-logic-helper/request.json New fixture request payload.
test/archives/formula-uses-logic-helper/README.md New fixture README/sample text.
test/archives/formula-uses-logic-helper/package.json Declares the fixture as a TypeScript-runtime template.
test/archives/formula-uses-logic-helper/model/model.cto New fixture model defining template/request/response/state.
test/archives/formula-uses-logic-helper/model/@models.accordproject.org.time@0.3.0.cto Vendored model dependency for offline tests.
test/archives/formula-uses-logic-helper/model/@models.accordproject.org.accordproject.runtime@0.2.0.cto Vendored model dependency for offline tests.
test/archives/formula-uses-logic-helper/model/@models.accordproject.org.accordproject.contract@0.2.0.cto Vendored model dependency for offline tests.
test/archives/formula-uses-logic-helper/logic/README.md New fixture logic README.
test/archives/formula-uses-logic-helper/logic/logic.ts New fixture logic with helper calc used by the grammar formula.
test/archives/formula-uses-logic-helper/logic/generated/org.accordproject.time@0.3.0.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/org.accordproject.runtime@0.2.0.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/org.accordproject.contract@0.2.0.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/io.clause.latedeliveryandpenalty@0.1.0.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/concerto@1.0.0.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/concerto.ts Generated TS types for the fixture.
test/archives/formula-uses-logic-helper/logic/generated/concerto.decorator@1.0.0.ts Generated TS types for the fixture.
src/UserLogic.ts Introduces shared compilation helper + module-syntax stripping + symbol extraction.
src/TypeScriptToJavaScriptCompiler.ts Extends compiler construction to accept user-logic symbol stubs for formula compilation.
src/TypeScriptCompilationContext.ts Emits declare const <name>: any; stubs for user-logic symbols to make formulas type-check.
src/TemplateMarkToJavaScriptCompiler.ts Threads user-logic symbols into the TS→JS compilation pipeline for TemplateMark code nodes.
src/TemplateMarkInterpreter.ts Threads compiled user-logic prelude into runtime formula evaluation.
src/TemplateArchiveProcessor.ts Refactors logic compilation into compileLogic() and wires it into draft()/trigger()/init().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +102 to +105
const logicFile = tsFiles.find(f => f.getIdentifier() === 'logic/logic.ts') ?? tsFiles[0];
if (!logicFile) {
return undefined;
}
Comment on lines +122 to 125
const userLogic = await this.compileLogic();
if (!userLogic) {
throw new Error('Only TypeScript is supported at this time');
}
Comment on lines +150 to 153
const userLogic = await this.compileLogic();
if (!userLogic) {
throw new Error('Only TypeScript is supported at this time');
}
Comment on lines 100 to 108
// chop the function header and closing
const expression = fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'));
if (expression.trim().length === 0) {
throw new Error('Empty expression');
}
const codeWithPrelude = userLogicPrelude ? `${userLogicPrelude}\n${expression}` : expression;
try {
const request = { code: expression, argumentNames: functionArgNames, arguments: functionArgValues };
const request = { code: codeWithPrelude, argumentNames: functionArgNames, arguments: functionArgValues };
if (options?.childProcessJavaScriptEvaluation) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

draft() can't resolve TypeScript helper functions defined in logic/logic.ts

4 participants