Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"prettier.configPath": ".prettierrc.js",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
"editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib"
}
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@
"url": "https://github.com/bhouston/behave-graph"
},
"devDependencies": {
"@openzeppelin/contracts": "^4.7.3",
"@preconstruct/cli": "^2.2.2",
"@typechain/ethers-v5": "^10.1.1",
"@typechain/hardhat": "^6.1.4",
"@babel/core": "^7.17.10",
"@babel/preset-env": "^7.17.10",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@openzeppelin/contracts": "^4.7.3",
"@preconstruct/cli": "^2.2.2",
"@typechain/ethers-v5": "^10.1.1",
"@typechain/hardhat": "^6.1.4",
"dotenv": "^16.0.3",
"prettier": "^2.7.1",
"prettier": "^2.8.0",
"ts-node": ">=8.0.0",
"typechain": "^8.1.1",
"typescript": ">=4.5.0"
"typescript": "^4.9.3"
},
"resolutions": {
"undici": "5.5.1"
Expand All @@ -57,7 +57,8 @@
],
"preconstruct": {
"packages": [
"editor"
"editor",
"packages/*"
]
}
}
24 changes: 24 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@interx/core",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha --require ts-node/register 'src/**/*.ts'"
},
"author": "",
"license": "ISC",
"dependencies": {
"@behave-graph/core": "^0.9.11",
"chai": "^4.3.7",
"hardhat": "^2.12.2",
"mocha": "^10.1.0",
"typescript": "^4.9.3"
},
"devDependencies": {
"@types/expect": "^24.3.0",
"@types/mocha": "^10.0.0",
"ts-mocha": "^10.0.0",
"type-fest": "^3.2.0"
}
}
38 changes: 38 additions & 0 deletions packages/core/src/nodes/flow/Branch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { expect } from 'chai';
import { buildStubEngineForFlowNode } from '../schema/testUtils';
import Branch from './Branch';

describe('Branch', () => {
describe('trigger', () => {
it('commits the true output when value is true', () => {
const { trigger, writeInput, getOutputWrites } = buildStubEngineForFlowNode(Branch);

writeInput('condition', true);
trigger('flow');

const expected = [
{
writeType: 'flow',
socketName: 'true',
},
];

expect(getOutputWrites()).to.eql(expected);
});
it('commits the true output when value is false', () => {
const { trigger, writeInput, getOutputWrites } = buildStubEngineForFlowNode(Branch);

writeInput('condition', false);
trigger('flow');

const expected = [
{
writeType: 'flow',
socketName: 'false',
},
];

expect(getOutputWrites()).to.eql(expected);
});
});
});
27 changes: 27 additions & 0 deletions packages/core/src/nodes/flow/Branch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { makeFlowNodeDefinition } from '../schema/FlowNodes';

const Branch = makeFlowNodeDefinition({
inputSockets: {
flow: {
valueType: 'flow',
},
condition: {
valueType: 'boolean',
},
},
outputSockets: {
true: {
valueType: 'flow',
},
false: {
valueType: 'flow',
},
},
triggered: ({ commit, readInput }) => {
const value = readInput('condition');
commit(value ? 'true' : 'false');
},
initialState: () => undefined,
});

export default Branch;
78 changes: 78 additions & 0 deletions packages/core/src/nodes/flow/Counter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { expect } from 'chai';
import { buildStubEngineForFlowNode, RecordedOutputWrites } from '../schema/testUtils';
import Counter from './Counter';

describe('Branch', () => {
describe('trigger', () => {
it('writes to the output and triggers a flow for each trigger', () => {
const { trigger, getOutputWrites } = buildStubEngineForFlowNode(Counter);

trigger('flow');
trigger('flow');

const expected: RecordedOutputWrites<typeof Counter.outputSockets> = [
{
writeType: 'value',
socketName: 'count',
value: 1n,
},
{
writeType: 'flow',
socketName: 'flow',
},
{
writeType: 'value',
socketName: 'count',
value: 2n,
},
{
writeType: 'flow',
socketName: 'flow',
},
];

expect(getOutputWrites()).to.eql(expected);
});
it('resets the value to 0 but doesnt write the update on reset', () => {
const { trigger, getOutputWrites } = buildStubEngineForFlowNode(Counter);

trigger('flow');
trigger('flow');
trigger('reset');
trigger('flow');

const expected: RecordedOutputWrites<typeof Counter.outputSockets> = [
{
writeType: 'value',
socketName: 'count',
value: 1n,
},
{
writeType: 'flow',
socketName: 'flow',
},
{
writeType: 'value',
socketName: 'count',
value: 2n,
},
{
writeType: 'flow',
socketName: 'flow',
},
// reset triggered - goes back to 0 but value isnt emitted
{
writeType: 'value',
socketName: 'count',
value: 1n,
},
{
writeType: 'flow',
socketName: 'flow',
},
];

expect(getOutputWrites()).to.eql(expected);
});
});
});
48 changes: 48 additions & 0 deletions packages/core/src/nodes/flow/Counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { makeFlowNodeDefinition } from '../schema/FlowNodes';

const Counter = makeFlowNodeDefinition({
inputSockets: {
flow: {
valueType: 'flow',
},
reset: {
valueType: 'flow',
},
},
outputSockets: {
flow: {
valueType: 'flow',
},
count: {
valueType: 'integer',
},
},
triggered: ({ commit, writeOutput, state, triggeringSocketName }) => {
// duplicate count to not modify the state
let count = state.count + 0n;

switch (triggeringSocketName) {
case 'flow': {
count++;
writeOutput('count', count);
commit('flow');
break;
}
case 'reset': {
count = 0n;
break;
}
default:
throw new Error('should not get here');
}

return {
count,
};
},
initialState: () => ({
count: 0n,
}),
});

export default Counter;
60 changes: 60 additions & 0 deletions packages/core/src/nodes/schema/FlowNodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { FlowSocketNames, FlowSockets, IHasSockets, Sockets, ValueSockets, ValueTypeNameMapping } from './Sockets';
/** Node Engine Access Functions */

export type readNodeInputFn<TSockets extends Sockets> = <
TValueSockets extends ValueSockets<TSockets>,
KValueSocket extends keyof TValueSockets
>(
param: KValueSocket
) => ValueTypeNameMapping<TValueSockets[KValueSocket]['valueType']>;

export type writeNodeOutputFn<TSockets extends Sockets> = <V extends ValueSockets<TSockets>, J extends keyof V>(
param: J,
value: ValueTypeNameMapping<V[J]['valueType']>
) => void;

export type commitFn<T extends Sockets> = <J extends keyof FlowSockets<T>>(param: J) => void;

/** Flow Node Definitions */

export type ReadWriteToNodeParams<TInput extends Sockets, TOutput extends Sockets> = {
/** reads a value from an input node */
readInput: readNodeInputFn<TInput>;
/** writes to an output node */
writeOutput: writeNodeOutputFn<TOutput>;
};

/** Arguments for the triggered function on a flow node */
export type TriggeredParams<TInput extends Sockets, TOutput extends Sockets, TNodeState> = ReadWriteToNodeParams<
TInput,
TOutput
> & {
/** commits a trigger to a flow node */
commit: commitFn<TOutput>;
/** The local node's state */
readonly state: TNodeState;
/** The name of the flow input socket that was triggered */
triggeringSocketName: FlowSocketNames<TInput>;
};

export type TriggeredFunction<
TInput extends Sockets,
TOutput extends Sockets,
TNodeState
> = TNodeState extends undefined
? (params: TriggeredParams<TInput, TOutput, TNodeState>) => void
: (params: TriggeredParams<TInput, TOutput, TNodeState>) => TNodeState;

export interface IFlowNode<TInputSockets extends Sockets, TOutputSockets extends Sockets, TNodeState>
extends IHasSockets<TInputSockets, TOutputSockets> {
/** Called when an input flow node is triggered */
triggered: TriggeredFunction<TInputSockets, TOutputSockets, TNodeState>;
initialState: () => TNodeState;
}
export function makeFlowNodeDefinition<
TInputSockets extends Sockets,
TOutputSockets extends Sockets,
TNodeState = undefined
>(flowNode: IFlowNode<TInputSockets, TOutputSockets, TNodeState>) {
return flowNode;
}
69 changes: 69 additions & 0 deletions packages/core/src/nodes/schema/INodeDefinition.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { makeFlowNodeDefinition, readNodeInputFn } from './FlowNodes';
import { FlowSockets, ValueSockets, ExtractValueType, ValueTypeNameMapping, Sockets } from './Sockets';
import { expectType } from './testUtils';

describe('TriggeredParams', () => {
describe('writeOutput', () => {
it('can only write output to a socket in the output definition that is a value type', () => {
const flowDef = makeFlowNodeDefinition({
inputSockets: {
a: {
valueType: 'boolean',
},
b: {
valueType: 'string',
},
c: {
valueType: 'flow',
},
},
outputSockets: {
c: {
valueType: 'float',
},
d: {
valueType: 'integer',
},
e: {
valueType: 'flow',
},
f: {
valueType: 'string',
},
},
triggered: ({ commit, readInput, writeOutput }) => {
const a = readInput('a');

writeOutput('c', a ? 1.0 : 0.0);

commit('e');

return undefined;
},
initialState: () => undefined,
});

expectType<ValueSockets<typeof flowDef.inputSockets>>({
a: {
valueType: 'boolean',
},
b: {
valueType: 'string',
},
});

expectType<FlowSockets<typeof flowDef.inputSockets>>({
c: {
valueType: 'flow',
},
});

expectType<ValueTypeNameMapping<'boolean'>>(true);
expectType<ValueTypeNameMapping<'string'>>('asdfasfd');
expectType<ExtractValueType<typeof flowDef.inputSockets, 'a'>>(false);

expectType<Parameters<readNodeInputFn<typeof flowDef.inputSockets>>>(['a']);
expectType<Parameters<readNodeInputFn<typeof flowDef.inputSockets>>>(['b']);
});
});
});
Loading