@ontemper/sdk 0.3.6 → 0.4.1
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/README.md +51 -2
- package/dist/Edi.d.ts +34 -0
- package/dist/Edi.d.ts.map +1 -0
- package/dist/Edi.js +244 -0
- package/dist/Edi.js.map +1 -0
- package/dist/EdiControlNumbers.d.ts +24 -0
- package/dist/EdiControlNumbers.d.ts.map +1 -0
- package/dist/EdiControlNumbers.js +65 -0
- package/dist/EdiControlNumbers.js.map +1 -0
- package/dist/EdiError.d.ts +21 -0
- package/dist/EdiError.d.ts.map +1 -0
- package/dist/EdiError.js +19 -0
- package/dist/EdiError.js.map +1 -0
- package/dist/EdiModel.d.ts +83 -0
- package/dist/EdiModel.d.ts.map +1 -0
- package/dist/EdiModel.js +2 -0
- package/dist/EdiModel.js.map +1 -0
- package/dist/Manifest.d.ts +1 -1
- package/dist/Manifest.js +1 -1
- package/dist/Sftp.d.ts +82 -0
- package/dist/Sftp.d.ts.map +1 -0
- package/dist/Sftp.js +135 -0
- package/dist/Sftp.js.map +1 -0
- package/dist/SftpTesting.d.ts +28 -0
- package/dist/SftpTesting.d.ts.map +1 -0
- package/dist/SftpTesting.js +91 -0
- package/dist/SftpTesting.js.map +1 -0
- package/dist/SftpTransport.d.ts +55 -0
- package/dist/SftpTransport.d.ts.map +1 -0
- package/dist/SftpTransport.js +203 -0
- package/dist/SftpTransport.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -15
- package/dist/cli.js.map +1 -1
- package/package.json +17 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdiModel.js","sourceRoot":"","sources":["../src/EdiModel.ts"],"names":[],"mappings":""}
|
package/dist/Manifest.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpApi, type HttpApiGroup } from 'effect/unstable/httpapi';
|
|
2
2
|
import { type WorkflowManifest } from './Contract/Manifest.js';
|
|
3
|
-
export declare const SDK_VERSION = "0.
|
|
3
|
+
export declare const SDK_VERSION = "0.4.1";
|
|
4
4
|
export declare const RUNTIME_API_VERSION = 1;
|
|
5
5
|
/** The route the SDK serves for platform probes; an API declaring `GET` on it collides with it on every request. */
|
|
6
6
|
export declare const HEALTHCHECK_PATH = "/healthcheck";
|
package/dist/Manifest.js
CHANGED
|
@@ -5,7 +5,7 @@ import { declarationOf } from './Resource.js';
|
|
|
5
5
|
import { Trigger } from './Trigger.js';
|
|
6
6
|
const decodeTrigger = Schema.decodeUnknownSync(TriggerMetadata, { onExcessProperty: 'error' });
|
|
7
7
|
const identityOf = (resource) => `${resource.kind}:${resource.kind === 'resourceType' ? resource.type : resource.key}`;
|
|
8
|
-
export const SDK_VERSION = '0.
|
|
8
|
+
export const SDK_VERSION = '0.4.1';
|
|
9
9
|
export const RUNTIME_API_VERSION = 1;
|
|
10
10
|
/** The route the SDK serves for platform probes; an API declaring `GET` on it collides with it on every request. */
|
|
11
11
|
export const HEALTHCHECK_PATH = '/healthcheck';
|
package/dist/Sftp.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Redacted, Schema, type Scope } from 'effect';
|
|
2
|
+
import { type SftpEntry, type SftpFileType, type SftpStats, type SftpTransport } from './SftpTransport.js';
|
|
3
|
+
export type { SftpChannel, SftpEntry, SftpFileType, SftpStats, SftpTransferOptions, SftpTransport, SftpTransportOptions, } from './SftpTransport.js';
|
|
4
|
+
declare const SftpError_base: Schema.Class<SftpError, Schema.TaggedStruct<"SftpError", {
|
|
5
|
+
readonly host: Schema.String;
|
|
6
|
+
readonly operation: Schema.String;
|
|
7
|
+
readonly path: Schema.optional<Schema.String>;
|
|
8
|
+
readonly message: Schema.String;
|
|
9
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
10
|
+
}>, import("effect/Cause").YieldableError>;
|
|
11
|
+
/** An SFTP operation failed; its `SFTP <operation> failed.` line carries the cause. */
|
|
12
|
+
export declare class SftpError extends SftpError_base {
|
|
13
|
+
}
|
|
14
|
+
declare const SftpHostKeyError_base: Schema.Class<SftpHostKeyError, Schema.TaggedStruct<"SftpHostKeyError", {
|
|
15
|
+
readonly host: Schema.String;
|
|
16
|
+
/** The OpenSSH `SHA256:` fingerprint the server offered, when it offered one. */
|
|
17
|
+
readonly offered: Schema.optional<Schema.String>;
|
|
18
|
+
readonly message: Schema.String;
|
|
19
|
+
}>, import("effect/Cause").YieldableError>;
|
|
20
|
+
/** The connection was refused before any credential left: no pins, an unreadable pin, or an unpinned host key. */
|
|
21
|
+
export declare class SftpHostKeyError extends SftpHostKeyError_base {
|
|
22
|
+
}
|
|
23
|
+
export interface SftpConnectOptions {
|
|
24
|
+
readonly host: string;
|
|
25
|
+
/** Default 22. */
|
|
26
|
+
readonly port?: number | undefined;
|
|
27
|
+
readonly username: string;
|
|
28
|
+
readonly password?: Redacted.Redacted<string> | undefined;
|
|
29
|
+
/** PEM or OpenSSH text; literal `\\n` sequences from one-line variables become newlines. */
|
|
30
|
+
readonly privateKey?: Redacted.Redacted<string> | undefined;
|
|
31
|
+
readonly passphrase?: Redacted.Redacted<string> | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Accepted server host keys as OpenSSH `SHA256:<base64>` fingerprints (`ssh-keygen -lf`). Required:
|
|
34
|
+
* with none, or with a key not in the list, the connection fails before authentication.
|
|
35
|
+
*/
|
|
36
|
+
readonly hostKeyFingerprints: ReadonlyArray<string>;
|
|
37
|
+
/** SSH handshake timeout in milliseconds. Default 20000. */
|
|
38
|
+
readonly readyTimeoutMs?: number | undefined;
|
|
39
|
+
/** Bytes per pipelined request, 32 KiB to 1 MiB. Default 32 KiB. */
|
|
40
|
+
readonly chunkBytes?: number | undefined;
|
|
41
|
+
/** Pipelined requests in flight per transfer, 1 to 64. Default 16. */
|
|
42
|
+
readonly concurrency?: number | undefined;
|
|
43
|
+
/** Largest file `get` buffers in memory. Default 256 MiB; use `download` for larger files. */
|
|
44
|
+
readonly maxBufferBytes?: number | undefined;
|
|
45
|
+
}
|
|
46
|
+
/** One connected SFTP mailbox. Paths are remote unless the name says `local`. */
|
|
47
|
+
export interface SftpSession {
|
|
48
|
+
readonly host: string;
|
|
49
|
+
readonly list: (path: string) => Effect.Effect<ReadonlyArray<SftpEntry>, SftpError>;
|
|
50
|
+
readonly stat: (path: string) => Effect.Effect<SftpStats, SftpError>;
|
|
51
|
+
/** The entry's type without following a link, or `false` when nothing exists at the path. */
|
|
52
|
+
readonly exists: (path: string) => Effect.Effect<false | SftpFileType, SftpError>;
|
|
53
|
+
/** Reads the whole file into memory with pipelined requests. */
|
|
54
|
+
readonly get: (path: string) => Effect.Effect<Buffer, SftpError>;
|
|
55
|
+
/** Replaces the file with `data` using pipelined requests. */
|
|
56
|
+
readonly put: (data: Uint8Array, path: string) => Effect.Effect<void, SftpError>;
|
|
57
|
+
/** Streams the remote file to a local file with pipelined requests, never holding it in memory. */
|
|
58
|
+
readonly download: (path: string, localPath: string) => Effect.Effect<void, SftpError>;
|
|
59
|
+
/** Streams a local file to the remote path with pipelined requests. */
|
|
60
|
+
readonly upload: (localPath: string, path: string) => Effect.Effect<void, SftpError>;
|
|
61
|
+
readonly rename: (from: string, to: string) => Effect.Effect<void, SftpError>;
|
|
62
|
+
readonly remove: (path: string) => Effect.Effect<void, SftpError>;
|
|
63
|
+
readonly mkdir: (path: string) => Effect.Effect<void, SftpError>;
|
|
64
|
+
}
|
|
65
|
+
export interface SftpConnector {
|
|
66
|
+
/**
|
|
67
|
+
* Connects for the life of the enclosing scope: closing the scope, including on the Run deadline or an
|
|
68
|
+
* interrupt, closes the connection and logs `SFTP close`.
|
|
69
|
+
*/
|
|
70
|
+
readonly connect: (options: SftpConnectOptions) => Effect.Effect<SftpSession, SftpError | SftpHostKeyError, Scope.Scope>;
|
|
71
|
+
}
|
|
72
|
+
/** The OpenSSH `SHA256:` fingerprint of a raw public host key, without base64 padding. */
|
|
73
|
+
export declare const hostKeyFingerprint: (key: Uint8Array) => string;
|
|
74
|
+
declare const Sftp_base: Context.ServiceClass<Sftp, "@temper/workflow/Sftp", SftpConnector>;
|
|
75
|
+
/** First-party SFTP: `yield* Sftp`, then `connect` inside the Run's scope. */
|
|
76
|
+
export declare class Sftp extends Sftp_base {
|
|
77
|
+
/** SFTP over `ssh2`. */
|
|
78
|
+
static readonly layer: Layer.Layer<Sftp, never, never>;
|
|
79
|
+
/** The same client over another transport, such as `memorySftp` from `@temper/workflow/sftp/testing`. */
|
|
80
|
+
static readonly layerWith: (transport: SftpTransport) => Layer.Layer<Sftp, never, never>;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=Sftp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sftp.d.ts","sourceRoot":"","sources":["../src/Sftp.ts"],"names":[],"mappings":"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"}
|
package/dist/Sftp.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { Context, Effect, Layer, Redacted, Schema } from 'effect';
|
|
3
|
+
import { withSpanLines } from './LogContext.js';
|
|
4
|
+
import { registerSecrets } from './Redaction.js';
|
|
5
|
+
import { ssh2Transport, } from './SftpTransport.js';
|
|
6
|
+
/** An SFTP operation failed; its `SFTP <operation> failed.` line carries the cause. */
|
|
7
|
+
export class SftpError extends Schema.TaggedError()('SftpError', {
|
|
8
|
+
host: Schema.String,
|
|
9
|
+
operation: Schema.String,
|
|
10
|
+
path: Schema.optional(Schema.String),
|
|
11
|
+
message: Schema.String,
|
|
12
|
+
cause: Schema.optional(Schema.Defect()),
|
|
13
|
+
}) {
|
|
14
|
+
}
|
|
15
|
+
/** The connection was refused before any credential left: no pins, an unreadable pin, or an unpinned host key. */
|
|
16
|
+
export class SftpHostKeyError extends Schema.TaggedError()('SftpHostKeyError', {
|
|
17
|
+
host: Schema.String,
|
|
18
|
+
/** The OpenSSH `SHA256:` fingerprint the server offered, when it offered one. */
|
|
19
|
+
offered: Schema.optional(Schema.String),
|
|
20
|
+
message: Schema.String,
|
|
21
|
+
}) {
|
|
22
|
+
}
|
|
23
|
+
const DEFAULTS = { port: 22, readyTimeoutMs: 20_000, chunkBytes: 32 * 1024, concurrency: 16 };
|
|
24
|
+
const DEFAULT_MAX_BUFFER_BYTES = 256 * 1024 * 1024;
|
|
25
|
+
const FINGERPRINT = /^SHA256:[A-Za-z0-9+/]{43}$/;
|
|
26
|
+
/** The OpenSSH `SHA256:` fingerprint of a raw public host key, without base64 padding. */
|
|
27
|
+
export const hostKeyFingerprint = (key) => `SHA256:${createHash('sha256').update(key).digest('base64').replace(/=+$/u, '')}`;
|
|
28
|
+
const inRange = (value, fallback, minimum, maximum) => value === undefined || (Number.isInteger(value) && value >= minimum && value <= maximum)
|
|
29
|
+
? (value ?? fallback)
|
|
30
|
+
: undefined;
|
|
31
|
+
/** Names logged per listing; a longer list would push the line past the logger's size cap and lose its fields. */
|
|
32
|
+
const LISTED_NAMES = 100;
|
|
33
|
+
const listed = (entries) => {
|
|
34
|
+
if (entries === undefined)
|
|
35
|
+
return {};
|
|
36
|
+
const files = entries.slice(0, LISTED_NAMES).map((entry) => entry.name);
|
|
37
|
+
return entries.length > LISTED_NAMES ? { files, fileCount: entries.length } : { files };
|
|
38
|
+
};
|
|
39
|
+
/** `SFTP <operation>` lines on the SDK logger and one tracer span; failures carry the raw cause as `err`. */
|
|
40
|
+
const traced = (host, operation, fields = () => ({})) => (effect) => effect.pipe(withSpanLines(`SFTP ${operation}`, (outcome) => ({ type: 'sftp', sftp: { host, operation, ...fields(outcome) } }), {
|
|
41
|
+
err: ({ error }) => (error instanceof SftpError && error.cause instanceof Error ? error.cause : undefined),
|
|
42
|
+
}), Effect.withSpan(`SFTP ${operation}`, { attributes: { 'sftp.host': host, 'sftp.operation': operation } }));
|
|
43
|
+
const attempt = (host, operation, path, run) => Effect.tryPromise({
|
|
44
|
+
try: run,
|
|
45
|
+
catch: (cause) => new SftpError({
|
|
46
|
+
host,
|
|
47
|
+
operation,
|
|
48
|
+
path,
|
|
49
|
+
message: `SFTP ${operation}${path === undefined ? '' : ` ${path}`} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
50
|
+
cause,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
const sessionOf = (host, channel, transfer, maxBytes) => {
|
|
54
|
+
const run = (operation, path, go, fields = () => ({})) => attempt(host, operation, path, go).pipe(traced(host, operation, (outcome) => ({ path, ...fields(outcome) })));
|
|
55
|
+
return {
|
|
56
|
+
host,
|
|
57
|
+
list: (path) => run('list', path, () => channel.list(path), (outcome) => listed(outcome?.result)),
|
|
58
|
+
stat: (path) => run('stat', path, () => channel.stat(path)),
|
|
59
|
+
exists: (path) => run('exists', path, async () => (await channel.lstat(path))?.type ?? false, (outcome) => ({ exists: outcome?.result })),
|
|
60
|
+
// File contents never reach a log line: only the byte count does.
|
|
61
|
+
get: (path) => run('get', path, () => channel.read(path, { ...transfer, maxBytes }), (outcome) => ({ bytes: outcome?.result?.length })),
|
|
62
|
+
put: (data, path) => run('put', path, () => channel.write(path, data, transfer), () => ({ bytes: data.byteLength })),
|
|
63
|
+
download: (path, localPath) => run('download', path, () => channel.download(path, localPath, transfer)),
|
|
64
|
+
upload: (localPath, path) => run('upload', path, () => channel.upload(localPath, path, transfer)),
|
|
65
|
+
rename: (from, to) => run('rename', from, () => channel.rename(from, to), () => ({ destinationPath: to })),
|
|
66
|
+
remove: (path) => run('remove', path, () => channel.remove(path)),
|
|
67
|
+
mkdir: (path) => run('mkdir', path, () => channel.mkdir(path)),
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const makeSftp = (transport) => ({
|
|
71
|
+
connect: (options) => {
|
|
72
|
+
const { host } = options;
|
|
73
|
+
const refuse = (message, offered) => new SftpHostKeyError({ host, offered, message });
|
|
74
|
+
const pins = new Set(options.hostKeyFingerprints.map((pin) => pin.trim().replace(/=+$/u, '')));
|
|
75
|
+
const privateKey = options.privateKey && Redacted.value(options.privateKey).split('\\n').join('\n');
|
|
76
|
+
const secrets = [options.password, options.privateKey, options.passphrase].flatMap((secret) => secret === undefined ? [] : [Redacted.value(secret)]);
|
|
77
|
+
const port = options.port ?? DEFAULTS.port;
|
|
78
|
+
const chunkBytes = inRange(options.chunkBytes, DEFAULTS.chunkBytes, 32 * 1024, 1024 * 1024);
|
|
79
|
+
const concurrency = inRange(options.concurrency, DEFAULTS.concurrency, 1, 64);
|
|
80
|
+
const maxBytes = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
|
|
81
|
+
let offered;
|
|
82
|
+
const open = Effect.gen(function* () {
|
|
83
|
+
offered = undefined;
|
|
84
|
+
if (pins.size === 0)
|
|
85
|
+
return yield* refuse('SFTP host-key pinning is required: set hostKeyFingerprints.');
|
|
86
|
+
const malformed = [...pins].find((pin) => !FINGERPRINT.test(pin));
|
|
87
|
+
if (malformed !== undefined)
|
|
88
|
+
return yield* refuse(`Host-key pin ${malformed} is not an OpenSSH SHA256 fingerprint.`);
|
|
89
|
+
if (chunkBytes === undefined || concurrency === undefined || !Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
|
|
90
|
+
return yield* new SftpError({ host, operation: 'connect', message: 'SFTP transfer options are out of range.' });
|
|
91
|
+
}
|
|
92
|
+
return yield* Effect.tryPromise({
|
|
93
|
+
try: (signal) => transport.connect({
|
|
94
|
+
host,
|
|
95
|
+
port,
|
|
96
|
+
username: options.username,
|
|
97
|
+
password: options.password && Redacted.value(options.password),
|
|
98
|
+
privateKey,
|
|
99
|
+
passphrase: options.passphrase && Redacted.value(options.passphrase),
|
|
100
|
+
readyTimeoutMs: options.readyTimeoutMs ?? DEFAULTS.readyTimeoutMs,
|
|
101
|
+
verifyHostKey: (key) => {
|
|
102
|
+
const fingerprint = hostKeyFingerprint(key);
|
|
103
|
+
if (pins.has(fingerprint))
|
|
104
|
+
return true;
|
|
105
|
+
offered = fingerprint;
|
|
106
|
+
return false;
|
|
107
|
+
},
|
|
108
|
+
}, signal),
|
|
109
|
+
catch: (cause) => offered === undefined
|
|
110
|
+
? new SftpError({
|
|
111
|
+
host,
|
|
112
|
+
operation: 'connect',
|
|
113
|
+
message: `SFTP connect failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
114
|
+
cause,
|
|
115
|
+
})
|
|
116
|
+
: refuse(`SFTP server ${host} offered untrusted host-key fingerprint ${offered}.`, offered),
|
|
117
|
+
}).pipe(Effect.map((channel) => ({ channel, chunkBytes, concurrency })));
|
|
118
|
+
}).pipe(traced(host, 'connect'));
|
|
119
|
+
const close = (channel) => Effect.promise(() => channel.end()).pipe(withSpanLines('SFTP close', { type: 'sftp', sftp: { host, operation: 'close' } }));
|
|
120
|
+
return Effect.gen(function* () {
|
|
121
|
+
yield* registerSecrets([...secrets, ...(privateKey === undefined ? [] : [privateKey])]);
|
|
122
|
+
// The connect attempt stays interruptible; once it succeeds, its close is registered before anything else runs.
|
|
123
|
+
const opened = yield* Effect.uninterruptibleMask((restore) => Effect.tap(restore(open), ({ channel }) => Effect.addFinalizer(() => close(channel))));
|
|
124
|
+
return sessionOf(host, opened.channel, opened, maxBytes);
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
/** First-party SFTP: `yield* Sftp`, then `connect` inside the Run's scope. */
|
|
129
|
+
export class Sftp extends Context.Service()('@temper/workflow/Sftp') {
|
|
130
|
+
/** SFTP over `ssh2`. */
|
|
131
|
+
static layer = Layer.succeed(Sftp, makeSftp(ssh2Transport));
|
|
132
|
+
/** The same client over another transport, such as `memorySftp` from `@temper/workflow/sftp/testing`. */
|
|
133
|
+
static layerWith = (transport) => Layer.succeed(Sftp, makeSftp(transport));
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=Sftp.js.map
|
package/dist/Sftp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sftp.js","sourceRoot":"","sources":["../src/Sftp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Layer } from 'effect';
|
|
2
|
+
import { Sftp } from './Sftp.js';
|
|
3
|
+
import type { SftpTransport, SftpTransportOptions } from './SftpTransport.js';
|
|
4
|
+
export interface MemorySftpOptions {
|
|
5
|
+
/** Host name → path → bytes; the test reads and seeds them directly. A host that is absent refuses the connection. */
|
|
6
|
+
readonly hosts: Record<string, Map<string, Buffer>>;
|
|
7
|
+
/** Every file's modification time, epoch milliseconds. Default 0. */
|
|
8
|
+
readonly modifyTime?: number;
|
|
9
|
+
/** The host key every memory host presents. Pin `MemorySftp.fingerprint`. */
|
|
10
|
+
readonly hostKey?: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
export interface MemorySftp {
|
|
13
|
+
/** Provide in place of `Sftp.layer`. */
|
|
14
|
+
readonly layer: Layer.Layer<Sftp>;
|
|
15
|
+
readonly transport: SftpTransport;
|
|
16
|
+
/** The OpenSSH fingerprint of the presented host key. */
|
|
17
|
+
readonly fingerprint: string;
|
|
18
|
+
/** Every connection the client completed, in order, with the options it sent. */
|
|
19
|
+
readonly connections: Array<Omit<SftpTransportOptions, 'verifyHostKey'>>;
|
|
20
|
+
/** Connections not yet closed. */
|
|
21
|
+
readonly open: () => number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An in-memory SFTP transport for workflow tests: the SDK client runs unchanged on top of it, so host-key
|
|
25
|
+
* pinning, span lines, redaction, and scope release behave as in production. Directories are implicit.
|
|
26
|
+
*/
|
|
27
|
+
export declare const memorySftp: (options: MemorySftpOptions) => MemorySftp;
|
|
28
|
+
//# sourceMappingURL=SftpTesting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SftpTesting.d.ts","sourceRoot":"","sources":["../src/SftpTesting.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAqC,aAAa,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEjH,MAAM,WAAW,iBAAiB;IAChC,sHAAsH;IACtH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC,CAAC;IACzE,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC;CAC7B;AAKD;;;GAGG;AACH,eAAO,MAAM,UAAU,YAAa,iBAAiB,KAAG,UA8EvD,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { Sftp, hostKeyFingerprint } from './Sftp.js';
|
|
3
|
+
const notFound = (operation, path) => Object.assign(new Error(`${operation}: No such file ${path}`), { code: 2 });
|
|
4
|
+
/**
|
|
5
|
+
* An in-memory SFTP transport for workflow tests: the SDK client runs unchanged on top of it, so host-key
|
|
6
|
+
* pinning, span lines, redaction, and scope release behave as in production. Directories are implicit.
|
|
7
|
+
*/
|
|
8
|
+
export const memorySftp = (options) => {
|
|
9
|
+
const hostKey = options.hostKey ?? Buffer.from('temper-memory-sftp-host-key');
|
|
10
|
+
const connections = [];
|
|
11
|
+
let open = 0;
|
|
12
|
+
const statsOf = (data) => ({
|
|
13
|
+
type: '-',
|
|
14
|
+
size: data.length,
|
|
15
|
+
modifyTime: options.modifyTime ?? 0,
|
|
16
|
+
accessTime: options.modifyTime ?? 0,
|
|
17
|
+
});
|
|
18
|
+
const transport = {
|
|
19
|
+
connect: async ({ verifyHostKey, ...sent }) => {
|
|
20
|
+
const files = Object.hasOwn(options.hosts, sent.host) ? options.hosts[sent.host] : undefined;
|
|
21
|
+
if (files === undefined)
|
|
22
|
+
throw Object.assign(new Error(`getaddrinfo ENOTFOUND ${sent.host}`), { code: 'ENOTFOUND' });
|
|
23
|
+
if (!verifyHostKey(hostKey))
|
|
24
|
+
throw new Error('Handshake failed: host key verification failed');
|
|
25
|
+
connections.push(sent);
|
|
26
|
+
open++;
|
|
27
|
+
let ended = false;
|
|
28
|
+
const file = (operation, path) => {
|
|
29
|
+
const found = files.get(path);
|
|
30
|
+
if (found === undefined)
|
|
31
|
+
throw notFound(operation, path);
|
|
32
|
+
return found;
|
|
33
|
+
};
|
|
34
|
+
const channel = {
|
|
35
|
+
list: async (dir) => {
|
|
36
|
+
const prefix = dir.endsWith('/') ? dir : `${dir}/`;
|
|
37
|
+
return [...files.entries()]
|
|
38
|
+
.filter(([path]) => path.startsWith(prefix) && !path.slice(prefix.length).includes('/'))
|
|
39
|
+
.map(([path, entry]) => ({ name: path.slice(prefix.length), ...statsOf(entry) }));
|
|
40
|
+
},
|
|
41
|
+
stat: async (path) => statsOf(file('stat', path)),
|
|
42
|
+
lstat: async (path) => {
|
|
43
|
+
const found = files.get(path);
|
|
44
|
+
return found === undefined ? undefined : statsOf(found);
|
|
45
|
+
},
|
|
46
|
+
read: async (path, { maxBytes }) => {
|
|
47
|
+
const data = file('get', path);
|
|
48
|
+
if (data.length > maxBytes)
|
|
49
|
+
throw new RangeError(`${path} has ${data.length} bytes; get buffers at most ${maxBytes}.`);
|
|
50
|
+
return Buffer.from(data);
|
|
51
|
+
},
|
|
52
|
+
write: async (path, data) => {
|
|
53
|
+
files.set(path, Buffer.from(data));
|
|
54
|
+
},
|
|
55
|
+
download: async (path, localPath) => {
|
|
56
|
+
await writeFile(localPath, file('download', path));
|
|
57
|
+
},
|
|
58
|
+
upload: async (localPath, path) => {
|
|
59
|
+
files.set(path, await readFile(localPath));
|
|
60
|
+
},
|
|
61
|
+
rename: async (from, to) => {
|
|
62
|
+
const moved = file('rename', from);
|
|
63
|
+
if (files.has(to))
|
|
64
|
+
throw Object.assign(new Error(`rename: ${to} exists`), { code: 4 });
|
|
65
|
+
files.delete(from);
|
|
66
|
+
files.set(to, moved);
|
|
67
|
+
},
|
|
68
|
+
remove: async (path) => {
|
|
69
|
+
file('remove', path);
|
|
70
|
+
files.delete(path);
|
|
71
|
+
},
|
|
72
|
+
mkdir: async () => undefined,
|
|
73
|
+
end: async () => {
|
|
74
|
+
if (ended)
|
|
75
|
+
return;
|
|
76
|
+
ended = true;
|
|
77
|
+
open--;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
return channel;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
layer: Sftp.layerWith(transport),
|
|
85
|
+
transport,
|
|
86
|
+
fingerprint: hostKeyFingerprint(hostKey),
|
|
87
|
+
connections,
|
|
88
|
+
open: () => open,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=SftpTesting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SftpTesting.js","sourceRoot":"","sources":["../src/SftpTesting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** `d` directory, `-` regular file, `l` symbolic link, `?` anything else; the `ls -l` letters. */
|
|
2
|
+
export type SftpFileType = 'd' | '-' | 'l' | '?';
|
|
3
|
+
export interface SftpStats {
|
|
4
|
+
readonly type: SftpFileType;
|
|
5
|
+
readonly size: number;
|
|
6
|
+
/** Epoch milliseconds. */
|
|
7
|
+
readonly modifyTime: number;
|
|
8
|
+
/** Epoch milliseconds. */
|
|
9
|
+
readonly accessTime: number;
|
|
10
|
+
}
|
|
11
|
+
export interface SftpEntry extends SftpStats {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
}
|
|
14
|
+
/** How one pipelined transfer splits a file: request size and requests in flight. */
|
|
15
|
+
export interface SftpTransferOptions {
|
|
16
|
+
readonly chunkBytes: number;
|
|
17
|
+
readonly concurrency: number;
|
|
18
|
+
}
|
|
19
|
+
/** What a transport receives to open one SSH connection with one SFTP channel. */
|
|
20
|
+
export interface SftpTransportOptions {
|
|
21
|
+
readonly host: string;
|
|
22
|
+
readonly port: number;
|
|
23
|
+
readonly username: string;
|
|
24
|
+
readonly password: string | undefined;
|
|
25
|
+
readonly privateKey: string | undefined;
|
|
26
|
+
readonly passphrase: string | undefined;
|
|
27
|
+
readonly readyTimeoutMs: number;
|
|
28
|
+
/** Decides on the server's raw public host key before any credential is sent; `false` aborts. */
|
|
29
|
+
readonly verifyHostKey: (key: Uint8Array) => boolean;
|
|
30
|
+
}
|
|
31
|
+
/** One open SFTP channel. Every path is remote unless the name says `local`. */
|
|
32
|
+
export interface SftpChannel {
|
|
33
|
+
readonly list: (path: string) => Promise<ReadonlyArray<SftpEntry>>;
|
|
34
|
+
readonly stat: (path: string) => Promise<SftpStats>;
|
|
35
|
+
/** `undefined` when nothing exists at the path; a link is reported as a link. */
|
|
36
|
+
readonly lstat: (path: string) => Promise<SftpStats | undefined>;
|
|
37
|
+
readonly read: (path: string, options: SftpTransferOptions & {
|
|
38
|
+
readonly maxBytes: number;
|
|
39
|
+
}) => Promise<Buffer>;
|
|
40
|
+
readonly write: (path: string, data: Uint8Array, options: SftpTransferOptions) => Promise<void>;
|
|
41
|
+
readonly download: (path: string, localPath: string, options: SftpTransferOptions) => Promise<void>;
|
|
42
|
+
readonly upload: (localPath: string, path: string, options: SftpTransferOptions) => Promise<void>;
|
|
43
|
+
readonly rename: (from: string, to: string) => Promise<void>;
|
|
44
|
+
readonly remove: (path: string) => Promise<void>;
|
|
45
|
+
readonly mkdir: (path: string) => Promise<void>;
|
|
46
|
+
/** Closes the connection; resolves once the socket closed or the close timed out. */
|
|
47
|
+
readonly end: () => Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
/** Opens SFTP channels; `signal` aborts a connection attempt that has not finished. */
|
|
50
|
+
export interface SftpTransport {
|
|
51
|
+
readonly connect: (options: SftpTransportOptions, signal: AbortSignal) => Promise<SftpChannel>;
|
|
52
|
+
}
|
|
53
|
+
/** SFTP over `ssh2`: one SSH connection and one SFTP channel per `connect`. */
|
|
54
|
+
export declare const ssh2Transport: SftpTransport;
|
|
55
|
+
//# sourceMappingURL=SftpTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SftpTransport.d.ts","sourceRoot":"","sources":["../src/SftpTransport.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,MAAM,MAAM,YAAY,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEjD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,iGAAiG;IACjG,QAAQ,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC;CACtD;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IACnE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACpD,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IACjE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/G,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpG,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,qFAAqF;IACrF,QAAQ,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAChG;AAmLD,+EAA+E;AAC/E,eAAO,MAAM,aAAa,EAAE,aAwC3B,CAAC"}
|