Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
semyenov committed May 28, 2024
1 parent f4eb9f6 commit a1e8ffe
Show file tree
Hide file tree
Showing 20 changed files with 1,619 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@
"zardoy.ts-essential-plugins",
"mongodb.mongodb-vscode"
]
}
},
}
}
11 changes: 2 additions & 9 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,15 @@
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},

"npm.packageManager": "yarn",
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "node_modules/typescript/lib",
// "typescript.inlayHints.parameterNames.enabled": "all",

"where-am-i.icon": "home",
"where-am-i.template": "{icon} {project-name}",
"where-am-i.colorful": true,
"where-am-i.color": "#fff",

"eslint.format.enable": true,
"eslint.lintTask.enable": true,
"eslint.experimental.useFlatConfig": true,
Expand All @@ -36,26 +33,22 @@
"json",
"jsonc"
],

"browse-lite.startUrl": "http://localhost:3000",
"browse-lite.chromeExecutable": "/usr/bin/chromium",

"fileNestingUpdater.autoUpdate": true,
"fileNestingUpdater.autoUpdateInterval": 720,
"fileNestingUpdater.promptOnAutoUpdate": true,
"fileNestingUpdater.upstreamRepo": "antfu/vscode-file-nesting-config",
"fileNestingUpdater.upstreamBranch": "main",

"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},

"[vue]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[markdown]": {
"editor.formatOnSave": false
Expand All @@ -75,4 +68,4 @@
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
}
1 change: 0 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const schema = [
'eslint-comments/no-unlimited-disable': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-unused-vars': ['warn', { args: 'none' }],
'no-use-before-define': ['error', { functions: false }],
'no-param-reassign': ['error', { props: false }],
'no-underscore-dangle': ['error', { allow: ['_id', '_count'] }],
'no-shadow': ['error', { allow: ['_id', '_count'] }],
Expand Down
60 changes: 18 additions & 42 deletions lib/automerge/index.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,40 @@
import { EventEmitter } from 'node:events'

import { next as A } from '@automerge/automerge'
import { Loro } from 'loro-crdt'

import { uint8ArrayToString } from '@/transformer'

import type { LoroEvent, LoroMap } from 'loro-crdt'

export interface DocType {
name: string
ideas: Array<string>
}

export const docs: Map<string, A.Doc<DocType>> = new Map()
const emitter = new EventEmitter()
const root = new Loro<{ docs: LoroMap<Record<string, DocType>> }>()

let n = 0
export function next() {
n += 1
const docs = root.getMap('docs')
docs.set('test', { name: '', ideas: [] })

return n
export function getKeys() {
return docs.keys() as string[]
}

export function getItem(id: string) {
if (!docs.has(id)) {
docs.set(id, A.init())
}

const doc = docs.get(id)!
export function getItem() {
return uint8ArrayToString(root.exportSnapshot())
}

return uint8ArrayToString(A.save(doc))
export function deleteItem(id: string) {
docs.delete(id)
}

export function change(
export function putItem(
id: string,
changeFn: (d: DocType) => void,
): A.Doc<DocType> {
if (!docs.has(id)) {
docs.set(id, A.init<DocType>())
}

let doc = docs.get(id)!

doc = A.change(doc, {
message: `Change ${next()}`,
time: new Date().getTime(),
}, changeFn)

doc: DocType,
) {
docs.set(id, doc)

const lastChange = A.getLastLocalChange(doc)
emitter.emit('change', {
i: id,
lastChange: lastChange
? uint8ArrayToString(lastChange)
: '',
})

return doc
}

export function onChange(
listener: (data: { id: string; lastChange: string }) => void,
listener: (event: LoroEvent) => void,
) {
emitter.on('change', listener)
root.subscribe(listener)
}
18 changes: 18 additions & 0 deletions lib/loro/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Loro } from 'loro-wasm'

import type { LoroMap, LoroTree } from 'loro-wasm'

interface DocType {
test: string
}

const root = new Loro<{
docs: LoroMap<Record<string, DocType>>
tree: LoroTree<Record<string, DocType>>
}>()

const tree = root.getTree('tree')
const node = tree.createNode()
node.data.set('test', { test: '' })

console.log(tree.nodes, node)
49 changes: 49 additions & 0 deletions lib/radix/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import consola from 'consola'
import { Radix } from 'radix-ts'

import { StoreInMemory } from './store'

import type { Query } from 'radix-ts'

const logger = consola.withTag('radix')

const store = new StoreInMemory()
const radix = new Radix(store)

const containerSchema = 'schema_a'
const containerID = 'example_key_b'

await radix.set(`${containerSchema}:${containerID}:HEAD:path1`, { value: 1 })
await radix.set(`${containerSchema}:${containerID}:HEAD:path2`, { value: 1 })
await radix.set(`${containerSchema}:${containerID}:4000:path1`, { value: 2 })
await radix.set(`${containerSchema}:${containerID}:2000:path1`, { value: 2 })
await radix.set(`${containerSchema}:${containerID}:1000:path1`, { value: 2 })
await radix.set(`${containerSchema}:${containerID}:3000:path1`, { value: 5 })

// Loop through the data
const query: Query = {
count: 4, // To retrieve four matching keys and values
sort: -1, // Sort in descending order (1 for ascending)
prefix: [containerSchema, containerID].join(':'), // Only return values with specified key prefix
}

// Use the loop method to iterate over keys and values
for await (const [key, delta] of radix.loop<{ value: number }>(query)) {
const [schema, id, version, path] = key.split(':')
logger.log({ schema, id, version, path, delta })
}

// Loop through the data
const query1: Query = {
count: 4, // To retrieve four matching keys and values
sort: -1, // Sort in descending order (1 for ascending)
prefix: [containerSchema, containerID, 'HEAD'].join(':'), // Only return values with specified key prefix
}

const container: Record<string, number> = {}
for await (const [key, delta] of radix.loop<{ value: number }>(query1)) {
const [schema, _id, _version, path] = key.split(':')
container[path] = delta.value
}

logger.log('Container', container)
33 changes: 33 additions & 0 deletions lib/radix/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createStorage } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs'

import type { IStore } from 'radix-ts'
import type { Storage } from 'unstorage'

export class StoreInMemory implements IStore {
public storage: Storage<any>

constructor() {
this.storage = createStorage<any>({
driver: fsDriver({ base: './tmp' }),
})
}

async get(key: string) {
return this.storage.getItem(key)
}

async set(key: string, value: any) {
return this.storage.setItem(key, value)
}

async del(key: string) {
return this.storage.removeItem(key)
}

toString(_pathsOnly = false) {
return JSON.stringify(
this.storage,
)
}
}
104 changes: 104 additions & 0 deletions lib/test3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
class CRDTMap<K, V> {
private values: Map<K, CRDTMapValue<V>>
private vectorClock: number[]

constructor() {
this.values = new Map<K, CRDTMapValue<V>>()
this.vectorClock = []
}

set(key: K, value: V): CRDTMap<K, V> {
const vectorClock = this.vectorClock.map(value => value + 1)
const newValue = new CRDTMapValue<V>(value, vectorClock)
const currentValue = this.values.get(key)

if (currentValue) {
const mergedValue = CRDTMapValue.merge(currentValue, newValue)
this.values.set(key, mergedValue)
}
else {
this.values.set(key, newValue)
}

this.vectorClock = vectorClock

return this
}

get(key: K): V | undefined {
const mapValue = this.values.get(key)

return mapValue?.value
}

delete(key: K): void {
const vectorClock = this.vectorClock.map(value => value + 1)
this.values.delete(key)
this.vectorClock = vectorClock
}

merge(other: CRDTMap<K, V>): CRDTMap<K, V> {
other.values.forEach((value, key) => {
const currentValue = this.values.get(key)

if (currentValue) {
const mergedValue = CRDTMapValue.merge(currentValue, value)
this.values.set(key, mergedValue)
}
else {
this.values.set(key, value)
}
})

this.vectorClock = CRDTMap.mergeVectorClocks(this.vectorClock, other.vectorClock)

return this
}

private static mergeVectorClocks(vectorClock1: number[], vectorClock2: number[]): number[] {
if (vectorClock1.length !== vectorClock2.length) {
throw new Error('Vector clocks have different lengths')
}

const mergedVectorClock = vectorClock1.map((value, index) => Math.max(value, vectorClock2[index]))

return mergedVectorClock
}
}

class CRDTMapValue<V> {
public value: V
private vectorClock: number[]

constructor(value: V, vectorClock: number[]) {
this.value = value
this.vectorClock = vectorClock
}

static merge<V>(value1: CRDTMapValue<V>, value2: CRDTMapValue<V>): CRDTMapValue<V> {
const mergedVectorClock = CRDTMapValue.mergeVectorClocks(value1.vectorClock, value2.vectorClock)

if (value1.vectorClock.every((value, index) => value === mergedVectorClock[index])) {
return value1
}

if (value2.vectorClock.every((value, index) => value === mergedVectorClock[index])) {
return value2
}

const mergedValue = mergeValues(value1.value, value2.value)

return new CRDTMapValue<V>(mergedValue, mergedVectorClock)
}

private static mergeVectorClocks(vectorClock1: number[], vectorClock2: number[]): number[] {
const mergedVectorClock = vectorClock1.map((value, index) => Math.max(value, vectorClock2[index]))

return mergedVectorClock
}
}

function mergeValues<V>(value1: V, value2: V): V {
// Implement a custom merge function for your value type here
return value1
}
17 changes: 17 additions & 0 deletions lib/test4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import fs from 'node:fs'

function convertJsonArrayToMultiline(inputFilePath: string, outputFilePath: string): void {
const jsonString = fs.readFileSync(inputFilePath, 'utf8')
const jsonArray = JSON.parse(jsonString)

for (const jsonObject of jsonArray) {
const formattedJsonString = JSON.stringify(jsonObject, null, 2)
fs.writeFileSync(outputFilePath, formattedJsonString, { flag: 'a' })
}
}

// Example usage
const inputFilePath = 'path/to/your/input.json'
const outputFilePath = 'path/to/your/output.json'

convertJsonArrayToMultiline(inputFilePath, outputFilePath)
Loading

0 comments on commit a1e8ffe

Please sign in to comment.