init branch
This commit is contained in:
parent
5f872e8287
commit
cddf92e432
|
@ -14,10 +14,15 @@
|
|||
devShells = forEachSupportedSystem ({ pkgs }: {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
# Frontend
|
||||
bun
|
||||
sqlite
|
||||
sqls
|
||||
sql-studio
|
||||
# Backend
|
||||
dotnetCorePackages.sdk_9_0
|
||||
dotnetCorePackages.aspnetcore_9_0
|
||||
nuget
|
||||
|
||||
# LSP
|
||||
typescript-language-server
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import { type } from "./common"
|
||||
import _ from "lodash"
|
||||
import { expect, test } from "bun:test"
|
||||
|
||||
const CYCLES = 10000
|
||||
test("Test Integer and Unsigned Integer", () => {
|
||||
for (let i = 0; i < CYCLES; i++) {
|
||||
|
||||
// Unsigned Integer
|
||||
expect(type.UInteger.safeParse(_.random(0, Math.pow(2, 53), false)).success).toBeTrue()
|
||||
expect(type.UInt8.safeParse(_.random(0, Math.pow(2, 8) - 1, false)).success).toBeTrue()
|
||||
expect(type.UInt8.safeParse(_.random(Math.pow(2, 8), Math.pow(2, 53), false)).success).toBeFalse()
|
||||
expect(type.UInt16.safeParse(_.random(0, Math.pow(2, 16) - 1, false)).success).toBeTrue()
|
||||
expect(type.UInt16.safeParse(_.random(Math.pow(2, 16), Math.pow(2, 53), false)).success).toBeFalse()
|
||||
expect(type.UInt32.safeParse(_.random(0, Math.pow(2, 32) - 1, false)).success).toBeTrue()
|
||||
expect(type.UInt32.safeParse(_.random(Math.pow(2, 32), Math.pow(2, 53), false)).success).toBeFalse()
|
||||
|
||||
// Integer
|
||||
expect(type.Integer.safeParse(_.random(-Math.pow(2, 52), Math.pow(2, 52) - 1, false)).success).toBeTrue()
|
||||
expect(type.Int8.safeParse(_.random(-Math.pow(2, 7), Math.pow(2, 7) - 1, false)).success).toBeTrue()
|
||||
expect(type.Int8.safeParse(_.random(Math.pow(2, 7), Math.pow(2, 52), false)).success).toBeFalse()
|
||||
expect(type.Int8.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 7) - 1, false)).success).toBeFalse()
|
||||
expect(type.Int16.safeParse(_.random(-Math.pow(2, 15), Math.pow(2, 15) - 1, false)).success).toBeTrue()
|
||||
expect(type.Int16.safeParse(_.random(Math.pow(2, 15), Math.pow(2, 52), false)).success).toBeFalse()
|
||||
expect(type.Int16.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 15) - 1, false)).success).toBeFalse()
|
||||
expect(type.Int32.safeParse(_.random(-Math.pow(2, 31), Math.pow(2, 31) - 1, false)).success).toBeTrue()
|
||||
expect(type.Int32.safeParse(_.random(Math.pow(2, 31), Math.pow(2, 52), false)).success).toBeFalse()
|
||||
expect(type.Int32.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 31) - 1, false)).success).toBeFalse()
|
||||
}
|
||||
})
|
||||
|
||||
test("Test Number Processor Function", () => {
|
||||
// Convert Number to Uint8Array
|
||||
expect(type.numberToBytes(0xFF, 1).unwrap()[0]).toBe(255)
|
||||
expect(type.numberToBytes(0xAAAA, 2).unwrap()).toEqual(new Uint8Array([0xAA, 0xAA]))
|
||||
expect(type.numberToBytes(0x12345678, 4).unwrap()).toEqual(new Uint8Array([0x78, 0x56, 0x34, 0x12]))
|
||||
expect(type.numberToBytes(0x12345678, 4, true).unwrap()).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78]))
|
||||
|
||||
// Convert Uint8Array to Number
|
||||
expect(type.bytesToNumber(new Uint8Array([0xFF]))).toBe(255)
|
||||
expect(type.bytesToNumber(new Uint8Array([0xAA, 0xAA]))).toEqual(0xAAAA)
|
||||
expect(type.bytesToNumber(new Uint8Array([0x78, 0x56, 0x34, 0x12]))).toEqual(0x12345678)
|
||||
expect(type.bytesToNumber(new Uint8Array([0x12, 0x34, 0x56, 0x78]), true)).toEqual(0x12345678)
|
||||
|
||||
|
||||
// Number Match
|
||||
for (let i = 0; i < CYCLES; i++) {
|
||||
const num1 = _.random(CYCLES / 2, false)
|
||||
const num2 = _.random(CYCLES / 2, false)
|
||||
|
||||
expect(type.numberMatch(num1, num2)).toBe((num1 & num2) === num2 ? true : false)
|
||||
expect(type.numberMatch(num1, num2, "True", "False")).toBe((num1 & num2) === num2 ? "True" : "False")
|
||||
}
|
||||
|
||||
// Number Set, Unset, Toggle and Get Bit
|
||||
expect(type.numberSetBit(0, 5)).toBe(0b10000)
|
||||
expect(type.numberUnsetBit(0b1111, 3)).toBe(0b1011)
|
||||
expect(type.numberToggleBit(0b1010, 3)).toBe(0b1110)
|
||||
expect(type.numberBit(0b1100, 2)).toBe(0)
|
||||
|
||||
// Get High / Low Bits Num
|
||||
expect(type.numberHighBitsNum(0xFF).unwrap()).toBe(8)
|
||||
expect(type.numberHighBitsNum(0xAA).unwrap()).toBe(4)
|
||||
expect(type.numberLowBitsNum(0xFF, 8).unwrap()).toBe(0)
|
||||
expect(type.numberLowBitsNum(0xAA, 8).unwrap()).toBe(4)
|
||||
})
|
191
server/common.ts
191
server/common.ts
|
@ -1,191 +0,0 @@
|
|||
import _ from "lodash"
|
||||
import { Option, Some, None, Result, Ok, Err } from "ts-results-es";
|
||||
import { z } from "zod";
|
||||
|
||||
export function UNUSED(_: unknown): void { }
|
||||
|
||||
export namespace type {
|
||||
|
||||
const NUMBER_MAX_LENGTH = 32
|
||||
|
||||
|
||||
export const ErrorTypeSchema = z.union([
|
||||
z.literal("Not Integer"),
|
||||
z.literal("Not Unsigned Integer"),
|
||||
z.literal("Not 32Bits Integer")
|
||||
])
|
||||
|
||||
export type ErrorType = z.infer<typeof ErrorTypeSchema>
|
||||
|
||||
export const Integer = z.number().int()
|
||||
export const UInteger = z.number().int().nonnegative()
|
||||
|
||||
export const UInt8 = UInteger.lt(Math.pow(2, 8))
|
||||
export const UInt16 = UInteger.lt(Math.pow(2, 16))
|
||||
export const UInt32 = UInteger.lt(Math.pow(2, 32))
|
||||
|
||||
export const Int8 = Integer.lt(Math.pow(2, 7)).gte(-Math.pow(2, 8))
|
||||
export const Int16 = Integer.lt(Math.pow(2, 15)).gte(-Math.pow(2, 16))
|
||||
export const Int32 = Integer.lt(Math.pow(2, 31)).gte(-Math.pow(2, 32))
|
||||
|
||||
export function numberToBytes(num: number, bytesLength: number, isRightHigh: boolean = false)
|
||||
: Result<Uint8Array, ErrorType> {
|
||||
// Check Integer
|
||||
if (!Int32.safeParse(num).success && !UInt32.lte(32).safeParse(bytesLength).success) {
|
||||
console.error(`Number : ${num}, 2 ^ 31 = ${2 ^ 31}`)
|
||||
console.error(Int32.safeParse(num).error?.message)
|
||||
return new Err("Not 32Bits Integer")
|
||||
}
|
||||
|
||||
var array = new Uint8Array(bytesLength)
|
||||
|
||||
if (isRightHigh) {
|
||||
for (let i = 0; i < bytesLength; i++) {
|
||||
array[bytesLength - 1 - i] = ((num >> (i << 3)) & 0xFF)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < bytesLength; i++) {
|
||||
array[i] = ((num >> (i << 3)) & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
return new Ok(array)
|
||||
}
|
||||
|
||||
export function bytesToNumber(bytes: Uint8Array, isRightHigh: boolean = false): number {
|
||||
let num = 0
|
||||
const len = bytes.length
|
||||
if (isRightHigh) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
num += bytes[len - 1 - i] << (i << 3)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < len; i++) {
|
||||
num += bytes[i] << (i << 3)
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
|
||||
export function numberMatch(
|
||||
srcNum: number,
|
||||
destNum: number
|
||||
): boolean;
|
||||
|
||||
export function numberMatch<T>(
|
||||
srcNum: number,
|
||||
destNum: number,
|
||||
True: T,
|
||||
False: T
|
||||
): T;
|
||||
|
||||
export function numberMatch<T>(
|
||||
srcNum: number,
|
||||
destNum: number,
|
||||
True: T = true as T,
|
||||
False: T = false as T
|
||||
): T {
|
||||
const ret = (srcNum & destNum) === destNum;
|
||||
return ret ? True : False;
|
||||
}
|
||||
|
||||
export function numberMatchEnum() {
|
||||
|
||||
}
|
||||
|
||||
export function numberSetBit(num: number, loc: number): number {
|
||||
return num | (1 << (loc - 1))
|
||||
}
|
||||
|
||||
export function numberUnsetBit(num: number, loc: number): number {
|
||||
return num & ~(1 << (loc - 1))
|
||||
}
|
||||
|
||||
export function numberToggleBit(num: number, loc: number): number {
|
||||
return num ^ (1 << (loc - 1))
|
||||
}
|
||||
|
||||
export function numberBit(num: number, loc: number): number {
|
||||
return (num >> (loc - 1)) & 1
|
||||
}
|
||||
|
||||
export function numberHighBitsNum(num: number, maxLen: number = NUMBER_MAX_LENGTH): Result<number, ErrorType> {
|
||||
if (!Int32.safeParse(num).success && maxLen > 32) {
|
||||
return new Err("Not 32Bits Integer")
|
||||
}
|
||||
|
||||
let cnt = 0
|
||||
for (let i = 0; i < maxLen; i++) {
|
||||
if (num & (1 << i)) {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
return new Ok(cnt)
|
||||
}
|
||||
|
||||
export function numberLowBitsNum(num: number, maxLen: number): Result<number, ErrorType> {
|
||||
if (!Int32.safeParse(num).success && maxLen > 32) {
|
||||
return new Err("Not 32Bits Integer")
|
||||
}
|
||||
|
||||
let cnt = 0
|
||||
for (let i = 0; i < maxLen; i++) {
|
||||
if (!(num & (1 << i))) {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
return new Ok(cnt)
|
||||
}
|
||||
|
||||
export function isStringArray(obj: any): obj is Array<string> {
|
||||
return z.string().array().safeParse(obj).success
|
||||
}
|
||||
|
||||
export function isNumberArray(obj: any): obj is Array<number> {
|
||||
return z.number().array().safeParse(obj).success
|
||||
}
|
||||
}
|
||||
|
||||
export namespace fun {
|
||||
|
||||
export function randomFromArray(array: Array<any>) {
|
||||
return array[_.random(0, array.length - 1, false)]
|
||||
}
|
||||
|
||||
export function sqlConditionFromArray(
|
||||
columnName: string,
|
||||
array: Array<string>,
|
||||
type: "AND" | "OR"
|
||||
): Option<string> {
|
||||
let condition: string = ""
|
||||
const len = array.length
|
||||
if (len == 0) {
|
||||
return None
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
condition.concat(`${columnName}=${array[i]}`)
|
||||
if (i != len - 1) {
|
||||
condition.concat(` ${type} `)
|
||||
}
|
||||
}
|
||||
|
||||
return new Some(condition)
|
||||
}
|
||||
|
||||
export function sqlConditionFromString(
|
||||
columnName: string,
|
||||
str: string,
|
||||
type: "AND" | "OR",
|
||||
delimiter: string = " "
|
||||
): Option<string> {
|
||||
if (str.length == 0) {
|
||||
return None
|
||||
}
|
||||
|
||||
const array = str.split(delimiter)
|
||||
return sqlConditionFromArray(columnName, array, type)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
import { test, expect } from "bun:test"
|
||||
import * as db from "./database.ts"
|
||||
import _, { rearg } from "lodash"
|
||||
import { None, Ok, Option, Some } from "ts-results-es"
|
||||
|
||||
// Test basic function for database
|
||||
test("DataBase", () => {
|
||||
const allTables = db.allTables()
|
||||
expect(allTables).toBeArray()
|
||||
expect(allTables).toEqual(["Users", "Boards"])
|
||||
expect(db.BoardTable.countAll()).toBe(0)
|
||||
expect(db.UserTable.countAll()).toBe(0)
|
||||
})
|
||||
|
||||
// Test Boards table function
|
||||
const boardsNumber = 10
|
||||
const rooms = ["A1", "A1", "A1", "A1", "A1", "A2", "A2", "A2", "A2", "A2"]
|
||||
test("Find something empty", () => {
|
||||
const findEmptyByID = db.BoardTable.find(_.random(0, boardsNumber))
|
||||
expect(findEmptyByID).toEqual(Ok(None))
|
||||
const findEmptyByName = db.BoardTable.find("Hello", "World")
|
||||
expect(findEmptyByName).toEqual(Ok(None))
|
||||
})
|
||||
|
||||
test("Add some boards", () => {
|
||||
const boardsArray: Array<db.Board> = []
|
||||
for (let i = 0; i < boardsNumber; i++) {
|
||||
boardsArray.push({
|
||||
id: i,
|
||||
name: `Board ${i}`,
|
||||
room: rooms[i],
|
||||
ipv4: `192.168.172.${i}`,
|
||||
port: i,
|
||||
})
|
||||
}
|
||||
const retAdd = db.BoardTable.addFromArray(boardsArray)
|
||||
const isAddOk = retAdd.isOk()
|
||||
expect(isAddOk).toBeTrue()
|
||||
})
|
||||
|
||||
test("Get boards table column unique element", () => {
|
||||
expect(db.BoardTable.rooms().unwrap()).toEqual(["A1", "A2"])
|
||||
})
|
||||
|
||||
test("Find something from boards table", () => {
|
||||
expect(db.BoardTable.find(1)).toEqual(Ok(Some({
|
||||
id: 1,
|
||||
name: `Board ${1}`,
|
||||
room: rooms[1],
|
||||
ipv4: `192.168.172.${1}`,
|
||||
port: 1,
|
||||
} as db.Board)))
|
||||
expect(db.BoardTable.find("Board 3", "A1")).toEqual(Ok(Some({
|
||||
id: 3,
|
||||
name: `Board ${3}`,
|
||||
room: rooms[3],
|
||||
ipv4: `192.168.172.${3}`,
|
||||
port: 3,
|
||||
} as db.Board)))
|
||||
})
|
||||
|
||||
test("Get count of elements from boards table", () => {
|
||||
expect(db.BoardTable.countByName("Board 1")).toBe(1)
|
||||
expect(db.BoardTable.countByRoom("A1")).toBe(5)
|
||||
})
|
|
@ -1,478 +0,0 @@
|
|||
import { Database, type Changes } from "bun:sqlite";
|
||||
import _ from "lodash";
|
||||
import { Ok, Err, Result, None, Some, Option } from "ts-results-es";
|
||||
import { z } from "zod";
|
||||
import { fun, type } from "./common";
|
||||
|
||||
const db = new Database("lab.sqlite", { strict: true })
|
||||
initDB(db);
|
||||
|
||||
// Error Type
|
||||
const BoardErrorTypeSchema = z.union([
|
||||
z.literal("Wrong Type"),
|
||||
z.literal("No Such Board(s)"),
|
||||
z.literal("ID Conflict"),
|
||||
z.literal("Name Conflict")
|
||||
])
|
||||
|
||||
export type BoardErrorType = z.infer<typeof BoardErrorTypeSchema>
|
||||
|
||||
const boardSchema = z.object({
|
||||
id: z.number().nonnegative(),
|
||||
name: z.string(),
|
||||
room: z.string(),
|
||||
ipv4: z.string().ip({ version: "v4" }),
|
||||
ipv6: z.string().ip({ version: "v6" }),
|
||||
port: z.number().nonnegative().lte(65535),
|
||||
cmdID: z.number().nonnegative()
|
||||
}).partial({
|
||||
ipv6: true,
|
||||
cmdID: true
|
||||
})
|
||||
|
||||
export type Board = z.infer<typeof boardSchema>
|
||||
export type BoardColumn = keyof Board
|
||||
|
||||
export function isBoard(obj: any): obj is Board {
|
||||
return boardSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
export function isBoardArray(obj: any): obj is Array<Board> {
|
||||
return boardSchema.array().safeParse(obj).success
|
||||
}
|
||||
|
||||
export function isBoardColumn(obj: any): obj is BoardColumn {
|
||||
return boardSchema.keyof().safeParse(obj).success
|
||||
}
|
||||
|
||||
const userSchema = z.object({
|
||||
id: z.number().nonnegative(),
|
||||
name: z.string(),
|
||||
password: z.string(),
|
||||
boardID: z.number(),
|
||||
}).partial({
|
||||
boardID: true,
|
||||
})
|
||||
|
||||
export type User = z.infer<typeof userSchema>
|
||||
export type UserColumn = keyof User
|
||||
|
||||
export function isUser(obj: any): obj is User {
|
||||
return userSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
export function isUserArray(obj: any): obj is Array<User> {
|
||||
return userSchema.array().safeParse(obj).success
|
||||
}
|
||||
|
||||
export function isUserColumn(obj: any): obj is UserColumn {
|
||||
return userSchema.keyof().safeParse(obj).success
|
||||
}
|
||||
|
||||
function initDB(db: Database) {
|
||||
const tables = allTables()
|
||||
|
||||
if (!tables.includes("Users")) {
|
||||
db.query(`
|
||||
CREATE TABLE Users(
|
||||
id INT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
`).run();
|
||||
}
|
||||
|
||||
if (!tables.includes("Boards"))
|
||||
db.query(`
|
||||
CREATE TABLE Boards(
|
||||
id INT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
room TEXT NOT NULL,
|
||||
ipv4 CHAR(16) NOT NULL,
|
||||
ipv6 CHAR(46) ,
|
||||
port INT NOT NULL
|
||||
)
|
||||
`).run();
|
||||
}
|
||||
|
||||
export function allTables(): Array<string> {
|
||||
const query = db.query(`SELECT name FROM sqlite_master WHERE type='table'`)
|
||||
var tables = new Array()
|
||||
// Flaten array
|
||||
for (const item of query.values()) {
|
||||
tables.push(item[0])
|
||||
}
|
||||
|
||||
query.finalize()
|
||||
return tables
|
||||
}
|
||||
|
||||
export function tableColumnName(table: string): Array<string> {
|
||||
const query = db.query(`PRAGMA table_info(${table})`)
|
||||
|
||||
var columnName = new Array()
|
||||
for (const column of query.values()) {
|
||||
columnName.push(column[1])
|
||||
}
|
||||
|
||||
return columnName
|
||||
}
|
||||
|
||||
export namespace BoardTable {
|
||||
|
||||
export function add(board: Board): Result<Changes, BoardErrorType> {
|
||||
if (!isBoard(board)) {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
// Ensure no id conflict
|
||||
if (board.id == 0) {
|
||||
const cnt = countAll()
|
||||
board.id = cnt + 1
|
||||
} else {
|
||||
const retID = includes(board.id)
|
||||
if (retID.isOk()) {
|
||||
if (retID.value) return new Err("ID Conflict")
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure no name conflict in the same room
|
||||
{
|
||||
const retName = includes(board.name, board.room)
|
||||
if (retName.isOk()) {
|
||||
if (retName.value) {
|
||||
return new Err("Name Conflict")
|
||||
}
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
}
|
||||
|
||||
const query = db.query(`
|
||||
INSERT INTO Boards VALUES
|
||||
(${board.id},
|
||||
'${board.name}',
|
||||
'${board.room}',
|
||||
'${board.ipv4}',
|
||||
'${_.isUndefined(board.ipv6) ? "NULL" : board.ipv6}',
|
||||
${board.port});
|
||||
`)
|
||||
|
||||
return Ok(query.run())
|
||||
}
|
||||
|
||||
export function addFromArray(array: Array<Board>)
|
||||
: Result<Array<Changes>, BoardErrorType> {
|
||||
|
||||
let arrayChanges: Array<Changes> = []
|
||||
for (const item of array) {
|
||||
const ret = add(item)
|
||||
if (ret.isErr()) {
|
||||
return ret
|
||||
} else {
|
||||
arrayChanges.push(ret.value)
|
||||
}
|
||||
}
|
||||
|
||||
return new Ok(arrayChanges)
|
||||
}
|
||||
|
||||
export function all(): Option<Array<Board>> {
|
||||
const query = db.query(`SELECT * FROM Boards`)
|
||||
const ret = query.all()
|
||||
query.finalize()
|
||||
|
||||
if (isBoardArray(ret)) {
|
||||
return Some(ret)
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
export function countAll(): number {
|
||||
const query = db.query(`SELECT COUNT(*) FROM Boards`)
|
||||
return query.values()[0][0] as number
|
||||
}
|
||||
|
||||
export function countByName(name: string): number {
|
||||
const query = db.query(`SELECT * FROM Boards WHERE name=${name}`)
|
||||
return query.values()[0][0] as number
|
||||
}
|
||||
|
||||
export function countByRoom(room: string): number {
|
||||
const query = db.query(`SELECT * FROM Boards WHERE room=${room}`)
|
||||
return query.values()[0][0] as number
|
||||
}
|
||||
|
||||
export function remove(name: string, room: string): Result<Board, BoardErrorType>
|
||||
export function remove(id: number): Result<Board, BoardErrorType>
|
||||
export function remove(board: Board): Result<Board, BoardErrorType>
|
||||
export function remove(arg1: any, arg2?: any): Result<Board, BoardErrorType> {
|
||||
let retBoard
|
||||
let condition: string
|
||||
if (isBoard(arg1)) {
|
||||
retBoard = _.cloneDeep(arg1)
|
||||
condition = `id=${arg1.id}`
|
||||
|
||||
} else if (_.isNumber(arg1)) {
|
||||
retBoard = find(arg1)
|
||||
if (retBoard.isOk() && retBoard.value.isSome()) {
|
||||
retBoard = _.cloneDeep(retBoard.value.value)
|
||||
condition = `id=${arg1}`
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
} else if (_.isString(arg1) && _.isString(arg2)) {
|
||||
retBoard = find(arg1, arg2)
|
||||
if (retBoard.isOk() && retBoard.value.isSome()) {
|
||||
retBoard = _.cloneDeep(retBoard.value.value)
|
||||
condition = `name=${arg1}`
|
||||
} else {
|
||||
return new Err("No Such Board(s)")
|
||||
}
|
||||
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
const query = db.query(`DELETE FROM Boards WHERE ${condition}`)
|
||||
query.run()
|
||||
|
||||
return new Ok(retBoard)
|
||||
}
|
||||
|
||||
export function removeAll(): Option<Array<Board>> {
|
||||
const array = all()
|
||||
const query = db.query(`DELETE FROM Boards`)
|
||||
query.run()
|
||||
|
||||
|
||||
if (array.isSome()) {
|
||||
return new Some(array.value)
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
export function removeByCondition(condition: string): Result<Array<Board>, BoardErrorType> {
|
||||
const rsArr = findByCondition(condition)
|
||||
if (rsArr.isNone()) {
|
||||
return new Err("No Such Board(s)")
|
||||
}
|
||||
|
||||
const query = db.query(`DELETE FROM Boards WHERE ${condition}`)
|
||||
query.run()
|
||||
|
||||
return new Ok(rsArr.value)
|
||||
}
|
||||
|
||||
export function removeByValue(columnName: string, val: string | Array<string>)
|
||||
: Result<Array<Board>, BoardErrorType> {
|
||||
|
||||
if (!isBoardColumn(columnName)) {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
let condition: string
|
||||
if (_.isString(val)) {
|
||||
const retCond = fun.sqlConditionFromString(columnName, val, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else if (type.isStringArray(val)) {
|
||||
const retCond = fun.sqlConditionFromArray(columnName, val, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
return removeByCondition(condition)
|
||||
}
|
||||
|
||||
export function removeByName(name: string | Array<string>)
|
||||
: Result<Array<Board>, BoardErrorType> {
|
||||
return removeByValue("name", name)
|
||||
}
|
||||
|
||||
export function removeByRoom(room: string | Array<string>)
|
||||
: Result<Array<Board>, BoardErrorType> {
|
||||
|
||||
return removeByValue("room", room)
|
||||
}
|
||||
|
||||
export function rooms(): Option<Array<string>> {
|
||||
const query = db.query(`SELECT DISTINCT room FROM Boards`)
|
||||
let rooms: Array<string> = []
|
||||
const retVal = query.values()
|
||||
|
||||
if (retVal.length > 0) {
|
||||
for (const item of retVal) {
|
||||
rooms.push(item[0] as string)
|
||||
}
|
||||
return new Some(rooms)
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function find(name: string, room: string): Result<Option<Board>, BoardErrorType>
|
||||
export function find(id: number): Result<Option<Board>, BoardErrorType>
|
||||
export function find(arg1: any, arg2?: any): Result<Option<Board>, BoardErrorType> {
|
||||
let condition: string
|
||||
if (_.isNumber(arg1)) {
|
||||
condition = `id=${arg1}`
|
||||
|
||||
} else if (_.isString(arg1) && _.isString(arg2)) {
|
||||
condition = `name='${arg1}' AND room='${arg2}'`
|
||||
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
const query = db.query(`SELECT * FROM Boards WHERE ${condition}`)
|
||||
|
||||
const spRet = boardSchema.safeParse(query.get())
|
||||
|
||||
if (spRet.success) {
|
||||
return new Ok(Some(spRet.data))
|
||||
} else {
|
||||
return new Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
export function findByName(name: string | Array<string>): Result<Option<Array<Board>>, BoardErrorType> {
|
||||
let condition: string
|
||||
if (_.isString(name)) {
|
||||
const retCond = fun.sqlConditionFromString("name", name, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else if (type.isStringArray(name)) {
|
||||
const retCond = fun.sqlConditionFromArray("name", name, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
return new Ok(findByCondition(condition))
|
||||
}
|
||||
|
||||
export function findByCondition(condition: string): Option<Array<Board>> {
|
||||
const query = db.query(`SELECT * FROM Boards WHERE ${condition}`)
|
||||
const ret = query.all()
|
||||
if (isBoardArray(ret)) {
|
||||
return new Some(ret)
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
export function findByValue(columnName: string, val: string | Array<string>)
|
||||
: Result<Option<Array<Board>>, BoardErrorType> {
|
||||
|
||||
if (!isBoardColumn(columnName)) {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
let condition: string
|
||||
if (_.isString(val)) {
|
||||
const retCond = fun.sqlConditionFromString(columnName, val, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else if (type.isStringArray(val)) {
|
||||
const retCond = fun.sqlConditionFromArray(columnName, val, "OR")
|
||||
if (retCond.isSome()) {
|
||||
condition = retCond.value
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
return new Ok(findByCondition(condition))
|
||||
}
|
||||
|
||||
export function includes(name: string, room?: string): Result<boolean, BoardErrorType>
|
||||
export function includes(id: number): Result<boolean, BoardErrorType>
|
||||
export function includes(arg1: any, arg2?: any): Result<boolean, BoardErrorType> {
|
||||
let condition: string
|
||||
if (_.isUndefined(arg2)) {
|
||||
if (_.isNumber(arg1)) {
|
||||
condition = `id=${arg1}`
|
||||
|
||||
} else if (_.isString(arg1)) {
|
||||
condition = `name='${arg1}'`
|
||||
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
} else {
|
||||
if (_.isString(arg1) && _.isString(arg2)) {
|
||||
condition = `name='${arg1} AND room=${arg2}'`
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
}
|
||||
|
||||
const query = db.query(`SELECT COUNT(*) FROM Boards WHERE ${condition}`)
|
||||
const num = query.values()[0][0] as number
|
||||
|
||||
return new Ok(num > 0 ? true : false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace UserTable {
|
||||
|
||||
export function countAll(): number {
|
||||
const query = db.query(`SELECT COUNT(*) FROM Users`)
|
||||
return query.values()[0][0] as number
|
||||
}
|
||||
|
||||
|
||||
export function find(id: number): Result<Option<User>, "Wrong Type">
|
||||
export function find(name: string): Result<Option<User>, "Wrong Type">
|
||||
export function find(arg: any): Result<Option<User>, "Wrong Type"> {
|
||||
let condition: string
|
||||
if (_.isNumber(arg)) {
|
||||
condition = `id=${arg}`
|
||||
} else if (_.isString(arg)) {
|
||||
condition = `name=${arg}`
|
||||
} else {
|
||||
return new Err("Wrong Type")
|
||||
}
|
||||
|
||||
const query = db.query(`SELECT * FROM Users WHERE name='${arg}'`)
|
||||
|
||||
const spRet = userSchema.safeParse(query.get())
|
||||
|
||||
if (spRet.success) {
|
||||
return new Ok(Some(spRet.data))
|
||||
} else {
|
||||
return new Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { createBunServeHandler } from "trpc-bun-adapter";
|
||||
import { appRouter } from "./router.ts"
|
||||
|
||||
Bun.serve(createBunServeHandler({
|
||||
router: appRouter,
|
||||
responseMeta() {
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, PUT, POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
port: 3002,
|
||||
fetch() {
|
||||
return Response.json({ message: "Not Found" }, { status: 404 });
|
||||
}
|
||||
}))
|
|
@ -1 +0,0 @@
|
|||
import { MsgProtocol } from "./msgProtocol";
|
|
@ -1,79 +0,0 @@
|
|||
import { type TransferListItem } from "worker_threads";
|
||||
import { z } from "zod";
|
||||
import { UNUSED } from "./common";
|
||||
|
||||
export namespace MsgProtocol {
|
||||
const QueryDataSchema = z.object({ type: z.literal("Query"), args: z.any() })
|
||||
const ResultDataSchema = z.object({ type: z.literal("Result"), result: z.any() })
|
||||
const ErrorDataSchema = z.object({ type: z.literal("Error"), error: z.string() })
|
||||
|
||||
const MessageSchema = z.object({
|
||||
command: z.string(),
|
||||
data: z.discriminatedUnion("type", [QueryDataSchema, ResultDataSchema, ErrorDataSchema]),
|
||||
dest: z.string(),
|
||||
src: z.string(),
|
||||
})
|
||||
const MessageQuerySchema = z.object({
|
||||
command: z.string(),
|
||||
data: QueryDataSchema,
|
||||
dest: z.string(),
|
||||
src: z.string(),
|
||||
})
|
||||
const MessageResultSchema = z.object({
|
||||
command: z.string(),
|
||||
data: ResultDataSchema,
|
||||
dest: z.string(),
|
||||
src: z.string(),
|
||||
})
|
||||
const MessageErrorSchema = z.object({
|
||||
command: z.string(),
|
||||
data: ErrorDataSchema,
|
||||
dest: z.string(),
|
||||
src: z.string(),
|
||||
})
|
||||
const MessageHandlerSchema = z.function()
|
||||
.args(z.union([MessageResultSchema, MessageErrorSchema]))
|
||||
.returns(z.void())
|
||||
|
||||
export type Message = z.infer<typeof MessageSchema>
|
||||
export type MessageQuery = z.infer<typeof MessageQuerySchema>
|
||||
export type MessageResult = z.infer<typeof MessageResultSchema>
|
||||
export type MessageError = z.infer<typeof MessageErrorSchema>
|
||||
export type MessageHandler = z.infer<typeof MessageHandlerSchema>
|
||||
|
||||
export function isMessage(obj: any): obj is Message {
|
||||
return MessageSchema.safeParse(obj).success
|
||||
}
|
||||
export function isMessageQuery(obj: any): obj is MessageQuery {
|
||||
return MessageQuerySchema.safeParse(obj).success
|
||||
}
|
||||
export function isMessageResult(obj: any): obj is MessageResult {
|
||||
return MessageResultSchema.safeParse(obj).success
|
||||
}
|
||||
export function isMessageError(obj: any): obj is MessageError {
|
||||
return MessageErrorSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
export function genMessageResult(result: any, srcMsg: MessageQuery): MessageResult {
|
||||
return {
|
||||
command: srcMsg.command,
|
||||
dest: srcMsg.src,
|
||||
src: srcMsg.dest,
|
||||
data: {
|
||||
type: "Result",
|
||||
result: result
|
||||
}
|
||||
} as MessageResult
|
||||
}
|
||||
export function genMessageError(error: string, srcMsg: MessageQuery): MessageError {
|
||||
return {
|
||||
command: srcMsg.command,
|
||||
dest: srcMsg.src,
|
||||
src: srcMsg.dest,
|
||||
data: {
|
||||
type: "Error",
|
||||
error: error
|
||||
}
|
||||
} as MessageError
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import { router, publicProcedure } from "./trpc.ts"
|
||||
|
||||
export const appRouter = router({
|
||||
api: router({
|
||||
status: publicProcedure.query(() => "OK"),
|
||||
signUp: publicProcedure.query((opts) => {
|
||||
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
|
@ -1,6 +0,0 @@
|
|||
import { initTRPC } from '@trpc/server';
|
||||
|
||||
const t = initTRPC.create();
|
||||
|
||||
export const router = t.router;
|
||||
export const publicProcedure = t.procedure;
|
|
@ -1,22 +0,0 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { udpServer, udpClient } from "./udp"
|
||||
|
||||
const udpServerAddr = "127.0.0.1"
|
||||
const udpServerPort = await udpServer.port()
|
||||
|
||||
console.info("Ready to Test UDP")
|
||||
|
||||
test("Test Send String", async () => {
|
||||
const str = "Hello My Server"
|
||||
const retSend = await udpClient.sendString(str, udpServerPort, udpServerAddr)
|
||||
expect(retSend).toBe(true)
|
||||
|
||||
const retReceive = await udpServer.lastestData(udpServerAddr)
|
||||
if (retReceive.isSome()) {
|
||||
const data = retReceive.value
|
||||
expect(data.body).toBe(str)
|
||||
expect(data.address).toBe(udpServerAddr)
|
||||
} else {
|
||||
expect().fail("Not Receive Anything")
|
||||
}
|
||||
})
|
|
@ -1,80 +0,0 @@
|
|||
import { resolve } from "path";
|
||||
import Tinypool from "tinypool";
|
||||
import type { UDPReceiveProcotol, UDPSendProtocol } from "./udpProtocol";
|
||||
import type { udp } from "bun";
|
||||
import { Option } from "ts-results-es";
|
||||
|
||||
const SERVER_PATH = resolve(__dirname, "./udpServer.ts")
|
||||
const CLIENT_PATH = resolve(__dirname, "./udpClient.ts")
|
||||
|
||||
export type UDPBodyType = (
|
||||
udp.Data |
|
||||
UDPSendProtocol.CmdPackage |
|
||||
UDPSendProtocol.DataPackage |
|
||||
UDPReceiveProcotol.ReadPackage |
|
||||
UDPReceiveProcotol.WritePackage
|
||||
)
|
||||
|
||||
type UDPDataType<T extends UDPBodyType = UDPBodyType> = {
|
||||
address: string,
|
||||
body: T,
|
||||
port: number,
|
||||
date?: string,
|
||||
}
|
||||
|
||||
|
||||
const udpClientsPool = new Tinypool({
|
||||
filename: CLIENT_PATH,
|
||||
workerData: {}
|
||||
})
|
||||
|
||||
const udpServerPool = new Tinypool({
|
||||
filename: SERVER_PATH,
|
||||
workerData: {},
|
||||
maxThreads: 1,
|
||||
minThreads: 1,
|
||||
})
|
||||
|
||||
export namespace udpServer {
|
||||
|
||||
export async function port(): Promise<number> {
|
||||
return udpServerPool.run(null, { name: "port" })
|
||||
}
|
||||
|
||||
export async function lastestData(address: string): Promise<Option<UDPDataType<udp.Data>>> {
|
||||
return udpServerPool.run(address, { name: "lastestData" })
|
||||
}
|
||||
|
||||
export async function oldestData(address: string): Promise<Option<UDPDataType<udp.Data>>> {
|
||||
return udpServerPool.run(address, { name: "oldestData" })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace udpClient {
|
||||
|
||||
export async function sendString(data: string, port: number, address: string): Promise<boolean> {
|
||||
return udpClientsPool.run({
|
||||
body: data,
|
||||
port: port,
|
||||
address: address
|
||||
} as UDPDataType<string>, { name: "sendString" })
|
||||
}
|
||||
|
||||
export async function sendUint8Array(data: Uint8Array, port: number, address: string): Promise<boolean> {
|
||||
return udpClientsPool.run({
|
||||
body: data,
|
||||
port: port,
|
||||
address: address
|
||||
} as UDPDataType<Uint8Array>, { name: "sendUint8Array" })
|
||||
}
|
||||
|
||||
export async function sendBunData(data: udp.Data, port: number, address: string): Promise<boolean> {
|
||||
return udpClientsPool.run({
|
||||
body: data,
|
||||
port: port,
|
||||
address: address
|
||||
} as UDPDataType<udp.Data>, { name: "sendUint8Array" })
|
||||
}
|
||||
}
|
||||
export type { UDPDataType }
|
|
@ -1,17 +0,0 @@
|
|||
import type { udp } from "bun"
|
||||
import type { UDPDataType } from "./udp"
|
||||
|
||||
const udpClient = await Bun.udpSocket({})
|
||||
|
||||
export function sendString(data: UDPDataType<string>): boolean {
|
||||
return udpClient.send(data.body, data.port, data.address)
|
||||
}
|
||||
|
||||
export function sendUint8Array(data: UDPDataType<Uint8Array>): boolean {
|
||||
return udpClient.send(data.body, data.port, data.address)
|
||||
}
|
||||
|
||||
export function sendBunData(data: UDPDataType<udp.Data>): boolean {
|
||||
return udpClient.send(data.body, data.port, data.address)
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
import { type } from "./common";
|
||||
import { z } from "zod";
|
||||
import _ from "lodash";
|
||||
import { Err, Ok, type Result } from "ts-results-es";
|
||||
import { isUint8Array } from "util/types";
|
||||
|
||||
const PKG_SIGN_ADDR = 0x00
|
||||
const PKG_SIGN_DATA = 0xFF
|
||||
const PKG_SIGN_READ = 0x0F
|
||||
const PKG_SIGN_WRITE = 0xF0
|
||||
|
||||
export namespace UDPSendProtocol {
|
||||
|
||||
const CMDLOC_BURST_TYPE = 5
|
||||
const CMDLOC_TASK_ID = 3
|
||||
const CMDLOC_READ_WRITE_TYPE = 0
|
||||
|
||||
const CmdPackageOptionsSchema = z.object({
|
||||
burstType: z.enum(["Fixed", "Extend"]),
|
||||
taskID: z.number().nonnegative().lt(4),
|
||||
readWriteType: z.enum(["r", "w"])
|
||||
})
|
||||
|
||||
export type CmdPackageOptions = z.infer<typeof CmdPackageOptionsSchema>
|
||||
|
||||
export function isCmdPackageOptions(obj: any): obj is CmdPackageOptions {
|
||||
return CmdPackageOptionsSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
export class CmdPackage {
|
||||
private ID: number = PKG_SIGN_ADDR
|
||||
private commandType: number = 0
|
||||
private burstLength: number = 0
|
||||
private _reserved: number = 0
|
||||
private address: number = 0
|
||||
|
||||
|
||||
constructor(array: Uint8Array)
|
||||
constructor(options: CmdPackageOptions)
|
||||
constructor(arg: any) {
|
||||
if (isCmdPackageOptions(arg)) {
|
||||
this.setCommandType(arg)
|
||||
} else if (isUint8Array(arg)) {
|
||||
this.ID = arg[0]
|
||||
this.commandType = arg[1]
|
||||
this.burstLength = arg[2]
|
||||
this._reserved = arg[3]
|
||||
this.address = type.bytesToNumber(arg.slice(4))
|
||||
} else {
|
||||
throw new Err("Wrong Type")
|
||||
}
|
||||
}
|
||||
|
||||
setCommandType(options: CmdPackageOptions) {
|
||||
const validOptions = CmdPackageOptionsSchema.parse(options)
|
||||
this.commandType = (
|
||||
((validOptions.burstType === "Fixed") ? 1 << CMDLOC_BURST_TYPE : 0) |
|
||||
(validOptions.taskID << CMDLOC_TASK_ID) |
|
||||
((validOptions.readWriteType === "r") ? 1 << CMDLOC_READ_WRITE_TYPE : 0)
|
||||
)
|
||||
}
|
||||
|
||||
setBurstLength(len: number): Result<null, "Not 8Bits Unsigned Integer"> {
|
||||
if (!type.UInt8.safeParse(len).success) {
|
||||
return new Err("Not 8Bits Unsigned Integer")
|
||||
}
|
||||
this.burstLength = len
|
||||
return new Ok(null)
|
||||
}
|
||||
|
||||
setAddress(addr: number): Result<null, "Not 32Bits Unsigned Integer"> {
|
||||
if (!type.UInt32.safeParse(addr).success) {
|
||||
return new Err("Not 32Bits Unsigned Integer")
|
||||
}
|
||||
this.address = addr
|
||||
return new Ok(null)
|
||||
}
|
||||
|
||||
options(): CmdPackageOptions {
|
||||
return {
|
||||
burstType: type.numberMatch(this.commandType, CMDLOC_BURST_TYPE, "Extend", "Fixed"),
|
||||
taskID: (this.commandType >> CMDLOC_TASK_ID) & 0b0011,
|
||||
readWriteType: type.numberMatch(this.commandType, CMDLOC_READ_WRITE_TYPE, "w", "r")
|
||||
}
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
var array = new Uint8Array(8)
|
||||
|
||||
array[0] = this.ID
|
||||
array[1] = this.commandType
|
||||
array[2] = this.burstLength
|
||||
array[3] = this._reserved
|
||||
|
||||
// Already check address length at the begining of set address
|
||||
let addressBytes = type.numberToBytes(this.address, 4).unwrap()
|
||||
array.set(addressBytes, 4)
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
export function isCmdPackage(obj: any): obj is CmdPackage {
|
||||
return obj instanceof CmdPackage
|
||||
}
|
||||
|
||||
export class DataPackage {
|
||||
private ID: number = PKG_SIGN_DATA
|
||||
private _reserved: number = 0
|
||||
private body: Uint8Array
|
||||
|
||||
constructor(body: Uint8Array) {
|
||||
this.body = body
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
var array = new Uint8Array(4 + this.body.length)
|
||||
|
||||
array[0] = this.ID
|
||||
array.fill(this._reserved, 1, 4)
|
||||
array.set(this.body, 4)
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
export function isDataPackage(obj: any): obj is DataPackage {
|
||||
return obj instanceof DataPackage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace UDPReceiveProcotol {
|
||||
|
||||
export class ReadPackage {
|
||||
private ID: number = PKG_SIGN_READ
|
||||
private taskID: number = 0
|
||||
private resp: number = 0
|
||||
private _reserved: number = 0
|
||||
body: Uint8Array
|
||||
|
||||
constructor(array: Uint8Array) {
|
||||
if (array.length < 5) {
|
||||
throw new Err("Not Long Enough")
|
||||
}
|
||||
this.ID = array[0]
|
||||
this.taskID = array[1]
|
||||
this.resp = array[2]
|
||||
this._reserved = array[3]
|
||||
this.body = new Uint8Array(array.slice(4))
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
let array = new Uint8Array(4 + this.body.length)
|
||||
|
||||
array[0] = this.ID
|
||||
array[1] = this.taskID
|
||||
array[2] = this.resp
|
||||
array[3] = this._reserved
|
||||
array.set(this.body, 4)
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
export function isReadPackage(obj: any): obj is ReadPackage {
|
||||
return obj instanceof ReadPackage
|
||||
}
|
||||
|
||||
export class WritePackage {
|
||||
private ID: number = PKG_SIGN_WRITE
|
||||
private taskID: number = 0
|
||||
private resp: number = 0
|
||||
private _reserved: number = 0
|
||||
|
||||
|
||||
constructor(array: Uint8Array) {
|
||||
if (array.length < 4) {
|
||||
throw new Err("Not Long Enough")
|
||||
}
|
||||
this.ID = array[0]
|
||||
this.taskID = array[1]
|
||||
this.resp = array[2]
|
||||
this._reserved = array[3]
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
return new Uint8Array([
|
||||
this.ID,
|
||||
this.taskID,
|
||||
this.resp,
|
||||
this._reserved
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function isWritePackage(obj: any): obj is WritePackage {
|
||||
return obj instanceof WritePackage
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
import _ from "lodash"
|
||||
import { type udp } from "bun"
|
||||
import type { UDPDataType } from "./udp";
|
||||
import { None, Some, type Option } from "ts-results-es";
|
||||
import { z } from "zod";
|
||||
|
||||
// Bun Data Type
|
||||
interface BinaryTypeList {
|
||||
arraybuffer: ArrayBuffer;
|
||||
buffer: Buffer;
|
||||
uint8array: Uint8Array;
|
||||
}
|
||||
type BinaryType = keyof BinaryTypeList;
|
||||
|
||||
const receivedData: Map<string, Array<{
|
||||
body: udp.Data,
|
||||
port: number,
|
||||
date: string
|
||||
}>> = new Map()
|
||||
|
||||
const udpServer = await Bun.udpSocket({
|
||||
port: 33000,
|
||||
socket: {
|
||||
data(
|
||||
_socket: udp.Socket<BinaryType>,
|
||||
data: BinaryTypeList[BinaryType],
|
||||
port: number,
|
||||
address: string,
|
||||
) {
|
||||
// Add Received Data
|
||||
let arrayData = receivedData.get(address)
|
||||
if (_.isUndefined(arrayData)) {
|
||||
|
||||
} else {
|
||||
receivedData.set(address, [])
|
||||
arrayData.push({
|
||||
body: data,
|
||||
port: port,
|
||||
date: new Date().toUTCString()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function port(): number {
|
||||
return udpServer.port
|
||||
}
|
||||
|
||||
function lastestData(address: string): Option<UDPDataType<udp.Data>> {
|
||||
if (!z.string().ip().safeParse(address).success) {
|
||||
return None
|
||||
}
|
||||
|
||||
const arrayData = receivedData.get(address)
|
||||
if (_.isUndefined(arrayData)) {
|
||||
return None
|
||||
}
|
||||
|
||||
const data = arrayData.pop()
|
||||
if (_.isUndefined(data)) {
|
||||
return None
|
||||
}
|
||||
|
||||
return Some({
|
||||
address: address,
|
||||
body: data.body,
|
||||
port: data.port,
|
||||
date: data.date,
|
||||
})
|
||||
}
|
||||
|
||||
function oldestData(address: string): Option<UDPDataType<udp.Data>> {
|
||||
if (!z.string().ip().safeParse(address).success) {
|
||||
return None
|
||||
}
|
||||
|
||||
const arrayData = receivedData.get(address)
|
||||
if (_.isUndefined(arrayData)) {
|
||||
return None
|
||||
}
|
||||
|
||||
const data = arrayData.shift()
|
||||
if (_.isUndefined(data)) {
|
||||
return None
|
||||
}
|
||||
|
||||
return Some({
|
||||
address: address,
|
||||
body: data.body,
|
||||
port: data.port,
|
||||
date: data.date,
|
||||
})
|
||||
}
|
||||
|
||||
export { port, lastestData, oldestData }
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
// Some stricter flags
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue