@private.me/xbind 1.2.0 → 1.2.2

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 (33) hide show
  1. package/README.md +13 -24
  2. package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts +4 -0
  3. package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +1 -1
  4. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  5. package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +1 -1
  6. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -83
  7. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -143
  8. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -119
  9. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -8
  10. package/dist-standalone/_deps/ux-helpers/errors.d.ts +4 -0
  11. package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +1 -1
  12. package/dist-standalone/_deps/ux-helpers/errors.js +1 -253
  13. package/dist-standalone/_deps/ux-helpers/errors.js.map +1 -1
  14. package/dist-standalone/_deps/ux-helpers/index.js +1 -16
  15. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -79
  16. package/dist-standalone/_deps/ux-helpers/progress.js +1 -138
  17. package/dist-standalone/_deps/ux-helpers/search.js +1 -116
  18. package/dist-standalone/_deps/ux-helpers/types.js +1 -7
  19. package/dist-standalone/agent.d.ts +9 -4
  20. package/dist-standalone/agent.js +20 -4
  21. package/dist-standalone/cjs/agent.js +20 -4
  22. package/dist-standalone/cjs/correlation-id.js +339 -0
  23. package/dist-standalone/cjs/http-status-map.js +571 -0
  24. package/dist-standalone/cjs/index.js +14 -1
  25. package/dist-standalone/cjs/types/error-response.js +56 -0
  26. package/dist-standalone/correlation-id.d.ts +222 -0
  27. package/dist-standalone/correlation-id.js +326 -0
  28. package/dist-standalone/http-status-map.d.ts +136 -0
  29. package/dist-standalone/http-status-map.js +561 -0
  30. package/dist-standalone/index.d.ts +2 -0
  31. package/dist-standalone/index.js +1 -0
  32. package/package.json +2 -2
  33. package/share1.dat +0 -0
@@ -282,13 +282,18 @@ export declare class Agent {
282
282
  * Create an Agent from a deterministic 32-byte seed.
283
283
  *
284
284
  * Uses HKDF-SHA256 to derive Ed25519 + X25519 keys from the seed.
285
- * The same seed always produces the same DID and keys. Skips registry
286
- * registration — the caller is responsible for registering externally.
285
+ * The same seed always produces the same DID and keys.
287
286
  *
288
- * Ideal for IoT: factory burns seed → field deploys → boot derives identity.
287
+ * **Just-in-Time Registration (JITR):** Automatically registers identity
288
+ * with the trust registry on first use. Follows industry standards (AWS IoT
289
+ * JITR, OAuth DCR, MCP 2025 spec) for zero-config onboarding. Idempotent:
290
+ * safe to call multiple times (ignores ALREADY_REGISTERED errors).
291
+ *
292
+ * Ideal for IoT, servers, AI agents: zero-config deployment with automatic
293
+ * registration on first connection.
289
294
  *
290
295
  * @param seed - Exactly 32 bytes of high-entropy key material.
291
- * @param opts - Agent options (registry, transport, etc.).
296
+ * @param opts - Agent options (registry, transport, scopes, etc.).
292
297
  * @returns Agent instance or error.
293
298
  */
294
299
  static fromSeed(seed: Uint8Array, opts: Omit<AgentCreateOptions, 'name'> & {
@@ -197,19 +197,35 @@ export class Agent {
197
197
  * Create an Agent from a deterministic 32-byte seed.
198
198
  *
199
199
  * Uses HKDF-SHA256 to derive Ed25519 + X25519 keys from the seed.
200
- * The same seed always produces the same DID and keys. Skips registry
201
- * registration — the caller is responsible for registering externally.
200
+ * The same seed always produces the same DID and keys.
202
201
  *
203
- * Ideal for IoT: factory burns seed → field deploys → boot derives identity.
202
+ * **Just-in-Time Registration (JITR):** Automatically registers identity
203
+ * with the trust registry on first use. Follows industry standards (AWS IoT
204
+ * JITR, OAuth DCR, MCP 2025 spec) for zero-config onboarding. Idempotent:
205
+ * safe to call multiple times (ignores ALREADY_REGISTERED errors).
206
+ *
207
+ * Ideal for IoT, servers, AI agents: zero-config deployment with automatic
208
+ * registration on first connection.
204
209
  *
205
210
  * @param seed - Exactly 32 bytes of high-entropy key material.
206
- * @param opts - Agent options (registry, transport, etc.).
211
+ * @param opts - Agent options (registry, transport, scopes, etc.).
207
212
  * @returns Agent instance or error.
208
213
  */
209
214
  static async fromSeed(seed, opts) {
210
215
  const idResult = await identityFromSeed(seed, { postQuantumSig: opts.postQuantumSig });
211
216
  if (!idResult.ok)
212
217
  return err('IDENTITY_FAILED:KEYGEN');
218
+ // JITR: Auto-register with trust registry (zero-config onboarding)
219
+ // Includes all keys: Ed25519 (signing), X25519 (encryption), ML-KEM, ML-DSA
220
+ const regResult = await opts.registry.register(idResult.value.did, idResult.value.rawPublicKey, opts.name ?? idResult.value.did, opts.scopes, idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, opts.xchange ?? false);
221
+ // Idempotent: Ignore ALREADY_REGISTERED (agent may have registered before)
222
+ // Fail on other errors (network issues, invalid data, etc.)
223
+ if (!regResult.ok && regResult.error !== 'ALREADY_REGISTERED') {
224
+ const sub = regResult.error === 'NETWORK_ERROR'
225
+ ? 'REGISTRATION_FAILED:NETWORK_ERROR'
226
+ : 'REGISTRATION_FAILED';
227
+ return err(sub);
228
+ }
213
229
  const nonceStore = opts.nonceStore ?? new MemoryNonceStore();
214
230
  const timestampWindowMs = opts.timestampWindowMs ?? TIMESTAMP_WINDOW_MS;
215
231
  const transports = Array.isArray(opts.transport) ? opts.transport : [opts.transport];
@@ -235,19 +235,35 @@ class Agent {
235
235
  * Create an Agent from a deterministic 32-byte seed.
236
236
  *
237
237
  * Uses HKDF-SHA256 to derive Ed25519 + X25519 keys from the seed.
238
- * The same seed always produces the same DID and keys. Skips registry
239
- * registration — the caller is responsible for registering externally.
238
+ * The same seed always produces the same DID and keys.
240
239
  *
241
- * Ideal for IoT: factory burns seed → field deploys → boot derives identity.
240
+ * **Just-in-Time Registration (JITR):** Automatically registers identity
241
+ * with the trust registry on first use. Follows industry standards (AWS IoT
242
+ * JITR, OAuth DCR, MCP 2025 spec) for zero-config onboarding. Idempotent:
243
+ * safe to call multiple times (ignores ALREADY_REGISTERED errors).
244
+ *
245
+ * Ideal for IoT, servers, AI agents: zero-config deployment with automatic
246
+ * registration on first connection.
242
247
  *
243
248
  * @param seed - Exactly 32 bytes of high-entropy key material.
244
- * @param opts - Agent options (registry, transport, etc.).
249
+ * @param opts - Agent options (registry, transport, scopes, etc.).
245
250
  * @returns Agent instance or error.
246
251
  */
247
252
  static async fromSeed(seed, opts) {
248
253
  const idResult = await (0, identity_js_1.identityFromSeed)(seed, { postQuantumSig: opts.postQuantumSig });
249
254
  if (!idResult.ok)
250
255
  return (0, shared_1.err)('IDENTITY_FAILED:KEYGEN');
256
+ // JITR: Auto-register with trust registry (zero-config onboarding)
257
+ // Includes all keys: Ed25519 (signing), X25519 (encryption), ML-KEM, ML-DSA
258
+ const regResult = await opts.registry.register(idResult.value.did, idResult.value.rawPublicKey, opts.name ?? idResult.value.did, opts.scopes, idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, opts.xchange ?? false);
259
+ // Idempotent: Ignore ALREADY_REGISTERED (agent may have registered before)
260
+ // Fail on other errors (network issues, invalid data, etc.)
261
+ if (!regResult.ok && regResult.error !== 'ALREADY_REGISTERED') {
262
+ const sub = regResult.error === 'NETWORK_ERROR'
263
+ ? 'REGISTRATION_FAILED:NETWORK_ERROR'
264
+ : 'REGISTRATION_FAILED';
265
+ return (0, shared_1.err)(sub);
266
+ }
251
267
  const nonceStore = opts.nonceStore ?? new nonce_store_js_1.MemoryNonceStore();
252
268
  const timestampWindowMs = opts.timestampWindowMs ?? TIMESTAMP_WINDOW_MS;
253
269
  const transports = Array.isArray(opts.transport) ? opts.transport : [opts.transport];
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ /**
3
+ * @module correlation-id
4
+ * Client-side correlation ID utilities for request tracking
5
+ *
6
+ * Correlation IDs enable distributed tracing across xBind agent operations,
7
+ * making it easier to debug issues, track requests across microservices,
8
+ * and correlate logs between client and server.
9
+ *
10
+ * Format: `req_{timestamp}_{random}`
11
+ * Example: `req_1716234567890_a3f5c9d2`
12
+ *
13
+ * Usage:
14
+ * ```typescript
15
+ * import { generateCorrelationId, attachCorrelationId } from '@private.me/xbind';
16
+ *
17
+ * // Generate a new correlation ID
18
+ * const id = generateCorrelationId();
19
+ *
20
+ * // Attach to request headers
21
+ * const headers = attachCorrelationId(new Headers(), id);
22
+ *
23
+ * // Validate format
24
+ * if (validateCorrelationId(id)) {
25
+ * console.log('Valid correlation ID');
26
+ * }
27
+ * ```
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.CORRELATION_ID_ALIASES = exports.CORRELATION_ID_HEADER = void 0;
31
+ exports.generateCorrelationId = generateCorrelationId;
32
+ exports.validateCorrelationId = validateCorrelationId;
33
+ exports.parseCorrelationId = parseCorrelationId;
34
+ exports.attachCorrelationId = attachCorrelationId;
35
+ exports.extractCorrelationId = extractCorrelationId;
36
+ exports.getOrCreateCorrelationId = getOrCreateCorrelationId;
37
+ exports.createCorrelationIdFromTimestamp = createCorrelationIdFromTimestamp;
38
+ exports.getCorrelationIdAge = getCorrelationIdAge;
39
+ exports.isCorrelationIdExpired = isCorrelationIdExpired;
40
+ exports.correlationIdMiddleware = correlationIdMiddleware;
41
+ /**
42
+ * Standard header name for correlation ID
43
+ */
44
+ exports.CORRELATION_ID_HEADER = 'X-Correlation-ID';
45
+ /**
46
+ * Alternative header names for compatibility
47
+ */
48
+ exports.CORRELATION_ID_ALIASES = [
49
+ 'X-Request-ID',
50
+ 'X-Trace-ID',
51
+ 'X-Transaction-ID',
52
+ ];
53
+ /**
54
+ * Regular expression for validating correlation ID format
55
+ */
56
+ const CORRELATION_ID_PATTERN = /^req_\d{13}_[a-f0-9]{8}$/;
57
+ /**
58
+ * Generate a new correlation ID
59
+ *
60
+ * Format: `req_{timestamp}_{random}`
61
+ * - `req`: Static prefix for identification
62
+ * - `timestamp`: Unix timestamp in milliseconds (13 digits)
63
+ * - `random`: 8-character hex string for uniqueness
64
+ *
65
+ * @returns New correlation ID string
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const id = generateCorrelationId();
70
+ * // => "req_1716234567890_a3f5c9d2"
71
+ * ```
72
+ */
73
+ function generateCorrelationId() {
74
+ const timestamp = Date.now();
75
+ const random = generateRandomHex(8);
76
+ return `req_${timestamp}_${random}`;
77
+ }
78
+ /**
79
+ * Validate correlation ID format
80
+ *
81
+ * Checks if the provided string matches the expected correlation ID format.
82
+ *
83
+ * @param id - String to validate
84
+ * @returns True if valid, false otherwise
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * validateCorrelationId('req_1716234567890_a3f5c9d2'); // => true
89
+ * validateCorrelationId('invalid'); // => false
90
+ * validateCorrelationId('req_123_abc'); // => false (wrong lengths)
91
+ * ```
92
+ */
93
+ function validateCorrelationId(id) {
94
+ if (typeof id !== 'string')
95
+ return false;
96
+ return CORRELATION_ID_PATTERN.test(id);
97
+ }
98
+ /**
99
+ * Parse correlation ID into components
100
+ *
101
+ * Extracts the timestamp and random components from a correlation ID.
102
+ *
103
+ * @param id - Correlation ID to parse
104
+ * @returns Parsed components or null if invalid
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const spec = parseCorrelationId('req_1716234567890_a3f5c9d2');
109
+ * // => { prefix: 'req', timestamp: 1716234567890, random: 'a3f5c9d2' }
110
+ * ```
111
+ */
112
+ function parseCorrelationId(id) {
113
+ if (!validateCorrelationId(id))
114
+ return null;
115
+ const parts = id.split('_');
116
+ return {
117
+ prefix: parts[0] ?? 'req',
118
+ timestamp: parseInt(parts[1] ?? '0', 10),
119
+ random: parts[2] ?? '',
120
+ };
121
+ }
122
+ /**
123
+ * Attach correlation ID to request headers
124
+ *
125
+ * Adds the correlation ID to the X-Correlation-ID header.
126
+ * If no ID is provided, generates a new one.
127
+ * Compatible with both Headers API and plain objects.
128
+ *
129
+ * @param headers - Headers object or plain object
130
+ * @param id - Correlation ID (generates new if not provided)
131
+ * @returns Updated headers object
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // With Headers API
136
+ * const headers = new Headers();
137
+ * attachCorrelationId(headers);
138
+ *
139
+ * // With plain object
140
+ * const headers = { 'Content-Type': 'application/json' };
141
+ * attachCorrelationId(headers, 'req_1716234567890_a3f5c9d2');
142
+ *
143
+ * // With existing correlation ID
144
+ * const id = generateCorrelationId();
145
+ * attachCorrelationId(headers, id);
146
+ * ```
147
+ */
148
+ function attachCorrelationId(headers, id) {
149
+ const correlationId = id ?? generateCorrelationId();
150
+ if (!validateCorrelationId(correlationId)) {
151
+ throw new Error(`Invalid correlation ID format: ${correlationId}. Expected format: req_{timestamp}_{random}`);
152
+ }
153
+ if (headers instanceof Headers) {
154
+ headers.set(exports.CORRELATION_ID_HEADER, correlationId);
155
+ }
156
+ else {
157
+ headers[exports.CORRELATION_ID_HEADER] = correlationId;
158
+ }
159
+ return headers;
160
+ }
161
+ /**
162
+ * Extract correlation ID from request headers
163
+ *
164
+ * Checks the standard header and common aliases.
165
+ *
166
+ * @param headers - Headers object or plain object
167
+ * @returns Correlation ID if found, undefined otherwise
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const headers = new Headers({
172
+ * 'X-Correlation-ID': 'req_1716234567890_a3f5c9d2'
173
+ * });
174
+ * const id = extractCorrelationId(headers);
175
+ * // => 'req_1716234567890_a3f5c9d2'
176
+ * ```
177
+ */
178
+ function extractCorrelationId(headers) {
179
+ // Check primary header
180
+ const primary = headers instanceof Headers
181
+ ? headers.get(exports.CORRELATION_ID_HEADER)
182
+ : headers[exports.CORRELATION_ID_HEADER];
183
+ if (primary && validateCorrelationId(primary)) {
184
+ return primary;
185
+ }
186
+ // Check aliases
187
+ for (const alias of exports.CORRELATION_ID_ALIASES) {
188
+ const value = headers instanceof Headers ? headers.get(alias) : headers[alias];
189
+ if (value && validateCorrelationId(value)) {
190
+ return value;
191
+ }
192
+ }
193
+ return undefined;
194
+ }
195
+ /**
196
+ * Get or create correlation ID from headers
197
+ *
198
+ * Returns existing correlation ID from headers, or generates a new one if not found.
199
+ * Does NOT modify the input headers.
200
+ *
201
+ * @param headers - Headers to check
202
+ * @returns Existing or new correlation ID
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const headers = new Headers();
207
+ * const id = getOrCreateCorrelationId(headers);
208
+ * // => Generates new ID if not found in headers
209
+ * ```
210
+ */
211
+ function getOrCreateCorrelationId(headers) {
212
+ if (!headers)
213
+ return generateCorrelationId();
214
+ const existing = extractCorrelationId(headers);
215
+ return existing ?? generateCorrelationId();
216
+ }
217
+ /**
218
+ * Create a correlation ID from a timestamp
219
+ *
220
+ * Useful for testing or when you need deterministic IDs.
221
+ *
222
+ * @param timestamp - Unix timestamp in milliseconds
223
+ * @param random - Optional random component (generates if not provided)
224
+ * @returns Correlation ID
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * const id = createCorrelationIdFromTimestamp(1716234567890);
229
+ * // => 'req_1716234567890_a3f5c9d2'
230
+ *
231
+ * const deterministicId = createCorrelationIdFromTimestamp(1716234567890, 'aaaaaaaa');
232
+ * // => 'req_1716234567890_aaaaaaaa'
233
+ * ```
234
+ */
235
+ function createCorrelationIdFromTimestamp(timestamp, random) {
236
+ const randomComponent = random ?? generateRandomHex(8);
237
+ if (!/^[a-f0-9]{8}$/.test(randomComponent)) {
238
+ throw new Error(`Invalid random component: ${randomComponent}. Expected 8 hex characters`);
239
+ }
240
+ return `req_${timestamp}_${randomComponent}`;
241
+ }
242
+ /**
243
+ * Calculate age of correlation ID in milliseconds
244
+ *
245
+ * @param id - Correlation ID
246
+ * @returns Age in milliseconds, or null if invalid
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const id = generateCorrelationId();
251
+ * setTimeout(() => {
252
+ * const age = getCorrelationIdAge(id);
253
+ * console.log(`Request age: ${age}ms`);
254
+ * }, 1000);
255
+ * ```
256
+ */
257
+ function getCorrelationIdAge(id) {
258
+ const spec = parseCorrelationId(id);
259
+ if (!spec)
260
+ return null;
261
+ const now = Date.now();
262
+ return now - spec.timestamp;
263
+ }
264
+ /**
265
+ * Check if correlation ID is expired
266
+ *
267
+ * @param id - Correlation ID
268
+ * @param maxAgeMs - Maximum age in milliseconds (default: 5 minutes)
269
+ * @returns True if expired, false otherwise
270
+ *
271
+ * @example
272
+ * ```typescript
273
+ * const id = generateCorrelationId();
274
+ * if (isCorrelationIdExpired(id, 60000)) {
275
+ * console.log('Request older than 1 minute');
276
+ * }
277
+ * ```
278
+ */
279
+ function isCorrelationIdExpired(id, maxAgeMs = 5 * 60 * 1000) {
280
+ const age = getCorrelationIdAge(id);
281
+ return age !== null && age > maxAgeMs;
282
+ }
283
+ /**
284
+ * Generate random hex string
285
+ *
286
+ * Uses Web Crypto API in browser, Node.js crypto in Node.
287
+ * Falls back to Math.random() if neither available (NOT cryptographically secure).
288
+ *
289
+ * @param length - Number of hex characters to generate
290
+ * @returns Random hex string
291
+ *
292
+ * @internal
293
+ */
294
+ function generateRandomHex(length) {
295
+ const bytes = Math.ceil(length / 2);
296
+ // Try Web Crypto API (browser)
297
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
298
+ const buffer = new Uint8Array(bytes);
299
+ crypto.getRandomValues(buffer);
300
+ return Array.from(buffer)
301
+ .map((b) => b.toString(16).padStart(2, '0'))
302
+ .join('')
303
+ .substring(0, length);
304
+ }
305
+ // Try Node.js crypto
306
+ try {
307
+ const nodeCrypto = require('node:crypto');
308
+ return nodeCrypto.randomBytes(bytes).toString('hex').substring(0, length);
309
+ }
310
+ catch {
311
+ // SECURITY: Never fall back to Math.random() in production (OWASP violation)
312
+ // Correlation IDs must be cryptographically random to prevent enumeration attacks
313
+ throw new Error('Cryptographic random generation unavailable. ' +
314
+ 'Install crypto polyfill or use environment with crypto support.');
315
+ }
316
+ }
317
+ /**
318
+ * Middleware helper for Express/Koa-style frameworks
319
+ *
320
+ * Automatically attaches correlation ID to incoming requests.
321
+ *
322
+ * @example
323
+ * ```typescript
324
+ * import express from 'express';
325
+ * import { correlationIdMiddleware } from '@private.me/xbind';
326
+ *
327
+ * const app = express();
328
+ * app.use(correlationIdMiddleware());
329
+ * ```
330
+ */
331
+ function correlationIdMiddleware() {
332
+ return (req, res, next) => {
333
+ const id = getOrCreateCorrelationId(req.headers);
334
+ req.correlationId = id;
335
+ res.setHeader(exports.CORRELATION_ID_HEADER, id);
336
+ if (typeof next === 'function')
337
+ next();
338
+ };
339
+ }