A small, stable TypeScript/JavaScript contract for turning already-received source payloads into deterministic PipSync signal envelopes.
The package contains:
- a fail-closed
SignalConnectorinterface - versioned
SignalEnvelopetypes - a side-effect-free synthetic JSON reference connector
- a reusable conformance harness
- an in-memory
PaperSinkthat emits only deterministicPAPER-…receipt IDs
It contains no HTTP, WebSocket, broker, credential, environment-variable, database, or live-order integration.
Important
This kit normalizes data; it does not authenticate a sender. Verify the exact raw request body and transport-specific credentials before parsing or passing a payload to a connector. Trading involves risk. This package is developer tooling, not financial advice or a guarantee of execution or returns.
- Node.js 20 or newer
- TypeScript is optional for consumers; compiled JavaScript and declarations are published together
The first npm registry release has not been published yet. The current supported path is to build and pack the audited GitHub source:
git clone https://github.com/pipsyncio/pipsync-connector-kit.git
cd pipsync-connector-kit
npm ci
npm test
npm packInstall the generated .tgz file into another local project:
npm install /path/to/pipsync-connector-kit/pipsync-connector-kit-1.0.0.tgzAfter the first npm registry publication, the shorter registry command will be:
npm install @pipsync/connector-kitDo not interpret that future command as evidence that the package is already present in npm. All repository tests are local and use synthetic data. No credentials or network service are required.
import { PaperSink, SyntheticJsonConnector } from "@pipsync/connector-kit";
const connector = new SyntheticJsonConnector();
const result = connector.normalize(
{
messageId: "synthetic-message-001",
symbol: "EUR/USD",
side: "long",
entryPrice: "1.0850",
stopLoss: "1.0820",
takeProfits: ["1.0900"]
},
{ receivedAt: "2026-01-15T10:30:00Z" }
);
if (!result.ok) {
console.error(result.error.code);
} else {
const paper = new PaperSink();
console.log(paper.submit(result.envelope));
}JavaScript uses the same ESM imports and return-value checks; the public API does not rely on TypeScript-only runtime behavior.
A connector receives an unknown payload plus an explicit deterministic context and returns one of two values:
type NormalizationResult =
| { ok: true; envelope: SignalEnvelope }
| { ok: false; error: NormalizationError };Missing or invalid instrument and direction always produce an error. A connector must never fill them from guesses, route an incomplete signal, read a credential, fetch remote data, persist an event, or execute an order.
The reference connector accepts JSON objects or JSON object strings with these synthetic aliases:
instrumentorsymboldirectionorsideBUY,LONG, orBULLISH→BUYSELL,SHORT, orBEARISH→SELL- optional
messageId,occurredAt,entryPrice,stopLoss, andtakeProfits
When both a canonical field and its alias are present, they must normalize to the same value; conflicting instrument/symbol or direction/side values fail closed. Unknown source-input fields are ignored and never copied into the normalized envelope. The conformance harness enforces exact property allowlists for envelopes, nested source, results, errors, and adapter metadata; unsupported output properties such as rawPayload are rejected. Errors contain stable codes, not raw payload content.
SignalEnvelope and the standalone pipsync.signal.v1 interchange object are intentionally separate contracts:
| Contract | Purpose |
|---|---|
pipsync.signal-envelope.v1 |
normalization-stage identity, source, occurrence time, and deterministic idempotency |
pipsync.signal.v1 |
public synthetic interchange object used by specs, SDKs, mocks, and conformance tooling; it does not define a live ingestion endpoint |
Use the explicit fail-closed adapter when crossing that boundary:
import { toPipsyncSignalV1 } from "@pipsync/connector-kit";
if (!result.ok) throw new Error(result.error.code);
const adapted = toPipsyncSignalV1(result.envelope, {
id: "123e4567-e89b-12d3-a456-426614174000",
channelName: "Synthetic channel"
});
if (!adapted.ok) {
console.error(adapted.error.code);
} else {
console.log(adapted.signal.schemaVersion); // pipsync.signal.v1
}The adapter requires entryPrice, a caller-supplied UUID, and channelName, enforces the standalone schema limits, and never copies source, idempotencyKey, occurredAt, or unknown fields. Missing required data returns an error instead of a guessed default.
The envelope idempotency key is a SHA-256 digest scoped by connector ID:
- When an authenticated, durable source
messageIdexists, identity isconnectorId + messageId. - Otherwise, identity is
connectorId + normalized signal content + occurredAt.
No timestamp, random value, or mutable delivery status is generated internally. The caller supplies receivedAt, and repeated normalization of the same occurrence produces the same key across processes.
The fallback prevents exact duplicate occurrences; it cannot infer whether two intentionally repeated signals are distinct. Source connectors should prefer a durable provider event/message ID. A production host still needs an atomic unique constraint or equivalent claim scoped by tenant, endpoint, and environment before side effects. PaperSink's in-memory map is same-process test support, not durable deduplication evidence.
Connector authors can run the exported test harness with synthetic cases:
import {
assertConnectorConformance,
SyntheticJsonConnector,
type ConnectorConformanceCase
} from "@pipsync/connector-kit";
const cases: ConnectorConformanceCase[] = [
{
kind: "valid",
name: "normalizes a buy",
input: {
messageId: "fixture-1",
instrument: "EURUSD",
direction: "BUY"
},
context: { receivedAt: "2026-01-15T10:30:00Z" },
expected: { instrument: "EURUSD", direction: "BUY" }
},
{
kind: "invalid",
name: "rejects a missing instrument",
input: { direction: "BUY" },
context: { receivedAt: "2026-01-15T10:30:00Z" },
expectedErrorCode: "INSTRUMENT_REQUIRED"
}
];
await assertConnectorConformance(new SyntheticJsonConnector(), cases);The harness checks exact descriptor/result/envelope shapes, input immutability, repeat determinism, semantic idempotency, cross-case identity uniqueness, exact expected fields, and fail-closed error codes. Passing it does not prove sender authenticity, durable replay protection, production safety, or broker compatibility.
| Level | Meaning |
|---|---|
verified |
A version-specific connector release reviewed and designated by PipSync maintainers against the published checklist |
community |
Community-owned, named maintainers, passing conformance; not reviewed or endorsed by PipSync |
experimental |
Prototype contract that may change and must not be enabled by default |
See GOVERNANCE.md for promotion, revocation, naming, and claim rules and VERIFIED_RELEASES.md for version-specific records. A governance label never means profitable, risk-free, live-ready, independently security-certified, or certified by a broker/provider.
- authenticate before parsing or trusted persistence
- use authenticated provider identity for idempotency when available
- atomically claim idempotency before any downstream side effect
- retain immutable receipt history for retry/replay outside this package
- keep raw payloads, tokens, signatures, customer data, and account identifiers out of errors and fixtures
- keep live execution in a separate, explicit, independently reviewed adapter
Report vulnerabilities privately as described in SECURITY.md.
The kit proves a connector's normalization contract, not hosted compatibility. For supported source and broker combinations, use the PipSync integration directory.
Apache-2.0. See LICENSE.