Middleware Library for Creating Apps For Gemini Protocol on top Of Deno Runtime

Middleware Library for Creating Apps For Gemini Protocol on top Of Deno Runtime

Middleware library for creating applications for Gemini protocol on top of Deno runtime, written in TypeScript. Heavily inspired by oak and denoscuri.

Kaksik

Middleware library for creating applications for Gemini protocol on top of Deno runtime, written in TypeScript.

Heavily inspired by oak and denoscuri.

Feature roadmap

  • Serve gemtext (out of the box, see Gemtext usage)
  • Serve static files at configured URLs (via middleware, see serveStatic)
  • Serve programmable resources at configured URLs (via middleware, see handleRoutes)
  • Serve redirect responses at configured URLs (via middleware, see handleRedirects)
  • Document Gemtext usage
  • Serve gone responses at configured URLs (via middleware)
  • Improve Response class
  • -- 'Good enough' point --
  • Propose yours by filing an issue

Usage

Prerequisites

  1. Install Deno executable

  2. Obtain SSL certificates. You can generate self-signed ones using openssl command:

    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

Your first app

Create minimal application in app.ts:

import { Application } from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(ctx => {
  ctx.response.body = '# Hello World!'
})

await app.start()

Then run it:

deno run --allow-net --allow-read app.ts

Gemtext usage

Gemtext class represents a text/gemini media type that is native to Gemini protocol (see chapter 5 of spec). It's a line-based text format, so essentially Gemtext is just an Array<Line> with helpers. All six line types are implemented:

  • LineText
  • LineLink
  • LinePreformattedToggle
  • LineHeading
  • LineQuote
  • LineListItem

Response.body setter accepts Gemtext for convenience.

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(ctx => {
  ctx.response.body = new Gemtext(
    new LineHeading('Gemtext demo', 1),
    new LineText(),
    new LineLink('gemini://s.tymo.name', 'stymo'),
    new LineText(),
    new LineText('There will be wrapped text. Elit eius magnam quae dolor ipsa eveniet aut? Facilis natus eum reiciendis reprehenderit odio. Sed et consectetur fuga quod illum ex minus. Iste quia dolor minus saepe in! Recusandae eligendi iusto blanditiis nostrum ipsum! Consequuntur tempora eaque dolore reiciendis sit. At exercitationem repudiandae doloremque quasi non. Nesciunt veritatis aliquid magnam unde pariatur'),
    new LineText(),
    new LineQuote('To be or not to be?'),
    new LinePreformattingToggle(),
    new LineText('There will be unwrapped text. Put some ASCII-art!'),
    new LinePreformattingToggle(),
  )
})

await app.start()

Appending new lines and other Gemtext instances:

const content = new Gemtext(
  new LineHeading('Second page', 1),
  new LineText(),
)

// do some calculation
const prevPageId = 1
const nextPageId = 3

// append more lines
content.append(
  new LineHeading('Navigation'),
  new LineText(),
)

// create anoter Gemtext instance
const nav = new Gemtext(
  new LineLink(`/pages/${prevPageId}`, 'Previous page'),
  new LineLink(`/pages/${nextPageId}`, 'Next page'),
  // Gemtext constructor accepts other Gemtext instances
  new Gemtext(
    new LineText('~~~~~~~~~'),
    new LineText('2020 A.D.'),
  ),
)

// appending mixed lines and Gemtext instances works too
content.append(
  new LineText('----'),
  nav,
  new LineText('----'),
)

Other examples

See examples folder.

Available middleware

serveStatic

Serves static files from a directory to specified URL

import { Application, serveStatic } from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(serveStatic('./log/', '/gemlog/'))
app.use(serveStatic('./public/'))

await app.start()

Beware of ordering of serveStatic middleware usages: more generic URLs should occur later that more specific, e.g., /path/subpath/ must be before /path/.

handleRoutes

Runs specified async function when request path matches configured route.

import {
  Application,
  handleRoutes,
  Route,
} from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(handleRoutes(
  new Route('/test', async (ctx) => {
    ctx.response.body = '# Test page'
  }),
  new Route<{id?: string}>('/param/:id', async (ctx) => {
    ctx.response.body = '# Parametrized page\r\n' +
      'id = ' + ctx.pathParams.id
  }),
  new Route('/', async (ctx) => {
    ctx.response.body = '# HOME page\r\n' +
      '=> /test Test page served by other route\r\n' +
      '=> /param/7 Parametrized page, where id=7\r\n' +
      '=> /404 No routes matched'
  }),
))

app.use(async (ctx) => {
  ctx.response.body = '# No routes matched\r\n' +
    'Running fallback middleware'
})

await app.start()

handleRedirects

Sends either temporary or permanent redirect response when path matches configuration.

import {
  Application,
  handleRedirects,
  handleRoutes,
  Redirect,
  Route,
} from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(handleRedirects(
  new Redirect('/short', '/long-very-long-url', true),
  new Redirect('/home', 'https://tymo.name'),
))

app.use(handleRoutes(
  new Route('/long-very-long-url', async (ctx) => {
    ctx.response.body = '# Redirect target page'
  }),
))

await app.start()

Trivia

"Kaksik" means "twin" in Estonian.

Download Details:

Author: sergetymo

Source Code: https://github.com/sergetymo/kaksik

deno nodejs node javascript

What is Geek Coin

What is GeekCash, Geek Token

Best Visual Studio Code Themes of 2021

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

AEM Querybuilder for JavaScript (Browser, Node, Deno)

Helix Querybuilder .AEM Querybuilder for JavaScript (Browser, Node, Deno)

NKeys for JavaScript - Node.js, Browsers, and Deno

A public-key signature system based on Ed25519 for the NATS ecosystem system for JavaScript. The nkeys.js library works in Deno, Node.js, and the browser!

Javascript module for easily generating mazes,compatible with both Deno and Node

A work-in-progress Javascript maze generator module, compatible with both Deno and Node.

Deno Tutorial for Beginners: Deno vs. Node: Better Than NodeJS?

Deno is released, it’s time to find out if it can prove a worthy competition for Node.js. What is Deno? Why Deno can be considered an alternative to Node? The main differences between Deno and Node. Write a simple Deno app.

What is Deno? The Node.JS Killer? The Deno-minator to Node?

What is Deno? The Node.JS Killer? The Deno-minator to Node? Deno is the new hype and will it be better than node.js? Deno is a secure runtime for JavaScript and TypeScript that is based on the V8 JavaScript engine and the Rust programming language that addresses Node’s shortcomings