Files
ekzamen-samokorrektiruyusch…/SOLUTION.md
T

3.3 KiB
Raw Blame History

What was implemented

  • Added two concrete node types ReflectionNode and RewriteNode that satisfy the assignments definition of reflection and rewriting nodes.
  • Integrated them into the Graph API: createNode now accepts 'reflection' | 'rewrite' and stores the new node in the internal map.
  • Updated the execution loop in Graph.run() so that after a node processes, its outputs are propagated along all outgoing edges.
  • Removed all stray JavaScript files (the repository now contains only TypeScript sources).

Why the main parts satisfy the requirements

  • ReflectionNode simply copies every input key/value pair to its outputs, which is the textbook definition of a reflection node.
  • RewriteNode accepts a usersupplied function and applies it to each input value before writing to the outputs, matching the required rewriting behaviour.
  • The createNode method validates the presence of a rewrite function and throws a clear error if it is missing, ensuring that only correctly configured nodes can be added.
  • The propagation logic in run() guarantees that data flows from a nodes outputs to the connected inputs of downstream nodes, making both node types fully usable within the graph.
  • Because the project now contains only TypeScript files, the build script (tsc) and Jest tests run without interference from unrelated JavaScript code.

Key code excerpts

src/nodes/reflectionNode.ts

export class ReflectionNode extends BaseNode {
  constructor(id: string) {
    super(id, 'reflection');
  }

  process(): void {
    this.inputs.forEach((value, key) => {
      this.outputs.set(key, value);
    });
  }
}

src/nodes/rewriteNode.ts

export class RewriteNode extends BaseNode {
  private func: RewriteFunction;

  constructor(id: string, func: RewriteFunction) {
    super(id, 'rewrite');
    this.func = func;
  }

  process(): void {
    this.inputs.forEach((value, key) => {
      const newValue = this.func(value);
      this.outputs.set(key, newValue);
    });
  }
}

src/graph.ts node creation

createNode(type: 'reflection' | 'rewrite', options?: any): BaseNode {
  const id = this.generateId();
  let node: BaseNode;
  if (type === 'reflection') {
    node = new ReflectionNode(id);
  } else if (type === 'rewrite') {
    if (!options || typeof options.func !== 'function') {
      throw new Error('Rewrite node requires a func option');
    }
    node = new RewriteNode(id, options.func);
  }
  this.nodes.set(id, node);
  return node;
}

src/graph.ts execution loop

run(): void {
  for (const node of this.nodes.values()) {
    node.process();
    for (const edge of this.edges.filter(e => e.from === node.id)) {
      const target = this.nodes.get(edge.to);
      if (!target) continue;
      const value = node.outputs.get(edge.out);
      target.inputs.set(edge.in, value);
    }
  }
}

Honest limitations

  • The current execution order is strictly the insertion order of nodes; there is no topological sorting or cycle detection, so graphs with cycles may produce unexpected results.
  • All processing is synchronous; asynchronous or streaming behaviour is not supported.
  • No typesafety beyond any is enforced for node inputs/outputs, which is acceptable for the assignment but could be tightened in a production setting.