辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

Technical exploration xLog 4 encapsulates xLogDriver with unStorage.

Technical Exploration xLog 4 Wrapping xLogDriver with unStorage

Internal Links

  • [[Technical Exploration xLog 1 Feasibility Study]]
  • [[Technical Exploration xLog 2 Understanding xlog Authentication]]
  • [[Technical Exploration xLog 3 Implementing an Obsidian Plugin]]
  • [[Technical Exploration xLog 4 Wrapping xLogDriver with unStorage]]
  • [[Quick Guide - CrossBell SDK Behind xLog]]
  • [[What Can We Learn from the Official XLOG Obsidian Plugin]]
  • [[Developing Obsidian Sync To Xlog Plugin - Handling Obsidian Images]]
  • [[Quick Guide - CrossBell's Open Source Works]]
  • [[Quick Guide - Obsidian Docs - Focusing on Plugin Development]]

Related

  • [[unjs_unstorage]]

Background#

Continuing from the previous three articles, we believe that xlog's ambition should not be limited to the current web-based blog, but should aim to be a better headlessCMS. In order to publish articles more smoothly, we developed the sync-to-xlog plugin to make the synchronization process easier.

Looking ahead, can we expand the display scenarios of xlog and create our own blog using Nuxt? Following this line of thought, I realized that the current method of obtaining xlog could be simplified using unjs/unstorage, while also adding a bit of difficulty to myself.

A brief introduction to unstorage, it can be understood as a universal plugin that provides a unified interface. By using different driver adapters, the differences in details can be smoothed out.

The usage is actually very simple, you can easily understand it, just like using localstorage.

import { createStorage } from "unstorage";
const storage = createStorage(/* opts */);
await storage.getItem("foo:bar");
await storage.hasItem()
// getItem getItems
// getItemRaw
// setItem setItems setItemRaw
// getMeta setMeta removeMeta
// getKeys

Technical Design#

It doesn't seem complicated.

First, customize the xLogDriver.

Implement the corresponding APIs:

  • Define getItem('site')
  • Define getKeys() - return a list
  • Define getItem('slugOrID')
const storage = createStorage({
  driver: xLogStorageDriver({
    token: "", // optional
    charactorID: "", // required
  }),
});

// console.log("list", list);

const list = await storage.getKesy();
console.log("list", list);

Code Implementation#

It doesn't seem difficult, I will implement it later. I will update here if there is any progress.

This time, we won't use the restful API request, but use the crossbell.js SDK to complete all operations.

I actually wrote it, but it didn't feel right, mainly because the return value of getKeys can only be string[], so I gave up. I'll come back to it after a few major versions.

It seems that implementing only getItems is also possible?

After hesitating for a while, I still implemented it. The source code is open source here: https://github.com/Otto-J/unstorage-xlog-driver

You can use it like this:

/* eslint-disable unicorn/prefer-top-level-await */
import { createStorage } from "unstorage";

import { xLogStorageDriver } from "unstorage-xlog-driver";

const OTTO_ID = 53_709;

async function main() {
  const storage = createStorage({
    driver: xLogStorageDriver({
      characterId: OTTO_ID,
      ttl: 60 * 60,
    }),
  });

  // const keys = await storage.getKeys();
  // console.log(keys);

  const info = await storage.getItem("72.md");
  // const info = await storage.getMeta("72.md");
  console.log(info);
}
main();

Pitfall Experience#

Not all APIs need to be fully implemented#

At first, I didn't have a clear understanding of unstorage. I thought that all functions needed to be implemented, but later I realized that only a few main methods need to be implemented.

What is the appropriate return value for getKeys?#

getKeys must return string[]. Through practice, I found that returning ${noteID}.md here is better, because it can be configured like this in the nuxt-content module, generating virtual files that are similar to local files, making it more consistent.

export default defineNuxtConfig({
  content: {
    sources: {
       driver: path.resolve(
          __dirname,
          "node_modules/unstorage-xlog-driver/dist/index.mjs",
        ),
        characterId: 53709,
        prefix: "/blogs",
    }
  }
})

With this magical sources configuration, remote data can be pulled to the local ls .nuxt/content-cache/parsed/xLog/blogs, and you can see these theoretically virtual files.

There is a good screenshot here, but after thinking about it, I decided not to include it. It's just a bunch of garbled characters and not aesthetically pleasing.

How to design getItem appropriately#

getItem('72.md') only returns the plain text of the markdown. In this way, I lose some information, such as slug/update_time, etc.

I came up with a solution that suits me well, which is to intercept the plain text, put the information I want to fill in into the frontmatter, and then merge it with the original markdown before returning it. This way, the remote data remains unchanged, and I can handle it myself.

The necessary cache to add#

I was surprised to find that there were countless requests triggered, which was very strange. I went to see the implementation of the github-driver and found that I could add my own cache. By maintaining an object mapping table, I can read from the cache if it is valid, so I don't have to request remote data again within a certain period of time.

There were countless triggers, which felt like an attack, and I was a bit amused and crying.

Using unjs's unbuild template#

Originally, I wanted to use tsup to create a pipeline, but then I found that unjs provided a template for unbuild projects. It comes with vitest/jiti and other things, which is quite handy to use.

Template link: https://github.com/unjs/template

I also wrote some vitest unit tests.

If I could start over#

If I could start over, I wouldn't hesitate as much as I did. I hesitated for two weeks about whether to write it or not. I wrote it at first, but then I thought it would be better to give up, so I deleted the code. Although there wasn't much in it, once deleted, it was gone, and I could only rewrite it, so I still respect my own code. I really don't want to keep it, I can put the code in my blog.

Outlook#

So far, we have implemented the unStorage xLogDriver. Regardless of the rendering environment I use in the future, I can use this method.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.