accordproject / template-archive

Smart Legal Contracts & Templating System
https://accordproject.org/projects/cicero/
Apache License 2.0
280 stars 119 forks source link

Add Support for Logic Files in TypeScript #808

Open dselman opened 4 months ago

dselman commented 4 months ago

Feature Request 🛍️

Support putting .ts TypeScript files in the logic folder of an archive.

Use Case

Allow folks to implement the contract logic of a template in TypeScript.

Possible Solution

Leverage the existing ScriptManager / LogicManager and extend to support TypeScript as a logic language. High-level plan will be to make this pluggable / extensible to other languages.

Implement a compilation pipeline from Typescript to JavaScript and then implement in-process and out-of-process evaluation of the generated JS code: leaning on the existing code in template-engine:

  1. Creation of a TS model from Concerto
  2. Compilation of TS code to JS
  3. In and out of process eval of JS code

Context

Detailed Description

Strawman...

// dynamically generated from template model
type MyRequest = {
  input: string;
};

type MyResponse = {
  output: string;
};

type HelloModuleClause = {
  name: string;
};

// statically packaged as part of the runtime contract
// State and Emit are optional...
interface IContract<Request,Response,Contract,State=void,Emit=void> {
    getData: () => Contract;
    init: (request:Request) => Promise<Response>;
    setState: (state:State) => void;
    getState: () => Promise<State>;
    emit: (event:Emit) => void;
    invoke: (request:Request, name: string) => Promise<Response>
}

abstract class DefaultContract<Request,Response,Contract,State=void,Emit=void> implements IContract<Request,Response,Contract,State,Emit> {
    contract:Contract;
    state:State;
    constructor(contract:Contract) {
        this.contract = contract;
    }
    getData() {
        return this.contract;
    }
    setState(state: State) {
        this.state = state;
    }
    async getState() {
        return this.state;
    }
    async emit(event: Emit) {
        console.log(event);
    };
    abstract init(request: Request) : Promise<Response>;
    abstract invoke(request: Request, name: string): Promise<Response>;
}

// end user code
export default class MyContract extends DefaultContract<MyRequest, MyResponse, HelloModuleClause> {
    init(request: MyRequest): Promise<MyResponse> {
        throw new Error("Method not implemented.");
    }
    invoke(request: MyRequest, name: string): Promise<MyResponse> {
        throw new Error("Method not implemented.");
    }
}