@origintrail-official/dkg-core 0.0.1-dev.1773506972.23bf9c0

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.
Files changed (115) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +33 -0
  3. package/dist/constants.d.ts +25 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +57 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/crypto/canonicalize.d.ts +12 -0
  8. package/dist/crypto/canonicalize.d.ts.map +1 -0
  9. package/dist/crypto/canonicalize.js +43 -0
  10. package/dist/crypto/canonicalize.js.map +1 -0
  11. package/dist/crypto/ed25519.d.ts +9 -0
  12. package/dist/crypto/ed25519.d.ts.map +1 -0
  13. package/dist/crypto/ed25519.js +14 -0
  14. package/dist/crypto/ed25519.js.map +1 -0
  15. package/dist/crypto/hashing.d.ts +2 -0
  16. package/dist/crypto/hashing.d.ts.map +1 -0
  17. package/dist/crypto/hashing.js +5 -0
  18. package/dist/crypto/hashing.js.map +1 -0
  19. package/dist/crypto/index.d.ts +6 -0
  20. package/dist/crypto/index.d.ts.map +1 -0
  21. package/dist/crypto/index.js +6 -0
  22. package/dist/crypto/index.js.map +1 -0
  23. package/dist/crypto/merkle.d.ts +19 -0
  24. package/dist/crypto/merkle.d.ts.map +1 -0
  25. package/dist/crypto/merkle.js +103 -0
  26. package/dist/crypto/merkle.js.map +1 -0
  27. package/dist/crypto/oracle-verify.d.ts +45 -0
  28. package/dist/crypto/oracle-verify.d.ts.map +1 -0
  29. package/dist/crypto/oracle-verify.js +76 -0
  30. package/dist/crypto/oracle-verify.js.map +1 -0
  31. package/dist/discovery.d.ts +23 -0
  32. package/dist/discovery.d.ts.map +1 -0
  33. package/dist/discovery.js +61 -0
  34. package/dist/discovery.js.map +1 -0
  35. package/dist/event-bus.d.ts +38 -0
  36. package/dist/event-bus.d.ts.map +1 -0
  37. package/dist/event-bus.js +75 -0
  38. package/dist/event-bus.js.map +1 -0
  39. package/dist/genesis.d.ts +51 -0
  40. package/dist/genesis.d.ts.map +1 -0
  41. package/dist/genesis.js +159 -0
  42. package/dist/genesis.js.map +1 -0
  43. package/dist/gossipsub-manager.d.ts +17 -0
  44. package/dist/gossipsub-manager.d.ts.map +1 -0
  45. package/dist/gossipsub-manager.js +69 -0
  46. package/dist/gossipsub-manager.js.map +1 -0
  47. package/dist/index.d.ts +14 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +14 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/logger.d.ts +35 -0
  52. package/dist/logger.d.ts.map +1 -0
  53. package/dist/logger.js +48 -0
  54. package/dist/logger.js.map +1 -0
  55. package/dist/node.d.ts +47 -0
  56. package/dist/node.d.ts.map +1 -0
  57. package/dist/node.js +307 -0
  58. package/dist/node.js.map +1 -0
  59. package/dist/proto/access.d.ts +22 -0
  60. package/dist/proto/access.d.ts.map +1 -0
  61. package/dist/proto/access.js +26 -0
  62. package/dist/proto/access.js.map +1 -0
  63. package/dist/proto/discover.d.ts +19 -0
  64. package/dist/proto/discover.d.ts.map +1 -0
  65. package/dist/proto/discover.js +24 -0
  66. package/dist/proto/discover.js.map +1 -0
  67. package/dist/proto/finalization.d.ts +28 -0
  68. package/dist/proto/finalization.d.ts.map +1 -0
  69. package/dist/proto/finalization.js +42 -0
  70. package/dist/proto/finalization.js.map +1 -0
  71. package/dist/proto/index.d.ts +9 -0
  72. package/dist/proto/index.d.ts.map +1 -0
  73. package/dist/proto/index.js +9 -0
  74. package/dist/proto/index.js.map +1 -0
  75. package/dist/proto/ka-update.d.ts +27 -0
  76. package/dist/proto/ka-update.d.ts.map +1 -0
  77. package/dist/proto/ka-update.js +55 -0
  78. package/dist/proto/ka-update.js.map +1 -0
  79. package/dist/proto/message.d.ts +21 -0
  80. package/dist/proto/message.d.ts.map +1 -0
  81. package/dist/proto/message.js +18 -0
  82. package/dist/proto/message.js.map +1 -0
  83. package/dist/proto/publish.d.ts +50 -0
  84. package/dist/proto/publish.d.ts.map +1 -0
  85. package/dist/proto/publish.js +44 -0
  86. package/dist/proto/publish.js.map +1 -0
  87. package/dist/proto/query.d.ts +27 -0
  88. package/dist/proto/query.d.ts.map +1 -0
  89. package/dist/proto/query.js +32 -0
  90. package/dist/proto/query.js.map +1 -0
  91. package/dist/proto/workspace.d.ts +34 -0
  92. package/dist/proto/workspace.d.ts.map +1 -0
  93. package/dist/proto/workspace.js +37 -0
  94. package/dist/proto/workspace.js.map +1 -0
  95. package/dist/protocol-router.d.ts +23 -0
  96. package/dist/protocol-router.d.ts.map +1 -0
  97. package/dist/protocol-router.js +127 -0
  98. package/dist/protocol-router.js.map +1 -0
  99. package/dist/rate-limiter.d.ts +33 -0
  100. package/dist/rate-limiter.d.ts.map +1 -0
  101. package/dist/rate-limiter.js +80 -0
  102. package/dist/rate-limiter.js.map +1 -0
  103. package/dist/retry.d.ts +21 -0
  104. package/dist/retry.d.ts.map +1 -0
  105. package/dist/retry.js +42 -0
  106. package/dist/retry.js.map +1 -0
  107. package/dist/sparql-safe.d.ts +59 -0
  108. package/dist/sparql-safe.d.ts.map +1 -0
  109. package/dist/sparql-safe.js +118 -0
  110. package/dist/sparql-safe.js.map +1 -0
  111. package/dist/types.d.ts +57 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +2 -0
  114. package/dist/types.js.map +1 -0
  115. package/package.json +57 -0
@@ -0,0 +1,127 @@
1
+ /** Default max bytes readAll will buffer before aborting (10 MB). */
2
+ export const DEFAULT_MAX_READ_BYTES = 10 * 1024 * 1024;
3
+ /** Default timeout for send() (ms). Sync over relay may need longer; callers can pass a higher value. */
4
+ export const DEFAULT_SEND_TIMEOUT_MS = 20_000;
5
+ /**
6
+ * Returns true if the error is recoverable (retry with backoff).
7
+ * Exported for tests.
8
+ */
9
+ export function isRecoverableSendError(err) {
10
+ const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();
11
+ return (msg.includes('closed') ||
12
+ msg.includes('reset') ||
13
+ msg.includes('stream returned in closed state') ||
14
+ msg.includes('econnreset') ||
15
+ msg.includes('etimedout') ||
16
+ msg.includes('econnrefused') ||
17
+ msg.includes('epipe') ||
18
+ msg.includes('aborted') ||
19
+ msg.includes('no valid addresses') ||
20
+ msg.includes('protocol selection failed') ||
21
+ msg.includes('could not negotiate'));
22
+ }
23
+ export class ProtocolRouter {
24
+ node;
25
+ handlers = new Map();
26
+ maxReadBytes;
27
+ constructor(node, options) {
28
+ this.node = node;
29
+ this.maxReadBytes = options?.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;
30
+ }
31
+ register(protocolId, handler) {
32
+ this.handlers.set(protocolId, handler);
33
+ const libp2p = this.node.libp2p;
34
+ const limit = this.maxReadBytes;
35
+ libp2p.handle(protocolId, async (stream, connection) => {
36
+ try {
37
+ const requestData = await readAll(stream, limit);
38
+ const peerId = {
39
+ toString: () => connection.remotePeer.toString(),
40
+ toBytes: () => connection.remotePeer.toMultihash().bytes,
41
+ };
42
+ const responseData = await handler(requestData, peerId);
43
+ stream.send(responseData);
44
+ await stream.close();
45
+ }
46
+ catch (err) {
47
+ console.error(`[ProtocolRouter] handler error on ${protocolId} from ${connection.remotePeer.toString().slice(-8)}:`, err instanceof Error ? err.message : err);
48
+ try {
49
+ stream.abort(new Error('handler error'));
50
+ }
51
+ catch {
52
+ // stream already closed
53
+ }
54
+ }
55
+ }, { runOnLimitedConnection: true });
56
+ }
57
+ unregister(protocolId) {
58
+ this.handlers.delete(protocolId);
59
+ this.node.libp2p.unhandle(protocolId);
60
+ }
61
+ async send(peerIdStr, protocolId, data, timeoutMs = DEFAULT_SEND_TIMEOUT_MS) {
62
+ const libp2p = this.node.libp2p;
63
+ const { peerIdFromString } = await import('@libp2p/peer-id');
64
+ const peerId = peerIdFromString(peerIdStr);
65
+ const signal = AbortSignal.timeout(timeoutMs);
66
+ // libp2p internally upgrades relay connections to direct during
67
+ // dialProtocol/newStream (peerStore.merge triggers the connection manager
68
+ // to dial the peer directly, closing the relay and any in-flight streams).
69
+ // We retry up to 3 times with back-off so the direct connection can
70
+ // stabilise before the next attempt.
71
+ let lastErr;
72
+ for (let attempt = 0; attempt < 3; attempt++) {
73
+ try {
74
+ const stream = await libp2p.dialProtocol(peerId, protocolId, {
75
+ runOnLimitedConnection: true,
76
+ signal,
77
+ });
78
+ if (stream.writeStatus === 'closed' || stream.writeStatus === 'closing') {
79
+ stream.abort(new Error('stream closed before send'));
80
+ throw new Error('stream returned in closed state');
81
+ }
82
+ stream.send(data);
83
+ await stream.close({ signal });
84
+ return await readAll(stream, this.maxReadBytes);
85
+ }
86
+ catch (err) {
87
+ lastErr = err;
88
+ if (!isRecoverableSendError(err) || attempt >= 2)
89
+ throw err;
90
+ const backoff = (attempt + 1) * 500;
91
+ await new Promise(r => setTimeout(r, backoff));
92
+ }
93
+ }
94
+ throw lastErr;
95
+ }
96
+ }
97
+ async function readAll(stream, maxBytes = DEFAULT_MAX_READ_BYTES) {
98
+ const chunks = [];
99
+ let total = 0;
100
+ for await (const chunk of stream) {
101
+ const buf = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk.subarray());
102
+ total += buf.length;
103
+ if (total > maxBytes) {
104
+ if ('abort' in stream && typeof stream.abort === 'function') {
105
+ stream.abort(new Error('read limit exceeded'));
106
+ }
107
+ throw new Error(`Read limit exceeded (${maxBytes} bytes)`);
108
+ }
109
+ chunks.push(buf);
110
+ }
111
+ return concat(chunks);
112
+ }
113
+ function concat(arrays) {
114
+ if (arrays.length === 0)
115
+ return new Uint8Array(0);
116
+ if (arrays.length === 1)
117
+ return arrays[0];
118
+ const total = arrays.reduce((sum, a) => sum + a.length, 0);
119
+ const result = new Uint8Array(total);
120
+ let offset = 0;
121
+ for (const a of arrays) {
122
+ result.set(a, offset);
123
+ offset += a.length;
124
+ }
125
+ return result;
126
+ }
127
+ //# sourceMappingURL=protocol-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-router.js","sourceRoot":"","sources":["../src/protocol-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Sliding-window rate limiter for per-peer / per-topic / per-protocol throttling.
3
+ *
4
+ * Uses a token-bucket approach with automatic stale-entry cleanup.
5
+ */
6
+ export interface RateLimitConfig {
7
+ /** Maximum number of events allowed within the window. */
8
+ maxPerWindow: number;
9
+ /** Window size in milliseconds. */
10
+ windowMs: number;
11
+ }
12
+ export declare class RateLimiter {
13
+ private readonly config;
14
+ private readonly buckets;
15
+ private cleanupTimer;
16
+ constructor(config: RateLimitConfig);
17
+ /**
18
+ * Returns true if the request is allowed, false if rate-limited.
19
+ * Automatically records the event if allowed.
20
+ */
21
+ allow(key: string): boolean;
22
+ /** Check without recording — peek at whether next request would be allowed. */
23
+ wouldAllow(key: string): boolean;
24
+ /** Number of requests remaining in the current window for this key. */
25
+ remaining(key: string): number;
26
+ /** Remove stale entries that have no timestamps in the current window. */
27
+ private cleanup;
28
+ /** Stop the cleanup timer (call on shutdown). */
29
+ destroy(): void;
30
+ /** Current number of tracked keys. */
31
+ get size(): number;
32
+ }
33
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAEvD,MAAM,EAAE,eAAe;IAMnC;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAuB3B,+EAA+E;IAC/E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAShC,uEAAuE;IACvE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAS9B,0EAA0E;IAC1E,OAAO,CAAC,OAAO;IAUf,iDAAiD;IACjD,OAAO,IAAI,IAAI;IAQf,sCAAsC;IACtC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Sliding-window rate limiter for per-peer / per-topic / per-protocol throttling.
3
+ *
4
+ * Uses a token-bucket approach with automatic stale-entry cleanup.
5
+ */
6
+ export class RateLimiter {
7
+ config;
8
+ buckets = new Map();
9
+ cleanupTimer = null;
10
+ constructor(config) {
11
+ this.config = config;
12
+ this.cleanupTimer = setInterval(() => this.cleanup(), Math.max(config.windowMs * 2, 60_000));
13
+ this.cleanupTimer.unref?.();
14
+ }
15
+ /**
16
+ * Returns true if the request is allowed, false if rate-limited.
17
+ * Automatically records the event if allowed.
18
+ */
19
+ allow(key) {
20
+ const now = Date.now();
21
+ const cutoff = now - this.config.windowMs;
22
+ let entry = this.buckets.get(key);
23
+ if (!entry) {
24
+ entry = { timestamps: [now], blocked: false };
25
+ this.buckets.set(key, entry);
26
+ return true;
27
+ }
28
+ entry.timestamps = entry.timestamps.filter(t => t > cutoff);
29
+ if (entry.timestamps.length >= this.config.maxPerWindow) {
30
+ entry.blocked = true;
31
+ return false;
32
+ }
33
+ entry.timestamps.push(now);
34
+ entry.blocked = false;
35
+ return true;
36
+ }
37
+ /** Check without recording — peek at whether next request would be allowed. */
38
+ wouldAllow(key) {
39
+ const now = Date.now();
40
+ const cutoff = now - this.config.windowMs;
41
+ const entry = this.buckets.get(key);
42
+ if (!entry)
43
+ return true;
44
+ const active = entry.timestamps.filter(t => t > cutoff);
45
+ return active.length < this.config.maxPerWindow;
46
+ }
47
+ /** Number of requests remaining in the current window for this key. */
48
+ remaining(key) {
49
+ const now = Date.now();
50
+ const cutoff = now - this.config.windowMs;
51
+ const entry = this.buckets.get(key);
52
+ if (!entry)
53
+ return this.config.maxPerWindow;
54
+ const active = entry.timestamps.filter(t => t > cutoff);
55
+ return Math.max(0, this.config.maxPerWindow - active.length);
56
+ }
57
+ /** Remove stale entries that have no timestamps in the current window. */
58
+ cleanup() {
59
+ const cutoff = Date.now() - this.config.windowMs;
60
+ for (const [key, entry] of this.buckets) {
61
+ entry.timestamps = entry.timestamps.filter(t => t > cutoff);
62
+ if (entry.timestamps.length === 0) {
63
+ this.buckets.delete(key);
64
+ }
65
+ }
66
+ }
67
+ /** Stop the cleanup timer (call on shutdown). */
68
+ destroy() {
69
+ if (this.cleanupTimer) {
70
+ clearInterval(this.cleanupTimer);
71
+ this.cleanupTimer = null;
72
+ }
73
+ this.buckets.clear();
74
+ }
75
+ /** Current number of tracked keys. */
76
+ get size() {
77
+ return this.buckets.size;
78
+ }
79
+ }
80
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface RetryOptions {
2
+ /** Maximum number of attempts (default: 3). */
3
+ maxAttempts?: number;
4
+ /** Initial delay in ms before the first retry (default: 500). */
5
+ baseDelayMs?: number;
6
+ /** Maximum delay cap in ms (default: 30_000). */
7
+ maxDelayMs?: number;
8
+ /** Jitter factor 0–1 added to each delay to avoid thundering herd (default: 0.2). */
9
+ jitter?: number;
10
+ /** Optional predicate to decide if an error is retryable. Defaults to all errors. */
11
+ isRetryable?: (err: unknown) => boolean;
12
+ /** Called on each retry with attempt number and delay (for logging). */
13
+ onRetry?: (attempt: number, delayMs: number, err: unknown) => void;
14
+ }
15
+ /**
16
+ * Execute an async function with exponential backoff retry.
17
+ *
18
+ * delay = min(baseDelay * 2^attempt + jitter, maxDelay)
19
+ */
20
+ export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
21
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACxC,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpE;AASD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,CAAC,CAAC,CA6BZ"}
package/dist/retry.js ADDED
@@ -0,0 +1,42 @@
1
+ const DEFAULTS = {
2
+ maxAttempts: 3,
3
+ baseDelayMs: 500,
4
+ maxDelayMs: 30_000,
5
+ jitter: 0.2,
6
+ };
7
+ /**
8
+ * Execute an async function with exponential backoff retry.
9
+ *
10
+ * delay = min(baseDelay * 2^attempt + jitter, maxDelay)
11
+ */
12
+ export async function withRetry(fn, opts = {}) {
13
+ const maxAttempts = opts.maxAttempts ?? DEFAULTS.maxAttempts;
14
+ const baseDelayMs = opts.baseDelayMs ?? DEFAULTS.baseDelayMs;
15
+ const maxDelayMs = opts.maxDelayMs ?? DEFAULTS.maxDelayMs;
16
+ const jitterFactor = opts.jitter ?? DEFAULTS.jitter;
17
+ const isRetryable = opts.isRetryable;
18
+ const onRetry = opts.onRetry;
19
+ let lastErr;
20
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
21
+ try {
22
+ return await fn();
23
+ }
24
+ catch (err) {
25
+ lastErr = err;
26
+ if (isRetryable && !isRetryable(err))
27
+ throw err;
28
+ if (attempt >= maxAttempts - 1)
29
+ throw err;
30
+ const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
31
+ const jitter = exponentialDelay * jitterFactor * Math.random();
32
+ const delay = Math.min(exponentialDelay + jitter, maxDelayMs);
33
+ onRetry?.(attempt + 1, delay, err);
34
+ await sleep(delay);
35
+ }
36
+ }
37
+ throw lastErr;
38
+ }
39
+ function sleep(ms) {
40
+ return new Promise(resolve => setTimeout(resolve, ms));
41
+ }
42
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAeA,MAAM,QAAQ,GAA4D;IACxE,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,MAAM;IAClB,MAAM,EAAE,GAAG;CACZ,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,OAAqB,EAAE;IAEvB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC;IAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAE7B,IAAI,OAAgB,CAAC;IAErB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,GAAG,CAAC;YAEd,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YAChD,IAAI,OAAO,IAAI,WAAW,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YAE1C,MAAM,gBAAgB,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,gBAAgB,GAAG,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,GAAG,MAAM,EAAE,UAAU,CAAC,CAAC;YAE9D,OAAO,EAAE,CAAC,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YACnC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,MAAM,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * SPARQL injection prevention utilities.
3
+ *
4
+ * Two complementary strategies:
5
+ *
6
+ * - **IRIs**: validate-and-reject. An IRI is either syntactically safe or
7
+ * it isn't — silently percent-encoding characters would change its meaning.
8
+ * Use `assertSafeIri` / `isSafeIri` / `sparqlIri`.
9
+ *
10
+ * - **String literals**: escape. The raw value is preserved but special
11
+ * characters are backslash-escaped per the SPARQL grammar.
12
+ * Use `escapeSparqlLiteral` / `sparqlString`.
13
+ *
14
+ * - **Integers**: validate type and optional bounds.
15
+ * Use `sparqlInt`.
16
+ */
17
+ /**
18
+ * Throws if `value` contains characters that would break a SPARQL IRI (`<...>`).
19
+ * Returns the value unchanged when safe, for ergonomic inline use:
20
+ *
21
+ * `<${assertSafeIri(uri)}>`
22
+ */
23
+ export declare function assertSafeIri(value: string): string;
24
+ /**
25
+ * Returns true when the string is a syntactically safe IRI with a scheme
26
+ * prefix (e.g. `did:dkg:...`, `http://...`, `urn:...`).
27
+ */
28
+ export declare function isSafeIri(value: string): boolean;
29
+ /**
30
+ * Returns `<value>` after validating the IRI is safe for SPARQL interpolation.
31
+ * Throws on unsafe input.
32
+ */
33
+ export declare function sparqlIri(value: string): string;
34
+ /**
35
+ * Escapes a raw string for use inside a SPARQL `"..."` literal.
36
+ * Handles all characters that the SPARQL grammar requires escaping
37
+ * in short string literals (production rule [157] STRING_LITERAL2).
38
+ */
39
+ export declare function escapeSparqlLiteral(value: string): string;
40
+ /**
41
+ * Returns `"escaped"` ready for SPARQL interpolation.
42
+ */
43
+ export declare function sparqlString(value: string): string;
44
+ /**
45
+ * Validates a complete SPARQL RDF term — either a quoted literal
46
+ * (with optional language tag or datatype) or an IRI in angle brackets.
47
+ * Throws on anything that could break SPARQL syntax.
48
+ *
49
+ * assertSafeRdfTerm('"hello"') // ok
50
+ * assertSafeRdfTerm('"42"^^<xsd:int>') // ok
51
+ * assertSafeRdfTerm('<http://ex.org/>') // ok
52
+ * assertSafeRdfTerm('" } DROP ALL #') // throws
53
+ */
54
+ export declare function assertSafeRdfTerm(value: string): void;
55
+ export declare function sparqlInt(value: number | bigint, opts?: {
56
+ min?: number;
57
+ max?: number;
58
+ }): string;
59
+ //# sourceMappingURL=sparql-safe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sparql-safe.d.ts","sourceRoot":"","sources":["../src/sparql-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC,MAAM,CAoBR"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * SPARQL injection prevention utilities.
3
+ *
4
+ * Two complementary strategies:
5
+ *
6
+ * - **IRIs**: validate-and-reject. An IRI is either syntactically safe or
7
+ * it isn't — silently percent-encoding characters would change its meaning.
8
+ * Use `assertSafeIri` / `isSafeIri` / `sparqlIri`.
9
+ *
10
+ * - **String literals**: escape. The raw value is preserved but special
11
+ * characters are backslash-escaped per the SPARQL grammar.
12
+ * Use `escapeSparqlLiteral` / `sparqlString`.
13
+ *
14
+ * - **Integers**: validate type and optional bounds.
15
+ * Use `sparqlInt`.
16
+ */
17
+ const UNSAFE_IRI_CHARS = /[<>"{}|\\^`\x00-\x20]/;
18
+ const IRI_SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s<>"{}|\\^`\x00-\x20]+$/;
19
+ /**
20
+ * Throws if `value` contains characters that would break a SPARQL IRI (`<...>`).
21
+ * Returns the value unchanged when safe, for ergonomic inline use:
22
+ *
23
+ * `<${assertSafeIri(uri)}>`
24
+ */
25
+ export function assertSafeIri(value) {
26
+ if (!value || UNSAFE_IRI_CHARS.test(value)) {
27
+ throw new Error(`Unsafe or empty IRI value: ${value}`);
28
+ }
29
+ return value;
30
+ }
31
+ /**
32
+ * Returns true when the string is a syntactically safe IRI with a scheme
33
+ * prefix (e.g. `did:dkg:...`, `http://...`, `urn:...`).
34
+ */
35
+ export function isSafeIri(value) {
36
+ if (!value)
37
+ return false;
38
+ return IRI_SCHEME_RE.test(value);
39
+ }
40
+ /**
41
+ * Returns `<value>` after validating the IRI is safe for SPARQL interpolation.
42
+ * Throws on unsafe input.
43
+ */
44
+ export function sparqlIri(value) {
45
+ assertSafeIri(value);
46
+ return `<${value}>`;
47
+ }
48
+ /**
49
+ * Escapes a raw string for use inside a SPARQL `"..."` literal.
50
+ * Handles all characters that the SPARQL grammar requires escaping
51
+ * in short string literals (production rule [157] STRING_LITERAL2).
52
+ */
53
+ export function escapeSparqlLiteral(value) {
54
+ return value
55
+ .replace(/\\/g, '\\\\')
56
+ .replace(/"/g, '\\"')
57
+ .replace(/\n/g, '\\n')
58
+ .replace(/\r/g, '\\r')
59
+ .replace(/\t/g, '\\t');
60
+ }
61
+ /**
62
+ * Returns `"escaped"` ready for SPARQL interpolation.
63
+ */
64
+ export function sparqlString(value) {
65
+ return `"${escapeSparqlLiteral(value)}"`;
66
+ }
67
+ /**
68
+ * Validates and returns a safe integer string for SPARQL LIMIT / OFFSET
69
+ * and similar numeric contexts. Rejects NaN, Infinity, and non-integer values.
70
+ *
71
+ * Handles `bigint` natively via `.toString()` to avoid precision loss
72
+ * for values beyond `Number.MAX_SAFE_INTEGER`.
73
+ */
74
+ // SPARQL STRING_LITERAL2: forbid raw CR/LF, only allow valid SPARQL escapes
75
+ // BCP47 lang tags: primary subtag alpha-only, subsequent subtags alphanumeric (e.g. de-CH-1996)
76
+ const SAFE_IRI_CHARS = '[^<>"{}|\\\\^`\\x00-\\x20>]';
77
+ const SAFE_RDF_LITERAL = new RegExp(`^"(?:[^"\\\\\\r\\n]|\\\\[tbnrf"'\\\\]|\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8})*"` +
78
+ `(?:@[A-Za-z]+(?:-[A-Za-z0-9]+)*|\\^\\^<${SAFE_IRI_CHARS}+>)?$`);
79
+ const SAFE_RDF_IRI_TERM = /^<[^<>"{}|\\^`\x00-\x20]+>$/;
80
+ /**
81
+ * Validates a complete SPARQL RDF term — either a quoted literal
82
+ * (with optional language tag or datatype) or an IRI in angle brackets.
83
+ * Throws on anything that could break SPARQL syntax.
84
+ *
85
+ * assertSafeRdfTerm('"hello"') // ok
86
+ * assertSafeRdfTerm('"42"^^<xsd:int>') // ok
87
+ * assertSafeRdfTerm('<http://ex.org/>') // ok
88
+ * assertSafeRdfTerm('" } DROP ALL #') // throws
89
+ */
90
+ export function assertSafeRdfTerm(value) {
91
+ if (SAFE_RDF_LITERAL.test(value))
92
+ return;
93
+ if (SAFE_RDF_IRI_TERM.test(value))
94
+ return;
95
+ throw new Error(`Unsafe RDF term for CAS condition: ${value.slice(0, 80)}`);
96
+ }
97
+ export function sparqlInt(value, opts) {
98
+ if (typeof value === 'bigint') {
99
+ if (opts?.min !== undefined && value < BigInt(opts.min)) {
100
+ throw new Error(`SPARQL integer ${value} below minimum ${opts.min}`);
101
+ }
102
+ if (opts?.max !== undefined && value > BigInt(opts.max)) {
103
+ throw new Error(`SPARQL integer ${value} above maximum ${opts.max}`);
104
+ }
105
+ return value.toString();
106
+ }
107
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
108
+ throw new Error(`Invalid SPARQL integer: ${value}`);
109
+ }
110
+ if (opts?.min !== undefined && value < opts.min) {
111
+ throw new Error(`SPARQL integer ${value} below minimum ${opts.min}`);
112
+ }
113
+ if (opts?.max !== undefined && value > opts.max) {
114
+ throw new Error(`SPARQL integer ${value} above maximum ${opts.max}`);
115
+ }
116
+ return String(value);
117
+ }
118
+ //# sourceMappingURL=sparql-safe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sparql-safe.js","sourceRoot":"","sources":["../src/sparql-safe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ export interface PeerId {
2
+ toString(): string;
3
+ toBytes(): Uint8Array;
4
+ }
5
+ export interface ProtocolMessage {
6
+ protocolId: string;
7
+ data: Uint8Array;
8
+ }
9
+ export interface EventBus {
10
+ emit(event: string, data: unknown): void;
11
+ on(event: string, handler: (data: unknown) => void): void;
12
+ off(event: string, handler: (data: unknown) => void): void;
13
+ }
14
+ export interface DKGNodeConfig {
15
+ /** Multiaddr strings to listen on. Defaults to TCP + WS on random ports. */
16
+ listenAddresses?: string[];
17
+ /** Multiaddr strings to announce to the network (for nodes behind NAT/VPS with a public IP not bound to the interface). */
18
+ announceAddresses?: string[];
19
+ /** DKG bootstrap peer multiaddrs (NOT public IPFS nodes). */
20
+ bootstrapPeers?: string[];
21
+ /** Enable mDNS for local peer discovery. Default: true. */
22
+ enableMdns?: boolean;
23
+ /** GossipSub paranet topics to subscribe to at startup. */
24
+ paranetSubscriptions?: string[];
25
+ /** Ed25519 private key bytes. Generated if absent. */
26
+ privateKey?: Uint8Array;
27
+ /** Data directory for persistent state. */
28
+ dataDir?: string;
29
+ /** Multiaddrs of relay nodes to connect to for NAT traversal. */
30
+ relayPeers?: string[];
31
+ /** Enable circuit relay server on this node (for nodes with public IPs). */
32
+ enableRelayServer?: boolean;
33
+ /**
34
+ * Enable autoNAT service for automatic NAT status detection.
35
+ * Default: true, but auto-disabled when relayPeers or enableRelayServer is
36
+ * set (nodes that already know their NAT status don't need probing).
37
+ */
38
+ enableAutoNAT?: boolean;
39
+ /**
40
+ * Node deployment tier. Core nodes act as relays and GossipSub backbone.
41
+ * Edge nodes are the typical deployment for personal agents behind NATs.
42
+ * Default: 'edge'.
43
+ */
44
+ nodeRole?: 'core' | 'edge';
45
+ }
46
+ export type ConnectionTransport = 'direct' | 'relayed';
47
+ export interface ConnectionInfo {
48
+ peerId: string;
49
+ remoteAddr: string;
50
+ transport: ConnectionTransport;
51
+ direction: 'inbound' | 'outbound';
52
+ openedAt: number;
53
+ }
54
+ export interface StreamHandler {
55
+ (data: Uint8Array, peerId: PeerId): Promise<Uint8Array>;
56
+ }
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,QAAQ,IAAI,MAAM,CAAC;IACnB,OAAO,IAAI,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IACzC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1D,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,2HAA2H;IAC3H,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,sDAAsD;IACtD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@origintrail-official/dkg-core",
3
+ "version": "0.0.1-dev.1773506972.23bf9c0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "dependencies": {
8
+ "@libp2p/autonat": "^3.0.10",
9
+ "@libp2p/bootstrap": "^12",
10
+ "@libp2p/circuit-relay-v2": "^4.1.3",
11
+ "@libp2p/crypto": "^5.1.13",
12
+ "@libp2p/dcutr": "^3.0.10",
13
+ "@libp2p/gossipsub": "^15",
14
+ "@libp2p/identify": "^4",
15
+ "@libp2p/interface": "^3.1.0",
16
+ "@libp2p/kad-dht": "^16",
17
+ "@libp2p/mdns": "^12",
18
+ "@libp2p/noise": "^1",
19
+ "@libp2p/peer-id": "^6.0.4",
20
+ "@libp2p/ping": "^3.0.10",
21
+ "@libp2p/tcp": "^11",
22
+ "@libp2p/websockets": "^10",
23
+ "@libp2p/yamux": "^8",
24
+ "@multiformats/multiaddr": "^13.0.1",
25
+ "@noble/ed25519": "^3",
26
+ "@noble/hashes": "^2",
27
+ "it-length-prefixed": "^10.0.1",
28
+ "it-pipe": "^3.0.1",
29
+ "libp2p": "^3",
30
+ "protobufjs": "^8",
31
+ "rdf-canonize": "^5.0.0",
32
+ "uint8arrays": "^5"
33
+ },
34
+ "devDependencies": {
35
+ "vitest": "^3.2.4"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "license": "Apache-2.0",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/OriginTrail/dkg-v9.git",
49
+ "directory": "packages/core"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc",
53
+ "test": "vitest run",
54
+ "test:coverage": "vitest run --coverage",
55
+ "clean": "rm -rf dist tsconfig.tsbuildinfo"
56
+ }
57
+ }