return paths
This commit is contained in:
parent
c9cf3239db
commit
aecbcd1d2c
12 changed files with 20 additions and 136 deletions
|
|
@ -1,9 +0,0 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Like [`DocumentVersion`] but without the `relative_path`.
|
||||
* Used only in create/update responses when the server had to merge the
|
||||
* client's content with a newer remote version and therefore must echo
|
||||
* the merged content back.
|
||||
*/
|
||||
export type DocumentUpdateMergedContent = { vaultUpdateId: number, documentId: string, updatedDate: string, contentBase64: string, isDeleted: boolean, userId: string, deviceId: string, };
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Like [`DocumentVersionWithoutContent`] but without the `relative_path`.
|
||||
* Used only in create/update responses where the client already tracks
|
||||
* the path locally (the server is the source of truth for the
|
||||
* document identity, not its path).
|
||||
*/
|
||||
export type DocumentUpdateMetadata = { vaultUpdateId: number, documentId: string, updatedDate: string, isDeleted: boolean, userId: string, deviceId: string, contentSize: number, };
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DocumentUpdateMergedContent } from "./DocumentUpdateMergedContent";
|
||||
import type { DocumentUpdateMetadata } from "./DocumentUpdateMetadata";
|
||||
import type { DocumentVersion } from "./DocumentVersion";
|
||||
import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutContent";
|
||||
|
||||
/**
|
||||
* Response to a create/update document request.
|
||||
*/
|
||||
export type DocumentUpdateResponse = { "type": "FastForwardUpdate" } & DocumentUpdateMetadata | { "type": "MergingUpdate" } & DocumentUpdateMergedContent;
|
||||
export type DocumentUpdateResponse = { "type": "FastForwardUpdate" } & DocumentVersionWithoutContent | { "type": "MergingUpdate" } & DocumentVersion;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { assertSetContainsExactly } from "../utils/assert-set-contains-exactly";
|
|||
import type { FileSystemOperations } from "./filesystem-operations";
|
||||
import type { TextWithCursors } from "reconcile-text";
|
||||
import type { ServerConfig, ServerConfigData } from "../services/server-config";
|
||||
import { isConflictPath } from "../sync-operations/conflict-path";
|
||||
import { CONFLICT_PATH_REGEX } from "../sync-operations/conflict-path";
|
||||
|
||||
class MockServerConfig implements Pick<ServerConfig, "getConfig"> {
|
||||
public async getConfig(): Promise<ServerConfigData> {
|
||||
|
|
@ -108,7 +108,7 @@ function singleConflictPath(
|
|||
`expected exactly one conflict-path entry, got ${JSON.stringify(conflicts)}`
|
||||
);
|
||||
assert.ok(
|
||||
isConflictPath(conflicts[0]),
|
||||
CONFLICT_PATH_REGEX.test(conflicts[0]),
|
||||
`expected ${conflicts[0]} to match the conflict-path pattern`
|
||||
);
|
||||
return conflicts[0];
|
||||
|
|
@ -195,7 +195,7 @@ describe("File operations", () => {
|
|||
(name) => name !== ".gitignore" && name !== ".config.json"
|
||||
);
|
||||
assert.equal(conflicts.length, 2);
|
||||
assert.ok(conflicts.every(isConflictPath));
|
||||
assert.ok(conflicts.every((c) => CONFLICT_PATH_REGEX.test(c)));
|
||||
assert.ok(conflicts.some((c) => c.endsWith("-.gitignore")));
|
||||
assert.ok(conflicts.some((c) => c.endsWith("-.config.json")));
|
||||
});
|
||||
|
|
@ -209,7 +209,7 @@ describe("File operations", () => {
|
|||
|
||||
const conflicts = Array.from(fs.names).filter((n) => n !== "x");
|
||||
assert.equal(conflicts.length, 2);
|
||||
assert.ok(conflicts.every(isConflictPath));
|
||||
assert.ok(conflicts.every((c) => CONFLICT_PATH_REGEX.test(c)));
|
||||
assert.notEqual(
|
||||
conflicts[0],
|
||||
conflicts[1],
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export class FileOperations {
|
|||
*
|
||||
* If a file is already there, it is moved aside to a `conflict-<uuid>-<name>`
|
||||
* path in the same directory. The sync layer treats conflict-named files
|
||||
* as invisible (see `isConflictPath`), so no events are enqueued and no
|
||||
* as invisible (see `CONFLICT_PATH_REGEX`), so no events are enqueued and no
|
||||
* document records are touched — any pre-existing record or pending
|
||||
* events for the displaced path are left behind for the caller to
|
||||
* overwrite as part of whatever operation prompted the displacement.
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Like [`DocumentVersion`] but without the `relative_path`.
|
||||
* Used only in create/update responses when the server had to merge the
|
||||
* client's content with a newer remote version and therefore must echo
|
||||
* the merged content back.
|
||||
*/
|
||||
export interface DocumentUpdateMergedContent { vaultUpdateId: number, documentId: string, updatedDate: string, contentBase64: string, isDeleted: boolean, userId: string, deviceId: string, }
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Like [`DocumentVersionWithoutContent`] but without the `relative_path`.
|
||||
* Used only in create/update responses where the client already tracks
|
||||
* the path locally (the server is the source of truth for the
|
||||
* document identity, not its path).
|
||||
*/
|
||||
export interface DocumentUpdateMetadata { vaultUpdateId: number, documentId: string, updatedDate: string, isDeleted: boolean, userId: string, deviceId: string, contentSize: number, }
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DocumentUpdateMergedContent } from "./DocumentUpdateMergedContent";
|
||||
import type { DocumentUpdateMetadata } from "./DocumentUpdateMetadata";
|
||||
import type { DocumentVersion } from "./DocumentVersion";
|
||||
import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutContent";
|
||||
|
||||
/**
|
||||
* Response to a create/update document request.
|
||||
*/
|
||||
export type DocumentUpdateResponse = { "type": "FastForwardUpdate" } & DocumentUpdateMetadata | { "type": "MergingUpdate" } & DocumentUpdateMergedContent;
|
||||
export type DocumentUpdateResponse = { "type": "FastForwardUpdate" } & DocumentVersionWithoutContent | { "type": "MergingUpdate" } & DocumentVersion;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { buildConflictFileName, isConflictPath } from "./conflict-path";
|
||||
import { buildConflictFileName, CONFLICT_PATH_REGEX } from "./conflict-path";
|
||||
|
||||
describe("buildConflictFileName", () => {
|
||||
it("truncates to the filesystem byte limit while preserving the extension", () => {
|
||||
|
|
@ -59,20 +59,20 @@ describe("buildConflictFileName", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("isConflictPath", () => {
|
||||
describe("CONFLICT_PATH_REGEX", () => {
|
||||
it("does not misclassify user-authored names that start with `conflict-`", () => {
|
||||
assert.strictEqual(isConflictPath("conflict-resolution.md"), false);
|
||||
assert.strictEqual(CONFLICT_PATH_REGEX.test("conflict-resolution.md"), false);
|
||||
});
|
||||
|
||||
it("only inspects the final path segment", () => {
|
||||
assert.strictEqual(
|
||||
isConflictPath(
|
||||
CONFLICT_PATH_REGEX.test(
|
||||
"conflict-12345678-1234-1234-1234-123456789abc-x/note.md"
|
||||
),
|
||||
false
|
||||
);
|
||||
assert.strictEqual(
|
||||
isConflictPath(
|
||||
CONFLICT_PATH_REGEX.test(
|
||||
"a/b/conflict-12345678-1234-1234-1234-123456789abc-note.md"
|
||||
),
|
||||
true
|
||||
|
|
@ -80,6 +80,6 @@ describe("isConflictPath", () => {
|
|||
});
|
||||
|
||||
it("round-trips with buildConflictFileName", () => {
|
||||
assert.strictEqual(isConflictPath(buildConflictFileName("note.md")), true);
|
||||
assert.strictEqual(CONFLICT_PATH_REGEX.test(buildConflictFileName("note.md")), true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import type { RelativePath } from "./types";
|
||||
|
||||
// Local-only files displaced by `FileOperations.ensureClearPath` are named
|
||||
// `conflict-<uuid>-<originalName>`. The UUID is a full RFC-4122 v4 value so
|
||||
// a user-authored filename that happens to start with `conflict-` doesn't
|
||||
|
|
@ -55,14 +53,3 @@ function truncateFileNameToByteLimit(
|
|||
}
|
||||
return truncatedStem + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is `path`'s final segment a conflict-displaced filename?
|
||||
*
|
||||
* Any sync code that would otherwise create/update/delete/sync the path
|
||||
* should short-circuit when this returns true: conflict-displaced files are
|
||||
* strictly local and must stay invisible to the server.
|
||||
*/
|
||||
export function isConflictPath(path: RelativePath): boolean {
|
||||
return CONFLICT_PATH_REGEX.test(path);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue