npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@feinarbyte/atom-module

v8.4.0

Published

The Atoms Framework is a state management library designed to manage your application's state in a predictable way. It's based on the concept of "atoms", which are units of state that can be read and written to. Key Concepts

Downloads

85

Readme

Atoms Framework

The Atoms Framework is a state management library designed to manage your application's state in a predictable way. It's based on the concept of "atoms", which are units of state that can be read and written to. Key Concepts

  1. Atoms: Atoms represent pieces of state in your application. They are the smallest units of state that can be read and written to. Atoms are defined in the backend and can be subscribed to in realtime using the useAtom hook.

  2. Context: All atoms live in a certain context. Context is automatically provided in the frontend using the useContext hook. Context is automatically provided to all api calls and all subscriptions.

  3. Reducers: The application is modified through reducers. Each reducers get the transaction as first argument and can specify an arbitrary number of other arguments.

  4. Project Config: The Project Config is a configuration for the project that specifies the atoms, relations, and reducers used in the application.

  5. Relations: Relations define how different atoms are related to each other.

Project Config

The Project Config is a configuration for the project that specifies the atoms, relations, and reducers used in the application.

export const AIConfig: ProjectConfig<AIAtoms, AIContext, AIRelations> = {
  atomIndex: {
    [AIAtoms.Todo]: TodoAtom,
    // other atoms...
  },
  relations: {
    [AIRelations.Todo]: {
      identifier: AIContext.TodoId,
      identifierType: 'string',
      parents: [],
      pathes(name) {
        return [`t/${AIContext.TodoId}/${name}`];
      },
      reducers: [createTodo, toggleTodo, deleteTodo],
    },
    // other relations...
  },
};

The ProjectConfig is defined by the AIAtoms, AIContext, and AIRelations enums. The AIAtoms enum represents the atoms in the application, which are the smallest units of state that can be read and written to. The AIContext enum represents the context in which all atoms live. This context is automatically provided in the frontend using the useContext hook and is automatically provided to all API calls and all subscriptions. The AIRelations enum defines are abstract entites like users. Each atom must belong to one of them. The entites can have hierarchical relations to each other, which are defined in the parents property. The pathes property defines the pathes in the database where the data for this relation is stored. The reducers property defines the reducers that can be used to update the state of the atoms in this relation.

Defining Atoms

Atoms are defined as classes with the @ActiveAtom decorator. They represent a piece of state in your application.

export interface ITodo {
  id: string;
  text: string;
  completed: boolean;
}

@ActiveAtom({
  relation: AIRelations.Todo,
  provideViaAtomResolver: (context, data): ITodo => {
    return data;
  },
})
export class TodoAtom extends Atom<AIAtoms.Todo, ITodo> implements ITodo {
  public readonly __type = AIAtoms.Todo;
  public id: string;
  public text: string;
  public completed: boolean;
}

Virtual Atoms

Virtual atoms are a special kind of atom that do not directly represent a piece of state, but instead derive their state from other atoms. They are defined as classes with the @ActiveAtom decorator, similar to regular atoms, but they extend the VirtualAtom class instead of the Atom class.

Here is an example of a virtual atom:

@ActiveAtom({
  // Defines the relation for this virtual atom
  relation: AIRelations.Tag,
  // Resolver function to provide data for this virtual atom
  provideViaAtomResolver: (context, data): ITag => {
    return data;
  },
})
// TagAtom is a virtual atom that derives its state from other atoms
export class TagAtom extends VirtualAtom<AIAtoms.Tag, ITag> implements ITag {
  // The type of this atom, used for identification
  public readonly __type = AIAtoms.Tag;
  // The entries for this atom, derived from other atoms
  public entries: { [entryId: string]: string };

  // Dependencies of this virtual atom, used to derive its state
  public static dependencies = [
    {
      // The EntryAtom dependency, from which this virtual atom derives its state
      ctor: EntryAtom,
      // Function to get context overrides from the EntryAtom dependency
      getContextOverridesFromDependency: (data: IEntry) => {
        return data.tags.map((tag) => ({ [AIContext.Tag]: tag }));
      },
    },
  ];

  // Function to handle changes in dependencies
  public async __onDependencyChange(
    _query: string,
    newValue: IEntry,
  ): Promise<void> {
    const entryId = this.__getContextEntry(AIContext.EntryId);
    this.entries[entryId] = newValue.title ?? (entryId as string);
  }
  // Function to handle deletion of entities
  __onEntityDelete(relation: string, id: string): Promise<void> {
    delete this.entries[id];
  }

  // Function to provide an empty value when no data is available
  protected override __provideEmptyValue(): ITag {
    return { entries: {} };
  }
}

Defining Reducers in the backend

Reducers are functions that specify how the application's state changes in response to actions. They are used to handle and update the state of an atom.

export async function createTodo(
  transaction: TransactionManager,
  text: string,
): Promise<string> {
  const todoId = Math.random().toString(36).substring(2, 15);
  transaction.setEntry(AIContext.TodoId, todoId);
  await transaction.spawnAtom(
    TodoAtom,
    {
      id: todoId,
      text,
      completed: false,
    },
    { [AIContext.TodoId]: todoId },
  );
  return todoId;
}

Accessing reducers in the frontend

there is a helper to create a hook that provides the api client to the frontend. The api client will group all reducers by relation and provide them as async functions that automatically trigger the reducer in the backend. The context is automatically passed to the reducer.

import { makeUseApiClient } from '@feinarbyte/atom-client';
import { APIClient } from '../generated/apiClient';

// Create a hook for the API client
export const useAiClient = makeUseApiClient(APIClient);

// Define a component that uses the API client
export const SomeComponent = () => {
  // Get the API client
  const aiClient = useAiClient();

  // Return a div that creates a Todo when clicked
  return <div onClick={() => aiClient.Todo.create('text')}>Click me</div>;
  // ...
};

Reading Atoms in the frontend

Atoms can be subscribed to, this way all information will always be in sync with the backend automatically.

###creating a subscription client

import { makeUseAtom } from '@feinarbyte/atom-client';
import { atomDataIndex } from '../generated/atomdataindex';
import { AIAtoms } from '../generated/types';
export const useAiAtom = makeUseAtom<AIAtoms, atomDataIndex>();

using the subscription client

import { useAiAtom } from './useAiAtom';

// Define a functional component
export const SomeComponent = () => {
  // Use the atom client to subscribe to the Tag atom
  const tagAtom = useAiAtom(AIAtoms.Tag);

  // Use the atom client to subscribe to the Tag atom with a specific context
  const tagAtom = useAiAtom(AIAtoms.Tag, {[AiContext.Tag]: tagId});

  // Use the atom client to subscribe to the Tag atom and extract the id
  // If the tag is not found, it will return null
  const tag = useAiAtom(AIAtoms.Tag, (tag) => tag.id) ?? null;
  ....