SnapshotDelta.ts overview
Opaque portable snapshot deltas and their public inspection models.
A delta names its base and target snapshots by their semantic identities and holds one change per path that differs, each carrying only the node it leaves behind, so its size follows what changed rather than the tree.
Since v0.1.0
models
MergeConflict (type alias)
One conflict between two snapshot deltas from the same base.
Signature
export type MergeConflict = {
readonly path: BytePath
readonly reason:
| "BothChanged"
| "ChangedRemoved"
| "BothAddedDifferent"
| "KindDiverged"
| "ParentRemoved"
| "HardLinkGroupDiverged"
readonly ours:
| { readonly _tag: "Added"; readonly kind: "file" | "directory" | "symlink" }
| { readonly _tag: "Removed"; readonly kind: "file" | "directory" | "symlink" }
| {
readonly _tag: "Updated"
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}
| { readonly _tag: "Unchanged" }
readonly theirs:
| { readonly _tag: "Added"; readonly kind: "file" | "directory" | "symlink" }
| { readonly _tag: "Removed"; readonly kind: "file" | "directory" | "symlink" }
| {
readonly _tag: "Updated"
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}
| { readonly _tag: "Unchanged" }
}Since v0.8.0
MergeConflictReason (type alias)
The reason a path-level merge reports a conflict.
Signature
export type MergeConflictReason =
| "BothChanged"
| "ChangedRemoved"
| "BothAddedDifferent"
| "KindDiverged"
| "ParentRemoved"
| "HardLinkGroupDiverged"Since v0.8.0
MergeOptions (type alias)
Snapshot-delta merge options.
Signature
export type MergeOptions = {
readonly limits?: {
readonly maxEncodedBytes: ByteSize.ByteSize
readonly maxIdentityBytes: ByteSize.ByteSize
readonly maxDeltaRecords: number
readonly maxDecodedDeltaBytes: ByteSize.ByteSize
readonly maxBaseRecords: number
readonly maxTargetRecords: number
readonly maxEntries: number
readonly maxOutputRecords: number
readonly maxOutputBytes: ByteSize.ByteSize
readonly maxInheritedRecords: number
}
readonly resolutions?: readonly {
readonly path: BytePath
readonly take: "ours" | "theirs" | "base"
}[]
}Since v0.8.0
MergeResolution (type alias)
One resolution of a reported merge conflict.
Signature
export type MergeResolution = { readonly path: BytePath; readonly take: "ours" | "theirs" | "base" }Since v0.8.0
MergeResult (type alias)
The result of merging two snapshot deltas.
Signature
export type MergeResult = {
readonly delta: SnapshotDelta
readonly conflicts: readonly {
readonly path: BytePath
readonly reason:
| "BothChanged"
| "ChangedRemoved"
| "BothAddedDifferent"
| "KindDiverged"
| "ParentRemoved"
| "HardLinkGroupDiverged"
readonly ours:
| { readonly _tag: "Added"; readonly kind: "file" | "directory" | "symlink" }
| { readonly _tag: "Removed"; readonly kind: "file" | "directory" | "symlink" }
| {
readonly _tag: "Updated"
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}
| { readonly _tag: "Unchanged" }
readonly theirs:
| { readonly _tag: "Added"; readonly kind: "file" | "directory" | "symlink" }
| { readonly _tag: "Removed"; readonly kind: "file" | "directory" | "symlink" }
| {
readonly _tag: "Updated"
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}
| { readonly _tag: "Unchanged" }
}[]
}Since v0.8.0
MergeSideChange (type alias)
What one side of a merge did at a conflicted path.
Signature
export type MergeSideChange =
| { readonly _tag: "Added"; readonly kind: "file" | "directory" | "symlink" }
| { readonly _tag: "Removed"; readonly kind: "file" | "directory" | "symlink" }
| {
readonly _tag: "Updated"
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}
| { readonly _tag: "Unchanged" }Since v0.8.0
MergeTake (type alias)
The side a merge takes at one conflicted path.
Signature
export type MergeTake = "ours" | "theirs" | "base"Since v0.8.0
SnapshotChange (type alias)
A path-oriented semantic difference between two snapshots.
Signature
export type SnapshotChange =
| {
readonly _tag: "Added"
readonly path: BytePath
readonly kind: "file" | "directory" | "symlink"
}
| {
readonly _tag: "Removed"
readonly path: BytePath
readonly kind: "file" | "directory" | "symlink"
}
| {
readonly _tag: "Updated"
readonly path: BytePath
readonly beforeKind: "file" | "directory" | "symlink"
readonly afterKind: "file" | "directory" | "symlink"
readonly differences: readonly (
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"
)[]
}Since v0.1.0
SnapshotChangesOptions (type alias)
Filtering options for snapshot-delta inspection.
Signature
export type SnapshotChangesOptions = { readonly includeTimestamps?: boolean }Since v0.1.0
SnapshotDelta (interface)
An immutable, opaque description of the exact difference between two snapshots.
Signature
export interface SnapshotDelta {
readonly [SnapshotDeltaTypeId]: SnapshotDeltaTypeId
}Since v0.1.0
SnapshotDeltaLimits (type alias)
Resource limits shared by delta creation, inspection, encoding, decoding, and application.
Signature
export type SnapshotDeltaLimits = {
readonly maxEncodedBytes: ByteSize.ByteSize
readonly maxIdentityBytes: ByteSize.ByteSize
readonly maxDeltaRecords: number
readonly maxDecodedDeltaBytes: ByteSize.ByteSize
readonly maxBaseRecords: number
readonly maxTargetRecords: number
readonly maxEntries: number
readonly maxOutputRecords: number
readonly maxOutputBytes: ByteSize.ByteSize
readonly maxInheritedRecords: number
}Since v0.1.0
SnapshotDifference (type alias)
A semantic field that differs between two snapshots.
Signature
export type SnapshotDifference =
| "uid"
| "gid"
| "kind"
| "mode"
| "atimeNs"
| "mtimeNs"
| "ctimeNs"
| "birthtimeNs"
| "content"
| "target"
| "hardLinks"Since v0.1.0
SnapshotNodeKind (type alias)
A filesystem entry kind reported by snapshot inspection.
Signature
export type SnapshotNodeKind = "file" | "directory" | "symlink"Since v0.1.0
schemas
MergeConflict
Schema for one conflict between two snapshot deltas from the same base.
A conflict carries what each side did, as kinds and changed fields, and no
payload bytes or metadata values. The application reads those from the base,
ours, and theirs snapshots it already holds. Timestamp fields are left out of
differences, because timestamps never conflict.
Signature
declare const MergeConflict: Schema.Struct<{
readonly path: Schema.declare<BytePath, BytePath>
readonly reason: Schema.Literals<
readonly [
"BothChanged",
"ChangedRemoved",
"BothAddedDifferent",
"KindDiverged",
"ParentRemoved",
"HardLinkGroupDiverged"
]
>
readonly ours: Schema.TaggedUnion<{
readonly Added: Schema.TaggedStruct<
"Added",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Removed: Schema.TaggedStruct<
"Removed",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Updated: Schema.TaggedStruct<
"Updated",
{
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
readonly Unchanged: Schema.TaggedStruct<"Unchanged", {}>
}>
readonly theirs: Schema.TaggedUnion<{
readonly Added: Schema.TaggedStruct<
"Added",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Removed: Schema.TaggedStruct<
"Removed",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Updated: Schema.TaggedStruct<
"Updated",
{
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
readonly Unchanged: Schema.TaggedStruct<"Unchanged", {}>
}>
}>Since v0.8.0
MergeConflictReason
Schema for the reasons a path-level merge reports a conflict.
BothChanged: both sides changed the same field of one node to different values.ChangedRemoved: one side changed a node the other side removed, or linked a new name to it.BothAddedDifferent: both sides added different nodes at one path.KindDiverged: one side changed a node's kind while the other side changed or removed the node.ParentRemoved: one side changed a path under a directory the other side removed or replaced.HardLinkGroupDiverged: both sides changed which names share one hard-linked node.
Signature
declare const MergeConflictReason: Schema.Literals<
readonly [
"BothChanged",
"ChangedRemoved",
"BothAddedDifferent",
"KindDiverged",
"ParentRemoved",
"HardLinkGroupDiverged"
]
>Since v0.8.0
MergeOptions
Schema for snapshot-delta merge options.
Signature
declare const MergeOptions: Schema.Struct<{
readonly resolutions: Schema.optionalKey<
Schema.$Array<
Schema.Struct<{
readonly path: Schema.declare<BytePath, BytePath>
readonly take: Schema.Literals<readonly ["ours", "theirs", "base"]>
}>
>
>
readonly limits: Schema.optionalKey<
Schema.Struct<{
readonly maxEncodedBytes: Schema.ByteSize
readonly maxIdentityBytes: Schema.ByteSize
readonly maxDeltaRecords: Schema.Natural
readonly maxDecodedDeltaBytes: Schema.ByteSize
readonly maxBaseRecords: Schema.Natural
readonly maxTargetRecords: Schema.Natural
readonly maxEntries: Schema.Natural
readonly maxOutputRecords: Schema.Natural
readonly maxOutputBytes: Schema.ByteSize
readonly maxInheritedRecords: Schema.Natural
}>
>
}>Since v0.8.0
MergeResolution
Schema for one resolution of a reported merge conflict.
The path must be one a previous merge of the same inputs reported. Conflicts that span several paths, a removed directory's subtree or a hard-link group, take one side as a whole: every reported path in the set must name the same side.
Signature
declare const MergeResolution: Schema.Struct<{
readonly path: Schema.declare<BytePath, BytePath>
readonly take: Schema.Literals<readonly ["ours", "theirs", "base"]>
}>Since v0.8.0
MergeResult
Schema for the result of merging two snapshot deltas.
The delta applies every change neither side disputed, plus every resolved
conflict; paths still in conflict keep the base node. It is bound to the
same base as its inputs and applies with applySnapshotDelta.
Signature
declare const MergeResult: Schema.Struct<{
readonly delta: Schema.declare<SnapshotDelta, SnapshotDelta>
readonly conflicts: Schema.$Array<
Schema.Struct<{
readonly path: Schema.declare<BytePath, BytePath>
readonly reason: Schema.Literals<
readonly [
"BothChanged",
"ChangedRemoved",
"BothAddedDifferent",
"KindDiverged",
"ParentRemoved",
"HardLinkGroupDiverged"
]
>
readonly ours: Schema.TaggedUnion<{
readonly Added: Schema.TaggedStruct<
"Added",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Removed: Schema.TaggedStruct<
"Removed",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Updated: Schema.TaggedStruct<
"Updated",
{
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
readonly Unchanged: Schema.TaggedStruct<"Unchanged", {}>
}>
readonly theirs: Schema.TaggedUnion<{
readonly Added: Schema.TaggedStruct<
"Added",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Removed: Schema.TaggedStruct<
"Removed",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Updated: Schema.TaggedStruct<
"Updated",
{
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
readonly Unchanged: Schema.TaggedStruct<"Unchanged", {}>
}>
}>
>
}>Since v0.8.0
MergeSideChange
Schema for what one side of a merge did at a conflicted path.
Unchanged means the side did not touch the path itself; a conflict can
still name it when the other side's change depends on it, for example a
hard-link group whose membership diverged, or a name held at base because
it shares a node with a conflicted name.
Signature
declare const MergeSideChange: Schema.TaggedUnion<{
readonly Added: Schema.TaggedStruct<
"Added",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Removed: Schema.TaggedStruct<
"Removed",
{ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]> }
>
readonly Updated: Schema.TaggedStruct<
"Updated",
{
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
readonly Unchanged: Schema.TaggedStruct<"Unchanged", {}>
}>Since v0.8.0
MergeTake
Schema for the side a merge takes at one conflicted path.
Signature
declare const MergeTake: Schema.Literals<readonly ["ours", "theirs", "base"]>Since v0.8.0
SnapshotChange
Schema for a path-oriented difference between two snapshots.
Independent snapshots do not preserve shared lineage, so moves are reported as a removal and an addition rather than an inferred rename.
Example
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const volume = yield* Vfs.make()
const caller = yield* volume.caller()
yield* caller.writeFile("/f", new Uint8Array([1]), {
access: "write",
create: "exclusive"
})
const base = yield* volume.snapshot
yield* caller.writeFile("/f", new Uint8Array([2, 3]), { access: "write", truncate: true })
yield* caller.chmod("/f", 0o600)
const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
const changes = yield* Vfs.inspectSnapshotDelta(base, delta)
// `Updated` carries which fields moved; `Added` and `Removed` carry a kind.
const change = changes[0]!
return change._tag === "Updated" ? change.differences : change.kind
}).pipe(Effect.provide(NodeCrypto.layer))
Effect.runPromise(program).then(console.log)
// [ 'content', 'mode' ]Signature
declare const SnapshotChange: Schema.Union<
readonly [
Schema.TaggedStruct<
"Added",
{
readonly path: Schema.declare<BytePath, BytePath>
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>
}
>,
Schema.TaggedStruct<
"Removed",
{
readonly path: Schema.declare<BytePath, BytePath>
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>
}
>,
Schema.TaggedStruct<
"Updated",
{
readonly path: Schema.declare<BytePath, BytePath>
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>
readonly differences: Schema.$Array<
Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>
>
}
>
]
>Since v0.1.0
SnapshotChangesOptions
Schema for snapshot-delta inspection options.
Example
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const volume = yield* Vfs.make()
const caller = yield* volume.caller()
const base = yield* volume.snapshot
yield* caller.mkdir("/work")
const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
// Timestamps are excluded by default, since they change on every write.
const plain = yield* Vfs.inspectSnapshotDelta(base, delta)
const timed = yield* Vfs.inspectSnapshotDelta(base, delta, { includeTimestamps: true })
return [plain.length, timed.length]
}).pipe(Effect.provide(NodeCrypto.layer))
Effect.runPromise(program).then(console.log)
// [ 1, 2 ]Signature
declare const SnapshotChangesOptions: Schema.Struct<{ readonly includeTimestamps: Schema.optionalKey<Schema.Boolean> }>Since v0.1.0
SnapshotDelta
Schema for an already validated opaque snapshot delta.
Signature
declare const SnapshotDelta: Schema.declare<SnapshotDelta, SnapshotDelta>Since v0.1.0
SnapshotDeltaLimits
Schema for a complete snapshot-delta resource policy, with frozen presets.
The constrained preset is intended for memory-sensitive environments. The default preset permits larger snapshots and payloads while remaining finite.
Example
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const volume = yield* Vfs.make()
const base = yield* volume.snapshot
yield* (yield* volume.caller()).mkdir("/work")
// Omitting limits uses `default`; pass `constrained` where memory is tight.
return yield* Vfs.diffSnapshots(base, yield* volume.snapshot, Vfs.SnapshotDeltaLimits.constrained)
}).pipe(Effect.provide(NodeCrypto.layer))Signature
declare const SnapshotDeltaLimits: Schema.Struct<{
readonly maxEncodedBytes: Schema.ByteSize
readonly maxIdentityBytes: Schema.ByteSize
readonly maxDeltaRecords: Schema.Natural
readonly maxDecodedDeltaBytes: Schema.ByteSize
readonly maxBaseRecords: Schema.Natural
readonly maxTargetRecords: Schema.Natural
readonly maxEntries: Schema.Natural
readonly maxOutputRecords: Schema.Natural
readonly maxOutputBytes: Schema.ByteSize
readonly maxInheritedRecords: Schema.Natural
}> & {
constrained: {
readonly maxEncodedBytes: ByteSize.ByteSize
readonly maxIdentityBytes: ByteSize.ByteSize
readonly maxDeltaRecords: number
readonly maxDecodedDeltaBytes: ByteSize.ByteSize
readonly maxBaseRecords: number
readonly maxTargetRecords: number
readonly maxEntries: number
readonly maxOutputRecords: number
readonly maxOutputBytes: ByteSize.ByteSize
readonly maxInheritedRecords: number
}
default: {
readonly maxEncodedBytes: ByteSize.ByteSize
readonly maxIdentityBytes: ByteSize.ByteSize
readonly maxDeltaRecords: number
readonly maxDecodedDeltaBytes: ByteSize.ByteSize
readonly maxBaseRecords: number
readonly maxTargetRecords: number
readonly maxEntries: number
readonly maxOutputRecords: number
readonly maxOutputBytes: ByteSize.ByteSize
readonly maxInheritedRecords: number
}
}Since v0.1.0
SnapshotDifference
Schema for semantic fields that can differ between snapshots.
Signature
declare const SnapshotDifference: Schema.Literals<
readonly [
"kind",
"content",
"target",
"hardLinks",
"mode",
"uid",
"gid",
"atimeNs",
"mtimeNs",
"ctimeNs",
"birthtimeNs"
]
>Since v0.1.0
SnapshotNodeKind
Schema for filesystem entry kinds reported by snapshot inspection.
Signature
declare const SnapshotNodeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>Since v0.1.0
type IDs
SnapshotDeltaTypeId
Type identifier for opaque snapshot deltas.
Signature
declare const SnapshotDeltaTypeId: "@effect-vfs/core/SnapshotDelta"Since v0.1.0
SnapshotDeltaTypeId (type alias)
Type identifier for opaque snapshot deltas.
Signature
export type SnapshotDeltaTypeId = "@effect-vfs/core/SnapshotDelta"Since v0.1.0