4.1.0's HTTP RPC for browsers capped at inline payloads (≤ 256 KiB).
4.2.0 unlocks streams: server queues outbound chunks + control
envelopes per peer, browser long-polls the queue. Browser-to-server
writes ride the existing /v1/transfer/<id>/chunk POST routes
unchanged.
For Dispatch this unlocks mod-jar uploads (50 MB) and world-backup
downloads (100+ MB) — the actual reason browser-side @shade/files
matters.
### New API
@shade/sdk:
- shade.transferQueueRoute(opts?) — Hono app with /queue +
/v1/transfer/* routes. Auto-configures the queue transport.
- shade.configureTransfers extended: transport + envelopeTransport
override slots; resolveBaseUrl optional when both supplied.
@shade/transfer:
- OutboundQueue — per-peer monotonic event log with long-poll
semantics, idle-eviction GC, ring-buffered to maxEventsPerPeer.
- QueueTransferTransport — enqueues instead of POSTing.
@shade/files:
- httpClient({ outboundQueueUrl, transferBaseUrl }) — when set,
starts a long-poll drainer + builds a streams-bridge. fs.read /
fs.write of >256 KiB work end-to-end.
- startQueueDrainer(shade, opts) — exported helper for advanced
consumers driving their own drainer.
### Implementation notes
- ClientStreamsBridge's TransformStream had HWM=0 by default which
stalled the drainer's await chain at chunk 4 (writer.write pended
before the consumer's reader was attached). Bumped to HWM=64 so
the receive loop can buffer ahead of the consumer.
### Tests
3 new integration tests in tests/integration/http-rpc-streams.test.ts:
4 MiB streamed read round-trip, inline-only error path, idle-timeout
long-poll behaviour.
Wire-compatible. Source-compatible. Lockstep bump to 4.2.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
217 lines
6.4 KiB
TypeScript
217 lines
6.4 KiB
TypeScript
// Schemas — Zod runtime + compile-time types
|
|
export * from './schemas/index.js';
|
|
|
|
// Protocol primitives — kinds, correlation, canonical bytes, envelope codec
|
|
export {
|
|
SHADE_FILES_VERSION,
|
|
KIND_PREFIX,
|
|
SUPPORTED_KINDS,
|
|
isSupportedKind,
|
|
} from './protocol/version.js';
|
|
export type { SupportedKind } from './protocol/version.js';
|
|
export {
|
|
KIND_LIST_V1,
|
|
KIND_STAT_V1,
|
|
KIND_MKDIR_V1,
|
|
KIND_DELETE_V1,
|
|
KIND_MOVE_V1,
|
|
KIND_READ_V1,
|
|
KIND_WRITE_V1,
|
|
KIND_GET_THUMBNAIL_V1,
|
|
KIND_CUSTOM_V1,
|
|
KIND_ERROR_V1,
|
|
KIND_CANCEL_V1,
|
|
responseKindOf,
|
|
opOfKind,
|
|
MUTATION_OPS,
|
|
} from './protocol/kinds.js';
|
|
export type { StandardOp } from './protocol/kinds.js';
|
|
export {
|
|
generateRequestId,
|
|
generateIdempotencyKey,
|
|
base64UrlEncode,
|
|
base64UrlDecode,
|
|
} from './protocol/correlate.js';
|
|
export {
|
|
canonicalRpcBytes,
|
|
canonicalJsonStringify,
|
|
hashArgs,
|
|
bytesToHex,
|
|
bytesToBase64,
|
|
base64ToBytes,
|
|
} from './protocol/canonical.js';
|
|
export {
|
|
encodeEnvelope,
|
|
looksLikeFileEnvelope,
|
|
tryParseEnvelope,
|
|
classify,
|
|
} from './protocol/envelope-codec.js';
|
|
export type { ClassifiedEnvelope } from './protocol/envelope-codec.js';
|
|
|
|
// RPC channel — wires Shade.send/onMessage to the file-RPC routing layer
|
|
export { ShadeFileRpcChannel } from './rpc/channel.js';
|
|
export type { RpcChannelHooks } from './rpc/channel.js';
|
|
export { PendingRpcRegistry } from './rpc/pending.js';
|
|
export type { RegisterOptions } from './rpc/pending.js';
|
|
|
|
// Server side
|
|
export { createFileHandler, INTERNAL_SYMBOL } from './server/handler.js';
|
|
export type {
|
|
FileHandler,
|
|
FileHandlerConfig,
|
|
FileHandlerOps,
|
|
} from './server/handler.js';
|
|
export type { OpContext, OpKind } from './server/handler-context.js';
|
|
export { validatePath } from './server/path-policy.js';
|
|
export type { PathPolicy, PathValidationResult } from './server/path-policy.js';
|
|
export { IdempotencyCache } from './server/idempotency-cache.js';
|
|
export type { IdempotencyCacheOptions } from './server/idempotency-cache.js';
|
|
export { RateLimiter } from './server/rate-limiter.js';
|
|
export type { RateLimitConfig } from './server/rate-limiter.js';
|
|
export { createCursorBuilder } from './server/cursor.js';
|
|
export type { CursorBuilder } from './server/cursor.js';
|
|
export {
|
|
createServerStreamsBridge,
|
|
META_KEY_READ_STREAM_ID,
|
|
META_KEY_WRITE_ID,
|
|
} from './server/streams-bridge.js';
|
|
export type {
|
|
ServerStreamsBridge,
|
|
StreamsBridgeShade,
|
|
CreateServerStreamsBridgeOptions,
|
|
ParkedWrite,
|
|
AwaitWriteOptions,
|
|
} from './server/streams-bridge.js';
|
|
export type {
|
|
UserReadResult,
|
|
UserReadResultInline,
|
|
UserReadResultStreams,
|
|
UserWriteArgs,
|
|
UserWriteContent,
|
|
UserWriteContentInline,
|
|
UserWriteContentStreams,
|
|
UserThumbnailResult,
|
|
} from './server/io-types.js';
|
|
export {
|
|
assertThumbnailFormat,
|
|
isThumbnailFormat,
|
|
} from './server/thumbnail.js';
|
|
export type { ThumbnailFormat } from './server/thumbnail.js';
|
|
|
|
// Client side
|
|
export { createFileClient } from './client/client.js';
|
|
export type {
|
|
FileClient,
|
|
BaseOpts,
|
|
CreateFileClientOptions,
|
|
ReadOpts,
|
|
WriteOpts,
|
|
ReadOutput,
|
|
ReadInlineOutput,
|
|
ReadStreamsOutput,
|
|
ThumbnailResult,
|
|
} from './client/client.js';
|
|
export {
|
|
createClientStreamsBridge,
|
|
} from './client/streams-bridge.js';
|
|
export type {
|
|
ClientStreamsBridge,
|
|
CreateClientStreamsBridgeOptions,
|
|
AwaitReadOptions,
|
|
ParkedRead,
|
|
} from './client/streams-bridge.js';
|
|
export {
|
|
decideInline,
|
|
INLINE_THRESHOLD,
|
|
} from './client/inline-threshold.js';
|
|
export type {
|
|
InlineDecision,
|
|
WriteSource,
|
|
} from './client/inline-threshold.js';
|
|
|
|
// Directory ops — walk + bulk transfers
|
|
export { walk } from './client/walk.js';
|
|
export type { WalkOpts, WalkItem } from './client/walk.js';
|
|
export { uploadDirectory } from './client/upload-directory.js';
|
|
export type { UploadDirectoryOptions } from './client/upload-directory.js';
|
|
export { downloadDirectory } from './client/download-directory.js';
|
|
export type { DownloadDirectoryOptions } from './client/download-directory.js';
|
|
export { runWithConcurrency } from './client/concurrency.js';
|
|
export type { ConcurrentMapOptions } from './client/concurrency.js';
|
|
export {
|
|
DEFAULT_BULK_CONCURRENCY,
|
|
MAX_BULK_CONCURRENCY,
|
|
} from './client/directory-types.js';
|
|
export type {
|
|
BulkOpts,
|
|
BulkTransferEvent,
|
|
BulkTransferHandle,
|
|
BulkTransferResult,
|
|
DirectoryHandleLike,
|
|
FileHandleLike,
|
|
FileLike,
|
|
WritableStreamLike,
|
|
} from './client/directory-types.js';
|
|
export { createMemoryDirectory } from './client/memory-directory.js';
|
|
|
|
// Custom-op registry typing (declaration-merged by consumers)
|
|
export type {
|
|
CustomOpDef,
|
|
CustomOpsMap,
|
|
CustomOpRegistration,
|
|
CustomOpRegistrations,
|
|
} from './server/custom-ops.js';
|
|
|
|
// Production hooks: metrics
|
|
export {
|
|
METRIC_BYTES_IN,
|
|
METRIC_BYTES_OUT,
|
|
METRIC_FINGERPRINT_REJECT_TOTAL,
|
|
METRIC_IDEMPOTENCY_CONFLICT_TOTAL,
|
|
METRIC_IDEMPOTENCY_HIT_TOTAL,
|
|
METRIC_OP_DURATION_MS,
|
|
METRIC_OP_TOTAL,
|
|
METRIC_RATE_LIMIT_REJECT_TOTAL,
|
|
METRIC_SIGNATURE_REJECT_TOTAL,
|
|
NOOP_METRIC_SINK,
|
|
} from './server/metrics.js';
|
|
export type { MetricSink, MetricTags } from './server/metrics.js';
|
|
|
|
export { MAX_SIGNATURE_AGE_MS } from './server/handler.js';
|
|
|
|
// High-level SDK integration entrypoint
|
|
export { createFilesNamespace } from './integration/files-namespace.js';
|
|
export type { FilesNamespace } from './integration/files-namespace.js';
|
|
|
|
// Request-response HTTP transport — for browser-style consumers
|
|
// (one HTTP POST per RPC, no inbound channel needed). See
|
|
// `docs/files.md § HTTP RPC`.
|
|
export { createFilesRpcRoute } from './server/rpc-route.js';
|
|
export type { FilesRpcRouteOptions } from './server/rpc-route.js';
|
|
export { createFilesHttpClient } from './client/http-client.js';
|
|
export type { FilesHttpClientOptions } from './client/http-client.js';
|
|
export { startQueueDrainer } from './client/queue-drainer.js';
|
|
export type {
|
|
QueueDrainerHandle,
|
|
QueueDrainerOptions,
|
|
} from './client/queue-drainer.js';
|
|
|
|
// Shared structural surface @shade/files needs from a Shade instance —
|
|
// exposed so consumers building custom Shade-shaped bridges can verify
|
|
// they implement every required member.
|
|
export type { ShadeBridge } from './integration/shade-bridge.js';
|
|
|
|
// Integration helpers — wire handler + pending registry onto a channel
|
|
export { attachFileHandler } from './integration/wire-server.js';
|
|
export { attachClientRouting } from './integration/wire-client.js';
|
|
|
|
// Path utilities
|
|
export {
|
|
posixNormalize,
|
|
posixJoin,
|
|
posixDirname,
|
|
posixBasename,
|
|
decodePercentEscapes,
|
|
isPathInside,
|
|
} from './utils/path.js';
|