@privacyscrubber/sdk 2.4.7 → 2.4.8

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/index.js CHANGED
@@ -41,7 +41,7 @@ export const SDK_VERSION = (() => {
41
41
  return pkgJson.version;
42
42
  }
43
43
  } catch (_) {}
44
- return '2.4.7';
44
+ return '2.4.8';
45
45
  })();
46
46
 
47
47
  /**
@@ -134,7 +134,7 @@ export function validateLicenseKey(key) {
134
134
  * @param {Record<string, string>|string[]} currentTokenMap
135
135
  * @returns {object}
136
136
  */
137
- export function buildAuditTelemetry(currentTokenMap = {}) {
137
+ export function buildAuditTelemetry(currentTokenMap = {}, extraStats = {}) {
138
138
  const entities = {};
139
139
  let totalCount = 0;
140
140
 
@@ -244,12 +244,31 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
244
244
  }
245
245
  ];
246
246
 
247
+ const rawLicenseTier = String(extraStats?.licenseTier || '').trim().toUpperCase();
248
+ const isOrganizationalScope = ['TEAMS', 'SDK', 'OEM', 'ENTERPRISE', 'ENTERPRISE_AIRGAP'].includes(rawLicenseTier);
249
+ const auditScope = isOrganizationalScope ? {
250
+ tier: rawLicenseTier,
251
+ isOrganizational: true,
252
+ badge: 'ORGANIZATIONAL MULTI-SEAT COMPLIANCE SCOPE',
253
+ coverage: 'Covers department, clinic, or firm personnel under unified ZTDS policy governance.',
254
+ upgradeRecommended: false,
255
+ upgradeUrl: null
256
+ } : {
257
+ tier: rawLicenseTier || 'SINGLE_OPERATOR',
258
+ isOrganizational: false,
259
+ badge: 'SINGLE-OPERATOR PERSONAL SCOPE',
260
+ coverage: 'Valid for individual workstation only. Multi-employee clinics, CPA firms, and departments require TEAMS ($99/mo flat, unlimited seats) for organizational audit coverage.',
261
+ upgradeRecommended: true,
262
+ upgradeUrl: 'https://privacyscrubber.com/pricing/?tier=teams&utm_source=audit_receipt&utm_medium=scope_upsell'
263
+ };
264
+
247
265
  return {
248
266
  totalCount,
249
267
  entities,
250
268
  types,
251
269
  riskLevel,
252
270
  frameworksList: Array.from(frameworksSet),
271
+ auditScope,
253
272
  statutoryCitations,
254
273
  ipHoldings,
255
274
  agenticProtocol,
@@ -303,6 +322,19 @@ export function _resetCommunityNoticeForTesting() {
303
322
  communityNoticeLogged = false;
304
323
  }
305
324
 
325
+ export const FREE_PROD_SPECIALIZED_CALL_LIMIT = 250;
326
+ let _freeProdSpecializedCalls = 0;
327
+ let _prodProfileFallbackNoticeLogged = false;
328
+
329
+ /**
330
+ * Resets the production specialized call quota state for testing purposes.
331
+ * @private
332
+ */
333
+ export function _resetProdQuotaForTesting() {
334
+ _freeProdSpecializedCalls = 0;
335
+ _prodProfileFallbackNoticeLogged = false;
336
+ }
337
+
306
338
  let devSecretNoticeLogged = false;
307
339
 
308
340
  function emitDevSecretNoticeIfNeeded(isPro, tokenMap = {}, limit = 15000) {
@@ -483,6 +515,82 @@ We request approval to acquire a **Developer SDK License** (\$299/month flat or
483
515
  * auditReceipt: string
484
516
  * }}
485
517
  */
518
+ const SDK_VERTICAL_DETECTORS = [
519
+ { profile: 'medical', label: 'Medical PHI (HIPAA)', regex: /\b(?:Patient\s*(?:Name|ID|DOB|Record)|MRN\b|Medical\s+Record|Attending\s+Physician|Chief\s+Complaint|Diagnosis\s*:|ICD-10|HIPAA|Clinical\s+Intake|Discharge\s+Summary|MedPod|NPI\s*(?:#|No|:)?\s*\d{10}|Medicare\s*(?:ID|Beneficiary|Claim)|Medicaid\s*ID|Prescription|Rx\s*#|Blood\s+Type|Vital\s+Signs|Radiology|Lab\s+Results|EHR\b|EMR\b)\b/gi },
520
+ { profile: 'accounting', label: 'Tax & Accounting', regex: /\b(?:Form\s*1040|Form\s*W-2|Form\s*1099|Adjusted\s+Gross\s+Income|Employer\s+Identification\s+Number|Schedule\s+C\b|Steuer-ID|USt-IdNr|Tax\s+Return|General\s+Ledger|Withholding\s+Allowance|Total\s+Tax\s+Liability)\b/gi },
521
+ { profile: 'wealthmgmt', label: 'Wealth Management', regex: /\b(?:Revocable\s+Living\s+Trust|Irrevocable\s+(?:Generation-Skipping\s+)?Trust|Grantor\s*:|Settlor\s*:|Trustee\s*:|Beneficiary\s+Designation|Roth\s+IRA|Asset\s+Allocation|FBO\b|CUSIP\s*:|Grantor\s+Retained\s+Annuity\s+Trust|AUM\s*:)\b/gi },
522
+ { profile: 'legal', label: 'Legal & Contracts', regex: /\b(?:Non-Disclosure\s+Agreement|Attorney-Client\s+Privilege|Docket\s+No|Hereinafter\s+referred|Indemnification\s+Clause|Governing\s+Law\s+and\s+Jurisdiction|Plaintiff\b|Defendant\b|Subpoena\b|Deposition\b|State\s+Bar\s*#)\b/gi },
523
+ { profile: 'finance', label: 'Finance & Banking', regex: /\b(?:Wire\s+Transfer\s+Authorization|SWIFT\s*\/?\s*BIC\s*:|IBAN\s*:\s*[A-Z]{2}\d{2}|ABA\s+Routing|ACH\s+Transfer|PCI-DSS|Primary\s+Account\s+Number)\b/gi },
524
+ { profile: 'hr', label: 'HR & Resumes', regex: /\b(?:Employee\s*ID|Performance\s+Improvement\s+Plan|Payroll\s+Register|Gross\s+Pay\b|Net\s+Pay\b|Candidate\s+Evaluation|Compensation\s+Band|Sozialversicherungsnummer|Personalnummer|EEID\s*:)\b/gi },
525
+ { profile: 'realestate', label: 'Real Estate', regex: /\b(?:Assessor\s+Parcel\s+Number|APN\s*:|MLS\s*#|Closing\s+Disclosure|Escrow\s+(?:Account|Number|Officer)|Property\s+Deed|Title\s+Insurance|Lease\s+Application)\b/gi },
526
+ { profile: 'dev', label: 'API & Dev Logs', regex: /\b(?:BEGIN\s+(?:RSA|OPENSSH|EC|PRIVATE)\s+PRIVATE\s+KEY|AWS_SECRET_ACCESS_KEY|DATABASE_URL\s*=|GITHUB_TOKEN\s*=|Bearer\s+eyJ[A-Za-z0-9_-]+)\b/g },
527
+ { profile: 'sales', label: 'Sales & CRM', regex: /\b(?:CRM\s+(?:Opportunity|Export)|HubSpot\b|Salesforce\b|MEDDPICC\b|Primary\s+Buyer\s*:|MSA\s+Redlines|Deal\s+Stage|Pipeline\s+Forecast|Opportunity\s+ID|Account\s+Executive)\b/gi },
528
+ { profile: 'engineering', label: 'DevOps & Infra', regex: /\b(?:Incident\s+Postmortem|Kubeconfig|Kubernetes\s+Cluster|Helm\s+Chart|Terraform\s+State|DMAIC\b|Bill\s+of\s+Materials|BOM\s+Spec|CNC\s+Machining|Shop\s+Floor\s+Batch)\b/gi },
529
+ { profile: 'insurance', label: 'Insurance & Claims', regex: /\b(?:Claim\s+Settlement|Policyholder\s*:|Policy\s*#?POL-|Claim\s+Number|Adjuster\s*:|Loss\s+Location|Loss\s+Run|Subrogation\b|FNOL\b|Deductible\s*:)\b/gi },
530
+ { profile: 'pharma', label: 'Pharma & Trials', regex: /\b(?:Clinical\s+Trial(?:\s+Protocol)?|NCT\d{8}\b|IRB\s+Protocol|FDA\s+1572|Adverse\s+Event|Pharmacovigilance|Principal\s+Investigator|Cohort\s+[A-Z]\b|Blinded\s+Study|CDISC\b|IND\s*(?:#|Protocol))\b/gi },
531
+ { profile: 'underwriting', label: 'Loan Underwriting & Income', regex: /\b(?:Loan\s+Underwriting|Mortgage\s+Appraisal|Loan\s+Application\s+#?LOS|FICO\s*:\s*\d{3}|Appraised\s+Value\s*:|80%\s+LTV|Monthly\s+Qualifying\s*:|YTD\s+Earnings|Borrower\s*:)\b/gi },
532
+ { profile: 'security', label: 'Security & SIEM', regex: /\b(?:SIEM\s+Alert|CVE-\d{4}-\d{4,7}\b|Threat\s*:\s*CRITICAL|Unauthorized\s+S3\s+bucket|SOC\s+Incident|Firewall\s+Rule\s+Violation|Intrusion\s+Detection)\b/gi },
533
+ { profile: 'support', label: 'Support & Tickets', regex: /\b(?:Support\s+Ticket|Ticket\s*#INC-|Zendesk\b|Intercom\b|Billing\s+Discrepancy|SLA\s+Escalation|CSAT\s+Score|Customer\s+Support\s+Escalation)\b/gi },
534
+ { profile: 'compliance', label: 'Compliance Audit', regex: /\b(?:Suspicious\s+Activity\s+Report|FinCEN\s+SAR|BSA\s+Officer|31\s+CFR\b|ROPA\s+Register|Data\s+Protection\s+Impact\s+Assessment|SOC\s*2\s+Type\s+II|ISO\s*27001\s+Audit|CMMC\s+Level|ITAR\s+Controlled)\b/gi },
535
+ { profile: 'ccpa', label: 'CCPA / CPRA Privacy', regex: /\b(?:CCPA\s*\/?\s*CPRA|1798\.105|Right\s+to\s+Delete|Opt-out\s+of\s+Sale|Ticket\s*#?DSAR-)\b/gi },
536
+ { profile: 'bizops', label: 'Biz Ops & M&A', regex: /\b(?:Vendor\s+Master\s+Agreement|Bill\s+of\s+Lading|Customs\s+Declaration|Purchase\s+Order\s*#?PO-|Supply\s+Chain\s+Manifest)\b/gi },
537
+ { profile: 'biotech', label: 'Biotech & Genomics', regex: /\b(?:Sequencing\s+Platform|Illumina\s+NovaSeq|Flowcell\s*:|FastQ\s+Sample|ClinVar\b|HGVS\s+Variant|Genomic\s+Sequencing|Bioinformatics\s+Intake|BRCA[12]\s+c\.)\b/gi },
538
+ { profile: 'automotive', label: 'Automotive & EV', regex: /\b(?:Connected\s+Vehicle\s+Telematics|ECU\s+Serial|Telematics\s+Device|ASIL-D\b|DTC\s+P\d{4}\b|CAN-Bus\s+Log)\b/gi },
539
+ { profile: 'energy', label: 'Energy & Smart Grid', regex: /\b(?:Critical\s+Infrastructure\s+SCADA|Substation\s+ID|Feeder\s+ID|Smart\s+Meter\s*:|ESI\s+ID\s*:|NERC\s+CIP|BES\s+Cyber\s+Asset)\b/gi },
540
+ { profile: 'hospitality', label: 'Hospitality & Travel', regex: /\b(?:Airline\s+Passenger\s+Dossier|PNR\s*:\s*[A-Z0-9]{6}|e-Ticket\s*:|Boarding\s+Pass\s*:|Known\s+Traveler\s+KTN|Guest\s+Folio|Marriott\s+Bonvoy|SkyMiles)\b/gi },
541
+ { profile: 'telecom', label: 'Telecom & Call Center', regex: /\b(?:Telecom\s+Subscriber\s+CDR|MSISDN\s*:|IMSI\s*:\s*\d{14,15}|IMEI\s*:\s*\d{14,16}|Cell\s+Tower\s+ID|SIP\s+Signaling)\b/gi },
542
+ { profile: 'academic', label: 'Academic & Research', regex: /\b(?:FERPA\s+Student\s+Record|Student\s+ID\s*:\s*#?STU-|Disciplinary\s+Record|IEP\s+Evaluation|IRB\s+Approval)\b/gi },
543
+ { profile: 'agents', label: 'AI Agents & Prompts', regex: /\b(?:Autonomous\s+Financial\s+Procurement\s+Agent|AgentID\s*:\s*agt_|Encrypted\s+Tool\s+Token|Agent\s+Memory\s*>)\b/gi }
544
+ ];
545
+
546
+ /**
547
+ * Automatically detects the most relevant industry compliance profile from text content.
548
+ * Executes in <0.05ms in local RAM with zero network egress.
549
+ *
550
+ * @param {string} text - Input document or prompt text.
551
+ * @returns {{
552
+ * detectedProfile: string,
553
+ * label: string,
554
+ * confidence: 'high'|'medium'|'low',
555
+ * score: number,
556
+ * alternatives: Array<{ profile: string, label: string, score: number }>
557
+ * }}
558
+ */
559
+ export function detectProfile(text) {
560
+ if (!text || typeof text !== 'string') {
561
+ return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
562
+ }
563
+ const sampleSlice = text.length > 16384 ? text.slice(0, 16384) : text;
564
+ const matches = [];
565
+ for (let i = 0; i < SDK_VERTICAL_DETECTORS.length; i++) {
566
+ const c = SDK_VERTICAL_DETECTORS[i];
567
+ c.regex.lastIndex = 0;
568
+ const found = sampleSlice.match(c.regex);
569
+ if (found && found.length > 0) {
570
+ matches.push({
571
+ profile: c.profile,
572
+ label: c.label,
573
+ score: found.length,
574
+ priority: i
575
+ });
576
+ }
577
+ }
578
+ if (matches.length === 0) {
579
+ return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
580
+ }
581
+ matches.sort((a, b) => (b.score !== a.score) ? (b.score - a.score) : (a.priority - b.priority));
582
+ const primary = matches[0];
583
+ const alternatives = matches.slice(1, 4).map(({ profile, label, score }) => ({ profile, label, score }));
584
+ const confidence = primary.score >= 3 ? 'high' : (primary.score >= 2 ? 'medium' : 'low');
585
+ return {
586
+ detectedProfile: primary.profile,
587
+ label: primary.label,
588
+ confidence,
589
+ score: primary.score,
590
+ alternatives
591
+ };
592
+ }
593
+
486
594
  export function sanitize(text, options = {}) {
487
595
  if (!text || typeof text !== 'string') {
488
596
  return {
@@ -501,7 +609,7 @@ export function sanitize(text, options = {}) {
501
609
  }
502
610
 
503
611
  const {
504
- profile = 'General',
612
+ profile: rawProfileOption = 'General',
505
613
  licenseKey = (typeof process !== 'undefined' && (process.env?.PRIVACYSCRUBBER_KEY || process.env?.PRIVACYSCRUBBER_LICENSE_KEY)) || '',
506
614
  customRules = [],
507
615
  tokenLabelMap = {},
@@ -513,6 +621,12 @@ export function sanitize(text, options = {}) {
513
621
  ignoreList = null
514
622
  } = options;
515
623
 
624
+ const isAutoProfile = Boolean(
625
+ (typeof rawProfileOption === 'string' && rawProfileOption.trim().toLowerCase() === 'auto') ||
626
+ options.autoDetectProfile === true
627
+ );
628
+ const autoDetected = isAutoProfile ? detectProfile(text) : null;
629
+
516
630
  const licenseCheck = validateLicense(licenseKey);
517
631
  const rawTier = licenseCheck.valid ? (licenseCheck.tier || 'PRO') : 'FREE';
518
632
  const isExpiredKey = !licenseCheck.valid && typeof licenseCheck.error === 'string' && /expired/i.test(licenseCheck.error);
@@ -534,6 +648,21 @@ export function sanitize(text, options = {}) {
534
648
  const isSdkTier = licenseCheck.valid && ['SDK', 'OEM', 'ENTERPRISE'].includes(rawTier);
535
649
  const isSeatTier = licenseCheck.valid && ['PRO', 'TEAMS'].includes(rawTier);
536
650
 
651
+ const isPro = isSdkTier;
652
+ const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
653
+
654
+ let autoSuggestedOnly = false;
655
+ let profile = rawProfileOption;
656
+ if (isAutoProfile && autoDetected) {
657
+ if (!isPro && text.length > 5000 && text.length <= 15000 && autoDetected.detectedProfile !== 'general') {
658
+ // Non-blocking PLG fallback: keep General for Free 5k-15k texts so auto-mode never breaks free calls
659
+ profile = 'General';
660
+ autoSuggestedOnly = true;
661
+ } else {
662
+ profile = autoDetected.detectedProfile;
663
+ }
664
+ }
665
+
537
666
  if (isSeatTier) {
538
667
  const keyPreview = licenseKey ? `${licenseKey.slice(0, 12)}...` : '';
539
668
  const errorMsg = `[PrivacyScrubber] License tier mismatch: ${rawTier} license (${keyPreview}) is licensed strictly for human seats (Web App & Chrome Extension). Backend automation, microservices, and CRM pipelines require a Developer SDK license ($299/mo). Upgrade: https://privacyscrubber.com/pricing?tier=sdk&src=seat_tier_mismatch`;
@@ -547,9 +676,6 @@ export function sanitize(text, options = {}) {
547
676
  }
548
677
  }
549
678
 
550
- const isPro = isSdkTier;
551
- const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
552
-
553
679
  emitCommunityNoticeIfNeeded(isPro);
554
680
 
555
681
  const isSpecialized = profile && profile.toLowerCase() !== 'general';
@@ -583,8 +709,33 @@ export function sanitize(text, options = {}) {
583
709
  });
584
710
 
585
711
  // Auto-inject DevOps Secrets rules when in Dev profile or when detectSecrets is requested
586
- const activeRules = [...safeCustomRules];
587
- if (profile.toLowerCase() === 'dev' || detectSecrets === true) {
712
+ const isProdEnv = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
713
+ const hasCustomEnterpriseConfig = isSpecialized || safeCustomRules.length > 0 || (Array.isArray(ignoreList) && ignoreList.length > 0);
714
+ let profileFallback = false;
715
+ let effectiveProfile = profile;
716
+ let effectiveCustomRules = safeCustomRules;
717
+ let effectiveIgnoreList = ignoreList;
718
+
719
+ if (!isPro && isProdEnv && hasCustomEnterpriseConfig) {
720
+ _freeProdSpecializedCalls++;
721
+ if (_freeProdSpecializedCalls > FREE_PROD_SPECIALIZED_CALL_LIMIT) {
722
+ profileFallback = true;
723
+ effectiveProfile = 'General';
724
+ effectiveCustomRules = [];
725
+ effectiveIgnoreList = null;
726
+ if (!_prodProfileFallbackNoticeLogged && typeof process !== 'undefined' && process.stderr && typeof process.stderr.write === 'function') {
727
+ _prodProfileFallbackNoticeLogged = true;
728
+ process.stderr.write(
729
+ `\n[@privacyscrubber/sdk] Free Production Quota (${FREE_PROD_SPECIALIZED_CALL_LIMIT} specialized calls) reached for profile '${profile}'.\n` +
730
+ `[@privacyscrubber/sdk] Gracefully falling back to 'General' profile (Core PII remains protected, 0 downtime).\n` +
731
+ `[@privacyscrubber/sdk] Unlock unlimited specialized profiles, custom rules & ignoreLists in production ($299/mo): https://privacyscrubber.com/pricing?tier=sdk&src=prod_profile_fallback (7-Day Trial: npx @privacyscrubber/sdk trial)\n\n`
732
+ );
733
+ }
734
+ }
735
+ }
736
+
737
+ const activeRules = [...effectiveCustomRules];
738
+ if (effectiveProfile.toLowerCase() === 'dev' || detectSecrets === true) {
588
739
  DEVOPS_SECRETS_DETECTOR.forEach(rule => {
589
740
  activeRules.push({
590
741
  name: rule.name,
@@ -605,10 +756,10 @@ export function sanitize(text, options = {}) {
605
756
  textToScrub,
606
757
  activeRules,
607
758
  tokenLabelMap,
608
- profile,
759
+ effectiveProfile,
609
760
  existingSessionMap,
610
761
  isPro,
611
- ignoreList
762
+ effectiveIgnoreList
612
763
  );
613
764
 
614
765
  const endTime = (typeof performance !== 'undefined' && typeof performance.now === 'function')
@@ -616,7 +767,7 @@ export function sanitize(text, options = {}) {
616
767
  : Date.now();
617
768
 
618
769
  const executionMs = Math.round((endTime - startTime) * 100) / 100;
619
- const telemetry = buildAuditTelemetry(rawResult.tokenMap || {});
770
+ const telemetry = buildAuditTelemetry(rawResult.tokenMap || {}, { licenseTier: tier });
620
771
  const auditReceipt = generateReceipt ? formatAuditReceipt(telemetry) : '';
621
772
 
622
773
  emitDevSecretNoticeIfNeeded(isPro, rawResult.tokenMap, limit);
@@ -644,6 +795,10 @@ export function sanitize(text, options = {}) {
644
795
  limit: limit,
645
796
  wasTruncated: wasTruncated,
646
797
  truncated: wasTruncated,
798
+ profileFallback: profileFallback,
799
+ effectiveProfile: effectiveProfile,
800
+ specializedCallsUsed: _freeProdSpecializedCalls,
801
+ specializedCallsLimit: isPro ? Infinity : FREE_PROD_SPECIALIZED_CALL_LIMIT,
647
802
  ...(isSeatTier && {
648
803
  upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
649
804
  trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
@@ -651,12 +806,20 @@ export function sanitize(text, options = {}) {
651
806
  recommendation: `Your ${rawTier} license is valid for interactive browser seats only. Upgrade to Developer SDK ($299/mo) for unlimited backend server throughput: https://privacyscrubber.com/pricing?tier=sdk`
652
807
  }),
653
808
  ...(!isPro && !isSeatTier && {
654
- upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=dev_upsell`,
655
- trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=dev_upsell`,
809
+ upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
810
+ trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
656
811
  trialCommand: 'npx @privacyscrubber/sdk trial',
657
812
  recommendation: 'Upgrade to Developer SDK ($299/mo) for unlimited backend text processing, custom regex rules, and specialized compliance profiles.'
658
813
  })
659
814
  },
815
+ profileDetection: {
816
+ autoDetected: isAutoProfile,
817
+ detectedProfile: autoDetected ? autoDetected.detectedProfile : String(effectiveProfile || 'General').toLowerCase(),
818
+ label: autoDetected ? autoDetected.label : effectiveProfile,
819
+ confidence: autoDetected ? autoDetected.confidence : 'high',
820
+ suggestedOnly: autoSuggestedOnly,
821
+ alternatives: autoDetected ? autoDetected.alternatives : []
822
+ },
660
823
  telemetry: telemetry,
661
824
  auditReceipt: auditReceipt
662
825
  };
@@ -3888,6 +4051,7 @@ import {
3888
4051
 
3889
4052
  export default {
3890
4053
  SDK_VERSION,
4054
+ detectProfile,
3891
4055
  sanitize,
3892
4056
  sanitizeObject,
3893
4057
  scrub,
@@ -3940,6 +4104,8 @@ export default {
3940
4104
  scrubText,
3941
4105
  unscrubText,
3942
4106
  DEVOPS_SECRETS_DETECTOR,
4107
+ FREE_PROD_SPECIALIZED_CALL_LIMIT,
4108
+ _resetProdQuotaForTesting,
3943
4109
  stripExifMetadata,
3944
4110
  redactImageBoundingBoxes,
3945
4111
  generateSvgRedactionOverlay
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privacyscrubber/sdk",
3
- "version": "2.4.7",
3
+ "version": "2.4.8",
4
4
  "description": "In-process consumer privacy defense and pre-emptive PII interception SDK for Node.js, Next.js, and AI pipelines. Zero data loss, zero third parties, sub-millisecond in-memory execution.",
5
5
  "main": "index.cjs",
6
6
  "module": "index.js",