Skip to content

Commit 33480d4

Browse files
committed
Add blog loader
1 parent 66e04c3 commit 33480d4

24 files changed

+309
-0
lines changed

.editorconfig

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Generated by editorconfig.timseverien.com
2+
# https://editor.timseverien.com/#config
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
indent_style = space
9+
insert_final_newline = true
10+
tab_width = 2
11+
trim_trailing_whitespace = true

.gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto
3+
*.lockb binary diff=lockb

.github/workflows/any.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Build & Test
2+
on:
3+
push:
4+
branches-ignore:
5+
- main
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v3
11+
- uses: oven-sh/setup-bun@v2
12+
- run: bun install
13+
- run: bun test
14+
- name: Upload coverage to Codecov
15+
uses: codecov/codecov-action@v4
16+
with:
17+
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/main.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Publish
2+
on:
3+
push:
4+
branches:
5+
- main
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v3
11+
- uses: oven-sh/setup-bun@v2
12+
with:
13+
registry-url: "https://registry.npmjs.org"
14+
scope: "@onhive.io"
15+
- run: bun install
16+
- run: bun test
17+
- name: Upload coverage to Codecov
18+
uses: codecov/codecov-action@v4
19+
with:
20+
token: ${{ secrets.CODECOV_TOKEN }}
21+
- run: bun run build
22+
- run: bun publish
23+
env:
24+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Editors
2+
.idea
3+
4+
# Node
5+
node_modules
6+
7+
# Dist
8+
dist
9+
coverage

.husky/pre-commit

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bunx lint-staged

.prettierignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore artifacts:
2+
dist
3+
coverage

.prettierrc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"printWidth": 80,
3+
"singleQuote": false,
4+
"trailingComma": "none",
5+
"tabWidth": 2
6+
}

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# hive-astro-loader
2+
23
Astro Content Layer loader for the Hive blockchain

bun.lockb

239 KB
Binary file not shown.

bunfig.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
telemetry = false
2+
3+
[test]
4+
coverage = true
5+
overageThreshold = 0.9
6+
coverageReporter = ["text", "lcov"]

eslint.config.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import globals from "globals";
2+
import pluginJs from "@eslint/js";
3+
import eslintConfigPrettier from "eslint-config-prettier";
4+
import tseslint from "typescript-eslint";
5+
6+
export default [
7+
{ files: ["**/*.{js,mjs,cjs,ts}"], ignores: ["./dist/"] },
8+
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
9+
...tseslint.configs.recommended,
10+
pluginJs.configs.recommended,
11+
eslintConfigPrettier
12+
];

package.json

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@onhive.io/astro-loader",
3+
"version": "0.0.0",
4+
"author": "mietek.dev <hi@mietek.dev>",
5+
"repository": {
6+
"type": "git",
7+
"url": "git+ssh://git@github.com/instytutfi/hive-astro-loader.git"
8+
},
9+
"module": "./dist/index.mjs",
10+
"types": "./dist/index.d.mts",
11+
"files": [
12+
"dist"
13+
],
14+
"scripts": {
15+
"build": "bunx tsup src/index.ts",
16+
"dev": "bunx tsup src/index.ts --watch",
17+
"lint": "bunx eslint src --fix",
18+
"pretty": "bunx prettier src --write",
19+
"prepare": "bunx husky"
20+
},
21+
"dependencies": {
22+
"@hiveio/dhive": "^1.3.1-beta"
23+
},
24+
"devDependencies": {
25+
"@eslint/js": "^9.13.0",
26+
"@types/bun": "latest",
27+
"eslint": "^9.13.0",
28+
"eslint-config-prettier": "^9.1.0",
29+
"globals": "^15.11.0",
30+
"husky": "^9.1.6",
31+
"lint-staged": "^15.2.10",
32+
"prettier": "^3.3.3",
33+
"tsup": "^8.3.0",
34+
"typescript": "^5.6.3",
35+
"typescript-eslint": "^8.11.0"
36+
},
37+
"peerDependencies": {
38+
"astro": "^4.16.7"
39+
},
40+
"lint-staged": {
41+
"**/*": [
42+
"bunx eslint --fix",
43+
"bunx prettier --write --ignore-unknown"
44+
]
45+
},
46+
"bugs": {
47+
"url": "https://github.com/instytutfi/hive-astro-loader/issues"
48+
},
49+
"description": "Astro Content Layer loader for the Hive blockchain",
50+
"homepage": "https:/onhive.io/tools/hive-astro-loader/",
51+
"keywords": [
52+
"astro",
53+
"astro-loader",
54+
"content-layer",
55+
"hive"
56+
],
57+
"license": "MIT"
58+
}

src/adapters/posts.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { Discussion } from "@hiveio/dhive";
2+
import type { Post } from "~/schema/posts.ts";
3+
4+
/**
5+
* Adapts dHive Discussion to our Post schema.
6+
* Unfortunately, data returned by dHive is different to Discussion type...
7+
*/
8+
export function adaptPost(post: Discussion): Post {
9+
return {
10+
id: post.post_id.toString(),
11+
author: post.author,
12+
title: post.title,
13+
description: post.json_metadata.description,
14+
created: new Date(post.created),
15+
updated: post.updated ? new Date(post.updated) : undefined,
16+
community: post?.community
17+
? { id: post.community, name: post.community_title }
18+
: undefined,
19+
category: post.category,
20+
tags: post.json_metadata.tags,
21+
image: post.json_metadata.image?.pop(),
22+
canonical: post.url,
23+
content: post.body,
24+
meta: {
25+
app: post.json_metadata.app,
26+
format: post.json_metadata.format
27+
}
28+
};
29+
}

src/api/__tests__/posts.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { describe, test, expect } from "bun:test";
2+
import { getAccountPosts } from "~/api/posts.ts";
3+
4+
describe("getAccountPosts", () => {
5+
test("returns discussions", async () => {
6+
const posts = await getAccountPosts("hive.coding");
7+
expect(posts).toBeArray();
8+
expect(posts.length).toBeGreaterThan(0);
9+
const firstPost = posts.pop();
10+
expect(firstPost?.author).toBe("hive.coding");
11+
expect(firstPost?.created).toBeInstanceOf(Date);
12+
});
13+
});

src/api/common.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Client } from "@hiveio/dhive";
2+
3+
const HIVE_API = "https://api.hive.blog";
4+
const HIVEKINGS_API = "https://api.hivekings.com";
5+
const ANYX_API = "https://anyx.io";
6+
const OPENHIVE_API = "https://api.openhive.network";
7+
8+
const APIS = [HIVE_API, HIVEKINGS_API, ANYX_API, OPENHIVE_API];
9+
10+
export const hive = new Client(APIS);

src/api/posts.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { hive } from "~/api/common";
2+
import { adaptPost } from "~/adapters/posts.ts";
3+
import type { Post } from "~/schema/posts.ts";
4+
5+
export async function getAccountPosts(author: string): Promise<Post[]> {
6+
return hive.hivemind
7+
.getAccountPosts({ account: author, sort: "posts" })
8+
.then((discussions) => {
9+
return discussions.map(adaptPost);
10+
});
11+
}

src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { hiveBlogLoader } from "~/loaders/blogLoader";
2+
3+
export type { Post } from "~/schema/posts.ts";
4+
export type { Community } from "~/schema/community.ts";

src/loaders/blogLoader.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Loader } from "astro/loaders";
2+
import { getAccountPosts } from "~/api/posts.ts";
3+
import { typeToZ } from "~/schema/utils.ts";
4+
import type { Post } from "~/schema/posts.ts";
5+
6+
export function hiveBlogLoader(author: string): Loader {
7+
return {
8+
name: "hive-blog-loader",
9+
load: async function (this: Loader, { store, logger }) {
10+
logger.debug(`Fetching blog posts [author: ${author}]`);
11+
const data = await getAccountPosts(author);
12+
store.clear();
13+
data.forEach((post) => {
14+
store.set({
15+
id: post.id.toString(),
16+
data: { ...post }
17+
});
18+
});
19+
},
20+
schema: typeToZ<Post[]>
21+
};
22+
}

src/schema/community.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type Community = {
2+
id: string;
3+
name: string;
4+
};

src/schema/posts.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Community } from "~/schema/community.ts";
2+
3+
export type Post = {
4+
id: string;
5+
author: string;
6+
title: string;
7+
description: string;
8+
created: Date;
9+
updated?: Date;
10+
community?: Community;
11+
category: string;
12+
tags: string[];
13+
image?: string;
14+
canonical: string;
15+
content: string;
16+
meta: {
17+
app: string;
18+
format: string;
19+
};
20+
};

src/schema/utils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { z } from "astro/zod";
2+
3+
export function typeToZ<T>() {
4+
return z.custom<T>(() => true) as z.ZodType<T>;
5+
}

tsconfig.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"extends": "astro/tsconfigs/strict",
3+
"compilerOptions": {
4+
"baseUrl": "./src",
5+
"paths": {
6+
"~/*": ["./*"]
7+
},
8+
/* Base Options: */
9+
"esModuleInterop": true,
10+
"skipLibCheck": true,
11+
"target": "es2022",
12+
"allowJs": true,
13+
"resolveJsonModule": true,
14+
"moduleDetection": "force",
15+
"isolatedModules": true,
16+
"verbatimModuleSyntax": true,
17+
/* Strictness */
18+
"strict": true,
19+
"noUncheckedIndexedAccess": true,
20+
"noImplicitOverride": true,
21+
/* AND if you're building for a library: */
22+
"declaration": true,
23+
"declarationMap": true,
24+
/* If NOT transpiling with TypeScript: */
25+
"module": "preserve",
26+
"noEmit": true,
27+
/* If your code doesn't run in the DOM: */
28+
"lib": ["es2022"]
29+
}
30+
}

tsup.config.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from "tsup";
2+
3+
export default defineConfig({
4+
entry: ["src/index.ts"],
5+
format: ["esm"],
6+
dts: true,
7+
splitting: false,
8+
sourcemap: true,
9+
clean: true
10+
});

0 commit comments

Comments
 (0)