Skip to content

Commit

Permalink
feat: add onCommit option
Browse files Browse the repository at this point in the history
  • Loading branch information
icidasset committed Dec 14, 2023
1 parent d0e2eea commit ac1b2c7
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 49 deletions.
83 changes: 59 additions & 24 deletions packages/nest/src/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import Emittery, {

import type {
AnySupportedDataType,
CommitVerifier,
DataForType,
DataRootChange,
DataType,
DirectoryItem,
DirectoryItemWithKind,
FileSystemChange,
Modification,
MutationOptions,
MutationResult,
NOOP,
PrivateMutationResult,
PublicMutationResult,
} from './types.js'
Expand Down Expand Up @@ -54,6 +55,7 @@ import { BasicRootTree } from './root-tree/basic.js'

export interface Options {
blockstore: Blockstore
onCommit?: CommitVerifier
rootTreeClass?: typeof RootTree
settleTimeBeforePublish?: number
}
Expand All @@ -62,6 +64,7 @@ export class FileSystem {
readonly #blockstore: Blockstore
readonly #debouncedDataRootUpdate: debounce.DebouncedFunction<any>
readonly #eventEmitter: Emittery<Events>
readonly #onCommit: CommitVerifier
readonly #rng: Rng.Rng

#privateNodes: MountedPrivateNodes = {}
Expand All @@ -70,11 +73,13 @@ export class FileSystem {
/** @hidden */
constructor(
blockstore: Blockstore,
onCommit: CommitVerifier | undefined,
rootTree: RootTree,
settleTimeBeforePublish: number
) {
this.#blockstore = blockstore
this.#eventEmitter = new Emittery<Events>()
this.#onCommit = onCommit ?? (async () => ({ commit: true }))
this.#rng = Rng.makeRngInterface()
this.#rootTree = rootTree

Expand All @@ -96,23 +101,35 @@ export class FileSystem {
* Creates a file system with an empty public tree & an empty private tree at the root.
*/
static async create(opts: Options): Promise<FileSystem> {
const { blockstore, rootTreeClass, settleTimeBeforePublish } = opts
const { blockstore, onCommit, rootTreeClass, settleTimeBeforePublish } =
opts
const rootTree = await (rootTreeClass ?? BasicRootTree).create(blockstore)

return new FileSystem(blockstore, rootTree, settleTimeBeforePublish ?? 2500)
return new FileSystem(
blockstore,
onCommit,
rootTree,
settleTimeBeforePublish ?? 2500
)
}

/**
* Loads an existing file system from a CID.
*/
static async fromCID(cid: CID, opts: Options): Promise<FileSystem> {
const { blockstore, rootTreeClass, settleTimeBeforePublish } = opts
const { blockstore, onCommit, rootTreeClass, settleTimeBeforePublish } =
opts
const rootTree = await (rootTreeClass ?? BasicRootTree).fromCID(
blockstore,
cid
)

return new FileSystem(blockstore, rootTree, settleTimeBeforePublish ?? 2500)
return new FileSystem(
blockstore,
onCommit,
rootTree,
settleTimeBeforePublish ?? 2500
)
}

// EVENTS
Expand Down Expand Up @@ -403,7 +420,7 @@ export class FileSystem {
| Path.File<PartitionedNonEmpty<Partition>>
| Path.Directory<Partitioned<Partition>>,
mutationOptions: MutationOptions = {}
): Promise<MutationResult<Partition> | null> {
): Promise<MutationResult<Partition>> {
return await this.#infusedTransaction(
async (t) => {
await t.copy(from, to)
Expand All @@ -421,15 +438,16 @@ export class FileSystem {
path: Path.Directory<PartitionedNonEmpty<P>>,
mutationOptions?: MutationOptions
): Promise<
MutationResult<P> & { path: Path.Directory<PartitionedNonEmpty<Partition>> }
MutationResult<P, { path: Path.Directory<PartitionedNonEmpty<Partition>> }>
>
async createDirectory(
path: Path.Directory<PartitionedNonEmpty<Partition>>,
mutationOptions: MutationOptions = {}
): Promise<
MutationResult<Partition> & {
path: Path.Directory<PartitionedNonEmpty<Partition>>
}
MutationResult<
Partition,
{ path: Path.Directory<PartitionedNonEmpty<Partition>> }
>
> {
let finalPath = path

Expand All @@ -455,17 +473,18 @@ export class FileSystem {
data: DataForType<D, V>,
mutationOptions?: MutationOptions
): Promise<
MutationResult<P> & { path: Path.File<PartitionedNonEmpty<Partition>> }
MutationResult<P, { path: Path.File<PartitionedNonEmpty<Partition>> }>
>
async createFile<D extends DataType, V = unknown>(
path: Path.File<PartitionedNonEmpty<Partition>>,
dataType: DataType,
data: DataForType<D, V>,
mutationOptions: MutationOptions = {}
): Promise<
MutationResult<Partition> & {
path: Path.File<PartitionedNonEmpty<Partition>>
}
MutationResult<
Partition,
{ path: Path.File<PartitionedNonEmpty<Partition>> }
>
> {
let finalPath = path

Expand Down Expand Up @@ -534,11 +553,15 @@ export class FileSystem {
async remove(
path: Path.Distinctive<PartitionedNonEmpty<Partition>>,
mutationOptions: MutationOptions = {}
): Promise<DataRootChange> {
): Promise<NOOP | { dataRoot: CID }> {
const transactionResult = await this.transaction(async (t) => {
await t.remove(path)
}, mutationOptions)

if (transactionResult === 'no-op') {
return 'no-op'
}

return {
dataRoot: transactionResult.dataRoot,
}
Expand Down Expand Up @@ -596,18 +619,23 @@ export class FileSystem {
async transaction(
handler: (t: TransactionContext) => Promise<void>,
mutationOptions: MutationOptions = {}
): Promise<{
changes: FileSystemChange[]
dataRoot: CID
}> {
): Promise<
| {
changes: Modification[]
dataRoot: CID
}
| NOOP
> {
const context = this.#transactionContext()

// Execute handler
await handler(context)

// Commit transaction
const { changes, privateNodes, rootTree } =
await TransactionContext.commit(context)
const commitResult = await TransactionContext.commit(context)
if (commitResult === 'no-op') return 'no-op'

const { changes, privateNodes, rootTree } = commitResult

this.#privateNodes = privateNodes
this.#rootTree = rootTree
Expand Down Expand Up @@ -668,6 +696,12 @@ export class FileSystem {
mutationOptions: MutationOptions = {}
): Promise<MutationResult<Partition>> {
const transactionResult = await this.transaction(handler, mutationOptions)

const dataRoot =
transactionResult === 'no-op'
? await this.calculateDataRoot()
: transactionResult.dataRoot

const partition = determinePartition(path)

switch (partition.name) {
Expand All @@ -693,7 +727,7 @@ export class FileSystem {
: capsuleCID

return {
dataRoot: transactionResult.dataRoot,
dataRoot,
capsuleCID,
contentCID,
}
Expand Down Expand Up @@ -733,7 +767,7 @@ export class FileSystem {
.then(([key]) => key)

return {
dataRoot: transactionResult.dataRoot,
dataRoot,
capsuleKey: accessKey.toBytes(),
}
}
Expand All @@ -743,6 +777,7 @@ export class FileSystem {
#transactionContext(): TransactionContext {
return new TransactionContext(
this.#blockstore,
this.#onCommit,
{ ...this.#privateNodes },
this.#rng,
this.#rootTree.clone()
Expand Down
6 changes: 3 additions & 3 deletions packages/nest/src/root-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Blockstore } from 'interface-blockstore'
import type { CID } from 'multiformats'
import type { PrivateForest, PublicDirectory } from 'wnfs'

import type { FileSystemChange } from './types.js'
import type { Modification } from './types.js'

/**
* The tree that ties different file systems together.
Expand All @@ -11,12 +11,12 @@ export abstract class RootTree {
abstract privateForest(): PrivateForest
abstract replacePrivateForest(
forest: PrivateForest,
changes: FileSystemChange[]
changes: Modification[]
): Promise<RootTree>
abstract publicRoot(): PublicDirectory
abstract replacePublicRoot(
dir: PublicDirectory,
changes: FileSystemChange[]
changes: Modification[]
): Promise<RootTree>

abstract clone(): RootTree
Expand Down
6 changes: 3 additions & 3 deletions packages/nest/src/root-tree/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as Unix from '../unix.js'
import * as Version from '../version.js'

import type { RootTree } from '../root-tree.js'
import type { FileSystemChange } from '../types.js'
import type { Modification } from '../types.js'

import { RootBranch } from '../path.js'
import { makeRngInterface } from '../rng.js'
Expand Down Expand Up @@ -150,7 +150,7 @@ export class BasicRootTree implements RootTree {

async replacePrivateForest(
forest: PrivateForest,
_changes: FileSystemChange[]
_changes: Modification[]
): Promise<RootTree> {
return new BasicRootTree({
blockstore: this.#blockstore,
Expand All @@ -169,7 +169,7 @@ export class BasicRootTree implements RootTree {

async replacePublicRoot(
dir: PublicDirectory,
changes: FileSystemChange[]
changes: Modification[]
): Promise<RootTree> {
const treeWithNewPublicRoot = new BasicRootTree({
blockstore: this.#blockstore,
Expand Down
28 changes: 20 additions & 8 deletions packages/nest/src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ import type { RootTree } from './root-tree.js'

import type {
AnySupportedDataType,
CommitVerifier,
DataForType,
DataType,
DirectoryItem,
DirectoryItemWithKind,
MutationType,
NOOP,
} from './types.js'

import type {
Expand All @@ -44,6 +46,7 @@ import type {
/** @group File System */
export class TransactionContext {
readonly #blockstore: Blockstore
readonly #onCommit: CommitVerifier
readonly #rng: Rng

#privateNodes: MountedPrivateNodes
Expand All @@ -57,11 +60,13 @@ export class TransactionContext {
/** @internal */
constructor(
blockstore: Blockstore,
onCommit: CommitVerifier,
privateNodes: MountedPrivateNodes,
rng: Rng,
rootTree: RootTree
) {
this.#blockstore = blockstore
this.#onCommit = onCommit
this.#privateNodes = privateNodes
this.#rng = rng
this.#rootTree = rootTree
Expand All @@ -70,16 +75,23 @@ export class TransactionContext {
}

/** @internal */
static async commit(context: TransactionContext): Promise<{
changes: Array<{
path: Path.Distinctive<Partitioned<Partition>>
type: MutationType
}>
privateNodes: MountedPrivateNodes
rootTree: RootTree
}> {
static async commit(context: TransactionContext): Promise<
| {
changes: Array<{
path: Path.Distinctive<Partitioned<Partition>>
type: MutationType
}>
privateNodes: MountedPrivateNodes
rootTree: RootTree
}
| NOOP
> {
const changes = [...context.#changes]

// Verify
const { commit } = await context.#onCommit([...changes])
if (!commit) return 'no-op'

// Private forest
const newForest = await changes.reduce(
async (oldForestPromise, change): Promise<PrivateForest> => {
Expand Down

0 comments on commit ac1b2c7

Please sign in to comment.