arcane-os 0.3.6 → 0.4.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/CHANGELOG.md +23 -0
- package/README.md +7 -7
- package/package.json +1 -1
- package/runtime/arcane/components/chat.html +13 -5
- package/runtime/arcane/components/directory-picker.html +2 -2
- package/runtime/arcane/components/file-drop.html +1 -12
- package/runtime/arcane/components/record-timeline.html +13 -11
- package/runtime/arcane/components/relationship-board.html +6 -6
- package/runtime/arcane/components/source-code-viewer.html +23 -27
- package/runtime/arcane/modules/ComponentContracts.js +2 -4
- package/runtime/arcane/modules/DevelopmentWorkspace.js +6 -10
- package/runtime/arcane/modules/MessageAdvisory.js +16 -14
- package/runtime/arcane/modules/RecordPassageIndex.js +12 -26
- package/runtime/arcane/modules/RecordReviewStore.js +16 -3
- package/runtime/arcane/modules/ScamRiskPolicy.js +8 -7
- package/src/import-map.mjs +1 -7
- package/runtime/arcane/entities/TWiNPolicyDecision.js +0 -995
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +0 -130
- package/runtime/arcane/modules/RevocableProjectionLedger.js +0 -1623
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import {readdir,readFile,writeFile,mkdir} from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
const natural=(a,b)=>a.localeCompare(b,undefined,{numeric:true,sensitivity:'base'});
|
|
5
|
-
const stem=value=>value.replace(/\.[^.]+$/,'');
|
|
6
|
-
const safeName=value=>value.replace(/[<>:"/\\|?*\x00-\x1f]/g,' ').replace(/\s+/g,' ').trim();
|
|
7
|
-
|
|
8
|
-
function parseStructuredRecordName(name,{
|
|
9
|
-
pattern=/^(?<date>\d{2}-\d{2}-\d{2})\s+\[(?<source>[^\]]+)\]\s+-\s+(?<title>.+?)(?<extension>\.[^.]+)$/
|
|
10
|
-
}={}){
|
|
11
|
-
const match=String(name||'').match(pattern);
|
|
12
|
-
if(!match?.groups) return null;
|
|
13
|
-
const [year,month,day]=match.groups.date.split('-').map(Number);
|
|
14
|
-
const isoDate=`20${String(year).padStart(2,'0')}-${String(month).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
|
|
15
|
-
const parsedDate=new Date(`${isoDate}T00:00:00Z`);
|
|
16
|
-
const dateValid=parsedDate.getUTCFullYear()===2000+year&&parsedDate.getUTCMonth()+1===month&&parsedDate.getUTCDate()===day;
|
|
17
|
-
return {
|
|
18
|
-
dateToken:match.groups.date,
|
|
19
|
-
isoDate:dateValid?isoDate:null,
|
|
20
|
-
source:safeName(match.groups.source),
|
|
21
|
-
title:safeName(match.groups.title),
|
|
22
|
-
extension:match.groups.extension.toLowerCase()
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function nearestPageMarker(text,index,pattern=/^#{1,6}\s+(?:Page|PDF Page)\s+(\d+)\b/gim){
|
|
27
|
-
const matches=[...text.slice(0,index).matchAll(pattern)];
|
|
28
|
-
return matches.length?Number(matches.at(-1)[1]):null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function normalizeEvidenceLabel(value=''){
|
|
32
|
-
return String(value)
|
|
33
|
-
.normalize('NFKD')
|
|
34
|
-
.replace(/[\u0300-\u036f]/g,'')
|
|
35
|
-
.replace(/[\u2018\u2019']/g,'')
|
|
36
|
-
.replace(/[^a-z0-9.]+/gi,' ')
|
|
37
|
-
.trim()
|
|
38
|
-
.toLowerCase();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function evidenceLabelTargets(title=''){
|
|
42
|
-
const value=String(title||'').trim();
|
|
43
|
-
const targets=new Set([normalizeEvidenceLabel(value)].filter(Boolean));
|
|
44
|
-
const label=value.match(/\b(Exhibit|Attachment)\s+([A-Za-z0-9]+(?:\.[A-Za-z0-9]+)?)/i);
|
|
45
|
-
const qualified=/\b(?:Petitioner|Respondent)(?:['\u2019]s)?\s+(?:Exhibit|Attachment)\b/i.test(value);
|
|
46
|
-
if(label&&!qualified&&(/^[A-Za-z]{1,3}(?:\.\d+)?$/.test(label[2])||/^\d+$/.test(label[2]))){
|
|
47
|
-
targets.add(normalizeEvidenceLabel(`${label[1]} ${label[2]}`));
|
|
48
|
-
}
|
|
49
|
-
return [...targets];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function renderedPageBlocks(markdown=''){
|
|
53
|
-
const text=String(markdown||'');
|
|
54
|
-
const markerPattern=/_rendered_pages[\\/][^\r\n]*?[\\/]page-(\d+)\.png\b/gi;
|
|
55
|
-
const markers=[...text.matchAll(markerPattern)]
|
|
56
|
-
.map(match=>({page:Number(match[1]),marker:match[0],index:match.index}))
|
|
57
|
-
.filter(item=>Number.isSafeInteger(item.page)&&item.page>0);
|
|
58
|
-
return markers.map((item,index)=>({
|
|
59
|
-
...item,
|
|
60
|
-
end:markers[index+1]?.index??text.length,
|
|
61
|
-
text:text.slice(item.index,markers[index+1]?.index??text.length)
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function standaloneEvidenceLabel(line='',targets=[]){
|
|
66
|
-
let value=normalizeEvidenceLabel(line);
|
|
67
|
-
value=value.replace(/^\d{2}(?:fl|dv)\d{6}\s+/,'').replace(/^\d{1,3}\s+/,'');
|
|
68
|
-
return targets.some(target=>value===target||new RegExp(`^${target.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')}\\s+(?:page\\s+)?\\d{1,3}$`,'i').test(value));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function resolveEvidenceSourcePage(markdown='',boundaryIndex=0,title=''){
|
|
72
|
-
const pages=renderedPageBlocks(markdown);
|
|
73
|
-
const containing=pages.find(page=>boundaryIndex>=page.index&&boundaryIndex<page.end);
|
|
74
|
-
if(containing){
|
|
75
|
-
return {sourcePage:containing.page,sourcePageStatus:'resolved',sourcePageMethod:'containing-rendered-page',sourcePageMarker:containing.marker,sourcePageCandidates:[containing.page]};
|
|
76
|
-
}
|
|
77
|
-
const targets=evidenceLabelTargets(title);
|
|
78
|
-
const candidates=pages.filter(page=>page.text.split(/\r?\n/).some(line=>standaloneEvidenceLabel(line,targets)));
|
|
79
|
-
const candidatePages=[...new Set(candidates.map(item=>item.page))].sort((left,right)=>left-right);
|
|
80
|
-
if(candidatePages.length===1){
|
|
81
|
-
const page=pages.find(item=>item.page===candidatePages[0]);
|
|
82
|
-
return {sourcePage:page.page,sourcePageStatus:'resolved',sourcePageMethod:'unique-standalone-label',sourcePageMarker:page.marker,sourcePageCandidates:candidatePages};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
sourcePage:null,
|
|
86
|
-
sourcePageStatus:candidatePages.length?'ambiguous':'unresolved',
|
|
87
|
-
sourcePageMethod:candidatePages.length?'multiple-standalone-labels':null,
|
|
88
|
-
sourcePageMarker:null,
|
|
89
|
-
sourcePageCandidates:candidatePages
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function indexPairedRecord({
|
|
94
|
-
rawRoot,markdownRoot,evidenceOutputRoot,rawExtension='.pdf',
|
|
95
|
-
evidenceBoundary=/^#{2,6}\s+((?:Exhibit|Attachment)\b[^\r\n]*)/gim,
|
|
96
|
-
signalRules=[],recordIdPrefix='F',evidenceIdPrefix='E',buildEvidenceMarkdown
|
|
97
|
-
}){
|
|
98
|
-
if(!rawRoot||!markdownRoot||!evidenceOutputRoot) throw new TypeError('Raw, Markdown, and evidence output roots are required.');
|
|
99
|
-
await mkdir(evidenceOutputRoot,{recursive:true});
|
|
100
|
-
const rawNames=(await readdir(rawRoot)).filter(name=>name.toLowerCase().endsWith(rawExtension.toLowerCase())).sort(natural);
|
|
101
|
-
const markdownNames=(await readdir(markdownRoot)).filter(name=>/\.md$/i.test(name)).sort(natural);
|
|
102
|
-
const markdownByStem=new Map(markdownNames.map(name=>[stem(name).toLowerCase(),name]));
|
|
103
|
-
const records=[]; const evidence=[];
|
|
104
|
-
for(let index=0;index<rawNames.length;index++){
|
|
105
|
-
const rawName=rawNames[index]; const markdownName=markdownByStem.get(stem(rawName).toLowerCase())||null;
|
|
106
|
-
const recordEvidence=[]; let signals=[];
|
|
107
|
-
if(markdownName){
|
|
108
|
-
const markdown=await readFile(path.join(markdownRoot,markdownName),'utf8');
|
|
109
|
-
signals=signalRules.filter(rule=>rule.pattern.test(markdown)).map(rule=>rule.id);
|
|
110
|
-
evidenceBoundary.lastIndex=0;
|
|
111
|
-
const boundaries=[...markdown.matchAll(evidenceBoundary)];
|
|
112
|
-
for(let n=0;n<boundaries.length;n++){
|
|
113
|
-
const match=boundaries[n]; const body=markdown.slice(match.index,boundaries[n+1]?.index??markdown.length);
|
|
114
|
-
const id=`${evidenceIdPrefix}${String(evidence.length+1).padStart(4,'0')}`;
|
|
115
|
-
const title=safeName(match[1]); const pageResolution=resolveEvidenceSourcePage(markdown,match.index,title);
|
|
116
|
-
// Keep generated evidence paths stable and short. Descriptive source and
|
|
117
|
-
// exhibit labels remain in the record and Markdown instead of the path.
|
|
118
|
-
const fileName=`${id}.md`;
|
|
119
|
-
const item={id,title,parentRaw:rawName,markdown:markdownName,...pageResolution,file:`Evidence/MD/${fileName}`,body};
|
|
120
|
-
const output=buildEvidenceMarkdown?buildEvidenceMarkdown(item):`# ${title}\n\n- Evidence ID: ${id}\n- Parent source: ${rawName}\n- Related Markdown: ${markdownName}\n- Source page: ${item.sourcePage??'not resolved from Markdown'}\n- Source page status: ${item.sourcePageStatus}\n- Source page method: ${item.sourcePageMethod??'none'}\n- Source page marker: ${item.sourcePageMarker??'none'}\n- Source page candidates: ${item.sourcePageCandidates.join(', ')||'none'}\n\n${body}\n`;
|
|
121
|
-
await writeFile(path.join(evidenceOutputRoot,fileName),output,'utf8'); evidence.push(item); recordEvidence.push(id);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
records.push({id:`${recordIdPrefix}${String(index+1).padStart(4,'0')}`,name:rawName,markdown:markdownName,status:markdownName?'paired':'missing-markdown',signals,evidence:recordEvidence,reviewStatus:'not-reviewed'});
|
|
125
|
-
}
|
|
126
|
-
const rawStems=new Set(rawNames.map(name=>stem(name).toLowerCase()));
|
|
127
|
-
return {records,evidence,markdownNames,orphanMarkdown:markdownNames.filter(name=>!rawStems.has(stem(name).toLowerCase()))};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export {indexPairedRecord,nearestPageMarker,parseStructuredRecordName,renderedPageBlocks,resolveEvidenceSourcePage,safeName,stem};
|