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

@tactics/luwio-app

v0.0.65

Published

Common components for all application powered by Luwio

Downloads

2,196

Readme

Luwio-Application

Common tools for all applications powered by Luwio

Internationalization

State: P.O.C

This is a wrapper around I18n (https://react.i18next.com/).

We do this to standardize everything around internationalization in all Luwio apps. This way we also prevent vendor detail from leaking into the entire code base of every app.

Initializing the internationalization.

This will return a Promise containing a Result Monad (Success or Failure). (@tactics/js-monad)

const MaybeI18n = Internationalization({
    fetch: "https://api.luwio.be/api/shop/translations/{{lng}}",
    app: "shop"
    domain: "luwio.sport.vlaanderen"
    language: "nl",    
    defaults: {}, 
    headers: () => ({}),
    showMissing: import.meta.env.MODE === "development",
}).bootstrap();

Passing to the provider.

You can then unwrap the Result when successful and pass it to the internationalizationProvider. Along with a default language and all supported languages of the app.

const I18n = MaybeI18n.unwrap();

<InternationalizationProvider
  i18n={I18n}
  defaultLanguage={"nl"}
  supportedLanguages={["nl", "en", "fr"]}
>
  {children}
</InternationalizationProvider>

Internationalization hooks.

Then we provide all hooks you needs for translations in your app.

const t = useTranslate();
const {changeLanguage, currentLanguage, defaultLanguage, supportedLanguages} = useLanguage();

Components

When bootstrapping a Luwio App, we first need to load the info about the App before we can render a page. Instead of showing a blank page during this setup, you can use the Luwio components to show a loading screen to the user.

if (MaybeI18n instanceof Awaiting) {
    return <Bootstrap />;
}

When something fails during the bootstrap fase, we need to show a generic error page. You can use the bootstrap failure component to do so.

if (MaybeI18n instanceof Failure) {
    return <BootstrapFailure />;
  }

LuwioRouter

State: Beta

The LuwioRouter is a powerful routing solution designed specifically for modern browser-based applications. Built on top of the robust @tanstack/router, it simplifies routing by exposing only the most essential, curated features developers need.

Read all about the LuwioRouter

Storage

State: P.O.C

Javascript has a default Storage interface and has 2 classes implementing this. Our storage interacts with this interface and adds a third storage, cookie storage.

  • window.sessionStorage : data in sessionStorage is cleared when the page session ends. (https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)

  • window.localStorage : localStorage data has no expiration time. (https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)

const StorageProvider = window.sessionStorage;
const [maybeThemeMode, setRawThemeMode] = useStorage<ThemeMode>("theme_mode", ThemeMode.STANDARD, StorageProvider, serialize, deserialize);
const StorageProvider = window.localStorage;
const [maybeThemeMode, setRawThemeMode] = useStorage<ThemeMode>("theme_mode", ThemeMode.STANDARD, StorageProvider, serialize, deserialize);
  • CookieStorage : store in Cookie on machine.

Due to the extra options a Cookie Storage has and that it needs to comply to the Storage interface. We add the extra Cookie specific options optionally in the contructor of the storage.

// With extra cookie options
const StorageProvider = new CookieStorage(
    {
      expires: new Date('2025-08-14T15:30:00'),
      path: '/',
      domain: '.luwio.be',
      secure: true,
      sameSite: "Lax"
    }
);
 ...

Another benefit of our Storage implementation is that we require serialization and deserialization functions inside our hook. This allows you to have fine-grained control over how the item is put into storage and read out of it.

Serialization is straightforward most of the time and will pretty much be the same for all items. It is just converting the item you want to store to a string.

Having fine-grained control over deserialization is the main benefit here. Since the structure of an object that was put into storage can change over time as your project evolves. But the old structure can still be present on the users device, you don't have control over this.

In the deserialization you can include some validation and when the object can't be recreated you just pass a Failure along, as-if the item did not exist in storage and move along.

interface UserProfile {
  name: string;
  age: number;
}

const serializeUserProfile = (profile: UserProfile): Result<string> => {
  try {
    const jsonString = JSON.stringify(profile);
    return Success.of(jsonString);
  } catch (error) {
    return Failure.dueTo("Failed to serialize user profile", "SERIALIZE_FAILURE");
  }
};

const deserializeUserProfile = (raw: string): Result<UserProfile> => {
  try {
    const parsed = JSON.parse(raw);
    if (typeof parsed.name === "string" && typeof parsed.age === "number") {
      return Success.of(parsed as UserProfile);
    } else {
      return Failure.dueTo("Invalid user profile format", "INVALID_FORMAT");
    }
  } catch (error) {
    return Failure.dueTo("Failed to deserialize user profile", "DESERIALIZE_FAILURE");
  }
};

const [user, setUser, clearUser] = useStorage<UserProfile>(
    'user',
    null,
    window.localStorage,
    serializeUserProfile,
    deserializeUserProfile
);

Error Handling

State: P.O.C

DateTime

State: P.O.C

Auth

State: TODO

Theming

State: TODO