@elisym/sdk 0.6.0 → 0.7.0

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.
package/dist/index.d.cts CHANGED
@@ -685,6 +685,82 @@ declare class BoundedSet<T> {
685
685
  add(item: T): void;
686
686
  }
687
687
 
688
+ /**
689
+ * Sliding-window rate limiter keyed by an arbitrary string (typically a
690
+ * customer pubkey). Each key gets at most `maxPerWindow` requests inside a
691
+ * rolling `windowMs`. Stale timestamps are pruned lazily on every `check`.
692
+ * When the tracked-key set grows past `maxKeys`, the least-recently-used
693
+ * key is evicted so an attacker cannot exhaust memory by cycling keys.
694
+ *
695
+ * Thread-safety: not required. Designed for single-threaded JS consumers
696
+ * (Node/Bun event loops, browser main thread). No timers - pruning happens
697
+ * inside `check` and `prune`.
698
+ */
699
+ interface SlidingWindowLimiterOptions {
700
+ /** Rolling window width, in ms. */
701
+ windowMs: number;
702
+ /** Max hits allowed per key inside the window. */
703
+ maxPerWindow: number;
704
+ /** Cap on total tracked keys. LRU-evicted past this cap. */
705
+ maxKeys: number;
706
+ }
707
+ interface RateLimitDecision {
708
+ allowed: boolean;
709
+ /** Wall-clock timestamp (ms) when the limit window will reset for this key. */
710
+ resetAt: number;
711
+ /** Number of hits inside the current window after this call (or the attempted hit if denied). */
712
+ count: number;
713
+ }
714
+ interface SlidingWindowLimiter {
715
+ /** Record a hit against `key`; return whether it was allowed. */
716
+ check(key: string, now?: number): RateLimitDecision;
717
+ /**
718
+ * Inspect the current state for `key` without recording a hit. Useful
719
+ * when a single request must clear multiple limiters and callers want
720
+ * to avoid double-counting against one limiter after another denies.
721
+ */
722
+ peek(key: string, now?: number): RateLimitDecision;
723
+ /** Drop entries whose windows have fully elapsed. Bounded memory hygiene. */
724
+ prune(now?: number): void;
725
+ /** Current number of tracked keys. */
726
+ size(): number;
727
+ /** Clear all state. */
728
+ reset(): void;
729
+ }
730
+ declare function createSlidingWindowLimiter(options: SlidingWindowLimiterOptions): SlidingWindowLimiter;
731
+
732
+ /**
733
+ * Canonical pino redact paths for the elisym stack. Plugin, CLI, MCP, and
734
+ * any downstream integrator should consume these arrays directly - one
735
+ * source of truth for "fields that carry user input or secrets".
736
+ *
737
+ * Wire them into a pino instance like:
738
+ *
739
+ * import pino from 'pino';
740
+ * import { DEFAULT_REDACT_PATHS, makeCensor } from '@elisym/sdk';
741
+ * const logger = pino({
742
+ * redact: { paths: DEFAULT_REDACT_PATHS, censor: makeCensor() },
743
+ * });
744
+ */
745
+ /**
746
+ * Field paths that carry Nostr / Solana secret keys or operator secrets.
747
+ * Censored as `[REDACTED]`.
748
+ */
749
+ declare const SECRET_REDACT_PATHS: string[];
750
+ /**
751
+ * Field paths that carry customer-confidential text (LLM prompts, raw
752
+ * event content, job input). Censored as `[INPUT REDACTED]` so log
753
+ * readers can distinguish redacted input from redacted secrets.
754
+ */
755
+ declare const INPUT_REDACT_PATHS: string[];
756
+ /**
757
+ * Union of the two arrays, in the order pino's redact engine should
758
+ * visit them. Prefer this when wiring a new logger so no downstream
759
+ * consumer forgets half the set.
760
+ */
761
+ declare const DEFAULT_REDACT_PATHS: string[];
762
+ declare function makeCensor(): (value: unknown, path: string[]) => string;
763
+
688
764
  declare const RELAYS: string[];
689
765
  declare const KIND_APP_HANDLER = 31990;
690
766
  declare const KIND_JOB_REQUEST_BASE = 5000;
@@ -760,4 +836,4 @@ declare const LIMITS: {
760
836
  readonly MAX_CAPABILITY_LENGTH: 64;
761
837
  };
762
838
 
763
- export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type Signer, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
839
+ export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
package/dist/index.d.ts CHANGED
@@ -685,6 +685,82 @@ declare class BoundedSet<T> {
685
685
  add(item: T): void;
686
686
  }
687
687
 
688
+ /**
689
+ * Sliding-window rate limiter keyed by an arbitrary string (typically a
690
+ * customer pubkey). Each key gets at most `maxPerWindow` requests inside a
691
+ * rolling `windowMs`. Stale timestamps are pruned lazily on every `check`.
692
+ * When the tracked-key set grows past `maxKeys`, the least-recently-used
693
+ * key is evicted so an attacker cannot exhaust memory by cycling keys.
694
+ *
695
+ * Thread-safety: not required. Designed for single-threaded JS consumers
696
+ * (Node/Bun event loops, browser main thread). No timers - pruning happens
697
+ * inside `check` and `prune`.
698
+ */
699
+ interface SlidingWindowLimiterOptions {
700
+ /** Rolling window width, in ms. */
701
+ windowMs: number;
702
+ /** Max hits allowed per key inside the window. */
703
+ maxPerWindow: number;
704
+ /** Cap on total tracked keys. LRU-evicted past this cap. */
705
+ maxKeys: number;
706
+ }
707
+ interface RateLimitDecision {
708
+ allowed: boolean;
709
+ /** Wall-clock timestamp (ms) when the limit window will reset for this key. */
710
+ resetAt: number;
711
+ /** Number of hits inside the current window after this call (or the attempted hit if denied). */
712
+ count: number;
713
+ }
714
+ interface SlidingWindowLimiter {
715
+ /** Record a hit against `key`; return whether it was allowed. */
716
+ check(key: string, now?: number): RateLimitDecision;
717
+ /**
718
+ * Inspect the current state for `key` without recording a hit. Useful
719
+ * when a single request must clear multiple limiters and callers want
720
+ * to avoid double-counting against one limiter after another denies.
721
+ */
722
+ peek(key: string, now?: number): RateLimitDecision;
723
+ /** Drop entries whose windows have fully elapsed. Bounded memory hygiene. */
724
+ prune(now?: number): void;
725
+ /** Current number of tracked keys. */
726
+ size(): number;
727
+ /** Clear all state. */
728
+ reset(): void;
729
+ }
730
+ declare function createSlidingWindowLimiter(options: SlidingWindowLimiterOptions): SlidingWindowLimiter;
731
+
732
+ /**
733
+ * Canonical pino redact paths for the elisym stack. Plugin, CLI, MCP, and
734
+ * any downstream integrator should consume these arrays directly - one
735
+ * source of truth for "fields that carry user input or secrets".
736
+ *
737
+ * Wire them into a pino instance like:
738
+ *
739
+ * import pino from 'pino';
740
+ * import { DEFAULT_REDACT_PATHS, makeCensor } from '@elisym/sdk';
741
+ * const logger = pino({
742
+ * redact: { paths: DEFAULT_REDACT_PATHS, censor: makeCensor() },
743
+ * });
744
+ */
745
+ /**
746
+ * Field paths that carry Nostr / Solana secret keys or operator secrets.
747
+ * Censored as `[REDACTED]`.
748
+ */
749
+ declare const SECRET_REDACT_PATHS: string[];
750
+ /**
751
+ * Field paths that carry customer-confidential text (LLM prompts, raw
752
+ * event content, job input). Censored as `[INPUT REDACTED]` so log
753
+ * readers can distinguish redacted input from redacted secrets.
754
+ */
755
+ declare const INPUT_REDACT_PATHS: string[];
756
+ /**
757
+ * Union of the two arrays, in the order pino's redact engine should
758
+ * visit them. Prefer this when wiring a new logger so no downstream
759
+ * consumer forgets half the set.
760
+ */
761
+ declare const DEFAULT_REDACT_PATHS: string[];
762
+ declare function makeCensor(): (value: unknown, path: string[]) => string;
763
+
688
764
  declare const RELAYS: string[];
689
765
  declare const KIND_APP_HANDLER = 31990;
690
766
  declare const KIND_JOB_REQUEST_BASE = 5000;
@@ -760,4 +836,4 @@ declare const LIMITS: {
760
836
  readonly MAX_CAPABILITY_LENGTH: 64;
761
837
  };
762
838
 
763
- export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type Signer, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
839
+ export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
package/dist/index.js CHANGED
@@ -2387,6 +2387,141 @@ function validateAgentName(name) {
2387
2387
  }
2388
2388
  }
2389
2389
 
2390
- export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DiscoveryService, ElisymClient, ElisymIdentity, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SolanaPaymentStrategy, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
2390
+ // src/primitives/rateLimiter.ts
2391
+ function createSlidingWindowLimiter(options) {
2392
+ const { windowMs, maxPerWindow, maxKeys } = options;
2393
+ if (windowMs <= 0) {
2394
+ throw new RangeError("windowMs must be > 0");
2395
+ }
2396
+ if (maxPerWindow <= 0) {
2397
+ throw new RangeError("maxPerWindow must be > 0");
2398
+ }
2399
+ if (maxKeys <= 0) {
2400
+ throw new RangeError("maxKeys must be > 0");
2401
+ }
2402
+ const entries = /* @__PURE__ */ new Map();
2403
+ function evictIfNeeded() {
2404
+ while (entries.size > maxKeys) {
2405
+ const oldestKey = entries.keys().next().value;
2406
+ if (oldestKey === void 0) {
2407
+ return;
2408
+ }
2409
+ entries.delete(oldestKey);
2410
+ }
2411
+ }
2412
+ return {
2413
+ peek(key, now = Date.now()) {
2414
+ const entry = entries.get(key);
2415
+ if (!entry) {
2416
+ return { allowed: true, resetAt: now + windowMs, count: 0 };
2417
+ }
2418
+ const cutoff = now - windowMs;
2419
+ const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
2420
+ return {
2421
+ allowed: fresh.length < maxPerWindow,
2422
+ resetAt: (fresh[0] ?? now) + windowMs,
2423
+ count: fresh.length
2424
+ };
2425
+ },
2426
+ check(key, now = Date.now()) {
2427
+ const entry = entries.get(key) ?? { hits: [] };
2428
+ const cutoff = now - windowMs;
2429
+ const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
2430
+ if (fresh.length >= maxPerWindow) {
2431
+ entries.delete(key);
2432
+ entries.set(key, { hits: fresh });
2433
+ return {
2434
+ allowed: false,
2435
+ resetAt: (fresh[0] ?? now) + windowMs,
2436
+ count: fresh.length
2437
+ };
2438
+ }
2439
+ fresh.push(now);
2440
+ entries.delete(key);
2441
+ entries.set(key, { hits: fresh });
2442
+ evictIfNeeded();
2443
+ return {
2444
+ allowed: true,
2445
+ resetAt: (fresh[0] ?? now) + windowMs,
2446
+ count: fresh.length
2447
+ };
2448
+ },
2449
+ prune(now = Date.now()) {
2450
+ const cutoff = now - windowMs;
2451
+ for (const [key, entry] of entries) {
2452
+ const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
2453
+ if (fresh.length === 0) {
2454
+ entries.delete(key);
2455
+ } else if (fresh.length !== entry.hits.length) {
2456
+ entry.hits = fresh;
2457
+ }
2458
+ }
2459
+ },
2460
+ size() {
2461
+ return entries.size;
2462
+ },
2463
+ reset() {
2464
+ entries.clear();
2465
+ }
2466
+ };
2467
+ }
2468
+
2469
+ // src/primitives/logRedact.ts
2470
+ var SECRET_REDACT_PATHS = [
2471
+ "*.ELISYM_NOSTR_PRIVATE_KEY",
2472
+ "*.ELISYM_SOLANA_PRIVATE_KEY",
2473
+ "*.nostrPrivateKeyHex",
2474
+ "*.solanaPrivateKeyBase58",
2475
+ "*.secretKey",
2476
+ "*.secret",
2477
+ "ELISYM_NOSTR_PRIVATE_KEY",
2478
+ "ELISYM_SOLANA_PRIVATE_KEY",
2479
+ // Canonical on-disk `.secrets.json` field names. Logging the whole
2480
+ // `secrets` object, or any single field directly, must not leak.
2481
+ "llm_api_key",
2482
+ "nostr_secret_key",
2483
+ "solana_secret_key",
2484
+ "*.llm_api_key",
2485
+ "*.nostr_secret_key",
2486
+ "*.solana_secret_key",
2487
+ "secrets",
2488
+ "*.secrets"
2489
+ ];
2490
+ var INPUT_REDACT_PATHS = [
2491
+ "content",
2492
+ "input",
2493
+ "prompt",
2494
+ "*.content",
2495
+ "*.input",
2496
+ "*.prompt",
2497
+ "event.content",
2498
+ "*.event.content",
2499
+ // JobLedger entries carry the raw Nostr event JSON (which embeds
2500
+ // `event.content`) and the full LLM `resultContent` - both are
2501
+ // customer-confidential and must never land in a structured log.
2502
+ "rawEventJson",
2503
+ "resultContent",
2504
+ "*.rawEventJson",
2505
+ "*.resultContent"
2506
+ ];
2507
+ var DEFAULT_REDACT_PATHS = [...SECRET_REDACT_PATHS, ...INPUT_REDACT_PATHS];
2508
+ var INPUT_REDACT_LEAVES = /* @__PURE__ */ new Set([
2509
+ "content",
2510
+ "input",
2511
+ "prompt",
2512
+ "rawEventJson",
2513
+ "resultContent"
2514
+ ]);
2515
+ function makeCensor() {
2516
+ return (_value, path) => {
2517
+ const last = path[path.length - 1];
2518
+ if (last !== void 0 && INPUT_REDACT_LEAVES.has(last)) {
2519
+ return "[INPUT REDACTED]";
2520
+ }
2521
+ return "[REDACTED]";
2522
+ };
2523
+ }
2524
+
2525
+ export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SolanaPaymentStrategy, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
2391
2526
  //# sourceMappingURL=index.js.map
2392
2527
  //# sourceMappingURL=index.js.map