-
Notifications
You must be signed in to change notification settings - Fork 31
feat(spore): dob-render-sdk migration #320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 12 commits
def2273
600a36e
a077bdd
694ae83
e2fba3d
2204c4f
4bb1040
19962f3
4d0934d
04a9592
cc3f59a
7c4e4f0
5260933
f3a303b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@ckb-ccc/spore": minor | ||
--- | ||
|
||
Migrate dob-render-sdk directly into spore module | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,42 @@ | ||||||
# @ckb-ccc/render | ||||||
|
||||||
|
||||||
CCC - CKBer's Codebase. Common Chains Connector's render SDK for DOB protocol. | ||||||
|
||||||
This package provides rendering capabilities for DOB (Decentralized Object) protocol, allowing you to render DOB tokens as SVG images. | ||||||
|
||||||
## Installation | ||||||
|
||||||
```bash | ||||||
npm install @ckb-ccc/dob-render | ||||||
``` | ||||||
|
||||||
## Usage | ||||||
|
||||||
```typescript | ||||||
import { | ||||||
renderByTokenKey, | ||||||
renderByDobDecodeResponse, | ||||||
} from "@ckb-ccc/dob-render"; | ||||||
|
||||||
// Render by token key | ||||||
const svg = await renderByTokenKey("your-token-key"); | ||||||
|
||||||
// Render by DOB decode response | ||||||
const svg = renderByDobDecodeResponse(renderOutput); | ||||||
``` | ||||||
|
||||||
## API | ||||||
|
||||||
### `renderByTokenKey(tokenKey: string, options?: RenderOptions)` | ||||||
|
||||||
Renders a DOB token by its key. | ||||||
|
||||||
### `renderByDobDecodeResponse(renderOutput: RenderOutput | string, props?: RenderProps)` | ||||||
|
### `renderByDobDecodeResponse(renderOutput: RenderOutput | string, props?: RenderProps)` | |
### `renderByDobDecodeResponse(renderOutput: RenderOutput, props?: RenderProps)` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// @ts-check | ||
|
||
import eslint from "@eslint/js"; | ||
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; | ||
import tseslint from "typescript-eslint"; | ||
|
||
import { dirname } from "path"; | ||
import { fileURLToPath } from "url"; | ||
|
||
export default [ | ||
...tseslint.config({ | ||
files: ["**/*.ts"], | ||
extends: [ | ||
eslint.configs.recommended, | ||
...tseslint.configs.recommendedTypeChecked, | ||
], | ||
rules: { | ||
"@typescript-eslint/no-unused-vars": [ | ||
"error", | ||
{ | ||
args: "all", | ||
argsIgnorePattern: "^_", | ||
caughtErrors: "all", | ||
caughtErrorsIgnorePattern: "^_", | ||
destructuredArrayIgnorePattern: "^_", | ||
varsIgnorePattern: "^_", | ||
ignoreRestSiblings: true, | ||
}, | ||
], | ||
"@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }], | ||
"@typescript-eslint/no-unsafe-member-access": "off", | ||
"@typescript-eslint/require-await": "off", | ||
"@typescript-eslint/only-throw-error": [ | ||
"error", | ||
{ | ||
allowThrowingAny: true, | ||
allowThrowingUnknown: true, | ||
allowRethrowing: true, | ||
}, | ||
], | ||
"@typescript-eslint/prefer-promise-reject-errors": [ | ||
"error", | ||
{ | ||
allowThrowingAny: true, | ||
allowThrowingUnknown: true, | ||
}, | ||
], | ||
"no-empty": "off", | ||
"prefer-const": [ | ||
"error", | ||
{ ignoreReadBeforeAssign: true, destructuring: "all" }, | ||
], | ||
}, | ||
languageOptions: { | ||
parserOptions: { | ||
project: true, | ||
tsconfigRootDir: dirname(fileURLToPath(import.meta.url)), | ||
}, | ||
}, | ||
}), | ||
eslintPluginPrettierRecommended, | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
{ | ||
"name": "@ckb-ccc/dob-render", | ||
"version": "1.0.1", | ||
"description": "CCC - CKBer's Codebase. Common Chains Connector's render SDK for DOB protocol", | ||
"author": "ashuralyk <ashuralyk@live.com>", | ||
"license": "MIT", | ||
"private": false, | ||
"homepage": "https://github.com/ckb-devrel/ccc", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/ckb-devrel/ccc.git" | ||
}, | ||
"sideEffects": false, | ||
"main": "dist.commonjs/index.js", | ||
"module": "dist/index.js", | ||
"exports": { | ||
".": { | ||
"import": "./dist/index.js", | ||
"require": "./dist.commonjs/index.js", | ||
"default": "./dist.commonjs/index.js" | ||
}, | ||
"./barrel": { | ||
"import": "./dist/barrel.js", | ||
"require": "./dist.commonjs/barrel.js", | ||
"default": "./dist.commonjs/barrel.js" | ||
} | ||
}, | ||
"scripts": { | ||
"build": "rimraf ./dist && rimraf ./dist.commonjs && tsc && tsc --project tsconfig.commonjs.json", | ||
"lint": "eslint ./src", | ||
"format": "prettier --write . && eslint --fix ./src" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.34.0", | ||
"@types/node": "^24.3.0", | ||
"eslint": "^9.34.0", | ||
"eslint-config-prettier": "^10.1.8", | ||
"eslint-plugin-prettier": "^5.5.4", | ||
"prettier": "^3.6.2", | ||
"prettier-plugin-organize-imports": "^4.2.0", | ||
"rimraf": "^6.0.1", | ||
"typescript": "^5.9.2", | ||
"typescript-eslint": "^8.41.0" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@ckb-ccc/spore": "workspace:*", | ||
"axios": "^1.11.0", | ||
"satori": "^0.10.13", | ||
"svgson": "^5.3.1" | ||
}, | ||
"peerDependencies": { | ||
"satori": "^0.10.13" | ||
}, | ||
|
||
"packageManager": "pnpm@10.8.1" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* @see https://prettier.io/docs/configuration | ||
* @type {import("prettier").Config} | ||
*/ | ||
const config = { | ||
singleQuote: false, | ||
trailingComma: "all", | ||
plugins: [require.resolve("prettier-plugin-organize-imports")], | ||
}; | ||
|
||
module.exports = config; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./renderDobDecode.js"; | ||
export * from "./renderToken.js"; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,35 @@ | ||||||
import { Key } from "../config/constants.js"; | ||||||
import { renderTextParamsParser } from "../core/parsers/textParamsParser.js"; | ||||||
import { traitsParser } from "../core/parsers/traitsParser.js"; | ||||||
import { renderDob1Svg } from "../core/renderers/dob1Render.js"; | ||||||
import { renderImageSvg } from "../core/renderers/imageRender.js"; | ||||||
import type { RenderProps } from "../core/renderers/textRender.js"; | ||||||
import { renderTextSvg } from "../core/renderers/textRender.js"; | ||||||
import type { RenderOutput } from "../types/external.js"; | ||||||
|
||||||
export function renderByDobDecodeResponse( | ||||||
renderOutput: RenderOutput | string, | ||||||
props?: Pick<RenderProps, "font"> & { | ||||||
outputType?: "svg"; | ||||||
}, | ||||||
|
||||||
) { | ||||||
let renderData: RenderOutput; | ||||||
if (typeof renderOutput === "string") { | ||||||
renderData = JSON.parse(renderOutput) as RenderOutput; | ||||||
|
||||||
} else { | ||||||
renderData = renderOutput; | ||||||
} | ||||||
|
||||||
const { traits, indexVarRegister } = traitsParser(renderData); | ||||||
for (const trait of traits) { | ||||||
if (trait.name === String(Key.Type) && trait.value === "image") { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
return renderImageSvg(traits); | ||||||
} | ||||||
// TODO: multiple images | ||||||
if (trait.name === String(Key.Image) && trait.value instanceof Promise) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
return renderDob1Svg(trait.value); | ||||||
} | ||||||
} | ||||||
const renderOptions = renderTextParamsParser(traits, indexVarRegister); | ||||||
return renderTextSvg({ ...renderOptions, font: props?.font }); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { dob } from "@ckb-ccc/spore"; | ||
import { config } from "../config.js"; | ||
import type { RenderProps } from "../core/renderers/textRender.js"; | ||
import { renderByDobDecodeResponse } from "./renderDobDecode.js"; | ||
|
||
export async function renderByTokenKey( | ||
tokenKey: string, | ||
options?: Pick<RenderProps, "font"> & { | ||
outputType?: "svg"; | ||
}, | ||
) { | ||
const renderOutput = await dob.decodeDobBySporeId( | ||
tokenKey, | ||
config.dobDecodeServerURL, | ||
); | ||
|
||
return renderByDobDecodeResponse(renderOutput, options); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from "./api/index.js"; | ||
export * from "./config/index.js"; | ||
export * from "./core/index.js"; | ||
export * from "./types/index.js"; | ||
export * from "./utils/index.js"; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,101 @@ | ||||||||||||||||||||||||||||||||||||||||
export type FileServerResult = | ||||||||||||||||||||||||||||||||||||||||
| string | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
content: string; | ||||||||||||||||||||||||||||||||||||||||
content_type: string; | ||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
export type BtcFsResult = FileServerResult; | ||||||||||||||||||||||||||||||||||||||||
export type IpfsResult = FileServerResult; | ||||||||||||||||||||||||||||||||||||||||
export type CkbFsResult = FileServerResult; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
export type BtcFsURI = `btcfs://${string}`; | ||||||||||||||||||||||||||||||||||||||||
export type IpfsURI = `ipfs://${string}`; | ||||||||||||||||||||||||||||||||||||||||
export type CkbFsURI = `ckbfs://${string}`; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
export type QueryBtcFsFn = (uri: BtcFsURI) => Promise<BtcFsResult>; | ||||||||||||||||||||||||||||||||||||||||
export type QueryIpfsFn = (uri: IpfsURI) => Promise<IpfsResult>; | ||||||||||||||||||||||||||||||||||||||||
export type QueryCkbFsFn = (uri: CkbFsURI) => Promise<CkbFsResult>; | ||||||||||||||||||||||||||||||||||||||||
export type QueryUrlFn = (uri: string) => Promise<FileServerResult>; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
export class Config { | ||||||||||||||||||||||||||||||||||||||||
private _dobDecodeServerURL = "https://dob-decoder.ckbccc.com"; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
private _queryBtcFsFn: QueryBtcFsFn = async (uri) => { | ||||||||||||||||||||||||||||||||||||||||
console.log("dob-render-sdk requiring", uri); | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
const response = await fetch( | ||||||||||||||||||||||||||||||||||||||||
`https://dob-decoder.ckbccc.com/restful/dob_extract_image?uri=${uri}&encode=base64`, | ||||||||||||||||||||||||||||||||||||||||
|
`https://dob-decoder.ckbccc.com/restful/dob_extract_image?uri=${uri}&encode=base64`, | |
`${this._dobDecodeServerURL}/restful/dob_extract_image?uri=${uri}&encode=base64`, |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function has a couple of issues:
- The
content_type
is hardcoded to an empty string. It should be dynamically determined from theContent-Type
header of the HTTP response. - There's no error handling for the
fetch
call. If the request fails (e.g., network error, 404 status), it will result in an unhandled promise rejection.
const response = await fetch( | |
`https://dob-decoder.ckbccc.com/restful/dob_extract_image?uri=${uri}&encode=base64`, | |
); | |
const text = await response.text(); | |
return { | |
content: text, | |
content_type: "", | |
}; | |
const response = await fetch( | |
`https://dob-decoder.ckbccc.com/restful/dob_extract_image?uri=${uri}&encode=base64`, | |
); | |
if (!response.ok) { | |
throw new Error(`Failed to fetch from BtcFs: ${response.statusText}`); | |
} | |
const text = await response.text(); | |
return { | |
content: text, | |
content_type: response.headers.get('content-type') || '', | |
}; |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FileReader
API is only available in browsers and will cause this code to crash in a Node.js environment. To ensure this library is isomorphic (runs in both browser and Node.js), you should provide a Node.js-compatible implementation for fetching and base64-encoding the content.
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined') {
// Browser environment
return new Promise<IpfsResult>((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
const dataUrl = this.result as string;
resolve(dataUrl);
};
reader.onerror = (error) => {
reject(new Error(`FileReader error: ${error.type}`));
};
reader.readAsDataURL(blob);
});
} else {
// Node.js environment
const buffer = await response.arrayBuffer();
const base64 = Buffer.from(buffer).toString('base64');
const mimeType = response.headers.get('content-type') || 'application/octet-stream';
return `data:${mimeType};base64,${base64}`;
}
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of FileReader
makes this function browser-specific and will cause it to fail in a Node.js environment. For a universal library, you should provide an environment-agnostic implementation. In Node.js, you can use Buffer.from(await blob.arrayBuffer()).toString('base64')
to achieve the same result.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of FileReader
is a browser-specific API and will cause this code to fail in a Node.js environment. Since this package seems to be intended for both environments, you should provide a Node.js-compatible implementation for converting a blob (or stream) to a base64 string, for example by using Buffer
.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export enum Key { | ||
Bg = "prev.bg", | ||
Type = "prev.type", | ||
BgColor = "prev.bgcolor", | ||
Prev = "prev", | ||
Image = "IMAGE", | ||
} | ||
|
||
export const ARRAY_REG = /\(%(.*?)\):(\[.*?\])/; | ||
export const ARRAY_INDEX_REG = /(\d+)<_>$/; | ||
export const GLOBAL_TEMPLATE_REG = /^prev<(.*?)>/; | ||
export const TEMPLATE_REG = /^(.*?)<(.*?)>/; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import SpaceGroteskBoldBase64 from "../fonts/spaceGroteskBold.base64.js"; | ||
import TurretRoadBoldBase64 from "../fonts/turretRoadBold.base64.js"; | ||
import TurretRoadMediumBase64 from "../fonts/turretRoadMedium.base64.js"; | ||
|
||
export const FONTS = { | ||
SpaceGroteskBold: SpaceGroteskBoldBase64, | ||
TurretRoadBold: TurretRoadBoldBase64, | ||
TurretRoadMedium: TurretRoadMediumBase64, | ||
} as const; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { config } from "../config.js"; | ||
export * from "./constants.js"; | ||
export * from "./fonts.js"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./parsers/index.js"; | ||
export * from "./renderers/index.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The package name in the README title is
@ckb-ccc/render
, but the actual package name is@ckb-ccc/dob-render
. This should be corrected to avoid confusion.