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

gist-fs

v0.3.1

Published

A daily rate-limited way to have free storage in the wild

Downloads

141

Readme

gist-fs

Social Media Photo by Gary Ellis on Unsplash

A daily rate-limited way to have free storage in the wild.

⚠️ Warning

It is very important to understand that GitHub tokens are extremely powerful and could damage your projects if leaked in the wild, hence I wrote this Fight Club style rules to keep in mind:

  • the first rule of this module, is that you don't expose your GitHub token in the wild
  • the second rule of this module, is that you don't ignore the first rule in this list
  • the third rule of this module is that it's your fault if you ignored previous rules on this list

If you believe your personal token leaked remember to revoke it and create a new one, bing more careful next time 👍

🛈 GitHub Token

In order to use this module you need to generate a new token, possibly dedicated to gists things only.

Please go in settings/tokens to do so, after being logged in, and do the following:

Generate new token

Once you reach that lovely interface, please constrain such token to deal with gists only, so that a potential leak won't grant any other "super power" for your account:

Setup token

Once you have done that and you have your read-once token, please do not store it in any HTML or JS or CSS file, or anyone else, as that's your token to be able to use this module and you don't want that to be abused by anyone out there.

If your token leaks please remember to start again the procedure, remove the previous one, and not commit the same mistake again 😉

Live Demo that creates a new Gist on your behalf once you have provided your TOKEN, that will be stored in localStorage, and then check the devtools console.

API

This module provides various ways to bootstrap an instance of GistFS.

import {
  // the class as it is
  GistFS,
  // aliases to create a new instance
  gist_fs, gistFS,
  // aliases to create a new instance
  // and retrieve the TOKEN from localStorage
  gist_key_fs, gistKeyFS,
} from 'https://esm.sh/gist-fs';

const TOKEN = '<YOUR_PRIVATE_GITHUB_TOKEN>';

// direct class initialization
const a = new GistFS(TOKEN);

// indirect class initialization
const b = gist_fs(TOKEN);
const c = gistFS(TOKEN);

// indirect class initialization that
// will prompt for the token once
const c = gist_key_fs('unique_project_name');
const d = gistKeyFS('unique_project_name');
// unique_project_name is the localStorage key

GistFS

This class requires the authorization token when constructed:

const gfs = new GistFS(AUTHORIZATION_TOKEN);

The instance will expose the following methods.

create(options = {})

Create a new gist and return its most important details:

type GistDetails = {
  id:string,
  files:object,
  description:string,
  html_url:string,
  created_at:string,
  updated_at:string,
  public:boolean,
  comments:number,
  fs:GistFSHandler,
}

Differently from Octokit, the files can be passed as paths that will be handled via ease through the fs GistFSHandler instance.

const { id, fs } = await gist.create({
  description: 'GistFS live demo - example',
  'public': false,
  files: {
    './README.md': {
      content: '# Hello Gist'
    },
    // a `sub` folder example
    './sub/a.txt': {
      content: 'a'
    },
    './sub/b.txt': {
      content: 'b'
    },
  }
});

// hint: store the id to not always create a new gist
localStorage.set('gist_id', id);

// list gist content
fs.ls('./');
// ['README.md', 'sub']

fs.isDir('./sub'); // true

fs.ls('./sub');
// ['a.txt', 'b.txt']

fs.stats('./sub');
// { directory: true, files: ['a.txt', 'b.txt'] }

fs.stats('README.md');
// all details about the file as returned by Octokit

get(options)

If a gist id is already known, which is basically the gist unique identifier in its url, this method returns a GistDetails of such gist.

const { fs } = await gfs.get(gist_id);
fs.ls('.');
// ['README.md', 'sub']

The options parameter can be either the id or a GistDetails type or an object with a gist_id field.

// these are all fine and do the same
await gfs.get("unique-gist-identifier");
await gfs.get({ id: "unique-gist-identifier" });
await gfs.get({ gist_id: "unique-gist-identifier" });

list(options = {})

Retrieve a list of gists either from the generic public list or, if a username is passed, from a specific user. The returned list of gists will be of type GistDetails[] so that each gist can be used via its fs utility.

const gists = await gfs.list({
  // optional user to consider
  username: 'WebReflection',
  // optional page to retrieve
  page: 1,
  // optional max per page - see rate limit
  // this cannot be more than 100
  per_page: 30,
  // optional ISO date as string or as
  // instance of Date (converted as ISO)
  since: '2024-01-01T00:00:00Z'
});

// show all files (if any) per each gist
for (const gist of gists)
  console.log(gist.fs.ls('.'));

delete(options)

Remove a gist by id:

// these are all fine and do the same
await gfs.delete("unique-gist-identifier");
await gfs.delete({ id: "unique-gist-identifier" });
await gfs.delete({ gist_id: "unique-gist-identifier" });

GistFSHandler

The fs reference of each GistDetails is a special instance able to:

  • synchronous methods
    • fs.isDir(path):boolean that returns true if the path is a folder, where each folder is defined by an internal naming convention as gists don't support folders out of the box
    • fs.ls(path):string[] that returns a list of file names within a specific folder, it throws if the path is not a folder
    • fs.stats(path) that returns the file details from Octokit or { directory: true, files: [...] } when path is a folder
  • asynchronous methods
    • fs.read(path):Promise<string|File> that returns the file content as string if it was stored as string, otherwise a File instance
    • fs.rm(path, options = {}):Promise<void> that removes a path if it's a file or remove all files if it's a folder. In latter case the options must contain recursive boolean true or it wll throw
    • fs.write(path, content:string|File):Promise<void> to write content in a file. If the content is a string it's also then retrieved as such. If the content is an instance of File, it's stored as compressed base64 string as the REST API accepts only strings so this seemed to be fair. Once read, the file with that name, content, and type will be returned.

Now, because I am going on vacation you can have a gist (no pun intended) of how this module works, out of the index.html file which already code cover most of this module.