Bookmate - Watch changes in Chrome bookmarks, and use bookmarks as an append-only key-value store via an fs-like API.

Overview

📗 Bookmate npm npm visitors+++

An append-only key-value store built on Chrome bookmarks, plus an asychronous stream of Bookmark changes. For NodeJS

Actual production example:

import {bookmarkChanges} from 'bookmate';

// ...

async function startObservingBookmarkChanges() {
  for await ( const change of bookmarkChanges() ) {
    switch(change.type) {
      case 'new':     archiveAndIndexURL(change.url);         break;
      case 'delete':  deleteFromIndexAndSearch(change.url);   break;
      default: break;
    }
  }
}

Features

Bookmate:

Very Long Introduction 🧙‍♂️

Have you ever wanted to build something that uses Chrome bookmarks but not release it as a Chrome extension? Have you ever wanted to programmatically alter the bookmarks in Chrome, or monitor these for additions, updates and deletions--again, without using extension APIs?

There's a lot of libraries out there to parse Chrome bookmarks, but none that actually make it simple to modify them or monitor them for changes. Maybe you want to trigger a certain job like archiving a web page every time a bookmark is added--or something else? Just imagine! The 🌏 is your 🦪 ! 💎

Imagine you could do this, what would you build? Because what you couldn't do before, you now can. Actually...you probably could have done it, because it's not that hard.

Bookmate makes it possible to monitor and modify Chrome bookmarks, and have those changes propagated to sync--which means that the bookmarks and folders you insert on one device, will show up in Chrome on other devices where Chrome is logged into that same account.

But cool your heels there a little bit, because there are a few major caveats that come with offering this functionality on the back of a flagship product of one of the internet-tycoon companies, one with major engineering chops and resources. Chrome has built a massive global infrastructure to sync data for their hundreds of millions of users, and something of this scale has to be reliable, and resilient against corruption. That prevents certain features from working with our current approach.

So the following things are currently impossible with Bookmate, because we don't know a simple way to add the relevant sync-related metadata to ensure the following operations propagate:

  • delete a bookmark. ✖️
  • move a bookmark. ✖️
  • rename a bookmark. ✖️

That sounds like everything you'd want to do!--right? Maybe so, maybe so. And if so, well I'm sorry, but you're flat of luck with Bookmate.

But if you're use-case is different to that, if maybe it includes adding bookmarks, or reading bookmarks, or monitoring bookmarks for changes, well there's still plenty you can do. Have you ever wanted to, for instance:

  • read a bookmark? ✔️
  • read a bookmark folder? ✔️
  • add a new bookmark? ✔️
  • see if a bookmark exists? ✔️
  • watch to see if any bookmarks are added, deleted or updated? ✔️

Well now you can do all those things! So, no stress friend--unfurrough that brow, it's gonna be OK 😄

The weird 🔦 thing about this...is that you are able to use Chrome bookmarks as a globally-synced single-tenant append-only key value multi-store, with the keys being valid URLs, and the values being the bookmark name text. Very interesting! Not that I am suggesting you do this, firstly, whether you are not discouraged from doing this or not is an unknown, but presumably the Sync team (if there is such a thing) would be unhappy with people using their infra for such purposes. But it certainly enables some interesting bookmark-related use cases. And if it were not discouraged it would most definitely enable some interesting use cases with respect to saving various forms of app data specific to a person or Profile, for various Chrome-related apps that existed outside of the Chrome Extensions WebStore and other places.

Get

$ npm i --save bookmate@latest

API

readFileSync(path[, options])

Returns the contents of the Bookmark at the path.

If the encoding option is 'json' then this function returns a <BookmarkNode>. Otherwise, if the encoding option is specified then this function returns a string, otherwise it returns a buffer.

It cannot be called on a folder. To get the contents of a folder use readdirSync()

readdirSync(path[, options])

Reads the contents of the folder.

If options.withFileTypes is set to true, the result will contain <BookmarkNode> objects.

🚧

Well this is a little embarrassing 😅 — I'm sorry, other documentation should go here. 👷‍♀️

The outstanding fs-like functions to document currently are:

  • existsSync
  • writeFileSync
  • mkdirSync
  • promisesWatch (*aka bookmarkChanges)

And other additional functions to document currently are:

  • mount
  • unmount
  • getProfileRootDir
  • saveWithChecksum
  • and bookmarkChanges (same as promisesWatch, actually--just an alias! 😜 😉 xx 😜 )

And, finally, the types that currently need documenting are:

  • BookmarkNode
  • SerializedPathArray
  • PathArray

But, not to worry--they (the fs-ones anyway) are pretty much like the NodeJS fs versions so you can head over there or read the code to know more—until somebody gets around to finishing these docs.

Implementation Progress & Roadmap 💹

  • emit change events for URL bookmark additions, deletions and name changes
  • existsSync
  • readFileSync
  • writeFileSync
  • readdirSync
  • mkdirSync
  • promisesWatch (*aka bookmarkChanges)
  • emit events for Folder additions, deletions and name changes

Decisions & Undecided 💭 🔎 (+ Research Log)

  • use an async generator to create a stream consunable via for await ... of
  • utilize an fs-like API for familiarity and structure
  • reasearch ways to overcome remote sync change merges overwriting local changes enacted through filesystem that do not have the expected sync-related metadata
    • observe profile directory filesystem changes on manual Bookmark deletion while Chrome is offline: will it save metadata?
      • appears to save in a sync store LevelDB but not certain
    • examine source code to determine exactly how deletion progresses
      • spent a couple hours looking at the code, and while I have a rough idea, nothing conclusive emerges and
      • certainly no clear way to leverage filesystem changes to insert valid metadata for changes we may make through the file system
    • consider using a bridge made from a browser extension running on a Chrome instance started by the NodeJS process with --silent-launch and extensions command-line flags that is instrumented with Chrome Remote Debugging Protocol to expose relevant Chrome Extension APIs to NodeJS via the CRDP WebSocket.
      • Likely possible, certainly so without silent launch (tho with we can probably just instrument an extension background page, even tho there's no visible window). Tho basically this seems like constructing a massive and elaborate Rube Goldberg machine just to thrust a red rubber glove to push a tiny button that says "Delete Bookmark".
    • See if Global Sync respects a local "Move" operation so that we may implement Delete via a "Move to Trash" semantic.
      • Unfortuantely Moves are neither propagated by Sync, but nor are they reverted. It's not a loophole, because: 1) The "deletions" (actually moves to a Trash folder we mkdirSync() are not propagated to other sync clients (other Chrome browsers on other devices where you are signed in); and 2) it's unclear how long these may actually persist for, if some other change triggers sync to identify these nodes have been moved, then the local changes may be reverted. So I think it's better to avoid providing this possibly unreliable API, than to do so, and end up breaking the implicit promise people took its existence to mean, which they didn't in any case dissuade themselves of by reading the docs or code details more closely.
  • abandon current attempts to implement deletion, renaming and moving that is not reverted by Chrome's Unified Sync and Storage and Sync Model API
  • try again in future to examine source code, monitor local filesystem in Chrome Profile directory, and otherwise attempt to innovate a way to perform local changes to the Bookmarks store (besides adds, which we can do, and which are propagated), and emit somehow the correct sync metadata to ensure: 1) those changes are propagated, and; 2) those changes are not reverted by sync merging in remote 'corrections'.

Disclaimer

No connection or endorsement expressed or implied with Google, Alphabet, Chrome, Sync or the Chromium authors.

Contributions ❤️

Welcome! It's all kind of new so many you can help also set up a contributing guidelines, documentation and so on 😹

License ⚖️

AGPL-3.0 © Cris


📗 Bookmate

You might also like...

Resolve parallel promises in key-value pairs whilst maintaining type information

async-kv Resolves promises in key-value pairs maintaining type information. Prerequisites NodeJS 12 or later Installation npm i async-kv yarn add asyn

Feb 17, 2022

A common front-end/Service Worker-based Key/Value database based on CacheStorage

Cache-DB A common front-end/Service Worker-based Key/Value database based on CacheStorage const db = new CacheDB('ChenYFanDB') undefined await d

Sep 30, 2022

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Dec 21, 2022

🧩 TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types such as number or boolean (not Value Objects)

🧩 TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types such as number or boolean (not Value Objects)

🧩 TypeScript Primitives type TypeScript utility type in order to ensure to return only properties (not methods) containing values in primitive types

Dec 7, 2022

Easily create key board shortcuts for your JS functions. Built using JS only with no other dependency. Inspired from MacOS spotlight

floodlightjs Inspired from macOS spotlight, floodlight is simple JS library that will show a search area. How the search is handled is completely on y

Aug 12, 2022

Application for organizing, rating, and sharing TV series and movies that you are watching or would like to watch.

Application for organizing, rating, and sharing TV series and movies that you are watching or would like to watch.

Watch This! We're living in an age of virtually unlimited ntertainment options, and keeping up with it all can be a little overwhelming. Using the Wat

Jun 13, 2022

A personal semantic search engine capable of surfacing relevant bookmarks, journal entries, notes, blogs, contacts, and more, built on an efficient document embedding algorithm and Monocle's personal search index.

A personal semantic search engine capable of surfacing relevant bookmarks, journal entries, notes, blogs, contacts, and more, built on an efficient document embedding algorithm and Monocle's personal search index.

Revery 🦅 Revery is a semantic search engine that operates on my Monocle search index. While Revery lets me search through the same database of tens o

Dec 30, 2022

Remarkable is a simple extension that automatically keeps your bookmarks clean & up-to-date.

Remarkable is a simple extension that automatically keeps your bookmarks clean & up-to-date.

Remarkable Remarkable is a simple extension that automatically keeps your bookmarks clean & up-to-date. Installation (Other browsers coming soon - sor

Dec 21, 2022
Comments
  • WriteFileSync usage?

    WriteFileSync usage?

    This is a really awesome package. However I can't seem to figure out the api for writing new bookmarks?

    My use case is a dev environment for bookmarklets. I'm hoping to have webpack update a bookmarklet anytime the source code changes (sorta like hot reloading). I want to keep track of the bookmark by ID and delete it when the program exits.

    Do you have an example of how to write a bookmark to the toolbar?

    opened by polyrhythmatic 0
Owner
Cris
Making real some ideas that want to be born.
Cris
A JavaScript library to watch the DOM changes in a single callback

watchmydom A Javascript library to watch the DOM changes in a single callback Demo Click here to see the detailed demo. Download Minified script file

Muhammed Saifudeen (saif) 2 Oct 30, 2020
Keep the type of storage value unchanged and change array and object directly. Supports listening to the changes and setting expires.

proxy-web-storage A more convenient way to use storage through proxy. try it on codesandbox. Install npm i proxy-web-storage Features Base Keep the ty

null 221 Dec 25, 2022
Easily open daily notes and periodic notes in new pane; customize periodic notes background; quick append new line to daily notes.

Obsidian daily notes opener This plugin adds a command for opening daily notes in a new pane (so that a keyboard shortcut could be used!) and gives ex

Xiao Meng 16 Dec 26, 2022
An Obsidian plugin to add (prepend or append) specified content to a note (existing or new) without opening another pane.

Obsidian Note Content Pusher An Obsidian plugin to add (prepend or append) specified content to a note (existing or new) without opening another pane.

Henry Gustafson 21 Nov 6, 2022
Link para download da extensão via Chrome Web Store:

Extensão Chrome - Gerador de Dados Essa extensão não exige permissões, não coleta dados do usuário e não processa nenhum dado com terceiros. Fique em

Eduardo Pinheiro 4 May 20, 2022
Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

BrowstorJS ?? ?? ?? Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all

Nullix 8 Aug 5, 2022
A simple in-memory key-value cache for function execution, allowing both sync and async operations using the same methods

A simple in-memory key-value cache for function execution, allowing both sync and async operations using the same methods. It provides an invalidation mechanism based both on exact string and regex.

cadienvan 10 Dec 15, 2022
Byteroo - Key-value storage for your Node.js applications

Byteroo Byteroo is a key-value storage for your Node.js applications. Usage: const Byteroo = require('byteroo'); const storage = new Byteroo({ name:

JMax 1 Jan 3, 2022