@plotday/twister 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/calendar.d.ts +3 -3
- package/dist/common/calendar.d.ts.map +1 -1
- package/dist/common/messaging.d.ts +8 -7
- package/dist/common/messaging.d.ts.map +1 -1
- package/dist/docs/assets/hierarchy.js +1 -1
- package/dist/docs/assets/highlight.css +8 -8
- package/dist/docs/assets/navigation.js +1 -1
- package/dist/docs/assets/search.js +1 -1
- package/dist/docs/classes/tool.ITool.html +1 -1
- package/dist/docs/classes/tool.Tool.html +1 -1
- package/dist/docs/classes/tools_ai.AI.html +3 -3
- package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
- package/dist/docs/classes/tools_integrations.Integrations.html +1 -1
- package/dist/docs/classes/tools_network.Network.html +1 -1
- package/dist/docs/classes/tools_plot.Plot.html +52 -31
- package/dist/docs/classes/tools_store.Store.html +1 -1
- package/dist/docs/classes/tools_tasks.Tasks.html +2 -2
- package/dist/docs/classes/tools_twists.Twists.html +6 -6
- package/dist/docs/classes/twist.Twist.html +1 -1
- package/dist/docs/documents/Building_Custom_Tools.html +8 -9
- package/dist/docs/documents/Built-in_Tools.html +26 -14
- package/dist/docs/documents/Core_Concepts.html +25 -7
- package/dist/docs/documents/Getting_Started.html +12 -3
- package/dist/docs/documents/Runtime_Environment.html +6 -7
- package/dist/docs/enums/plot.ActorType.html +4 -4
- package/dist/docs/enums/tools_plot.ActivityAccess.html +6 -6
- package/dist/docs/enums/tools_plot.ContactAccess.html +3 -3
- package/dist/docs/enums/tools_plot.PriorityAccess.html +3 -3
- package/dist/docs/functions/utils_hash.quickHash.html +1 -0
- package/dist/docs/hierarchy.html +1 -1
- package/dist/docs/index.html +2 -11
- package/dist/docs/interfaces/common_calendar.CalendarTool.html +5 -5
- package/dist/docs/interfaces/tools_ai.AIRequest.html +4 -4
- package/dist/docs/modules/index.html +1 -1
- package/dist/docs/modules/plot.html +1 -1
- package/dist/docs/modules/tools_plot.html +1 -1
- package/dist/docs/modules/utils_hash.html +1 -0
- package/dist/docs/modules.html +1 -1
- package/dist/docs/types/plot.Activity.html +16 -52
- package/dist/docs/types/plot.ActivityCommon.html +20 -0
- package/dist/docs/types/plot.ActivityUpdate.html +2 -8
- package/dist/docs/types/plot.ActivityWithNotes.html +1 -0
- package/dist/docs/types/plot.Actor.html +5 -5
- package/dist/docs/types/plot.NewActivity.html +3 -3
- package/dist/docs/types/plot.NewActivityWithNotes.html +1 -0
- package/dist/docs/types/plot.NewContact.html +4 -4
- package/dist/docs/types/plot.NewNote.html +10 -0
- package/dist/docs/types/plot.Note.html +7 -0
- package/dist/docs/types/plot.NoteType.html +1 -1
- package/dist/docs/types/plot.NoteUpdate.html +14 -0
- package/dist/docs/types/plot.PickPriorityConfig.html +3 -3
- package/dist/docs/types/tools_network.WebhookRequest.html +1 -1
- package/dist/docs/types/{tools_plot.ActivityIntentHandler.html → tools_plot.NoteIntentHandler.html} +5 -5
- package/dist/llm-docs/common/calendar.d.ts +1 -1
- package/dist/llm-docs/common/calendar.d.ts.map +1 -1
- package/dist/llm-docs/common/calendar.js +1 -1
- package/dist/llm-docs/common/calendar.js.map +1 -1
- package/dist/llm-docs/common/messaging.d.ts +1 -1
- package/dist/llm-docs/common/messaging.d.ts.map +1 -1
- package/dist/llm-docs/common/messaging.js +1 -1
- package/dist/llm-docs/common/messaging.js.map +1 -1
- package/dist/llm-docs/plot.d.ts +1 -1
- package/dist/llm-docs/plot.d.ts.map +1 -1
- package/dist/llm-docs/plot.js +1 -1
- package/dist/llm-docs/plot.js.map +1 -1
- package/dist/llm-docs/tools/plot.d.ts +1 -1
- package/dist/llm-docs/tools/plot.d.ts.map +1 -1
- package/dist/llm-docs/tools/plot.js +1 -1
- package/dist/llm-docs/tools/plot.js.map +1 -1
- package/dist/plot.d.ts +75 -33
- package/dist/plot.d.ts.map +1 -1
- package/dist/plot.js.map +1 -1
- package/dist/tools/plot.d.ts +118 -29
- package/dist/tools/plot.d.ts.map +1 -1
- package/dist/tools/plot.js +4 -4
- package/dist/tools/plot.js.map +1 -1
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +9 -0
- package/dist/utils/hash.js.map +1 -0
- package/package.json +5 -1
- package/dist/docs/documents/Advanced.html +0 -91
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>Advanced | Creating Plot Twists</title><link rel="icon" href="../assets/favicon.svg" type="image/svg+xml"/><meta name="description" content="Documentation for Creating Plot Twists"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="/" class="title">Creating Plot Twists</a><div id="tsd-toolbar-links"><a href="https://plot.day">Plot</a><a href="https://github.com/plotday/plot">GitHub</a><a href="https://www.npmjs.com/package/@plotday/twister">NPM</a></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">Advanced</a></li></ul></div><div class="tsd-panel tsd-typography"><h1 id="advanced-topics" class="tsd-anchor-link">Advanced Topics<a href="#advanced-topics" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h1><p>Advanced patterns and techniques for building sophisticated Plot twists.</p>
|
|
2
|
-
<h2 id="table-of-contents" class="tsd-anchor-link">Table of Contents<a href="#table-of-contents" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
|
3
|
-
<li><a href="#complex-twist-architectures">Complex twist Architectures</a></li>
|
|
4
|
-
<li><a href="#error-handling">Error Handling</a></li>
|
|
5
|
-
<li><a href="#debugging-and-logging">Debugging and Logging</a></li>
|
|
6
|
-
<li><a href="#security-best-practices">Security Best Practices</a></li>
|
|
7
|
-
<li><a href="#migration-and-versioning">Migration and Versioning</a></li>
|
|
8
|
-
<li><a href="#performance-patterns">Performance Patterns</a></li>
|
|
9
|
-
</ul>
|
|
10
|
-
<hr>
|
|
11
|
-
<h2 id="complex-twist-architectures" class="tsd-anchor-link">Complex twist Architectures<a href="#complex-twist-architectures" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="multi-service-integration" class="tsd-anchor-link">Multi-Service Integration<a href="#multi-service-integration" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Coordinate multiple external services:</p>
|
|
12
|
-
<pre><code class="typescript"><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">GitHubTool</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">"@mycompany/plot-github-tool"</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">JiraTool</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">"@mycompany/plot-jira-tool"</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">SlackTool</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">"@mycompany/plot-slack-tool"</span><span class="hl-1">;</span><br/><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">twist</span><span class="hl-1">, </span><span class="hl-0">type</span><span class="hl-1"> </span><span class="hl-2">Priority</span><span class="hl-1">, </span><span class="hl-0">type</span><span class="hl-1"> </span><span class="hl-2">ToolBuilder</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">"@plotday/twister"</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">Plot</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">"@plotday/twister/tools/plot"</span><span class="hl-1">;</span><br/><br/><span class="hl-0">export</span><span class="hl-1"> </span><span class="hl-0">default</span><span class="hl-1"> </span><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">DevOpsTwist</span><span class="hl-1"> </span><span class="hl-4">extends</span><span class="hl-1"> </span><span class="hl-5">Twist</span><span class="hl-1"><</span><span class="hl-5">DevOpsTwist</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-6">build</span><span class="hl-1">(</span><span class="hl-2">build</span><span class="hl-1">: </span><span class="hl-5">ToolBuilder</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">plot:</span><span class="hl-1"> </span><span class="hl-6">build</span><span class="hl-1">(</span><span class="hl-2">Plot</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-2">github:</span><span class="hl-1"> </span><span class="hl-6">build</span><span class="hl-1">(</span><span class="hl-2">GitHubTool</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">owner:</span><span class="hl-1"> </span><span class="hl-3">"mycompany"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">repo:</span><span class="hl-1"> </span><span class="hl-3">"myapp"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">token:</span><span class="hl-1"> </span><span class="hl-2">process</span><span class="hl-1">.</span><span class="hl-2">env</span><span class="hl-1">.</span><span class="hl-8">GITHUB_TOKEN</span><span class="hl-1">!,</span><br/><span class="hl-1"> }),</span><br/><span class="hl-1"> </span><span class="hl-2">slack:</span><span class="hl-1"> </span><span class="hl-6">build</span><span class="hl-1">(</span><span class="hl-2">SlackTool</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">webhookUrl:</span><span class="hl-1"> </span><span class="hl-2">process</span><span class="hl-1">.</span><span class="hl-2">env</span><span class="hl-1">.</span><span class="hl-8">SLACK_WEBHOOK_URL</span><span class="hl-1">!,</span><br/><span class="hl-1"> }),</span><br/><span class="hl-1"> </span><span class="hl-2">jira:</span><span class="hl-1"> </span><span class="hl-6">build</span><span class="hl-1">(</span><span class="hl-2">JiraTool</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">domain:</span><span class="hl-1"> </span><span class="hl-3">"mycompany.atlassian.net"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">apiToken:</span><span class="hl-1"> </span><span class="hl-2">process</span><span class="hl-1">.</span><span class="hl-2">env</span><span class="hl-1">.</span><span class="hl-8">JIRA_TOKEN</span><span class="hl-1">!,</span><br/><span class="hl-1"> }),</span><br/><span class="hl-1"> };</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">activate</span><span class="hl-1">(</span><span class="hl-2">priority</span><span class="hl-1">: </span><span class="hl-5">Pick</span><span class="hl-1"><</span><span class="hl-5">Priority</span><span class="hl-1">, </span><span class="hl-3">"id"</span><span class="hl-1">>) {</span><br/><span class="hl-1"> </span><span class="hl-7">// Set up cross-service workflow</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">setupIssueSync</span><span class="hl-1">();</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">setupIssueSync</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-7">// When GitHub issue is created, create Jira ticket and post to Slack</span><br/><span class="hl-1"> </span><span class="hl-7">// When Jira ticket is updated, update GitHub issue</span><br/><span class="hl-1"> </span><span class="hl-7">// When PR is merged, update both and notify Slack</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
13
|
-
</code><button type="button">Copy</button></pre>
|
|
14
|
-
|
|
15
|
-
<h3 id="state-machine-pattern" class="tsd-anchor-link">State Machine Pattern<a href="#state-machine-pattern" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Implement complex workflows with state machines:</p>
|
|
16
|
-
<pre><code class="typescript"><span class="hl-4">type</span><span class="hl-1"> </span><span class="hl-5">WorkflowState</span><span class="hl-1"> = </span><span class="hl-3">"pending"</span><span class="hl-1"> | </span><span class="hl-3">"in_progress"</span><span class="hl-1"> | </span><span class="hl-3">"review"</span><span class="hl-1"> | </span><span class="hl-3">"complete"</span><span class="hl-1">;</span><br/><br/><span class="hl-4">interface</span><span class="hl-1"> </span><span class="hl-5">WorkflowData</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">state</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-2">activityId</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-2">metadata</span><span class="hl-1">: </span><span class="hl-5">Record</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-5">any</span><span class="hl-1">>;</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">WorkflowTwist</span><span class="hl-1"> </span><span class="hl-4">extends</span><span class="hl-1"> </span><span class="hl-5">Twist</span><span class="hl-1"><</span><span class="hl-5">WorkflowTwist</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">transitionTo</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-2">newState</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">workflow</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">WorkflowData</span><span class="hl-1">>(</span><span class="hl-3">`workflow:</span><span class="hl-4">${</span><span class="hl-2">workflowId</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-2">workflow</span><span class="hl-1">) </span><span class="hl-0">throw</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Error</span><span class="hl-1">(</span><span class="hl-3">"Workflow not found"</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">oldState</span><span class="hl-1"> = </span><span class="hl-2">workflow</span><span class="hl-1">.</span><span class="hl-2">state</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Validate transition</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">isValidTransition</span><span class="hl-1">(</span><span class="hl-2">oldState</span><span class="hl-1">, </span><span class="hl-2">newState</span><span class="hl-1">)) {</span><br/><span class="hl-1"> </span><span class="hl-0">throw</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Error</span><span class="hl-1">(</span><span class="hl-3">`Invalid transition: </span><span class="hl-4">${</span><span class="hl-2">oldState</span><span class="hl-4">}</span><span class="hl-3"> -> </span><span class="hl-4">${</span><span class="hl-2">newState</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Execute transition logic</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">onExit</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">, </span><span class="hl-2">oldState</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-2">workflow</span><span class="hl-1">.</span><span class="hl-2">state</span><span class="hl-1"> = </span><span class="hl-2">newState</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">`workflow:</span><span class="hl-4">${</span><span class="hl-2">workflowId</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">, </span><span class="hl-2">workflow</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">onEnter</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">, </span><span class="hl-2">newState</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-6">isValidTransition</span><span class="hl-1">(</span><span class="hl-2">from</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">, </span><span class="hl-2">to</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">): </span><span class="hl-5">boolean</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">transitions</span><span class="hl-1">: </span><span class="hl-5">Record</span><span class="hl-1"><</span><span class="hl-5">WorkflowState</span><span class="hl-1">, </span><span class="hl-5">WorkflowState</span><span class="hl-1">[]> = {</span><br/><span class="hl-1"> </span><span class="hl-2">pending:</span><span class="hl-1"> [</span><span class="hl-3">"in_progress"</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-2">in_progress:</span><span class="hl-1"> [</span><span class="hl-3">"review"</span><span class="hl-1">, </span><span class="hl-3">"pending"</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-2">review:</span><span class="hl-1"> [</span><span class="hl-3">"complete"</span><span class="hl-1">, </span><span class="hl-3">"in_progress"</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-2">complete:</span><span class="hl-1"> [],</span><br/><span class="hl-1"> };</span><br/><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-2">transitions</span><span class="hl-1">[</span><span class="hl-2">from</span><span class="hl-1">]?.</span><span class="hl-6">includes</span><span class="hl-1">(</span><span class="hl-2">to</span><span class="hl-1">) ?? </span><span class="hl-4">false</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">onEnter</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-2">state</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">switch</span><span class="hl-1"> (</span><span class="hl-2">state</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">case</span><span class="hl-1"> </span><span class="hl-3">"in_progress"</span><span class="hl-1">:</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">notifyAssigned</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">break</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">case</span><span class="hl-1"> </span><span class="hl-3">"review"</span><span class="hl-1">:</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">requestReview</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">break</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">case</span><span class="hl-1"> </span><span class="hl-3">"complete"</span><span class="hl-1">:</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">markComplete</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">break</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">onExit</span><span class="hl-1">(</span><span class="hl-2">workflowId</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-2">state</span><span class="hl-1">: </span><span class="hl-5">WorkflowState</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-7">// Cleanup for previous state</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
17
|
-
</code><button type="button">Copy</button></pre>
|
|
18
|
-
|
|
19
|
-
<hr>
|
|
20
|
-
<h2 id="error-handling" class="tsd-anchor-link">Error Handling<a href="#error-handling" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="graceful-degradation" class="tsd-anchor-link">Graceful Degradation<a href="#graceful-degradation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Handle errors without breaking the twist:</p>
|
|
21
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">activate</span><span class="hl-1">(</span><span class="hl-2">priority</span><span class="hl-1">: </span><span class="hl-2">Pick</span><span class="hl-1"><</span><span class="hl-2">Priority</span><span class="hl-1">, </span><span class="hl-3">"id"</span><span class="hl-1">>) {</span><br/><span class="hl-1"> </span><span class="hl-0">try</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">setupWebhooks</span><span class="hl-1">();</span><br/><span class="hl-1"> } </span><span class="hl-0">catch</span><span class="hl-1"> (</span><span class="hl-2">error</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">error</span><span class="hl-1">(</span><span class="hl-3">"Failed to setup webhooks:"</span><span class="hl-1">, </span><span class="hl-2">error</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-7">// twist still activates, just without webhooks</span><br/><span class="hl-1"> </span><span class="hl-7">// Consider creating an activity to notify the user</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">tools</span><span class="hl-1">.</span><span class="hl-2">plot</span><span class="hl-1">.</span><span class="hl-6">createActivity</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">type:</span><span class="hl-1"> </span><span class="hl-2">ActivityType</span><span class="hl-1">.</span><span class="hl-2">Note</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">title:</span><span class="hl-1"> </span><span class="hl-3">"⚠️ Webhook setup failed"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">note:</span><span class="hl-1"> </span><span class="hl-3">`Could not set up automatic syncing. Error: </span><span class="hl-4">${</span><span class="hl-2">error</span><span class="hl-13">.</span><span class="hl-2">message</span><span class="hl-4">}</span><span class="hl-3">`</span><br/><span class="hl-1"> });</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Continue with other initialization</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">initialSync</span><span class="hl-1">();</span><br/><span class="hl-1">}</span>
|
|
22
|
-
</code><button type="button">Copy</button></pre>
|
|
23
|
-
|
|
24
|
-
<h3 id="retry-logic" class="tsd-anchor-link">Retry Logic<a href="#retry-logic" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Implement exponential backoff for transient failures:</p>
|
|
25
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">fetchWithRetry</span><span class="hl-1"><</span><span class="hl-5">T</span><span class="hl-1">>(</span><br/><span class="hl-1"> </span><span class="hl-2">url</span><span class="hl-1">: </span><span class="hl-2">string</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">maxRetries</span><span class="hl-1">: </span><span class="hl-2">number</span><span class="hl-1"> = </span><span class="hl-12">3</span><br/><span class="hl-1">): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-8">T</span><span class="hl-1">> {</span><br/><span class="hl-1"> let </span><span class="hl-2">lastError:</span><span class="hl-1"> </span><span class="hl-2">Error</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-6">for</span><span class="hl-1"> (</span><span class="hl-2">let</span><span class="hl-1"> </span><span class="hl-2">attempt</span><span class="hl-1"> = </span><span class="hl-12">0</span><span class="hl-1">; </span><span class="hl-2">attempt</span><span class="hl-1"> < </span><span class="hl-2">maxRetries</span><span class="hl-1">; </span><span class="hl-2">attempt</span><span class="hl-1">++) {</span><br/><span class="hl-1"> try {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">response</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-6">fetch</span><span class="hl-1">(</span><span class="hl-2">url</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-2">response</span><span class="hl-1">.</span><span class="hl-2">ok</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">throw</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Error</span><span class="hl-1">(</span><span class="hl-3">`HTTP </span><span class="hl-4">${</span><span class="hl-2">response</span><span class="hl-13">.</span><span class="hl-2">status</span><span class="hl-4">}</span><span class="hl-3">: </span><span class="hl-4">${</span><span class="hl-2">response</span><span class="hl-13">.</span><span class="hl-2">statusText</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-2">response</span><span class="hl-1">.</span><span class="hl-6">json</span><span class="hl-1">();</span><br/><span class="hl-1"> } catch (error) {</span><br/><span class="hl-1"> lastError = error as Error;</span><br/><span class="hl-1"> console.error(</span><span class="hl-3">`Attempt </span><span class="hl-4">${</span><span class="hl-2">attempt</span><span class="hl-13"> </span><span class="hl-1">+</span><span class="hl-13"> </span><span class="hl-12">1</span><span class="hl-4">}</span><span class="hl-3"> failed:`</span><span class="hl-1">, error);</span><br/><br/><span class="hl-1"> </span><span class="hl-6">if</span><span class="hl-1"> (</span><span class="hl-2">attempt</span><span class="hl-1"> < </span><span class="hl-2">maxRetries</span><span class="hl-1"> - 1) {</span><br/><span class="hl-1"> </span><span class="hl-7">// Exponential backoff: 1s, 2s, 4s</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">delay</span><span class="hl-1"> = </span><span class="hl-2">Math</span><span class="hl-1">.</span><span class="hl-6">pow</span><span class="hl-1">(</span><span class="hl-12">2</span><span class="hl-1">, </span><span class="hl-2">attempt</span><span class="hl-1">) * </span><span class="hl-12">1000</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-5">Promise</span><span class="hl-1">(</span><span class="hl-2">resolve</span><span class="hl-1"> </span><span class="hl-4">=></span><span class="hl-1"> </span><span class="hl-6">setTimeout</span><span class="hl-1">(</span><span class="hl-2">resolve</span><span class="hl-1">, </span><span class="hl-2">delay</span><span class="hl-1">));</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-0">throw</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Error</span><span class="hl-1">(</span><span class="hl-3">`Failed after </span><span class="hl-4">${</span><span class="hl-2">maxRetries</span><span class="hl-4">}</span><span class="hl-3"> attempts: </span><span class="hl-4">${</span><span class="hl-2">lastError</span><span class="hl-1">!</span><span class="hl-13">.</span><span class="hl-2">message</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">);</span><br/><span class="hl-1">}</span>
|
|
26
|
-
</code><button type="button">Copy</button></pre>
|
|
27
|
-
|
|
28
|
-
<h3 id="error-recovery" class="tsd-anchor-link">Error Recovery<a href="#error-recovery" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Save state before risky operations:</p>
|
|
29
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">processLargeDataset</span><span class="hl-1">(</span><span class="hl-2">items</span><span class="hl-1">: </span><span class="hl-2">Item</span><span class="hl-1">[]) {</span><br/><span class="hl-1"> </span><span class="hl-0">for</span><span class="hl-1"> (</span><span class="hl-4">let</span><span class="hl-1"> </span><span class="hl-2">i</span><span class="hl-1"> = </span><span class="hl-12">0</span><span class="hl-1">; </span><span class="hl-2">i</span><span class="hl-1"> < </span><span class="hl-2">items</span><span class="hl-1">.</span><span class="hl-2">length</span><span class="hl-1">; </span><span class="hl-2">i</span><span class="hl-1">++) {</span><br/><span class="hl-1"> </span><span class="hl-0">try</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">processItem</span><span class="hl-1">(</span><span class="hl-2">items</span><span class="hl-1">[</span><span class="hl-2">i</span><span class="hl-1">]);</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Save progress</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">"last_processed_index"</span><span class="hl-1">, </span><span class="hl-2">i</span><span class="hl-1">);</span><br/><span class="hl-1"> } </span><span class="hl-0">catch</span><span class="hl-1"> (</span><span class="hl-2">error</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">error</span><span class="hl-1">(</span><span class="hl-3">`Error processing item </span><span class="hl-4">${</span><span class="hl-2">i</span><span class="hl-4">}</span><span class="hl-3">:`</span><span class="hl-1">, </span><span class="hl-2">error</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Create activity for manual review</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">tools</span><span class="hl-1">.</span><span class="hl-2">plot</span><span class="hl-1">.</span><span class="hl-6">createActivity</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">type:</span><span class="hl-1"> </span><span class="hl-2">ActivityType</span><span class="hl-1">.</span><span class="hl-2">Note</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">title:</span><span class="hl-1"> </span><span class="hl-3">`Processing error at item </span><span class="hl-4">${</span><span class="hl-2">i</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">note:</span><span class="hl-1"> </span><span class="hl-2">error</span><span class="hl-1">.</span><span class="hl-2">message</span><br/><span class="hl-1"> });</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Continue with next item</span><br/><span class="hl-1"> </span><span class="hl-0">continue</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-7">// Resume from last checkpoint</span><br/><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">resumeProcessing</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">lastIndex</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">number</span><span class="hl-1">>(</span><span class="hl-3">"last_processed_index"</span><span class="hl-1">) || </span><span class="hl-12">0</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">items</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">Item</span><span class="hl-1">[]>(</span><span class="hl-3">"items_to_process"</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-2">items</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">processLargeDataset</span><span class="hl-1">(</span><span class="hl-2">items</span><span class="hl-1">.</span><span class="hl-6">slice</span><span class="hl-1">(</span><span class="hl-2">lastIndex</span><span class="hl-1"> + </span><span class="hl-12">1</span><span class="hl-1">));</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
30
|
-
</code><button type="button">Copy</button></pre>
|
|
31
|
-
|
|
32
|
-
<hr>
|
|
33
|
-
<h2 id="debugging-and-logging" class="tsd-anchor-link">Debugging and Logging<a href="#debugging-and-logging" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="structured-logging" class="tsd-anchor-link">Structured Logging<a href="#structured-logging" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Use consistent log formats:</p>
|
|
34
|
-
<pre><code class="typescript"><span class="hl-4">interface</span><span class="hl-1"> </span><span class="hl-5">LogContext</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">twistId</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-2">priorityId</span><span class="hl-1">?: </span><span class="hl-5">string</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-2">operation</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">;</span><br/><span class="hl-1"> [</span><span class="hl-2">key</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">]: </span><span class="hl-5">any</span><span class="hl-1">;</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">MyTwist</span><span class="hl-1"> </span><span class="hl-4">extends</span><span class="hl-1"> </span><span class="hl-5">Twist</span><span class="hl-1"><</span><span class="hl-5">MyTwist</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-6">log</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">level</span><span class="hl-1">: </span><span class="hl-3">"info"</span><span class="hl-1"> | </span><span class="hl-3">"warn"</span><span class="hl-1"> | </span><span class="hl-3">"error"</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">message</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">context</span><span class="hl-1">?: </span><span class="hl-5">Partial</span><span class="hl-1"><</span><span class="hl-5">LogContext</span><span class="hl-1">></span><br/><span class="hl-1"> ) {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">logEntry</span><span class="hl-1"> = {</span><br/><span class="hl-1"> </span><span class="hl-2">timestamp:</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Date</span><span class="hl-1">().</span><span class="hl-6">toISOString</span><span class="hl-1">(),</span><br/><span class="hl-1"> </span><span class="hl-2">level</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">message</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">twist:</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">id</span><span class="hl-1">,</span><br/><span class="hl-1"> ...</span><span class="hl-2">context</span><span class="hl-1">,</span><br/><span class="hl-1"> };</span><br/><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-8">JSON</span><span class="hl-1">.</span><span class="hl-6">stringify</span><span class="hl-1">(</span><span class="hl-2">logEntry</span><span class="hl-1">));</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">activate</span><span class="hl-1">(</span><span class="hl-2">priority</span><span class="hl-1">: </span><span class="hl-5">Pick</span><span class="hl-1"><</span><span class="hl-5">Priority</span><span class="hl-1">, </span><span class="hl-3">"id"</span><span class="hl-1">>) {</span><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-3">"info"</span><span class="hl-1">, </span><span class="hl-3">"twist activating"</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">priorityId:</span><span class="hl-1"> </span><span class="hl-2">priority</span><span class="hl-1">.</span><span class="hl-2">id</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">operation:</span><span class="hl-1"> </span><span class="hl-3">"activate"</span><span class="hl-1">,</span><br/><span class="hl-1"> });</span><br/><br/><span class="hl-1"> </span><span class="hl-0">try</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">setupWebhooks</span><span class="hl-1">();</span><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-3">"info"</span><span class="hl-1">, </span><span class="hl-3">"Webhooks configured successfully"</span><span class="hl-1">);</span><br/><span class="hl-1"> } </span><span class="hl-0">catch</span><span class="hl-1"> (</span><span class="hl-2">error</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-3">"error"</span><span class="hl-1">, </span><span class="hl-3">"Failed to setup webhooks"</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">error:</span><span class="hl-1"> </span><span class="hl-2">error</span><span class="hl-1">.</span><span class="hl-2">message</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">stack:</span><span class="hl-1"> </span><span class="hl-2">error</span><span class="hl-1">.</span><span class="hl-2">stack</span><span class="hl-1">,</span><br/><span class="hl-1"> });</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
35
|
-
</code><button type="button">Copy</button></pre>
|
|
36
|
-
|
|
37
|
-
<h3 id="debug-mode" class="tsd-anchor-link">Debug Mode<a href="#debug-mode" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Add debug flag for verbose logging:</p>
|
|
38
|
-
<pre><code class="typescript"><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">MyTwist</span><span class="hl-1"> </span><span class="hl-4">extends</span><span class="hl-1"> </span><span class="hl-5">Twist</span><span class="hl-1"><</span><span class="hl-5">MyTwist</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-4">get</span><span class="hl-1"> </span><span class="hl-6">debugMode</span><span class="hl-1">(): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-5">boolean</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">boolean</span><span class="hl-1">>(</span><span class="hl-3">"debug_mode"</span><span class="hl-1">).</span><span class="hl-6">then</span><span class="hl-1">((</span><span class="hl-2">v</span><span class="hl-1">) </span><span class="hl-4">=></span><span class="hl-1"> </span><span class="hl-2">v</span><span class="hl-1"> ?? </span><span class="hl-4">false</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">debug</span><span class="hl-1">(</span><span class="hl-2">message</span><span class="hl-1">: </span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-2">data</span><span class="hl-1">?: </span><span class="hl-5">any</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">debugMode</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-3">`[DEBUG] </span><span class="hl-4">${</span><span class="hl-2">message</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-1">, </span><span class="hl-2">data</span><span class="hl-1"> || </span><span class="hl-3">""</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">processData</span><span class="hl-1">(</span><span class="hl-2">data</span><span class="hl-1">: </span><span class="hl-5">any</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">debug</span><span class="hl-1">(</span><span class="hl-3">"Processing data"</span><span class="hl-1">, { </span><span class="hl-2">itemCount:</span><span class="hl-1"> </span><span class="hl-2">data</span><span class="hl-1">.</span><span class="hl-2">length</span><span class="hl-1"> });</span><br/><br/><span class="hl-1"> </span><span class="hl-0">for</span><span class="hl-1"> (</span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">item</span><span class="hl-1"> </span><span class="hl-4">of</span><span class="hl-1"> </span><span class="hl-2">data</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">debug</span><span class="hl-1">(</span><span class="hl-3">"Processing item"</span><span class="hl-1">, </span><span class="hl-2">item</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">processItem</span><span class="hl-1">(</span><span class="hl-2">item</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
39
|
-
</code><button type="button">Copy</button></pre>
|
|
40
|
-
|
|
41
|
-
<h3 id="performance-monitoring" class="tsd-anchor-link">Performance Monitoring<a href="#performance-monitoring" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Track operation durations:</p>
|
|
42
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">withTiming</span><span class="hl-1"><</span><span class="hl-5">T</span><span class="hl-1">>(</span><br/><span class="hl-1"> </span><span class="hl-2">operation</span><span class="hl-1">: </span><span class="hl-2">string</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">fn</span><span class="hl-1">: () </span><span class="hl-4">=></span><span class="hl-1"> </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-8">T</span><span class="hl-1">></span><br/><span class="hl-1">): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-8">T</span><span class="hl-1">> {</span><br/><span class="hl-1"> const </span><span class="hl-2">start</span><span class="hl-1"> = </span><span class="hl-2">Date</span><span class="hl-1">.</span><span class="hl-6">now</span><span class="hl-1">();</span><br/><br/><span class="hl-1"> try {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">result</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-6">fn</span><span class="hl-1">();</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">duration</span><span class="hl-1"> = </span><span class="hl-2">Date</span><span class="hl-1">.</span><span class="hl-6">now</span><span class="hl-1">() - </span><span class="hl-2">start</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">log</span><span class="hl-1">(</span><span class="hl-3">`[PERF] </span><span class="hl-4">${</span><span class="hl-2">operation</span><span class="hl-4">}</span><span class="hl-3">: </span><span class="hl-4">${</span><span class="hl-2">duration</span><span class="hl-4">}</span><span class="hl-3">ms`</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-2">result</span><span class="hl-1">;</span><br/><span class="hl-1"> } catch (error) {</span><br/><span class="hl-1"> const duration = Date.</span><span class="hl-6">now</span><span class="hl-1">() - start;</span><br/><span class="hl-1"> console.log(</span><span class="hl-3">`[PERF] </span><span class="hl-4">${</span><span class="hl-2">operation</span><span class="hl-4">}</span><span class="hl-3">: </span><span class="hl-4">${</span><span class="hl-2">duration</span><span class="hl-4">}</span><span class="hl-3">ms (failed)`</span><span class="hl-2">);</span><br/><span class="hl-2"> throw error;</span><br/><span class="hl-2"> }</span><br/><span class="hl-2">}</span><br/><br/><span class="hl-7">// Usage</span><br/><span class="hl-2">await this.withTiming(</span><span class="hl-3">"sync-calendar"</span><span class="hl-1">, </span><span class="hl-6">async</span><span class="hl-1"> () </span><span class="hl-4">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">await</span><span class="hl-1"> </span><span class="hl-5">this</span><span class="hl-1">.</span><span class="hl-5">syncCalendar</span><span class="hl-1">();</span><br/><span class="hl-1">});</span>
|
|
43
|
-
</code><button type="button">Copy</button></pre>
|
|
44
|
-
|
|
45
|
-
<hr>
|
|
46
|
-
<h2 id="security-best-practices" class="tsd-anchor-link">Security Best Practices<a href="#security-best-practices" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="secrets-management" class="tsd-anchor-link">Secrets Management<a href="#secrets-management" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Never hardcode secrets:</p>
|
|
47
|
-
<pre><code class="typescript"><span class="hl-7">// ❌ WRONG</span><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">apiKey</span><span class="hl-1"> = </span><span class="hl-3">"sk-1234567890abcdef"</span><span class="hl-1">;</span><br/><br/><span class="hl-7">// ✅ CORRECT - Use environment variables</span><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">apiKey</span><span class="hl-1"> = </span><span class="hl-2">process</span><span class="hl-1">.</span><span class="hl-2">env</span><span class="hl-1">.</span><span class="hl-8">API_KEY</span><span class="hl-1">;</span><br/><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-2">apiKey</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">throw</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Error</span><span class="hl-1">(</span><span class="hl-3">"API_KEY environment variable is required"</span><span class="hl-1">);</span><br/><span class="hl-1">}</span>
|
|
48
|
-
</code><button type="button">Copy</button></pre>
|
|
49
|
-
|
|
50
|
-
<h3 id="input-validation" class="tsd-anchor-link">Input Validation<a href="#input-validation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Validate all external input:</p>
|
|
51
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">onWebhook</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">: </span><span class="hl-2">WebhookRequest</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-7">// Validate signature</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">validateSignature</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">)) {</span><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">error</span><span class="hl-1">(</span><span class="hl-3">"Invalid webhook signature"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Validate schema</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">isValidPayload</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">.</span><span class="hl-2">body</span><span class="hl-1">)) {</span><br/><span class="hl-1"> </span><span class="hl-2">console</span><span class="hl-1">.</span><span class="hl-6">error</span><span class="hl-1">(</span><span class="hl-3">"Invalid webhook payload"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Process safely</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">processWebhook</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">.</span><span class="hl-2">body</span><span class="hl-1">);</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-2">private</span><span class="hl-1"> </span><span class="hl-6">validateSignature</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">: </span><span class="hl-2">WebhookRequest</span><span class="hl-1">): </span><span class="hl-2">boolean</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">signature</span><span class="hl-1"> = </span><span class="hl-2">request</span><span class="hl-1">.</span><span class="hl-2">headers</span><span class="hl-1">[</span><span class="hl-3">"x-webhook-signature"</span><span class="hl-1">];</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">expectedSignature</span><span class="hl-1"> = </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">computeSignature</span><span class="hl-1">(</span><span class="hl-2">request</span><span class="hl-1">.</span><span class="hl-2">body</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-2">signature</span><span class="hl-1"> === </span><span class="hl-2">expectedSignature</span><span class="hl-1">;</span><br/><span class="hl-1">}</span>
|
|
52
|
-
</code><button type="button">Copy</button></pre>
|
|
53
|
-
|
|
54
|
-
<h3 id="rate-limiting" class="tsd-anchor-link">Rate Limiting<a href="#rate-limiting" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Protect external APIs:</p>
|
|
55
|
-
<pre><code class="typescript"><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">RateLimiter</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-2">lastRequest</span><span class="hl-1">: </span><span class="hl-5">number</span><span class="hl-1"> = </span><span class="hl-12">0</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-2">minInterval</span><span class="hl-1">: </span><span class="hl-5">number</span><span class="hl-1"> = </span><span class="hl-12">1000</span><span class="hl-1">; </span><span class="hl-7">// 1 request per second</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">throttle</span><span class="hl-1"><</span><span class="hl-5">T</span><span class="hl-1">>(</span><span class="hl-6">fn</span><span class="hl-1">: () </span><span class="hl-4">=></span><span class="hl-1"> </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-5">T</span><span class="hl-1">>): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-5">T</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">now</span><span class="hl-1"> = </span><span class="hl-2">Date</span><span class="hl-1">.</span><span class="hl-6">now</span><span class="hl-1">();</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">timeSinceLastRequest</span><span class="hl-1"> = </span><span class="hl-2">now</span><span class="hl-1"> - </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">lastRequest</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-2">timeSinceLastRequest</span><span class="hl-1"> < </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">minInterval</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">delay</span><span class="hl-1"> = </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">minInterval</span><span class="hl-1"> - </span><span class="hl-2">timeSinceLastRequest</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-5">Promise</span><span class="hl-1">(</span><span class="hl-2">resolve</span><span class="hl-1"> </span><span class="hl-4">=></span><span class="hl-1"> </span><span class="hl-6">setTimeout</span><span class="hl-1">(</span><span class="hl-2">resolve</span><span class="hl-1">, </span><span class="hl-2">delay</span><span class="hl-1">));</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">lastRequest</span><span class="hl-1"> = </span><span class="hl-2">Date</span><span class="hl-1">.</span><span class="hl-6">now</span><span class="hl-1">();</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-6">fn</span><span class="hl-1">();</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-7">// Usage</span><br/><span class="hl-2">private</span><span class="hl-1"> </span><span class="hl-2">rateLimiter</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">RateLimiter</span><span class="hl-1">();</span><br/><br/><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">fetchData</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">rateLimiter</span><span class="hl-1">.</span><span class="hl-6">throttle</span><span class="hl-1">(</span><span class="hl-4">async</span><span class="hl-1"> () </span><span class="hl-4">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-6">fetch</span><span class="hl-1">(</span><span class="hl-3">"https://api.example.com/data"</span><span class="hl-1">);</span><br/><span class="hl-1"> });</span><br/><span class="hl-1">}</span>
|
|
56
|
-
</code><button type="button">Copy</button></pre>
|
|
57
|
-
|
|
58
|
-
<hr>
|
|
59
|
-
<h2 id="migration-and-versioning" class="tsd-anchor-link">Migration and Versioning<a href="#migration-and-versioning" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="version-tracking" class="tsd-anchor-link">Version Tracking<a href="#version-tracking" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Track twist version for migrations:</p>
|
|
60
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">activate</span><span class="hl-1">(</span><span class="hl-2">priority</span><span class="hl-1">: </span><span class="hl-2">Pick</span><span class="hl-1"><</span><span class="hl-2">Priority</span><span class="hl-1">, </span><span class="hl-3">"id"</span><span class="hl-1">>) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">"twist_version"</span><span class="hl-1">, </span><span class="hl-3">"1.0.0"</span><span class="hl-1">);</span><br/><span class="hl-1">}</span><br/><br/><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">upgrade</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">currentVersion</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">>(</span><span class="hl-3">"twist_version"</span><span class="hl-1">) || </span><span class="hl-3">"0.0.0"</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">compareVersions</span><span class="hl-1">(</span><span class="hl-2">currentVersion</span><span class="hl-1">, </span><span class="hl-3">"2.0.0"</span><span class="hl-1">) < </span><span class="hl-12">0</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">migrateToV2</span><span class="hl-1">();</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">compareVersions</span><span class="hl-1">(</span><span class="hl-2">currentVersion</span><span class="hl-1">, </span><span class="hl-3">"2.1.0"</span><span class="hl-1">) < </span><span class="hl-12">0</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">migrateToV21</span><span class="hl-1">();</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">"twist_version"</span><span class="hl-1">, </span><span class="hl-3">"2.1.0"</span><span class="hl-1">);</span><br/><span class="hl-1">}</span>
|
|
61
|
-
</code><button type="button">Copy</button></pre>
|
|
62
|
-
|
|
63
|
-
<h3 id="data-migration" class="tsd-anchor-link">Data Migration<a href="#data-migration" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Migrate stored data structures:</p>
|
|
64
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">migrateToV2</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-7">// V1 stored user data as separate fields</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">userId</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">>(</span><span class="hl-3">"user_id"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">userName</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">>(</span><span class="hl-3">"user_name"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">userEmail</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">>(</span><span class="hl-3">"user_email"</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-2">userId</span><span class="hl-1"> && </span><span class="hl-2">userName</span><span class="hl-1"> && </span><span class="hl-2">userEmail</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-7">// V2 uses a single user object</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">"user"</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-2">id:</span><span class="hl-1"> </span><span class="hl-2">userId</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">name:</span><span class="hl-1"> </span><span class="hl-2">userName</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">email:</span><span class="hl-1"> </span><span class="hl-2">userEmail</span><br/><span class="hl-1"> });</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Clean up old fields</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">clear</span><span class="hl-1">(</span><span class="hl-3">"user_id"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">clear</span><span class="hl-1">(</span><span class="hl-3">"user_name"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">clear</span><span class="hl-1">(</span><span class="hl-3">"user_email"</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
65
|
-
</code><button type="button">Copy</button></pre>
|
|
66
|
-
|
|
67
|
-
<h3 id="breaking-changes" class="tsd-anchor-link">Breaking Changes<a href="#breaking-changes" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Handle breaking changes gracefully:</p>
|
|
68
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">upgrade</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">version</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">>(</span><span class="hl-3">"version"</span><span class="hl-1">) || </span><span class="hl-3">"1.0.0"</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-2">version</span><span class="hl-1"> < </span><span class="hl-3">"2.0.0"</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-7">// V2 completely changed how webhooks work</span><br/><span class="hl-1"> </span><span class="hl-7">// Clean up old webhooks</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">oldWebhooks</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">[]>(</span><span class="hl-3">"webhooks"</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (</span><span class="hl-2">oldWebhooks</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">for</span><span class="hl-1"> (</span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">webhook</span><span class="hl-1"> </span><span class="hl-4">of</span><span class="hl-1"> </span><span class="hl-2">oldWebhooks</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">deleteOldWebhook</span><span class="hl-1">(</span><span class="hl-2">webhook</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">clear</span><span class="hl-1">(</span><span class="hl-3">"webhooks"</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Set up new webhook system</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">setupNewWebhooks</span><span class="hl-1">();</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">set</span><span class="hl-1">(</span><span class="hl-3">"version"</span><span class="hl-1">, </span><span class="hl-3">"2.0.0"</span><span class="hl-1">);</span><br/><span class="hl-1">}</span>
|
|
69
|
-
</code><button type="button">Copy</button></pre>
|
|
70
|
-
|
|
71
|
-
<hr>
|
|
72
|
-
<h2 id="performance-patterns" class="tsd-anchor-link">Performance Patterns<a href="#performance-patterns" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="lazy-loading" class="tsd-anchor-link">Lazy Loading<a href="#lazy-loading" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Load data only when needed:</p>
|
|
73
|
-
<pre><code class="typescript"><span class="hl-4">class</span><span class="hl-1"> </span><span class="hl-5">MyTwist</span><span class="hl-1"> </span><span class="hl-4">extends</span><span class="hl-1"> </span><span class="hl-5">Twist</span><span class="hl-1"><</span><span class="hl-5">MyTwist</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-2">_config</span><span class="hl-1">: </span><span class="hl-5">Config</span><span class="hl-1"> | </span><span class="hl-5">null</span><span class="hl-1"> = </span><span class="hl-4">null</span><span class="hl-1">;</span><br/><br/><span class="hl-1"> </span><span class="hl-4">private</span><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">getConfig</span><span class="hl-1">(): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-5">Config</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-0">if</span><span class="hl-1"> (!</span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">_config</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">_config</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1"><</span><span class="hl-5">Config</span><span class="hl-1">>(</span><span class="hl-3">"config"</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">_config</span><span class="hl-1">!;</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-4">async</span><span class="hl-1"> </span><span class="hl-6">someMethod</span><span class="hl-1">() {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">config</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">getConfig</span><span class="hl-1">(); </span><span class="hl-7">// Loaded once</span><br/><span class="hl-1"> </span><span class="hl-7">// Use config...</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
74
|
-
</code><button type="button">Copy</button></pre>
|
|
75
|
-
|
|
76
|
-
<h3 id="request-coalescing" class="tsd-anchor-link">Request Coalescing<a href="#request-coalescing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Combine multiple similar requests:</p>
|
|
77
|
-
<pre><code class="typescript"><span class="hl-2">private</span><span class="hl-1"> </span><span class="hl-2">pendingUserFetches</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Map</span><span class="hl-1"><</span><span class="hl-5">string</span><span class="hl-1">, </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-5">User</span><span class="hl-1">>>();</span><br/><br/><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">getUser</span><span class="hl-1">(</span><span class="hl-2">userId</span><span class="hl-1">: </span><span class="hl-2">string</span><span class="hl-1">): </span><span class="hl-5">Promise</span><span class="hl-1"><</span><span class="hl-2">User</span><span class="hl-1">> {</span><br/><span class="hl-1"> </span><span class="hl-7">// If already fetching, return existing promise</span><br/><span class="hl-1"> </span><span class="hl-6">if</span><span class="hl-1"> (this.pendingUserFetches.has(</span><span class="hl-2">userId</span><span class="hl-1">)) {</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">pendingUserFetches</span><span class="hl-1">.</span><span class="hl-6">get</span><span class="hl-1">(</span><span class="hl-2">userId</span><span class="hl-1">)!;</span><br/><span class="hl-1"> }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// Start new fetch</span><br/><span class="hl-1"> const </span><span class="hl-2">promise</span><span class="hl-1"> = </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-6">fetchUser</span><span class="hl-1">(</span><span class="hl-2">userId</span><span class="hl-1">);</span><br/><span class="hl-1"> this.pendingUserFetches.set(</span><span class="hl-2">userId</span><span class="hl-1">, promise);</span><br/><br/><span class="hl-1"> try {</span><br/><span class="hl-1"> </span><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-8">user</span><span class="hl-1"> = </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-2">promise</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-0">return</span><span class="hl-1"> </span><span class="hl-2">user</span><span class="hl-1">;</span><br/><span class="hl-1"> } finally {</span><br/><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">pendingUserFetches</span><span class="hl-1">.</span><span class="hl-6">delete</span><span class="hl-1">(</span><span class="hl-2">userId</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">}</span>
|
|
78
|
-
</code><button type="button">Copy</button></pre>
|
|
79
|
-
|
|
80
|
-
<h3 id="bulk-operations" class="tsd-anchor-link">Bulk Operations<a href="#bulk-operations" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Batch database operations:</p>
|
|
81
|
-
<pre><code class="typescript"><span class="hl-2">async</span><span class="hl-1"> </span><span class="hl-6">syncAllItems</span><span class="hl-1">(</span><span class="hl-2">items</span><span class="hl-1">: </span><span class="hl-2">Item</span><span class="hl-1">[]) {</span><br/><span class="hl-1"> </span><span class="hl-7">// ❌ SLOW - One at a time</span><br/><span class="hl-1"> </span><span class="hl-7">// for (const item of items) {</span><br/><span class="hl-1"> </span><span class="hl-7">// await this.tools.plot.createActivity({...});</span><br/><span class="hl-1"> </span><span class="hl-7">// }</span><br/><br/><span class="hl-1"> </span><span class="hl-7">// ✅ FAST - Bulk create</span><br/><span class="hl-1"> </span><span class="hl-0">await</span><span class="hl-1"> </span><span class="hl-4">this</span><span class="hl-1">.</span><span class="hl-2">tools</span><span class="hl-1">.</span><span class="hl-2">plot</span><span class="hl-1">.</span><span class="hl-6">createActivities</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">items</span><span class="hl-1">.</span><span class="hl-6">map</span><span class="hl-1">(</span><span class="hl-2">item</span><span class="hl-1"> </span><span class="hl-4">=></span><span class="hl-1"> ({</span><br/><span class="hl-1"> </span><span class="hl-2">type:</span><span class="hl-1"> </span><span class="hl-2">ActivityType</span><span class="hl-1">.</span><span class="hl-2">Action</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">title:</span><span class="hl-1"> </span><span class="hl-2">item</span><span class="hl-1">.</span><span class="hl-2">title</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">note:</span><span class="hl-1"> </span><span class="hl-2">item</span><span class="hl-1">.</span><span class="hl-2">description</span><br/><span class="hl-1"> }))</span><br/><span class="hl-1"> );</span><br/><span class="hl-1">}</span>
|
|
82
|
-
</code><button type="button">Copy</button></pre>
|
|
83
|
-
|
|
84
|
-
<hr>
|
|
85
|
-
<h2 id="next-steps" class="tsd-anchor-link">Next Steps<a href="#next-steps" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
|
|
86
|
-
<li><strong><a href="Runtime_Environment.html">Runtime Environment</a></strong> - Understanding execution constraints</li>
|
|
87
|
-
<li><strong><a href="Building_Custom_Tools.html">Building Tools</a></strong> - Creating reusable tools</li>
|
|
88
|
-
<li><strong><a href="Built-in_Tools.html">Built-in Tools</a></strong> - Comprehensive tool documentation</li>
|
|
89
|
-
<li><strong>API Reference</strong> - Explore detailed API documentation</li>
|
|
90
|
-
</ul>
|
|
91
|
-
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#advanced-topics"><span>Advanced <wbr/>Topics</span></a><ul><li><a href="#table-of-contents"><span>Table of <wbr/>Contents</span></a></li><li><a href="#complex-twist-architectures"><span>Complex twist <wbr/>Architectures</span></a></li><li><ul><li><a href="#multi-service-integration"><span>Multi-<wbr/>Service <wbr/>Integration</span></a></li><li><a href="#state-machine-pattern"><span>State <wbr/>Machine <wbr/>Pattern</span></a></li></ul></li><li><a href="#error-handling"><span>Error <wbr/>Handling</span></a></li><li><ul><li><a href="#graceful-degradation"><span>Graceful <wbr/>Degradation</span></a></li><li><a href="#retry-logic"><span>Retry <wbr/>Logic</span></a></li><li><a href="#error-recovery"><span>Error <wbr/>Recovery</span></a></li></ul></li><li><a href="#debugging-and-logging"><span>Debugging and <wbr/>Logging</span></a></li><li><ul><li><a href="#structured-logging"><span>Structured <wbr/>Logging</span></a></li><li><a href="#debug-mode"><span>Debug <wbr/>Mode</span></a></li><li><a href="#performance-monitoring"><span>Performance <wbr/>Monitoring</span></a></li></ul></li><li><a href="#security-best-practices"><span>Security <wbr/>Best <wbr/>Practices</span></a></li><li><ul><li><a href="#secrets-management"><span>Secrets <wbr/>Management</span></a></li><li><a href="#input-validation"><span>Input <wbr/>Validation</span></a></li><li><a href="#rate-limiting"><span>Rate <wbr/>Limiting</span></a></li></ul></li><li><a href="#migration-and-versioning"><span>Migration and <wbr/>Versioning</span></a></li><li><ul><li><a href="#version-tracking"><span>Version <wbr/>Tracking</span></a></li><li><a href="#data-migration"><span>Data <wbr/>Migration</span></a></li><li><a href="#breaking-changes"><span>Breaking <wbr/>Changes</span></a></li></ul></li><li><a href="#performance-patterns"><span>Performance <wbr/>Patterns</span></a></li><li><ul><li><a href="#lazy-loading"><span>Lazy <wbr/>Loading</span></a></li><li><a href="#request-coalescing"><span>Request <wbr/>Coalescing</span></a></li><li><a href="#bulk-operations"><span>Bulk <wbr/>Operations</span></a></li></ul></li><li><a href="#next-steps"><span>Next <wbr/>Steps</span></a></li></ul></div></details></div><div class="site-menu"><nav id="tsd-sidebar-links" class="tsd-navigation"><a href="https://plot.day" class="tsd-nav-link">Plot</a><a href="https://github.com/plotday/plot" class="tsd-nav-link">GitHub</a><a href="https://www.npmjs.com/package/@plotday/twister" class="tsd-nav-link">NPM</a></nav><nav class="tsd-navigation"><a href="../modules.html">Creating Plot Twists</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
|