Skip to content

Commit 1956dce

Browse files
committed
feat: create @qifi/generate package
1 parent 1f4f1c9 commit 1956dce

File tree

9 files changed

+226
-0
lines changed

9 files changed

+226
-0
lines changed

cspell.config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ words:
88
- luby
99
- Nuxt
1010
- pako
11+
- qifi
1112
- Soliton
1213
- timeseries
1314
- unocss

packages/generate/README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# @qifi/generate
2+
3+
Stream Generated QR Codes for data transmission
4+
5+
# Usage
6+
7+
```javascript
8+
import {
9+
createGeneraterANSI,
10+
createGeneraterUnicode,
11+
createGeneraterUnicodeCompact,
12+
createGeneraterSVG,
13+
createGeneraterQRCodeArray,
14+
} from '@qifi/generate'
15+
16+
const generaterSvg = createGeneraterSVG(new Uint8Array(file.buffer))
17+
18+
const generaterANSI = createGeneraterANSI(new Uint8Array(file.buffer), {
19+
// Size of each data slice
20+
indicesSize: 250,
21+
// Error correction level
22+
ecc: 'L',
23+
// Border width
24+
border: 2,
25+
})
26+
27+
// display QR Code in terminal
28+
for (const blockQRCode of generaterANSI) {
29+
console.log(blockQRCode)
30+
}
31+
32+
```

packages/generate/build.config.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { defineBuildConfig } from 'unbuild'
2+
3+
export default defineBuildConfig({
4+
entries: [
5+
'src/index.ts',
6+
],
7+
declaration: true,
8+
rollup: {
9+
emitCJS: false,
10+
dts: {
11+
compilerOptions: {
12+
paths: {},
13+
},
14+
},
15+
},
16+
externals: [
17+
'pako',
18+
],
19+
})

packages/generate/package.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "@qifi/generate",
3+
"type": "module",
4+
"version": "0.0.2",
5+
"description": "Stream Generated QR Codes for data transmission",
6+
"author": "Rizumu Ayaka <[email protected]>",
7+
"license": "MIT",
8+
"homepage": "https://github.com/qifi-dev/qrs#readme",
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/qifi-dev/qrs.git",
12+
"directory": "packages/generate"
13+
},
14+
"bug": "https://github.com/qifi-dev/qrs/issues",
15+
"sideEffects": false,
16+
"exports": {
17+
".": {
18+
"types": "./dist/index.d.mts",
19+
"default": "./dist/index.mjs"
20+
}
21+
},
22+
"main": "./dist/index.mjs",
23+
"module": "./dist/index.mjs",
24+
"types": "./dist/index.d.mts",
25+
"files": [
26+
"dist"
27+
],
28+
"scripts": {
29+
"build": "unbuild",
30+
"stub": "unbuild --stub"
31+
},
32+
"dependencies": {
33+
"js-base64": "^3.7.7",
34+
"luby-transform": "workspace:*",
35+
"uqr": "^0.1.2"
36+
}
37+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { createEncoder } from 'luby-transform'
2+
import { type CreateBlockRenderFn, type GeneraterBaseOptions, GeneraterDefaultOptions } from './shared'
3+
4+
export function createGeneraterWithRender<Result, RenderOptions>(createRender: CreateBlockRenderFn<Result, RenderOptions>) {
5+
return (data: Uint8Array, options?: GeneraterBaseOptions<RenderOptions>) => {
6+
const _options = Object.assign({}, GeneraterDefaultOptions, options)
7+
8+
const encoder = createEncoder(data, _options.indicesSize, _options.compress)
9+
10+
let _fountain: ReturnType<typeof encoder.fountain>
11+
12+
const render = createRender(_options)
13+
14+
return {
15+
encoder,
16+
17+
/**
18+
* Generate random encoded blocks that **never** ends
19+
*/
20+
*fountain(): Generator<Result> {
21+
while (true) {
22+
_fountain ||= encoder.fountain()
23+
const block = _fountain.next().value
24+
yield render(block)
25+
}
26+
},
27+
28+
/**
29+
* Manually creates an encoded block from the original data.
30+
*/
31+
createBlock(indices: number[]): Result {
32+
return render(encoder.createBlock(indices))
33+
},
34+
}
35+
}
36+
}

packages/generate/src/generater.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { EncodedBlock } from 'luby-transform'
2+
import { type QrCodeGenerateOptions, type QrCodeGenerateSvgOptions, type QrCodeGenerateUnicodeOptions, encode as qrEncode, renderANSI, renderSVG, renderUnicode } from 'uqr'
3+
import { createGeneraterWithRender } from './base-generater'
4+
import { addUrlPrefix, blockToBase64, type GeneraterBaseOptions } from './shared'
5+
6+
/**
7+
* Render QR Code to ANSI colored string.
8+
*/
9+
export const createGeneraterANSI = /* @__PURE__ */ createGeneraterWithRender(withRenderANSI)
10+
function withRenderANSI(options: GeneraterBaseOptions<QrCodeGenerateOptions>) {
11+
return (data: EncodedBlock) => {
12+
let base64str = blockToBase64(data)
13+
base64str = addUrlPrefix(options.urlPrefix, base64str)
14+
return renderANSI(base64str, options)
15+
}
16+
}
17+
18+
/**
19+
* Render QR Code to Unicode string for each pixel. By default it uses █ and ░ to represent black and white pixels, and it can be customizable.
20+
*/
21+
export const createGeneraterUnicode = /* @__PURE__ */ createGeneraterWithRender(withRenderUnicode)
22+
function withRenderUnicode(options: GeneraterBaseOptions<QrCodeGenerateUnicodeOptions>) {
23+
return (data: EncodedBlock) => {
24+
let base64str = blockToBase64(data)
25+
base64str = addUrlPrefix(options.urlPrefix, base64str)
26+
return renderUnicode(base64str, options)
27+
}
28+
}
29+
30+
/**
31+
* Render QR Code with two rows into one line with unicode ▀, ▄, █, . It is useful when you want to display QR Code in terminal with limited height.
32+
*/
33+
export const createGeneraterUnicodeCompact = /* @__PURE__ */ createGeneraterWithRender(withRenderUnicodeCompact)
34+
function withRenderUnicodeCompact(options: GeneraterBaseOptions<QrCodeGenerateUnicodeOptions>) {
35+
return (data: EncodedBlock) => {
36+
let base64str = blockToBase64(data)
37+
base64str = addUrlPrefix(options.urlPrefix, base64str)
38+
return renderUnicode(base64str, options)
39+
}
40+
}
41+
42+
/**
43+
* Render QR Code to SVG string.
44+
*/
45+
export const createGeneraterSVG = /* @__PURE__ */ createGeneraterWithRender(withRenderSVG)
46+
function withRenderSVG(options: GeneraterBaseOptions<QrCodeGenerateSvgOptions>) {
47+
return (data: EncodedBlock) => {
48+
let base64str = blockToBase64(data)
49+
base64str = addUrlPrefix(options.urlPrefix, base64str)
50+
return renderSVG(base64str, options)
51+
}
52+
}
53+
54+
/**
55+
* Encode data into QR Code represented by a 2D array.
56+
*/
57+
export const createGeneraterQRCodeArray = /* @__PURE__ */ createGeneraterWithRender(withRenderQRCodeArray)
58+
function withRenderQRCodeArray(options: GeneraterBaseOptions<QrCodeGenerateOptions>) {
59+
return (data: EncodedBlock) => {
60+
let base64str = blockToBase64(data)
61+
base64str = addUrlPrefix(options.urlPrefix, base64str)
62+
return qrEncode(base64str, options)
63+
}
64+
}

packages/generate/src/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export * from './base-generater'
2+
export * from './generater'
3+
4+
export {
5+
type CreateBlockRenderFn,
6+
type GeneraterBaseOptions,
7+
8+
GeneraterDefaultOptions,
9+
} from './shared'

packages/generate/src/shared.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { fromUint8Array } from 'js-base64'
2+
import { blockToBinary, type EncodedBlock } from 'luby-transform'
3+
4+
export type CreateBlockRenderFn<Result, RenderOptions> = (renderOptions: GeneraterBaseOptions<RenderOptions>) => (block: EncodedBlock) => Result
5+
6+
export type GeneraterBaseOptions<RenderOptions> = {
7+
indicesSize?: number
8+
compress?: boolean
9+
urlPrefix?: string
10+
} & RenderOptions
11+
12+
export const GeneraterDefaultOptions = {
13+
indicesSize: 500,
14+
urlPrefix: '',
15+
}
16+
17+
export function blockToBase64(block: EncodedBlock) {
18+
return fromUint8Array(blockToBinary(block))
19+
}
20+
21+
export function addUrlPrefix(urlPrefix: string | undefined, base64str: string) {
22+
return urlPrefix ? urlPrefix + base64str : base64str
23+
}
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect, it } from 'vitest'
2+
3+
it.todo('should generate test', () => {
4+
expect(1).toBe(1)
5+
})

0 commit comments

Comments
 (0)