@flowscripter/pluggable-io-framework-api 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 +133 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/src/ChunkRef.d.ts +40 -0
- package/dist/src/ChunkRef.d.ts.map +1 -0
- package/dist/src/ChunkRef.js +47 -0
- package/dist/src/IOProvider.d.ts +39 -0
- package/dist/src/IOProvider.d.ts.map +1 -0
- package/dist/src/IOProvider.js +1 -0
- package/dist/src/IOProviderFactory.d.ts +23 -0
- package/dist/src/IOProviderFactory.d.ts.map +1 -0
- package/dist/src/IOProviderFactory.js +6 -0
- package/dist/src/ItemProperties.d.ts +16 -0
- package/dist/src/ItemProperties.d.ts.map +1 -0
- package/dist/src/ItemProperties.js +1 -0
- package/dist/src/Part.d.ts +13 -0
- package/dist/src/Part.d.ts.map +1 -0
- package/dist/src/Part.js +1 -0
- package/dist/src/StreamDecorator.d.ts +9 -0
- package/dist/src/StreamDecorator.d.ts.map +1 -0
- package/dist/src/StreamDecorator.js +1 -0
- package/dist/src/StreamHandle.d.ts +15 -0
- package/dist/src/StreamHandle.d.ts.map +1 -0
- package/dist/src/StreamHandle.js +1 -0
- package/dist/src/TelemetryHooks.d.ts +21 -0
- package/dist/src/TelemetryHooks.d.ts.map +1 -0
- package/dist/src/TelemetryHooks.js +1 -0
- package/index.ts +8 -0
- package/package.json +60 -0
- package/src/ChunkRef.ts +83 -0
- package/src/IOProvider.ts +37 -0
- package/src/IOProviderFactory.ts +25 -0
- package/src/ItemProperties.ts +15 -0
- package/src/Part.ts +13 -0
- package/src/StreamDecorator.ts +9 -0
- package/src/StreamHandle.ts +15 -0
- package/src/TelemetryHooks.ts +16 -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,133 @@
|
|
|
1
|
+
# pluggable-io-framework-api
|
|
2
|
+
|
|
3
|
+
[](https://github.com/flowscripter/pluggable-io-framework-api/releases)
|
|
4
|
+
[](https://github.com/flowscripter/pluggable-io-framework-api/actions/workflows/release-bun-library.yml)
|
|
5
|
+
[](https://flowscripter.github.io/pluggable-io-framework-api/index.html)
|
|
6
|
+
[](https://github.com/flowscripter/pluggable-io-framework-api/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
> API contracts for [pluggable-io-framework](https://github.com/flowscripter/pluggable-io-framework)
|
|
9
|
+
> source/sink provider plugins
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- Defines the `IOProviderFactory`/`IOProvider` contract that source/sink
|
|
14
|
+
plugins (e.g. local filesystem, object storage) implement, discovered and
|
|
15
|
+
loaded via
|
|
16
|
+
[dynamic-plugin-framework](https://github.com/flowscripter/dynamic-plugin-framework).
|
|
17
|
+
- Config and per-item property schemas are defined with
|
|
18
|
+
[Zod](https://zod.dev) (source of truth), portable to JSON Schema via
|
|
19
|
+
`zod-to-json-schema` for hand-edited config files or non-TS validation.
|
|
20
|
+
- `ChunkRef`: a tagged-union stream payload (`js` vs `native`) that carries
|
|
21
|
+
memory ownership/origin with it, enabling zero-copy handoff to/from
|
|
22
|
+
Rust-FFI-backed providers and decorators, with a small adapter to the
|
|
23
|
+
standard Web Streams `ReadableStream<Uint8Array>`/`WritableStream<Uint8Array>`
|
|
24
|
+
for interop (`fetch`, `pipeTo`, etc.).
|
|
25
|
+
- Well-known item properties (`size`, `lastModified`, `isFolder`,
|
|
26
|
+
`contentType`) guaranteed by every provider, plus a provider-specific
|
|
27
|
+
`properties` extension bag for anything else (etag, storage class, custom
|
|
28
|
+
tags).
|
|
29
|
+
- Multipart transfer modeled as a stream of independently readable/writable
|
|
30
|
+
`Part` handles, so parts can be processed concurrently.
|
|
31
|
+
- Telemetry as a global `TelemetryHooks` object supplied once at
|
|
32
|
+
initialisation; every operation reports through it tagged with a
|
|
33
|
+
correlation id.
|
|
34
|
+
- Pure TypeScript, minimal dependencies, no concrete provider
|
|
35
|
+
implementations - see
|
|
36
|
+
[pluggable-io-framework](https://github.com/flowscripter/pluggable-io-framework)
|
|
37
|
+
for orchestration and
|
|
38
|
+
[pluggable-io-framework-plugin-filesystem](https://github.com/flowscripter/pluggable-io-framework-plugin-filesystem)
|
|
39
|
+
for a reference implementation.
|
|
40
|
+
|
|
41
|
+
## Bun Module Usage
|
|
42
|
+
|
|
43
|
+
Add the module:
|
|
44
|
+
|
|
45
|
+
`bun add @flowscripter/pluggable-io-framework-api`
|
|
46
|
+
|
|
47
|
+
Implement a provider factory:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { z } from "zod";
|
|
51
|
+
import type { IOProviderFactory } from "@flowscripter/pluggable-io-framework-api";
|
|
52
|
+
|
|
53
|
+
const configSchema = z.object({ rootPath: z.string() });
|
|
54
|
+
const propertySchema = z.object({ etag: z.string().optional() });
|
|
55
|
+
|
|
56
|
+
const factory: IOProviderFactory<z.infer<typeof configSchema>> = {
|
|
57
|
+
configSchema,
|
|
58
|
+
propertySchema,
|
|
59
|
+
async createProvider(config) {
|
|
60
|
+
// return an IOProvider implementation
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
Install dependencies:
|
|
68
|
+
|
|
69
|
+
`bun install`
|
|
70
|
+
|
|
71
|
+
Build (produces `dist/` for Node.js and TypeScript consumers; Bun uses raw source directly):
|
|
72
|
+
|
|
73
|
+
`bun run build`
|
|
74
|
+
|
|
75
|
+
Test:
|
|
76
|
+
|
|
77
|
+
`bun test`
|
|
78
|
+
|
|
79
|
+
Format:
|
|
80
|
+
|
|
81
|
+
`bunx oxfmt`
|
|
82
|
+
|
|
83
|
+
Lint:
|
|
84
|
+
|
|
85
|
+
`bunx oxlint index.ts src/ tests/`
|
|
86
|
+
|
|
87
|
+
Generate HTML API Documentation:
|
|
88
|
+
|
|
89
|
+
`bunx typedoc index.ts`
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
### Overview
|
|
94
|
+
|
|
95
|
+
```mermaid
|
|
96
|
+
classDiagram
|
|
97
|
+
IOProviderFactory --> IOProvider : creates
|
|
98
|
+
IOProvider --> StreamHandle : returns
|
|
99
|
+
IOProvider --> Part : returns (multipart)
|
|
100
|
+
StreamHandle --> ChunkRef : streams
|
|
101
|
+
Part --> ChunkRef : streams
|
|
102
|
+
IOProvider --> ItemProperties : returns
|
|
103
|
+
|
|
104
|
+
class IOProviderFactory {
|
|
105
|
+
+configSchema
|
|
106
|
+
+propertySchema
|
|
107
|
+
+createProvider(config)
|
|
108
|
+
}
|
|
109
|
+
class IOProvider {
|
|
110
|
+
+dispose()
|
|
111
|
+
+list(path, options)
|
|
112
|
+
+getProperties(path)
|
|
113
|
+
+setProperties(path, properties)
|
|
114
|
+
+delete(path)
|
|
115
|
+
+getReadableStream(path)
|
|
116
|
+
+getWritableStream(path)
|
|
117
|
+
+getMultipartReader(path)
|
|
118
|
+
+getMultipartWriter(path)
|
|
119
|
+
+canDirectTransfer(other)
|
|
120
|
+
+directCopy(sourcePath, destPath)
|
|
121
|
+
+directMove(sourcePath, destPath)
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### API
|
|
126
|
+
|
|
127
|
+
Link to auto-generated API docs:
|
|
128
|
+
|
|
129
|
+
[API Documentation](https://flowscripter.github.io/pluggable-io-framework-api/index.html)
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT © Flowscripter
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./src/ChunkRef.ts";
|
|
2
|
+
export * from "./src/IOProvider.ts";
|
|
3
|
+
export * from "./src/IOProviderFactory.ts";
|
|
4
|
+
export * from "./src/ItemProperties.ts";
|
|
5
|
+
export * from "./src/Part.ts";
|
|
6
|
+
export * from "./src/StreamDecorator.ts";
|
|
7
|
+
export * from "./src/StreamHandle.ts";
|
|
8
|
+
export * from "./src/TelemetryHooks.ts";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./src/ChunkRef.js";
|
|
2
|
+
export * from "./src/IOProvider.js";
|
|
3
|
+
export * from "./src/IOProviderFactory.js";
|
|
4
|
+
export * from "./src/ItemProperties.js";
|
|
5
|
+
export * from "./src/Part.js";
|
|
6
|
+
export * from "./src/StreamDecorator.js";
|
|
7
|
+
export * from "./src/StreamHandle.js";
|
|
8
|
+
export * from "./src/TelemetryHooks.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chunk of stream payload, tagged with its memory origin/ownership.
|
|
3
|
+
*
|
|
4
|
+
* This is the actual unit flowing through {@link StreamHandle} and
|
|
5
|
+
* {@link Part} streams - not a TS-vs-Rust-specific type. A pure-TS pipeline
|
|
6
|
+
* is simply a stream of `kind: "js"` chunks end to end.
|
|
7
|
+
*
|
|
8
|
+
* Tagging origin lets a consumer choose a zero-copy pointer handoff when
|
|
9
|
+
* compatible (js -> rust always; rust -> rust always) or fall back to an
|
|
10
|
+
* explicit copy when required (rust -> js, since JS code cannot safely
|
|
11
|
+
* retain a raw pointer past the call that produced it).
|
|
12
|
+
*
|
|
13
|
+
* `attributes` is deliberately shaped close to the future Flowscripter
|
|
14
|
+
* runtime's Item (attributes + payload) so this type is a natural fit if/when
|
|
15
|
+
* an `adapt` operator wraps these streams later. It is unused today.
|
|
16
|
+
*/
|
|
17
|
+
export type ChunkRef = {
|
|
18
|
+
readonly kind: "js";
|
|
19
|
+
readonly data: Uint8Array;
|
|
20
|
+
readonly attributes?: Readonly<Record<string, unknown>>;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: "native";
|
|
23
|
+
readonly ptr: number;
|
|
24
|
+
readonly length: number;
|
|
25
|
+
release(): void;
|
|
26
|
+
readonly attributes?: Readonly<Record<string, unknown>>;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Copy a chunk's bytes into a plain Uint8Array, regardless of origin.
|
|
30
|
+
* The only copy incurred is for `kind: "native"` chunks.
|
|
31
|
+
*/
|
|
32
|
+
export declare function toUint8Array(chunk: ChunkRef): Uint8Array;
|
|
33
|
+
/**
|
|
34
|
+
* Adapter to the standard Web Streams interop surface (fetch, pipeTo
|
|
35
|
+
* external consumers). This is the one clearly-marked copy boundary -
|
|
36
|
+
* internal source/sink/decorator code speaks {@link ChunkRef} directly.
|
|
37
|
+
*/
|
|
38
|
+
export declare function toWebReadableStream(source: ReadableStream<ChunkRef>): ReadableStream<Uint8Array>;
|
|
39
|
+
export declare function fromWebReadableStream(source: ReadableStream<Uint8Array>): ReadableStream<ChunkRef>;
|
|
40
|
+
//# sourceMappingURL=ChunkRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChunkRef.d.ts","sourceRoot":"","sources":["../../src/ChunkRef.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,QAAQ,GAChB;IACE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzD,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzD,CAAC;AAEN;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAOxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,CAehG;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GACjC,cAAc,CAAC,QAAQ,CAAC,CAe1B"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy a chunk's bytes into a plain Uint8Array, regardless of origin.
|
|
3
|
+
* The only copy incurred is for `kind: "native"` chunks.
|
|
4
|
+
*/
|
|
5
|
+
export function toUint8Array(chunk) {
|
|
6
|
+
if (chunk.kind === "js") {
|
|
7
|
+
return chunk.data;
|
|
8
|
+
}
|
|
9
|
+
throw new Error("Copying a native ChunkRef to Uint8Array requires an FFI-capable runtime helper - not implemented in pluggable-io-framework-api");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Adapter to the standard Web Streams interop surface (fetch, pipeTo
|
|
13
|
+
* external consumers). This is the one clearly-marked copy boundary -
|
|
14
|
+
* internal source/sink/decorator code speaks {@link ChunkRef} directly.
|
|
15
|
+
*/
|
|
16
|
+
export function toWebReadableStream(source) {
|
|
17
|
+
const reader = source.getReader();
|
|
18
|
+
return new ReadableStream({
|
|
19
|
+
async pull(controller) {
|
|
20
|
+
const { done, value } = await reader.read();
|
|
21
|
+
if (done) {
|
|
22
|
+
controller.close();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
controller.enqueue(toUint8Array(value));
|
|
26
|
+
},
|
|
27
|
+
cancel(reason) {
|
|
28
|
+
return reader.cancel(reason);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function fromWebReadableStream(source) {
|
|
33
|
+
const reader = source.getReader();
|
|
34
|
+
return new ReadableStream({
|
|
35
|
+
async pull(controller) {
|
|
36
|
+
const { done, value } = await reader.read();
|
|
37
|
+
if (done) {
|
|
38
|
+
controller.close();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
controller.enqueue({ kind: "js", data: value });
|
|
42
|
+
},
|
|
43
|
+
cancel(reason) {
|
|
44
|
+
return reader.cancel(reason);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ItemProperties } from "./ItemProperties.ts";
|
|
2
|
+
import type { Part } from "./Part.ts";
|
|
3
|
+
import type { StreamHandle } from "./StreamHandle.ts";
|
|
4
|
+
/**
|
|
5
|
+
* A configured source/sink instance, as returned by
|
|
6
|
+
* {@link IOProviderFactory.createProvider}.
|
|
7
|
+
*
|
|
8
|
+
* Disposal is the explicit {@link IOProvider.dispose} method - not a reliance
|
|
9
|
+
* on `Symbol.asyncDispose` - so plugins have an unambiguous, discoverable
|
|
10
|
+
* contract for releasing connections/handles.
|
|
11
|
+
*/
|
|
12
|
+
export interface IOProvider {
|
|
13
|
+
dispose(): Promise<void>;
|
|
14
|
+
list(path: string, options?: {
|
|
15
|
+
recursive?: boolean;
|
|
16
|
+
regex?: RegExp;
|
|
17
|
+
}): AsyncIterable<{
|
|
18
|
+
path: string;
|
|
19
|
+
properties: ItemProperties;
|
|
20
|
+
}>;
|
|
21
|
+
getProperties(path: string): Promise<ItemProperties>;
|
|
22
|
+
setProperties(path: string, properties: Partial<Record<string, unknown>>): Promise<void>;
|
|
23
|
+
delete(path: string): Promise<void>;
|
|
24
|
+
getReadableStream(path: string): Promise<StreamHandle>;
|
|
25
|
+
getWritableStream(path: string): Promise<StreamHandle>;
|
|
26
|
+
getMultipartReader(path: string): AsyncIterable<Part>;
|
|
27
|
+
getMultipartWriter(path: string): {
|
|
28
|
+
write(parts: AsyncIterable<Part>): Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Self-reported direct-transfer eligibility - the provider owns what
|
|
32
|
+
* "same" means for its backend (e.g. same mount for filesystem, same
|
|
33
|
+
* bucket+region+credentials for object storage).
|
|
34
|
+
*/
|
|
35
|
+
canDirectTransfer?(other: IOProvider): boolean;
|
|
36
|
+
directCopy?(sourcePath: string, destPath: string): Promise<void>;
|
|
37
|
+
directMove?(sourcePath: string, destPath: string): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=IOProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IOProvider.d.ts","sourceRoot":"","sources":["../../src/IOProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;IAC/D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrD,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACvD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACvD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAEvF;;;;OAIG;IACH,iBAAiB,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IAC/C,UAAU,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
import type { IOProvider } from "./IOProvider.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Extension point constant that a `dynamic-plugin-framework` `Plugin`'s
|
|
5
|
+
* `ExtensionDescriptor.extensionPoint` must match to be discovered as a
|
|
6
|
+
* pluggable-io-framework source/sink provider.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT = "@flowscripter/pluggable-io-framework/provider-factory";
|
|
9
|
+
/**
|
|
10
|
+
* Returned (as `unknown`, cast at the extension point boundary) from
|
|
11
|
+
* `ExtensionFactory.create()` in a `dynamic-plugin-framework`
|
|
12
|
+
* `ExtensionDescriptor`.
|
|
13
|
+
*
|
|
14
|
+
* Config/property schema: Zod is the source of truth; JSON Schema can be
|
|
15
|
+
* derived via `zod-to-json-schema` for portability (hand-edited config
|
|
16
|
+
* files, non-TS validation).
|
|
17
|
+
*/
|
|
18
|
+
export interface IOProviderFactory<TConfig = unknown> {
|
|
19
|
+
readonly configSchema: ZodType<TConfig>;
|
|
20
|
+
readonly propertySchema: ZodType<Record<string, unknown>>;
|
|
21
|
+
createProvider(config: TConfig): Promise<IOProvider>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=IOProviderFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IOProviderFactory.d.ts","sourceRoot":"","sources":["../../src/IOProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,uDAAuD,0DACX,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB,CAAC,OAAO,GAAG,OAAO;IAClD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACtD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension point constant that a `dynamic-plugin-framework` `Plugin`'s
|
|
3
|
+
* `ExtensionDescriptor.extensionPoint` must match to be discovered as a
|
|
4
|
+
* pluggable-io-framework source/sink provider.
|
|
5
|
+
*/
|
|
6
|
+
export const PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT = "@flowscripter/pluggable-io-framework/provider-factory";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Properties of a file/folder item. `size`, `lastModified`, `isFolder` and
|
|
3
|
+
* `contentType` are well-known, framework-guaranteed fields every provider
|
|
4
|
+
* must populate so generic consumers (CLI, copy/move orchestration) can rely
|
|
5
|
+
* on them without knowing the provider. Anything provider-specific (etag,
|
|
6
|
+
* storage class, custom tags) goes in `properties`, validated against that
|
|
7
|
+
* provider's `propertySchema`.
|
|
8
|
+
*/
|
|
9
|
+
export interface ItemProperties {
|
|
10
|
+
readonly size: number | undefined;
|
|
11
|
+
readonly lastModified: Date | undefined;
|
|
12
|
+
readonly isFolder: boolean;
|
|
13
|
+
readonly contentType?: string;
|
|
14
|
+
readonly properties: Readonly<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=ItemProperties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemProperties.d.ts","sourceRoot":"","sources":["../../src/ItemProperties.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,IAAI,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACxD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChunkRef } from "./ChunkRef.ts";
|
|
2
|
+
/**
|
|
3
|
+
* One independently readable/writable part of a multipart transfer.
|
|
4
|
+
* Parts may be processed concurrently (e.g. `Promise.all` over N parts);
|
|
5
|
+
* the provider assembles/commits them once all parts finish.
|
|
6
|
+
*/
|
|
7
|
+
export interface Part {
|
|
8
|
+
readonly index: number;
|
|
9
|
+
readonly offset: number;
|
|
10
|
+
readonly stream: ReadableStream<ChunkRef> | WritableStream<ChunkRef>;
|
|
11
|
+
complete(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Part.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Part.d.ts","sourceRoot":"","sources":["../../src/Part.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
|
package/dist/src/Part.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StreamHandle } from "./StreamHandle.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A decorator wraps a {@link StreamHandle} and returns an enhanced handle
|
|
4
|
+
* with additional capability methods (e.g. `seekable` adds `seek`/
|
|
5
|
+
* `readRange`; `locally-cached` transparently serves repeated reads from a
|
|
6
|
+
* local cache).
|
|
7
|
+
*/
|
|
8
|
+
export type StreamDecorator = (handle: StreamHandle) => StreamHandle;
|
|
9
|
+
//# sourceMappingURL=StreamDecorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamDecorator.d.ts","sourceRoot":"","sources":["../../src/StreamDecorator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ChunkRef } from "./ChunkRef.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A handle to a readable or writable stream of {@link ChunkRef}s, plus
|
|
4
|
+
* optional capability methods for providers/decorators that support
|
|
5
|
+
* random-access beyond plain sequential read (e.g. `seekable`).
|
|
6
|
+
*
|
|
7
|
+
* Capability methods are present only when supported - consumers
|
|
8
|
+
* feature-detect via `typeof handle.seek === "function"` etc.
|
|
9
|
+
*/
|
|
10
|
+
export interface StreamHandle {
|
|
11
|
+
readonly stream: ReadableStream<ChunkRef> | WritableStream<ChunkRef>;
|
|
12
|
+
seek?(offset: number): Promise<void>;
|
|
13
|
+
readRange?(start: number, end: number): Promise<ReadableStream<ChunkRef>>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=StreamHandle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamHandle.d.ts","sourceRoot":"","sources":["../../src/StreamHandle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC3E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global telemetry hooks supplied once at framework initialisation. Every
|
|
3
|
+
* operation is given a correlation id and reports through the same hooks,
|
|
4
|
+
* tagged with that id/operation-type - callers don't need to thread a
|
|
5
|
+
* callback through every call, but can still track individual operations
|
|
6
|
+
* via the id in emitted events.
|
|
7
|
+
*/
|
|
8
|
+
export interface TelemetryHooks {
|
|
9
|
+
onProgress?(event: {
|
|
10
|
+
operationId: string;
|
|
11
|
+
type: string;
|
|
12
|
+
bytesProcessed: number;
|
|
13
|
+
totalBytes?: number;
|
|
14
|
+
}): void;
|
|
15
|
+
onMetric?(event: {
|
|
16
|
+
name: string;
|
|
17
|
+
value: number;
|
|
18
|
+
tags?: Record<string, string>;
|
|
19
|
+
}): void;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=TelemetryHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelemetryHooks.d.ts","sourceRoot":"","sources":["../../src/TelemetryHooks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,CAAC,KAAK,EAAE;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;CACxF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./src/ChunkRef.ts";
|
|
2
|
+
export * from "./src/IOProvider.ts";
|
|
3
|
+
export * from "./src/IOProviderFactory.ts";
|
|
4
|
+
export * from "./src/ItemProperties.ts";
|
|
5
|
+
export * from "./src/Part.ts";
|
|
6
|
+
export * from "./src/StreamDecorator.ts";
|
|
7
|
+
export * from "./src/StreamHandle.ts";
|
|
8
|
+
export * from "./src/TelemetryHooks.ts";
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowscripter/pluggable-io-framework-api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "API contracts for pluggable-io-framework source/sink provider plugins",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"api",
|
|
7
|
+
"bun",
|
|
8
|
+
"io",
|
|
9
|
+
"plugin"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/flowscripter/pluggable-io-framework-api#readme",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/flowscripter/pluggable-io-framework-api.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
|
+
"zod": "^3.24.0",
|
|
44
|
+
"zod-to-json-schema": "^3.24.0"
|
|
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
|
+
}
|
package/src/ChunkRef.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chunk of stream payload, tagged with its memory origin/ownership.
|
|
3
|
+
*
|
|
4
|
+
* This is the actual unit flowing through {@link StreamHandle} and
|
|
5
|
+
* {@link Part} streams - not a TS-vs-Rust-specific type. A pure-TS pipeline
|
|
6
|
+
* is simply a stream of `kind: "js"` chunks end to end.
|
|
7
|
+
*
|
|
8
|
+
* Tagging origin lets a consumer choose a zero-copy pointer handoff when
|
|
9
|
+
* compatible (js -> rust always; rust -> rust always) or fall back to an
|
|
10
|
+
* explicit copy when required (rust -> js, since JS code cannot safely
|
|
11
|
+
* retain a raw pointer past the call that produced it).
|
|
12
|
+
*
|
|
13
|
+
* `attributes` is deliberately shaped close to the future Flowscripter
|
|
14
|
+
* runtime's Item (attributes + payload) so this type is a natural fit if/when
|
|
15
|
+
* an `adapt` operator wraps these streams later. It is unused today.
|
|
16
|
+
*/
|
|
17
|
+
export type ChunkRef =
|
|
18
|
+
| {
|
|
19
|
+
readonly kind: "js";
|
|
20
|
+
readonly data: Uint8Array;
|
|
21
|
+
readonly attributes?: Readonly<Record<string, unknown>>;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
readonly kind: "native";
|
|
25
|
+
readonly ptr: number;
|
|
26
|
+
readonly length: number;
|
|
27
|
+
release(): void;
|
|
28
|
+
readonly attributes?: Readonly<Record<string, unknown>>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Copy a chunk's bytes into a plain Uint8Array, regardless of origin.
|
|
33
|
+
* The only copy incurred is for `kind: "native"` chunks.
|
|
34
|
+
*/
|
|
35
|
+
export function toUint8Array(chunk: ChunkRef): Uint8Array {
|
|
36
|
+
if (chunk.kind === "js") {
|
|
37
|
+
return chunk.data;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(
|
|
40
|
+
"Copying a native ChunkRef to Uint8Array requires an FFI-capable runtime helper - not implemented in pluggable-io-framework-api",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adapter to the standard Web Streams interop surface (fetch, pipeTo
|
|
46
|
+
* external consumers). This is the one clearly-marked copy boundary -
|
|
47
|
+
* internal source/sink/decorator code speaks {@link ChunkRef} directly.
|
|
48
|
+
*/
|
|
49
|
+
export function toWebReadableStream(source: ReadableStream<ChunkRef>): ReadableStream<Uint8Array> {
|
|
50
|
+
const reader = source.getReader();
|
|
51
|
+
return new ReadableStream<Uint8Array>({
|
|
52
|
+
async pull(controller) {
|
|
53
|
+
const { done, value } = await reader.read();
|
|
54
|
+
if (done) {
|
|
55
|
+
controller.close();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
controller.enqueue(toUint8Array(value));
|
|
59
|
+
},
|
|
60
|
+
cancel(reason) {
|
|
61
|
+
return reader.cancel(reason);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function fromWebReadableStream(
|
|
67
|
+
source: ReadableStream<Uint8Array>,
|
|
68
|
+
): ReadableStream<ChunkRef> {
|
|
69
|
+
const reader = source.getReader();
|
|
70
|
+
return new ReadableStream<ChunkRef>({
|
|
71
|
+
async pull(controller) {
|
|
72
|
+
const { done, value } = await reader.read();
|
|
73
|
+
if (done) {
|
|
74
|
+
controller.close();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
controller.enqueue({ kind: "js", data: value });
|
|
78
|
+
},
|
|
79
|
+
cancel(reason) {
|
|
80
|
+
return reader.cancel(reason);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ItemProperties } from "./ItemProperties.ts";
|
|
2
|
+
import type { Part } from "./Part.ts";
|
|
3
|
+
import type { StreamHandle } from "./StreamHandle.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A configured source/sink instance, as returned by
|
|
7
|
+
* {@link IOProviderFactory.createProvider}.
|
|
8
|
+
*
|
|
9
|
+
* Disposal is the explicit {@link IOProvider.dispose} method - not a reliance
|
|
10
|
+
* on `Symbol.asyncDispose` - so plugins have an unambiguous, discoverable
|
|
11
|
+
* contract for releasing connections/handles.
|
|
12
|
+
*/
|
|
13
|
+
export interface IOProvider {
|
|
14
|
+
dispose(): Promise<void>;
|
|
15
|
+
|
|
16
|
+
list(
|
|
17
|
+
path: string,
|
|
18
|
+
options?: { recursive?: boolean; regex?: RegExp },
|
|
19
|
+
): AsyncIterable<{ path: string; properties: ItemProperties }>;
|
|
20
|
+
getProperties(path: string): Promise<ItemProperties>;
|
|
21
|
+
setProperties(path: string, properties: Partial<Record<string, unknown>>): Promise<void>;
|
|
22
|
+
delete(path: string): Promise<void>;
|
|
23
|
+
|
|
24
|
+
getReadableStream(path: string): Promise<StreamHandle>;
|
|
25
|
+
getWritableStream(path: string): Promise<StreamHandle>;
|
|
26
|
+
getMultipartReader(path: string): AsyncIterable<Part>;
|
|
27
|
+
getMultipartWriter(path: string): { write(parts: AsyncIterable<Part>): Promise<void> };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Self-reported direct-transfer eligibility - the provider owns what
|
|
31
|
+
* "same" means for its backend (e.g. same mount for filesystem, same
|
|
32
|
+
* bucket+region+credentials for object storage).
|
|
33
|
+
*/
|
|
34
|
+
canDirectTransfer?(other: IOProvider): boolean;
|
|
35
|
+
directCopy?(sourcePath: string, destPath: string): Promise<void>;
|
|
36
|
+
directMove?(sourcePath: string, destPath: string): Promise<void>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
import type { IOProvider } from "./IOProvider.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extension point constant that a `dynamic-plugin-framework` `Plugin`'s
|
|
6
|
+
* `ExtensionDescriptor.extensionPoint` must match to be discovered as a
|
|
7
|
+
* pluggable-io-framework source/sink provider.
|
|
8
|
+
*/
|
|
9
|
+
export const PLUGGABLE_IO_FRAMEWORK_PROVIDER_FACTORY_EXTENSION_POINT =
|
|
10
|
+
"@flowscripter/pluggable-io-framework/provider-factory";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returned (as `unknown`, cast at the extension point boundary) from
|
|
14
|
+
* `ExtensionFactory.create()` in a `dynamic-plugin-framework`
|
|
15
|
+
* `ExtensionDescriptor`.
|
|
16
|
+
*
|
|
17
|
+
* Config/property schema: Zod is the source of truth; JSON Schema can be
|
|
18
|
+
* derived via `zod-to-json-schema` for portability (hand-edited config
|
|
19
|
+
* files, non-TS validation).
|
|
20
|
+
*/
|
|
21
|
+
export interface IOProviderFactory<TConfig = unknown> {
|
|
22
|
+
readonly configSchema: ZodType<TConfig>;
|
|
23
|
+
readonly propertySchema: ZodType<Record<string, unknown>>;
|
|
24
|
+
createProvider(config: TConfig): Promise<IOProvider>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Properties of a file/folder item. `size`, `lastModified`, `isFolder` and
|
|
3
|
+
* `contentType` are well-known, framework-guaranteed fields every provider
|
|
4
|
+
* must populate so generic consumers (CLI, copy/move orchestration) can rely
|
|
5
|
+
* on them without knowing the provider. Anything provider-specific (etag,
|
|
6
|
+
* storage class, custom tags) goes in `properties`, validated against that
|
|
7
|
+
* provider's `propertySchema`.
|
|
8
|
+
*/
|
|
9
|
+
export interface ItemProperties {
|
|
10
|
+
readonly size: number | undefined;
|
|
11
|
+
readonly lastModified: Date | undefined;
|
|
12
|
+
readonly isFolder: boolean;
|
|
13
|
+
readonly contentType?: string;
|
|
14
|
+
readonly properties: Readonly<Record<string, unknown>>;
|
|
15
|
+
}
|
package/src/Part.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChunkRef } from "./ChunkRef.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One independently readable/writable part of a multipart transfer.
|
|
5
|
+
* Parts may be processed concurrently (e.g. `Promise.all` over N parts);
|
|
6
|
+
* the provider assembles/commits them once all parts finish.
|
|
7
|
+
*/
|
|
8
|
+
export interface Part {
|
|
9
|
+
readonly index: number;
|
|
10
|
+
readonly offset: number;
|
|
11
|
+
readonly stream: ReadableStream<ChunkRef> | WritableStream<ChunkRef>;
|
|
12
|
+
complete(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StreamHandle } from "./StreamHandle.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A decorator wraps a {@link StreamHandle} and returns an enhanced handle
|
|
5
|
+
* with additional capability methods (e.g. `seekable` adds `seek`/
|
|
6
|
+
* `readRange`; `locally-cached` transparently serves repeated reads from a
|
|
7
|
+
* local cache).
|
|
8
|
+
*/
|
|
9
|
+
export type StreamDecorator = (handle: StreamHandle) => StreamHandle;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ChunkRef } from "./ChunkRef.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A handle to a readable or writable stream of {@link ChunkRef}s, plus
|
|
5
|
+
* optional capability methods for providers/decorators that support
|
|
6
|
+
* random-access beyond plain sequential read (e.g. `seekable`).
|
|
7
|
+
*
|
|
8
|
+
* Capability methods are present only when supported - consumers
|
|
9
|
+
* feature-detect via `typeof handle.seek === "function"` etc.
|
|
10
|
+
*/
|
|
11
|
+
export interface StreamHandle {
|
|
12
|
+
readonly stream: ReadableStream<ChunkRef> | WritableStream<ChunkRef>;
|
|
13
|
+
seek?(offset: number): Promise<void>;
|
|
14
|
+
readRange?(start: number, end: number): Promise<ReadableStream<ChunkRef>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global telemetry hooks supplied once at framework initialisation. Every
|
|
3
|
+
* operation is given a correlation id and reports through the same hooks,
|
|
4
|
+
* tagged with that id/operation-type - callers don't need to thread a
|
|
5
|
+
* callback through every call, but can still track individual operations
|
|
6
|
+
* via the id in emitted events.
|
|
7
|
+
*/
|
|
8
|
+
export interface TelemetryHooks {
|
|
9
|
+
onProgress?(event: {
|
|
10
|
+
operationId: string;
|
|
11
|
+
type: string;
|
|
12
|
+
bytesProcessed: number;
|
|
13
|
+
totalBytes?: number;
|
|
14
|
+
}): void;
|
|
15
|
+
onMetric?(event: { name: string; value: number; tags?: Record<string, string> }): void;
|
|
16
|
+
}
|