@modusoperandi/licit-import-utils 0.1.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/LICENSE +21 -0
- package/capco.util.d.ts +38 -0
- package/capco.util.js +195 -0
- package/index.d.ts +8 -0
- package/index.js +8 -0
- package/licit-elements.d.ts +878 -0
- package/licit-elements.js +2588 -0
- package/licit-transform.d.ts +360 -0
- package/licit-transform.js +2197 -0
- package/package.json +52 -0
- package/transform.docx.d.ts +16 -0
- package/transform.docx.js +154 -0
- package/transform.utils.d.ts +17 -0
- package/transform.utils.js +155 -0
- package/transform.zip.d.ts +5 -0
- package/transform.zip.js +296 -0
- package/types.d.ts +9 -0
- package/types.js +5 -0
- package/zip.utils.d.ts +6 -0
- package/zip.utils.js +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Modus Operandi Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/capco.util.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* @copyright Copyright 2026 Modus Operandi Inc. All Rights Reserved.
|
|
4
|
+
*/
|
|
5
|
+
export interface Capco {
|
|
6
|
+
portionMarking?: string;
|
|
7
|
+
ism: ISM;
|
|
8
|
+
}
|
|
9
|
+
interface ISM {
|
|
10
|
+
system: string;
|
|
11
|
+
classification: string;
|
|
12
|
+
joint?: boolean;
|
|
13
|
+
ownerProducer?: string[];
|
|
14
|
+
atomicEnergyMarkings?: string[];
|
|
15
|
+
declassExceptions?: string[];
|
|
16
|
+
displayOnlyTo?: string[];
|
|
17
|
+
disseminationControls?: string[];
|
|
18
|
+
fgiSourceOpen?: string[];
|
|
19
|
+
fgiSourceProtected?: string[];
|
|
20
|
+
nonICMarkings?: string[];
|
|
21
|
+
nonUSControls?: string[];
|
|
22
|
+
releasableTo?: string[];
|
|
23
|
+
sciControls?: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface UpdatedCapco {
|
|
26
|
+
containsCapco: boolean;
|
|
27
|
+
capco: Capco;
|
|
28
|
+
updatedTextContent: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function updateCapcoFromContent(element: Element): UpdatedCapco | undefined;
|
|
31
|
+
export declare function getShortCapcoString(capcoName: string): string;
|
|
32
|
+
export declare function getCapcoNames(): string[];
|
|
33
|
+
export declare function getCapcoNameFromShortString(capcoShort: string): string | undefined;
|
|
34
|
+
export declare function getCapcoObject(capcoString: string): Capco;
|
|
35
|
+
export declare function getCapcoFromNode(node: HTMLElement): string | null | undefined;
|
|
36
|
+
export declare function safeCapcoParse(capco: unknown, fallback?: Capco): Capco;
|
|
37
|
+
export declare function removeCapcoTextFromNode(node: Node): void;
|
|
38
|
+
export {};
|
package/capco.util.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* @copyright Copyright 2026 Modus Operandi Inc. All Rights Reserved.
|
|
4
|
+
*/
|
|
5
|
+
// System Capcos list.
|
|
6
|
+
const systemCapcos = ['TBD', 'U', 'C', 'S', 'TS', 'CUI', 'FOUO'];
|
|
7
|
+
const capcoMap = {
|
|
8
|
+
'TO BE DETERMINED': 'TBD',
|
|
9
|
+
UNCLASSIFIED: 'U',
|
|
10
|
+
CONFIDENTIAL: 'C',
|
|
11
|
+
SECRET: 'S',
|
|
12
|
+
'TOP SECRET': 'TS',
|
|
13
|
+
'CONTROLLED UNCLASSIFIED INFORMATION': 'CUI',
|
|
14
|
+
'FOR OFFICIAL USE ONLY': 'FOUO',
|
|
15
|
+
};
|
|
16
|
+
const UNCLASSIFIED = 'U';
|
|
17
|
+
export function updateCapcoFromContent(element) {
|
|
18
|
+
const capco = {
|
|
19
|
+
ism: {
|
|
20
|
+
classification: UNCLASSIFIED,
|
|
21
|
+
system: 'USA',
|
|
22
|
+
ownerProducer: [],
|
|
23
|
+
sciControls: [],
|
|
24
|
+
atomicEnergyMarkings: [],
|
|
25
|
+
disseminationControls: [],
|
|
26
|
+
},
|
|
27
|
+
portionMarking: 'U',
|
|
28
|
+
};
|
|
29
|
+
const textContent = element.textContent?.trimStart();
|
|
30
|
+
const updatedTextContent = textContent;
|
|
31
|
+
// Handle various CAPCO combinations
|
|
32
|
+
if (textContent) {
|
|
33
|
+
const upperText = textContent.toUpperCase();
|
|
34
|
+
// Check for (U) or (CUI) only - remove text
|
|
35
|
+
const simpleResult = handleUAndCuiCapcos(textContent, upperText, capco);
|
|
36
|
+
if (simpleResult) {
|
|
37
|
+
return simpleResult;
|
|
38
|
+
}
|
|
39
|
+
// Check if starts with ( and contains //
|
|
40
|
+
const doubleSlashResult = handleCapcoCombinations(upperText, updatedTextContent, capco);
|
|
41
|
+
if (doubleSlashResult) {
|
|
42
|
+
return doubleSlashResult;
|
|
43
|
+
}
|
|
44
|
+
// Check for full form without // - exact match with capcoMap values
|
|
45
|
+
const fullFormResult = handleFullFormCapco(upperText, updatedTextContent, capco);
|
|
46
|
+
if (fullFormResult) {
|
|
47
|
+
return fullFormResult;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Handle (U) and (CUI) CAPCOs
|
|
52
|
+
function handleUAndCuiCapcos(textContent, upperText, capco) {
|
|
53
|
+
if (upperText.startsWith('(U)') || upperText.startsWith('(CUI)')) {
|
|
54
|
+
const isU = upperText.startsWith('(U)');
|
|
55
|
+
const marker = isU ? '(U)' : '(CUI)';
|
|
56
|
+
const portionMark = isU ? 'U' : 'CUI';
|
|
57
|
+
capco.ism.classification = UNCLASSIFIED;
|
|
58
|
+
capco.portionMarking = portionMark;
|
|
59
|
+
// Remove the CAPCO text
|
|
60
|
+
const updatedTextContent = textContent.slice(marker.length).trimStart();
|
|
61
|
+
return { containsCapco: true, capco, updatedTextContent };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// Handle CAPCO combinations with //
|
|
66
|
+
function handleCapcoCombinations(upperText, updatedTextContent, capco) {
|
|
67
|
+
if (upperText.startsWith('(') && upperText.includes('//')) {
|
|
68
|
+
const closingParenIndex = upperText.indexOf(')');
|
|
69
|
+
if (closingParenIndex > 0) {
|
|
70
|
+
const insideBracket = upperText.substring(1, closingParenIndex);
|
|
71
|
+
// Check if it starts with any systemCapco letter followed by //
|
|
72
|
+
for (const systemCapco of systemCapcos) {
|
|
73
|
+
if (insideBracket.startsWith(`${systemCapco}//`)) {
|
|
74
|
+
capco.ism.classification = UNCLASSIFIED;
|
|
75
|
+
capco.portionMarking = 'TBD';
|
|
76
|
+
// Keep the original text as is
|
|
77
|
+
return { containsCapco: true, capco, updatedTextContent };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check if it starts with any capcoMap full form followed by //
|
|
81
|
+
const capcoMapResult = checkCapcoMap(insideBracket, capco, updatedTextContent);
|
|
82
|
+
if (capcoMapResult) {
|
|
83
|
+
return capcoMapResult;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// Check capcoMap full form with //
|
|
90
|
+
function checkCapcoMap(insideBracket, capco, updatedTextContent) {
|
|
91
|
+
for (const [fullForm] of Object.entries(capcoMap)) {
|
|
92
|
+
if (insideBracket.startsWith(`${fullForm}//`)) {
|
|
93
|
+
capco.ism.classification = UNCLASSIFIED;
|
|
94
|
+
capco.portionMarking = 'TBD';
|
|
95
|
+
// Keep the original text as is
|
|
96
|
+
return { containsCapco: true, capco, updatedTextContent };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Full form CAPCO without //
|
|
102
|
+
function handleFullFormCapco(upperText, updatedTextContent, capco) {
|
|
103
|
+
if (upperText.startsWith('(')) {
|
|
104
|
+
const closingParenIndex = upperText.indexOf(')');
|
|
105
|
+
if (closingParenIndex > 0) {
|
|
106
|
+
const insideBracket = upperText.substring(1, closingParenIndex).trim();
|
|
107
|
+
// Check if it exactly matches any capcoMap full form
|
|
108
|
+
for (const [fullForm] of Object.entries(capcoMap)) {
|
|
109
|
+
if (insideBracket === fullForm) {
|
|
110
|
+
capco.ism.classification = UNCLASSIFIED;
|
|
111
|
+
capco.portionMarking = 'TBD';
|
|
112
|
+
// Keep the original text as is
|
|
113
|
+
return { containsCapco: true, capco, updatedTextContent };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
//Generic function to return capco string for corresponding capco name
|
|
121
|
+
export function getShortCapcoString(capcoName) {
|
|
122
|
+
return capcoMap[capcoName] || 'U';
|
|
123
|
+
}
|
|
124
|
+
//Get capco Names
|
|
125
|
+
export function getCapcoNames() {
|
|
126
|
+
return Object.keys(capcoMap);
|
|
127
|
+
}
|
|
128
|
+
//Get Capco Name from short string. Eg: 'U' -> 'UNCLASSIFIED'
|
|
129
|
+
export function getCapcoNameFromShortString(capcoShort) {
|
|
130
|
+
const name = Object.entries(capcoMap).find(([_, value]) => value === capcoShort);
|
|
131
|
+
return name?.[0];
|
|
132
|
+
}
|
|
133
|
+
//Generic function to convert capco string to capco object
|
|
134
|
+
export function getCapcoObject(capcoString) {
|
|
135
|
+
const capco = {
|
|
136
|
+
ism: {
|
|
137
|
+
classification: UNCLASSIFIED,
|
|
138
|
+
system: 'USA',
|
|
139
|
+
ownerProducer: [],
|
|
140
|
+
sciControls: [],
|
|
141
|
+
atomicEnergyMarkings: [],
|
|
142
|
+
disseminationControls: [],
|
|
143
|
+
},
|
|
144
|
+
portionMarking: 'U',
|
|
145
|
+
};
|
|
146
|
+
if (capcoString && systemCapcos.includes(capcoString)) {
|
|
147
|
+
capco.ism.classification = capcoString;
|
|
148
|
+
capco.portionMarking = capcoString;
|
|
149
|
+
}
|
|
150
|
+
return capco;
|
|
151
|
+
}
|
|
152
|
+
export function getCapcoFromNode(node) {
|
|
153
|
+
return (node?.getAttribute('capco') ??
|
|
154
|
+
node?.querySelector('span')?.getAttribute('capco'));
|
|
155
|
+
}
|
|
156
|
+
export function safeCapcoParse(capco, fallback) {
|
|
157
|
+
const defaultFallBack = {
|
|
158
|
+
ism: undefined,
|
|
159
|
+
portionMarking: 'error',
|
|
160
|
+
};
|
|
161
|
+
fallback = fallback ?? defaultFallBack;
|
|
162
|
+
if (typeof capco === 'string') {
|
|
163
|
+
try {
|
|
164
|
+
return JSON.parse(capco);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
console.warn('could not parse capco text: ' + capco, e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (capco && typeof capco === 'object') {
|
|
171
|
+
return capco;
|
|
172
|
+
}
|
|
173
|
+
return fallback;
|
|
174
|
+
}
|
|
175
|
+
export function removeCapcoTextFromNode(node) {
|
|
176
|
+
for (const child of Array.from(node.childNodes)) {
|
|
177
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
178
|
+
let text = child.textContent ?? '';
|
|
179
|
+
for (const systemCapco of systemCapcos) {
|
|
180
|
+
const marker = `(${systemCapco.toUpperCase()})`;
|
|
181
|
+
// check only at the start
|
|
182
|
+
if (text.trimStart().toUpperCase().startsWith(marker)) {
|
|
183
|
+
// remove only the first occurrence at the beginning
|
|
184
|
+
text = text.trimStart().substring(marker.length).trimStart();
|
|
185
|
+
child.textContent = text;
|
|
186
|
+
break; // no need to check other markers
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (child.hasChildNodes()) {
|
|
191
|
+
// recurse into nested elements
|
|
192
|
+
removeCapcoTextFromNode(child);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
package/index.d.ts
ADDED