-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
1,619 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,6 +72,6 @@ | |
"zardoy.ts-essential-plugins", | ||
"mongodb.mongodb-vscode" | ||
] | ||
} | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.