BlockChaining - Is it Possible?

In French which is my mother language I would answer with the most accurate expressivity that yes! certainly yes! :star_struck: I hope I can communicate this in English! What does your datascience sentiment analysis application think about it ? :wink:

Before I called you, I had examined this (aRecords) myself, like this:

// moving forward? (update the answer to the table)
if (outcome != "Back") {
    // update the table with the answer to this question
    await sourceTable.updateRecordAsync(aRecords[i].id, {
        [targetOutcomeCol]: outcome,
    // update the array with the outcome
    aRecords[i]["outcome"] = outcome;
    output.markdown(`** aRecords[i]["outcome"] = outcome; **`);
    BreakInput = await input.buttonsAsync("ONE STEP BEYOND ?", ['Yes']);

to get it STEP by STEP.

However, when I examine ages in the life of aRecords throughout your Script:

  1. aRecords’s life is beginning like this:

    var queryResults = await sourceTable.selectRecordsAsync();
    var aRecords = queryResults.records;

which seems to me the most normal in Script-Block API.

  1. What continues to puzzle me


because I didn’t know


formalism to push result to array in the context of your Script.

  1. this step

    // update the array with the outcome
    aRecords[i][“outcome”] = outcome;

that becames friend of mine.



Your English is almost as good as some English people I’ve met and certainly better than some Americans. :slight_smile: And despite my last name, you do not want to hear a single utterance in French from me.

One of the design goals of this “sample/example” script was to determine how late script could be bound in a Script Block. That was my only objective in building this - I wanted to see and understand at an architectural level how abstract code could be made to run in a script block or even a custom block.

And, of course, the answer is that it may be bound extremely late - script “injection” into a running framework is entirely within the scope of apps built in Script Block and it demonstrates “just-in-time” application behaviours.

Using this approach, very large script “libraries” (perhaps not built in Airtable but certainly) managed in Airtable could be deployed to avoid the Script Block size ceiling. It also means that new scripts and script versions could be deployed without users copying and pasting into each their own blocks.

A secondary design goal was to see how well the vast benefits of tables could be used to create a smart system, or at least some simple wizards and surveys that compile answers.

Thank you very much @Bill.French, really !

In the meantime I’ve come to understand :smile:
You did add an “outcome” “property (?)” to aRecords[i]

// update the array with the outcome
            aRecords[i]["outcome"] = outcome;

which isn’t a “ghost” additional FIELD anyway
that I will never could read through a


but well through

let aRecordsOutcome = aRecords[i].outcome


Right now, but I still really don’t understand :

and I wish to know:

EXAMPLE from your Code:

for (var i = 0; i < aRecords.length; i++)

    if (aRecords[i].getCellValueAsString("Status") == "Active") {

aRecords[i] is not an iterable object.
I wish it would.
How should I add this , if it was possible ?

Contextual AddOn to DOCUMENTATION:
aRecords[i] Properties and Methods:

1. 0: "id"

2. 1: "name"

3. 2: "__parentTable"

4. 3: "_cellValuesByFieldId"

5. 4: "_stringCellValuesByFieldId"

6. 5: "outcome"

7. 6: "constructor"

8. 7: "getCellValue"

9. 8: "getCellValueAsString"

10. 9: "constructor"

11. 10: "__defineGetter__"

12. 11: "__defineSetter__"

13. 12: "hasOwnProperty"

14. 13: "__lookupGetter__"

15. 14: "__lookupSetter__"

16. 15: "isPrototypeOf"

17. 16: "propertyIsEnumerable"

18. 17: "toString"

19. 18: "valueOf"

20. 19: "__proto__"

21. 20: "toLocaleString"

and finally,

This raises a lot of questions on my side as well. Thank you for having developed a little bit this broad and stimulating subject.




In the current state of my knowledge and subject to any hidden bias in my experimental process:

[“outcome”] is a KEY added to aRecords[i]
outcome is a VALUE.
It’s a KEY:VALUE pair added to aRecords[i]

got from:

let keysArray = Object.keys(aRecords[i]);
let valuesArray = Object.values(aRecords[i]);
output.text(`let keysArray = Object.keys(aRecords[i]);`);
output.text(`let valuesArray = Object.values(aRecords[i]);`);

but we can get outcome VALUE from



Correct, and it is populated via a call back from the eval() call I believe.

1 Like

Thanks @Bill.French !

I could believe it too, so why did you execute the following code ?

Does a call back from the eval() call not be enough ?



I did try following:

// process the block
aRecords[i]["outcome"] = null;
var outcome = await eval(thisCode);


    // update the table with the answer to this question
    await sourceTable.updateRecordAsync(aRecords[i].id, {
        [targetOutcomeCol]: outcome,
    // update the array with the outcome
    // aRecords[i]["outcome"] = outcome;

Result: “outcome” : outcome is null anyway.

Subject to any hidden bias in my experimental process,
it seems that it is not populated via a call back from the eval() call.


Correct; the callback only passes the variable at the time of the action (a question was answered, nothing more) to the eval’d script fragment so that it can be used by the fragment if needed. Then we have to embellish that node with what was actually selected.

OK :slight_smile:

Thank you @Bill.French ,


Hi @Bill.French,
Sorry to ask you again, to take your time, but as soon as I don’t work on Color Grading of films, I’ll come back to your Script:

exact way of working in the context of this EVAL.

As your script is especially rich for a javascript learner like me, I think I already analyzed it well with Console.log or with output.inspect as well as by dissecting as much as possible of the object aRecords along his content’s dynamic changes but I didn’t understand this point in particular.



Imagine you’re running multiple script fragments from a list of records and in that process you need to summarize the outcomes of each fragment’s choices (button 1, button 2, etc.). Now imagine you need a global variable that sustains those choices made as each fragment is executed. Lastly, imagine a requirement where the final script fragment being executed needs to know the choices made by all the other script fragments executed by the eval() method.

You need a global variable that can sustain the outcomes across all script evaluations and such that this variable is known and accessible inside each script fragment evaluation. That is the purpose of aRecords (i.e. answer records).

Feel free to find other ways to meet these requirements with a different design, but it’s likely this design is a good one.

Mke sense?

Thanks a lot @Bill.French,
I’ll read to you with all due attention after my work.

Yes, it could always happen if I experiment in order to learn, but be sure that I had no second thoughts of thinking that I would find a better design!
I consult you on this Community.airtable to learn the art, the technique of good design.
Thank you very much for bringing us your teaching since Script-Block exists.
It’s very kind of you!
(As I tried to express non-technical nuances in English, I hope I used the right words.)


Hello, @Bill.French,
I come back to the subject again, not to appear to be the smartest, but because my approach is experimental.

var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";


var outcome = await eval(thisCode);


thisCode could be (for exemple from STEP1):

await eval("(async() => {let scriptQuestion = “Are you happy?”; let stepOutcome = await input.buttonsAsync(scriptQuestion, [‘Yes’, ‘No’, ‘Back’]);; return(stepOutcome);})(aRecords)");

are focused in what follows:

Assuming that there might be a hidden bias in my experience, in the current state of the results I get:

(1) It seems well that aRecords in


at the end of EVAL is never modified anyway by this EVAL code line ;

(2) It seems well that })(anythingElse) is running and giving back the same script results if I first add

let anythingElse

at begin of the script
AND it appears in this case that console.log(anythingElse) is giving back undefined at each script STEP ;

(3) If I’m replacing


by })() script is running and giving back the same script results ;

(4) If I’m replacing end-of-script-line


by })`


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";

that becomes then:

var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})";

script raised to error then stopped this way:

R: Cannot deserialize 15
at main on line 

(5) It seemed well that only script line

// update the array with the outcome
            aRecords[i]["outcome"] = outcome;

is updating aRecords step by step as you wished.

Be sure that my approach is purely well-intended: I’m not trying to have the last word, nor to look like I’m the smartest.
I’m just trying to understand how this EVAL really works in relation to (aRecords) end-of-script-line, aRecords which I also inspected everything (in my opinion) that’s readable from throughout the script to follow its evolution before coming back here.



No one truly understands how eval() really works. :wink: Perhaps this will help.

This is the magic of eval(); it makes it possible to construct code in a very late binding process purely as a string and then execute that code AFTER the string has been fully constructed.

This doesn’t pertain to the ongoing conversation, but is a question regarding the OP, @Bill.French

Could I use this strategy to save the text of a minified JavaScript library (say Moment.js) in a table, to be implemented in any Script block in that base simply by using eval() on the value of the field holding the minified library? Could I use that pattern to basically “import“ libraries I enjoy using into my Scripts?

I believe this is possible. Never checked into it, but I have often wondered if this approach wasn’t a pathway to extending the flight ceiling of Script Blocks by dynamically introducing code into a solution for which the code occupies no space (i.e., script memory per-se) in the lately-bound process. Note - you may have experienced a script block is unable to load more than about 2,000 lines of script and minifying seems to help.

My example makes it obvious that …

  1. Script fragments can exist in records/fields; a fragment is no different than a dynamic library
  2. Script in records/fields can be executed with eval()
  3. Return values from dynamically called script fragments can be persisted
  4. Processes that use multiple script fragments can be strung together and even reordered, filtered, and updated dynamically

I’ll give it a shot then, when I’m not rolling the internet on my phone :wink:

It totally works!

Pasting the entire minified moment.js library into a script block is rejected for making the script too long. However, a long text field can hold the entire minified library:

And that library can be called into the Script block with eval(), as if you were importing it with import.

As you see from my screenshot above, you will clearly not have intellisense while editing, and any calls to the eval'd library will be flagged as errors, but once the library is eval'd its models can be used in the script.

Thank you for discovering this strategy, @Bill.French – this is going to make the Script block so much more flexible for junior scripters who prefer to lean on other people’s work :wink:

1 Like

You’re welcome!

Yep - perfect use case for this feature looking for a problem to solve. :wink:

This will probably not be a big issue as libraries – for the most part – are well-tested and few will have the desire to modify them. But, this doesn’t stop any of us from using this approach for creating and managing our own reusable libraries in this way.

Sidebar - if Airtable is able to create a JSON editor for long text fields, it can certainly create and install a code editor for javascript because - wait for it - json is javascript. :wink:

Sidebar.2 - BTW, this happened when I envisioned a way to manage code as if it were data and what better example for doing so with the advantageous relationship that Script Blocks share with data tables?

Sidebar.3 - feel free to write the Show & Tell with a title that’s more aligned with what you are showing here; “BlockChaining” doesn’t really express what you’re accomplishing with libraries.

Sidebar.4 - no developer (me included) should ever be given credit for using eval() in creative or potentially dangerous ways. LOL

1 Like

Many thanks @Bill.French !
It seems already to me to be the right link but I would ingest and process it during free hours !

Bill had already suggested this in this thread or elsewhere and there you tried, and succeeded! Bravo and thank you very much!
It’s time for me to go on vacation (soon) to try to give something to the Community in return, but for the moment, in javascript, I’m learning to play the Who and the Rolling Stones as perfectly as possible : I’m not composing anything original yet. But it will come !