openrtc-file-transfer 2.9.7 → 2.9.8
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 +118 -0
- package/dist/capabilities.d.ts +25 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +58 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/flowControl.d.ts +45 -0
- package/dist/flowControl.d.ts.map +1 -0
- package/dist/flowControl.js +192 -0
- package/dist/flowControl.js.map +1 -0
- package/dist/index.d.ts +93 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +599 -145
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -53,3 +53,121 @@ constructs a hidden client or activates a second avenue. It also never caches a
|
|
|
53
53
|
transport, dials a peer, or promotes a route. The
|
|
54
54
|
consumer keeps durable product intent pending until the receiver confirms the
|
|
55
55
|
completed size and integrity.
|
|
56
|
+
|
|
57
|
+
### Durable accepted sources
|
|
58
|
+
|
|
59
|
+
The routed `sendAccepted(deviceId, { header, openSource }, options)` source API
|
|
60
|
+
accepts the product's persisted immutable header, including SHA-256. It first
|
|
61
|
+
reconciles the offer with the authenticated receiver. A committed completion
|
|
62
|
+
receipt returns without opening the local file; otherwise missing bytes cause
|
|
63
|
+
one lazy source open and bounded, abort-aware revision verification before body
|
|
64
|
+
transmission. The product owns source storage, authorization, queue state and
|
|
65
|
+
receipt persistence. No connection or additional protocol handler is created.
|
|
66
|
+
The legacy stream implementation rejects this API before sending bytes. Consumers
|
|
67
|
+
of older published packages must detect API availability and retain their prior
|
|
68
|
+
source-first path until qualified artifacts are adopted.
|
|
69
|
+
|
|
70
|
+
### Receive pacing and deadline renewal
|
|
71
|
+
|
|
72
|
+
Routed message and segmented-stream receivers may use `beforeReceiveBody` to
|
|
73
|
+
admit each authenticated body attempt into a product rate/quota controller.
|
|
74
|
+
It receives transfer ID, remote node ID, offset, byte count and an abort signal.
|
|
75
|
+
Duplicate body attempts consume policy bytes without advancing logical progress
|
|
76
|
+
or writing the same prefix twice. Control frames bypass body policy. Payload
|
|
77
|
+
attempts are application bytes, not an exact physical-network counter.
|
|
78
|
+
|
|
79
|
+
Receiver pacing requires the immutable offer metadata
|
|
80
|
+
`openrtc.receive-wait: "1"`. Fresh routed `send` offers advertise this capability;
|
|
81
|
+
call `withReceiverWaitCapability(header)` **before** persisting a new accepted
|
|
82
|
+
offer. `sendAccepted` preserves that exact persisted header, including metadata.
|
|
83
|
+
A paced receiver rejects a legacy offer before allocating its sink or reading
|
|
84
|
+
source bytes. A product-confirmed completion receipt still returns body-free,
|
|
85
|
+
without running a body policy or requiring the legacy source to exist.
|
|
86
|
+
|
|
87
|
+
`BULK_RECEIVE_FLOW_CONTROL_VERSION === 1` also supports per-sink `bodyPolicy`.
|
|
88
|
+
Use its captured execution `signal`, `beforeAttempt` callback and
|
|
89
|
+
`requiresReceiverWait` admission flag when product storage owns each job's
|
|
90
|
+
policy/lease. The runtime callback still runs first; a sink cannot bypass it.
|
|
91
|
+
A sink with `requiresReceiverWait: false` admits old offers without sending
|
|
92
|
+
unsupported wait controls. Its callback must remain unpaced for such an offer
|
|
93
|
+
or reject if a later policy requires waiting. Required waits reject before body
|
|
94
|
+
acceptance and abort any prepared sink. Product receipts are checked first.
|
|
95
|
+
Signal retirement aborts active policy work and releases idle receive sinks.
|
|
96
|
+
Stream/v1 cannot host this body policy and rejects it before reading body data.
|
|
97
|
+
|
|
98
|
+
OFT2 kind `0x08` is a waiting control frame: transfer ID, big-endian u64 last
|
|
99
|
+
checkpointed offset and big-endian u32 lease milliseconds (1–60,000). The
|
|
100
|
+
receiver renews its configurable lease (default 10,000 ms) during body policy,
|
|
101
|
+
sink IO/checkpointing and final verification/publication. The sender accepts
|
|
102
|
+
renewal only from the expected authenticated peer, for the active transfer,
|
|
103
|
+
within its current body window and only when the offer negotiated support.
|
|
104
|
+
Waiting neither ACKs bytes nor confirms delivery. If renewal stops, the current
|
|
105
|
+
lease expires and the normal bounded recovery applies. Application expiry and
|
|
106
|
+
execution permission remain the product's responsibility.
|
|
107
|
+
|
|
108
|
+
Caller cancellation, receiver subscription stop/replacement and protocol
|
|
109
|
+
disposal abort active policy work and stop renewal. Hooks must honor the abort
|
|
110
|
+
signal; durable sinks must retain their product execution/commit fences. A sink
|
|
111
|
+
that finishes creation after execution retirement is aborted rather than
|
|
112
|
+
accepted. Legacy raw-stream framing rejects receive pacing; native consumers
|
|
113
|
+
must qualify and implement the same negotiated deadline behavior before
|
|
114
|
+
advertising the capability. Neither waiting nor body policy changes OpenRTC's
|
|
115
|
+
connection, admission or route ownership.
|
|
116
|
+
|
|
117
|
+
### Required capabilities and large-file admission
|
|
118
|
+
|
|
119
|
+
For routed message or segmented-stream transfers, `send` accepts
|
|
120
|
+
`requiredCapabilities: { durableResume?, completionReceipt?, extensions? }`.
|
|
121
|
+
Accepted jobs instead call `withRequiredCapabilities(header, requirements)` before
|
|
122
|
+
persisting their immutable header; `sendAccepted` preserves that exact metadata.
|
|
123
|
+
A requirement is encoded under `openrtc.required-capabilities.v1` as a closed
|
|
124
|
+
version-1 JSON document. Unknown fields, duplicate or invalid extension names,
|
|
125
|
+
and unsupported versions fail closed. Extensions are consumer-owned names; they
|
|
126
|
+
must describe real receiver authorization/storage behavior rather than silently
|
|
127
|
+
changing generic delivery into a managed destination operation.
|
|
128
|
+
|
|
129
|
+
The sender probes before offering bytes whenever the header has requirements,
|
|
130
|
+
exceeds the 256 MiB legacy buffered ceiling, or an accepted job exceeds its
|
|
131
|
+
current sender size limit. An unsupported verdict rejects before opening an
|
|
132
|
+
accepted source or allocating a receive sink. A legacy peer that ignores the
|
|
133
|
+
probe produces an actionable error after the existing bounded protocol retries;
|
|
134
|
+
OpenRTC still owns admission, routes and connection recovery. Explicit stream/v1
|
|
135
|
+
send and receive reject required-feature metadata before accepting a body.
|
|
136
|
+
|
|
137
|
+
The receiver derives its admitted maximum from `maxFileBytes` and the sink:
|
|
138
|
+
without a custom streaming sink it never advertises above 256 MiB. With one,
|
|
139
|
+
`receiveCapabilities` may declare durable resume and application extensions.
|
|
140
|
+
A durable-resume claim requires a checkpointing sink; the application must make
|
|
141
|
+
those checkpoints durable and verify reopened prefixes. `completionReceipt`
|
|
142
|
+
support requires the product `hasCompletionReceipt(header, authenticatedPeer)`
|
|
143
|
+
callback. When a job requires receipts, completion checks that callback after
|
|
144
|
+
publication/commit and rejects success if the receipt is still missing. Neither
|
|
145
|
+
the SDK's in-memory completed map nor a capability declaration is durable proof.
|
|
146
|
+
An absent or aborted receive subscription advertises `receiveAvailable: false`,
|
|
147
|
+
including for zero-byte files.
|
|
148
|
+
|
|
149
|
+
OFT2 kind `0x09` carries a capability request: transfer ID, big-endian u32 header
|
|
150
|
+
length and optional immutable JSON header (zero length for a general query).
|
|
151
|
+
Kind `0x0a` carries a big-endian u32 JSON length and a closed version-1 response
|
|
152
|
+
bounded to 4096 bytes: `version`, `durableResume`, `completionReceipt`,
|
|
153
|
+
`extensions`, `receiveAvailable`, and `maxFileBytes` (a nonnegative safe integer).
|
|
154
|
+
Header probes retain the configured header-byte bound but validate size against
|
|
155
|
+
the JavaScript safe-integer ceiling, independently of current body admission.
|
|
156
|
+
|
|
157
|
+
Before returning capabilities, a header probe checks the authenticated product
|
|
158
|
+
receipt. A matching receipt returns the existing `Completed` frame, even if
|
|
159
|
+
receiving is disabled or current file limits have decreased. An accepted-source
|
|
160
|
+
sender likewise reconciles that receipt before enforcing its current body limit,
|
|
161
|
+
without reopening or resending the source. Immutable header conflicts fail
|
|
162
|
+
closed; replies must match the intended authenticated peer and transfer ID.
|
|
163
|
+
Native codec support does not by itself implement or advertise these execution,
|
|
164
|
+
application extension, authorization, or persistence guarantees.
|
|
165
|
+
|
|
166
|
+
The staged `BULK_JOB_FLOW_CONTROL_VERSION = 1` contract also accepts
|
|
167
|
+
`beforeSendBody` on an individual `send` or `sendAccepted` call. The runtime
|
|
168
|
+
callback and job callback both run before each body attempt, outside the ACK
|
|
169
|
+
deadline; a job callback cannot override a runtime restriction. Product owners
|
|
170
|
+
retain authorization, execution cancellation, durable counters and policy state.
|
|
171
|
+
Persist `BulkFlowController.snapshot()` before allowing body work and restore it
|
|
172
|
+
into a fresh controller when a durable execution owner changes. Publication and
|
|
173
|
+
consumer capability checks are required before relying on these staged APIs.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FileTransferHeader } from './index.js';
|
|
2
|
+
export declare const REQUIRED_CAPABILITIES_METADATA_KEY = "openrtc.required-capabilities.v1";
|
|
3
|
+
export declare const MAX_CAPABILITY_BYTES = 4096;
|
|
4
|
+
export declare const LEGACY_MAX_FILE_BYTES: number;
|
|
5
|
+
export interface TransferRequirements {
|
|
6
|
+
durableResume?: boolean;
|
|
7
|
+
completionReceipt?: boolean;
|
|
8
|
+
extensions?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
export interface RequiredTransferCapabilities {
|
|
11
|
+
version: 1;
|
|
12
|
+
durableResume: boolean;
|
|
13
|
+
completionReceipt: boolean;
|
|
14
|
+
extensions: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface TransferCapabilities extends RequiredTransferCapabilities {
|
|
17
|
+
receiveAvailable: boolean;
|
|
18
|
+
maxFileBytes: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function requiredCapabilities(value: unknown, wire?: boolean): RequiredTransferCapabilities;
|
|
21
|
+
export declare function transferCapabilities(value: unknown): TransferCapabilities;
|
|
22
|
+
export declare function requiredTransferCapabilities(header: FileTransferHeader): RequiredTransferCapabilities | null;
|
|
23
|
+
export declare function withRequiredCapabilities(header: FileTransferHeader, requirements: TransferRequirements): FileTransferHeader;
|
|
24
|
+
export declare function requireCapabilities(capabilities: TransferCapabilities, header: FileTransferHeader): void;
|
|
25
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,eAAO,MAAM,kCAAkC,qCAAqC,CAAC;AACrF,eAAO,MAAM,oBAAoB,OAAO,CAAC;AACzC,eAAO,MAAM,qBAAqB,QAAoB,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AACD,MAAM,WAAW,4BAA4B;IACzC,OAAO,EAAE,CAAC,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAC;CACxF;AACD,MAAM,WAAW,oBAAqB,SAAQ,4BAA4B;IAAG,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE;AAe9H,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,UAAQ,GAAG,4BAA4B,CAU/F;AACD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAMzE;AACD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,kBAAkB,GAAG,4BAA4B,GAAG,IAAI,CAM5G;AACD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,oBAAoB,GAAG,kBAAkB,CAM3H;AACD,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,oBAAoB,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAMxG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const REQUIRED_CAPABILITIES_METADATA_KEY = 'openrtc.required-capabilities.v1';
|
|
2
|
+
export const MAX_CAPABILITY_BYTES = 4096;
|
|
3
|
+
export const LEGACY_MAX_FILE_BYTES = 256 * 1024 * 1024;
|
|
4
|
+
function record(value, keys) {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
6
|
+
|| Object.keys(value).some(key => !keys.includes(key)))
|
|
7
|
+
throw new Error('invalid transfer capability fields');
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
function extensions(value) {
|
|
11
|
+
if (!Array.isArray(value) || value.length > 16 || value.some(item => typeof item !== 'string' || item.length > 128 || !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(item)))
|
|
12
|
+
throw new Error('invalid transfer capability extensions');
|
|
13
|
+
const sorted = [...value].sort();
|
|
14
|
+
if (sorted.some((item, index) => index > 0 && item === sorted[index - 1]))
|
|
15
|
+
throw new Error('duplicate transfer capability extension');
|
|
16
|
+
return sorted;
|
|
17
|
+
}
|
|
18
|
+
export function requiredCapabilities(value, wire = false) {
|
|
19
|
+
const row = record(value, ['version', 'durableResume', 'completionReceipt', 'extensions']);
|
|
20
|
+
if (wire ? row.version !== 1 || typeof row.durableResume !== 'boolean'
|
|
21
|
+
|| typeof row.completionReceipt !== 'boolean' || row.extensions === undefined
|
|
22
|
+
: row.version !== undefined && row.version !== 1
|
|
23
|
+
|| row.durableResume !== undefined && typeof row.durableResume !== 'boolean'
|
|
24
|
+
|| row.completionReceipt !== undefined && typeof row.completionReceipt !== 'boolean')
|
|
25
|
+
throw new Error('invalid transfer capability version or flags');
|
|
26
|
+
return { version: 1, durableResume: row.durableResume === true, completionReceipt: row.completionReceipt === true,
|
|
27
|
+
extensions: extensions(row.extensions ?? []) };
|
|
28
|
+
}
|
|
29
|
+
export function transferCapabilities(value) {
|
|
30
|
+
const row = record(value, ['version', 'maxFileBytes', 'receiveAvailable', 'durableResume', 'completionReceipt', 'extensions']);
|
|
31
|
+
if (typeof row.receiveAvailable !== 'boolean' || !Number.isSafeInteger(row.maxFileBytes) || row.maxFileBytes < 0)
|
|
32
|
+
throw new Error('invalid transfer capability file limit');
|
|
33
|
+
return { ...requiredCapabilities({ version: row.version, durableResume: row.durableResume,
|
|
34
|
+
completionReceipt: row.completionReceipt, extensions: row.extensions }, true), receiveAvailable: row.receiveAvailable, maxFileBytes: row.maxFileBytes };
|
|
35
|
+
}
|
|
36
|
+
export function requiredTransferCapabilities(header) {
|
|
37
|
+
const encoded = header.metadata?.[REQUIRED_CAPABILITIES_METADATA_KEY];
|
|
38
|
+
if (encoded === undefined)
|
|
39
|
+
return null;
|
|
40
|
+
if (typeof encoded !== 'string' || new TextEncoder().encode(encoded).byteLength > MAX_CAPABILITY_BYTES)
|
|
41
|
+
throw new Error('invalid required transfer capability metadata');
|
|
42
|
+
return requiredCapabilities(JSON.parse(encoded), true);
|
|
43
|
+
}
|
|
44
|
+
export function withRequiredCapabilities(header, requirements) {
|
|
45
|
+
const encoded = JSON.stringify(requiredCapabilities(requirements));
|
|
46
|
+
const old = header.metadata?.[REQUIRED_CAPABILITIES_METADATA_KEY];
|
|
47
|
+
if (old !== undefined && old !== encoded)
|
|
48
|
+
throw new Error('transfer_identity_conflict: required transfer capabilities differ from the accepted header');
|
|
49
|
+
return { ...header, metadata: { ...header.metadata, [REQUIRED_CAPABILITIES_METADATA_KEY]: encoded } };
|
|
50
|
+
}
|
|
51
|
+
export function requireCapabilities(capabilities, header) {
|
|
52
|
+
const required = requiredTransferCapabilities(header);
|
|
53
|
+
if (!capabilities.receiveAvailable || header.size > capabilities.maxFileBytes || required?.durableResume && !capabilities.durableResume
|
|
54
|
+
|| required?.completionReceipt && !capabilities.completionReceipt
|
|
55
|
+
|| required?.extensions.some(extension => !capabilities.extensions.includes(extension)))
|
|
56
|
+
throw new Error('transfer_capability_required: receiver cannot admit the selected size or required transfer features');
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Application bulk-body pacing only. Admission, ACKs, and lifecycle messages
|
|
2
|
+
* bypass this controller. Share one instance across all bulk work in a runtime. */
|
|
3
|
+
export type BulkDirection = 'upload' | 'download';
|
|
4
|
+
export interface BulkRates {
|
|
5
|
+
uploadBytesPerSecond: number | null;
|
|
6
|
+
downloadBytesPerSecond: number | null;
|
|
7
|
+
}
|
|
8
|
+
export interface BulkFlowSnapshot {
|
|
9
|
+
version: 1;
|
|
10
|
+
burstBytes: number;
|
|
11
|
+
upload: {
|
|
12
|
+
rate: number | null;
|
|
13
|
+
tokens: number;
|
|
14
|
+
};
|
|
15
|
+
download: {
|
|
16
|
+
rate: number | null;
|
|
17
|
+
tokens: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare class BulkFlowController {
|
|
21
|
+
readonly burstBytes: number;
|
|
22
|
+
private readonly maxPending;
|
|
23
|
+
private readonly states;
|
|
24
|
+
private readonly changes;
|
|
25
|
+
private pending;
|
|
26
|
+
private used;
|
|
27
|
+
constructor(rates?: BulkRates, burstBytes?: number, maxPending?: number);
|
|
28
|
+
configure(rates: BulkRates): void;
|
|
29
|
+
/** Product storage owns durability and wall-clock elapsed time. Export
|
|
30
|
+
* after acquisition and persist before allowing the body attempt. Pending
|
|
31
|
+
* requests are not grants and are deliberately excluded. */
|
|
32
|
+
snapshot(): BulkFlowSnapshot;
|
|
33
|
+
/** Restore into a fresh owner only; switching execution owners must not
|
|
34
|
+
* mint a new burst. The caller supplies conservative offline elapsed time. */
|
|
35
|
+
restore(raw: unknown, elapsedMs: number): void;
|
|
36
|
+
/** Wait before submitting a bounded body slice to a transport. Control
|
|
37
|
+
* deadlines start after this wait; cancellation never authorizes a slice. */
|
|
38
|
+
acquire(direction: BulkDirection, peer: string, bytes: number, signal?: AbortSignal): Promise<void>;
|
|
39
|
+
/** Admit all applicable budgets synchronously, before any token debit.
|
|
40
|
+
* Product owners retain/persist these controllers across job handoff. */
|
|
41
|
+
acquireWith(direction: BulkDirection, peer: string, bytes: number, constraints: readonly BulkFlowController[], signal?: AbortSignal): Promise<void>;
|
|
42
|
+
private refill;
|
|
43
|
+
private pump;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=flowControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowControl.d.ts","sourceRoot":"","sources":["../src/flowControl.ts"],"names":[],"mappings":"AAAA;mFACmF;AACnF,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;AAClD,MAAM,WAAW,SAAS;IAAG,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AACzG,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAMD,qBAAa,kBAAkB;IAMvB,QAAQ,CAAC,UAAU;IAAc,OAAO,CAAC,QAAQ,CAAC,UAAU;IALhE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsE;IAC9F,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,IAAI,CAAS;gBACT,KAAK,GAAE,SAAwE,EAC9E,UAAU,SAAY,EAAmB,UAAU,SAAO;IASvE,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAcjC;;gEAE4D;IAC5D,QAAQ,IAAI,gBAAgB;IAM5B;kFAC8E;IAC9E,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAsB9C;iFAC6E;IAC7E,OAAO,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAGnG;6EACyE;IACzE,WAAW,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAC7D,WAAW,EAAE,SAAS,kBAAkB,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCpF,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,IAAI;CA4Cf"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export class BulkFlowController {
|
|
2
|
+
constructor(rates = { uploadBytesPerSecond: null, downloadBytesPerSecond: null }, burstBytes = 64 * 1024, maxPending = 4096) {
|
|
3
|
+
this.burstBytes = burstBytes;
|
|
4
|
+
this.maxPending = maxPending;
|
|
5
|
+
this.changes = { upload: new Set(), download: new Set() };
|
|
6
|
+
this.pending = 0;
|
|
7
|
+
this.used = false;
|
|
8
|
+
if (!Number.isSafeInteger(burstBytes) || burstBytes < 1 || burstBytes > 64 * 1024
|
|
9
|
+
|| !Number.isSafeInteger(maxPending) || maxPending < 1 || maxPending > 4096)
|
|
10
|
+
throw new Error('Invalid bulk flow resource bound');
|
|
11
|
+
const state = () => ({ rate: null, tokens: burstBytes,
|
|
12
|
+
updatedAt: performance.now(), peers: new Map(), order: [] });
|
|
13
|
+
this.states = { upload: state(), download: state() };
|
|
14
|
+
this.configure(rates);
|
|
15
|
+
}
|
|
16
|
+
configure(rates) {
|
|
17
|
+
for (const direction of ['upload', 'download']) {
|
|
18
|
+
const rate = rates[`${direction}BytesPerSecond`];
|
|
19
|
+
if (rate !== null && (!Number.isSafeInteger(rate) || rate <= 0))
|
|
20
|
+
throw new Error('Bulk rate must be unset or a positive safe integer');
|
|
21
|
+
}
|
|
22
|
+
for (const direction of ['upload', 'download']) {
|
|
23
|
+
const state = this.states[direction];
|
|
24
|
+
this.refill(state);
|
|
25
|
+
state.rate = rates[`${direction}BytesPerSecond`];
|
|
26
|
+
this.pump(direction);
|
|
27
|
+
for (const changed of this.changes[direction])
|
|
28
|
+
changed();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Product storage owns durability and wall-clock elapsed time. Export
|
|
32
|
+
* after acquisition and persist before allowing the body attempt. Pending
|
|
33
|
+
* requests are not grants and are deliberately excluded. */
|
|
34
|
+
snapshot() {
|
|
35
|
+
this.refill(this.states.upload);
|
|
36
|
+
this.refill(this.states.download);
|
|
37
|
+
const value = (direction) => ({ rate: this.states[direction].rate,
|
|
38
|
+
tokens: this.states[direction].tokens });
|
|
39
|
+
return { version: 1, burstBytes: this.burstBytes, upload: value('upload'), download: value('download') };
|
|
40
|
+
}
|
|
41
|
+
/** Restore into a fresh owner only; switching execution owners must not
|
|
42
|
+
* mint a new burst. The caller supplies conservative offline elapsed time. */
|
|
43
|
+
restore(raw, elapsedMs) {
|
|
44
|
+
if (this.used || !Number.isFinite(elapsedMs) || elapsedMs < 0)
|
|
45
|
+
throw new Error('Invalid bulk flow restoration');
|
|
46
|
+
const saved = raw;
|
|
47
|
+
if (saved?.version !== 1 || saved.burstBytes !== this.burstBytes)
|
|
48
|
+
throw new Error('Unsupported bulk flow state');
|
|
49
|
+
for (const direction of ['upload', 'download']) {
|
|
50
|
+
const value = saved[direction];
|
|
51
|
+
if (!value || (value.rate !== null && (!Number.isSafeInteger(value.rate) || value.rate <= 0))
|
|
52
|
+
|| !Number.isFinite(value.tokens) || value.tokens < 0 || value.tokens > this.burstBytes)
|
|
53
|
+
throw new Error('Invalid bulk flow state');
|
|
54
|
+
}
|
|
55
|
+
const now = performance.now();
|
|
56
|
+
for (const direction of ['upload', 'download']) {
|
|
57
|
+
const value = saved[direction], state = this.states[direction];
|
|
58
|
+
state.rate = value.rate;
|
|
59
|
+
state.tokens = value.rate === null ? value.tokens : Math.min(this.burstBytes, value.tokens + elapsedMs * value.rate / 1000);
|
|
60
|
+
state.updatedAt = now;
|
|
61
|
+
}
|
|
62
|
+
this.used = true;
|
|
63
|
+
}
|
|
64
|
+
/** Wait before submitting a bounded body slice to a transport. Control
|
|
65
|
+
* deadlines start after this wait; cancellation never authorizes a slice. */
|
|
66
|
+
acquire(direction, peer, bytes, signal) {
|
|
67
|
+
return this.acquireWith(direction, peer, bytes, [], signal);
|
|
68
|
+
}
|
|
69
|
+
/** Admit all applicable budgets synchronously, before any token debit.
|
|
70
|
+
* Product owners retain/persist these controllers across job handoff. */
|
|
71
|
+
acquireWith(direction, peer, bytes, constraints, signal) {
|
|
72
|
+
if (!Array.isArray(constraints) || constraints.length > 16 || new Set(constraints).size !== constraints.length
|
|
73
|
+
|| constraints.some(flow => !(flow instanceof BulkFlowController) || flow === this || bytes > flow.burstBytes))
|
|
74
|
+
return Promise.reject(new Error('Invalid bulk flow constraints'));
|
|
75
|
+
if (!this.states[direction] || !peer || peer.length > 512 || !Number.isSafeInteger(bytes)
|
|
76
|
+
|| bytes < 0 || bytes > this.burstBytes)
|
|
77
|
+
return Promise.reject(new Error('Invalid bulk body slice'));
|
|
78
|
+
if (signal?.aborted)
|
|
79
|
+
return Promise.reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
80
|
+
if (!bytes)
|
|
81
|
+
return Promise.resolve();
|
|
82
|
+
this.used = true;
|
|
83
|
+
for (const flow of constraints)
|
|
84
|
+
flow.used = true;
|
|
85
|
+
if (this.pending >= this.maxPending)
|
|
86
|
+
return Promise.reject(new Error('receiver_busy: bulk flow queue is full'));
|
|
87
|
+
const state = this.states[direction];
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const wake = () => this.pump(direction);
|
|
90
|
+
const dependencies = [...constraints];
|
|
91
|
+
for (const flow of dependencies)
|
|
92
|
+
flow.changes[direction].add(wake);
|
|
93
|
+
const request = { bytes, resolve, reject, signal, constraints: dependencies,
|
|
94
|
+
release: () => { for (const flow of dependencies)
|
|
95
|
+
flow.changes[direction].delete(wake); } };
|
|
96
|
+
const queue = state.peers.get(peer) ?? [];
|
|
97
|
+
if (!queue.length) {
|
|
98
|
+
state.peers.set(peer, queue);
|
|
99
|
+
state.order.push(peer);
|
|
100
|
+
}
|
|
101
|
+
queue.push(request);
|
|
102
|
+
this.pending++;
|
|
103
|
+
if (signal) {
|
|
104
|
+
request.abort = () => {
|
|
105
|
+
const index = queue.indexOf(request);
|
|
106
|
+
if (index < 0)
|
|
107
|
+
return;
|
|
108
|
+
queue.splice(index, 1);
|
|
109
|
+
this.pending--;
|
|
110
|
+
signal.removeEventListener('abort', request.abort);
|
|
111
|
+
if (!queue.length) {
|
|
112
|
+
state.peers.delete(peer);
|
|
113
|
+
state.order = state.order.filter(value => value !== peer);
|
|
114
|
+
}
|
|
115
|
+
request.release();
|
|
116
|
+
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
117
|
+
this.pump(direction);
|
|
118
|
+
};
|
|
119
|
+
signal.addEventListener('abort', request.abort, { once: true });
|
|
120
|
+
}
|
|
121
|
+
this.pump(direction);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
refill(state) {
|
|
125
|
+
const now = performance.now();
|
|
126
|
+
if (state.rate !== null)
|
|
127
|
+
state.tokens = Math.min(this.burstBytes, state.tokens + Math.max(0, now - state.updatedAt) * state.rate / 1000);
|
|
128
|
+
state.updatedAt = now;
|
|
129
|
+
}
|
|
130
|
+
pump(direction) {
|
|
131
|
+
const state = this.states[direction];
|
|
132
|
+
if (state.timer !== undefined) {
|
|
133
|
+
clearTimeout(state.timer);
|
|
134
|
+
state.timer = undefined;
|
|
135
|
+
}
|
|
136
|
+
this.refill(state);
|
|
137
|
+
let blocked = 0, nextWait;
|
|
138
|
+
while (state.order.length && blocked < state.order.length) {
|
|
139
|
+
const peer = state.order[0], queue = state.peers.get(peer);
|
|
140
|
+
const request = queue[0];
|
|
141
|
+
const globalWait = state.rate !== null && state.tokens + 1e-9 < request.bytes
|
|
142
|
+
? (request.bytes - state.tokens) * 1000 / state.rate : undefined;
|
|
143
|
+
let constraintWait;
|
|
144
|
+
for (const flow of request.constraints) {
|
|
145
|
+
const child = flow.states[direction];
|
|
146
|
+
flow.refill(child);
|
|
147
|
+
if (child.rate !== null && child.tokens + 1e-9 < request.bytes) {
|
|
148
|
+
const wait = (request.bytes - child.tokens) * 1000 / child.rate;
|
|
149
|
+
constraintWait = Math.max(constraintWait ?? 0, wait);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (constraintWait === undefined && globalWait !== undefined) {
|
|
153
|
+
// An aggregate-ready large slice keeps its turn; small slices
|
|
154
|
+
// cannot repeatedly consume its earned global credit.
|
|
155
|
+
nextWait = Math.min(nextWait ?? Infinity, globalWait);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (constraintWait !== undefined) {
|
|
159
|
+
// A job-blocked slice reserves nothing. Rotate only this
|
|
160
|
+
// ineligible peer so unrelated ready work can use its turn.
|
|
161
|
+
nextWait = Math.min(nextWait ?? Infinity, Math.max(globalWait ?? 0, constraintWait));
|
|
162
|
+
state.order.push(state.order.shift());
|
|
163
|
+
blocked++;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (state.rate !== null)
|
|
167
|
+
state.tokens = Math.max(0, state.tokens - request.bytes);
|
|
168
|
+
for (const flow of request.constraints) {
|
|
169
|
+
const child = flow.states[direction];
|
|
170
|
+
if (child.rate !== null)
|
|
171
|
+
child.tokens = Math.max(0, child.tokens - request.bytes);
|
|
172
|
+
}
|
|
173
|
+
queue.shift();
|
|
174
|
+
state.order.shift();
|
|
175
|
+
this.pending--;
|
|
176
|
+
if (queue.length)
|
|
177
|
+
state.order.push(peer);
|
|
178
|
+
else
|
|
179
|
+
state.peers.delete(peer);
|
|
180
|
+
if (request.abort)
|
|
181
|
+
request.signal?.removeEventListener('abort', request.abort);
|
|
182
|
+
request.release();
|
|
183
|
+
request.resolve();
|
|
184
|
+
blocked = 0;
|
|
185
|
+
}
|
|
186
|
+
if (state.order.length && nextWait !== undefined) {
|
|
187
|
+
const waitMs = Math.max(1, Math.ceil(nextWait));
|
|
188
|
+
state.timer = setTimeout(() => { state.timer = undefined; this.pump(direction); }, Math.min(waitMs, 2147483647));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=flowControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowControl.js","sourceRoot":"","sources":["../src/flowControl.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import { type TransferCapabilities, type TransferRequirements } from './capabilities.js';
|
|
2
|
+
export { requiredTransferCapabilities, withRequiredCapabilities, REQUIRED_CAPABILITIES_METADATA_KEY, LEGACY_MAX_FILE_BYTES } from './capabilities.js';
|
|
3
|
+
export type { TransferCapabilities, TransferRequirements, RequiredTransferCapabilities } from './capabilities.js';
|
|
1
4
|
import type { Avenue } from 'openrtc';
|
|
2
5
|
import type { ChannelDescriptor, RuntimeClient } from 'openrtc/runtime';
|
|
6
|
+
/** Checkpoint-before-offset and product-commit-before-completion semantics. */
|
|
7
|
+
export declare const DURABLE_DELIVERY_VERSION: 1;
|
|
8
|
+
/** Per-sink body policy, legacy admission and execution cancellation. */
|
|
9
|
+
export declare const BULK_RECEIVE_FLOW_CONTROL_VERSION: 1;
|
|
10
|
+
export declare const BULK_RECEIVE_ADMISSION_WAIT_VERSION: 1;
|
|
11
|
+
/** Unpaced stream/v1 sink policy and idle execution retirement. */
|
|
12
|
+
export declare const BULK_STREAM_RECEIVE_FLOW_CONTROL_VERSION: 1;
|
|
3
13
|
/** File transfer binds to one already-active 2.0 capability avenue. */
|
|
4
14
|
export type TransferClient = Pick<Avenue, 'channels' | 'diagnostics' | 'peers'> & {
|
|
5
15
|
/** Stable, route-level application intake for replaceable connection wrappers. */
|
|
@@ -12,6 +22,12 @@ export type TransferClient = Pick<Avenue, 'channels' | 'diagnostics' | 'peers'>
|
|
|
12
22
|
*/
|
|
13
23
|
export declare function transferFromRuntime(runtime: RuntimeClient): TransferClient;
|
|
14
24
|
export declare const CHANNEL: ChannelDescriptor;
|
|
25
|
+
export declare const RECEIVER_WAIT_METADATA_KEY = "openrtc.receive-wait";
|
|
26
|
+
export declare const MAX_RECEIVER_WAIT_LEASE_MS = 60000;
|
|
27
|
+
/** Persist this capability as part of the immutable offer before acceptance. */
|
|
28
|
+
export declare function withReceiverWaitCapability(header: FileTransferHeader): FileTransferHeader;
|
|
29
|
+
export declare const MAX_TRANSFER_CHUNK_BYTES: number;
|
|
30
|
+
export declare const MAX_TRANSFER_WINDOW_CHUNKS = 64;
|
|
15
31
|
export interface FileTransferHeader {
|
|
16
32
|
version: 1;
|
|
17
33
|
transferId: string;
|
|
@@ -40,10 +56,22 @@ export interface ReceivedFile {
|
|
|
40
56
|
remoteNodeId: string;
|
|
41
57
|
}
|
|
42
58
|
export interface ReceiveSink<Result = unknown> {
|
|
59
|
+
/** Product execution policy for this accepted receive. Unpaced legacy
|
|
60
|
+
* offers may opt out of wait negotiation; their callback must not wait for
|
|
61
|
+
* rate credit and must reject if a later policy requires it. */
|
|
62
|
+
bodyPolicy?: {
|
|
63
|
+
requiresReceiverWait: boolean;
|
|
64
|
+
signal: AbortSignal;
|
|
65
|
+
beforeAttempt(attempt: TransferReceiveBodyAttempt): Promise<void>;
|
|
66
|
+
};
|
|
43
67
|
/** Must equal the requested header offset for a resumed transfer. */
|
|
44
68
|
resumeOffset?: number;
|
|
45
69
|
write(chunk: Uint8Array): Promise<void>;
|
|
70
|
+
/** Persist the accepted prefix before the protocol advertises its offset. */
|
|
71
|
+
checkpoint?(): Promise<void>;
|
|
46
72
|
close(): Promise<Result>;
|
|
73
|
+
/** Commit the product receipt after verification and history publication. */
|
|
74
|
+
commit?(): Promise<void>;
|
|
47
75
|
abort(reason: unknown): Promise<void>;
|
|
48
76
|
/** Verify a sender-declared digest without requiring WebView buffering. */
|
|
49
77
|
verifySha256?(expectedHex: string): Promise<boolean>;
|
|
@@ -62,14 +90,63 @@ export interface TransferOptions<Result = ReceivedFile> {
|
|
|
62
90
|
maxFileBytes?: number;
|
|
63
91
|
/** Maximum time to wait for the receiver's persisted-and-verified ACK. */
|
|
64
92
|
ackTimeoutMs?: number;
|
|
65
|
-
createReceiveSink?: (header: FileTransferHeader, remoteNodeId: string) => Promise<ReceiveSink<Result>>;
|
|
93
|
+
createReceiveSink?: (header: FileTransferHeader, remoteNodeId: string, signal?: AbortSignal) => Promise<ReceiveSink<Result>>;
|
|
94
|
+
/** Product-owned durable receipt lookup on the authenticated peer scope. */
|
|
95
|
+
hasCompletionReceipt?: (header: FileTransferHeader, remoteNodeId: string) => Promise<boolean>;
|
|
96
|
+
/** Application aggregate pacing/quota checkpoint before each body attempt.
|
|
97
|
+
* Runs outside the receiver-ACK deadline; headers and controls bypass it. */
|
|
98
|
+
beforeSendBody?: (attempt: TransferBodyAttempt) => Promise<void>;
|
|
99
|
+
/** Routed receivers pace/quota-check body work without granting a durable
|
|
100
|
+
* offset. Legacy offers are refused before sink acceptance unless a retained
|
|
101
|
+
* completion receipt exists. The callback must honor its execution signal. */
|
|
102
|
+
beforeReceiveBody?: (attempt: TransferReceiveBodyAttempt) => Promise<void>;
|
|
103
|
+
/** Each negotiated wait lease is bounded; only live receiver work renews it. */
|
|
104
|
+
receiveWaitLeaseMs?: number;
|
|
105
|
+
/** Claims belong to the product sink/authorization owner, never transport.
|
|
106
|
+
* A durable-resume claim requires an installed streaming receive sink. */
|
|
107
|
+
receiveCapabilities?: {
|
|
108
|
+
durableResume?: boolean;
|
|
109
|
+
extensions?: readonly string[];
|
|
110
|
+
};
|
|
66
111
|
}
|
|
112
|
+
export interface TransferBodyAttempt {
|
|
113
|
+
/** Source position of this immutable body slice; absent on older adapters. */
|
|
114
|
+
offset?: number;
|
|
115
|
+
transferId: string;
|
|
116
|
+
/** Target peer selector supplied to send(); the runtime resolves identity. */
|
|
117
|
+
peerId: string;
|
|
118
|
+
bytes: number;
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
}
|
|
121
|
+
export interface TransferReceiveBodyAttempt {
|
|
122
|
+
transferId: string;
|
|
123
|
+
remoteNodeId: string;
|
|
124
|
+
offset: number;
|
|
125
|
+
bytes: number;
|
|
126
|
+
signal: AbortSignal;
|
|
127
|
+
}
|
|
128
|
+
export { BulkFlowController } from './flowControl.js';
|
|
129
|
+
export type { BulkDirection, BulkRates, BulkFlowSnapshot } from './flowControl.js';
|
|
130
|
+
export declare const BULK_SEND_FLOW_CONTROL_VERSION = 1;
|
|
131
|
+
export declare const BULK_JOB_FLOW_CONTROL_VERSION = 1;
|
|
132
|
+
export declare const BULK_SCOPED_FLOW_CONTROL_VERSION = 1;
|
|
133
|
+
/** Product-persisted immutable offer. Body access is deferred until the
|
|
134
|
+
* authenticated receiver requests bytes rather than returning its receipt. */
|
|
135
|
+
export interface AcceptedFileSource {
|
|
136
|
+
header: FileTransferHeader;
|
|
137
|
+
openSource(): Promise<File>;
|
|
138
|
+
}
|
|
139
|
+
export type AcceptedSendOptions = Pick<SendFileOptions, 'signal' | 'onProgress' | 'beforeSendBody'>;
|
|
67
140
|
export interface SendFileOptions {
|
|
68
141
|
transferId?: string;
|
|
142
|
+
requiredCapabilities?: TransferRequirements;
|
|
69
143
|
metadata?: Record<string, string>;
|
|
70
144
|
integrity?: 'none' | 'sha256';
|
|
71
145
|
offsetBytes?: number;
|
|
72
146
|
signal?: AbortSignal;
|
|
147
|
+
/** Product execution-specific policy, in addition to the runtime policy.
|
|
148
|
+
* Both complete before a body attempt and outside the receiver ACK timer. */
|
|
149
|
+
beforeSendBody?: (attempt: TransferBodyAttempt) => Promise<void>;
|
|
73
150
|
onProgress?: (progress: TransferProgress) => void;
|
|
74
151
|
}
|
|
75
152
|
export interface ReceiveFileOptions<Result> {
|
|
@@ -81,6 +158,14 @@ export interface ReceiveFileOptions<Result> {
|
|
|
81
158
|
export type RoutedTransferFrame = {
|
|
82
159
|
kind: 'offer';
|
|
83
160
|
header: FileTransferHeader;
|
|
161
|
+
} | {
|
|
162
|
+
kind: 'capabilities-request';
|
|
163
|
+
transferId: string;
|
|
164
|
+
header?: FileTransferHeader;
|
|
165
|
+
} | {
|
|
166
|
+
kind: 'capabilities';
|
|
167
|
+
transferId: string;
|
|
168
|
+
capabilities: TransferCapabilities;
|
|
84
169
|
} | {
|
|
85
170
|
kind: 'resume';
|
|
86
171
|
transferId: string;
|
|
@@ -95,6 +180,11 @@ export type RoutedTransferFrame = {
|
|
|
95
180
|
kind: 'ack';
|
|
96
181
|
transferId: string;
|
|
97
182
|
offset: number;
|
|
183
|
+
} | {
|
|
184
|
+
kind: 'waiting';
|
|
185
|
+
transferId: string;
|
|
186
|
+
offset: number;
|
|
187
|
+
leaseMs: number;
|
|
98
188
|
} | {
|
|
99
189
|
kind: 'complete';
|
|
100
190
|
transferId: string;
|
|
@@ -117,6 +207,7 @@ export interface TransferRouteOptions<Result = ReceivedFile> extends TransferOpt
|
|
|
117
207
|
export declare function encodeFrame(frame: RoutedTransferFrame, maxHeaderBytes?: number, maxChunkBytes?: number): Uint8Array;
|
|
118
208
|
export declare function decodeFrame(input: Uint8Array, maxFileBytes?: number, maxHeaderBytes?: number, maxChunkBytes?: number): RoutedTransferFrame | null;
|
|
119
209
|
export declare function createTransfer<Result = ReceivedFile>(client: TransferClient, options?: TransferRouteOptions<Result>): {
|
|
210
|
+
sendAccepted(deviceId: string, source: AcceptedFileSource, sendOptions?: AcceptedSendOptions): Promise<string>;
|
|
120
211
|
send(deviceId: string, file: File, sendOptions?: SendFileOptions): Promise<string>;
|
|
121
212
|
onIncoming(configured: ReceiveFileOptions<Result>): () => void;
|
|
122
213
|
dispose(): void;
|
|
@@ -128,6 +219,7 @@ export declare function createTransfer<Result = ReceivedFile>(client: TransferCl
|
|
|
128
219
|
* the consumer remains responsible for persisting the surrounding intent.
|
|
129
220
|
*/
|
|
130
221
|
export declare function createRoutedTransfer<Result = ReceivedFile>(client: TransferClient, options?: TransferRouteOptions<Result>): {
|
|
222
|
+
sendAccepted(deviceId: string, source: AcceptedFileSource, sendOptions?: AcceptedSendOptions): Promise<string>;
|
|
131
223
|
send(deviceId: string, file: File, sendOptions?: SendFileOptions): Promise<string>;
|
|
132
224
|
onIncoming(configured: ReceiveFileOptions<Result>): () => void;
|
|
133
225
|
dispose(): void;
|