@zero-transfer/mft 0.4.0 → 0.4.2
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/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +368 -6
- package/dist/index.d.ts +368 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -833,7 +833,41 @@ interface TransferEngineOptions {
|
|
|
833
833
|
/** Clock used for receipts and progress events. Defaults to `new Date()`. */
|
|
834
834
|
now?: () => Date;
|
|
835
835
|
}
|
|
836
|
-
/**
|
|
836
|
+
/**
|
|
837
|
+
* Executes transfer jobs and produces audit-friendly receipts.
|
|
838
|
+
*
|
|
839
|
+
* The engine is the lowest-level entry point in the transfer stack: it owns
|
|
840
|
+
* retry policy, attempt history, abort propagation, progress event
|
|
841
|
+
* normalization, and receipt construction. Most callers reach the engine
|
|
842
|
+
* indirectly through {@link runRoute}, {@link uploadFile}, {@link downloadFile},
|
|
843
|
+
* {@link copyBetween}, or {@link TransferQueue}; instantiate it directly when
|
|
844
|
+
* you need full control over execution semantics.
|
|
845
|
+
*
|
|
846
|
+
* @example Execute a single job with a custom executor
|
|
847
|
+
* ```ts
|
|
848
|
+
* import { TransferEngine, type TransferExecutor, type TransferJob } from "@zero-transfer/sdk";
|
|
849
|
+
*
|
|
850
|
+
* const engine = new TransferEngine();
|
|
851
|
+
*
|
|
852
|
+
* const executor: TransferExecutor = async ({ job, signal, onProgress }) => {
|
|
853
|
+
* onProgress?.({ jobId: job.id, bytesTransferred: 0 });
|
|
854
|
+
* // … perform the bytes here, honoring `signal` …
|
|
855
|
+
* return { jobId: job.id, bytesTransferred: 1234, completedAt: new Date() };
|
|
856
|
+
* };
|
|
857
|
+
*
|
|
858
|
+
* const job: TransferJob = {
|
|
859
|
+
* id: "manual-1",
|
|
860
|
+
* operation: "upload",
|
|
861
|
+
* source: { profile: localProfile, path: "./data.bin" },
|
|
862
|
+
* destination: { profile: s3Profile, path: "/data/data.bin" },
|
|
863
|
+
* };
|
|
864
|
+
*
|
|
865
|
+
* const receipt = await engine.execute(job, executor, {
|
|
866
|
+
* retry: { maxAttempts: 3, baseDelayMs: 250 },
|
|
867
|
+
* });
|
|
868
|
+
* console.log(receipt.attempts.length); // 1 on success
|
|
869
|
+
* ```
|
|
870
|
+
*/
|
|
837
871
|
declare class TransferEngine {
|
|
838
872
|
private readonly now;
|
|
839
873
|
/**
|
|
@@ -1416,9 +1450,41 @@ interface RunRouteOptions {
|
|
|
1416
1450
|
/**
|
|
1417
1451
|
* Executes an MFT route as a single transfer through the supplied client.
|
|
1418
1452
|
*
|
|
1453
|
+
* Connects the source and destination profiles, runs the route's transfer
|
|
1454
|
+
* through the engine, and returns the resulting receipt. The friendly helpers
|
|
1455
|
+
* {@link uploadFile}, {@link downloadFile}, and {@link copyBetween} synthesize
|
|
1456
|
+
* routes and delegate to this function, so behaviour around retry, abort,
|
|
1457
|
+
* progress, timeout, and bandwidth limits is identical.
|
|
1458
|
+
*
|
|
1419
1459
|
* @param options - Client, route, and optional engine/abort/retry hooks.
|
|
1420
1460
|
* @returns Receipt produced by the underlying transfer engine.
|
|
1421
1461
|
* @throws {@link ConfigurationError} When the route is disabled.
|
|
1462
|
+
*
|
|
1463
|
+
* @example Run a pre-built route with progress + retry
|
|
1464
|
+
* ```ts
|
|
1465
|
+
* import { createTransferClient, runRoute, type MftRoute } from "@zero-transfer/sdk";
|
|
1466
|
+
*
|
|
1467
|
+
* const route: MftRoute = {
|
|
1468
|
+
* id: "nightly-export",
|
|
1469
|
+
* operation: "copy",
|
|
1470
|
+
* source: {
|
|
1471
|
+
* path: "/exports/daily.csv",
|
|
1472
|
+
* profile: { host: "sftp.example.com", provider: "sftp", username: "etl" },
|
|
1473
|
+
* },
|
|
1474
|
+
* destination: {
|
|
1475
|
+
* path: "warehouse/daily.csv",
|
|
1476
|
+
* profile: { host: "warehouse", provider: "s3", s3: { region: "us-east-1" } },
|
|
1477
|
+
* },
|
|
1478
|
+
* };
|
|
1479
|
+
*
|
|
1480
|
+
* const receipt = await runRoute({
|
|
1481
|
+
* client,
|
|
1482
|
+
* route,
|
|
1483
|
+
* onProgress: (e) => console.log(`${e.bytesTransferred}/${e.totalBytes ?? "?"}`),
|
|
1484
|
+
* retry: { maxAttempts: 3, baseDelayMs: 500 },
|
|
1485
|
+
* });
|
|
1486
|
+
* console.log(`Job ${receipt.jobId} moved ${receipt.bytesTransferred} bytes…`);
|
|
1487
|
+
* ```
|
|
1422
1488
|
*/
|
|
1423
1489
|
declare function runRoute(options: RunRouteOptions): Promise<TransferReceipt>;
|
|
1424
1490
|
|
|
@@ -1592,8 +1658,20 @@ interface ClientDiagnostics {
|
|
|
1592
1658
|
/**
|
|
1593
1659
|
* Returns a redaction-safe snapshot of the providers registered with a client.
|
|
1594
1660
|
*
|
|
1661
|
+
* Use this when rendering a setup screen, generating a support bundle, or
|
|
1662
|
+
* asserting in tests that the expected provider factories were registered.
|
|
1663
|
+
*
|
|
1595
1664
|
* @param client - Transfer client to inspect.
|
|
1596
1665
|
* @returns Provider id and capability snapshot tuples.
|
|
1666
|
+
*
|
|
1667
|
+
* @example List registered providers
|
|
1668
|
+
* ```ts
|
|
1669
|
+
* import { summarizeClientDiagnostics } from "@zero-transfer/sdk";
|
|
1670
|
+
*
|
|
1671
|
+
* for (const { id, capabilities } of summarizeClientDiagnostics(client).providers) {
|
|
1672
|
+
* console.log(`${id}: streaming=${capabilities.readStream} resume=${capabilities.resumeDownload}`);
|
|
1673
|
+
* }
|
|
1674
|
+
* ```
|
|
1597
1675
|
*/
|
|
1598
1676
|
declare function summarizeClientDiagnostics(client: TransferClient): ClientDiagnostics;
|
|
1599
1677
|
/** Per-step duration measurements collected by {@link runConnectionDiagnostics}. */
|
|
@@ -1646,8 +1724,36 @@ interface RunConnectionDiagnosticsOptions {
|
|
|
1646
1724
|
/**
|
|
1647
1725
|
* Connects to a profile, captures capability and listing samples, and returns a redaction-safe report.
|
|
1648
1726
|
*
|
|
1727
|
+
* Useful for connectivity "ping" pages, smoke tests, and bug reports. Secrets
|
|
1728
|
+
* in the profile are redacted via {@link redactConnectionProfile} before being
|
|
1729
|
+
* returned. The session is always disconnected before the function returns,
|
|
1730
|
+
* including when probes throw.
|
|
1731
|
+
*
|
|
1649
1732
|
* @param options - Diagnostic probe options.
|
|
1650
1733
|
* @returns Diagnostic report including timings and any captured error.
|
|
1734
|
+
*
|
|
1735
|
+
* @example Probe an SFTP connection
|
|
1736
|
+
* ```ts
|
|
1737
|
+
* import { runConnectionDiagnostics } from "@zero-transfer/sdk";
|
|
1738
|
+
*
|
|
1739
|
+
* const report = await runConnectionDiagnostics({
|
|
1740
|
+
* client,
|
|
1741
|
+
* profile: {
|
|
1742
|
+
* host: "sftp.example.com",
|
|
1743
|
+
* provider: "sftp",
|
|
1744
|
+
* username: "deploy",
|
|
1745
|
+
* ssh: { privateKey: { path: "./keys/id_ed25519" } },
|
|
1746
|
+
* },
|
|
1747
|
+
* listPath: "/uploads",
|
|
1748
|
+
* });
|
|
1749
|
+
*
|
|
1750
|
+
* if (!report.ok) {
|
|
1751
|
+
* console.error("connection failed:", report.error);
|
|
1752
|
+
* } else {
|
|
1753
|
+
* console.log(`connect=${report.timings.connectMs}ms list=${report.timings.listMs}ms`);
|
|
1754
|
+
* console.log(report.sample); // up to 5 entries from /uploads
|
|
1755
|
+
* }
|
|
1756
|
+
* ```
|
|
1651
1757
|
*/
|
|
1652
1758
|
declare function runConnectionDiagnostics(options: RunConnectionDiagnosticsOptions): Promise<ConnectionDiagnosticsResult>;
|
|
1653
1759
|
|
|
@@ -1754,8 +1860,29 @@ interface MemoryProviderOptions {
|
|
|
1754
1860
|
/**
|
|
1755
1861
|
* Creates a provider factory backed by deterministic in-memory fixture entries.
|
|
1756
1862
|
*
|
|
1863
|
+
* Useful for tests and examples where you want a real `TransferSession` without
|
|
1864
|
+
* touching disk or the network. Entries are pre-seeded; mutations made through
|
|
1865
|
+
* the session are visible to subsequent operations on the same provider.
|
|
1866
|
+
*
|
|
1757
1867
|
* @param options - Optional fixture entries to expose through the memory provider.
|
|
1758
1868
|
* @returns Provider factory suitable for `createTransferClient({ providers: [...] })`.
|
|
1869
|
+
*
|
|
1870
|
+
* @example Seed entries and read them back
|
|
1871
|
+
* ```ts
|
|
1872
|
+
* import { createMemoryProviderFactory, createTransferClient } from "@zero-transfer/sdk";
|
|
1873
|
+
*
|
|
1874
|
+
* const client = createTransferClient({
|
|
1875
|
+
* providers: [createMemoryProviderFactory({
|
|
1876
|
+
* entries: [
|
|
1877
|
+
* { path: "/fixtures/hello.txt", content: "hello world" },
|
|
1878
|
+
* { path: "/fixtures/data.bin", content: new Uint8Array([1, 2, 3]) },
|
|
1879
|
+
* ],
|
|
1880
|
+
* })],
|
|
1881
|
+
* });
|
|
1882
|
+
*
|
|
1883
|
+
* const session = await client.connect({ host: "fixtures", provider: "memory" });
|
|
1884
|
+
* console.log(await session.fs.list("/fixtures"));
|
|
1885
|
+
* ```
|
|
1759
1886
|
*/
|
|
1760
1887
|
declare function createMemoryProviderFactory(options?: MemoryProviderOptions): ProviderFactory;
|
|
1761
1888
|
|
|
@@ -2493,9 +2620,47 @@ interface TransferPlanSummary {
|
|
|
2493
2620
|
/** Counts grouped by action. */
|
|
2494
2621
|
actions: Record<string, number>;
|
|
2495
2622
|
}
|
|
2496
|
-
/**
|
|
2623
|
+
/**
|
|
2624
|
+
* Creates a transfer plan from dry-run planning input.
|
|
2625
|
+
*
|
|
2626
|
+
* Plans are immutable, structured descriptions of intended work. Pair with
|
|
2627
|
+
* {@link createSyncPlan} or {@link createAtomicDeployPlan} for end-to-end
|
|
2628
|
+
* planning, or build steps by hand when you need full control. Pass the plan
|
|
2629
|
+
* to {@link createTransferJobsFromPlan} to materialize executable jobs.
|
|
2630
|
+
*
|
|
2631
|
+
* @example Build a plan with two upload steps and inspect it
|
|
2632
|
+
* ```ts
|
|
2633
|
+
* import { createTransferPlan, summarizeTransferPlan } from "@zero-transfer/sdk";
|
|
2634
|
+
*
|
|
2635
|
+
* const plan = createTransferPlan({
|
|
2636
|
+
* id: "manual-batch",
|
|
2637
|
+
* steps: [
|
|
2638
|
+
* { action: "upload", source: "./a.bin", destination: "/lake/a.bin", expectedBytes: 1024 },
|
|
2639
|
+
* { action: "upload", source: "./b.bin", destination: "/lake/b.bin", expectedBytes: 2048 },
|
|
2640
|
+
* ],
|
|
2641
|
+
* });
|
|
2642
|
+
*
|
|
2643
|
+
* console.table(summarizeTransferPlan(plan));
|
|
2644
|
+
* ```
|
|
2645
|
+
*/
|
|
2497
2646
|
declare function createTransferPlan(input: TransferPlanInput): TransferPlan;
|
|
2498
|
-
/**
|
|
2647
|
+
/**
|
|
2648
|
+
* Summarizes a transfer plan for diagnostics, previews, and tests.
|
|
2649
|
+
*
|
|
2650
|
+
* Returns aggregate counts (total / executable / skipped / destructive),
|
|
2651
|
+
* total expected bytes, and a per-action histogram. Useful for printing a
|
|
2652
|
+
* one-line plan summary before executing or for asserting plan shape in
|
|
2653
|
+
* tests.
|
|
2654
|
+
*
|
|
2655
|
+
* @example Print a plan preview
|
|
2656
|
+
* ```ts
|
|
2657
|
+
* import { summarizeTransferPlan } from "@zero-transfer/sdk";
|
|
2658
|
+
*
|
|
2659
|
+
* const summary = summarizeTransferPlan(plan);
|
|
2660
|
+
* console.log(`${summary.executableSteps} steps, ${summary.totalExpectedBytes} bytes total`);
|
|
2661
|
+
* console.log("Actions:", summary.actions);
|
|
2662
|
+
* ```
|
|
2663
|
+
*/
|
|
2499
2664
|
declare function summarizeTransferPlan(plan: TransferPlan): TransferPlanSummary;
|
|
2500
2665
|
/** Converts executable plan steps into transfer jobs while preserving order. */
|
|
2501
2666
|
declare function createTransferJobsFromPlan(plan: TransferPlan): TransferJob[];
|
|
@@ -2572,7 +2737,41 @@ interface TransferQueueSummary {
|
|
|
2572
2737
|
/** Failed queue items in queue order. */
|
|
2573
2738
|
failures: TransferQueueItem[];
|
|
2574
2739
|
}
|
|
2575
|
-
/**
|
|
2740
|
+
/**
|
|
2741
|
+
* Minimal transfer queue with concurrency, pause/resume, cancellation, and drain summaries.
|
|
2742
|
+
*
|
|
2743
|
+
* Wrap a {@link TransferEngine} with a queue when you need to run many transfers
|
|
2744
|
+
* concurrently with bounded parallelism, observe per-job progress, or drive
|
|
2745
|
+
* a UI from a single source of truth. Items are FIFO; failures and successes
|
|
2746
|
+
* are surfaced via observers and in the final {@link TransferQueueSummary}.
|
|
2747
|
+
*
|
|
2748
|
+
* @example Run a batch of uploads with concurrency=4
|
|
2749
|
+
* ```ts
|
|
2750
|
+
* import {
|
|
2751
|
+
* TransferQueue,
|
|
2752
|
+
* createProviderTransferExecutor,
|
|
2753
|
+
* } from "@zero-transfer/sdk";
|
|
2754
|
+
*
|
|
2755
|
+
* const queue = new TransferQueue({
|
|
2756
|
+
* concurrency: 4,
|
|
2757
|
+
* executor: createProviderTransferExecutor({ client }),
|
|
2758
|
+
* onProgress: (e) => console.log(`${e.jobId}: ${e.bytesTransferred}`),
|
|
2759
|
+
* onError: (item, err) => console.error(`${item.job.id} failed`, err),
|
|
2760
|
+
* });
|
|
2761
|
+
*
|
|
2762
|
+
* for (const file of files) {
|
|
2763
|
+
* queue.enqueue({
|
|
2764
|
+
* id: file.name,
|
|
2765
|
+
* operation: "upload",
|
|
2766
|
+
* source: { profile: localProfile, path: file.path },
|
|
2767
|
+
* destination: { profile: s3Profile, path: `/lake/${file.name}` },
|
|
2768
|
+
* });
|
|
2769
|
+
* }
|
|
2770
|
+
*
|
|
2771
|
+
* const summary = await queue.drain();
|
|
2772
|
+
* console.log(`Completed ${summary.completed} / ${summary.total}`);
|
|
2773
|
+
* ```
|
|
2774
|
+
*/
|
|
2576
2775
|
declare class TransferQueue {
|
|
2577
2776
|
private readonly engine;
|
|
2578
2777
|
private readonly items;
|
|
@@ -2852,6 +3051,26 @@ interface DiffRemoteTreesOptions {
|
|
|
2852
3051
|
* @param destinationPath - Destination-side root path being compared.
|
|
2853
3052
|
* @param options - Optional comparison controls.
|
|
2854
3053
|
* @returns Diff result containing entries and a summary.
|
|
3054
|
+
*
|
|
3055
|
+
* @example Diff two SFTP subtrees and feed the result into createSyncPlan
|
|
3056
|
+
* ```ts
|
|
3057
|
+
* import { createSyncPlan, diffRemoteTrees } from "@zero-transfer/sdk";
|
|
3058
|
+
*
|
|
3059
|
+
* const diff = await diffRemoteTrees(
|
|
3060
|
+
* srcSession.fs, "/exports",
|
|
3061
|
+
* dstSession.fs, "/exports",
|
|
3062
|
+
* { compareUniqueId: true },
|
|
3063
|
+
* );
|
|
3064
|
+
*
|
|
3065
|
+
* console.log(diff.summary); // { added, removed, changed, unchanged }
|
|
3066
|
+
*
|
|
3067
|
+
* const plan = createSyncPlan({
|
|
3068
|
+
* id: "exports-sync",
|
|
3069
|
+
* diff,
|
|
3070
|
+
* source: { provider: "sftp", rootPath: "/exports" },
|
|
3071
|
+
* destination: { provider: "sftp", rootPath: "/exports" },
|
|
3072
|
+
* });
|
|
3073
|
+
* ```
|
|
2855
3074
|
*/
|
|
2856
3075
|
declare function diffRemoteTrees(source: RemoteFileSystem, sourcePath: string, destination: RemoteFileSystem, destinationPath: string, options?: DiffRemoteTreesOptions): Promise<RemoteTreeDiff>;
|
|
2857
3076
|
|
|
@@ -2923,6 +3142,31 @@ interface CreateSyncPlanOptions {
|
|
|
2923
3142
|
* @param options - Inputs and policies that shape the plan.
|
|
2924
3143
|
* @returns Transfer plan ready for `createTransferJobsFromPlan` or queue execution.
|
|
2925
3144
|
* @throws {@link ConfigurationError} When `conflictPolicy: "error"` encounters a conflict.
|
|
3145
|
+
*
|
|
3146
|
+
* @example Mirror SFTP → S3 with deletes
|
|
3147
|
+
* ```ts
|
|
3148
|
+
* import {
|
|
3149
|
+
* createSyncPlan,
|
|
3150
|
+
* diffRemoteTrees,
|
|
3151
|
+
* summarizeTransferPlan,
|
|
3152
|
+
* } from "@zero-transfer/sdk";
|
|
3153
|
+
*
|
|
3154
|
+
* const diff = await diffRemoteTrees(
|
|
3155
|
+
* srcSession.fs, "/dist",
|
|
3156
|
+
* dstSession.fs, "/releases/current",
|
|
3157
|
+
* );
|
|
3158
|
+
*
|
|
3159
|
+
* const plan = createSyncPlan({
|
|
3160
|
+
* id: "release-mirror",
|
|
3161
|
+
* diff,
|
|
3162
|
+
* source: { provider: "sftp", rootPath: "/dist" },
|
|
3163
|
+
* destination: { provider: "s3", rootPath: "/releases/current" },
|
|
3164
|
+
* deletePolicy: "mirror",
|
|
3165
|
+
* conflictPolicy: "overwrite",
|
|
3166
|
+
* });
|
|
3167
|
+
*
|
|
3168
|
+
* console.table(summarizeTransferPlan(plan));
|
|
3169
|
+
* ```
|
|
2926
3170
|
*/
|
|
2927
3171
|
declare function createSyncPlan(options: CreateSyncPlanOptions): TransferPlan;
|
|
2928
3172
|
|
|
@@ -3038,9 +3282,39 @@ interface CreateAtomicDeployPlanOptions {
|
|
|
3038
3282
|
/**
|
|
3039
3283
|
* Builds an {@link AtomicDeployPlan} that stages a release, swaps it live, and prunes old releases.
|
|
3040
3284
|
*
|
|
3285
|
+
* The plan describes a blue/green-style deploy:
|
|
3286
|
+
* 1. Upload to a timestamped staging directory under `<destination>/.releases/`.
|
|
3287
|
+
* 2. Atomically swap the `current` symlink/rename to point at the new release.
|
|
3288
|
+
* 3. Optionally prune old releases beyond `retain`.
|
|
3289
|
+
*
|
|
3290
|
+
* No I/O is performed — the host executes the plan steps. Pair with
|
|
3291
|
+
* {@link createTransferPlan} or {@link createTransferJobsFromPlan} to execute.
|
|
3292
|
+
*
|
|
3041
3293
|
* @param options - Inputs and policies that shape the deploy.
|
|
3042
3294
|
* @returns Structured deploy plan ready for execution by the calling host.
|
|
3043
3295
|
* @throws {@link ConfigurationError} When `retain` is less than `1` or the destination root is empty.
|
|
3296
|
+
*
|
|
3297
|
+
* @example Plan a release with rollback path
|
|
3298
|
+
* ```ts
|
|
3299
|
+
* import { createAtomicDeployPlan } from "@zero-transfer/sdk";
|
|
3300
|
+
*
|
|
3301
|
+
* const plan = createAtomicDeployPlan({
|
|
3302
|
+
* id: "web-2026-04-28",
|
|
3303
|
+
* source: { rootPath: "./dist" },
|
|
3304
|
+
* destination: {
|
|
3305
|
+
* profile: { host: "web1.example.com", provider: "sftp", username: "deploy" },
|
|
3306
|
+
* rootPath: "/srv/www",
|
|
3307
|
+
* },
|
|
3308
|
+
* retain: 5,
|
|
3309
|
+
* existingReleases: [
|
|
3310
|
+
* "/srv/www/.releases/2026-04-21T00-00-00Z",
|
|
3311
|
+
* "/srv/www/.releases/2026-04-14T00-00-00Z",
|
|
3312
|
+
* ],
|
|
3313
|
+
* });
|
|
3314
|
+
*
|
|
3315
|
+
* console.log(plan.swap); // staging → current rename
|
|
3316
|
+
* console.log(plan.prune); // releases scheduled for removal
|
|
3317
|
+
* ```
|
|
3044
3318
|
*/
|
|
3045
3319
|
declare function createAtomicDeployPlan(options: CreateAtomicDeployPlanOptions): AtomicDeployPlan;
|
|
3046
3320
|
|
|
@@ -3634,9 +3908,33 @@ interface CreateWebhookAuditLogOptions {
|
|
|
3634
3908
|
*
|
|
3635
3909
|
* Entries whose `type` is not in `target.types` are silently dropped. `list()`
|
|
3636
3910
|
* always returns an empty array because webhook deliveries are not buffered.
|
|
3911
|
+
* Payloads are HMAC-signed with `target.secret` (when provided) so receivers
|
|
3912
|
+
* can verify authenticity before acting on them.
|
|
3637
3913
|
*
|
|
3638
3914
|
* @param options - Webhook target plus optional retry/observer hooks.
|
|
3639
3915
|
* @returns An audit log that delivers each `record` call to the webhook.
|
|
3916
|
+
*
|
|
3917
|
+
* @example Compose a webhook log with an in-memory log for local replay
|
|
3918
|
+
* ```ts
|
|
3919
|
+
* import {
|
|
3920
|
+
* InMemoryAuditLog,
|
|
3921
|
+
* composeAuditLogs,
|
|
3922
|
+
* createWebhookAuditLog,
|
|
3923
|
+
* } from "@zero-transfer/sdk";
|
|
3924
|
+
*
|
|
3925
|
+
* const memory = new InMemoryAuditLog();
|
|
3926
|
+
* const webhook = createWebhookAuditLog({
|
|
3927
|
+
* target: {
|
|
3928
|
+
* url: "https://hooks.example.com/zt",
|
|
3929
|
+
* secret: { env: "ZT_WEBHOOK_SECRET" },
|
|
3930
|
+
* types: ["transfer.success", "transfer.failure"],
|
|
3931
|
+
* },
|
|
3932
|
+
* onDelivery: ({ result }) => console.log("delivered", result.statusCode),
|
|
3933
|
+
* });
|
|
3934
|
+
*
|
|
3935
|
+
* const audit = composeAuditLogs(memory, webhook);
|
|
3936
|
+
* await audit.record({ type: "transfer.success", receipt });
|
|
3937
|
+
* ```
|
|
3640
3938
|
*/
|
|
3641
3939
|
declare function createWebhookAuditLog(options: CreateWebhookAuditLogOptions): MftAuditLog;
|
|
3642
3940
|
|
|
@@ -3791,7 +4089,48 @@ interface MftSchedulerOptions {
|
|
|
3791
4089
|
/** Timer/clock injection used by tests. */
|
|
3792
4090
|
timer?: ScheduleTimerHooks;
|
|
3793
4091
|
}
|
|
3794
|
-
/**
|
|
4092
|
+
/**
|
|
4093
|
+
* Runs routes on configured schedules.
|
|
4094
|
+
*
|
|
4095
|
+
* Subscribes to a {@link ScheduleRegistry}, computes the next fire time for
|
|
4096
|
+
* each schedule (cron or interval), and dispatches the matching route through
|
|
4097
|
+
* a runner of your choice (`runRoute` by default, or a wrapped runner for
|
|
4098
|
+
* approvals / rate limiting / circuit breaking). Observers fire on each cycle
|
|
4099
|
+
* for telemetry. Tests can inject a deterministic timer via `timer`.
|
|
4100
|
+
*
|
|
4101
|
+
* @example Wire a cron schedule with audit + approval
|
|
4102
|
+
* ```ts
|
|
4103
|
+
* import {
|
|
4104
|
+
* ApprovalRegistry,
|
|
4105
|
+
* InMemoryAuditLog,
|
|
4106
|
+
* MftScheduler,
|
|
4107
|
+
* RouteRegistry,
|
|
4108
|
+
* ScheduleRegistry,
|
|
4109
|
+
* createApprovalGate,
|
|
4110
|
+
* runRoute,
|
|
4111
|
+
* } from "@zero-transfer/sdk";
|
|
4112
|
+
*
|
|
4113
|
+
* const audit = new InMemoryAuditLog();
|
|
4114
|
+
* const approvals = new ApprovalRegistry();
|
|
4115
|
+
*
|
|
4116
|
+
* const scheduler = new MftScheduler({
|
|
4117
|
+
* client,
|
|
4118
|
+
* routes: new RouteRegistry([route]),
|
|
4119
|
+
* schedules: new ScheduleRegistry([
|
|
4120
|
+
* { id: "nightly", routeId: route.id, cron: "0 2 * * *" },
|
|
4121
|
+
* ]),
|
|
4122
|
+
* runner: createApprovalGate({
|
|
4123
|
+
* registry: approvals,
|
|
4124
|
+
* approvalId: ({ route }) => `release:${route.id}`,
|
|
4125
|
+
* runner: ({ client: c, route: r, signal }) => runRoute({ client: c, route: r, signal }),
|
|
4126
|
+
* }),
|
|
4127
|
+
* onResult: ({ receipt }) => audit.record({ type: "transfer.success", receipt }),
|
|
4128
|
+
* onError: ({ error }) => audit.record({ type: "transfer.failure", error }),
|
|
4129
|
+
* });
|
|
4130
|
+
*
|
|
4131
|
+
* scheduler.start();
|
|
4132
|
+
* ```
|
|
4133
|
+
*/
|
|
3795
4134
|
declare class MftScheduler {
|
|
3796
4135
|
private readonly options;
|
|
3797
4136
|
private readonly now;
|
|
@@ -3935,10 +4274,33 @@ interface CreateApprovalGateOptions {
|
|
|
3935
4274
|
*
|
|
3936
4275
|
* The returned runner creates an approval request, waits for resolution, and
|
|
3937
4276
|
* dispatches the underlying runner only when the request is approved. Rejection
|
|
3938
|
-
* surfaces an {@link ApprovalRejectedError}.
|
|
4277
|
+
* surfaces an {@link ApprovalRejectedError}. Pair with {@link MftScheduler} to
|
|
4278
|
+
* implement two-person rules and human-in-the-loop release flows.
|
|
3939
4279
|
*
|
|
3940
4280
|
* @param options - Registry, downstream runner, approval-id derivation, hooks.
|
|
3941
4281
|
* @returns A {@link ScheduleRouteRunner} that gates execution behind approval.
|
|
4282
|
+
*
|
|
4283
|
+
* @example Two-person rule on a release route
|
|
4284
|
+
* ```ts
|
|
4285
|
+
* import {
|
|
4286
|
+
* ApprovalRegistry,
|
|
4287
|
+
* createApprovalGate,
|
|
4288
|
+
* runRoute,
|
|
4289
|
+
* } from "@zero-transfer/sdk";
|
|
4290
|
+
*
|
|
4291
|
+
* const approvals = new ApprovalRegistry();
|
|
4292
|
+
*
|
|
4293
|
+
* const gatedRunner = createApprovalGate({
|
|
4294
|
+
* registry: approvals,
|
|
4295
|
+
* approvalId: ({ route }) => `release:${route.id}:${Date.now()}`,
|
|
4296
|
+
* onRequested: (req) => notifyOnCallChannel(req),
|
|
4297
|
+
* runner: ({ client, route, signal }) => runRoute({ client, route, signal }),
|
|
4298
|
+
* });
|
|
4299
|
+
*
|
|
4300
|
+
* // Elsewhere, an authorized operator approves or rejects:
|
|
4301
|
+
* approvals.approve(approvalId, { actor: "alice@example.com" });
|
|
4302
|
+
* // approvals.reject(approvalId, { actor: "bob@example.com", reason: "hold release" });
|
|
4303
|
+
* ```
|
|
3942
4304
|
*/
|
|
3943
4305
|
declare function createApprovalGate(options: CreateApprovalGateOptions): ScheduleRouteRunner;
|
|
3944
4306
|
|