@aiquants/contact-map 0.1.1

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.
@@ -0,0 +1,148 @@
1
+ interface ContactRecord {
2
+ person_id: number;
3
+ person_id_redirect_to?: number | null;
4
+ full_name: string | null;
5
+ full_name_reading?: string | null;
6
+ company_name: string | null;
7
+ company_name_reading?: string | null;
8
+ department: string | null;
9
+ title: string | null;
10
+ address?: string | null;
11
+ postal_code?: string | null;
12
+ email?: string | null;
13
+ custom_email?: string | null;
14
+ company_phone_number?: string | null;
15
+ custom_company_phone_number?: string | null;
16
+ company_fax_number?: string | null;
17
+ mobile_phone_number?: string | null;
18
+ custom_mobile_phone_number?: string | null;
19
+ url?: string | null;
20
+ contacted_at: string | Date | null;
21
+ contacted_by?: string | null;
22
+ is_in_eight?: boolean | null;
23
+ is_contact_synced?: boolean | null;
24
+ is_active: boolean;
25
+ is_incomplete?: boolean | null;
26
+ created_at?: string | Date;
27
+ created_by?: string;
28
+ updated_at?: string | Date;
29
+ updated_by?: string;
30
+ }
31
+ type ContactMapStreamPhase = "idle" | "streaming" | "retrying" | "complete" | "auth-required" | "failed";
32
+ interface ContactMapStreamState {
33
+ records: ContactRecord[];
34
+ loadedCount: number;
35
+ companyCount: number;
36
+ totalCount: number | null;
37
+ isLoading: boolean;
38
+ isComplete: boolean;
39
+ phase: ContactMapStreamPhase;
40
+ error: Error | null;
41
+ }
42
+ interface ContactMapOutletContext {
43
+ contactMapRecords: ContactRecord[];
44
+ isStreamingComplete: boolean;
45
+ isStreamingFailed: boolean;
46
+ streamPhase: ContactMapStreamPhase;
47
+ loadedCount: number;
48
+ companyCount: number;
49
+ totalCount: number | null;
50
+ errorMessage: string | null;
51
+ }
52
+ interface SearchResultEntry {
53
+ item: ContactRecord;
54
+ score?: number;
55
+ baseScore?: number;
56
+ matches?: Array<{
57
+ key: string;
58
+ indices: Array<[
59
+ number,
60
+ number
61
+ ]>;
62
+ value?: string;
63
+ }>;
64
+ }
65
+ interface ContactMapSearchOutletContext {
66
+ contactMapRecords: ContactRecord[];
67
+ filteredItems: readonly SearchResultEntry[];
68
+ searchTerm: string;
69
+ isSearching: boolean;
70
+ isIndexBuilding: boolean;
71
+ isPending: boolean;
72
+ hasSearchTerm: boolean;
73
+ enableHighlighting: boolean;
74
+ openEmailsWithGmail: boolean;
75
+ isSearchInProgress: boolean;
76
+ isSearchTabActive: boolean;
77
+ errorMessage: string | null;
78
+ isStreamingComplete: boolean;
79
+ isStreamingFailed: boolean;
80
+ }
81
+ interface IndexWorkerMetrics {
82
+ totalIndexBuilds?: number;
83
+ currentDataSize?: number;
84
+ lastIndexTime?: number;
85
+ cacheSize?: {
86
+ ngram?: number;
87
+ word?: number;
88
+ phonetic?: number;
89
+ };
90
+ indexMetadata?: Record<string, unknown>;
91
+ }
92
+ interface LevenshteinWorkerMetrics {
93
+ totalCalculations?: number;
94
+ totalDistanceCalculations?: number;
95
+ averageDistance?: number;
96
+ minDistance?: number | string;
97
+ maxDistance?: number;
98
+ partialMatches?: number;
99
+ perfectMatches?: number;
100
+ similarityThresholds?: {
101
+ above80?: number;
102
+ above60?: number;
103
+ above40?: number;
104
+ below40?: number;
105
+ };
106
+ distanceDistribution?: Record<string, number>;
107
+ queryLengthStats?: Record<string, {
108
+ count: number;
109
+ averageScore: number;
110
+ }>;
111
+ earlyTerminations?: number;
112
+ }
113
+ interface FieldPerformanceMetric {
114
+ matchCount: number;
115
+ averageScore: number;
116
+ totalScore: number;
117
+ bestScore: number;
118
+ worstScore: number;
119
+ }
120
+ interface ContactMapLogger {
121
+ info(message: string, ...args: unknown[]): void;
122
+ warn(message: string, ...args: unknown[]): void;
123
+ error(message: string, ...args: unknown[]): void;
124
+ }
125
+ declare const defaultLogger: ContactMapLogger;
126
+ type StreamCursor = {
127
+ contacted_at: string | null;
128
+ person_id: number;
129
+ };
130
+ type JsonSerialized<T> = {
131
+ [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K];
132
+ };
133
+ type SerializedContactStreamRow = JsonSerialized<ContactRecord>;
134
+ type StreamChunkWire = {
135
+ records: SerializedContactStreamRow[];
136
+ nextCursor: StreamCursor | null;
137
+ isLast: boolean;
138
+ total?: number;
139
+ };
140
+ type StreamMetadataWire = {
141
+ type: "METADATA";
142
+ total: number;
143
+ };
144
+ type StreamErrorWire = {
145
+ error: string;
146
+ };
147
+ type StreamLineWire = StreamChunkWire | StreamMetadataWire | StreamErrorWire;
148
+ export { type ContactRecord as C, type FieldPerformanceMetric as F, type IndexWorkerMetrics as I, type JsonSerialized as J, type LevenshteinWorkerMetrics as L, type SerializedContactStreamRow as S, type ContactMapStreamPhase as a, type ContactMapSearchOutletContext as b, type ContactMapOutletContext as c, type ContactMapLogger as d, type SearchResultEntry as e, type ContactMapStreamState as f, type StreamChunkWire as g, type StreamCursor as h, type StreamErrorWire as i, type StreamLineWire as j, type StreamMetadataWire as k, defaultLogger as l };
@@ -0,0 +1 @@
1
+ var S=(t,n)=>t&&t.trim()||n;var H={\u672C\u90E8:{rank:10,type:"Organization"},\u30AB\u30F3\u30D1\u30CB\u30FC:{rank:10,type:"Organization"},\u672C\u793E:{rank:10,type:"Location"},\u30D6\u30ED\u30C3\u30AF:{rank:11,type:"Location"},\u652F\u5E97:{rank:12,type:"Location"},\u7814\u7A76\u6240:{rank:15,type:"Location"},\u5DE5\u5834:{rank:15,type:"Location"},\u4E8B\u696D\u90E8:{rank:18,type:"Organization"},\u90E8\u9580:{rank:18,type:"Organization"},\u7D71\u62EC\u90E8:{rank:19,type:"Organization"},\u90E8:{rank:20,type:"Organization"},\u958B\u767A:{rank:20,type:"Organization"},\u30B0\u30EB\u30FC\u30D7:{rank:22,type:"Organization"},\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8:{rank:25,type:"Organization"},\u63A8\u9032:{rank:20,type:"Organization"},\u4E8B\u696D\u6240:{rank:25,type:"Location"},\u55B6\u696D\u6240:{rank:28,type:"Location"},\u5BA4:{rank:30,type:"Organization"},\u8AB2:{rank:30,type:"Organization"},\u30BB\u30F3\u30BF\u30FC:{rank:30,type:"Location"},\u30D3\u30B8\u30CD\u30B9\u30D7\u30ED\u30C7\u30E5\u30FC\u30B9\u30BB\u30F3\u30BF\u30FC:{rank:30,type:"Location"},\u30C1\u30FC\u30E0:{rank:40,type:"Organization"},\u4FC2:{rank:50,type:"Organization"},Department:{rank:20,type:"Organization"},Dept:{rank:20,type:"Organization"},"Dept.":{rank:20,type:"Organization"},Division:{rank:20,type:"Organization"},Div:{rank:20,type:"Organization"},"Div.":{rank:20,type:"Organization"},Section:{rank:30,type:"Organization"},Sect:{rank:30,type:"Organization"},"Sect.":{rank:30,type:"Organization"},Unit:{rank:30,type:"Organization"},Group:{rank:22,type:"Organization"},Team:{rank:40,type:"Organization"},Branch:{rank:12,type:"Location"},Office:{rank:28,type:"Location"},Center:{rank:30,type:"Location"},Centre:{rank:30,type:"Location"},HQ:{rank:10,type:"Organization"},Headquarters:{rank:10,type:"Organization"}},k=t=>{for(let[n,i]of Object.entries(H))if(t.endsWith(n))return i;return{rank:99,type:"Other"}},z=t=>k(t).rank,Q=["\u30B0\u30EB\u30FC\u30D7","\u30C1\u30FC\u30E0","Group","Team","\u5BA4","\u8AB2","Unit","Area","\u90E8","Department","Dept","Division","Div","\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8","\u63A8\u9032"],v=t=>Q.find(n=>t.endsWith(n)),X=new Set(["&","Quality","Innovation","R&D","Solutions","Development","Laboratory","and","Food","Beverage"]),J=(t,n)=>n>1&&(t.endsWith("\u652F\u5E97")||t.endsWith("\u672C\u90E8")||t.endsWith("\u4E8B\u696D\u6240")||t.endsWith("\u5DE5\u5834")),Z=(t,n)=>{if(!n)return!0;let i=k(t),r=k(n);if(i.rank<r.rank||t.endsWith("\u672C\u90E8")&&n.endsWith("\u672C\u90E8"))return!1;let p=v(t),f=v(n);return!(p&&f&&p!==f)},x=(t,n)=>{if(!t)return[];if(t==="\u517C")return["\u517C"];if(t==="\u517C\u52D9")return["\u517C"];if(t.startsWith("\u517C ")||t.startsWith("\u517C\u3000")){let r=t.slice(1).trim();return["\u517C",...x(r,n)]}if(t.startsWith("\u517C\u52D9 ")||t.startsWith("\u517C\u52D9\u3000")){let r=t.slice(2).trim();return["\u517C",...x(r,n)]}let i="";for(let r=t.length-1;r>0;r--){if(t[r]!==" ")continue;let p=t.slice(0,r);if(!X.has(p)&&n.has(p)){i=p;break}}if(i){let r=i,p=t.slice(i.length),f=/^\s+(and|&)\s+([A-Za-z0-9]+)/i,b=p.match(f);b&&(r=t.slice(0,i.length+b[0].length));let l=t.slice(r.length),e=["Department","Dept","Division","Div","Section","Sect","Unit","Group","Team","Branch","Office","Center","Centre","HQ","Headquarters"];for(let s of e){let g=new RegExp(`^\\s+${s}\\.?(?=$|[\\s,])`,"i"),c=l.match(g);if(c){r=t.slice(0,r.length+c[0].length);break}}let u=t.slice(r.length).trim();return[r,...x(u,n)]}return[t]},V=(t,n)=>{let r=t.replace(/[\s\u3000]+(?:兼務|兼)[\s\u3000]+/g,"\u3000\u517C\u3000").replace(/^(?:兼務|兼)[\s\u3000]+/,"\u517C\u3000").replace(/[\s\u3000]+(?:兼務|兼)$/,"\u3000\u517C").split(/\u3000/),p=[];for(let f of r){let b=f.trim();b&&p.push(...x(b,n))}return p},A=(t,n)=>{let i=[],r=["\u90E8","\u8AB2","\u5BA4","\u672C\u90E8","\u652F\u5E97","\u55B6\u696D\u6240","\u30B0\u30EB\u30FC\u30D7","\u30C1\u30FC\u30E0","\u30BB\u30F3\u30BF\u30FC","\u30AB\u30F3\u30D1\u30CB\u30FC","\u4E8B\u696D\u6240","\u5DE5\u5834","\u7814\u7A76\u6240","\u4FC2","\u90E8\u9580","\u4E8B\u696D\u90E8","\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8","\u63A8\u9032","\u958B\u767A","Department","Dept","Division","Div","Section","Sect","Unit","Group","Team","Branch","Office","Center","Centre","HQ","Headquarters"],p=t.split(/[\s\u3000,()()[\]]+/).filter(Boolean),f=p.length,b=new Set;for(let l=f;l>=1;l--)for(let e=0;e<=f-l;e++){let u=!1;for(let g=e;g<e+l;g++)if(b.has(g)){u=!0;break}if(u)continue;let s=p.slice(e,e+l).join(" ");if(!(s==="\u517C"||s==="\u517C\u52D9"||s==="\u4FC2"||s==="of"||s==="&"||s==="Quality"||s==="Innovation"||s==="R&D"||s==="Solutions"||s==="Development"||s==="Laboratory"||s==="and"||s==="Food"||s==="Beverage")&&n.allKnownDepartments.has(s)){i.push({name:s,index:e});for(let g=e;g<e+l;g++)b.add(g)}}for(let l=0;l<f;l++){if(b.has(l))continue;let e=p[l];for(e.endsWith("\u9577")&&e.length>1?e=e.slice(0,-1):e.endsWith("\u7D71\u62EC")&&e.length>2&&(e=e.slice(0,-2));;){let c=!1;if(e.startsWith("\u62C5\u5F53")&&e.length>2||e.startsWith("\u5C02\u4EFB")&&e.length>2||e.startsWith("\u517C\u52D9")&&e.length>2?(e=e.slice(2),c=!0):(e.startsWith("\u526F")||e.startsWith("\u517C"))&&e.length>1&&(e=e.slice(1),c=!0),!c)break}if(e.includes("\u30FB")&&!n.allKnownDepartments.has(e)){let c=e.split("\u30FB"),y=c[c.length-1],T=r.find(E=>y.endsWith(E));if(T){for(let E of c){let C=E;r.some(o=>C.endsWith(o))||(C=C+T);let P=e!=="\u517C"&&e!=="\u4FC2"&&e!=="of"&&e!=="&"&&e!=="Quality"&&e!=="Innovation"&&e!=="R&D"&&e!=="Solutions"&&e!=="Development"&&e!=="Laboratory"&&e!=="and"&&e!=="Food"&&e!=="Beverage"&&n.allKnownDepartments.has(C),h=r.some(o=>C.endsWith(o)),a=r.includes(C);(P||h&&!a)&&i.push({name:C,index:l})}continue}}let u=e!=="\u517C"&&e!=="\u4FC2"&&e!=="of"&&e!=="&"&&e!=="Quality"&&e!=="Innovation"&&e!=="R&D"&&e!=="Solutions"&&e!=="Development"&&e!=="Laboratory"&&e!=="and"&&e!=="Food"&&e!=="Beverage"&&n.allKnownDepartments.has(e),s=r.some(c=>e.endsWith(c)),g=r.includes(e);(u||s&&!g)&&i.push({name:e,index:l})}return i.sort((l,e)=>l.index-e.index),i.map(l=>l.name)},L=(t,n)=>{let i=0;for(let r of n)i<t.length&&r===t[i]&&i++;return i===t.length},_={name:"Standard",parse:(t,n,i="full")=>{let r=S(t.department,"");if(!r)return null;let p=V(r,n.allKnownDepartments),f=new Map;for(let e of p)f.set(e,(f.get(e)||0)+1);let b=e=>{let u=[],s=[];for(let g=0;g<e.length;g++){let c=e[g];if(c==="\u517C"){s.length>0&&(u.push(s),s=[]);continue}let y=k(c),T=y.rank,E=f.get(c)||0,C=J(c,E);if(s.length===0)s.push(c);else{let P=s[s.length-1],h=k(P),a=h.rank;if(s.includes(c))u.push(s),s=[c];else{let o=!1;if(C)o=!0;else if(h.type===y.type){let m=v(P),d=v(c);m&&d&&m!==d||P.endsWith("\u672C\u90E8")&&c.endsWith("\u672C\u90E8")||P.endsWith("\u90E8\u9580")&&c.endsWith("\u4E8B\u696D\u90E8")||P.endsWith("\u4E8B\u696D\u90E8")&&c.endsWith("\u90E8\u9580")||P.endsWith("\u90E8\u9580")&&c.endsWith("\u672C\u90E8")||h.type==="Other"&&y.type==="Other"?o=!0:o=a<T}else o=T>=10,o&&h.type==="Location"&&y.type==="Organization"&&a>=T&&e.slice(g+1).includes(P)&&(o=!1);o?s.push(c):(u.push(s),s=[c])}}if(C){let P=g+1<e.length?e[g+1]:void 0;Z(c,P)&&s.length>0&&(u.push(s),s=[])}}return s.length>0&&u.push(s),u},l=[];if(i==="full"&&l.push(...b(p)),i==="pure_org"){let e=p.filter(u=>k(u).type!=="Location");e.length>0&&l.push(...b(e))}if(t.title){let u=/[^\x20-\x7E]/.test(t.title)?/[\s\u3000]*(?:兼務|兼)[\s\u3000]*/:/[,;]|(?:兼務|兼)/,s=t.title.split(u).filter(Boolean),g=[],c=(h,a)=>{let o=z(h),m=z(a),d=o<m;if(!d){let D=v(h),R=v(a);if(D&&R&&D!==R)d=!0;else if(h.endsWith("\u672C\u90E8")&&a.endsWith("\u672C\u90E8"))d=!0;else if(h.endsWith("\u90E8\u9580")&&a.endsWith("\u4E8B\u696D\u90E8")||h.endsWith("\u4E8B\u696D\u90E8")&&a.endsWith("\u90E8\u9580"))d=!0;else if(h.endsWith("\u90E8\u9580")&&a.endsWith("\u672C\u90E8"))d=!0;else{let w=k(h),G=k(a);w.type==="Other"&&G.type==="Other"&&(d=!0)}}return d};for(let h of s){let a=A(h,n);if(a.length===0)continue;let o=[];for(let m of a)if(o.length===0)o.push(m);else{let d=o[o.length-1];if(c(d,m))o.push(m);else{g.push([...o]);let D=!1;for(let R=o.length-2;R>=0;R--){let w=o[R];if(c(w,m)){o=[...o.slice(0,R+1),m],D=!0;break}}D||(o=[m])}}o.length>0&&g.push(o)}let y=[...l],T=h=>s.some(a=>a.includes(h)&&a.length>h.length);for(let h of g){let a=!1;for(let o of l){let m=o.findIndex(d=>h.includes(d));if(m!==-1){let d=o[m],D=h.indexOf(d);if(m>0&&D===0){let R=o.slice(0,m);y.push([...R,...h]),a=!0}else if(D>0&&m===0){let R=h.slice(0,D);y.push([...R,...o]),y.push(h),a=!0}else h.length>o.length-m,a=!0}if(!a){let d=o[o.length-1],D=h[0];c(d,D)&&(y.push([...o,...h]),a=!0)}}a||y.push(h)}let E=[],C=[],P=new Set;for(let h of y){let a=h.join("|");P.has(a)||(P.add(a),C.push(h))}for(let h=0;h<C.length;h++){let a=C[h],o=!1;for(let m=0;m<C.length;m++){if(h===m)continue;let d=C[m];if(d.length>a.length&&d.slice(0,a.length).join("|")===a.join("|")){let D=a[a.length-1];if(!T(D)){o=!0;break}}if(d.length>a.length&&L(a,d)){let D=a[a.length-1];if(!T(D)){o=!0;break}}if(a.length===1&&d.length>0){let D=a[0],R=d[d.length-1];if(D!==R&&R.endsWith(D)){o=!0;break}}}o||E.push(a)}return E.length>0?E:null}if(l.length>0){let e=[];for(let u=0;u<l.length;u++){let s=l[u],g=!1;for(let c=0;c<l.length;c++){if(u===c)continue;let y=l[c];if(y.length>s.length&&L(s,y)){g=!0;break}}g||e.push(s)}return e.length>0?e:null}return null}},F={name:"TitleBased",parse:(t,n,i="full")=>{let r=t.title;if(!r)return null;let p=A(r,n);return p.length>0?[p]:null}};var Y="\u672A\u5206\u985E",$="\u540D\u524D\u672A\u8A2D\u5B9A",ee=[_,F],te=t=>{let n=S(t.full_name,$),i=S(t.title,"");return i.length>0?`${n} (${i})`:n},B=(t,n)=>`${t} (${n.toLocaleString()})`,W=t=>t.sort((n,i)=>n.label.localeCompare(i.label,"ja")),O=(t,n,i)=>{let r=t.get(n);if(r)return r;let p=i();return t.set(n,p),p},N=t=>encodeURIComponent(t.replace(/\//g,"\uFF0F")),q=t=>t.sort((n,i)=>{let r=!!(n.contact.title&&n.contact.title.trim().length>0),p=!!(i.contact.title&&i.contact.title.trim().length>0);return r&&!p?-1:!r&&p?1:n.label.localeCompare(i.label,"ja")}),U=t=>{let n=S(t.contact.full_name,$),i=S(t.contact.title,"");return{name:i?`${n} (${i})`:n,type:"file",absolutePath:t.path,relativePath:t.segment,personId:t.contact.person_id,title:t.contact.title||void 0,isPrimary:t.isPrimary}},K=t=>{let n=W(Array.from(t.children.values())).map(K),i=q([...t.persons]).map(U);return{name:B(t.label,t.personCount),type:"directory",absolutePath:t.path,relativePath:t.segment,children:[...i,...n]}},M=class{constructor(){this.companyMap=new Map;this.processedPersonIds=new Set;this.allDepts=new Set;this.cachedResult=null}appendRecords(n){if(!n||n.length===0)return;let i="full",r=!1;for(let p of n)if(!this.processedPersonIds.has(p.person_id)&&p.department){let f=p.department.split(/\u3000/);for(let b of f){let l=b.trim();if(!l||l==="\u517C"||l==="\u4FC2")continue;if(/[^\x20-\x7E]/.test(l)){let u=l.split(" ").filter(s=>s.length>0&&s!=="\u517C"&&s!=="\u4FC2");for(let s of u)this.allDepts.add(s)}else this.allDepts.add(l)}}for(let p of n){let f=p.person_id;if(!Number.isFinite(f)||this.processedPersonIds.has(f))continue;this.processedPersonIds.add(f),r=!0;let b=S(p.company_name,Y),l=te(p),e=`company:${N(b)}`,u=O(this.companyMap,e,()=>({label:b,segment:e,path:`/${e}`,departments:new Map,persons:[],personCount:0})),s=[],g={allKnownDepartments:this.allDepts};for(let c of ee){let y=c.parse(p,g,i);if(y&&y.length>0){s=y;break}}s.length===0&&s.push([]);for(let c=0;c<s.length;c++){let y=s[c];if(y.length===0){let a=`person:${f}`,o=`${u.path}/${a}`;u.persons.some(d=>d.contact.person_id===f)||u.persons.push({contact:p,label:l,path:o,segment:a,isPrimary:!0});continue}let T=null,E=u.path;if(y.length>0){let a=y[0],o=`department:${N(a)}`;T=O(u.departments,o,()=>({label:a,segment:o,path:`${E}/${o}`,children:new Map,persons:[],personCount:0})),E=T.path,T.personCount+=1;for(let m=1;m<y.length;m++){let d=y[m],D=`department:${N(d)}`;if(!T)throw new Error("Unexpected null currentDepartmentNode");T=O(T.children,D,()=>({label:d,segment:D,path:`${E}/${D}`,children:new Map,persons:[],personCount:0})),E=T.path,T.personCount+=1}}else continue;let C=`person:${f}`,P=`${T.path}/${C}`;T.persons.some(a=>a.contact.person_id===f)||T.persons.push({contact:p,label:l,path:P,segment:C,isPrimary:c===0})}u.personCount+=1}r&&(this.cachedResult=null)}getSnapshot(){if(this.cachedResult)return this.cachedResult;let n={},i=W(Array.from(this.companyMap.values())).map(r=>{let p=W(Array.from(r.departments.values())).map(K),f=q([...r.persons]).map(U),b={name:B(r.label,r.personCount),type:"directory",absolutePath:r.path,relativePath:r.segment,children:[...f,...p]};return n[r.label]=b,b});return this.cachedResult={tree:i,companyMap:n},this.cachedResult}append(n){this.appendRecords(n)}build(){return this.getSnapshot().tree}reset(){this.companyMap.clear(),this.processedPersonIds.clear(),this.allDepts.clear(),this.cachedResult=null}getUniquePersonCount(){return this.processedPersonIds.size}get size(){return this.processedPersonIds.size}};var ne=3e3,j=60,re=700,se=25,ie=t=>{let n=j+t/se;return Math.min(re,Math.max(j,n))};var oe={setTimeout:(t,n)=>setTimeout(t,n),clearTimeout:t=>clearTimeout(t),now:()=>Date.now()},I=class{constructor(n,i=oe){this.postMessage=n;this.timer=i;this.builder=new M;this.pendingRecords=[];this.rawRecords=[];this.activeGeneration=0;this.rebuildEpoch=0;this.lastEmitTime=0;this.emitTimerId=null;this.processTimerId=null;this.isProcessing=!1;this.finalizeRequested=!1}handleMessage(n){switch(n.type){case"RESET_TREE":{this.cancelTimers(),this.activeGeneration=n.generation,this.rebuildEpoch=Math.max(this.rebuildEpoch,n.epochBase)+1,this.builder=new M,this.pendingRecords.length=0,this.rawRecords.length=0,this.lastEmitTime=0,this.finalizeRequested=!1,this.isProcessing=!1;break}case"APPEND_RECORDS":{if(n.generation!==this.activeGeneration)return;for(let i=0;i<n.records.length;i++){let r=n.records[i];this.pendingRecords.push(r),this.rawRecords.push(r)}this.scheduleProcess();break}case"FINALIZE_TREE":{if(n.generation!==this.activeGeneration)return;this.finalizeRequested=!0,this.scheduleProcess();break}}}scheduleProcess(){this.isProcessing||this.processTimerId!==null||(this.processTimerId=this.timer.setTimeout(()=>{this.processTimerId=null,this.processStep()},0))}processStep(){this.isProcessing=!0;let n=Math.min(ne,this.pendingRecords.length);if(n>0){let r=this.pendingRecords.splice(0,n);this.builder.append(r)}if(this.pendingRecords.length>0){this.isProcessing=!1,this.scheduleProcess(),this.scheduleEmit(!1);return}if(this.finalizeRequested){this.finalizeRequested=!1,this.builder=new M,this.builder.append(this.rawRecords),this.rebuildEpoch+=1,this.isProcessing=!1,this.emitNow(!0);return}this.isProcessing=!1,this.scheduleEmit(!0)}scheduleEmit(n){if(this.emitTimerId!==null){n&&(this.timer.clearTimeout(this.emitTimerId),this.emitTimerId=null,this.emitNow(!0));return}let i=this.timer.now(),r=ie(this.builder.getUniquePersonCount()),p=i-this.lastEmitTime;if(p>=r||n)this.emitNow(n);else{let f=r-p;this.emitTimerId=this.timer.setTimeout(()=>{this.emitTimerId=null,this.emitNow(this.pendingRecords.length===0&&!this.finalizeRequested)},f)}}emitNow(n){this.lastEmitTime=this.timer.now();let i=this.builder.build();this.postMessage({type:"TREE_CHUNK",generation:this.activeGeneration,tree:i,consumedRecords:this.rawRecords.length,uniquePersonCount:this.builder.getUniquePersonCount(),isIdle:n,rebuildEpoch:this.rebuildEpoch})}cancelTimers(){this.processTimerId!==null&&(this.timer.clearTimeout(this.processTimerId),this.processTimerId=null),this.emitTimerId!==null&&(this.timer.clearTimeout(this.emitTimerId),this.emitTimerId=null)}dispose(){this.cancelTimers(),this.pendingRecords.length=0,this.rawRecords.length=0}};var ae=new I(t=>{self.postMessage(t)});self.onmessage=t=>{ae.handleMessage(t.data)};
package/package.json ADDED
@@ -0,0 +1,132 @@
1
+ {
2
+ "name": "@aiquants/contact-map",
3
+ "version": "0.1.1",
4
+ "description": "Reusable contact-map feature package: shared types/schemas, React (Router v7 / v8) UI with virtual scrolling + tree view + incremental Web Worker + fuzzy search + resize panels, and a drizzle (mssql) server layer with DI ports.",
5
+ "sideEffects": [
6
+ "**/*.css"
7
+ ],
8
+ "main": "dist/index.js",
9
+ "module": "dist/index.mjs",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.js"
16
+ },
17
+ "./client": {
18
+ "types": "./dist/client.d.ts",
19
+ "import": "./dist/client.mjs",
20
+ "require": "./dist/client.js"
21
+ },
22
+ "./server": {
23
+ "types": "./dist/server.d.ts",
24
+ "import": "./dist/server.mjs",
25
+ "require": "./dist/server.js"
26
+ },
27
+ "./styles/contact-map.standalone.css": "./dist/styles/contact-map.standalone.css",
28
+ "./worker/treeWorker": {
29
+ "import": "./dist/worker/treeWorker.mjs"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "keywords": [
38
+ "contact-map",
39
+ "react-router",
40
+ "drizzle",
41
+ "mssql",
42
+ "ndjson",
43
+ "web-worker",
44
+ "virtual-scroll",
45
+ "directory-tree",
46
+ "fuzzy-search",
47
+ "typescript"
48
+ ],
49
+ "author": "AI Quants",
50
+ "license": "MIT",
51
+ "dependencies": {
52
+ "@heroicons/react": "^2.2.0",
53
+ "@radix-ui/react-label": "^2.1.8",
54
+ "@radix-ui/react-slot": "^1.2.4",
55
+ "@radix-ui/react-tabs": "^1.1.13",
56
+ "class-variance-authority": "^0.7.1",
57
+ "clsx": "^2.1.1",
58
+ "lucide-react": "^0.561.0",
59
+ "tailwind-merge": "^3.5.0"
60
+ },
61
+ "devDependencies": {
62
+ "@aiquants/directory-tree": "^3.11.1",
63
+ "@aiquants/fuzzy-search": "^2.2.1",
64
+ "@aiquants/resize-panels": "^2.0.1",
65
+ "@aiquants/select-box": "^0.9.2",
66
+ "@aiquants/swipe-overlay": "^1.2.5",
67
+ "@aiquants/virtualscroll": "^3.5.0",
68
+ "@tailwindcss/cli": "^4.1.18",
69
+ "@testing-library/jest-dom": "^6.9.1",
70
+ "@testing-library/react": "^16.3.2",
71
+ "@types/node": "^25.9.1",
72
+ "@types/react": "^19.2.7",
73
+ "@vitest/coverage-v8": "^4.1.10",
74
+ "drizzle-orm": "1.0.0-rc.4",
75
+ "jsdom": "^27.4.0",
76
+ "react": "^19.2.7",
77
+ "react-dom": "^19.2.7",
78
+ "react-router": "^8.3.0",
79
+ "rimraf": "^6.1.2",
80
+ "tailwindcss": "^4.1.18",
81
+ "tailwindcss-animate": "^1.0.7",
82
+ "tsup": "^8.5.1",
83
+ "typescript": "^5.9.3",
84
+ "vitest": "^4.1.10",
85
+ "zod": "^3.25.76"
86
+ },
87
+ "peerDependencies": {
88
+ "@aiquants/directory-tree": "^3.11.1",
89
+ "@aiquants/fuzzy-search": "^2.2.1",
90
+ "@aiquants/resize-panels": "^2.0.1",
91
+ "@aiquants/select-box": "^0.9.2",
92
+ "@aiquants/swipe-overlay": "^1.2.5",
93
+ "@aiquants/virtualscroll": "^3.5.0",
94
+ "drizzle-orm": ">=1.0.0-beta.4 <2.0.0",
95
+ "react": ">=18",
96
+ "react-dom": ">=18",
97
+ "react-router": "^7.0.0 || ^8.0.0",
98
+ "zod": ">=3.25.0 <5.0.0"
99
+ },
100
+ "peerDependenciesMeta": {
101
+ "drizzle-orm": {
102
+ "optional": true
103
+ }
104
+ },
105
+ "engines": {
106
+ "node": ">=18.0.0",
107
+ "pnpm": ">=8.0.0"
108
+ },
109
+ "publishConfig": {
110
+ "access": "public"
111
+ },
112
+ "scripts": {
113
+ "build": "tsup && node ../../.config/scripts/strip-dts-comments.mjs dist && pnpm build:css",
114
+ "build:css": "tailwindcss -i src/styles/standalone.entry.css -o dist/styles/contact-map.standalone.css --minify",
115
+ "watch:css": "tailwindcss -i src/styles/standalone.entry.css -o dist/styles/contact-map.standalone.css --minify --watch",
116
+ "build:watch": "tsup --watch --onSuccess \"pnpm build:css\"",
117
+ "dev": "tsup --watch --onSuccess \"pnpm build:css\"",
118
+ "typecheck": "tsc --noEmit",
119
+ "clean": "rimraf dist",
120
+ "publish:patch": "pnpm run verify && pnpm version patch --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
121
+ "publish:minor": "pnpm run verify && pnpm version minor --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
122
+ "publish:major": "pnpm run verify && pnpm version major --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
123
+ "lint": "biome lint src/",
124
+ "check": "biome check src/",
125
+ "check:fix": "biome check --write src/",
126
+ "test": "vitest run",
127
+ "test:coverage": "vitest run --coverage",
128
+ "typecheck:spec": "tsc --noEmit -p tsconfig.spec.json",
129
+ "check:md": "cd ../.. && node_modules/.bin/markdownlint-cli2 \"packages/contact-map/**/*.md\"",
130
+ "verify": "node_modules/.bin/tsc --noEmit && node_modules/.bin/tsc --noEmit -p tsconfig.spec.json && ../../node_modules/.bin/biome check src/ && pnpm run check:md && node_modules/.bin/vitest run"
131
+ }
132
+ }