@poe-platform/safe-fs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +290 -0
  3. package/dist/safe-fs/bridge/confinement.d.ts +3 -0
  4. package/dist/safe-fs/bridge/confinement.js +171 -0
  5. package/dist/safe-fs/bridge/filesystem.d.ts +88 -0
  6. package/dist/safe-fs/bridge/filesystem.js +411 -0
  7. package/dist/safe-fs/bridge/index.d.ts +6 -0
  8. package/dist/safe-fs/bridge/index.js +18 -0
  9. package/dist/safe-fs/bridge/stats.d.ts +4 -0
  10. package/dist/safe-fs/bridge/stats.js +37 -0
  11. package/dist/safe-fs/bridge/types.d.ts +75 -0
  12. package/dist/safe-fs/bridge/types.js +1 -0
  13. package/dist/safe-fs/bridge/values.d.ts +6 -0
  14. package/dist/safe-fs/bridge/values.js +65 -0
  15. package/dist/safe-fs/config/memory.d.ts +3 -0
  16. package/dist/safe-fs/config/memory.js +9 -0
  17. package/dist/safe-fs/config/real.d.ts +5 -0
  18. package/dist/safe-fs/config/real.js +16 -0
  19. package/dist/safe-fs/config.d.ts +15 -0
  20. package/dist/safe-fs/config.js +46 -0
  21. package/dist/safe-fs/config.node.d.ts +2 -0
  22. package/dist/safe-fs/config.node.js +22 -0
  23. package/dist/safe-fs/contracts/abort.d.ts +5 -0
  24. package/dist/safe-fs/contracts/abort.js +34 -0
  25. package/dist/safe-fs/contracts/cleanup.d.ts +1 -0
  26. package/dist/safe-fs/contracts/cleanup.js +9 -0
  27. package/dist/safe-fs/contracts/errors.d.ts +50 -0
  28. package/dist/safe-fs/contracts/errors.js +77 -0
  29. package/dist/safe-fs/contracts/filesystem.d.ts +96 -0
  30. package/dist/safe-fs/contracts/filesystem.js +1 -0
  31. package/dist/safe-fs/contracts/index.d.ts +4 -0
  32. package/dist/safe-fs/contracts/index.js +4 -0
  33. package/dist/safe-fs/contracts/io.d.ts +8 -0
  34. package/dist/safe-fs/contracts/io.js +92 -0
  35. package/dist/safe-fs/contracts/path.d.ts +8 -0
  36. package/dist/safe-fs/contracts/path.js +8 -0
  37. package/dist/safe-fs/contracts/portable-path.d.ts +13 -0
  38. package/dist/safe-fs/contracts/portable-path.js +57 -0
  39. package/dist/safe-fs/contracts/virtual-path.d.ts +7 -0
  40. package/dist/safe-fs/contracts/virtual-path.js +66 -0
  41. package/dist/safe-fs/core.d.ts +12 -0
  42. package/dist/safe-fs/core.js +12 -0
  43. package/dist/safe-fs/fs/memory/index.d.ts +58 -0
  44. package/dist/safe-fs/fs/memory/index.js +532 -0
  45. package/dist/safe-fs/fs/mount/comparison.d.ts +19 -0
  46. package/dist/safe-fs/fs/mount/comparison.js +118 -0
  47. package/dist/safe-fs/fs/mount/identity.d.ts +2 -0
  48. package/dist/safe-fs/fs/mount/identity.js +12 -0
  49. package/dist/safe-fs/fs/mount/index.d.ts +44 -0
  50. package/dist/safe-fs/fs/mount/index.js +546 -0
  51. package/dist/safe-fs/fs/overlay/index.d.ts +73 -0
  52. package/dist/safe-fs/fs/overlay/index.js +927 -0
  53. package/dist/safe-fs/fs/readonly/index.d.ts +31 -0
  54. package/dist/safe-fs/fs/readonly/index.js +128 -0
  55. package/dist/safe-fs/fs/real/allocation.d.ts +1 -0
  56. package/dist/safe-fs/fs/real/allocation.js +8 -0
  57. package/dist/safe-fs/fs/real/index.d.ts +76 -0
  58. package/dist/safe-fs/fs/real/index.js +543 -0
  59. package/dist/safe-fs/fs/s3/authority.d.ts +7 -0
  60. package/dist/safe-fs/fs/s3/authority.js +31 -0
  61. package/dist/safe-fs/fs/s3/filesystem.d.ts +93 -0
  62. package/dist/safe-fs/fs/s3/filesystem.js +1024 -0
  63. package/dist/safe-fs/fs/s3/http/index.d.ts +2 -0
  64. package/dist/safe-fs/fs/s3/http/index.js +1 -0
  65. package/dist/safe-fs/fs/s3/http/request.d.ts +16 -0
  66. package/dist/safe-fs/fs/s3/http/request.js +196 -0
  67. package/dist/safe-fs/fs/s3/http/signature.d.ts +20 -0
  68. package/dist/safe-fs/fs/s3/http/signature.js +74 -0
  69. package/dist/safe-fs/fs/s3/http/transport.d.ts +3 -0
  70. package/dist/safe-fs/fs/s3/http/transport.js +455 -0
  71. package/dist/safe-fs/fs/s3/http/types.d.ts +30 -0
  72. package/dist/safe-fs/fs/s3/http/types.js +1 -0
  73. package/dist/safe-fs/fs/s3/http/xml.d.ts +11 -0
  74. package/dist/safe-fs/fs/s3/http/xml.js +188 -0
  75. package/dist/safe-fs/fs/s3/index.d.ts +6 -0
  76. package/dist/safe-fs/fs/s3/index.js +3 -0
  77. package/dist/safe-fs/fs/s3/mock.d.ts +48 -0
  78. package/dist/safe-fs/fs/s3/mock.js +233 -0
  79. package/dist/safe-fs/fs/s3/registry.d.ts +10 -0
  80. package/dist/safe-fs/fs/s3/registry.js +71 -0
  81. package/dist/safe-fs/fs/s3/transport.d.ts +103 -0
  82. package/dist/safe-fs/fs/s3/transport.js +28 -0
  83. package/dist/safe-fs/fs/webdav/index.d.ts +2 -0
  84. package/dist/safe-fs/fs/webdav/index.js +1 -0
  85. package/dist/safe-fs/fs/webdav/resource-id.d.ts +18 -0
  86. package/dist/safe-fs/fs/webdav/resource-id.js +109 -0
  87. package/dist/safe-fs/fs/webdav/webdav.d.ts +101 -0
  88. package/dist/safe-fs/fs/webdav/webdav.js +1226 -0
  89. package/dist/safe-fs/fs/webdav/xml.d.ts +15 -0
  90. package/dist/safe-fs/fs/webdav/xml.js +250 -0
  91. package/dist/safe-fs/index.d.ts +14 -0
  92. package/dist/safe-fs/index.js +12 -0
  93. package/dist/safe-fs/node/filesystem.d.ts +11 -0
  94. package/dist/safe-fs/node/filesystem.js +28 -0
  95. package/dist/safe-fs/node/index.d.ts +2 -0
  96. package/dist/safe-fs/node/index.js +1 -0
  97. package/dist/safe-fs/node/stats.d.ts +1 -0
  98. package/dist/safe-fs/node/stats.js +1 -0
  99. package/dist/safe-fs/node/values.d.ts +1 -0
  100. package/dist/safe-fs/node/values.js +1 -0
  101. package/dist/safe-fs/node-host.d.ts +1 -0
  102. package/dist/safe-fs/node-host.js +1 -0
  103. package/dist/safe-fs/node-unavailable.d.ts +1 -0
  104. package/dist/safe-fs/node-unavailable.js +1 -0
  105. package/dist/safe-fs/platform/browser.d.ts +12 -0
  106. package/dist/safe-fs/platform/browser.js +39 -0
  107. package/dist/safe-fs/platform/node.d.ts +13 -0
  108. package/dist/safe-fs/platform/node.js +26 -0
  109. package/package.json +108 -0
@@ -0,0 +1,28 @@
1
+ export function createS3Transport(client, capabilities = {}) {
2
+ const transport = {
3
+ capabilities: Object.freeze({ ...capabilities }),
4
+ ...(client.getObjectStream ? { getObjectStream: (input, options) => client.getObjectStream(input, options) } : {}),
5
+ ...(client.putObjectStream ? { putObjectStream: (input, options) => client.putObjectStream(input, options) } : {}),
6
+ headObject: (input, options) => client.headObject(input, options),
7
+ getObject: (input, options) => client.getObject(input, options),
8
+ putObject: (input, options) => client.putObject(input, options),
9
+ deleteObject: (input, options) => client.deleteObject(input, options),
10
+ copyObject: (input, options) => client.copyObject(input, options),
11
+ listObjectsV2: (input, options) => client.listObjectsV2(input, options),
12
+ };
13
+ return transport;
14
+ }
15
+ export class S3ServiceError extends Error {
16
+ code;
17
+ $metadata;
18
+ constructor(code, status, message = code) {
19
+ super(message);
20
+ this.name = code;
21
+ this.code = code;
22
+ this.$metadata = { httpStatusCode: status };
23
+ }
24
+ }
25
+ export function encodeCopySource(bucket, key) {
26
+ return [bucket, ...key.split("/")].map((part) => encodeURIComponent(part)
27
+ .replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)).join("/");
28
+ }
@@ -0,0 +1,2 @@
1
+ export { WebDavFileSystem } from "./webdav.js";
2
+ export type { WebDavFileSystemOptions, WebDavFetch, WebDavAtomicEmptyDirectoryBinding, WebDavAtomicEmptyDirectoryRequest, WebDavAtomicEmptyDirectoryResult, } from "./webdav.js";
@@ -0,0 +1 @@
1
+ export { WebDavFileSystem } from "./webdav.js";
@@ -0,0 +1,18 @@
1
+ import type { FileStat, FileSystem, FsOptions } from "../../contracts/filesystem.js";
2
+ import type { EntryAuthority, EntryView } from "../mount/comparison.js";
3
+ export interface OwnedWebDavEntry {
4
+ readonly storage: object;
5
+ readonly resource: object;
6
+ }
7
+ export interface OwnedWebDavResource extends OwnedWebDavEntry {
8
+ readonly identifier: string;
9
+ }
10
+ export declare function registerOwnedResourceResponse(response: Response, entries: ReadonlyMap<string, OwnedWebDavResource>): void;
11
+ export declare function recordOwnedResourceStat(response: Response, filesystem: FileSystem, path: string, stat: FileStat): void;
12
+ export declare function getOwnedWebDavEntry(view: EntryView): OwnedWebDavEntry | undefined;
13
+ export declare function ownedResponseIdentifier(response: Response, path: string): string | undefined;
14
+ type ResourceQuery = (path: string, options: FsOptions) => Promise<string | undefined>;
15
+ export declare function registerResourceQuery(filesystem: FileSystem, query: ResourceQuery, baseComparison: NonNullable<FileSystem["compareEntry"]>, callback?: NonNullable<FileSystem["compareEntry"]>): void;
16
+ export declare function resourceIdentifier(value: string): string;
17
+ export declare const compareWebDavResources: EntryAuthority;
18
+ export {};
@@ -0,0 +1,109 @@
1
+ import { FsError } from "../../contracts/errors.js";
2
+ import { assertCallbackAuthorityAllowed } from "../mount/comparison.js";
3
+ const responses = new WeakMap();
4
+ const observations = new WeakMap();
5
+ export function registerOwnedResourceResponse(response, entries) {
6
+ if (responses.has(response))
7
+ throw new TypeError("resource response already registered");
8
+ responses.set(response, new Map(entries));
9
+ }
10
+ export function recordOwnedResourceStat(response, filesystem, path, stat) {
11
+ const entry = responses.get(response)?.get(path);
12
+ if (entry)
13
+ observations.set(stat, { filesystem, path, entry });
14
+ }
15
+ export function getOwnedWebDavEntry(view) {
16
+ const observation = observations.get(view.stat);
17
+ return observation?.filesystem === view.filesystem && observation.path === view.path
18
+ ? observation.entry : undefined;
19
+ }
20
+ export function ownedResponseIdentifier(response, path) {
21
+ return responses.get(response)?.get(path)?.identifier;
22
+ }
23
+ const queries = new WeakMap();
24
+ const comparisons = new WeakMap();
25
+ const callbacks = new WeakMap();
26
+ export function registerResourceQuery(filesystem, query, baseComparison, callback) {
27
+ comparisons.set(filesystem, baseComparison);
28
+ if (callback)
29
+ callbacks.set(filesystem, callback);
30
+ queries.set(filesystem, async (path, options) => {
31
+ options.signal?.throwIfAborted();
32
+ const identifier = await query(path, options);
33
+ options.signal?.throwIfAborted();
34
+ return identifier;
35
+ });
36
+ }
37
+ export function resourceIdentifier(value) {
38
+ if (!/^[A-Za-z][A-Za-z0-9+.-]*:[^\s<>"{}|\\^`]+$/.test(value) || /%(?![0-9A-Fa-f]{2})/.test(value)
39
+ || /[^\x21-\x7e]/.test(value))
40
+ throw new Error("invalid DAV:resource-id URI");
41
+ new URL(value);
42
+ if (/^urn:uuid:/i.test(value)) {
43
+ if (!/^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {
44
+ throw new Error("invalid DAV:resource-id UUID");
45
+ }
46
+ return value.toLowerCase();
47
+ }
48
+ return value;
49
+ }
50
+ export const compareWebDavResources = async (own, peer, options) => {
51
+ options.signal?.throwIfAborted();
52
+ let explicit = false;
53
+ let answer = "unknown";
54
+ const visited = new Set();
55
+ for (const [left, right] of [[own, peer], [peer, own]]) {
56
+ const baseComparison = comparisons.get(left.filesystem);
57
+ if (!baseComparison)
58
+ continue;
59
+ const method = left.filesystem.compareEntry;
60
+ const comparison = method === baseComparison ? callbacks.get(left.filesystem) : method;
61
+ if (method === baseComparison && !comparison)
62
+ continue;
63
+ explicit = true;
64
+ if (!comparison || visited.has(left.filesystem))
65
+ continue;
66
+ visited.add(left.filesystem);
67
+ options.signal?.throwIfAborted();
68
+ assertCallbackAuthorityAllowed();
69
+ const result = await comparison.call(left.filesystem, left.path, right.filesystem, right.path, options);
70
+ options.signal?.throwIfAborted();
71
+ if (result !== "same" && result !== "distinct" && result !== "unknown") {
72
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "invalid explicit WebDAV comparison" });
73
+ }
74
+ if (result === "unknown")
75
+ continue;
76
+ if (answer !== "unknown" && answer !== result) {
77
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting explicit WebDAV comparisons" });
78
+ }
79
+ answer = result;
80
+ }
81
+ const builtin = await compareProtocolEntries(own, peer, options);
82
+ if (builtin === "same" && answer === "distinct") {
83
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "explicit WebDAV comparison contradicts built-in identity" });
84
+ }
85
+ if (builtin === "same")
86
+ return "same";
87
+ return explicit ? answer : builtin;
88
+ };
89
+ async function compareProtocolEntries(own, peer, options) {
90
+ const left = getOwnedWebDavEntry(own);
91
+ const right = getOwnedWebDavEntry(peer);
92
+ const ownQuery = queries.get(own.filesystem);
93
+ const peerQuery = queries.get(peer.filesystem);
94
+ if (!ownQuery || !peerQuery)
95
+ return "unknown";
96
+ options.signal?.throwIfAborted();
97
+ const ownId = await ownQuery(own.path, options);
98
+ options.signal?.throwIfAborted();
99
+ const peerId = await peerQuery(peer.path, options);
100
+ options.signal?.throwIfAborted();
101
+ if (ownId === undefined || peerId === undefined)
102
+ return "unknown";
103
+ if (left && right) {
104
+ const same = left.storage === right.storage && left.resource === right.resource;
105
+ if (same !== (ownId === peerId))
106
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting owned and protocol WebDAV identities" });
107
+ }
108
+ return ownId === peerId ? "same" : "distinct";
109
+ }
@@ -0,0 +1,101 @@
1
+ import type { PlatformComparisonCallback } from "#safe-fs-platform";
2
+ import type { ByteSource } from "../../contracts/io.js";
3
+ import type { AppendFileOptions, CopyFileOptions, DirectoryEntry, EntryComparison, FileStat, FileSystem, FsOptions, MkdirOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions } from "../../contracts/filesystem.js";
4
+ export type WebDavFetch = (url: string, init: RequestInit) => Promise<Response>;
5
+ export interface WebDavAtomicEmptyDirectoryRequest {
6
+ readonly operation: "atomic-empty-rmdir/v1";
7
+ readonly namespaceUrl: string;
8
+ readonly path: string;
9
+ readonly signal?: AbortSignal;
10
+ }
11
+ export interface WebDavAtomicEmptyDirectoryResult {
12
+ readonly operation: "atomic-empty-rmdir/v1";
13
+ readonly namespaceUrl: string;
14
+ readonly path: string;
15
+ readonly outcome: "removed";
16
+ }
17
+ export interface WebDavAtomicEmptyDirectoryBinding {
18
+ readonly namespaceUrl: string;
19
+ readonly removeEmptyDirectory: (request: WebDavAtomicEmptyDirectoryRequest) => Promise<WebDavAtomicEmptyDirectoryResult>;
20
+ }
21
+ export interface WebDavFileSystemOptions {
22
+ readonly baseUrl: string;
23
+ readonly fetch: WebDavFetch;
24
+ readonly requestStreamSupport?: "native" | boolean;
25
+ readonly headers?: Readonly<Record<string, string>>;
26
+ readonly maxResponseBytes?: number;
27
+ readonly maxXmlBytes?: number;
28
+ readonly maxEntries?: number;
29
+ readonly timeoutMs?: number;
30
+ readonly overwritePolicy?: "lock" | "etag";
31
+ readonly atomicEmptyDirectory?: WebDavAtomicEmptyDirectoryBinding;
32
+ readonly compareEntry?: PlatformComparisonCallback<(this: FileSystem, ...args: Parameters<NonNullable<FileSystem["compareEntry"]>>) => ReturnType<NonNullable<FileSystem["compareEntry"]>>>;
33
+ }
34
+ export declare class WebDavFileSystem implements FileSystem {
35
+ readonly capabilities: Readonly<{
36
+ symlinks: false;
37
+ hardlinks: false;
38
+ permissions: false;
39
+ timestamps: true;
40
+ atomicRename: false;
41
+ streamingRead: true;
42
+ streamingWrite: true;
43
+ }>;
44
+ private readonly base;
45
+ private readonly baseSegments;
46
+ private readonly transport;
47
+ private readonly requestStreamSupport;
48
+ private readonly headers;
49
+ private readonly maxResponseBytes;
50
+ private readonly maxXmlBytes;
51
+ private readonly maxEntries;
52
+ private readonly timeoutMs;
53
+ private readonly overwritePolicy;
54
+ private readonly configuredComparison;
55
+ private readonly atomicEmptyDirectory;
56
+ private readonly etags;
57
+ constructor(options: WebDavFileSystemOptions);
58
+ compareEntry(path: string, peer: FileSystem, peerPath: string, options?: FsOptions): Promise<EntryComparison>;
59
+ private resourceId;
60
+ private url;
61
+ private hrefPath;
62
+ private httpError;
63
+ private request;
64
+ private requestStream;
65
+ private bytes;
66
+ private bodyChunks;
67
+ private xml;
68
+ private multistatus;
69
+ private lockFailure;
70
+ private entryStat;
71
+ private entries;
72
+ private mutation;
73
+ private unsupported;
74
+ private maybeStat;
75
+ stat(path: string, options?: FsOptions): Promise<FileStat>;
76
+ lstat(path: string, options?: FsOptions): Promise<FileStat>;
77
+ readdir(path: string, options?: FsOptions): Promise<DirectoryEntry[]>;
78
+ readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
79
+ readStream(path: string, options?: ReadStreamOptions): AsyncGenerator<Uint8Array>;
80
+ private prepareWrite;
81
+ writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
82
+ writeStream(path: string, source: ByteSource, options?: WriteFileOptions): Promise<void>;
83
+ appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
84
+ mkdir(path: string, options?: MkdirOptions): Promise<void>;
85
+ rmdir(path: string, options?: FsOptions): Promise<void>;
86
+ private atomicRmdir;
87
+ rm(path: string, options?: RemoveOptions): Promise<void>;
88
+ private guardTransferIdentity;
89
+ private transfer;
90
+ private withDestinationLock;
91
+ rename(source: string, destination: string, options?: FsOptions): Promise<void>;
92
+ copyFile(source: string, destination: string, options?: CopyFileOptions): Promise<void>;
93
+ realpath(path: string, options?: FsOptions): Promise<string>;
94
+ access(path: string, mode?: number, options?: FsOptions): Promise<void>;
95
+ readlink(path: string, _options?: FsOptions): Promise<string>;
96
+ symlink(_target: string, path: string, _options?: FsOptions): Promise<void>;
97
+ link(_source: string, path: string, _options?: FsOptions): Promise<void>;
98
+ chmod(path: string, _mode: number, _options?: FsOptions): Promise<void>;
99
+ utimes(path: string, atimeMs: number, mtimeMs: number, options?: FsOptions): Promise<void>;
100
+ truncate(path: string, _length?: number, _options?: FsOptions): Promise<void>;
101
+ }