@flowscripter/pluggable-io-framework 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.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/src/ProviderRegistry.d.ts +14 -0
- package/dist/src/ProviderRegistry.d.ts.map +1 -0
- package/dist/src/ProviderRegistry.js +22 -0
- package/dist/src/chunkLength.d.ts +3 -0
- package/dist/src/chunkLength.d.ts.map +1 -0
- package/dist/src/chunkLength.js +3 -0
- package/index.ts +2 -0
- package/package.json +60 -0
- package/src/ProviderRegistry.ts +32 -0
- package/src/chunkLength.ts +5 -0
- package/src/copyMove.ts +161 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Flowscripter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# pluggable-io-framework
|
|
2
|
+
|
|
3
|
+
[](https://github.com/flowscripter/pluggable-io-framework/releases)
|
|
4
|
+
[](https://github.com/flowscripter/pluggable-io-framework/actions/workflows/release-bun-library.yml)
|
|
5
|
+
[](https://flowscripter.github.io/pluggable-io-framework/index.html)
|
|
6
|
+
[](https://github.com/flowscripter/pluggable-io-framework/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
> A pluggable source/sink IO framework - provider discovery, copy/move
|
|
9
|
+
> orchestration and telemetry, for files and folders across different
|
|
10
|
+
> backends (local filesystem, object storage, HTTP, etc.)
|
|
11
|
+
|
|
12
|
+
## Key Features
|
|
13
|
+
|
|
14
|
+
- Discovers and instantiates source/sink provider plugins (implementing the
|
|
15
|
+
`IOProviderFactory`/`IOProvider` contract from
|
|
16
|
+
[pluggable-io-framework-api](https://github.com/flowscripter/pluggable-io-framework-api))
|
|
17
|
+
via
|
|
18
|
+
[dynamic-plugin-framework](https://github.com/flowscripter/dynamic-plugin-framework),
|
|
19
|
+
including config validation against each plugin's Zod schema.
|
|
20
|
+
- Copy/move orchestration:
|
|
21
|
+
- Uses a provider's `directCopy`/`directMove` when
|
|
22
|
+
`canDirectTransfer` reports the source and sink are the same
|
|
23
|
+
underlying provider (e.g. same filesystem mount, same object storage
|
|
24
|
+
bucket).
|
|
25
|
+
- Otherwise transfers via multipart (when both sides support it and file
|
|
26
|
+
size crosses a configurable threshold) or plain streaming.
|
|
27
|
+
- Reports progress via a global `TelemetryHooks` callback, tagged with a
|
|
28
|
+
per-operation correlation id.
|
|
29
|
+
- Standalone today - usable directly or via the
|
|
30
|
+
[flowscripter-io-cli](https://github.com/flowscripter/flowscripter-io-cli)
|
|
31
|
+
built on
|
|
32
|
+
[dynamic-cli-framework](https://github.com/flowscripter/dynamic-cli-framework).
|
|
33
|
+
When the full Flowscripter graph runtime exists, an `adapt` operator will
|
|
34
|
+
wrap these providers to bridge them into the processing graph - that
|
|
35
|
+
wrapping is out of scope for this repo.
|
|
36
|
+
- See
|
|
37
|
+
[pluggable-io-framework-plugin-filesystem](https://github.com/flowscripter/pluggable-io-framework-plugin-filesystem)
|
|
38
|
+
for a reference local filesystem source/sink plugin.
|
|
39
|
+
|
|
40
|
+
## Bun Module Usage
|
|
41
|
+
|
|
42
|
+
Add the module:
|
|
43
|
+
|
|
44
|
+
`bun add @flowscripter/pluggable-io-framework`
|
|
45
|
+
|
|
46
|
+
Discover and use a provider:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import {
|
|
50
|
+
DefaultPluginManager,
|
|
51
|
+
LocalFolderPluginRepository,
|
|
52
|
+
} from "@flowscripter/dynamic-plugin-framework";
|
|
53
|
+
import { ProviderRegistry, copy } from "@flowscripter/pluggable-io-framework";
|
|
54
|
+
|
|
55
|
+
const pluginManager = new DefaultPluginManager([new LocalFolderPluginRepository("./plugins")]);
|
|
56
|
+
const registry = new ProviderRegistry(pluginManager);
|
|
57
|
+
await registry.discover();
|
|
58
|
+
|
|
59
|
+
const [extension] = await registry.listAvailableProviders();
|
|
60
|
+
const provider = await registry.createProvider(extension.extensionHandle, { rootPath: "/data" });
|
|
61
|
+
|
|
62
|
+
await copy(provider, "a.txt", provider, "b.txt", {
|
|
63
|
+
telemetry: { onProgress: (event) => console.log(event) },
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
Install dependencies:
|
|
70
|
+
|
|
71
|
+
`bun install`
|
|
72
|
+
|
|
73
|
+
Build (produces `dist/` for Node.js and TypeScript consumers; Bun uses raw source directly):
|
|
74
|
+
|
|
75
|
+
`bun run build`
|
|
76
|
+
|
|
77
|
+
Test:
|
|
78
|
+
|
|
79
|
+
`bun test`
|
|
80
|
+
|
|
81
|
+
Format:
|
|
82
|
+
|
|
83
|
+
`bunx oxfmt`
|
|
84
|
+
|
|
85
|
+
Lint:
|
|
86
|
+
|
|
87
|
+
`bunx oxlint index.ts src/ tests/`
|
|
88
|
+
|
|
89
|
+
Generate HTML API Documentation:
|
|
90
|
+
|
|
91
|
+
`bunx typedoc index.ts`
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
### Overview
|
|
96
|
+
|
|
97
|
+
```mermaid
|
|
98
|
+
sequenceDiagram
|
|
99
|
+
participant Host
|
|
100
|
+
participant ProviderRegistry
|
|
101
|
+
participant PluginManager
|
|
102
|
+
participant Source as IOProvider (source)
|
|
103
|
+
participant Sink as IOProvider (sink)
|
|
104
|
+
|
|
105
|
+
Host->>ProviderRegistry: discover()
|
|
106
|
+
ProviderRegistry->>PluginManager: registerExtensions(extensionPoint)
|
|
107
|
+
Host->>ProviderRegistry: createProvider(handle, config)
|
|
108
|
+
ProviderRegistry->>PluginManager: instantiate(handle)
|
|
109
|
+
ProviderRegistry-->>Host: IOProvider
|
|
110
|
+
|
|
111
|
+
Host->>Source: copy(source, path, sink, path)
|
|
112
|
+
alt canDirectTransfer
|
|
113
|
+
Source->>Sink: directCopy(path, path)
|
|
114
|
+
else multipart eligible
|
|
115
|
+
Source-->>Sink: transfer Parts concurrently
|
|
116
|
+
else
|
|
117
|
+
Source-->>Sink: stream ChunkRefs
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### API
|
|
122
|
+
|
|
123
|
+
Link to auto-generated API docs:
|
|
124
|
+
|
|
125
|
+
[API Documentation](https://flowscripter.github.io/pluggable-io-framework/index.html)
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT © Flowscripter
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PluginManager } from "@flowscripter/dynamic-plugin-framework";
|
|
2
|
+
import { type IOProvider } from "@flowscripter/pluggable-io-framework-api";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a `dynamic-plugin-framework` {@link PluginManager} to discover and
|
|
5
|
+
* instantiate pluggable-io-framework source/sink provider plugins.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ProviderRegistry {
|
|
8
|
+
private readonly pluginManager;
|
|
9
|
+
constructor(pluginManager: PluginManager);
|
|
10
|
+
discover(): Promise<void>;
|
|
11
|
+
listAvailableProviders(): Promise<readonly import("@flowscripter/dynamic-plugin-framework").ExtensionInfo[]>;
|
|
12
|
+
createProvider(extensionHandle: string, config: unknown): Promise<IOProvider>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ProviderRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderRegistry.d.ts","sourceRoot":"","sources":["../../src/ProviderRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,0CAA0C,CAAC;AAElD;;;GAGG;AACH,qBAAa,gBAAgB;IACR,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEnD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,sBAAsB;IAItB,cAAc,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;CAK3F"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT, } from "@flowscripter/pluggable-io-framework-api";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a `dynamic-plugin-framework` {@link PluginManager} to discover and
|
|
4
|
+
* instantiate pluggable-io-framework source/sink provider plugins.
|
|
5
|
+
*/
|
|
6
|
+
export class ProviderRegistry {
|
|
7
|
+
pluginManager;
|
|
8
|
+
constructor(pluginManager) {
|
|
9
|
+
this.pluginManager = pluginManager;
|
|
10
|
+
}
|
|
11
|
+
async discover() {
|
|
12
|
+
await this.pluginManager.registerExtensions(PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT);
|
|
13
|
+
}
|
|
14
|
+
async listAvailableProviders() {
|
|
15
|
+
return this.pluginManager.getRegisteredExtensions(PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT);
|
|
16
|
+
}
|
|
17
|
+
async createProvider(extensionHandle, config) {
|
|
18
|
+
const factory = (await this.pluginManager.instantiate(extensionHandle));
|
|
19
|
+
const validatedConfig = factory.configSchema.parse(config);
|
|
20
|
+
return factory.createProvider(validatedConfig);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunkLength.d.ts","sourceRoot":"","sources":["../../src/chunkLength.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAEzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAEnD"}
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowscripter/pluggable-io-framework",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pluggable source/sink IO framework - provider discovery, copy/move orchestration, decorators, telemetry",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bun",
|
|
7
|
+
"io",
|
|
8
|
+
"plugin",
|
|
9
|
+
"streaming"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/flowscripter/pluggable-io-framework#readme",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/flowscripter/pluggable-io-framework.git"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"index.ts",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"module": "dist/index.js",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"bun": "./index.ts",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json",
|
|
40
|
+
"test": "bun test"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@flowscripter/dynamic-plugin-framework": "file:../dynamic-plugin-framework",
|
|
44
|
+
"@flowscripter/pluggable-io-framework-api": "file:../pluggable-io-framework-api"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/bun": "^1.3.14",
|
|
48
|
+
"oxfmt": "0.57.0",
|
|
49
|
+
"oxlint": "1.72.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"typescript": "^6.0.3"
|
|
53
|
+
},
|
|
54
|
+
"typedocOptions": {
|
|
55
|
+
"readme": "none",
|
|
56
|
+
"exclude": [
|
|
57
|
+
"tests/**"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PluginManager } from "@flowscripter/dynamic-plugin-framework";
|
|
2
|
+
import {
|
|
3
|
+
PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT,
|
|
4
|
+
type IOProvider,
|
|
5
|
+
type IOProviderFactory,
|
|
6
|
+
} from "@flowscripter/pluggable-io-framework-api";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wraps a `dynamic-plugin-framework` {@link PluginManager} to discover and
|
|
10
|
+
* instantiate pluggable-io-framework source/sink provider plugins.
|
|
11
|
+
*/
|
|
12
|
+
export class ProviderRegistry {
|
|
13
|
+
public constructor(private readonly pluginManager: PluginManager) {}
|
|
14
|
+
|
|
15
|
+
public async discover(): Promise<void> {
|
|
16
|
+
await this.pluginManager.registerExtensions(
|
|
17
|
+
PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async listAvailableProviders() {
|
|
22
|
+
return this.pluginManager.getRegisteredExtensions(
|
|
23
|
+
PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async createProvider(extensionHandle: string, config: unknown): Promise<IOProvider> {
|
|
28
|
+
const factory = (await this.pluginManager.instantiate(extensionHandle)) as IOProviderFactory;
|
|
29
|
+
const validatedConfig = factory.configSchema.parse(config);
|
|
30
|
+
return factory.createProvider(validatedConfig);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/copyMove.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChunkRef,
|
|
3
|
+
IOProvider,
|
|
4
|
+
Part,
|
|
5
|
+
TelemetryHooks,
|
|
6
|
+
} from "@flowscripter/pluggable-io-framework-api";
|
|
7
|
+
import { chunkLength } from "./chunkLength.ts";
|
|
8
|
+
|
|
9
|
+
export interface TransferOptions {
|
|
10
|
+
readonly telemetry?: TelemetryHooks;
|
|
11
|
+
/** Minimum file size (bytes) before multipart transfer is attempted over plain streaming. */
|
|
12
|
+
readonly multipartThreshold?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_MULTIPART_THRESHOLD = 64 * 1024 * 1024;
|
|
16
|
+
|
|
17
|
+
async function streamingTransfer(
|
|
18
|
+
source: IOProvider,
|
|
19
|
+
sourcePath: string,
|
|
20
|
+
sink: IOProvider,
|
|
21
|
+
destPath: string,
|
|
22
|
+
operationId: string,
|
|
23
|
+
telemetry: TelemetryHooks | undefined,
|
|
24
|
+
totalBytes: number | undefined,
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
const readable = await source.getReadableStream(sourcePath);
|
|
27
|
+
const writable = await sink.getWritableStream(destPath);
|
|
28
|
+
const reader = (readable.stream as ReadableStream<ChunkRef>).getReader();
|
|
29
|
+
const writer = (writable.stream as WritableStream<ChunkRef>).getWriter();
|
|
30
|
+
let bytesProcessed = 0;
|
|
31
|
+
try {
|
|
32
|
+
for (;;) {
|
|
33
|
+
const { done, value } = await reader.read();
|
|
34
|
+
if (done) break;
|
|
35
|
+
await writer.write(value);
|
|
36
|
+
bytesProcessed += chunkLength(value);
|
|
37
|
+
telemetry?.onProgress?.({ operationId, type: "copy", bytesProcessed, totalBytes });
|
|
38
|
+
}
|
|
39
|
+
await writer.close();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
await writer.abort(error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function multipartTransfer(
|
|
47
|
+
source: IOProvider,
|
|
48
|
+
sourcePath: string,
|
|
49
|
+
sink: IOProvider,
|
|
50
|
+
destPath: string,
|
|
51
|
+
operationId: string,
|
|
52
|
+
telemetry: TelemetryHooks | undefined,
|
|
53
|
+
totalBytes: number | undefined,
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
let bytesProcessed = 0;
|
|
56
|
+
const writer = sink.getMultipartWriter(destPath);
|
|
57
|
+
async function* transferParts(): AsyncIterable<Part> {
|
|
58
|
+
for await (const part of source.getMultipartReader(sourcePath)) {
|
|
59
|
+
const reader = (part.stream as ReadableStream<ChunkRef>).getReader();
|
|
60
|
+
const chunks: ChunkRef[] = [];
|
|
61
|
+
for (;;) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done) break;
|
|
64
|
+
chunks.push(value);
|
|
65
|
+
bytesProcessed += chunkLength(value);
|
|
66
|
+
telemetry?.onProgress?.({ operationId, type: "copy", bytesProcessed, totalBytes });
|
|
67
|
+
}
|
|
68
|
+
yield {
|
|
69
|
+
index: part.index,
|
|
70
|
+
offset: part.offset,
|
|
71
|
+
stream: new ReadableStream<ChunkRef>({
|
|
72
|
+
start(controller) {
|
|
73
|
+
for (const chunk of chunks) controller.enqueue(chunk);
|
|
74
|
+
controller.close();
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
complete: () => part.complete(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
await writer.write(transferParts());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function canUseMultipart(
|
|
85
|
+
source: IOProvider,
|
|
86
|
+
sink: IOProvider,
|
|
87
|
+
size: number | undefined,
|
|
88
|
+
threshold: number,
|
|
89
|
+
): boolean {
|
|
90
|
+
return (
|
|
91
|
+
size !== undefined &&
|
|
92
|
+
size >= threshold &&
|
|
93
|
+
typeof source.getMultipartReader === "function" &&
|
|
94
|
+
typeof sink.getMultipartWriter === "function"
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Copies `sourcePath` on `source` to `destPath` on `sink`.
|
|
100
|
+
*
|
|
101
|
+
* Uses `source.directCopy` when `source.canDirectTransfer?.(sink)` reports
|
|
102
|
+
* eligibility (same-provider direct transfer). Otherwise falls back to
|
|
103
|
+
* multipart transfer (when both sides support it and size crosses
|
|
104
|
+
* `options.multipartThreshold`) or plain streaming.
|
|
105
|
+
*/
|
|
106
|
+
export async function copy(
|
|
107
|
+
source: IOProvider,
|
|
108
|
+
sourcePath: string,
|
|
109
|
+
sink: IOProvider,
|
|
110
|
+
destPath: string,
|
|
111
|
+
options: TransferOptions = {},
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
const operationId = crypto.randomUUID();
|
|
114
|
+
if (source.canDirectTransfer?.(sink) && source.directCopy) {
|
|
115
|
+
await source.directCopy(sourcePath, destPath);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const properties = await source.getProperties(sourcePath);
|
|
119
|
+
const threshold = options.multipartThreshold ?? DEFAULT_MULTIPART_THRESHOLD;
|
|
120
|
+
if (canUseMultipart(source, sink, properties.size, threshold)) {
|
|
121
|
+
await multipartTransfer(
|
|
122
|
+
source,
|
|
123
|
+
sourcePath,
|
|
124
|
+
sink,
|
|
125
|
+
destPath,
|
|
126
|
+
operationId,
|
|
127
|
+
options.telemetry,
|
|
128
|
+
properties.size,
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
await streamingTransfer(
|
|
133
|
+
source,
|
|
134
|
+
sourcePath,
|
|
135
|
+
sink,
|
|
136
|
+
destPath,
|
|
137
|
+
operationId,
|
|
138
|
+
options.telemetry,
|
|
139
|
+
properties.size,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Moves `sourcePath` on `source` to `destPath` on `sink`. Uses
|
|
145
|
+
* `source.directMove` when eligible, otherwise performs a {@link copy}
|
|
146
|
+
* followed by deleting the source item.
|
|
147
|
+
*/
|
|
148
|
+
export async function move(
|
|
149
|
+
source: IOProvider,
|
|
150
|
+
sourcePath: string,
|
|
151
|
+
sink: IOProvider,
|
|
152
|
+
destPath: string,
|
|
153
|
+
options: TransferOptions = {},
|
|
154
|
+
): Promise<void> {
|
|
155
|
+
if (source.canDirectTransfer?.(sink) && source.directMove) {
|
|
156
|
+
await source.directMove(sourcePath, destPath);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
await copy(source, sourcePath, sink, destPath, options);
|
|
160
|
+
await source.delete(sourcePath);
|
|
161
|
+
}
|