@oatscenter/trellis 0.100.0-rc.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 +59 -0
- package/auth/authorization/client_context.d.ts +61 -0
- package/auth/authorization/client_context.js +266 -0
- package/auth/authorization/nats_registry.d.ts +41 -0
- package/auth/authorization/nats_registry.js +283 -0
- package/auth/authorization/provider_cache.d.ts +90 -0
- package/auth/authorization/provider_cache.js +783 -0
- package/auth/authorization/refresh.d.ts +46 -0
- package/auth/authorization/refresh.js +246 -0
- package/auth/authorization/types.d.ts +207 -0
- package/auth/authorization/types.js +60 -0
- package/auth/authorization_context.d.ts +11 -0
- package/auth/authorization_context.js +10 -0
- package/auth/browser/flow_types.d.ts +84 -0
- package/auth/browser/flow_types.js +70 -0
- package/auth/browser/portal.d.ts +21 -0
- package/auth/browser/portal.js +307 -0
- package/auth/browser/storage.d.ts +26 -0
- package/auth/browser/storage.js +224 -0
- package/auth/browser.d.ts +11 -0
- package/auth/browser.js +10 -0
- package/auth/browser_recovery.d.ts +13 -0
- package/auth/browser_recovery.js +38 -0
- package/auth/device_activation.d.ts +134 -0
- package/auth/device_activation.js +376 -0
- package/auth/http_error.d.ts +12 -0
- package/auth/http_error.js +32 -0
- package/auth/keys.d.ts +5 -0
- package/auth/keys.js +63 -0
- package/auth/mod.d.ts +16 -0
- package/auth/mod.js +16 -0
- package/auth/proof.d.ts +31 -0
- package/auth/proof.js +95 -0
- package/auth/protocol_wasm/trellis_protocol_wasm.d.ts +138 -0
- package/auth/protocol_wasm/trellis_protocol_wasm.js +605 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bg.wasm +0 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bytes.d.ts +1 -0
- package/auth/protocol_wasm/trellis_protocol_wasm_bytes.js +2 -0
- package/auth/protocol_wasm.d.ts +179 -0
- package/auth/protocol_wasm.js +133 -0
- package/auth/session_auth.d.ts +30 -0
- package/auth/session_auth.js +77 -0
- package/auth/session_proof.d.ts +53 -0
- package/auth/session_proof.js +43 -0
- package/auth/time.d.ts +6 -0
- package/auth/time.js +7 -0
- package/auth/trellis_id.d.ts +1 -0
- package/auth/trellis_id.js +5 -0
- package/auth/utils.d.ts +10 -0
- package/auth/utils.js +69 -0
- package/auth.d.ts +1 -0
- package/auth.js +1 -0
- package/caller.d.ts +111 -0
- package/caller.js +85 -0
- package/client.d.ts +14 -0
- package/client.js +1 -0
- package/client_connect.d.ts +143 -0
- package/client_connect.js +1097 -0
- package/codec.d.ts +13 -0
- package/codec.js +192 -0
- package/connection.d.ts +107 -0
- package/connection.js +396 -0
- package/device/activation.d.ts +42 -0
- package/device/activation.js +319 -0
- package/device.d.ts +80 -0
- package/device.js +687 -0
- package/env.d.ts +2 -0
- package/env.js +10 -0
- package/errors/AuthError.d.ts +30 -0
- package/errors/AuthError.js +34 -0
- package/errors/KVError.d.ts +30 -0
- package/errors/KVError.js +36 -0
- package/errors/OperationLifecycleError.d.ts +110 -0
- package/errors/OperationLifecycleError.js +142 -0
- package/errors/RemoteError.d.ts +61 -0
- package/errors/RemoteError.js +72 -0
- package/errors/SchemaValidationError.d.ts +48 -0
- package/errors/SchemaValidationError.js +49 -0
- package/errors/StoreError.d.ts +21 -0
- package/errors/StoreError.js +31 -0
- package/errors/TransferError.d.ts +21 -0
- package/errors/TransferError.js +31 -0
- package/errors/TransportError.d.ts +29 -0
- package/errors/TransportError.js +33 -0
- package/errors/TrellisError.d.ts +15 -0
- package/errors/TrellisError.js +15 -0
- package/errors/ValidationError.d.ts +50 -0
- package/errors/ValidationError.js +59 -0
- package/errors/index.d.ts +254 -0
- package/errors/index.js +238 -0
- package/generated.d.ts +4 -0
- package/generated.js +3 -0
- package/generated_support.d.ts +120 -0
- package/generated_support.js +359 -0
- package/globals.d.ts +9 -0
- package/globals.js +16 -0
- package/health_transport.d.ts +18 -0
- package/health_transport.js +32 -0
- package/helpers.d.ts +11 -0
- package/helpers.js +53 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/internal_sdk/generated/apis/auth/mod.d.ts +805 -0
- package/internal_sdk/generated/apis/auth/mod.js +958 -0
- package/internal_sdk/generated/apis/core/mod.d.ts +79 -0
- package/internal_sdk/generated/apis/core/mod.js +93 -0
- package/internal_sdk/generated/apis/events/mod.d.ts +224 -0
- package/internal_sdk/generated/apis/events/mod.js +313 -0
- package/internal_sdk/generated/apis/health/mod.d.ts +106 -0
- package/internal_sdk/generated/apis/health/mod.js +132 -0
- package/internal_sdk/generated/apis/index.d.ts +7 -0
- package/internal_sdk/generated/apis/index.js +8 -0
- package/internal_sdk/generated/apis/jobs/mod.d.ts +176 -0
- package/internal_sdk/generated/apis/jobs/mod.js +209 -0
- package/internal_sdk/generated/apis/state/mod.d.ts +104 -0
- package/internal_sdk/generated/apis/state/mod.js +139 -0
- package/internal_sdk/generated/index.d.ts +4 -0
- package/internal_sdk/generated/index.js +5 -0
- package/internal_sdk/generated/package.json +18 -0
- package/internal_sdk/generated/participants/cli/mod.d.ts +402 -0
- package/internal_sdk/generated/participants/cli/mod.js +460 -0
- package/internal_sdk/generated/participants/cli/types.d.ts +2 -0
- package/internal_sdk/generated/participants/cli/types.js +3 -0
- package/internal_sdk/generated/participants/console/mod.d.ts +489 -0
- package/internal_sdk/generated/participants/console/mod.js +579 -0
- package/internal_sdk/generated/participants/console/types.d.ts +2 -0
- package/internal_sdk/generated/participants/console/types.js +3 -0
- package/internal_sdk/generated/participants/eventsRuntime/mod.d.ts +64 -0
- package/internal_sdk/generated/participants/eventsRuntime/mod.js +42 -0
- package/internal_sdk/generated/participants/eventsRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/eventsRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/healthRuntime/mod.d.ts +58 -0
- package/internal_sdk/generated/participants/healthRuntime/mod.js +36 -0
- package/internal_sdk/generated/participants/healthRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/healthRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/index.d.ts +8 -0
- package/internal_sdk/generated/participants/index.js +9 -0
- package/internal_sdk/generated/participants/jobsRuntime/mod.d.ts +64 -0
- package/internal_sdk/generated/participants/jobsRuntime/mod.js +42 -0
- package/internal_sdk/generated/participants/jobsRuntime/types.d.ts +2 -0
- package/internal_sdk/generated/participants/jobsRuntime/types.js +3 -0
- package/internal_sdk/generated/participants/platform/mod.d.ts +164 -0
- package/internal_sdk/generated/participants/platform/mod.js +149 -0
- package/internal_sdk/generated/participants/platform/types.d.ts +7 -0
- package/internal_sdk/generated/participants/platform/types.js +5 -0
- package/internal_sdk/generated/participants/portal/mod.d.ts +131 -0
- package/internal_sdk/generated/participants/portal/mod.js +109 -0
- package/internal_sdk/generated/participants/portal/types.d.ts +2 -0
- package/internal_sdk/generated/participants/portal/types.js +3 -0
- package/internal_sdk/generated/types/_internal/p0.d.ts +13511 -0
- package/internal_sdk/generated/types/_internal/p0.js +6105 -0
- package/internal_sdk/generated/types/index.d.ts +2951 -0
- package/internal_sdk/generated/types/index.js +1477 -0
- package/jobs.d.ts +309 -0
- package/jobs.js +389 -0
- package/kv.d.ts +85 -0
- package/kv.js +330 -0
- package/operations.d.ts +189 -0
- package/operations.js +847 -0
- package/package.json +73 -0
- package/pagination.d.ts +25 -0
- package/pagination.js +117 -0
- package/participant.d.ts +6 -0
- package/participant.js +6 -0
- package/participant_runtime/api.d.ts +131 -0
- package/participant_runtime/api.js +42 -0
- package/participant_runtime/json.d.ts +16 -0
- package/participant_runtime/json.js +193 -0
- package/participant_runtime/metadata.d.ts +34 -0
- package/participant_runtime/metadata.js +4 -0
- package/participant_runtime/participant.d.ts +200 -0
- package/participant_runtime/participant.js +330 -0
- package/participant_runtime/schemas.d.ts +347 -0
- package/participant_runtime/schemas.js +211 -0
- package/participant_runtime/surface_names.d.ts +16 -0
- package/participant_runtime/surface_names.js +34 -0
- package/portable.d.ts +30 -0
- package/portable.js +14 -0
- package/provider.d.ts +69 -0
- package/provider.js +68 -0
- package/runtime_transport.d.ts +25 -0
- package/runtime_transport.js +48 -0
- package/service/mod.d.ts +14 -0
- package/service/mod.js +13 -0
- package/service/outbox_inbox.d.ts +259 -0
- package/service/outbox_inbox.js +908 -0
- package/service/runtime/bootstrap.d.ts +56 -0
- package/service/runtime/bootstrap.js +226 -0
- package/service/runtime/core.d.ts +46 -0
- package/service/runtime/core.js +1747 -0
- package/service/runtime/health.d.ts +97 -0
- package/service/runtime/health.js +153 -0
- package/service/runtime/health_schemas.d.ts +23 -0
- package/service/runtime/health_schemas.js +20 -0
- package/service/runtime/internal_jobs/active-job.d.ts +40 -0
- package/service/runtime/internal_jobs/active-job.js +91 -0
- package/service/runtime/internal_jobs/bindings.d.ts +40 -0
- package/service/runtime/internal_jobs/bindings.js +1 -0
- package/service/runtime/internal_jobs/cancellation-registry.d.ts +13 -0
- package/service/runtime/internal_jobs/cancellation-registry.js +55 -0
- package/service/runtime/internal_jobs/heartbeat.d.ts +33 -0
- package/service/runtime/internal_jobs/heartbeat.js +65 -0
- package/service/runtime/internal_jobs/job-manager.d.ts +126 -0
- package/service/runtime/internal_jobs/job-manager.js +821 -0
- package/service/runtime/internal_jobs/key-coordinator.d.ts +280 -0
- package/service/runtime/internal_jobs/key-coordinator.js +747 -0
- package/service/runtime/internal_jobs/projection.d.ts +3 -0
- package/service/runtime/internal_jobs/projection.js +42 -0
- package/service/runtime/internal_jobs/runtime-worker.d.ts +126 -0
- package/service/runtime/internal_jobs/runtime-worker.js +454 -0
- package/service/runtime/internal_jobs/types.d.ts +246 -0
- package/service/runtime/internal_jobs/types.js +163 -0
- package/service/runtime/logger.d.ts +2 -0
- package/service/runtime/logger.js +7 -0
- package/service/runtime/runtime.d.ts +16 -0
- package/service/runtime/runtime.js +1 -0
- package/service/runtime/service.d.ts +372 -0
- package/service/runtime/service.js +1896 -0
- package/service/runtime/transfer/download.d.ts +27 -0
- package/service/runtime/transfer/download.js +1 -0
- package/service/runtime/transfer/protocol.d.ts +15 -0
- package/service/runtime/transfer/protocol.js +46 -0
- package/service/runtime/transfer/queue.d.ts +18 -0
- package/service/runtime/transfer/queue.js +136 -0
- package/service/runtime/transfer/upload.d.ts +40 -0
- package/service/runtime/transfer/upload.js +27 -0
- package/service/runtime/transfer.d.ts +62 -0
- package/service/runtime/transfer.js +763 -0
- package/session.d.ts +862 -0
- package/session.js +3407 -0
- package/store.d.ts +71 -0
- package/store.js +412 -0
- package/store_entry.d.ts +20 -0
- package/store_entry.js +75 -0
- package/tasks.d.ts +10 -0
- package/tasks.js +28 -0
- package/telemetry/carrier.d.ts +8 -0
- package/telemetry/carrier.js +20 -0
- package/telemetry/core.d.ts +7 -0
- package/telemetry/core.js +14 -0
- package/telemetry/env.d.ts +2 -0
- package/telemetry/env.js +10 -0
- package/telemetry/init.d.ts +2 -0
- package/telemetry/init.js +7 -0
- package/telemetry/metrics.d.ts +81 -0
- package/telemetry/metrics.js +267 -0
- package/telemetry/mod.d.ts +13 -0
- package/telemetry/mod.js +8 -0
- package/telemetry/nats.d.ts +6 -0
- package/telemetry/nats.js +6 -0
- package/telemetry/result.d.ts +1 -0
- package/telemetry/result.js +18 -0
- package/telemetry/runtime.d.ts +1 -0
- package/telemetry/runtime.js +141 -0
- package/telemetry/trace.d.ts +4 -0
- package/telemetry/trace.js +26 -0
- package/telemetry/trellis.d.ts +1 -0
- package/telemetry/trellis.js +1 -0
- package/telemetry.d.ts +1 -0
- package/telemetry.js +1 -0
- package/transfer.d.ts +112 -0
- package/transfer.js +525 -0
- package/transfer_protocol.d.ts +1 -0
- package/transfer_protocol.js +13 -0
package/transfer.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { AsyncResult, type Result as ResultType } from "@oatscenter/result";
|
|
2
|
+
import { type MsgHdrs, type NatsConnection } from "@nats-io/nats-core";
|
|
3
|
+
import Type, { type Static } from "typebox";
|
|
4
|
+
import { TransferError } from "./errors/TransferError.ts";
|
|
5
|
+
export declare const MAX_TRANSFER_CHUNK_BYTES: number;
|
|
6
|
+
export declare const FileInfoSchema: Type.TObject<{
|
|
7
|
+
key: Type.TString;
|
|
8
|
+
size: Type.TInteger;
|
|
9
|
+
updatedAt: Type.TString;
|
|
10
|
+
digest: Type.TOptional<Type.TString>;
|
|
11
|
+
contentType: Type.TOptional<Type.TString>;
|
|
12
|
+
metadata: Type.TRecord<"^.*$", Type.TString>;
|
|
13
|
+
}>;
|
|
14
|
+
export type FileInfo = Static<typeof FileInfoSchema>;
|
|
15
|
+
export declare const SendTransferGrantSchema: Type.TObject<{
|
|
16
|
+
direction: Type.TLiteral<"send">;
|
|
17
|
+
maxBytes: Type.TOptional<Type.TInteger>;
|
|
18
|
+
contentType: Type.TOptional<Type.TString>;
|
|
19
|
+
metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
20
|
+
type: Type.TLiteral<"TransferGrant">;
|
|
21
|
+
service: Type.TString;
|
|
22
|
+
sessionKey: Type.TString;
|
|
23
|
+
transferId: Type.TString;
|
|
24
|
+
subject: Type.TString;
|
|
25
|
+
expiresAt: Type.TString;
|
|
26
|
+
chunkBytes: Type.TInteger;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const ReceiveTransferGrantSchema: Type.TObject<{
|
|
29
|
+
direction: Type.TLiteral<"receive">;
|
|
30
|
+
info: Type.TObject<{
|
|
31
|
+
digest: Type.TString;
|
|
32
|
+
key: Type.TString;
|
|
33
|
+
size: Type.TInteger;
|
|
34
|
+
updatedAt: Type.TString;
|
|
35
|
+
contentType: Type.TOptional<Type.TString>;
|
|
36
|
+
metadata: Type.TRecord<"^.*$", Type.TString>;
|
|
37
|
+
}>;
|
|
38
|
+
type: Type.TLiteral<"TransferGrant">;
|
|
39
|
+
service: Type.TString;
|
|
40
|
+
sessionKey: Type.TString;
|
|
41
|
+
transferId: Type.TString;
|
|
42
|
+
subject: Type.TString;
|
|
43
|
+
expiresAt: Type.TString;
|
|
44
|
+
chunkBytes: Type.TInteger;
|
|
45
|
+
}>;
|
|
46
|
+
export declare const TransferGrantSchema: Type.TUnion<[Type.TObject<{
|
|
47
|
+
direction: Type.TLiteral<"send">;
|
|
48
|
+
maxBytes: Type.TOptional<Type.TInteger>;
|
|
49
|
+
contentType: Type.TOptional<Type.TString>;
|
|
50
|
+
metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
51
|
+
type: Type.TLiteral<"TransferGrant">;
|
|
52
|
+
service: Type.TString;
|
|
53
|
+
sessionKey: Type.TString;
|
|
54
|
+
transferId: Type.TString;
|
|
55
|
+
subject: Type.TString;
|
|
56
|
+
expiresAt: Type.TString;
|
|
57
|
+
chunkBytes: Type.TInteger;
|
|
58
|
+
}>, Type.TObject<{
|
|
59
|
+
direction: Type.TLiteral<"receive">;
|
|
60
|
+
info: Type.TObject<{
|
|
61
|
+
digest: Type.TString;
|
|
62
|
+
key: Type.TString;
|
|
63
|
+
size: Type.TInteger;
|
|
64
|
+
updatedAt: Type.TString;
|
|
65
|
+
contentType: Type.TOptional<Type.TString>;
|
|
66
|
+
metadata: Type.TRecord<"^.*$", Type.TString>;
|
|
67
|
+
}>;
|
|
68
|
+
type: Type.TLiteral<"TransferGrant">;
|
|
69
|
+
service: Type.TString;
|
|
70
|
+
sessionKey: Type.TString;
|
|
71
|
+
transferId: Type.TString;
|
|
72
|
+
subject: Type.TString;
|
|
73
|
+
expiresAt: Type.TString;
|
|
74
|
+
chunkBytes: Type.TInteger;
|
|
75
|
+
}>]>;
|
|
76
|
+
export type SendTransferGrant = Static<typeof SendTransferGrantSchema>;
|
|
77
|
+
export type ReceiveTransferGrant = Static<typeof ReceiveTransferGrantSchema>;
|
|
78
|
+
export type TransferGrant = Static<typeof TransferGrantSchema>;
|
|
79
|
+
export type TransferBody = Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
80
|
+
type TrellisTransferAuth = {
|
|
81
|
+
sessionKey: string;
|
|
82
|
+
sign(data: Uint8Array): Promise<Uint8Array> | Uint8Array;
|
|
83
|
+
currentIat?: () => number;
|
|
84
|
+
contextDigest?: string | (() => string);
|
|
85
|
+
};
|
|
86
|
+
declare class BaseTransferHandle {
|
|
87
|
+
#private;
|
|
88
|
+
protected constructor(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, inboxPrefix?: string);
|
|
89
|
+
protected get nc(): NatsConnection;
|
|
90
|
+
protected get inboxPrefix(): string;
|
|
91
|
+
protected get auth(): TrellisTransferAuth;
|
|
92
|
+
protected get timeoutMs(): number;
|
|
93
|
+
protected validateGrant(grant: TransferGrant, operation: string): ResultType<void, TransferError>;
|
|
94
|
+
protected buildHeaders(subject: string, reply: string, payload: Uint8Array, seq?: number, control?: "complete" | "cancel"): Promise<MsgHdrs>;
|
|
95
|
+
protected cancelTransfer(subject: string): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
export declare class SendTransferHandle extends BaseTransferHandle {
|
|
98
|
+
#private;
|
|
99
|
+
constructor(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, grant: SendTransferGrant, inboxPrefix?: string);
|
|
100
|
+
send(body: TransferBody): AsyncResult<FileInfo, TransferError>;
|
|
101
|
+
}
|
|
102
|
+
export declare class ReceiveTransferHandle extends BaseTransferHandle {
|
|
103
|
+
#private;
|
|
104
|
+
constructor(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, grant: ReceiveTransferGrant, inboxPrefix?: string);
|
|
105
|
+
stream(): AsyncResult<ReadableStream<Uint8Array>, TransferError>;
|
|
106
|
+
bytes(): AsyncResult<Uint8Array, TransferError>;
|
|
107
|
+
}
|
|
108
|
+
export type TransferHandle = SendTransferHandle | ReceiveTransferHandle;
|
|
109
|
+
export declare function createTransferHandle(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, grant: SendTransferGrant, inboxPrefix?: string): SendTransferHandle;
|
|
110
|
+
export declare function createTransferHandle(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, grant: ReceiveTransferGrant, inboxPrefix?: string): ReceiveTransferHandle;
|
|
111
|
+
export declare function createTransferHandle(nc: NatsConnection, auth: TrellisTransferAuth, timeoutMs: number, grant: TransferGrant, inboxPrefix?: string): TransferHandle;
|
|
112
|
+
export {};
|
package/transfer.js
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { AsyncResult, isErr, Result, } from "@oatscenter/result";
|
|
2
|
+
import { createInbox, headers as natsHeaders, } from "@nats-io/nats-core";
|
|
3
|
+
import Type, {} from "typebox";
|
|
4
|
+
import { ulid } from "ulid";
|
|
5
|
+
import { sha256 as incrementalSha256 } from "@noble/hashes/sha256";
|
|
6
|
+
import { buildProofInput } from "./auth/proof.js";
|
|
7
|
+
import { base64urlEncode, sha256 } from "./auth/utils.js";
|
|
8
|
+
import { TransferError } from "./errors/TransferError.js";
|
|
9
|
+
import { createNatsHeaderCarrier, injectTraceContext, recordTrellisError, } from "./telemetry/mod.js";
|
|
10
|
+
import { transferFrameProofPayload } from "./transfer_protocol.js";
|
|
11
|
+
const TRANSFER_SEQUENCE_HEADER = "trellis-transfer-seq";
|
|
12
|
+
const TRANSFER_EOF_HEADER = "trellis-transfer-eof";
|
|
13
|
+
const TRANSFER_CONTROL_HEADER = "trellis-transfer-control";
|
|
14
|
+
export const MAX_TRANSFER_CHUNK_BYTES = 1024 * 1024;
|
|
15
|
+
export const FileInfoSchema = Type.Object({
|
|
16
|
+
key: Type.String({ minLength: 1 }),
|
|
17
|
+
size: Type.Integer({ minimum: 0 }),
|
|
18
|
+
updatedAt: Type.String({ minLength: 1 }),
|
|
19
|
+
digest: Type.Optional(Type.String({ minLength: 1 })),
|
|
20
|
+
contentType: Type.Optional(Type.String({ minLength: 1 })),
|
|
21
|
+
metadata: Type.Record(Type.String({ minLength: 1 }), Type.String()),
|
|
22
|
+
});
|
|
23
|
+
const TransferGrantBaseSchema = Type.Object({
|
|
24
|
+
type: Type.Literal("TransferGrant"),
|
|
25
|
+
service: Type.String({ minLength: 1 }),
|
|
26
|
+
sessionKey: Type.String({ minLength: 1 }),
|
|
27
|
+
transferId: Type.String({ minLength: 1 }),
|
|
28
|
+
subject: Type.String({ minLength: 1 }),
|
|
29
|
+
expiresAt: Type.String({ minLength: 1 }),
|
|
30
|
+
chunkBytes: Type.Integer({ minimum: 1, maximum: MAX_TRANSFER_CHUNK_BYTES }),
|
|
31
|
+
});
|
|
32
|
+
export const SendTransferGrantSchema = Type.Object({
|
|
33
|
+
...TransferGrantBaseSchema.properties,
|
|
34
|
+
direction: Type.Literal("send"),
|
|
35
|
+
maxBytes: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
36
|
+
contentType: Type.Optional(Type.String({ minLength: 1 })),
|
|
37
|
+
metadata: Type.Optional(Type.Record(Type.String({ minLength: 1 }), Type.String())),
|
|
38
|
+
});
|
|
39
|
+
export const ReceiveTransferGrantSchema = Type.Object({
|
|
40
|
+
...TransferGrantBaseSchema.properties,
|
|
41
|
+
direction: Type.Literal("receive"),
|
|
42
|
+
info: Type.Object({
|
|
43
|
+
...FileInfoSchema.properties,
|
|
44
|
+
digest: Type.String({ minLength: 1 }),
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
export const TransferGrantSchema = Type.Union([
|
|
48
|
+
SendTransferGrantSchema,
|
|
49
|
+
ReceiveTransferGrantSchema,
|
|
50
|
+
]);
|
|
51
|
+
async function createTransferProof(auth, subject, reply, payload) {
|
|
52
|
+
const contextDigest = typeof auth.contextDigest === "function"
|
|
53
|
+
? auth.contextDigest()
|
|
54
|
+
: auth.contextDigest;
|
|
55
|
+
if (contextDigest === undefined) {
|
|
56
|
+
throw new Error("contextDigest is required to sign transfer proofs");
|
|
57
|
+
}
|
|
58
|
+
if (reply.length === 0) {
|
|
59
|
+
throw new Error("transfer reply subject must not be empty");
|
|
60
|
+
}
|
|
61
|
+
const payloadHash = await sha256(payload);
|
|
62
|
+
const iat = auth.currentIat?.() ?? Math.floor(Date.now() / 1000);
|
|
63
|
+
const requestId = ulid();
|
|
64
|
+
const proofOk = await auth.sign(await sha256(buildProofInput(contextDigest, subject, reply, payloadHash, iat, requestId)));
|
|
65
|
+
return {
|
|
66
|
+
proof: base64urlEncode(proofOk),
|
|
67
|
+
iat,
|
|
68
|
+
requestId,
|
|
69
|
+
contextDigest,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function expired(expiresAt) {
|
|
73
|
+
return Date.now() >= Date.parse(expiresAt);
|
|
74
|
+
}
|
|
75
|
+
function asUint8Array(body) {
|
|
76
|
+
return body instanceof Uint8Array ? body : new Uint8Array(body);
|
|
77
|
+
}
|
|
78
|
+
function streamFromAsyncIterable(iterable) {
|
|
79
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
80
|
+
return new ReadableStream({
|
|
81
|
+
async pull(controller) {
|
|
82
|
+
const next = await iterator.next();
|
|
83
|
+
if (next.done) {
|
|
84
|
+
controller.close();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
controller.enqueue(next.value);
|
|
88
|
+
},
|
|
89
|
+
async cancel(reason) {
|
|
90
|
+
await iterator.return?.(reason);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function streamFromBody(body) {
|
|
95
|
+
if (body instanceof Uint8Array || body instanceof ArrayBuffer) {
|
|
96
|
+
const bytes = asUint8Array(body);
|
|
97
|
+
return new ReadableStream({
|
|
98
|
+
start(controller) {
|
|
99
|
+
controller.enqueue(bytes);
|
|
100
|
+
controller.close();
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return body instanceof ReadableStream ? body : streamFromAsyncIterable(body);
|
|
105
|
+
}
|
|
106
|
+
async function* chunkBody(body, chunkBytes) {
|
|
107
|
+
const reader = streamFromBody(body).getReader();
|
|
108
|
+
let completed = false;
|
|
109
|
+
try {
|
|
110
|
+
while (true) {
|
|
111
|
+
const next = await reader.read();
|
|
112
|
+
if (next.done) {
|
|
113
|
+
completed = true;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let offset = 0;
|
|
117
|
+
while (offset < next.value.length) {
|
|
118
|
+
const end = Math.min(offset + chunkBytes, next.value.length);
|
|
119
|
+
yield next.value.slice(offset, end);
|
|
120
|
+
offset = end;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
if (!completed)
|
|
126
|
+
void reader.cancel().catch(() => undefined);
|
|
127
|
+
reader.releaseLock();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function parseTransferAck(msg, operation) {
|
|
131
|
+
if (msg.headers?.get("status") === "error") {
|
|
132
|
+
return Result.err(deserializeTransferError(msg, operation));
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const value = JSON.parse(msg.string());
|
|
136
|
+
return Result.ok(value);
|
|
137
|
+
}
|
|
138
|
+
catch (cause) {
|
|
139
|
+
return Result.err(new TransferError({ operation, cause }));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function deserializeTransferError(msg, operation) {
|
|
143
|
+
try {
|
|
144
|
+
const value = JSON.parse(msg.string());
|
|
145
|
+
return new TransferError({
|
|
146
|
+
operation,
|
|
147
|
+
context: value.context,
|
|
148
|
+
cause: typeof value.context?.causeMessage === "string"
|
|
149
|
+
? new Error(value.context.causeMessage)
|
|
150
|
+
: typeof value.context?.reason === "string"
|
|
151
|
+
? new Error(`${value.message ?? "Transfer failed"}: ${JSON.stringify(value.context)}`)
|
|
152
|
+
: value.message
|
|
153
|
+
? new Error(value.message)
|
|
154
|
+
: undefined,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (cause) {
|
|
158
|
+
return new TransferError({ operation, cause });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function recordTransferError(error, direction, phase) {
|
|
162
|
+
recordTrellisError(error, {
|
|
163
|
+
surface: "transfer",
|
|
164
|
+
direction,
|
|
165
|
+
operation: error.operation ?? direction,
|
|
166
|
+
phase,
|
|
167
|
+
messagingSystem: "nats",
|
|
168
|
+
});
|
|
169
|
+
return error;
|
|
170
|
+
}
|
|
171
|
+
async function requestTransfer(nc, subject, payload, headers, reply, timeoutMs) {
|
|
172
|
+
const subscription = nc.subscribe(reply, { max: 1 });
|
|
173
|
+
let timer;
|
|
174
|
+
try {
|
|
175
|
+
nc.publish(subject, payload, { headers, reply });
|
|
176
|
+
return await Promise.race([
|
|
177
|
+
subscription[Symbol.asyncIterator]().next().then((result) => {
|
|
178
|
+
if (result.done)
|
|
179
|
+
throw new Error("Transfer reply subscription closed");
|
|
180
|
+
return result.value;
|
|
181
|
+
}),
|
|
182
|
+
new Promise((_, reject) => {
|
|
183
|
+
timer = setTimeout(() => reject(new Error("Transfer request timed out")), timeoutMs);
|
|
184
|
+
}),
|
|
185
|
+
]);
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
if (timer !== undefined)
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
subscription.unsubscribe();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function receiveStream(grant, requestFrame, cancelTransfer) {
|
|
194
|
+
const hasher = incrementalSha256.create();
|
|
195
|
+
let expectedSeq = 0;
|
|
196
|
+
let receivedBytes = 0;
|
|
197
|
+
return new ReadableStream({
|
|
198
|
+
async pull(controller) {
|
|
199
|
+
try {
|
|
200
|
+
const msg = await requestFrame(expectedSeq);
|
|
201
|
+
if (msg.headers?.get("status") === "error") {
|
|
202
|
+
throw deserializeTransferError(msg, "stream");
|
|
203
|
+
}
|
|
204
|
+
const actualSeq = Number(msg.headers?.get(TRANSFER_SEQUENCE_HEADER));
|
|
205
|
+
if (!Number.isSafeInteger(actualSeq) || actualSeq !== expectedSeq) {
|
|
206
|
+
throw new TransferError({
|
|
207
|
+
operation: "stream",
|
|
208
|
+
context: { reason: "sequence", expectedSeq, actualSeq },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
expectedSeq += 1;
|
|
212
|
+
if (msg.data.length > grant.chunkBytes) {
|
|
213
|
+
throw new TransferError({
|
|
214
|
+
operation: "stream",
|
|
215
|
+
context: {
|
|
216
|
+
reason: "chunk_too_large",
|
|
217
|
+
maxChunkBytes: grant.chunkBytes,
|
|
218
|
+
actualChunkBytes: msg.data.length,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const eof = msg.headers?.get(TRANSFER_EOF_HEADER) === "true";
|
|
223
|
+
if (eof && msg.data.length !== 0) {
|
|
224
|
+
throw new TransferError({
|
|
225
|
+
operation: "stream",
|
|
226
|
+
context: {
|
|
227
|
+
reason: "nonempty_eof",
|
|
228
|
+
actualChunkBytes: msg.data.length,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (!eof && msg.data.length > 0) {
|
|
233
|
+
receivedBytes += msg.data.length;
|
|
234
|
+
if (receivedBytes > grant.info.size) {
|
|
235
|
+
throw new TransferError({
|
|
236
|
+
operation: "stream",
|
|
237
|
+
context: {
|
|
238
|
+
reason: "size_mismatch",
|
|
239
|
+
expectedBytes: grant.info.size,
|
|
240
|
+
actualBytes: receivedBytes,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
hasher.update(msg.data);
|
|
245
|
+
controller.enqueue(msg.data);
|
|
246
|
+
}
|
|
247
|
+
if (eof) {
|
|
248
|
+
if (receivedBytes !== grant.info.size) {
|
|
249
|
+
throw new TransferError({
|
|
250
|
+
operation: "stream",
|
|
251
|
+
context: {
|
|
252
|
+
reason: "size_mismatch",
|
|
253
|
+
expectedBytes: grant.info.size,
|
|
254
|
+
actualBytes: receivedBytes,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const digest = `SHA-256=${base64urlEncode(hasher.digest())}`;
|
|
259
|
+
if (grant.info.digest.replace(/=+$/, "") !== digest.replace(/=+$/, "")) {
|
|
260
|
+
throw new TransferError({
|
|
261
|
+
operation: "stream",
|
|
262
|
+
context: {
|
|
263
|
+
reason: "digest_mismatch",
|
|
264
|
+
expectedDigest: grant.info.digest,
|
|
265
|
+
actualDigest: digest,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
controller.close();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (cause) {
|
|
273
|
+
await cancelTransfer().catch(() => undefined);
|
|
274
|
+
const error = cause instanceof TransferError
|
|
275
|
+
? cause
|
|
276
|
+
: new TransferError({ operation: "stream", cause });
|
|
277
|
+
controller.error(recordTransferError(error, "receive", "stream"));
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
async cancel() {
|
|
281
|
+
await cancelTransfer();
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async function collectStream(stream) {
|
|
286
|
+
const reader = stream.getReader();
|
|
287
|
+
const chunks = [];
|
|
288
|
+
let total = 0;
|
|
289
|
+
try {
|
|
290
|
+
while (true) {
|
|
291
|
+
const next = await reader.read();
|
|
292
|
+
if (next.done) {
|
|
293
|
+
const merged = new Uint8Array(total);
|
|
294
|
+
let offset = 0;
|
|
295
|
+
for (const chunk of chunks) {
|
|
296
|
+
merged.set(chunk, offset);
|
|
297
|
+
offset += chunk.length;
|
|
298
|
+
}
|
|
299
|
+
return Result.ok(merged);
|
|
300
|
+
}
|
|
301
|
+
chunks.push(next.value);
|
|
302
|
+
total += next.value.length;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (cause) {
|
|
306
|
+
return Result.err(cause instanceof TransferError
|
|
307
|
+
? cause
|
|
308
|
+
: new TransferError({ operation: "bytes", cause }));
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
reader.releaseLock();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
class BaseTransferHandle {
|
|
315
|
+
#nc;
|
|
316
|
+
#auth;
|
|
317
|
+
#timeoutMs;
|
|
318
|
+
#inboxPrefix;
|
|
319
|
+
constructor(nc, auth, timeoutMs, inboxPrefix = "_INBOX") {
|
|
320
|
+
this.#nc = nc;
|
|
321
|
+
this.#auth = auth;
|
|
322
|
+
this.#timeoutMs = timeoutMs;
|
|
323
|
+
this.#inboxPrefix = inboxPrefix;
|
|
324
|
+
}
|
|
325
|
+
get nc() {
|
|
326
|
+
return this.#nc;
|
|
327
|
+
}
|
|
328
|
+
get inboxPrefix() {
|
|
329
|
+
return this.#inboxPrefix;
|
|
330
|
+
}
|
|
331
|
+
get auth() {
|
|
332
|
+
return this.#auth;
|
|
333
|
+
}
|
|
334
|
+
get timeoutMs() {
|
|
335
|
+
return this.#timeoutMs;
|
|
336
|
+
}
|
|
337
|
+
validateGrant(grant, operation) {
|
|
338
|
+
if (expired(grant.expiresAt)) {
|
|
339
|
+
return Result.err(new TransferError({
|
|
340
|
+
operation,
|
|
341
|
+
context: { reason: "expired", transferId: grant.transferId },
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
if (grant.sessionKey !== this.#auth.sessionKey) {
|
|
345
|
+
return Result.err(new TransferError({
|
|
346
|
+
operation,
|
|
347
|
+
context: {
|
|
348
|
+
reason: "session_mismatch",
|
|
349
|
+
expectedSessionKey: grant.sessionKey,
|
|
350
|
+
actualSessionKey: this.#auth.sessionKey,
|
|
351
|
+
},
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
return Result.ok(undefined);
|
|
355
|
+
}
|
|
356
|
+
async buildHeaders(subject, reply, payload, seq, control) {
|
|
357
|
+
const headers = natsHeaders();
|
|
358
|
+
const authHeaders = await createTransferProof(this.#auth, subject, reply, transferFrameProofPayload(seq ?? 0, control, payload));
|
|
359
|
+
headers.set("authorization-context", authHeaders.contextDigest);
|
|
360
|
+
headers.set("session-key", this.#auth.sessionKey);
|
|
361
|
+
headers.set("proof", authHeaders.proof);
|
|
362
|
+
headers.set("iat", String(authHeaders.iat));
|
|
363
|
+
headers.set("request-id", authHeaders.requestId);
|
|
364
|
+
if (seq !== undefined) {
|
|
365
|
+
headers.set(TRANSFER_SEQUENCE_HEADER, String(seq));
|
|
366
|
+
}
|
|
367
|
+
if (control !== undefined) {
|
|
368
|
+
headers.set(TRANSFER_CONTROL_HEADER, control);
|
|
369
|
+
}
|
|
370
|
+
injectTraceContext(createNatsHeaderCarrier(headers));
|
|
371
|
+
return headers;
|
|
372
|
+
}
|
|
373
|
+
async cancelTransfer(subject) {
|
|
374
|
+
const payload = new TextEncoder().encode(JSON.stringify({ action: "cancel" }));
|
|
375
|
+
const reply = createInbox(this.inboxPrefix);
|
|
376
|
+
const headers = await this.buildHeaders(subject, reply, payload, 0, "cancel");
|
|
377
|
+
const response = await requestTransfer(this.nc, subject, payload, headers, reply, this.timeoutMs);
|
|
378
|
+
const ack = parseTransferAck(response, "cancel").take();
|
|
379
|
+
if (isErr(ack) || ack.status !== "cancelled") {
|
|
380
|
+
throw isErr(ack) ? ack.error : new TransferError({
|
|
381
|
+
operation: "cancel",
|
|
382
|
+
context: { reason: "not_acknowledged" },
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
export class SendTransferHandle extends BaseTransferHandle {
|
|
388
|
+
#grant;
|
|
389
|
+
constructor(nc, auth, timeoutMs, grant, inboxPrefix = "_INBOX") {
|
|
390
|
+
super(nc, auth, timeoutMs, inboxPrefix);
|
|
391
|
+
this.#grant = grant;
|
|
392
|
+
}
|
|
393
|
+
send(body) {
|
|
394
|
+
return AsyncResult.from((async () => {
|
|
395
|
+
const valid = this.validateGrant(this.#grant, "send").take();
|
|
396
|
+
if (isErr(valid)) {
|
|
397
|
+
return Result.err(recordTransferError(valid.error, "send", "grant"));
|
|
398
|
+
}
|
|
399
|
+
let sentBytes = 0;
|
|
400
|
+
let seq = 0;
|
|
401
|
+
const hasher = incrementalSha256.create();
|
|
402
|
+
const abort = () => this.cancelTransfer(this.#grant.subject).catch(() => { });
|
|
403
|
+
try {
|
|
404
|
+
for await (const chunk of chunkBody(body, this.#grant.chunkBytes)) {
|
|
405
|
+
sentBytes += chunk.length;
|
|
406
|
+
if (this.#grant.maxBytes !== undefined &&
|
|
407
|
+
sentBytes > this.#grant.maxBytes) {
|
|
408
|
+
await abort();
|
|
409
|
+
return Result.err(recordTransferError(new TransferError({
|
|
410
|
+
operation: "send",
|
|
411
|
+
context: {
|
|
412
|
+
reason: "max_bytes_exceeded",
|
|
413
|
+
maxBytes: this.#grant.maxBytes,
|
|
414
|
+
attemptedBytes: sentBytes,
|
|
415
|
+
},
|
|
416
|
+
}), "send", "validation"));
|
|
417
|
+
}
|
|
418
|
+
const reply = createInbox(this.inboxPrefix);
|
|
419
|
+
const headers = await this.buildHeaders(this.#grant.subject, reply, chunk, seq, undefined);
|
|
420
|
+
const response = await AsyncResult.try(() => requestTransfer(this.nc, this.#grant.subject, chunk, headers, reply, this.timeoutMs)).take();
|
|
421
|
+
if (isErr(response)) {
|
|
422
|
+
await abort();
|
|
423
|
+
return Result.err(recordTransferError(new TransferError({
|
|
424
|
+
operation: "send",
|
|
425
|
+
cause: response.error,
|
|
426
|
+
}), "send", "send"));
|
|
427
|
+
}
|
|
428
|
+
const ack = parseTransferAck(response, "send").take();
|
|
429
|
+
if (isErr(ack)) {
|
|
430
|
+
await abort();
|
|
431
|
+
return Result.err(recordTransferError(ack.error, "send", "ack"));
|
|
432
|
+
}
|
|
433
|
+
if (ack.status === "complete") {
|
|
434
|
+
await abort();
|
|
435
|
+
return Result.err(recordTransferError(new TransferError({
|
|
436
|
+
operation: "send",
|
|
437
|
+
context: { reason: "premature_completion" },
|
|
438
|
+
}), "send", "ack"));
|
|
439
|
+
}
|
|
440
|
+
hasher.update(chunk);
|
|
441
|
+
seq += 1;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (cause) {
|
|
445
|
+
await abort();
|
|
446
|
+
return Result.err(recordTransferError(new TransferError({ operation: "send", cause }), "send", "source"));
|
|
447
|
+
}
|
|
448
|
+
const sentDigest = `SHA-256=${base64urlEncode(hasher.digest())}`;
|
|
449
|
+
const completion = new TextEncoder().encode(JSON.stringify({
|
|
450
|
+
action: "complete",
|
|
451
|
+
size: sentBytes,
|
|
452
|
+
digest: sentDigest,
|
|
453
|
+
}));
|
|
454
|
+
const reply = createInbox(this.inboxPrefix);
|
|
455
|
+
const finalHeaders = await this.buildHeaders(this.#grant.subject, reply, completion, seq, "complete");
|
|
456
|
+
const finalResponse = await AsyncResult.try(() => requestTransfer(this.nc, this.#grant.subject, completion, finalHeaders, reply, this.timeoutMs)).take();
|
|
457
|
+
if (isErr(finalResponse)) {
|
|
458
|
+
return Result.err(recordTransferError(new TransferError({
|
|
459
|
+
operation: "send",
|
|
460
|
+
cause: finalResponse.error,
|
|
461
|
+
}), "send", "send"));
|
|
462
|
+
}
|
|
463
|
+
const finalAck = parseTransferAck(finalResponse, "send").take();
|
|
464
|
+
if (isErr(finalAck)) {
|
|
465
|
+
return Result.err(recordTransferError(finalAck.error, "send", "ack"));
|
|
466
|
+
}
|
|
467
|
+
if (finalAck.status !== "complete") {
|
|
468
|
+
return Result.err(recordTransferError(new TransferError({
|
|
469
|
+
operation: "send",
|
|
470
|
+
context: { reason: "missing_completion" },
|
|
471
|
+
}), "send", "ack"));
|
|
472
|
+
}
|
|
473
|
+
if (finalAck.info.size !== sentBytes ||
|
|
474
|
+
finalAck.info.digest?.replace(/=+$/, "") !==
|
|
475
|
+
sentDigest.replace(/=+$/, "")) {
|
|
476
|
+
return Result.err(recordTransferError(new TransferError({
|
|
477
|
+
operation: "send",
|
|
478
|
+
context: {
|
|
479
|
+
reason: "result_metadata_mismatch",
|
|
480
|
+
expectedSize: sentBytes,
|
|
481
|
+
actualSize: finalAck.info.size,
|
|
482
|
+
expectedDigest: sentDigest,
|
|
483
|
+
actualDigest: finalAck.info.digest,
|
|
484
|
+
},
|
|
485
|
+
}), "send", "ack"));
|
|
486
|
+
}
|
|
487
|
+
return Result.ok(finalAck.info);
|
|
488
|
+
})());
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
export class ReceiveTransferHandle extends BaseTransferHandle {
|
|
492
|
+
#grant;
|
|
493
|
+
constructor(nc, auth, timeoutMs, grant, inboxPrefix = "_INBOX") {
|
|
494
|
+
super(nc, auth, timeoutMs, inboxPrefix);
|
|
495
|
+
this.#grant = grant;
|
|
496
|
+
}
|
|
497
|
+
stream() {
|
|
498
|
+
return AsyncResult.from((async () => {
|
|
499
|
+
const valid = this.validateGrant(this.#grant, "stream").take();
|
|
500
|
+
if (isErr(valid)) {
|
|
501
|
+
return Result.err(recordTransferError(valid.error, "receive", "grant"));
|
|
502
|
+
}
|
|
503
|
+
return Result.ok(receiveStream(this.#grant, async (seq) => {
|
|
504
|
+
const payload = new Uint8Array();
|
|
505
|
+
const reply = createInbox(this.inboxPrefix);
|
|
506
|
+
const headers = await this.buildHeaders(this.#grant.subject, reply, payload, seq, undefined);
|
|
507
|
+
return await requestTransfer(this.nc, this.#grant.subject, payload, headers, reply, this.timeoutMs);
|
|
508
|
+
}, () => this.cancelTransfer(this.#grant.subject)));
|
|
509
|
+
})());
|
|
510
|
+
}
|
|
511
|
+
bytes() {
|
|
512
|
+
return AsyncResult.from((async () => {
|
|
513
|
+
const streamResult = await this.stream().take();
|
|
514
|
+
if (isErr(streamResult)) {
|
|
515
|
+
return Result.err(streamResult.error);
|
|
516
|
+
}
|
|
517
|
+
return await collectStream(streamResult);
|
|
518
|
+
})());
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
export function createTransferHandle(nc, auth, timeoutMs, grant, inboxPrefix = "_INBOX") {
|
|
522
|
+
return grant.direction === "send"
|
|
523
|
+
? new SendTransferHandle(nc, auth, timeoutMs, grant, inboxPrefix)
|
|
524
|
+
: new ReceiveTransferHandle(nc, auth, timeoutMs, grant, inboxPrefix);
|
|
525
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function transferFrameProofPayload(seq: number, control: string | undefined, payload: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const TRANSFER_FRAME_PROOF_DOMAIN = new TextEncoder().encode("trellis.transfer.v1.frame\0");
|
|
2
|
+
export function transferFrameProofPayload(seq, control, payload) {
|
|
3
|
+
const controlBytes = new TextEncoder().encode(control ?? "");
|
|
4
|
+
const framed = new Uint8Array(TRANSFER_FRAME_PROOF_DOMAIN.length + 8 + 4 + controlBytes.length +
|
|
5
|
+
payload.length);
|
|
6
|
+
framed.set(TRANSFER_FRAME_PROOF_DOMAIN);
|
|
7
|
+
const view = new DataView(framed.buffer);
|
|
8
|
+
view.setBigUint64(TRANSFER_FRAME_PROOF_DOMAIN.length, BigInt(seq));
|
|
9
|
+
view.setUint32(TRANSFER_FRAME_PROOF_DOMAIN.length + 8, controlBytes.length);
|
|
10
|
+
framed.set(controlBytes, TRANSFER_FRAME_PROOF_DOMAIN.length + 12);
|
|
11
|
+
framed.set(payload, TRANSFER_FRAME_PROOF_DOMAIN.length + 12 + controlBytes.length);
|
|
12
|
+
return framed;
|
|
13
|
+
}
|