@ndn/endpoint
Advanced tools
Comparing version 0.0.20240113 to 0.0.20240630
@@ -1,6 +0,14 @@ | ||
import { CancelInterest, FwPacket } from "@ndn/fw"; | ||
import { CancelInterest, Forwarder, FwPacket } from "@ndn/fw"; | ||
import { Data, Interest } from "@ndn/packet"; | ||
import { pushable } from "it-pushable"; | ||
import { pushable } from "@ndn/util"; | ||
import { exactOptions } from "./common_browser.js"; | ||
import { makeRetxGenerator } from "./retx_browser.js"; | ||
export function makeConsumer(fw, interest, { describe = `consume(${interest.name})`, signal, modifyInterest, retx, verifier, }) { | ||
export var ConsumerOptions; | ||
(function (ConsumerOptions) { | ||
function exact(opts = {}) { | ||
return exactOptions(opts, ["modifyInterest", "retx", "verifier"]); | ||
} | ||
ConsumerOptions.exact = exact; | ||
})(ConsumerOptions || (ConsumerOptions = {})); | ||
function makeConsumer(interest, { fw = Forwarder.getDefault(), describe = `consume(${interest.name})`, signal, modifyInterest, retx, verifier, }) { | ||
Interest.makeModifyFunc(modifyInterest)(interest); | ||
@@ -10,3 +18,3 @@ let nRetx = -1; | ||
const promise = new Promise((resolve, reject) => { | ||
const rx = pushable({ objectMode: true }); | ||
const rx = pushable(); | ||
let timer; | ||
@@ -53,3 +61,3 @@ const cancelRetx = () => { | ||
signal?.removeEventListener("abort", onAbort); | ||
rx.end(); | ||
rx.stop(); | ||
}, | ||
@@ -67,1 +75,8 @@ }, { | ||
} | ||
/** | ||
* Retrieve a single piece of Data. | ||
* @param interest - Interest or Interest name. | ||
*/ | ||
export function consume(interest, opts = {}) { | ||
return makeConsumer(interest instanceof Interest ? interest : new Interest(interest), opts); | ||
} |
@@ -1,6 +0,14 @@ | ||
import { CancelInterest, FwPacket } from "@ndn/fw"; | ||
import { CancelInterest, Forwarder, FwPacket } from "@ndn/fw"; | ||
import { Data, Interest } from "@ndn/packet"; | ||
import { pushable } from "it-pushable"; | ||
import { pushable } from "@ndn/util"; | ||
import { exactOptions } from "./common_node.js"; | ||
import { makeRetxGenerator } from "./retx_node.js"; | ||
export function makeConsumer(fw, interest, { describe = `consume(${interest.name})`, signal, modifyInterest, retx, verifier, }) { | ||
export var ConsumerOptions; | ||
(function (ConsumerOptions) { | ||
function exact(opts = {}) { | ||
return exactOptions(opts, ["modifyInterest", "retx", "verifier"]); | ||
} | ||
ConsumerOptions.exact = exact; | ||
})(ConsumerOptions || (ConsumerOptions = {})); | ||
function makeConsumer(interest, { fw = Forwarder.getDefault(), describe = `consume(${interest.name})`, signal, modifyInterest, retx, verifier, }) { | ||
Interest.makeModifyFunc(modifyInterest)(interest); | ||
@@ -10,3 +18,3 @@ let nRetx = -1; | ||
const promise = new Promise((resolve, reject) => { | ||
const rx = pushable({ objectMode: true }); | ||
const rx = pushable(); | ||
let timer; | ||
@@ -53,3 +61,3 @@ const cancelRetx = () => { | ||
signal?.removeEventListener("abort", onAbort); | ||
rx.end(); | ||
rx.stop(); | ||
}, | ||
@@ -67,1 +75,8 @@ }, { | ||
} | ||
/** | ||
* Retrieve a single piece of Data. | ||
* @param interest - Interest or Interest name. | ||
*/ | ||
export function consume(interest, opts = {}) { | ||
return makeConsumer(interest instanceof Interest ? interest : new Interest(interest), opts); | ||
} |
@@ -1,12 +0,10 @@ | ||
import { type Forwarder } from "@ndn/fw"; | ||
import { Data, Interest, type Verifier } from "@ndn/packet"; | ||
import { Data, Interest, type NameLike, type Verifier } from "@ndn/packet"; | ||
import { type CommonOptions } from "./common.js"; | ||
import { type RetxPolicy } from "./retx.js"; | ||
export interface ConsumerOptions { | ||
/** Description for debugging purpose. */ | ||
describe?: string; | ||
/** AbortSignal that allows canceling the Interest via AbortController. */ | ||
signal?: AbortSignal; | ||
/** {@link consume} options. */ | ||
export interface ConsumerOptions extends CommonOptions { | ||
/** | ||
* Modify Interest according to specified options. | ||
* Default is no modification. | ||
* @defaultValue | ||
* `undefined`, no modification. | ||
*/ | ||
@@ -16,3 +14,4 @@ modifyInterest?: Interest.Modify; | ||
* Retransmission policy. | ||
* Default is disabling retransmission. | ||
* @defaultValue | ||
* `undefined`, no retransmission. | ||
*/ | ||
@@ -22,9 +21,14 @@ retx?: RetxPolicy; | ||
* Data verifier. | ||
* Default is no verification. | ||
* @defaultValue | ||
* `undefined`, no verification. | ||
*/ | ||
verifier?: Verifier; | ||
} | ||
export declare namespace ConsumerOptions { | ||
function exact(opts?: ConsumerOptions): ConsumerOptions; | ||
} | ||
/** | ||
* Progress of Data retrieval. | ||
* | ||
* @remarks | ||
* This is a Promise that resolves with the retrieved Data and rejects upon timeout, | ||
@@ -37,2 +41,6 @@ * annotated with the Interest and some counters. | ||
} | ||
export declare function makeConsumer(fw: Forwarder, interest: Interest, { describe, signal, modifyInterest, retx, verifier, }: ConsumerOptions): ConsumerContext; | ||
/** | ||
* Retrieve a single piece of Data. | ||
* @param interest - Interest or Interest name. | ||
*/ | ||
export declare function consume(interest: Interest | NameLike, opts?: ConsumerOptions): ConsumerContext; |
@@ -0,14 +1,23 @@ | ||
import { Signer } from "@ndn/packet"; | ||
import { assert } from "@ndn/util"; | ||
import { signUnsignedData } from "./producer_browser.js"; | ||
// We declare an interface here instead of importing DataStore, in order to reduce bundle size for | ||
// webapps that do not use DataBuffer. The trade-off is that, applications wanting to use | ||
// DataBuffer would have to import memdown and @ndn/repo themselves. | ||
/** | ||
* DataBuffer implementation based on DataStore from @ndn/repo package. | ||
* | ||
* @example | ||
* new DataStoreBuffer(new DataStore(memdown())) | ||
*/ | ||
/** DataBuffer implementation based on `@ndn/repo`. */ | ||
export class DataStoreBuffer { | ||
store; | ||
/* eslint-disable tsdoc/syntax -- tsdoc-missing-reference */ | ||
/** | ||
* Constructor. | ||
* @param store - {@link \@ndn/repo!DataStore} instance. | ||
* | ||
* @example | ||
* ```ts | ||
* new DataStoreBuffer(await makeInMemoryDataStore()) | ||
* ``` | ||
* | ||
* @remarks | ||
* `DataStore` is declared as an interface instead of importing, in order to reduce bundle size | ||
* for webapps that do not use DataBuffer. The trade-off is that, applications wanting to use | ||
* DataBuffer would have to import `@ndn/repo` themselves. | ||
* Note: {@link \@ndn/repo-api!DataArray} is insufficient because it lacks `expireTime` option. | ||
*/ | ||
/* eslint-enable tsdoc/syntax */ | ||
constructor(store, { ttl = 60000, dataSigner, } = {}) { | ||
@@ -18,3 +27,3 @@ this.store = store; | ||
this.ttl = ttl; | ||
this.dataSigner = dataSigner; | ||
this.dataSigner = dataSigner && Signer.onlyIfUnsigned(dataSigner); | ||
} | ||
@@ -29,3 +38,3 @@ ttl; | ||
if (this.dataSigner) { | ||
await Promise.all(pkts.map((data) => signUnsignedData(data, this.dataSigner))); | ||
await Promise.all(pkts.map((data) => this.dataSigner.sign(data))); | ||
} | ||
@@ -32,0 +41,0 @@ return this.store.insert({ expireTime }, ...pkts); |
@@ -0,14 +1,23 @@ | ||
import { Signer } from "@ndn/packet"; | ||
import { assert } from "@ndn/util"; | ||
import { signUnsignedData } from "./producer_node.js"; | ||
// We declare an interface here instead of importing DataStore, in order to reduce bundle size for | ||
// webapps that do not use DataBuffer. The trade-off is that, applications wanting to use | ||
// DataBuffer would have to import memdown and @ndn/repo themselves. | ||
/** | ||
* DataBuffer implementation based on DataStore from @ndn/repo package. | ||
* | ||
* @example | ||
* new DataStoreBuffer(new DataStore(memdown())) | ||
*/ | ||
/** DataBuffer implementation based on `@ndn/repo`. */ | ||
export class DataStoreBuffer { | ||
store; | ||
/* eslint-disable tsdoc/syntax -- tsdoc-missing-reference */ | ||
/** | ||
* Constructor. | ||
* @param store - {@link \@ndn/repo!DataStore} instance. | ||
* | ||
* @example | ||
* ```ts | ||
* new DataStoreBuffer(await makeInMemoryDataStore()) | ||
* ``` | ||
* | ||
* @remarks | ||
* `DataStore` is declared as an interface instead of importing, in order to reduce bundle size | ||
* for webapps that do not use DataBuffer. The trade-off is that, applications wanting to use | ||
* DataBuffer would have to import `@ndn/repo` themselves. | ||
* Note: {@link \@ndn/repo-api!DataArray} is insufficient because it lacks `expireTime` option. | ||
*/ | ||
/* eslint-enable tsdoc/syntax */ | ||
constructor(store, { ttl = 60000, dataSigner, } = {}) { | ||
@@ -18,3 +27,3 @@ this.store = store; | ||
this.ttl = ttl; | ||
this.dataSigner = dataSigner; | ||
this.dataSigner = dataSigner && Signer.onlyIfUnsigned(dataSigner); | ||
} | ||
@@ -29,3 +38,3 @@ ttl; | ||
if (this.dataSigner) { | ||
await Promise.all(pkts.map((data) => signUnsignedData(data, this.dataSigner))); | ||
await Promise.all(pkts.map((data) => this.dataSigner.sign(data))); | ||
} | ||
@@ -32,0 +41,0 @@ return this.store.insert({ expireTime }, ...pkts); |
@@ -1,8 +0,7 @@ | ||
import type { Data, Interest, Signer } from "@ndn/packet"; | ||
import { type Data, type Interest, Signer } from "@ndn/packet"; | ||
/** Outgoing Data buffer for producer. */ | ||
export interface DataBuffer { | ||
find: (interest: Interest) => Promise<Data | undefined>; | ||
insert: (...pkts: Data[]) => Promise<void>; | ||
insert: (...pkts: readonly Data[]) => Promise<void>; | ||
} | ||
/** Prototype of DataStore from @ndn/repo package. */ | ||
interface DataStore { | ||
@@ -12,12 +11,22 @@ find: (interest: Interest) => Promise<Data | undefined>; | ||
expireTime?: number; | ||
}, ...pkts: Data[]) => Promise<void>; | ||
}, ...pkts: readonly Data[]) => Promise<void>; | ||
} | ||
/** | ||
* DataBuffer implementation based on DataStore from @ndn/repo package. | ||
* | ||
* @example | ||
* new DataStoreBuffer(new DataStore(memdown())) | ||
*/ | ||
/** DataBuffer implementation based on `@ndn/repo`. */ | ||
export declare class DataStoreBuffer implements DataBuffer { | ||
readonly store: DataStore; | ||
/** | ||
* Constructor. | ||
* @param store - {@link \@ndn/repo!DataStore} instance. | ||
* | ||
* @example | ||
* ```ts | ||
* new DataStoreBuffer(await makeInMemoryDataStore()) | ||
* ``` | ||
* | ||
* @remarks | ||
* `DataStore` is declared as an interface instead of importing, in order to reduce bundle size | ||
* for webapps that do not use DataBuffer. The trade-off is that, applications wanting to use | ||
* DataBuffer would have to import `@ndn/repo` themselves. | ||
* Note: {@link \@ndn/repo-api!DataArray} is insufficient because it lacks `expireTime` option. | ||
*/ | ||
constructor(store: DataStore, { ttl, dataSigner, }?: DataStoreBuffer.Options); | ||
@@ -30,6 +39,14 @@ private readonly ttl; | ||
export declare namespace DataStoreBuffer { | ||
/** {@link DataStoreBuffer} constructor options. */ | ||
interface Options { | ||
/** Data expiration time. Default is 60000ms. 0 means infinity. */ | ||
/** | ||
* Data expiration time in milliseconds. | ||
* 0 means infinity. | ||
* @defaultValue 60000 | ||
*/ | ||
ttl?: number; | ||
/** If specified, automatically sign Data packets unless already signed. */ | ||
/** | ||
* If specified, automatically sign Data packets unless already signed. | ||
* @see {@link ProducerOptions.dataSigner} | ||
*/ | ||
dataSigner?: Signer; | ||
@@ -36,0 +53,0 @@ } |
@@ -0,2 +1,3 @@ | ||
export { ConsumerOptions, consume } from "./consumer_browser.js"; | ||
export { DataStoreBuffer } from "./data-buffer_browser.js"; | ||
export * from "./endpoint_browser.js"; | ||
export { ProducerOptions, produce } from "./producer_browser.js"; |
@@ -0,2 +1,3 @@ | ||
export { ConsumerOptions, consume } from "./consumer_node.js"; | ||
export { DataStoreBuffer } from "./data-buffer_node.js"; | ||
export * from "./endpoint_node.js"; | ||
export { ProducerOptions, produce } from "./producer_node.js"; |
@@ -1,5 +0,4 @@ | ||
export type { RetxOptions, RetxPolicy } from "./retx.js"; | ||
export type { ConsumerContext, ConsumerOptions } from "./consumer.js"; | ||
export { type ConsumerContext, ConsumerOptions, consume } from "./consumer.js"; | ||
export { type DataBuffer, DataStoreBuffer } from "./data-buffer.js"; | ||
export type { ProducerHandler, ProducerOptions, Producer } from "./producer.js"; | ||
export * from "./endpoint.js"; | ||
export { type ProducerHandler, ProducerOptions, type Producer, produce } from "./producer.js"; | ||
export type { RetxOptions, RetxGenerator, RetxPolicy } from "./retx.js"; |
@@ -1,12 +0,20 @@ | ||
import { FwPacket } from "@ndn/fw"; | ||
import { Data, Interest, SigType } from "@ndn/packet"; | ||
import { Forwarder, FwPacket } from "@ndn/fw"; | ||
import { Data, Interest, Name, Signer } from "@ndn/packet"; | ||
import { flatTransform } from "streaming-iterables"; | ||
export class ProducerImpl { | ||
import { exactOptions } from "./common_browser.js"; | ||
export var ProducerOptions; | ||
(function (ProducerOptions) { | ||
function exact(opts = {}) { | ||
return exactOptions(opts, ["routeCapture", "announcement", "concurrency", "dataSigner", "dataBuffer", "autoBuffer"]); | ||
} | ||
ProducerOptions.exact = exact; | ||
})(ProducerOptions || (ProducerOptions = {})); | ||
class ProducerImpl { | ||
prefix; | ||
handler; | ||
constructor(fw, prefix, handler, { describe = `produce(${prefix})`, signal, routeCapture = true, announcement, concurrency = 1, dataSigner, dataBuffer, autoBuffer = true, }) { | ||
constructor(prefix, handler, { fw = Forwarder.getDefault(), describe = `produce(${prefix})`, signal, routeCapture = true, announcement, concurrency = 1, dataSigner, dataBuffer, autoBuffer = true, }) { | ||
this.prefix = prefix; | ||
this.handler = handler; | ||
this.signal = signal; | ||
this.dataSigner = dataSigner; | ||
this.dataSigner = dataSigner && Signer.onlyIfUnsigned(dataSigner); | ||
this.dataBuffer = dataBuffer; | ||
@@ -25,3 +33,3 @@ this.face = fw.addFace({ | ||
this.processBuffered.bind(this, autoBuffer) : | ||
this.processUnbuffered.bind(this); | ||
this.processUnbuffered; | ||
signal?.addEventListener("abort", this.close); | ||
@@ -43,3 +51,3 @@ } | ||
processInterest; | ||
async processUnbuffered(interest) { | ||
processUnbuffered = async (interest) => { | ||
const data = await this.handler(interest, this); | ||
@@ -49,3 +57,3 @@ if (!(data instanceof Data)) { | ||
} | ||
await signUnsignedData(data, this.dataSigner); | ||
await this.dataSigner?.sign(data); | ||
if (!await data.canSatisfy(interest)) { // isCacheLookup=false because the buffer is not considered a cache | ||
@@ -55,3 +63,3 @@ return undefined; | ||
return data; | ||
} | ||
}; | ||
async processBuffered(autoBuffer, interest) { | ||
@@ -75,7 +83,13 @@ let found = await this.dataBuffer.find(interest); | ||
}; | ||
} | ||
export async function signUnsignedData(data, dataSigner) { | ||
if (dataSigner && data.sigInfo.type === SigType.Null) { | ||
await dataSigner.sign(data); | ||
[Symbol.dispose]() { | ||
this.close(); | ||
} | ||
} | ||
/** | ||
* Start a producer. | ||
* @param prefix - Prefix registration; if `undefined`, prefixes may be added later. | ||
* @param handler - Function to handle incoming Interest. | ||
*/ | ||
export function produce(prefix, handler, opts = {}) { | ||
return new ProducerImpl(prefix === undefined ? undefined : Name.from(prefix), handler, opts); | ||
} |
@@ -1,12 +0,20 @@ | ||
import { FwPacket } from "@ndn/fw"; | ||
import { Data, Interest, SigType } from "@ndn/packet"; | ||
import { Forwarder, FwPacket } from "@ndn/fw"; | ||
import { Data, Interest, Name, Signer } from "@ndn/packet"; | ||
import { flatTransform } from "streaming-iterables"; | ||
export class ProducerImpl { | ||
import { exactOptions } from "./common_node.js"; | ||
export var ProducerOptions; | ||
(function (ProducerOptions) { | ||
function exact(opts = {}) { | ||
return exactOptions(opts, ["routeCapture", "announcement", "concurrency", "dataSigner", "dataBuffer", "autoBuffer"]); | ||
} | ||
ProducerOptions.exact = exact; | ||
})(ProducerOptions || (ProducerOptions = {})); | ||
class ProducerImpl { | ||
prefix; | ||
handler; | ||
constructor(fw, prefix, handler, { describe = `produce(${prefix})`, signal, routeCapture = true, announcement, concurrency = 1, dataSigner, dataBuffer, autoBuffer = true, }) { | ||
constructor(prefix, handler, { fw = Forwarder.getDefault(), describe = `produce(${prefix})`, signal, routeCapture = true, announcement, concurrency = 1, dataSigner, dataBuffer, autoBuffer = true, }) { | ||
this.prefix = prefix; | ||
this.handler = handler; | ||
this.signal = signal; | ||
this.dataSigner = dataSigner; | ||
this.dataSigner = dataSigner && Signer.onlyIfUnsigned(dataSigner); | ||
this.dataBuffer = dataBuffer; | ||
@@ -25,3 +33,3 @@ this.face = fw.addFace({ | ||
this.processBuffered.bind(this, autoBuffer) : | ||
this.processUnbuffered.bind(this); | ||
this.processUnbuffered; | ||
signal?.addEventListener("abort", this.close); | ||
@@ -43,3 +51,3 @@ } | ||
processInterest; | ||
async processUnbuffered(interest) { | ||
processUnbuffered = async (interest) => { | ||
const data = await this.handler(interest, this); | ||
@@ -49,3 +57,3 @@ if (!(data instanceof Data)) { | ||
} | ||
await signUnsignedData(data, this.dataSigner); | ||
await this.dataSigner?.sign(data); | ||
if (!await data.canSatisfy(interest)) { // isCacheLookup=false because the buffer is not considered a cache | ||
@@ -55,3 +63,3 @@ return undefined; | ||
return data; | ||
} | ||
}; | ||
async processBuffered(autoBuffer, interest) { | ||
@@ -75,7 +83,13 @@ let found = await this.dataBuffer.find(interest); | ||
}; | ||
} | ||
export async function signUnsignedData(data, dataSigner) { | ||
if (dataSigner && data.sigInfo.type === SigType.Null) { | ||
await dataSigner.sign(data); | ||
[Symbol.dispose]() { | ||
this.close(); | ||
} | ||
} | ||
/** | ||
* Start a producer. | ||
* @param prefix - Prefix registration; if `undefined`, prefixes may be added later. | ||
* @param handler - Function to handle incoming Interest. | ||
*/ | ||
export function produce(prefix, handler, opts = {}) { | ||
return new ProducerImpl(prefix === undefined ? undefined : Name.from(prefix), handler, opts); | ||
} |
@@ -1,27 +0,31 @@ | ||
import { type Forwarder, type FwFace } from "@ndn/fw"; | ||
import { Data, Interest, type Name, type Signer } from "@ndn/packet"; | ||
import { type FwFace } from "@ndn/fw"; | ||
import { Data, Interest, Name, type NameLike, Signer } from "@ndn/packet"; | ||
import { type CommonOptions } from "./common.js"; | ||
import type { DataBuffer } from "./data-buffer.js"; | ||
/** | ||
* Producer handler function. | ||
* @param interest - Incoming Interest. | ||
* @param producer - Producer context. | ||
* | ||
* The handler can return a Data to respond to the Interest, or return `undefined` to cause a timeout. | ||
* | ||
* If Options.dataBuffer is provided, the handler can access the DataBuffer via producer.dataBuffer . | ||
* The handler can return a Data to respond to the Interest, which is also inserted to the DataBuffer | ||
* unless Options.autoBuffer is set to false. If the handler returns `undefined`, the Interest is used | ||
* to query the DataBuffer, and any matching Data may be sent. | ||
* @remarks | ||
* The handler may be invoked concurrently up to {@link ProducerOptions.concurrency} instances. | ||
* The handler should return a Promise that resolves to: | ||
* - Data satisfying the Interest: send Data to consumer(s). | ||
* - If Data is not signed, it is signed with {@link ProducerOptions.dataSigner}. | ||
* - Data that does not satisfy the Interest or `undefined`: | ||
* - {@link ProducerOptions.dataBuffer} is unset: cause a timeout. | ||
* - {@link ProducerOptions.dataBuffer} is provided: query the DataBuffer. | ||
*/ | ||
export type ProducerHandler = (interest: Interest, producer: Producer) => Promise<Data | undefined>; | ||
export interface ProducerOptions { | ||
/** Description for debugging purpose. */ | ||
describe?: string; | ||
/** AbortSignal that allows closing the producer via AbortController. */ | ||
signal?: AbortSignal; | ||
/** {@link produce} options. */ | ||
export interface ProducerOptions extends CommonOptions { | ||
/** | ||
* Whether routes registered by producer would cause @ndn/fw internal FIB to stop matching toward | ||
* shorter prefixes. Default is true. | ||
* Whether routes registered by producer would cause `@ndn/fw` internal FIB to stop matching | ||
* toward shorter prefixes. | ||
* @defaultValue `true` | ||
* | ||
* @remarks | ||
* If all nexthops of a FIB entry are set to non-capture, FIB lookup may continue onto nexthops | ||
* on FIB entries with shorter prefixes. One use case is in @ndn/sync package, where both local | ||
* and remote sync participants want to receive each other's Interests. | ||
* on FIB entries with shorter prefixes. One use case is in dataset synchronization protocols, | ||
* where both local and remote sync participants want to receive each other's Interests. | ||
*/ | ||
@@ -31,8 +35,8 @@ routeCapture?: boolean; | ||
* What name to be readvertised. | ||
* Ignored if prefix is undefined. | ||
* Ignored if prefix is `undefined`. | ||
*/ | ||
announcement?: FwFace.RouteAnnouncement; | ||
announcement?: ProducerOptions.RouteAnnouncement; | ||
/** | ||
* How many Interests to process in parallel. | ||
* @default 1 | ||
* @defaultValue 1 | ||
*/ | ||
@@ -42,18 +46,53 @@ concurrency?: number; | ||
* If specified, automatically sign Data packets that are not yet signed. | ||
* This does not apply to Data packets manually inserted to the dataBuffer. | ||
* | ||
* @remarks | ||
* If the {@link ProducerHandler} returns a Data packet that is not signed (its SigType is | ||
* *Null*), it is automatically signed with this signer. | ||
* | ||
* This option does not apply to Data packets manually inserted into `.dataBuffer`. To auto-sign | ||
* those packet, specify {@link DataStoreBuffer.Options.dataSigner} in addition. | ||
*/ | ||
dataSigner?: Signer; | ||
/** Outgoing Data buffer. */ | ||
/** | ||
* Outgoing Data buffer. | ||
* | ||
* @remarks | ||
* Providing an outgoing Data buffer allows the {@link ProducerHandler} to prepare multiple Data | ||
* packets in response to one Interest, in which one Data satisfies the current Interest and | ||
* additional Data satisfy upcoming Interests. This is useful for a producer that generates a | ||
* multi-segment response triggered by a single Interest, such as a | ||
* {@link https://redmine.named-data.net/projects/nfd/wiki/StatusDataset | StatusDataset} | ||
* producer in NFD Management protocol. | ||
* | ||
* The producer handler can prepare the Data packets and insert them to the DataBuffer. Either it | ||
* can return `undefined`, so that the DataBuffer is queried with the current Interest and the | ||
* first matching Data is sent. Or it can return a specific Data packet for satisfying the | ||
* current Interest. | ||
*/ | ||
dataBuffer?: DataBuffer; | ||
/** | ||
* Whether to add handler return value to buffer. | ||
* Ignored when dataBuffer is not specified. | ||
* @default true | ||
* Whether to add handler return value to `.dataBuffer`. | ||
* @defaultValue `true` | ||
* | ||
* @remarks | ||
* This is only relevant when `.dataBuffer` is set. If `true`, when the {@link ProducerHandler} | ||
* returns a Data packet, it is automatically inserted to the DataBuffer. | ||
*/ | ||
autoBuffer?: boolean; | ||
} | ||
export declare namespace ProducerOptions { | ||
/** Describe how to derive route announcement from name prefix in {@link produce}. */ | ||
type RouteAnnouncement = FwFace.RouteAnnouncement; | ||
function exact(opts?: ProducerOptions): ProducerOptions; | ||
} | ||
/** A running producer. */ | ||
export interface Producer { | ||
export interface Producer extends Disposable { | ||
/** | ||
* Prefix specified in {@link produce} call. | ||
* Additional prefixes can be added via `.face.addRoute()`. | ||
*/ | ||
readonly prefix: Name | undefined; | ||
/** Logical forwarder face for this producer. */ | ||
readonly face: FwFace; | ||
/** Outgoing Data buffer. */ | ||
readonly dataBuffer?: DataBuffer; | ||
@@ -63,2 +102,3 @@ /** | ||
* | ||
* @remarks | ||
* Use case of this function: | ||
@@ -73,16 +113,7 @@ * 1. Producer A dynamically creates producer B upon receiving an Interest. | ||
} | ||
export declare class ProducerImpl implements Producer { | ||
readonly prefix: Name | undefined; | ||
private readonly handler; | ||
constructor(fw: Forwarder, prefix: Name | undefined, handler: ProducerHandler, { describe, signal, routeCapture, announcement, concurrency, dataSigner, dataBuffer, autoBuffer, }: ProducerOptions); | ||
readonly face: FwFace; | ||
private readonly signal?; | ||
private readonly dataSigner?; | ||
readonly dataBuffer?: DataBuffer; | ||
private faceDuplex; | ||
readonly processInterest: (interest: Interest) => Promise<Data | undefined>; | ||
private processUnbuffered; | ||
private processBuffered; | ||
close: () => void; | ||
} | ||
export declare function signUnsignedData(data: Data, dataSigner: Signer | undefined): Promise<void>; | ||
/** | ||
* Start a producer. | ||
* @param prefix - Prefix registration; if `undefined`, prefixes may be added later. | ||
* @param handler - Function to handle incoming Interest. | ||
*/ | ||
export declare function produce(prefix: NameLike | undefined, handler: ProducerHandler, opts?: ProducerOptions): Producer; |
import { randomJitter } from "@ndn/util"; | ||
/** Construct RetxGenerator from RetxPolicy. */ | ||
export function makeRetxGenerator(policy) { | ||
if (!policy) { | ||
if (!policy) { // applies to both `undefined` and zero | ||
return () => []; | ||
} | ||
if (typeof policy === "number") { | ||
return makeRetxGenerator({ limit: policy }); | ||
} | ||
if (typeof policy === "function") { | ||
return policy; | ||
} | ||
if (typeof policy === "number") { | ||
policy = { limit: policy }; | ||
} | ||
return function* (interestLifetime) { | ||
@@ -14,0 +14,0 @@ const { limit = 0, interval = interestLifetime * 0.5, randomize = 0.1, backoff = 1, max = interestLifetime * 0.9, } = policy; |
import { randomJitter } from "@ndn/util"; | ||
/** Construct RetxGenerator from RetxPolicy. */ | ||
export function makeRetxGenerator(policy) { | ||
if (!policy) { | ||
if (!policy) { // applies to both `undefined` and zero | ||
return () => []; | ||
} | ||
if (typeof policy === "number") { | ||
return makeRetxGenerator({ limit: policy }); | ||
} | ||
if (typeof policy === "function") { | ||
return policy; | ||
} | ||
if (typeof policy === "number") { | ||
policy = { limit: policy }; | ||
} | ||
return function* (interestLifetime) { | ||
@@ -14,0 +14,0 @@ const { limit = 0, interval = interestLifetime * 0.5, randomize = 0.1, backoff = 1, max = interestLifetime * 0.9, } = policy; |
@@ -5,10 +5,10 @@ /** Interest retransmission policy options. */ | ||
* Maximum number of retransmissions, excluding initial Interest. | ||
* | ||
* Default is 0, which disables retransmissions. | ||
* @defaultValue | ||
* `0`, which disables retransmissions | ||
*/ | ||
limit?: number; | ||
/** | ||
* Initial retx interval | ||
* | ||
* Default is 50% of InterestLifetime. | ||
* Initial retx interval. | ||
* @defaultValue | ||
* 50% of InterestLifetime | ||
*/ | ||
@@ -18,5 +18,6 @@ interval?: number; | ||
* Randomize retx interval within [1-randomize, 1+randomize]. | ||
* @defaultValue `0.1` | ||
* | ||
* Suppose this is set to 0.1, an interval of 100ms would become [90ms, 110ms]. | ||
* Default is 0.1. | ||
* @remarks | ||
* Suppose this is set to `0.1`, an interval of 100ms would become `[90ms,110ms]`. | ||
*/ | ||
@@ -26,5 +27,6 @@ randomize?: number; | ||
* Multiply retx interval by backoff factor after each retx. | ||
* @defaultValue `1.0` | ||
* | ||
* This number should be in range [1.0, 2.0]. | ||
* Default is 1.0. | ||
* @remarks | ||
* Valid range is `[1.0, 2.0]`. | ||
*/ | ||
@@ -34,8 +36,15 @@ backoff?: number; | ||
* Maximum retx interval. | ||
* | ||
* Default is 90% of InterestLifetime. | ||
* @defaultValue | ||
* 90% of InterestLifetime | ||
*/ | ||
max?: number; | ||
} | ||
/** A function to generate retx intervals. */ | ||
/** | ||
* Function to generate retransmission intervals. | ||
* | ||
* @remarks | ||
* The generator function is invoked once for each Interest. It should generate successive retx | ||
* intervals for the given Interest, based on the policy it represents. When the generator ends | ||
* (no more values from the returned iterable), no more retx is allowed. | ||
*/ | ||
export type RetxGenerator = (interestLifetime: number) => Iterable<number>; | ||
@@ -45,4 +54,5 @@ /** | ||
* | ||
* A number is interpreted as the limit. | ||
* Set 0 to disable retransmissions. | ||
* @remarks | ||
* A number is interpreted as {@link RetxOptions.limit} with other options at their defaults. | ||
* Set `0` to disable retransmissions. | ||
*/ | ||
@@ -49,0 +59,0 @@ export type RetxPolicy = RetxOptions | RetxGenerator | number; |
{ | ||
"name": "@ndn/endpoint", | ||
"version": "0.0.20240113", | ||
"version": "0.0.20240630", | ||
"description": "NDNts: Client Endpoint", | ||
@@ -22,13 +22,12 @@ "keywords": [ | ||
"url": "https://github.com/yoursunny/NDNts.git", | ||
"directory": "packages/endpoint" | ||
"directory": "pkg/endpoint" | ||
}, | ||
"dependencies": { | ||
"@ndn/fw": "0.0.20240113", | ||
"@ndn/packet": "0.0.20240113", | ||
"@ndn/util": "0.0.20240113", | ||
"it-pushable": "^3.2.3", | ||
"@ndn/fw": "0.0.20240630", | ||
"@ndn/packet": "0.0.20240630", | ||
"@ndn/util": "0.0.20240630", | ||
"streaming-iterables": "^8.0.1", | ||
"tslib": "^2.6.2" | ||
"tslib": "^2.6.3" | ||
}, | ||
"types": "lib/mod.d.ts" | ||
} |
@@ -5,5 +5,6 @@ # @ndn/endpoint | ||
This package implements **Endpoint** type, which is the basic abstraction through which an application can communicate with the NDN network. | ||
This package implements the *endpoint* concept, consisting of `consume` and `produce` functions. | ||
These are the basic abstractions through which an application can communicate with the NDN network. | ||
An endpoint is similar to a "client face" in other NDN libraries, with the enhancement that it handles these details automatically: | ||
The endpoint concept is similar to a "client face" in other NDN libraries, with the enhancement that it handles these details automatically: | ||
@@ -14,3 +15,78 @@ * [X] Outgoing packets are signed and incoming packets are verified, if trust schema is provided. | ||
Data will be sent automatically upon Interest arrival. | ||
* [X] The underlying transport is reconnected upon failure, if transport failure policy is specified (implemented in `@ndn/l3face` package). | ||
* [ ] Prefix registrations are refreshed periodically or upon transport reconnection. | ||
* [X] The underlying transport is reconnected upon failure, if transport failure policy is specified (implemented in `@ndn/l3face` package). | ||
* [X] Prefix registrations are refreshed periodically or upon transport reconnection (implemented in `@ndn/nfdmgmt` package). | ||
```ts | ||
import { consume, produce } from "@ndn/endpoint"; | ||
// other imports for examples | ||
import { generateSigningKey } from "@ndn/keychain"; | ||
import { Data, digestSigning } from "@ndn/packet"; | ||
import { fromUtf8, toUtf8 } from "@ndn/util"; | ||
// Generate a key pair for the demo. | ||
const [signer, verifier] = await generateSigningKey("/identity"); | ||
``` | ||
## Producer | ||
The `produce()` standalone function creates a producer. | ||
It accepts three parameters: | ||
1. A name prefix that the producer should listen on. | ||
2. A handler function that produces the Data in reply to an Interest. | ||
3. Additional options. | ||
```ts | ||
using producer = produce("/P", async (interest) => { | ||
console.log(`Producer is handling Interest ${interest.name}`); | ||
return new Data(interest.name, toUtf8("served by NDNts")); | ||
}, { | ||
concurrency: 16, // allow concurrent calls to the handler function | ||
dataSigner: signer, // enable automatic signing | ||
}); | ||
``` | ||
The return value of `produce()` function is an object that implements **Producer** interface. | ||
This interface contains accessors and methods for observing and controlling the producer. | ||
The object implements [Disposable](https://github.com/tc39/proposal-explicit-resource-management) interface. | ||
With `using` keyword (TypeScript only), the producer is closed when the variable goes out of scope. | ||
Alternatively, you can invoke `producer[Symbol.dispose]()` explicitly. | ||
## Consumer | ||
The `consume()` standalone function creates a consumer to receive a single Data packet. | ||
It accepts two parameters: | ||
1. An Interest or Interest name. | ||
2. Additional options. | ||
```ts | ||
const consumer1 = consume("/P/1", { | ||
retx: 2, // enable retransmission | ||
verifier, // enable automatic verification | ||
}); | ||
try { | ||
const data1 = await consumer1; | ||
console.log(`Consumer receives Data ${data1.name} with content "${ | ||
fromUtf8(data1.content)}" after ${consumer1.nRetx} retransmissions`); | ||
} catch (err: unknown) { | ||
console.log("Consumer error", err); | ||
} | ||
``` | ||
The return value of `consume()` function is an object that implements **ConsumerContext** interface. | ||
This interface contains accessors and methods for observing and controlling the consumer. | ||
Most importantly, the return value is a Promise that resolves to the retrieved Data or rejects upon error (including timeout). | ||
Thus, you can simply `await consume(..)` to obtain the Data. | ||
```ts | ||
try { | ||
const data2 = await consume("/P/2", { retx: 2, verifier }); | ||
console.log(`Consumer receives Data ${data2.name}`); | ||
} catch (err: unknown) { | ||
console.log("Consumer error", err); | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34907
5
22
808
91
+ Added@ndn/fw@0.0.20240630(transitive)
+ Added@ndn/packet@0.0.20240630(transitive)
+ Added@ndn/tlv@0.0.20240630(transitive)
+ Added@ndn/util@0.0.20240630(transitive)
+ Added@shigen/polyfill-symbol-dispose@1.0.1(transitive)
+ Addedevent-iterator@2.0.0(transitive)
+ Addedtiny-invariant@1.3.3(transitive)
+ Addedtype-fest@4.29.0(transitive)
+ Addedwait-your-turn@1.0.1(transitive)
- Removedit-pushable@^3.2.3
- Removed@ndn/fw@0.0.20240113(transitive)
- Removed@ndn/packet@0.0.20240113(transitive)
- Removed@ndn/tlv@0.0.20240113(transitive)
- Removed@ndn/util@0.0.20240113(transitive)
- Removed@types/minimalistic-assert@1.0.3(transitive)
- Removedit-pushable@3.2.3(transitive)
- Removedminimalistic-assert@1.0.1(transitive)
- Removedp-defer@4.0.1(transitive)
- Removedtype-fest@4.27.1(transitive)
Updated@ndn/fw@0.0.20240630
Updated@ndn/packet@0.0.20240630
Updated@ndn/util@0.0.20240630
Updatedtslib@^2.6.3