<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sebastian Wessel</title>
    <description>The latest articles on DEV Community by Sebastian Wessel (@sebastian_wessel).</description>
    <link>https://dev.to/sebastian_wessel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1054904%2F53658dbd-ccaf-4bfc-b4d9-14f102e981dc.jpg</url>
      <title>DEV Community: Sebastian Wessel</title>
      <link>https://dev.to/sebastian_wessel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sebastian_wessel"/>
    <language>en</language>
    <item>
      <title>How-To Spec-Driven AI Development</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Thu, 28 May 2026 17:02:50 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/how-to-spec-driven-ai-development-1602</link>
      <guid>https://dev.to/sebastian_wessel/how-to-spec-driven-ai-development-1602</guid>
      <description>&lt;p&gt;AI does not fail only because it is unreliable.&lt;/p&gt;

&lt;p&gt;It often fails because we ask it to fulfill expectations we never explained.&lt;/p&gt;

&lt;p&gt;That is the core of spec-driven development for me.&lt;/p&gt;

&lt;p&gt;Not process. Not documentation theater. Not a return to slow software delivery.&lt;/p&gt;

&lt;p&gt;Spec-driven development is the practice of giving AI the information it needs to do the work correctly: purpose, context, boundaries, constraints, architecture, business expectations, acceptance criteria, and review gates.&lt;/p&gt;

&lt;p&gt;If the AI should meet an expectation, the expectation must be available to the AI.&lt;/p&gt;

&lt;p&gt;That sounds obvious.&lt;/p&gt;

&lt;p&gt;In practice, it is where many AI-assisted projects break.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Goal Is Not More Code&lt;/li&gt;
&lt;li&gt;AI Can Only Meet Expectations It Can See&lt;/li&gt;
&lt;li&gt;Human Language Becomes The Interface&lt;/li&gt;
&lt;li&gt;Spec-Driven Development Starts With The Knowns&lt;/li&gt;
&lt;li&gt;Human Work Moves Up A Level&lt;/li&gt;
&lt;li&gt;Good Specs Make Expectations Executable&lt;/li&gt;
&lt;li&gt;Planning Makes Work Delegatable&lt;/li&gt;
&lt;li&gt;Implementation Should Not Invent&lt;/li&gt;
&lt;li&gt;Review Becomes A Trust Pipeline&lt;/li&gt;
&lt;li&gt;What Works For Me&lt;/li&gt;
&lt;li&gt;Open Questions To Handle&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Goal Is Not More Code
&lt;/h2&gt;

&lt;p&gt;The goal is not to produce more code faster.&lt;/p&gt;

&lt;p&gt;That is too small.&lt;/p&gt;

&lt;p&gt;Enterprise software has users, data, security boundaries, compliance pressure, legacy systems, audit questions, operations, budgets, deadlines, and people who need to understand why a system behaves the way it behaves.&lt;/p&gt;

&lt;p&gt;AI can make implementation faster, but that is only the shallow part.&lt;/p&gt;

&lt;p&gt;Used well, AI can also help make enterprise requirements visible. It can check whether security boundaries are covered. It can explain how data flows through the system. It can point to tests. It can summarize architectural decisions. It can prepare audit evidence. It can answer a human who asks: "How is this topic covered? Show me. Explain it. Prove it."&lt;/p&gt;

&lt;p&gt;That is the more interesting goal.&lt;/p&gt;

&lt;p&gt;AI-assisted delivery should help teams build software that meets expectations, respects constraints, can be explained, can be tested, and can be audited.&lt;/p&gt;

&lt;p&gt;That requires a shared knowledge layer.&lt;/p&gt;

&lt;p&gt;For me, that is one of the most important roles of a spec. It is centralized, comprehensive knowledge for humans and AI at the same time. People can see the intent, boundaries, decisions, and acceptance criteria. AI agents can use the same source to implement, explain, test, and prove the work.&lt;/p&gt;

&lt;p&gt;But that only works if the AI has the right information to reason from.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Can Only Meet Expectations It Can See
&lt;/h2&gt;

&lt;p&gt;If you know it, write it down.&lt;/p&gt;

&lt;p&gt;That sounds almost too simple, but it is the center of the whole workflow.&lt;/p&gt;

&lt;p&gt;If you know the business reason, write it down.&lt;/p&gt;

&lt;p&gt;If you know the technical boundary, write it down.&lt;/p&gt;

&lt;p&gt;If you know the existing architecture pattern, write it down.&lt;/p&gt;

&lt;p&gt;If you know the security constraint, write it down.&lt;/p&gt;

&lt;p&gt;If you know what would make the result wrong, write it down.&lt;/p&gt;

&lt;p&gt;AI agents do not share your memory. They do not know the meeting, the political constraint, the legacy scar, the product promise, the audit pressure, or the architecture rule unless that information is present in the task.&lt;/p&gt;

&lt;p&gt;Start with the knowns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;purpose, intent, and business point of view&lt;/li&gt;
&lt;li&gt;boundaries, non-goals, and existing context&lt;/li&gt;
&lt;li&gt;tech stack and architecture decisions&lt;/li&gt;
&lt;li&gt;security, compliance, and operational constraints&lt;/li&gt;
&lt;li&gt;acceptance criteria and review questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The better the expectation is described, the less the AI has to guess.&lt;/p&gt;

&lt;p&gt;And guessing is where delivery risk starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human Language Becomes The Interface
&lt;/h2&gt;

&lt;p&gt;Programming languages have always been an interface between humans and computers.&lt;/p&gt;

&lt;p&gt;But they were never a native language for either side.&lt;/p&gt;

&lt;p&gt;Humans think in goals, constraints, tradeoffs, examples, risks, and stories. Then we translate that into TypeScript, Python, SQL, or whatever stack the system needs. The computer still needs parsers, compilers, interpreters, runtimes, operating systems, and machines that execute instructions at a much lower level.&lt;/p&gt;

&lt;p&gt;Programming languages are the middle layer both sides can use.&lt;/p&gt;

&lt;p&gt;They are powerful, precise, and necessary.&lt;/p&gt;

&lt;p&gt;But for humans, they are still a second language.&lt;/p&gt;

&lt;p&gt;For decades, software development was mostly one directional. The human translated intent into code. The machine accepted or rejected it. The feedback loop existed, but the computer did not discuss intent with us in our own language.&lt;/p&gt;

&lt;p&gt;That is the paradigm shift.&lt;/p&gt;

&lt;p&gt;With AI-assisted development, human language becomes a two-way interface. We can describe goals, constraints, behavior, examples, risks, and review questions. The AI can respond, ask clarifying questions, explain tradeoffs, inspect code, generate tests, summarize architecture, and help translate the result into implementation.&lt;/p&gt;

&lt;p&gt;This does not mean programming languages stop mattering.&lt;/p&gt;

&lt;p&gt;They still matter a lot. Runtime behavior, type systems, package ecosystems, performance, security, deployment, and maintainability are still real.&lt;/p&gt;

&lt;p&gt;But from the human interface point of view, the higher-level artifact is no longer only the code.&lt;/p&gt;

&lt;p&gt;It is the spec, the architecture, the acceptance criteria, the review checklist, and the reusable instructions that tell agents what is expected and how their work will be checked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spec-Driven Development Starts With The Knowns
&lt;/h2&gt;

&lt;p&gt;Software teams have always planned.&lt;/p&gt;

&lt;p&gt;Waterfall planned heavily before implementation. Agile reacted against that and moved planning closer to delivery. User stories, architecture decision records, RFCs, tickets, acceptance criteria, and sprint planning all came from the same basic problem:&lt;/p&gt;

&lt;p&gt;Software is too expensive to build by accident.&lt;/p&gt;

&lt;p&gt;Spec-driven development starts with the knowns.&lt;/p&gt;

&lt;p&gt;That gives it part of the strength of waterfall: precision, completeness where completeness is possible, and a serious attempt to make expectations explicit before expensive work starts.&lt;/p&gt;

&lt;p&gt;But it should not inherit waterfall's weakness.&lt;/p&gt;

&lt;p&gt;The spec is not written in stone upfront.&lt;/p&gt;

&lt;p&gt;It is a living document.&lt;/p&gt;

&lt;p&gt;That is where it takes the useful part of agile. You do not need to describe the full final system before the first useful implementation happens. You describe what is known now, implement against that, learn, and extend the spec for the next feature, the next workflow, the next constraint, or the next architectural decision.&lt;/p&gt;

&lt;p&gt;The important difference is that iteration does not mean guessing.&lt;/p&gt;

&lt;p&gt;Each iteration should still make its knowns explicit before implementation starts.&lt;/p&gt;

&lt;p&gt;Vibe coding is one symptom. It works for exploration, but it breaks as a delivery model when the human expects the AI to know business intent, architecture rules, security constraints, and product expectations that were never provided. Spec-driven development changes the question from "can the AI guess enough?" to "did we give the AI the right information, boundaries, and checks?"&lt;/p&gt;

&lt;p&gt;Planning used to align humans with humans.&lt;/p&gt;

&lt;p&gt;Spec-driven development must align humans with humans and humans with AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Human Work Moves Up A Level
&lt;/h2&gt;

&lt;p&gt;Once the knowns are explicit, the human role changes.&lt;/p&gt;

&lt;p&gt;The most valuable human work moves closer to purpose, business pressure, architecture, and risk.&lt;/p&gt;

&lt;p&gt;The work becomes explaining what outcome matters, where the tradeoffs are, and how the existing system behaves. It also means making boundaries, failure modes, and delivery constraints explicit: maintainability, operations, compliance, ownership, and risk.&lt;/p&gt;

&lt;p&gt;That does not make engineering or architecture judgment less important.&lt;/p&gt;

&lt;p&gt;It makes that thinking more important.&lt;/p&gt;

&lt;p&gt;If you only measure your value by how many lines you personally write, AI feels like a threat. If you measure your value by how well you turn business pressure into robust systems, AI becomes leverage.&lt;/p&gt;

&lt;p&gt;The human job is no longer only to produce the implementation.&lt;/p&gt;

&lt;p&gt;The human job is to make the expectation clear enough that implementation can be delegated, checked, explained, and improved.&lt;/p&gt;

&lt;p&gt;The better humans define purpose, boundaries, and risks, the better AI can handle the workflow: write the spec, plan the work, implement scoped tickets, and review the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good Specs Make Expectations Executable
&lt;/h2&gt;

&lt;p&gt;The obvious question comes quickly: how do I get to a good spec?&lt;/p&gt;

&lt;p&gt;That was hard for me.&lt;/p&gt;

&lt;p&gt;My first real contact with spec-driven development came through &lt;a href="https://kiro.dev/docs/specs/" rel="noopener noreferrer"&gt;Kiro specs&lt;/a&gt;. Later I looked at &lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;GitHub Spec Kit&lt;/a&gt;, &lt;a href="https://docs.tessl.io/common-workflows/spec-driven-development-with-tessl" rel="noopener noreferrer"&gt;Tessl's spec-driven development workflow&lt;/a&gt;, and similar approaches. Conceptually, I liked the direction immediately: requirements, design, tasks, implementation, review.&lt;/p&gt;

&lt;p&gt;But in practice, it still required too much manual human work.&lt;/p&gt;

&lt;p&gt;Too much writing. Too many manual steps. Too much structure that busy teams will not consistently fill with enough quality under delivery pressure.&lt;/p&gt;

&lt;p&gt;Those tools are valuable from a conceptual and technical perspective. But for enterprise delivery, the workflow has to be feasible. If it depends on humans manually writing perfect specs, tickets, and review notes every time, it will fail.&lt;/p&gt;

&lt;p&gt;That is why I think the spec workflow itself must be AI-assisted and automated.&lt;/p&gt;

&lt;p&gt;The spec is the main place where human judgment belongs, but the AI should help create it, challenge it, repair it, and review it. Otherwise humans still have to write specs, repair specs, collect context, split plans into tickets, police scope, compare implementation against intent, and keep teams aligned.&lt;/p&gt;

&lt;p&gt;That is exactly the work that becomes inconsistent when people are tired, rushed, or under delivery pressure.&lt;/p&gt;

&lt;p&gt;This is why I created &lt;a href="https://github.com/sebastianwessel/skills" rel="noopener noreferrer"&gt;sebastianwessel/skills&lt;/a&gt;: not to replace senior judgment, but to move senior judgment into reusable context, gates, and instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills Turn Judgment Into Guidance
&lt;/h3&gt;

&lt;p&gt;My approach is not to ask an AI to "write a good spec" and hope for the best.&lt;/p&gt;

&lt;p&gt;The skill gives the AI the operating model for writing and maintaining specs. It tells the AI what good means: concise, gap-free, traceable, implementation-ready, and explicit enough that agents do not have to guess.&lt;/p&gt;

&lt;p&gt;The human provides the intent, context, constraints, and decisions.&lt;/p&gt;

&lt;p&gt;The AI uses the skill to turn that input into precise specs, ask focused questions, find gaps, structure the material, maintain consistency, and keep the source of truth clean as the project evolves.&lt;/p&gt;

&lt;p&gt;That is the division of work I want: humans decide what matters, and AI handles the disciplined spec work required to make those expectations usable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Specs Is An Iterative Loop
&lt;/h3&gt;

&lt;p&gt;Writing a spec is not one big document dump.&lt;/p&gt;

&lt;p&gt;It is an iterative loop from top to bottom.&lt;/p&gt;

&lt;p&gt;I start with the overall purpose: what should exist, why it matters, who it is for, which business outcome it supports, and which constraints are already known.&lt;/p&gt;

&lt;p&gt;Then I move down into features, workflows, boundaries, interfaces, data, failure modes, security, operations, and acceptance criteria.&lt;/p&gt;

&lt;p&gt;At each level, I do not use the AI only as a task fulfiller.&lt;/p&gt;

&lt;p&gt;I use it as a sparring partner. I want it to challenge my proposal, push back on weak assumptions, and tell me where my idea is not precise enough yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where are the gaps?&lt;/li&gt;
&lt;li&gt;What would you challenge in this proposal?&lt;/li&gt;
&lt;li&gt;Which unhappy paths are missing?&lt;/li&gt;
&lt;li&gt;What should happen when this step fails?&lt;/li&gt;
&lt;li&gt;Which interfaces or dependencies are unclear?&lt;/li&gt;
&lt;li&gt;Which risks would you expect in production?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where AI is extremely useful. It can hold a larger context than a human can comfortably keep in mind. It can follow dependency trees, interfaces, flows, and edge cases. It can repeatedly ask the boring questions humans skip when they are tired.&lt;/p&gt;

&lt;p&gt;This is what my &lt;code&gt;spec-architect&lt;/code&gt; skill is built around. A spec is not ready because it sounds plausible. It is ready when an agent can implement from it without deciding behavior, interfaces, failures, security, data, recovery, release, observability, or tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reviewing Specs Is Also An AI Workflow
&lt;/h3&gt;

&lt;p&gt;I do not want to manually read every spec document line by line.&lt;/p&gt;

&lt;p&gt;That is not the best use of human attention.&lt;/p&gt;

&lt;p&gt;Instead, I ask the AI precise questions. Explain this flow step by step. Show me the risky assumptions. Visualize the dependencies. Find unclear interfaces. Suggest simpler patterns. Look for scaling and performance issues. Check whether the acceptance criteria actually prove the behavior.&lt;/p&gt;

&lt;p&gt;Basically, I ask the AI the questions I would ask during a manual review.&lt;/p&gt;

&lt;p&gt;The important difference is that those questions can become reusable: a skill, a checklist, a Markdown file, a review gate, or a project convention.&lt;/p&gt;

&lt;p&gt;That is the leverage.&lt;/p&gt;

&lt;p&gt;Human review moves from reading everything manually to improving the review system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Planning Makes Work Delegatable
&lt;/h2&gt;

&lt;p&gt;A spec is not yet a delivery plan.&lt;/p&gt;

&lt;p&gt;Planning turns the source of truth into work that can be delegated.&lt;/p&gt;

&lt;p&gt;This is what teams have tried to do for years with Jira tickets, refinements, planning sessions, and acceptance criteria. The intention was right: create work items with enough context that implementation becomes predictable.&lt;/p&gt;

&lt;p&gt;But it was always expensive.&lt;/p&gt;

&lt;p&gt;Good tickets require real context: acceptance criteria, boundaries, dependencies, and enough detail to reduce interpretation without becoming impossible to maintain.&lt;/p&gt;

&lt;p&gt;In many teams, that never worked consistently.&lt;/p&gt;

&lt;p&gt;Spec-driven planning can finally achieve what agile planning tried to achieve because the planning can be generated from the spec.&lt;/p&gt;

&lt;p&gt;The human effort moves into spec creation, where it belongs.&lt;/p&gt;

&lt;p&gt;That is a better place for it. The spec is the long-lived source of truth. Plans and tickets are temporary: they orchestrate and track a short implementation period.&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;spec-implementation-planner&lt;/code&gt; skill exists for that step.&lt;/p&gt;

&lt;p&gt;The planner should slice work horizontally first to create foundations and interfaces for safe parallel work. Then it should create vertical tickets that are small, isolated, and end-to-end useful. Each slice should move the system toward a working feature, not just produce disconnected technical fragments.&lt;/p&gt;

&lt;p&gt;The core of planning is enablement: make autonomous implementation safe, parallel, traceable, and focused on working outcomes.&lt;/p&gt;

&lt;p&gt;If a ticket cannot be filled from the approved spec, the answer is not to let the implementer guess.&lt;/p&gt;

&lt;p&gt;The answer is to go back to the spec.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation Should Not Invent
&lt;/h2&gt;

&lt;p&gt;Implementation is where AI can move very fast.&lt;/p&gt;

&lt;p&gt;That speed is only useful when the work is isolated and precise.&lt;/p&gt;

&lt;p&gt;An implementation ticket should be a focused piece of work with defined scope, defined interfaces, clear acceptance criteria, and no room for interpretation. The implementer should know what can be read, what can be changed, which dependencies must be ready, and how success will be verified.&lt;/p&gt;

&lt;p&gt;That is why this works so well with test-driven development.&lt;/p&gt;

&lt;p&gt;Explicit interfaces and expectations make it much easier to write the tests first, implement against them, and verify that the behavior matches the ticket. The AI can iterate inside a narrow boundary instead of roaming through the system.&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;spec-ticket-implementation&lt;/code&gt; skill is intentionally strict: implement one approved ticket, respect read and write scope, check dependencies, cover happy and unhappy paths, verify acceptance criteria, and stop when behavior is missing.&lt;/p&gt;

&lt;p&gt;An AI implementer should not silently become the person deciding product behavior, architecture, security, and release strategy because the ticket was vague.&lt;/p&gt;

&lt;p&gt;If implementation needs a decision that is not in the spec or ticket, that is not implementation work.&lt;/p&gt;

&lt;p&gt;That is a spec or plan gap.&lt;/p&gt;

&lt;p&gt;The output should include evidence: changed files, tests, verification commands, acceptance coverage, and blockers. That evidence becomes the input for review.&lt;/p&gt;




&lt;h2&gt;
  
  
  Review Becomes A Trust Pipeline
&lt;/h2&gt;

&lt;p&gt;Review is not one final human checkpoint.&lt;/p&gt;

&lt;p&gt;Review happens across the whole pipeline.&lt;/p&gt;

&lt;p&gt;Specs are reviewed for gaps and ambiguity. Plans are reviewed for scope, dependencies, and parallel safety. Implementation tickets have preflight and done gates. Final implementation review checks whether the implemented paths still match the approved spec and plan.&lt;/p&gt;

&lt;p&gt;That is what &lt;code&gt;spec-implementation-review&lt;/code&gt; is for. It compares the implementation path to the specs, verifies there was no drift, checks that the expected behavior was implemented, and makes sure the different ticket slices work together to fulfill the original expectations.&lt;/p&gt;

&lt;p&gt;A reviewer can ask AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the spec itself clear, complete, and free of contradictions?&lt;/li&gt;
&lt;li&gt;Does the implementation match the approved spec, plan, and architecture?&lt;/li&gt;
&lt;li&gt;Which edge cases, bugs, failure modes, or evidence gaps remain?&lt;/li&gt;
&lt;li&gt;Are security, compliance, data, and operational concerns covered?&lt;/li&gt;
&lt;li&gt;Can findings route back to a ticket, plan gap, or spec gap?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where I would push hard against a common argument.&lt;/p&gt;

&lt;p&gt;"I am better than AI at reviewing code" is usually the wrong conclusion.&lt;/p&gt;

&lt;p&gt;When a human finds something AI missed, that does not prove the human is generally better at review. It usually proves that the human had context, experience, suspicion, or focus that was not available to the AI in that run.&lt;/p&gt;

&lt;p&gt;That is not magic. That is knowledge.&lt;/p&gt;

&lt;p&gt;The same is true between humans. An expert sees things a junior developer misses because the expert has more context, patterns, and prior failures in their head. AI is not different in that regard. It needs the right knowledge, the right focus, and the right review instruction.&lt;/p&gt;

&lt;p&gt;So the useful question is not "why did the AI miss this?"&lt;/p&gt;

&lt;p&gt;The useful question is: "what did I fail to give the AI, and how do I make sure it has that knowledge next time?"&lt;/p&gt;

&lt;p&gt;Human intervention in planning, implementation, or review should increasingly prioritize improving the AI capability itself: better specs, better context packages, better reusable prompts, better gates, better tests, and better review skills.&lt;/p&gt;

&lt;p&gt;That is where the cost changes.&lt;/p&gt;

&lt;p&gt;A human review is expensive, limited, and easy to skip under pressure. A reusable AI-supported check can become part of the regular workflow. It can run more often. It can be improved when it misses something. It can grow with the system.&lt;/p&gt;

&lt;p&gt;Over time, the important questions are no longer asked only a few times by tired humans. They become integrated into delivery as a repetitive pipeline that runs again and again.&lt;/p&gt;

&lt;p&gt;This is how software becomes better tested, more reliable, and more predictable.&lt;/p&gt;

&lt;p&gt;The goal is to make AI trustable: not by hoping, but by giving it context, checks, and feedback loops.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Works For Me
&lt;/h2&gt;

&lt;p&gt;This is still a young workflow, but a few patterns already work very well for me.&lt;/p&gt;

&lt;p&gt;Use different reasoning levels for different jobs. I like medium reasoning for creating and extending specs because it keeps momentum. For review, cleanup, refactoring, gap finding, and quality gates, I prefer high reasoning. That is where I want the AI to slow down, follow paths step by step, judge honestly, and not optimize for making me happy.&lt;/p&gt;

&lt;p&gt;Always ask questions. Ask the AI to explain flows, find gaps, challenge your proposal, inspect unhappy paths, check scaling limits, and self-reflect on weak evidence. I explicitly ask it to reason, judge, disagree when needed, and be honest about uncertainty.&lt;/p&gt;

&lt;p&gt;Use independent models when possible. Write with one model family and review with another. My personal experience is that GPT-5.5-style models write stronger specs for this kind of work because they follow instructions more strictly and are less likely to optimize for pleasing the user. Claude models often feel more eager to start implementing or guess missing intent. That can be useful in other modes, but for specs I want discipline first.&lt;/p&gt;

&lt;p&gt;Persist what works. If a review question is useful once, turn it into a checklist, skill, Markdown file, or project convention. Do not rely on memory. The point is to make the AI better next time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop asking: how do I improve this implementation? Start asking: how do I improve the AI system that creates, checks, and improves implementations?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That mindset shift is the whole game.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Questions To Handle
&lt;/h2&gt;

&lt;p&gt;I do not think spec-driven development breaks when the workflow above is followed seriously.&lt;/p&gt;

&lt;p&gt;But there are still operating questions teams have to solve.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team spec creation. How do multiple people contribute without losing alignment? Who owns the source of truth? How do product, architecture, security, and engineering decisions get merged without becoming a noisy document pile?&lt;/li&gt;
&lt;li&gt;Project knowledge base. Specs should grow beyond feature documents. They should include business context, project history, architecture rules, operational decisions, security assumptions, and the reusable knowledge agents need to work well.&lt;/li&gt;
&lt;li&gt;Versioning. Git is a good foundation because specs should live close to the system and be reviewed like other source artifacts. But teams still need better ways to quickly understand how expectations changed over time, which decisions moved, and which implementation plans were affected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those questions are not reasons to avoid spec-driven development.&lt;/p&gt;

&lt;p&gt;They are reasons to treat specs as a serious delivery asset.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The enterprise impact is not "developers wrote more documents".&lt;/p&gt;

&lt;p&gt;That would be a terrible result.&lt;/p&gt;

&lt;p&gt;The impact is delivery that can be explained before, during, and after implementation. Specs make expectations, scope, and tradeoffs visible earlier. They make work clearer, decisions more explicit, and AI adoption less like a pile of experiments and more like a delivery capability an organization can understand.&lt;/p&gt;

&lt;p&gt;But the real game changer is the economics.&lt;/p&gt;

&lt;p&gt;Spec-driven development makes speed and quality move together instead of fighting each other. Implementation gets faster, but quality control also becomes more repeatable. Reviews, checks, gap analysis, acceptance validation, and proof can run as an autonomous pipeline instead of depending only on scarce human review time.&lt;/p&gt;

&lt;p&gt;The same quality questions can be asked again and again. The review system can improve when it misses something. The knowledge can be reused across teams and projects. Quality control becomes cheaper, more frequent, and more consistent over time.&lt;/p&gt;

&lt;p&gt;Personally, I have fully switched my personal and open-source work to spec-driven development.&lt;/p&gt;

&lt;p&gt;That shift was honestly mind-blowing.&lt;/p&gt;

&lt;p&gt;The real unlock was not "AI writes code now". It was changing my reaction when the AI got something wrong.&lt;/p&gt;

&lt;p&gt;Instead of blaming the AI, I started asking: what did I fail to give it? Which context was missing? Which expectation was unclear? Which review question should become reusable? Which part of the workflow needs to improve so this does not happen again?&lt;/p&gt;

&lt;p&gt;I get better results than I would by manually coding everything myself, and I deliver faster. Work that previously would have taken me months, sometimes years, can now move toward a production-ready product in a couple of focused two-week sprints when the spec, plan, implementation, and review loop is running well.&lt;/p&gt;

&lt;p&gt;More importantly, I can work on multiple projects in parallel. I do not need to personally write every line of code anymore. I improve the specs, verify the results, improve the feedback loops, and improve the skills. That improves the AI system, and the better AI system improves delivery again.&lt;/p&gt;

&lt;p&gt;That is the compounding effect.&lt;/p&gt;

&lt;p&gt;This is why I think judging AI only by today's visible failures is strategically wrong.&lt;/p&gt;

&lt;p&gt;The model you use today was trained, tested, packaged, and shipped before today. The tools around it were designed before today's workflows became obvious. Compare AI-assisted development one year ago with what is possible now, then imagine the next 3, 6, or 12 months.&lt;/p&gt;

&lt;p&gt;That matters because the product you start building today will go live in the future, not in the world you planned it in. By the time it reaches users, the market, tooling, model capabilities, and expectations around AI-assisted delivery may already have moved.&lt;/p&gt;

&lt;p&gt;So the pressure is not only to deliver faster. The pressure is to deliver faster while improving quality.&lt;/p&gt;

&lt;p&gt;Spec-driven development is the workflow that makes that possible for me: move faster, keep the source of truth clear, automate more quality control, and improve the AI system while the product evolves.&lt;/p&gt;

&lt;p&gt;Teams that only ask whether today's AI can replace yesterday's workflow are already late.&lt;/p&gt;

&lt;p&gt;So here is the mindset shift I would leave you with.&lt;/p&gt;

&lt;p&gt;Whenever you catch yourself doing manual work, ask why this specific work is still manual.&lt;/p&gt;

&lt;p&gt;What outcome is it supposed to create? What judgment, context, evidence, or constraint is hidden inside it? Why can AI not do it on your behalf yet? What would AI need to know, check, or prove to take it over safely? And what can you do now to make that possible next time?&lt;/p&gt;

&lt;p&gt;That is the real work: turning repeated human effort into explicit knowledge, reusable instructions, review gates, and automated feedback loops.&lt;/p&gt;

&lt;p&gt;Bad specs are bureaucracy.&lt;/p&gt;

&lt;p&gt;Good specs are leverage.&lt;/p&gt;

&lt;p&gt;And if we want AI agents to do more real engineering work, we need to become much better at giving them the kind of work they can actually do well.&lt;/p&gt;




&lt;p&gt;If this way of working resonates with you, I also maintain the skills I use for spec-driven AI development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Skills repository:&lt;/strong&gt; &lt;a href="https://github.com/sebastianwessel/skills" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My website:&lt;/strong&gt; &lt;a href="https://sebastianwessel.de" rel="noopener noreferrer"&gt;https://sebastianwessel.de&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The skills cover the workflow I described in this article: spec creation, implementation planning, scoped ticket implementation, and review against the approved spec.&lt;/p&gt;

&lt;p&gt;I write about enterprise software, AI-assisted delivery, open source, and the shift from manual coding to improving the systems that guide AI agents.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Conclusion on using a QuickJS sandbox</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 07 Jul 2024 13:24:06 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/conclusion-on-using-a-quickjs-sandbox-2gm3</link>
      <guid>https://dev.to/sebastian_wessel/conclusion-on-using-a-quickjs-sandbox-2gm3</guid>
      <description>&lt;p&gt;Creating the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; has been a journey in addressing real needs I've encountered in my development career, especially as AI and LLMs become more prevalent in our workflows. It's designed to empower developers to push the boundaries of what's possible in JavaScript applications without compromising on security or code quality.&lt;/p&gt;

&lt;p&gt;Whether you're building a complex web application, a desktop app, an AI-powered coding platform, or simply need a safe space to run and test untrusted scripts with Node.js-like capabilities, I believe this package offers the tools you need to execute JavaScript with confidence.&lt;/p&gt;

&lt;p&gt;I encourage you to give the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; a try in your next project, especially if you're working with AI and LLMs. Experience the peace of mind that comes with secure code execution, coupled with the convenience of Node.js module support and robust testing capabilities.&lt;/p&gt;

&lt;p&gt;As we continue to explore the frontiers of AI and secure code execution, the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; stands ready to support your innovative projects. I'm always open to feedback and suggestions from fellow developers. Let's make secure JavaScript execution easier and more accessible together, paving the way for the next generation of AI-powered applications!&lt;/p&gt;




&lt;p&gt;Documentation: &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;https://sebastianwessel.github.io/quickjs/&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/sebastianwessel/quickjs" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/quickjs&lt;/a&gt;&lt;br&gt;
Submit Feedback: &lt;a href="https://github.com/sebastianwessel/quickjs/issues" rel="noopener noreferrer"&gt;Create an Issue on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sandbox</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webassembly</category>
    </item>
    <item>
      <title>Running Tests Inside QuickJS with TestRunner</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 07 Jul 2024 13:23:56 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/running-tests-inside-quickjs-with-testrunner-gk5</link>
      <guid>https://dev.to/sebastian_wessel/running-tests-inside-quickjs-with-testrunner-gk5</guid>
      <description>&lt;p&gt;The TestRunner is a lightweight testing library that allows you to write and run tests for JavaScript code with ease. It provides a simple interface for defining test suites, tests, and hooks with support for asynchronous code and configurable timeouts. The library also includes Chai's &lt;code&gt;expect&lt;/code&gt; function for assertions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Running Tests in a Sandbox
&lt;/h2&gt;

&lt;p&gt;Running tests in a sandbox environment like QuickJS provides several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Each test runs in a separate environment, ensuring no side effects between tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: A sandboxed environment limits the potential for malicious code to affect the host system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Ensures that tests are run in a controlled environment, leading to more consistent and reliable results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Importing the Library
&lt;/h2&gt;

&lt;p&gt;To use the TestRunner library in your tests, you need to import it. You also need to ensure that the result of &lt;code&gt;runTests&lt;/code&gt; is exported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testRunner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing Tests
&lt;/h2&gt;

&lt;p&gt;Developers should feel comfortable using the TestRunner as it follows the general approach used in the JavaScript testing ecosystem, similar to Mocha or Jasmine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining a Test Suite
&lt;/h3&gt;

&lt;p&gt;A test suite is defined using the &lt;code&gt;describe&lt;/code&gt; function. Inside a test suite, you can define multiple tests using the &lt;code&gt;it&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sample Test Suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fail this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Hooks
&lt;/h3&gt;

&lt;p&gt;Hooks are functions that run before or after tests in a suite. The available hooks are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;beforeAll&lt;/code&gt;: Runs once before all tests in the suite.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterAll&lt;/code&gt;: Runs once after all tests in the suite.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;beforeEach&lt;/code&gt;: Runs before each test in the suite.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterEach&lt;/code&gt;: Runs after each test in the suite.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Suite with Hooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running beforeAll hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running afterAll hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running beforeEach hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running afterEach hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Asynchronous Tests and Hooks
&lt;/h3&gt;

&lt;p&gt;The TestRunner supports asynchronous tests and hooks. Simply return a promise or use async/await in your test or hook functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Asynchronous Test Suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this async test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Chai's &lt;code&gt;expect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The TestRunner includes Chai's &lt;code&gt;expect&lt;/code&gt; function for writing assertions in your tests. The &lt;code&gt;expect&lt;/code&gt; function is part of Chai's BDD (Behavior-Driven Development) interface. You can learn more about Chai's &lt;code&gt;expect&lt;/code&gt; function and its usage at the &lt;a href="https://www.chaijs.com/api/bdd/" rel="noopener noreferrer"&gt;Chai documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples of &lt;code&gt;expect&lt;/code&gt; Usage
&lt;/h3&gt;

&lt;p&gt;Here are some examples of using Chai's &lt;code&gt;expect&lt;/code&gt; function in your tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should assert true is true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should assert equality&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Tests
&lt;/h2&gt;

&lt;p&gt;To run the tests, you need to call &lt;code&gt;runTests&lt;/code&gt; and export the result. You can specify a custom timeout for tests and hooks by passing the timeout value in milliseconds as an argument to &lt;code&gt;runTests&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testRunner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sample Test Suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fail this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runTests&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Timeout
&lt;/h3&gt;

&lt;p&gt;You can set a custom timeout for all tests and hooks in the suite by passing the timeout value to &lt;code&gt;runTests&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testRunner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sample Test Suite with Custom Timeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fail this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Set timeout to 10 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example Usage
&lt;/h2&gt;

&lt;p&gt;Here is an example demonstrating the complete setup and usage of the TestRunner library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testRunner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example Test Suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running global beforeAll hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running global afterAll hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running global beforeEach hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running global afterEach hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nested Suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running nested beforeAll hook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fail this test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass this test outside nested suite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runTests&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The TestRunner library provides a straightforward way to define and run tests for your JavaScript code. With support for asynchronous operations, configurable timeouts, and Chai's &lt;code&gt;expect&lt;/code&gt; function for assertions, you can ensure that your tests are robust and reliable. Happy testing!&lt;/p&gt;




&lt;p&gt;Documentation: &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;https://sebastianwessel.github.io/quickjs/&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/sebastianwessel/quickjs" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/quickjs&lt;/a&gt;&lt;br&gt;
Submit Feedback: &lt;a href="https://github.com/sebastianwessel/quickjs/issues" rel="noopener noreferrer"&gt;Create an Issue on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>typescript</category>
      <category>sandbox</category>
    </item>
    <item>
      <title>Secure Data Transformation in ETL Pipelines</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 07 Jul 2024 13:23:46 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/secure-data-transformation-in-etl-pipelines-1o94</link>
      <guid>https://dev.to/sebastian_wessel/secure-data-transformation-in-etl-pipelines-1o94</guid>
      <description>&lt;p&gt;One particularly powerful use case for the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; is in the realm of data pipelines and ETL (Extract, Transform, Load) processes.&lt;/p&gt;

&lt;p&gt;Imagine a scenario where you're building a flexible data transformation service. Your platform needs to handle incoming data and transform it into a specific format, but here's the catch: the transformation logic is provided by the users themselves in the form of custom JavaScript code.&lt;/p&gt;

&lt;p&gt;This presents a significant challenge. On one hand, you want to offer users the flexibility to define their own transformation logic. On the other hand, you need to ensure that this user-provided code doesn't compromise the security or integrity of your system. This is where the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; truly shines.&lt;/p&gt;

&lt;p&gt;Here's how you might implement such a system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;quickJS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sebastianwessel/quickjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupTransformationEnvironment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRuntime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;quickJS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createRuntime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;allowFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Disable network access for security&lt;/span&gt;
    &lt;span class="na"&gt;allowFs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Disable file system access&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Add any other environment variables needed for transformation&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transformData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transformationCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupTransformationEnvironment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;evalCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrappedCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    const transform = (data) =&amp;gt; {
      &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transformationCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    };
    export default transform(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;);
  `&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;evalCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrappedCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transformedData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userProvidedTransformationCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  // User-defined transformation logic
  return {
    fullName: data.name,
    isAdult: data.age &amp;gt;= 18,
    location: data.city.toUpperCase()
  };
`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;transformData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userProvidedTransformationCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We set up a secure runtime environment using QuickJS, disabling potentially dangerous features like network and file system access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;transformData&lt;/code&gt; function takes input data and user-provided transformation code as parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We wrap the user's code in a function and pass the input data to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The code is executed in the sandboxed environment, ensuring that it can't affect the rest of your system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The transformed data is returned, or an error is caught and reported if the transformation fails.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach offers several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: User-provided code runs in a sandbox, protecting your system from malicious or poorly written scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Users can define complex transformation logic tailored to their specific needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control&lt;/strong&gt;: You can precisely limit what the transformation code can do (e.g., no network requests, no file system access).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Errors in user code are caught and handled gracefully, preventing them from crashing your service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; in this way, you can build a robust, flexible, and secure data transformation service. This pattern can be extended to various other scenarios where you need to run user-provided code safely, such as custom report generation, dynamic data analysis, or even building a secure coding playground for educational purposes.&lt;/p&gt;

&lt;p&gt;The ability to execute arbitrary JavaScript securely opens up a world of possibilities for building flexible, user-customizable systems without compromising on security. Whether you're dealing with ETL processes, building a low-code platform, or creating any system that needs to safely execute user-defined logic, the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; provides the tools you need to do so with confidence.&lt;/p&gt;




&lt;p&gt;Documentation: &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;https://sebastianwessel.github.io/quickjs/&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/sebastianwessel/quickjs" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/quickjs&lt;/a&gt;&lt;br&gt;
Submit Feedback: &lt;a href="https://github.com/sebastianwessel/quickjs/issues" rel="noopener noreferrer"&gt;Create an Issue on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webassembly</category>
      <category>programming</category>
    </item>
    <item>
      <title>Empowering AI: The QuickJS Package for LLM Tool Calling</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 07 Jul 2024 13:23:36 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/empowering-ai-the-quickjs-package-for-llm-tool-calling-n1o</link>
      <guid>https://dev.to/sebastian_wessel/empowering-ai-the-quickjs-package-for-llm-tool-calling-n1o</guid>
      <description>&lt;p&gt;As AI continues to evolve, the ability to safely execute code becomes increasingly important. The &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; is perfectly positioned to fill this gap, offering a powerful solution for implementing tool calling capabilities in AI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why QuickJS for AI Tool Calling?
&lt;/h2&gt;

&lt;p&gt;When developing AI systems that interact with user-provided or dynamically generated code, security is paramount. The &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package&lt;/a&gt; provides a secure sandbox that allows LLMs to execute JavaScript code safely, opening up a world of possibilities for AI-powered tools and applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits for AI Developers:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secure Execution&lt;/strong&gt;: Run AI-generated or user-provided JavaScript code without risking your main application or server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Integration&lt;/strong&gt;: Easily integrate with various LLM frameworks and AI platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich Environment&lt;/strong&gt;: Provide a Node.js-like environment for more complex tool implementations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Leverage the speed of QuickJS for quick code execution in AI workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Sandbox&lt;/strong&gt;: Control what capabilities are available to the AI, ensuring safe and appropriate tool use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example: Implementing an AI Code Executor with &lt;a href="https://js.langchain.com/v0.1/docs/get_started/installation/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here's an example of how you can use the QuickJS package with LangChain to create a powerful AI code execution tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatOpenAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamicStructuredTool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/core/tools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;quickJS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sebastianwessel/quickjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize QuickJS (this should be done once)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initQuickJS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRuntime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;quickJS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createRuntime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;allowFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Disable network access for safety&lt;/span&gt;
    &lt;span class="na"&gt;allowFs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Allow file system operations if needed&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;AI_VERSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Create a QuickJS runtime&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quickJSRuntime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initQuickJS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Define the schema for our JavaScript execution tool&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsExecutionSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The JavaScript code to execute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create the JavaScript execution tool&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsExecutionTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DynamicStructuredTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript_executor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Executes JavaScript code and returns the result.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jsExecutionSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;func&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;evalCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;quickJSRuntime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;evalCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Error executing code: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the language model with the tool&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;llmWithTools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jsExecutionTool&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Calculate the 10th Fibonacci number using JavaScript.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;llmWithTools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI Response:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tool Calls:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;additional_kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// If there's a tool call, we can execute it&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;additional_kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;additional_kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;jsExecutionTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCall&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tool Execution Result:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows the AI to write and execute JavaScript code safely within the QuickJS sandbox, providing a powerful and flexible tool for various tasks. It combines the strengths of LangChain for AI interactions with the security and flexibility of the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;QuickJS package &lt;/a&gt;for code execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing AI Capabilities
&lt;/h2&gt;

&lt;p&gt;By integrating the QuickJS package into your AI toolkit, you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Implement Complex Tools&lt;/strong&gt;: Allow your AI to write and execute more sophisticated tools on the fly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe Code Testing&lt;/strong&gt;: Let AI assistants test and debug code in a controlled environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Data Processing&lt;/strong&gt;: Enable AI to process data using custom JavaScript functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Coding Tutorials&lt;/strong&gt;: Create AI-powered coding education platforms with live code execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure Serverless Functions&lt;/strong&gt;: Implement AI-generated serverless functions with built-in security.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Documentation: &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;https://sebastianwessel.github.io/quickjs/&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/sebastianwessel/quickjs" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/quickjs&lt;/a&gt;&lt;br&gt;
Submit Feedback: &lt;a href="https://github.com/sebastianwessel/quickjs/issues" rel="noopener noreferrer"&gt;Create an Issue on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Execute JavaScript in a WebAssembly QuickJS Sandbox</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 07 Jul 2024 13:23:22 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/execute-javascript-in-a-webassembly-quickjs-sandbox-14nn</link>
      <guid>https://dev.to/sebastian_wessel/execute-javascript-in-a-webassembly-quickjs-sandbox-14nn</guid>
      <description>&lt;h1&gt;
  
  
  Typescript QuickJS Package: Empowering Secure JavaScript Execution in AI and Beyond
&lt;/h1&gt;

&lt;p&gt;As an experienced developer who understands the needs of the community, I've created the QuickJS package to address a critical challenge in modern software development: executing JavaScript securely while maintaining flexibility and performance across various platforms. This TypeScript library offers a robust solution for running JavaScript code in a secure sandbox environment, with particular benefits for AI and large language model (LLM) applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Genesis of the QuickJS Package
&lt;/h2&gt;

&lt;p&gt;My journey began with QuickJS, an impressive small and efficient JavaScript engine originally written in C by Fabrice Bellard and Charlie Gordon. While QuickJS itself is a C program, I saw an opportunity to make it more accessible to the JavaScript and TypeScript community.&lt;/p&gt;

&lt;p&gt;The foundation of my package is built upon the excellent work done by the quickjs-emscripten project (&lt;a href="https://github.com/justjake/quickjs-emscripten" rel="noopener noreferrer"&gt;https://github.com/justjake/quickjs-emscripten&lt;/a&gt;). They've done the crucial work of compiling QuickJS to WebAssembly, enabling its use in web and Node.js environments. What I've created is a high-level abstraction layer around this WebAssembly implementation, designed with developer experience in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Sandbox Built for Developers, by a Developer
&lt;/h2&gt;

&lt;p&gt;As someone who's been in the trenches of software development, I understand the importance of a tool that's not only powerful but also easy to use. The QuickJS package creates an isolated environment where untrusted code can run freely, separated from your main application, whether it's running in a browser or on a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features Designed with You in Mind
&lt;/h2&gt;

&lt;p&gt;When developing this package, I focused on bringing together features that I, as a developer, would want:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node-like Module Support&lt;/strong&gt;: I've included support for core modules similar to Node.js, including &lt;code&gt;node:fs&lt;/code&gt;, &lt;code&gt;node:assert&lt;/code&gt;, &lt;code&gt;node:util&lt;/code&gt;, and &lt;code&gt;node:path&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetch API&lt;/strong&gt;: Because making HTTP requests is such a common need, I've made sure &lt;code&gt;fetch&lt;/code&gt; is available within the sandbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Module Support&lt;/strong&gt;: Knowing that every project has unique needs, I've made it possible to use your own modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual File System&lt;/strong&gt;: This allows for file operations without risking your actual file system - a must-have for secure environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript Compatibility&lt;/strong&gt;: As a TypeScript enthusiast, ensuring seamless integration with TypeScript projects was a priority.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Friendly API&lt;/strong&gt;: I've put a lot of effort into making the API intuitive and easy to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated TestRunner&lt;/strong&gt;: Testing is crucial, so I've included a lightweight testing library right in the sandbox.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The QuickJS Package in Action
&lt;/h2&gt;

&lt;p&gt;Here's a quick example of how you can use the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;quickJS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sebastianwessel/quickjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// General setup - do this once&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRuntime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;quickJS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Create a runtime instance for code execution&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;evalCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createRuntime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;allowFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;allowFs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;MY_ENV_VAR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;env var value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Execute code in the sandbox&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;evalCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  import { join } from 'node:path'
  import { writeFileSync, readFileSync } from 'node:fs'
  import assert from 'node:assert'

  const fn = async () =&amp;gt; {
    console.log(join('src','dist'))
    console.log(env.MY_ENV_VAR)

    // Using node:fs
    writeFileSync('/test.txt', 'Hello, QuickJS!')
    const content = readFileSync('/test.txt', 'utf8')
    assert.strictEqual(content, 'Hello, QuickJS!')

    // Using fetch
    const url = new URL('https://example.com')
    const f = await fetch(url)
    return f.text()
  }

  export default await fn()
`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Leveraging Modern Tools for a Better Developer Experience
&lt;/h2&gt;

&lt;p&gt;In developing this package, I've leveraged some of the most exciting tools in the JavaScript ecosystem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bun&lt;/strong&gt;: This all-in-one JavaScript runtime has been instrumental in the development process, offering speed and simplicity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hono&lt;/strong&gt;: For handling HTTP requests, Hono has been a game-changer with its lightweight and performant approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Biome&lt;/strong&gt;: This toolchain has helped maintain code quality and consistency throughout the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've also used other modern tools like poolifier-web-worker for efficient multithreading, tshy for TypeScript package building, and autocannon for performance testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Applications
&lt;/h2&gt;

&lt;p&gt;From my experience, I've seen this package shine in various scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running user-provided scripts safely in web or desktop applications&lt;/li&gt;
&lt;li&gt;Executing plugins or extensions securely&lt;/li&gt;
&lt;li&gt;Creating sandboxed environments for coding challenges or educational platforms&lt;/li&gt;
&lt;li&gt;Testing potentially unsafe code snippets in development environments&lt;/li&gt;
&lt;li&gt;Implementing secure scripting capabilities in backend services&lt;/li&gt;
&lt;li&gt;Powering AI-driven code generation and execution in LLM applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance and Security: No Compromises
&lt;/h2&gt;

&lt;p&gt;As a developer, I know the importance of both performance and security. That's why I've ensured that this package, leveraging the lightweight QuickJS engine compiled to WebAssembly, offers excellent performance without sacrificing security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;I've made sure that getting started with the QuickJS package is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @sebastianwessel/quickjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more detailed documentation and examples, check out the &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; and the &lt;a href="https://github.com/sebastianwessel/quickjs/tree/main/example" rel="noopener noreferrer"&gt;example repository&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Documentation: &lt;a href="https://sebastianwessel.github.io/quickjs/" rel="noopener noreferrer"&gt;https://sebastianwessel.github.io/quickjs/&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/sebastianwessel/quickjs" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/quickjs&lt;/a&gt;&lt;br&gt;
Submit Feedback: &lt;a href="https://github.com/sebastianwessel/quickjs/issues" rel="noopener noreferrer"&gt;Create an Issue on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webassembly</category>
      <category>sandbox</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Design a SurrealDB schema and create a basic client for TypeScript</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Sun, 17 Sep 2023 13:04:15 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/how-to-design-a-surrealdb-schema-and-create-a-basic-client-for-typescript-o6o</link>
      <guid>https://dev.to/sebastian_wessel/how-to-design-a-surrealdb-schema-and-create-a-basic-client-for-typescript-o6o</guid>
      <description>&lt;p&gt;In the midst of a dynamic landscape of exciting new projects, one name shines bright — &lt;a href="https://surrealdb.com/" rel="noopener noreferrer"&gt;SurrealDB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's not just another database - it's touted as 'The ultimate multi-model database.'.&lt;br&gt;
Just last week, during the 'SurrealDB World' event, they celebrated the launch of their first production-ready version.&lt;/p&gt;

&lt;p&gt;What makes SurrealDB unique is its exceptional flexibility in data storage.&lt;br&gt;
Unlike traditional databases that force you to choose between fixed schemas or total chaos, SurrealDB lets you have the best of both worlds.&lt;br&gt;
You can define a table as schema-less while still specifying schema information for known fields.&lt;br&gt;
In this article, we'll explore SurrealDB's features and learn how to create a data schema and a basic client to make the most of this versatile database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Defining Your Schema
&lt;/h2&gt;

&lt;p&gt;When it comes to creating a schema definition, you have two primary methods at your disposal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using SurrealQL for Schema Definition
&lt;/h3&gt;

&lt;p&gt;If you're already familiar with SQL, you'll find SurrealQL to be quite approachable since it shares similarities with standard SQL.&lt;/p&gt;

&lt;p&gt;As described in the SurrealDB documentation, you can define your schema like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Let's start by creating a schema-full user table.&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;SCHEMAFULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Now, let's define some fields.&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;ASSERT&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With SurrealQL, you can structure your schema just the way you want it, making it a powerful tool for designing your data model.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, the same principles apply when defining a table as &lt;code&gt;SCHEMALESS&lt;/code&gt; instead of &lt;code&gt;SCHEMAFULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example in SurrealQL:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Let's create a schemaless user table.&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;SCHEMALESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Now, we'll proceed to define some fields.&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;ASSERT&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When it comes to field definition, SurrealQL offers a rich array of features. Some of the key ones include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specifying the data type&lt;/li&gt;
&lt;li&gt;Setting up validation rules&lt;/li&gt;
&lt;li&gt;Defining default values&lt;/li&gt;
&lt;li&gt;Applying data transformations&lt;/li&gt;
&lt;li&gt;Establishing a list of possible values, similar to enums&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more detailed information, be sure to explore the &lt;a href="https://surrealdb.com/docs/surrealql/statements/define" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Leveraging Surrealist.app
&lt;/h3&gt;

&lt;p&gt;When it comes to working with SurrealDB, there's one tool that I wholeheartedly recommend: &lt;a href="https://surrealist.app" rel="noopener noreferrer"&gt;Surrealist&lt;/a&gt;.&lt;br&gt;
This software is a game-changer for simplifying your SurrealDB experience.&lt;/p&gt;

&lt;p&gt;Surrealist offers a user-friendly interface that streamlines your interactions with SurrealDB. Not only does it come in a convenient browser version, but it also offers the powerful &lt;strong&gt;Surrealist Desktop&lt;/strong&gt; application.&lt;/p&gt;

&lt;p&gt;Surrealist Desktop takes your SurrealDB workflow to the next level by introducing a range of additional features, including the highly versatile &lt;strong&gt;Designer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhr5pmqcg7ndylv4mkap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhr5pmqcg7ndylv4mkap.png" alt="Surrealist Designer" width="799" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Surrealist Designer empowers you to effortlessly craft your table schema and manage entity relations. It's not just for beginners; the intuitive UI makes it accessible to newcomers, while experts can appreciate its ability to visualize even the most intricate data structures.&lt;/p&gt;

&lt;p&gt;Whether you're just starting or diving into complex projects, Surrealist is your trusted companion for making the most of SurrealDB. Give it a try and see how it enhances your database design journey.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generating Zod Schemas and TypeScript Clients from SurrealDB
&lt;/h2&gt;

&lt;p&gt;Being a TypeScript developer, my journey with a database doesn't end at just interacting with it.&lt;br&gt;
Naturally, I want a TypeScript program that seamlessly connects to SurrealDB.&lt;/p&gt;

&lt;p&gt;I soon found myself at a point where I wanted to automate the creation of a simple client, leveraging the information already present in the database.&lt;/p&gt;

&lt;p&gt;So, I embarked on a weekend coding session and crafted a small CLI tool. This tool works its magic by extracting schema information directly from SurrealDB, and it doesn't stop there. It also generates corresponding &lt;a href="https://zod.dev" rel="noopener noreferrer"&gt;Zod schemas&lt;/a&gt; and a TypeScript client for basic CRUD operations.&lt;br&gt;
The client is designed to work seamlessly with &lt;a href="https://surrealdb.com/docs/integration/sdks/nodejs" rel="noopener noreferrer"&gt;the official SDK&lt;/a&gt;, making the integration process smooth and efficient.&lt;/p&gt;

&lt;p&gt;With this tool, I aimed to simplify the process of working with SurrealDB, allowing TypeScript developers to dive into their projects with confidence, knowing they have a solid foundation to build upon.&lt;/p&gt;

&lt;p&gt;The intention is, to provide a solid starting point for development, and to speed up the processed.&lt;/p&gt;

&lt;p&gt;You can find the project on GitHub:&lt;br&gt;
&lt;a href="https://github.com/sebastianwessel/surrealdb-client-generator" rel="noopener noreferrer"&gt;https://github.com/sebastianwessel/surrealdb-client-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have multiple configuration options at your disposal, which can be set either as command-line interface (CLI) options or within a configuration file.&lt;br&gt;
What's more, you can even use both methods simultaneously.&lt;br&gt;
In such cases, the CLI options take precedence.&lt;/p&gt;

&lt;p&gt;Getting started with the tool is a breeze:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx surql-gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Options:
  &lt;span class="nt"&gt;-V&lt;/span&gt;, &lt;span class="nt"&gt;--version&lt;/span&gt;                          output the version number
  &lt;span class="nt"&gt;-f&lt;/span&gt;, &lt;span class="nt"&gt;--file&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;schemaFile]                a SurrealQL file containing the definitions &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"myschema.surql"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt;, &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;configFile]              SurrealDB connection url &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"surql-gen.json"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt;, &lt;span class="nt"&gt;--surreal&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;surreal]                SurrealDB connection url &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"ws://127.0.0.1:8000"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt;, &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;username]              auth username &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;, &lt;span class="nt"&gt;--password&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;password]              auth password &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt;, &lt;span class="nt"&gt;--ns&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;ns]                          the namspace &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt;, &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;db]                          the database &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt;, &lt;span class="nt"&gt;--outputFolder&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;outputFolder]      output folder &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"client_generated"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-g&lt;/span&gt;, &lt;span class="nt"&gt;--generateClient&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;generateClient]  generate client &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For further information, just check out &lt;a href="https://github.com/sebastianwessel/surrealdb-client-generator" rel="noopener noreferrer"&gt;the readme file&lt;/a&gt; in the repository.&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding Generated Files
&lt;/h3&gt;

&lt;p&gt;In your designated output folder, it's essential to grasp how the generated files are organized for efficient management:&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;_generated&lt;/code&gt; Subfolder
&lt;/h4&gt;

&lt;p&gt;Anything residing beneath the &lt;code&gt;_generated&lt;/code&gt; subfolder is subject to potential overwriting during subsequent runs of the generator. This serves as the workspace where dynamic updates can occur.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;schema&lt;/code&gt; Subfolder
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;schema&lt;/code&gt; subfolder plays a pivotal role. It contains the schema and type definitions, offering customization possibilities. Unlike the &lt;code&gt;_generated&lt;/code&gt; folder, everything within &lt;code&gt;schema&lt;/code&gt; is generated only once and remains untouched or deleted in subsequent runs of the generator.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;client&lt;/code&gt; Subfolder
&lt;/h4&gt;

&lt;p&gt;When it comes to accessing the generated client code, you'll find it neatly tucked away within the &lt;code&gt;client&lt;/code&gt; subfolder.&lt;/p&gt;

&lt;p&gt;This organized structure ensures that your generated files are readily accessible while maintaining the integrity of your project's foundation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using the Client
&lt;/h3&gt;

&lt;p&gt;The generated client is designed with simplicity in mind, adhering to the efficient repository pattern. In this pattern, each table is treated as a distinct entity, complete with its dedicated repository. These repositories, in turn, consolidate actions specific to the respective entity.&lt;/p&gt;

&lt;p&gt;Using the client is straightforward and user-friendly. You'll always encounter methods in the format of get[Entity Name]Repository(db: Surreal), requiring only the database instance as an argument.&lt;/p&gt;

&lt;p&gt;For a quick illustration, let's take a peek at how we can access all the generated methods for a project entity:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Surreal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;surrealdb.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getProjectRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./client_generated/client/project/getProjectRepository.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws://0.0.0.0:8000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;voyage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;voyage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getProjectRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createProject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/mylogo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAllProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;projects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Renamed !&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProjectById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deleted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectsDel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAllProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;projects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectsDel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;All return values are automatically typed based on the Zod schema, simplifying your code and ensuring consistency.&lt;br&gt;
Even if you customize a schema, TypeScript types adapt seamlessly, eliminating the need for manual updates.&lt;br&gt;
This streamlined process keeps your codebase error-free and agile, saving you time and effort.&lt;/p&gt;
&lt;h3&gt;
  
  
  Alternative
&lt;/h3&gt;

&lt;p&gt;There is the possibility, to disable the generation of the TypeScript client.&lt;br&gt;
The intention is, to only generate Zod schema, which than can simply used in &lt;a href="https://cirql.starlane.studio/" rel="noopener noreferrer"&gt;Cirql - SurrealDB ORM &amp;amp; Query Builder&lt;/a&gt; or in your own choice.&lt;/p&gt;
&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;
&lt;h2&gt;
  
  
  In Closing
&lt;/h2&gt;

&lt;p&gt;SurrealDB is undeniably a remarkable addition to the world of databases, and it's a platform that deserves your attention. With Surrealist, the entry barrier for beginners is lowered to a minimum, offering a professional UI enriched with fantastic features.&lt;/p&gt;

&lt;p&gt;I trust that my small tool will serve as a valuable resource for TypeScript developers looking to dive into the world of SurrealDB, making it more accessible and enjoyable.&lt;/p&gt;

&lt;p&gt;If you find this tool helpful, I kindly ask you to show your appreciation by giving it a star &lt;a href="https://github.com/sebastianwessel/surrealdb-client-generator" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And, of course, if you encounter any issues or have suggestions for improvements, please don't hesitate to open an issue on GitHub. Your feedback is invaluable.&lt;/p&gt;

&lt;p&gt;For further inquiries about SurrealDB, don't hesitate to explore their comprehensive documentation or engage with the community on their &lt;a href="https://discord.gg/surrealdb" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy coding - Cheers and bye bye! 👋&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Looking to dive deeper into SurrealDB?&lt;br&gt;
Don't miss out on the comprehensive series I've recently penned&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/sebastian_wessel/surrealdb-the-magic-database-to-keep-on-your-radar-4a22" class="crayons-story__hidden-navigation-link"&gt;SurrealDB  - The Magic Database to Keep on Your Radar&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/sebastian_wessel" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1054904%2F53658dbd-ccaf-4bfc-b4d9-14f102e981dc.jpg" alt="sebastian_wessel profile" class="crayons-avatar__image" width="200" height="200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/sebastian_wessel" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sebastian Wessel
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sebastian Wessel
                
              
              &lt;div id="story-author-preview-content-1593412" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/sebastian_wessel" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1054904%2F53658dbd-ccaf-4bfc-b4d9-14f102e981dc.jpg" class="crayons-avatar__image" alt="" width="200" height="200"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sebastian Wessel&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/sebastian_wessel/surrealdb-the-magic-database-to-keep-on-your-radar-4a22" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 8 '23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/sebastian_wessel/surrealdb-the-magic-database-to-keep-on-your-radar-4a22" id="article-link-1593412"&gt;
          SurrealDB  - The Magic Database to Keep on Your Radar
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/news"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;news&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/sebastian_wessel/surrealdb-the-magic-database-to-keep-on-your-radar-4a22" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;17&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/sebastian_wessel/surrealdb-the-magic-database-to-keep-on-your-radar-4a22#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


</description>
      <category>typescript</category>
      <category>database</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>PURISTA - Tests with Jest, Sinon.js and Testcontainers</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Tue, 12 Sep 2023 08:48:46 +0000</pubDate>
      <link>https://dev.to/purista/purista-tests-with-jest-sinonjs-and-testcontainers-31lh</link>
      <guid>https://dev.to/purista/purista-tests-with-jest-sinonjs-and-testcontainers-31lh</guid>
      <description>&lt;p&gt;In the journey of crafting the &lt;a href="https://purista.dev" rel="noopener noreferrer"&gt;PURISTA TypeScript backend framework&lt;/a&gt;, the need for automated software testing became increasingly apparent.&lt;br&gt;
PURISTA's extensive amount of adapters and integrations with third-party solutions makes it impractical to conduct manual testing repeatedly.&lt;br&gt;
In this article, we dive into the pivotal role of automated testing in ensuring the reliability and stability of PURISTA's offerings.&lt;/p&gt;

&lt;p&gt;Moreover, enabling developers to effortlessly test their code built on top of PURISTA stands as a pivotal aspect of the framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  Jest
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jestjs.io" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; offers a comprehensive test suite where you can efficiently organize, implement, and execute your tests. With approximately &lt;strong&gt;1490 contributors&lt;/strong&gt;, Jest has played a pivotal role in simplifying the process of writing tests, as demonstrated below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;asyncOperation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syncOperation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;mytestfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My testfile works&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is exected to be ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;syncOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;works with async await&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;asyncOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;resolves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the number on GitHub shows: This awesome tool is &lt;strong&gt;used by 10.4 millions&lt;/strong&gt;!&lt;br&gt;
This unbelievable number shows, how important it is.&lt;/p&gt;

&lt;p&gt;A massive shout-out to Jest and all its dedicated contributors! Consider starring it on GitHub, giving a shout-out on social media, or mentioning it in your readme file to show your appreciation. 🌟👏&lt;/p&gt;

&lt;p&gt;We use Jest together with &lt;a href="https://swc.rs/docs/usage/jest" rel="noopener noreferrer"&gt;@swc/jest&lt;/a&gt;, which speeds up our test runs dramatically.&lt;br&gt;
They don't lie with "Super-fast alternative for babel-jest or ts-jest without type checking".&lt;br&gt;
It's the second great tool by &lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; we use at PURISTA.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sinon.js
&lt;/h2&gt;

&lt;p&gt;When writing tests, you probably will come to the point where you need to mock, fake or spy on something. Even if the test framework is providing some helpers for it, you should have a look at &lt;a href="https://sinonjs.org" rel="noopener noreferrer"&gt;Sinon.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At PURISTA, we primarily leverage Sinon.js to deliver a seamless testing experience for developers.&lt;br&gt;
We provide for example the &lt;code&gt;getLoggerMock&lt;/code&gt; function, which returns an easy way to test logging, without poluting the test runner output.&lt;/p&gt;

&lt;p&gt;This function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLoggerMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SinonSandbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;warn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fatal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;getChildLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stubs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we've created stubs, which are a form of mock implementations. You can use them to check whether a stub is called, how many times it's called, if it's called with specific parameters, and more. Additionally, Sinon.js offers sandboxing, a highly recommended feature. It prevents your tests from interfering with each other, ensuring clean and reliable results.&lt;/p&gt;

&lt;p&gt;As an example on how it might look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@purista/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSandbox&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sinon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My testfile works&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSandbox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is exected to be ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loggerMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLoggerMock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;syncOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loggerMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calledOnceWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sinon.js is unbelievably helpful, and the 328 contributors are doing an incredible job. It's no surprise that Sinon.js is trusted by over 574k developers. Show them some love with a big shout-out and a star on GitHub!&lt;/p&gt;

&lt;p&gt;If you haven't used Sinon.js before, it's definitely worth giving it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testcontainers
&lt;/h2&gt;

&lt;p&gt;To be honest, &lt;a href="https://node.testcontainers.org" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt; wasn't on our radar initially.&lt;br&gt;
However, we soon realized the need for a viable solution to conduct integration tests for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rabbitmq.com" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mosquitto.org/" rel="noopener noreferrer"&gt;Mosquitto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.emqx.com/en/products/nanomq" rel="noopener noreferrer"&gt;NanoMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hivemq.com/" rel="noopener noreferrer"&gt;HiveMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vernemq.com/" rel="noopener noreferrer"&gt;VerneMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://activemq.apache.org/" rel="noopener noreferrer"&gt;ActiveMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nats.io/" rel="noopener noreferrer"&gt;NATS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infisical.com" rel="noopener noreferrer"&gt;Infisical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;...and much more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Testcontainers was the gamechanger here.&lt;br&gt;
It is super easy to spin up a docker container and run tests against a real running system.&lt;/p&gt;

&lt;p&gt;To show you, how simple it is:&lt;/p&gt;

&lt;p&gt;Here is the example, on how start a full RabbitMQ before we run our tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GenericContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StartedTestContainer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testcontainers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AMQP_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5672&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@purista/amqpbridge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StartedTestContainer&lt;/span&gt;

  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rabbitmq:alpine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withExposedPorts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AMQP_PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AMQP_PORT&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It significantly enhanced our local testing process. Furthermore, Testcontainers is a versatile tool available for multiple programming languages.&lt;/p&gt;

&lt;p&gt;A heartfelt thank you to all the contributors of Testcontainers for this incredible project! We deeply appreciate the outstanding work you've put into it.&lt;/p&gt;

&lt;p&gt;If you're searching for content ideas for the next article, consider crafting a cool piece about Testcontainers and its practical applications. Dive into how to harness its power effectively.&lt;/p&gt;




&lt;p&gt;Now that we've explored the powerful tooling required to build PURISTA itself, let's shift our focus to the components under the hood that make PURISTA tick.&lt;/p&gt;

&lt;p&gt;Be sure to check out the upcoming article in this series!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PURISTA: Build with rimraf, esbuild, Turbo &amp; git-cliff</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Mon, 11 Sep 2023 21:15:26 +0000</pubDate>
      <link>https://dev.to/purista/purista-build-with-rimraf-esbuild-turbo-git-cliff-5h5e</link>
      <guid>https://dev.to/purista/purista-build-with-rimraf-esbuild-turbo-git-cliff-5h5e</guid>
      <description>&lt;p&gt;In our previous article, we laid the foundation with a glimpse of our coding setup.&lt;/p&gt;

&lt;p&gt;It's now time to spotlight the indispensable build tools that power its development.&lt;/p&gt;

&lt;h2&gt;
  
  
  rimraf
&lt;/h2&gt;

&lt;p&gt;Huge thanks to &lt;a href="http://blog.izs.me/" rel="noopener noreferrer"&gt;Isaacs&lt;/a&gt;!&lt;br&gt;
&lt;a href="https://github.com/isaacs/rimraf" rel="noopener noreferrer"&gt;Rimraf&lt;/a&gt; comes to the rescue, providing a reliable solution for deep, recursive removal of folders and files.&lt;br&gt;
At PURISTA, we rely on rimraf to maintain pristine build output directories. &lt;/p&gt;
&lt;h2&gt;
  
  
  Turbo
&lt;/h2&gt;

&lt;p&gt;PURISTA is organized in a monorepo.&lt;br&gt;
During the development and build process, &lt;a href="https://turbo.build" rel="noopener noreferrer"&gt;Turbo&lt;/a&gt; is used to execute different tasks and steps on multiple packages with one command.&lt;/p&gt;

&lt;p&gt;We aren't harnessing the full potential and capabilities of Turbo at the moment since PURISTA currently requires only a few straightforward build tasks.&lt;/p&gt;

&lt;p&gt;If you have more complex tasks, especially those with interdependencies between steps, it's worth exploring &lt;a href="https://turbo.build" rel="noopener noreferrer"&gt;https://turbo.build&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Esbuild
&lt;/h2&gt;

&lt;p&gt;In its early stages, PURISTA was built using the standard TypeScript compiler.&lt;br&gt;
However, this approach excelled in creating CommonJS packages but turned into a nightmare when it came to generating ESM builds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://esbuild.github.io" rel="noopener noreferrer"&gt;esbuild&lt;/a&gt; - the rescue!&lt;br&gt;
No longer struggling with configs, file extensions or similar.&lt;/p&gt;

&lt;p&gt;With esbuild, it was becoming simple and fast.&lt;/p&gt;

&lt;p&gt;We created a small script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esbuild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esbuild&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;esbuild&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/index.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/esm/index.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;splitting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;esbuild&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/index.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/cjs/index.cjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;splitting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this approach only generates JavaScript files, and we aim to provide proper TypeScript types to PURISTA users, we utilize the TypeScript compiler specifically for building types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tsc --emitDeclarationOnly --declarationMap false --declaration --outDir lib/types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;package.json&lt;/code&gt;, only these additions were necessary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"directories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/cjs/index.cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/esm/index.mjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/esm/index.mjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/cjs/index.cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./lib/types/index.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/types/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"lib"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A big thank you ❤️ to all contributors and maintainers of esbuild!&lt;br&gt;
It solved the issue with CommonJS/ESM for us and produces size optimized outputs for all PURISTA packages.&lt;/p&gt;
&lt;h2&gt;
  
  
  git-cliff
&lt;/h2&gt;

&lt;p&gt;Changelogs are nice and everybody likes them, but nobody wants to maintain them 😜.&lt;br&gt;
Here, &lt;a href="https://git-cliff.org/" rel="noopener noreferrer"&gt;git-cliff&lt;/a&gt; by &lt;a href="https://blog.orhun.dev" rel="noopener noreferrer"&gt;Orhun Parmaksız&lt;/a&gt; is one simple to use solution.&lt;/p&gt;

&lt;p&gt;One simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git-cliff &amp;gt; CHANGELOG.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your changelog gets generated from your git history.&lt;/p&gt;

&lt;p&gt;Undoubtedly one of the time-saving tools, you should check out at &lt;a href="https://git-cliff.org" rel="noopener noreferrer"&gt;https://git-cliff.org&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In the upcoming article of this series, we will delve deeper into the test setup. Stay tuned!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PURISTA - Thanks to amazing open-source software</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Mon, 11 Sep 2023 20:17:57 +0000</pubDate>
      <link>https://dev.to/purista/purista-thanks-to-amazing-open-source-software-4k2e</link>
      <guid>https://dev.to/purista/purista-thanks-to-amazing-open-source-software-4k2e</guid>
      <description>&lt;p&gt;Welcome to our series on the unsung heroes behind PURISTA!&lt;/p&gt;

&lt;p&gt;Ever wondered about PURISTA?&lt;br&gt;
It's not just another Typescript backend framework for simply building HTTP-endpoints.&lt;br&gt;
It's a versatile solution that embraces diverse techniques for highly distributed deployments, taking inspiration from event-driven architecture.&lt;br&gt;
If you're interested, you're invited to take a look at the official website &lt;a href="//purista.dev"&gt;http://purista.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hold on, this series isn't about the framework itself. Instead, we're here to shine a spotlight on the incredible libraries, tools, software, and passionate contributors that make PURISTA possible. &lt;/p&gt;

&lt;p&gt;In this series, we will introduce all the tools, explain how we use them, why we find them essential, and endeavor to illustrate why they might also be a suitable solution for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  General setup
&lt;/h2&gt;

&lt;p&gt;Most developers should be more or less familiar with the basic setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://code.visualstudio.com" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt; as a code editor with  ESLint plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sebastianwessel/purista" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; is uses as repository, issue &amp;amp; project tracker, and also for hosting the &lt;a href="https://purista.dev/" rel="noopener noreferrer"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding Setup
&lt;/h2&gt;

&lt;p&gt;In addition to &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and &lt;a href="https://www.typescriptlang.org" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;, we rely on two indispensable tools available as npm modules:&lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint and Prettier
&lt;/h3&gt;

&lt;p&gt;Maintaining a clean, readable, and consistent codebase is essential, and ESLint and Prettier are the perfect tools for the job.&lt;br&gt;
Just enable "lint on save" in VSCode, and you won't have to worry about it. They're a "must-have," even for small personal projects.&lt;/p&gt;

&lt;p&gt;See &lt;a href="//eslint.org"&gt;https://eslint.org&lt;/a&gt; and &lt;a href="//prettier.io"&gt;https://prettier.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ESLint is extended by some awesome plugins and extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/lydell/eslint-plugin-simple-import-sort" rel="noopener noreferrer"&gt;eslint-plugin-simple-import-sort&lt;/a&gt; by Simon Lydell &lt;a href="https://twitter.com/SimonLydell" rel="noopener noreferrer"&gt;@SimonLydell&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mysticatea/eslint-plugin-node" rel="noopener noreferrer"&gt;eslint-plugin-node&lt;/a&gt; by Toru Nagashima - Dev.to: &lt;a class="mentioned-user" href="https://dev.to/mysticatea"&gt;@mysticatea&lt;/a&gt; &amp;amp;  Twitter: &lt;a href="https://twitter.com/mysticatea" rel="noopener noreferrer"&gt;@mysticatea&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/azeemba/eslint-plugin-json" rel="noopener noreferrer"&gt;eslint-plugin-json&lt;/a&gt; by &lt;a href="https://azeemba.com" rel="noopener noreferrer"&gt;Azeem Bande-Ali&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/import-js/eslint-plugin-import" rel="noopener noreferrer"&gt;eslint-plugin-import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/standard/eslint-config-standard" rel="noopener noreferrer"&gt;eslint-config-standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;In our upcoming article, we'll delve deep into the inner workings of PURISTA's build pipeline. Stay tuned for an in-depth exploration!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>programming</category>
      <category>opensource</category>
      <category>coding</category>
    </item>
    <item>
      <title>SurrealDB - Improve data integrity by adding schema information</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Fri, 08 Sep 2023 20:46:26 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/surrealdb-improve-data-integrity-by-adding-schema-information-3ee3</link>
      <guid>https://dev.to/sebastian_wessel/surrealdb-improve-data-integrity-by-adding-schema-information-3ee3</guid>
      <description>&lt;p&gt;In the concluding chapter of this series, we'll delve into the intriguing world of SurrealDB's schema possibilities and explore how they can elevate our data integrity to new heights.&lt;/p&gt;

&lt;p&gt;You can define fields for tables. We will take the &lt;code&gt;user&lt;/code&gt; table as an example here.&lt;/p&gt;

&lt;p&gt;Our initial definition was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Define the user table
DEFINE TABLE user SCHEMALESS;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will add now some field definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Define the user table
DEFINE TABLE user SCHEMALESS;
DEFINE FIELD name ON TABLE user TYPE string;
DEFINE FIELD email ON TABLE user TYPE string;
DEFINE FIELD firstName ON TABLE user TYPE string;
DEFINE FIELD lastName ON TABLE user TYPE string;
DEFINE FIELD createdAt ON TABLE user TYPE datetime;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're on the right track so far. We've defined the necessary fields, and we've also specified their types.&lt;br&gt;
But there are some improvements, we can make.&lt;/p&gt;

&lt;p&gt;Now, let's focus on the createdAt attribute. In this case, we should establish a default value that automatically captures the creation timestamp when the entity is generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE FIELD createdAt ON TABLE user TYPE datetime DEFAULT time::now();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should also enhance the email field.&lt;br&gt;
There are two improvements we can make.&lt;br&gt;
First, we should specify the field type as more than just a generic string, as we know it must adhere to the email pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, we want to ensure, that the email is globally unique. To ensure this, we &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/indexes" rel="noopener noreferrer"&gt;define a unique index&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE INDEX uniqueEmailIndex ON TABLE user COLUMNS email UNIQUE;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's our ultimate definition for our user table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Define the user table
DEFINE TABLE user SCHEMALESS;
DEFINE FIELD name ON TABLE user TYPE string;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD firstName ON TABLE user TYPE string;
DEFINE FIELD lastName ON TABLE user TYPE string;
DEFINE FIELD createdAt ON TABLE user TYPE datetime DEFAULT time::now();

DEFINE INDEX uniqueEmailIndex ON TABLE user COLUMNS email UNIQUE;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concerning the relationships we've established between various entities, it's important to avoid duplicates between the same entities.&lt;br&gt;
In this context, we can create a unique index to prevent multiple connections of a specific user to a particular tenant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- prevent linking a user to the same tenant multiple times
DEFINE INDEX tenant_memberIndex
    ON TABLE tenant_member
    COLUMNS in, out UNIQUE;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;As we conclude this series, we've only just scratched the surface of SurrealDB's capabilities, with a strong focus on its graph structures.&lt;/p&gt;

&lt;p&gt;SurrealDB has an exciting set of features and upcoming announcements that make it a truly fascinating unicorn in the database world.&lt;/p&gt;

&lt;p&gt;The handling of unique record-IDs and the possibilities they unlock are nothing short of amazing.&lt;br&gt;
The freedom to choose between a relational database, key-value store, NoSQL/document database, graph database, or specialized time series database while using one database simplifies life significantly.&lt;br&gt;
Tailoring how you store and access data based on your specific needs and mixing things up as required is the way forward.&lt;/p&gt;

&lt;p&gt;While SurrealDB is still in its early stages and not officially production-ready, I encourage you to give it a try in one of your upcoming small (or side) projects.&lt;/p&gt;

&lt;p&gt;The adventure with SurrealDB may just be the unique and rewarding experience you've been looking for.&lt;/p&gt;

</description>
      <category>database</category>
      <category>tutorial</category>
      <category>datastructures</category>
      <category>programming</category>
    </item>
    <item>
      <title>SurrealDB - Query and combine data via relations</title>
      <dc:creator>Sebastian Wessel</dc:creator>
      <pubDate>Fri, 08 Sep 2023 20:46:15 +0000</pubDate>
      <link>https://dev.to/sebastian_wessel/surrealdb-query-and-combine-data-via-relations-5863</link>
      <guid>https://dev.to/sebastian_wessel/surrealdb-query-and-combine-data-via-relations-5863</guid>
      <description>&lt;p&gt;In the previous article of this series, we set up the foundation and added some sample data.&lt;/p&gt;

&lt;p&gt;Now, we'll dive into retrieving and merging that data.&lt;br&gt;
I strongly encourage you to explore the SurrealDB documentation. It will make understanding the upcoming examples much smoother.&lt;/p&gt;

&lt;p&gt;See: &lt;a href="https://surrealdb.com/docs/surrealql/statements/select" rel="noopener noreferrer"&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://surrealdb.com/docs/surrealql/statements/relate" rel="noopener noreferrer"&gt;&lt;code&gt;RELATE&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Query tenant(s)
&lt;/h2&gt;

&lt;p&gt;To start, we'll keep it simple and begin by fetching all the tenants.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- fetch all tenants
SELECT * from tenant;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:cat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cat Owners"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:musician"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Musicians"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add available roles
&lt;/h3&gt;

&lt;p&gt;Our next step is to find out which roles are generally accessible within the context of a particular tenant.&lt;/p&gt;

&lt;p&gt;This information is stored in the &lt;code&gt;tenant_role&lt;/code&gt; relation.&lt;br&gt;
We can now extend our query as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- select the tenant car enthusisast
-- fetch the related roles and return it as availableRoles
SELECT *,
  (SELECT * FROM &amp;lt;-tenant_role&amp;lt;-role) as availableRoles
FROM tenant:car;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Administrator"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content Reader"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content Author"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's tidy up the output. Since we're aware that we're fetching just one tenant, we can use the &lt;a href="https://surrealdb.com/docs/surrealql/statements/select" rel="noopener noreferrer"&gt;&lt;code&gt;ONLY&lt;/code&gt;&lt;/a&gt; statement between FROM and &lt;code&gt;tenant:car&lt;/code&gt; to directly obtain a single result object. Additionally, we'll limit the output to only display the role IDs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- return only id´s of roles and return as single object
SELECT *,
  (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles 
FROM ONLY tenant:car;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add tenant members
&lt;/h3&gt;

&lt;p&gt;In the next step, we will also return the users, which are car enthusiasts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- add members of tenant car
SELECT *,
      (SELECT * FROM &amp;lt;-tenant_member&amp;lt;-user) as members,
      (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles 
FROM ONLY tenant:car;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"members"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Add tenant member roles
&lt;/h4&gt;

&lt;p&gt;We will now add the role information to each user who is a member of the given tenant.&lt;br&gt;
We will change&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(SELECT * FROM &amp;lt;-tenant_member&amp;lt;-user) as members,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(
 SELECT 
  (SELECT * FROM ONLY &amp;lt;-person) as person,
  (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles,
 FROM &amp;lt;-tenant_member
) as members
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can observe, we've made a change in how we resolve data. Now, we don't go all the way up to the user through &amp;lt;-tenant_member&amp;lt;-user. Instead, we stop at the relation entry itself with &lt;code&gt;&amp;lt;-tenant_member&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Recall our data schema. We've established a relationship between users and tenants. This relationship, in turn, has its own connections to one or even multiple roles.&lt;/p&gt;

&lt;p&gt;Think of it as a crossroads. If you head straight ahead, you'll discover user information. But if you veer left or right, you'll stumble upon the roles assigned to that user within a specific tenant.&lt;br&gt;
The full query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT *, (
      SELECT 
            (SELECT * FROM ONLY &amp;lt;-user) as user,
            (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles
      FROM &amp;lt;-tenant_member
      ) as members,
      (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles 
FROM ONLY tenant:car;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"members"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Add tenant permissions for roles of members
&lt;/h4&gt;

&lt;p&gt;In most scenarios, it may not be practical to display the permissions for every user within a tenant at this stage.&lt;/p&gt;

&lt;p&gt;However, for the sake of learning and demonstration, I'll illustrate the impressive capabilities of SurrealDB.&lt;/p&gt;

&lt;p&gt;We will need to add something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(SELECT * FROM -&amp;gt;member_role-&amp;gt;role-&amp;gt;role_permission-&amp;gt;permission) as permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will add something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update content"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read content"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create content"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example, everything appears neat and straightforward.&lt;br&gt;
However, our user currently has just one role assigned.&lt;br&gt;
When a user holds multiple roles, they might end up with the same permission through different roles. This could lead to duplicate entries in our results.&lt;/p&gt;

&lt;p&gt;Fortunately, SurrealDB offers a &lt;a href="https://surrealdb.com/docs/surrealql/functions" rel="noopener noreferrer"&gt;set of handy helper functions&lt;/a&gt;. We'll employ &lt;code&gt;array::group&lt;/code&gt; to eliminate these duplicates from our results.&lt;/p&gt;

&lt;p&gt;The complete query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT *, (
      SELECT 
            (SELECT * FROM ONLY &amp;lt;-user) as user,
            (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles,
            array::group((SELECT * FROM -&amp;gt;member_role-&amp;gt;role-&amp;gt;role_permission-&amp;gt;permission)) as permissions
      FROM &amp;lt;-tenant_member
      ) as members,
      (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles 
FROM ONLY tenant:car;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"members"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update content"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read content"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create content"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final query 🎉
&lt;/h3&gt;

&lt;p&gt;Let's remove the permissions and the &lt;code&gt;ONLY&lt;/code&gt; Statement.&lt;br&gt;
Our query, for fetching single or multiple tenants, is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT *, (
      SELECT 
            (SELECT * FROM ONLY &amp;lt;-user) as user,
            (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles
      FROM &amp;lt;-tenant_member
      ) as members,
      (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles 
FROM tenant;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Query user(s)
&lt;/h2&gt;

&lt;p&gt;When we initiate a query for a user, our goal is to retrieve the user's details.&lt;/p&gt;

&lt;p&gt;Additionally, we aim to determine which tenants the user belongs to and understand the roles associated with each of these tenant memberships.&lt;/p&gt;

&lt;p&gt;We will utilize the same relationships as we did for the tenant query. The only distinction here lies in our approach: we will traverse most of the graph relationships in the reverse direction.&lt;br&gt;
Because of this, I will skip the detailed explanation.&lt;/p&gt;

&lt;p&gt;Our user query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- query user(s) and return related tenants, roles and permissions

SELECT *, (
      SELECT 
            (SELECT * FROM ONLY -&amp;gt;tenant) as tenant,
            (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles,
            array::group((SELECT * FROM -&amp;gt;member_role-&amp;gt;role-&amp;gt;role_permission-&amp;gt;permission)) as permissions
      FROM -&amp;gt;tenant_member
      ) as tenants
FROM user;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tenants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update content"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read content"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create content"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"tenant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Query information for a specific user within a particular tenant
&lt;/h2&gt;

&lt;p&gt;As an addition to the previous queries, I will provide a query, which you might need in similar way in real world scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- query the information for a specific user within a particular tenant and return user information, tenant information, roles and permissions

SELECT
      (SELECT * FROM ONLY &amp;lt;-user) as user,
      (SELECT *, (SELECT id FROM &amp;lt;-tenant_role&amp;lt;-role).id as availableRoles FROM ONLY -&amp;gt;tenant) as tenant,
      (SELECT id FROM -&amp;gt;member_role-&amp;gt;role).id as roles,
      array::group((SELECT * FROM -&amp;gt;member_role-&amp;gt;role-&amp;gt;role_permission-&amp;gt;permission)) as permissions
FROM ONLY tenant_member
WHERE in='user:1' and out='tenant:car'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update content"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read content"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission:create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create content"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tenant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"availableRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"role:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"role:reader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"role:author"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant:car"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Car Enthusiasts"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>database</category>
      <category>tutorial</category>
      <category>architecture</category>
      <category>datastructures</category>
    </item>
  </channel>
</rss>
