Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

static-tree

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

static-tree

Zero dependency builder for strongly typed static tree

  • 1.3.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
20
Maintainers
1
Weekly downloads
 
Created
Source

static-tree

bundlephobia.badge npm.static-tree.badge

This package is part of the static-tree monorepo


Table of Contents

Installation

npm install static-tree
yarn add static-tree
pnpm add static-tree

Quick Start

  1. Use the idiomatic tBuild to create a static tree. See tBuild for examples and options.

    import { tBuild } from 'static-tree';
    
    const { node: api } = tBuild('api', { // you can rename node here to whatever
      pathResolver: () => 'https://api.domain.example',
      build: (builder) =>
        builder
          .addChild('auth', {
            build: (builder) =>
              builder
                .addChild('logout')
                .addChild('oauth', {
                  build: (builder) => builder.addChild('google').addChild('discord'),
                  //...
                })
          })
          .addChild('camelCaseKey', {
            pathResolver: () => 'camel-case-key',
          })
          .addChild(':organization', { // notice some dynamic path here
            pathResolver: (_node, arg) => arg,
            build: (builder) =>
              builder.addChild(':user', {
                pathResolver: (_node, arg) => arg,
              }),
          }),
    });
    

    The declaration above will produce this tree structure:

    api (resolve statically to 'https://api.domain.example' at runtime)
      |
      |-- auth
      .     |-- logout
      .     |-- oauth
      .          |
      .          |-- google
      .          |-- discord
      |-- camelCaseKey (resolved statically to 'camel-case-key' at runtime)
      |-- :organization (resolved dynamically to the given argument at runtime)
            |
            |-- :user (resolved dynamically to the given argument at runtime)
    
  2. Access type-safe nested children

    root.auth.oauth.google.$.path();
    // -> 'https://api.domain.example/auth/oauth/google'
    
    // note that ":" here has no special effect
    // just for easier recognition as dynamic (think backend router system)
    root[':organization'][':user'].$.path({
      args: {
        ':organization': 'test-org',
        ':user': 'test-user',
      }
    });
    // -> 'https://api.domain.example/test-org/test-user'
    
    root.auth.logout.$.depth(); // -> 3
    root.auth.oauth.discord.$.root() // -> point back to root node
    

    the $ getter returns the TNodePublicApi collection of methods.

  3. Access type-safe data

    root.$.data(); // -> { some: 'data' }
    root.nestedChild.$.data().nestedChildData; // -> 101
    

Documentation & Terminologies

This repo includes a full api extracted documentation generated by @microsoft/api-extractor & @microsoft/api-documenter. Please refer to said docs for examples and details.

TerminologyDescription
static treea tree whose nodes are declared at build time and not likely to change at runtime
TNodea node of the static tree with optional inner TNodeData, optional parent, and zero or more children
ExtendedTNodea TNode with children inline as properties for better DX
ExtendedTNodeBuildertype-safe builder for ExtendedTNode
tBuildfunctional wrapper for ExtendedTNodeBuilder

Original Use Case

This package was derived from the solution to a specific problem I encountered frequently. Consider having this "config" object:

const AppConfig = {
  urls: {
    web: 'https://domain.example',
    api: {
      index: 'https://api.domain.example',
      auth: {
        index: '/auth',
        logout: '/logout',
        oauth: {
          index: '/oauth',
          google: '/google',
          // ...
        },
      },
    },
  },
};

To get a full api url of google oauth, we have to do quite a lot:

const { api: { auth, index } } = AppConfig.urls;
const path = index + auth.index + auth.index.oauth.index + auth.oauth.google;

Already there are some problems:

  • Verbosity: lots of reference to get to something, more typing equals more typos equals less productive time.

  • The inconsistency of the config structure: some path will require an object index, some path is just a string. We could refactor to something more predictable, although i think we can agree that this would quickly get out of hand and is very disorienting to look at:

    const AppConfig = {
      urls: {
        web: {
          base: 'https://domain.example',
          paths: {},
        },
        api: {
          base: 'https://api.domain.example',
          paths: {
            auth: {
              base: '/auth',
              paths: {
                logout: {
                  base: '/logout',
                },
                // ...
              },
            },
          },
        },
      },
    };
    

Introducing static-tree, arguably a better alternative to the above.

import { tBuild } from 'static-tree';

const { node: api } = tBuild('api', {
  pathResolver: () => 'https://api.domain.example',
  build: (builder) => builder
    .addChild('auth', {
      build: (builder) => builder
        .addChild('logout')
        .addChild('oauth', {
          build: (builder) => builder
            .addChild('google')
            .addChild('discord'),
            //...
        }),
    }),
});

You might say, why the ugly builder pattern? Because I have not figured out any other pattern that allows the same level of type-safety. It seems like a lot for what would be an object declaration, but consider what we can do now:

api.auth.oauth.google.$.path(); // -> 'https://api.domain.example/auth/oauth/google'

Even more cool things (and perhaps more in the future if we need to extend the api):

api.auth.oauth.google.$.path({ depth: 2 }); // -> 'oauth/google'
api.auth.oauth.google.$.path({ depth: -2 }); // -> 'https://api.domain.example/auth'

Keywords

FAQs

Package last updated on 09 Feb 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc