arcane-os 0.1.0-dev.5 → 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.
- package/NOTICE +10 -0
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +162 -0
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +69 -0
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1151 -0
- package/browser-runtime/ai/browser-wasm.mjs +44 -0
- package/browser-runtime/ai/browser-wllama-runtime.mjs +390 -0
- package/browser-runtime/ai/internal/sha256.mjs +166 -0
- package/browser-runtime/ai/model-controller.mjs +581 -0
- package/browser-runtime/ai/wllama/LICENCE +21 -0
- package/browser-runtime/ai/wllama/index.mjs +3494 -0
- package/browser-runtime/ai/wllama/llama.cpp-LICENSE +21 -0
- package/browser-runtime/ai/wllama/wllama.wasm +0 -0
- package/browser-runtime/dependencies/event-pubsub/index.js +141 -0
- package/browser-runtime/dependencies/event-pubsub/licence +21 -0
- package/browser-runtime/dependencies/event-pubsub/package.json +59 -0
- package/browser-runtime/dependencies/strong-type/index.js +1151 -0
- package/browser-runtime/dependencies/strong-type/licence +21 -0
- package/browser-runtime/dependencies/strong-type/package.json +61 -0
- package/browser-runtime/dom-event-instrumentation.mjs +594 -0
- package/browser-runtime/event-manager.mjs +1342 -0
- package/docs/publishing.md +65 -67
- package/docs/reference/README.md +2 -1
- package/docs/reference/cli.md +86 -3
- package/docs/reference/event-manager.md +20 -11
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/protocols.md +112 -14
- package/docs/reference/sdk-api.md +40 -11
- package/docs/work-amplification.md +4 -3
- package/package.json +15 -8
- package/runtime/ARCANE_RUNTIME_RELEASE.json +1 -1
- package/schemas/arcane-lock.schema.json +97 -1
- package/src/cli/main.mjs +5 -0
- package/src/dev-server.mjs +78 -34
- package/src/doctor.mjs +77 -3
- package/src/import-map.mjs +2352 -0
- package/src/packager/core.mjs +607 -29
- package/src/scaffold.mjs +122 -5
- package/src/sdk-browser-runtime.mjs +702 -0
- package/src/targets/index.mjs +31 -4
- package/src/templates/workspace-template.mjs +141 -23
- package/src/toolchain.mjs +288 -55
- package/src/workspace-operation-lock.mjs +716 -0
- package/src/workspace-runtime.mjs +841 -0
- package/src/workspace.mjs +292 -37
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Brandon Nozaki Miller
|
|
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.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "strong-type",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Dependency-free native ESM type enforcement for JavaScript values, objects, classes, browsers, and Node.",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./index.js": "./index.js",
|
|
10
|
+
"./node": "./node.js",
|
|
11
|
+
"./node.js": "./node.js",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"index.js",
|
|
16
|
+
"node.js",
|
|
17
|
+
"assets/strong-type-header.png",
|
|
18
|
+
"README.md",
|
|
19
|
+
"licence"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"directories": {
|
|
23
|
+
"example": "example"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=12.21.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "node scripts/test.js",
|
|
30
|
+
"test:core": "node scripts/test.js core",
|
|
31
|
+
"test:node": "node scripts/test.js node",
|
|
32
|
+
"test:docs": "node scripts/test.js docs",
|
|
33
|
+
"test:legacy": "node scripts/test.js legacy",
|
|
34
|
+
"coverage": "node --test --experimental-test-coverage --test-coverage-include=index.js --test-coverage-include=node.js --test-coverage-lines=95 --test-coverage-branches=90 --test-coverage-functions=95 test/CI.js test/node.js test/docs.js",
|
|
35
|
+
"start": "node scripts/serve.js",
|
|
36
|
+
"nodeExample": "node ./example/node/typecheck.js",
|
|
37
|
+
"check": "npm test"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/RIAEvangelist/strong-type.git"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"strong",
|
|
45
|
+
"type",
|
|
46
|
+
"validation",
|
|
47
|
+
"validate",
|
|
48
|
+
"check",
|
|
49
|
+
"checking",
|
|
50
|
+
"esm",
|
|
51
|
+
"isomorphic",
|
|
52
|
+
"browser",
|
|
53
|
+
"node"
|
|
54
|
+
],
|
|
55
|
+
"author": "Roshi _ _",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/RIAEvangelist/strong-type/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://riaevangelist.github.io/strong-type/"
|
|
61
|
+
}
|
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
export const DOM_INTERACTION_EVENT='arcane.dom.interaction';
|
|
2
|
+
export const DOM_MUTATION_EVENT='arcane.dom.mutation';
|
|
3
|
+
export const DOM_OBSERVATION_STARTED_EVENT='arcane.dom.observation.started';
|
|
4
|
+
export const DOM_OBSERVATION_STOPPED_EVENT='arcane.dom.observation.stopped';
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_DOM_EVENT_TYPES=Object.freeze([
|
|
7
|
+
'auxclick','beforeinput','blur','change','click','compositionend',
|
|
8
|
+
'compositionstart','compositionupdate','contextmenu','copy','cut','dblclick',
|
|
9
|
+
'drag','dragend','dragenter','dragleave','dragover','dragstart','drop','focus',
|
|
10
|
+
'focusin','focusout','input','keydown','keypress','keyup','paste','pointercancel',
|
|
11
|
+
'pointerdown','pointerenter','pointerleave','pointermove','pointerout','pointerover',
|
|
12
|
+
'pointerup','reset','scroll','selectionchange','submit','touchcancel','touchend',
|
|
13
|
+
'touchmove','touchstart','wheel'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const EVENT_FIELDS=Object.freeze([
|
|
17
|
+
'altKey','button','buttons','charCode','clientX','clientY','code','ctrlKey','data',
|
|
18
|
+
'deltaMode','deltaX','deltaY','deltaZ','detail','inputType','key','keyCode',
|
|
19
|
+
'location','metaKey','movementX','movementY','offsetX','offsetY','pageX','pageY',
|
|
20
|
+
'pointerId','pointerType','repeat','screenX','screenY','shiftKey','which'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const TEXT_ENTRY_EVENT_PATTERN=/^(?:beforeinput|composition(?:end|start|update)|input|key(?:down|press|up))$/u;
|
|
24
|
+
const LEGACY_CHARACTER_CODE_PATTERN=/^(?:charCode|keyCode|which)$/u;
|
|
25
|
+
const URL_IDENTIFIER_PATTERN=/\b(?:blob|data|file|ftp|ftps|http|https|ws|wss):/iu;
|
|
26
|
+
const URL_ATTRIBUTE_PATTERN=/^(?:action|cite|data|formaction|href|poster|src|srcset)$/iu;
|
|
27
|
+
const SENSITIVE_ATTRIBUTE_PATTERN=/(?:authorization|cookie|credential|password|secret|session|srcdoc|style|token|value|api[-_]?key)/iu;
|
|
28
|
+
|
|
29
|
+
function boundedString(value,maximum){
|
|
30
|
+
const text=String(value??'');
|
|
31
|
+
return text.length<=maximum?text:`${text.slice(0,maximum)}…`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cssEscape(value){
|
|
35
|
+
if(typeof globalThis.CSS?.escape==='function')return globalThis.CSS.escape(value);
|
|
36
|
+
return String(value).replaceAll(/[^a-zA-Z0-9_-]/gu,character=>`\\${character.codePointAt(0).toString(16)} `);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readAttribute(target,name){
|
|
40
|
+
try{
|
|
41
|
+
return typeof target?.getAttribute==='function'?target.getAttribute(name):null;
|
|
42
|
+
}catch{
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasAttribute(target,name){
|
|
48
|
+
try{
|
|
49
|
+
return typeof target?.hasAttribute==='function'&&target.hasAttribute(name);
|
|
50
|
+
}catch{
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function elementName(target){
|
|
56
|
+
return String(target?.localName??target?.tagName??'element').toLowerCase();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function siblingIndex(target){
|
|
60
|
+
const siblings=target?.parentElement?.children;
|
|
61
|
+
if(!siblings)return null;
|
|
62
|
+
const name=elementName(target);
|
|
63
|
+
let index=0;
|
|
64
|
+
for(const sibling of siblings){
|
|
65
|
+
if(elementName(sibling)!==name)continue;
|
|
66
|
+
index+=1;
|
|
67
|
+
if(sibling===target)return index;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function selectorSegment(target){
|
|
73
|
+
const id=String(target?.id??readAttribute(target,'id')??'');
|
|
74
|
+
if(id){
|
|
75
|
+
return URL_IDENTIFIER_PATTERN.test(id)?`${elementName(target)}[id]`:`#${cssEscape(id)}`;
|
|
76
|
+
}
|
|
77
|
+
for(const attribute of ['data-arcane-id','data-testid']){
|
|
78
|
+
const value=readAttribute(target,attribute);
|
|
79
|
+
if(value){
|
|
80
|
+
return URL_IDENTIFIER_PATTERN.test(value)
|
|
81
|
+
?`[${attribute}]`
|
|
82
|
+
:`[${attribute}="${cssEscape(value)}"]`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const name=elementName(target);
|
|
86
|
+
const index=siblingIndex(target);
|
|
87
|
+
return index===null?name:`${name}:nth-of-type(${index})`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function domSelector(target,root){
|
|
91
|
+
if(!target||typeof target!=='object')return null;
|
|
92
|
+
if(target.nodeType===9)return ':document';
|
|
93
|
+
if(target.nodeType===11)return ':shadow-root';
|
|
94
|
+
if(target.nodeType===3)return domSelector(target.parentElement??target.parentNode,root);
|
|
95
|
+
if(target.nodeType!==1&&target.localName===undefined&&target.tagName===undefined)return null;
|
|
96
|
+
|
|
97
|
+
const groups=[];
|
|
98
|
+
let segments=[];
|
|
99
|
+
let current=target;
|
|
100
|
+
const visited=new Set();
|
|
101
|
+
while(current&¤t!==root&&!visited.has(current)){
|
|
102
|
+
visited.add(current);
|
|
103
|
+
segments.unshift(selectorSegment(current));
|
|
104
|
+
const hasId=String(current?.id??readAttribute(current,'id')??'')!=='';
|
|
105
|
+
if(hasId){
|
|
106
|
+
let currentRoot=null;
|
|
107
|
+
try{currentRoot=current.getRootNode?.()??null;}catch{}
|
|
108
|
+
if(currentRoot?.host){
|
|
109
|
+
groups.unshift(segments.join(' > '));
|
|
110
|
+
segments=[];
|
|
111
|
+
current=currentRoot.host;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if(current.parentElement){
|
|
117
|
+
current=current.parentElement;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
let currentRoot=null;
|
|
121
|
+
try{currentRoot=current.getRootNode?.()??null;}catch{}
|
|
122
|
+
if(currentRoot?.host){
|
|
123
|
+
groups.unshift(segments.join(' > '));
|
|
124
|
+
segments=[];
|
|
125
|
+
current=currentRoot.host;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
if(segments.length)groups.unshift(segments.join(' > '));
|
|
131
|
+
return groups.join(' >>> ')||selectorSegment(target);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function privateElement(target){
|
|
135
|
+
let current=target?.nodeType===3?target.parentElement??target.parentNode:target;
|
|
136
|
+
const visited=new Set();
|
|
137
|
+
while(current&&typeof current==='object'&&!visited.has(current)){
|
|
138
|
+
visited.add(current);
|
|
139
|
+
const type=String(current.type??readAttribute(current,'type')??'').toLowerCase();
|
|
140
|
+
const autocomplete=String(readAttribute(current,'autocomplete')??'').toLowerCase();
|
|
141
|
+
if(type==='password'||hasAttribute(current,'data-arcane-private')
|
|
142
|
+
||autocomplete.includes('password')){
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
if(current.parentElement){
|
|
146
|
+
current=current.parentElement;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
let currentRoot=null;
|
|
150
|
+
try{currentRoot=current.getRootNode?.()??null;}catch{}
|
|
151
|
+
current=currentRoot?.host??null;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function describeDOMTarget(target,root){
|
|
157
|
+
if(!target||typeof target!=='object')return null;
|
|
158
|
+
if(target.nodeType===9){
|
|
159
|
+
return Object.freeze({kind:'document',selector:':document'});
|
|
160
|
+
}
|
|
161
|
+
if(target.nodeType===11){
|
|
162
|
+
return Object.freeze({
|
|
163
|
+
kind:'shadow-root',
|
|
164
|
+
selector:domSelector(target.host,root),
|
|
165
|
+
host:target.host?describeDOMTarget(target.host,root):null
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if(target.nodeType===3){
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
kind:'text',
|
|
171
|
+
selector:domSelector(target,root),
|
|
172
|
+
private:privateElement(target)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if(target.nodeType!==1&&target.localName===undefined&&target.tagName===undefined){
|
|
176
|
+
return Object.freeze({kind:target===globalThis?'global':'event-target',selector:null});
|
|
177
|
+
}
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
kind:'element',
|
|
180
|
+
selector:domSelector(target,root),
|
|
181
|
+
tagName:elementName(target),
|
|
182
|
+
id:String(target.id??readAttribute(target,'id')??'')||null,
|
|
183
|
+
role:readAttribute(target,'role'),
|
|
184
|
+
name:readAttribute(target,'name'),
|
|
185
|
+
type:String(target.type??readAttribute(target,'type')??'')||null,
|
|
186
|
+
private:privateElement(target)
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function targetValue(target,{captureInputValues,maxValueLength}){
|
|
191
|
+
if(!captureInputValues||!target||typeof target!=='object')return undefined;
|
|
192
|
+
if(privateElement(target))return '[REDACTED]';
|
|
193
|
+
const type=String(target.type??'').toLowerCase();
|
|
194
|
+
if(type==='checkbox'||type==='radio')return Boolean(target.checked);
|
|
195
|
+
if(type==='file'){
|
|
196
|
+
return Array.from(target.files??[],file=>({name:String(file?.name??''),size:Number(file?.size??0)}));
|
|
197
|
+
}
|
|
198
|
+
if(!('value' in target))return undefined;
|
|
199
|
+
const value=String(target.value??'');
|
|
200
|
+
if(value.length<=maxValueLength)return value;
|
|
201
|
+
return `${value.slice(0,maxValueLength)}…`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function safeEventDetail(field,value,{captureEventDetails,maxValueLength},isPrivate,eventType){
|
|
205
|
+
if(isPrivate&&(field==='code'||field==='data'||field==='detail'||field==='key')){
|
|
206
|
+
return '[REDACTED]';
|
|
207
|
+
}
|
|
208
|
+
if(field==='data'||field==='detail'){
|
|
209
|
+
if(!captureEventDetails)return value===null?null:'[REDACTED]';
|
|
210
|
+
}
|
|
211
|
+
if(field==='key'||field==='code'){
|
|
212
|
+
const text=String(value??'');
|
|
213
|
+
const sensitive=TEXT_ENTRY_EVENT_PATTERN.test(eventType)
|
|
214
|
+
&&(field==='code'||text.length===1||text==='Unidentified');
|
|
215
|
+
if(sensitive&&!captureEventDetails)return '[REDACTED]';
|
|
216
|
+
}
|
|
217
|
+
if(LEGACY_CHARACTER_CODE_PATTERN.test(field)
|
|
218
|
+
&&(isPrivate||(TEXT_ENTRY_EVENT_PATTERN.test(eventType)&&!captureEventDetails))){
|
|
219
|
+
return '[REDACTED]';
|
|
220
|
+
}
|
|
221
|
+
if(typeof value==='string')return boundedString(value,maxValueLength);
|
|
222
|
+
if(value===null||typeof value==='number'||typeof value==='boolean')return value;
|
|
223
|
+
return captureEventDetails?'[UNSERIALIZED EVENT DETAIL]':'[REDACTED]';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function interactionRecord(event,root,options){
|
|
227
|
+
const path=typeof event?.composedPath==='function'?event.composedPath():[event?.target];
|
|
228
|
+
const target=path.find(item=>item&&typeof item==='object')??event?.target??null;
|
|
229
|
+
const isPrivate=privateElement(target);
|
|
230
|
+
const eventType=String(event?.type??'');
|
|
231
|
+
const details={};
|
|
232
|
+
for(const field of EVENT_FIELDS){
|
|
233
|
+
let value;
|
|
234
|
+
try{value=event?.[field];}catch{continue;}
|
|
235
|
+
if(value!==undefined&&typeof value!=='function'){
|
|
236
|
+
details[field]=safeEventDetail(field,value,options,isPrivate,eventType);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const value=targetValue(target,options);
|
|
240
|
+
return {
|
|
241
|
+
eventType,
|
|
242
|
+
target:describeDOMTarget(target,root),
|
|
243
|
+
path:path.map(item=>describeDOMTarget(item,root)).filter(Boolean),
|
|
244
|
+
bubbles:Boolean(event?.bubbles),
|
|
245
|
+
cancelable:Boolean(event?.cancelable),
|
|
246
|
+
composed:Boolean(event?.composed),
|
|
247
|
+
defaultPrevented:Boolean(event?.defaultPrevented),
|
|
248
|
+
trusted:Boolean(event?.isTrusted),
|
|
249
|
+
...(Object.keys(details).length?{details}:{}),
|
|
250
|
+
...(value===undefined?{}:{value})
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function serializeNode(node,root,{captureNodeMarkup,maxSerializedNodeLength},privateContext=false){
|
|
255
|
+
const descriptor=describeDOMTarget(node,root);
|
|
256
|
+
if(privateContext||privateElement(node)){
|
|
257
|
+
return {target:descriptor,content:'[REDACTED]',truncated:false};
|
|
258
|
+
}
|
|
259
|
+
if(!captureNodeMarkup)return {target:descriptor,content:'[CONTENT OMITTED]',truncated:false};
|
|
260
|
+
let content='';
|
|
261
|
+
if(node?.nodeType===3||node?.nodeType===8)content=String(node.data??node.textContent??'');
|
|
262
|
+
else content=String(node?.outerHTML??node?.textContent??'');
|
|
263
|
+
const truncated=content.length>maxSerializedNodeLength;
|
|
264
|
+
return {
|
|
265
|
+
target:descriptor,
|
|
266
|
+
content:truncated?`${content.slice(0,maxSerializedNodeLength)}…`:content,
|
|
267
|
+
truncated
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function attributeValue(value,attributeName,target,options){
|
|
272
|
+
if(value===null||value===undefined)return null;
|
|
273
|
+
if(privateElement(target)||SENSITIVE_ATTRIBUTE_PATTERN.test(attributeName))return '[REDACTED]';
|
|
274
|
+
if(URL_ATTRIBUTE_PATTERN.test(attributeName))return '[REDACTED URL]';
|
|
275
|
+
return boundedString(value,options.maxValueLength);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function mutationRecord(record,root,options){
|
|
279
|
+
const target=describeDOMTarget(record?.target,root);
|
|
280
|
+
if(record?.type==='attributes'){
|
|
281
|
+
const attributeName=String(record.attributeName??'');
|
|
282
|
+
const current=readAttribute(record.target,attributeName);
|
|
283
|
+
return {
|
|
284
|
+
mutationType:'attributes',target,attributeName,
|
|
285
|
+
namespace:record.attributeNamespace??null,
|
|
286
|
+
before:attributeValue(record.oldValue,attributeName,record.target,options),
|
|
287
|
+
after:attributeValue(current,attributeName,record.target,options)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if(record?.type==='characterData'){
|
|
291
|
+
const redacted=privateElement(record.target)||!options.captureNodeMarkup;
|
|
292
|
+
return {
|
|
293
|
+
mutationType:'characterData',target,
|
|
294
|
+
before:redacted?'[REDACTED]':record.oldValue??null,
|
|
295
|
+
after:redacted?'[REDACTED]':String(record.target?.data??record.target?.textContent??'')
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const privateContext=privateElement(record?.target);
|
|
299
|
+
return {
|
|
300
|
+
mutationType:'childList',target,
|
|
301
|
+
previousSibling:describeDOMTarget(record?.previousSibling,root),
|
|
302
|
+
nextSibling:describeDOMTarget(record?.nextSibling,root),
|
|
303
|
+
added:Array.from(
|
|
304
|
+
record?.addedNodes??[],node=>serializeNode(node,root,options,privateContext)
|
|
305
|
+
),
|
|
306
|
+
removed:Array.from(
|
|
307
|
+
record?.removedNodes??[],node=>serializeNode(node,root,options,privateContext)
|
|
308
|
+
)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function rootDescription(root,{captureNodeMarkup=false,maxValueLength=10_000}={}){
|
|
313
|
+
if(root?.nodeType===9){
|
|
314
|
+
const title=String(root.title??'');
|
|
315
|
+
return {
|
|
316
|
+
kind:'document',
|
|
317
|
+
url:root.location?.href?'[REDACTED URL]':null,
|
|
318
|
+
title:title?(captureNodeMarkup?boundedString(title,maxValueLength):'[CONTENT OMITTED]'):null,
|
|
319
|
+
root:describeDOMTarget(root.documentElement,root)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
return describeDOMTarget(root,root);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function collectOpenShadowRoots(root){
|
|
326
|
+
const roots=[];
|
|
327
|
+
const candidates=[];
|
|
328
|
+
try{
|
|
329
|
+
if(typeof root?.querySelectorAll==='function')candidates.push(...root.querySelectorAll('*'));
|
|
330
|
+
}catch{}
|
|
331
|
+
for(const candidate of candidates){
|
|
332
|
+
if(candidate?.shadowRoot)roots.push(candidate.shadowRoot,...collectOpenShadowRoots(candidate.shadowRoot));
|
|
333
|
+
}
|
|
334
|
+
return roots;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function createDOMInstrumentation({
|
|
338
|
+
eventManager,
|
|
339
|
+
root=globalThis.document,
|
|
340
|
+
eventTypes=DEFAULT_DOM_EVENT_TYPES,
|
|
341
|
+
MutationObserver:MutationObserverImpl=globalThis.MutationObserver,
|
|
342
|
+
captureEventDetails=false,
|
|
343
|
+
captureInputValues=false,
|
|
344
|
+
captureNodeMarkup=false,
|
|
345
|
+
captureMutations=true,
|
|
346
|
+
maxValueLength=10_000,
|
|
347
|
+
maxSerializedNodeLength=100_000,
|
|
348
|
+
observeOpenShadowRoots=true
|
|
349
|
+
}={}){
|
|
350
|
+
if(!eventManager||typeof eventManager.emit!=='function'){
|
|
351
|
+
throw new TypeError('DOM instrumentation requires an event manager.');
|
|
352
|
+
}
|
|
353
|
+
if(!root||typeof root.addEventListener!=='function'||typeof root.removeEventListener!=='function'){
|
|
354
|
+
throw new TypeError('DOM instrumentation requires an EventTarget-compatible root.');
|
|
355
|
+
}
|
|
356
|
+
if(!Array.isArray(eventTypes)||eventTypes.some(type=>typeof type!=='string'||!type)){
|
|
357
|
+
throw new TypeError('DOM event types must be non-empty strings.');
|
|
358
|
+
}
|
|
359
|
+
eventTypes=Object.freeze([...new Set(eventTypes)]);
|
|
360
|
+
if(!Number.isSafeInteger(maxValueLength)||maxValueLength<0
|
|
361
|
+
||!Number.isSafeInteger(maxSerializedNodeLength)||maxSerializedNodeLength<0){
|
|
362
|
+
throw new RangeError('DOM capture length limits must be non-negative safe integers.');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
for(const [name,value] of Object.entries({
|
|
366
|
+
captureEventDetails,captureInputValues,captureNodeMarkup,captureMutations,observeOpenShadowRoots
|
|
367
|
+
})){
|
|
368
|
+
if(typeof value!=='boolean')throw new TypeError(`${name} must be boolean.`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const options={
|
|
372
|
+
captureEventDetails,captureInputValues,captureNodeMarkup,
|
|
373
|
+
maxValueLength,maxSerializedNodeLength
|
|
374
|
+
};
|
|
375
|
+
const observedRoots=new Set();
|
|
376
|
+
const listenerRegistrations=new Map();
|
|
377
|
+
let observer=null;
|
|
378
|
+
let active=false;
|
|
379
|
+
let cleanupPending=false;
|
|
380
|
+
let lifecycleStarted=false;
|
|
381
|
+
let pendingCausationId=null;
|
|
382
|
+
let causationTimer=null;
|
|
383
|
+
|
|
384
|
+
const removeInteractionListener=(target,type,handler)=>{
|
|
385
|
+
let failure=null;
|
|
386
|
+
for(let attempt=0;attempt<2;attempt+=1){
|
|
387
|
+
try{
|
|
388
|
+
target.removeEventListener(type,handler,true);
|
|
389
|
+
return null;
|
|
390
|
+
}catch(error){
|
|
391
|
+
failure=error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return failure;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const disconnectObserver=()=>{
|
|
398
|
+
let failure=null;
|
|
399
|
+
for(let attempt=0;attempt<2;attempt+=1){
|
|
400
|
+
try{
|
|
401
|
+
observer?.disconnect();
|
|
402
|
+
return null;
|
|
403
|
+
}catch(error){
|
|
404
|
+
failure=error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return failure;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const cleanupResources=()=>{
|
|
411
|
+
let failure=null;
|
|
412
|
+
for(const target of observedRoots){
|
|
413
|
+
const registrations=listenerRegistrations.get(target)??new Map();
|
|
414
|
+
for(const [type,handler] of [...registrations]){
|
|
415
|
+
const removalFailure=removeInteractionListener(target,type,handler);
|
|
416
|
+
if(removalFailure)failure??=removalFailure;
|
|
417
|
+
else registrations.delete(type);
|
|
418
|
+
}
|
|
419
|
+
if(registrations.size===0)listenerRegistrations.delete(target);
|
|
420
|
+
}
|
|
421
|
+
const observerFailure=disconnectObserver();
|
|
422
|
+
failure??=observerFailure;
|
|
423
|
+
if(failure){
|
|
424
|
+
active=true;
|
|
425
|
+
cleanupPending=true;
|
|
426
|
+
return failure;
|
|
427
|
+
}
|
|
428
|
+
observer=null;
|
|
429
|
+
listenerRegistrations.clear();
|
|
430
|
+
observedRoots.clear();
|
|
431
|
+
active=false;
|
|
432
|
+
cleanupPending=false;
|
|
433
|
+
pendingCausationId=null;
|
|
434
|
+
if(causationTimer!==null){
|
|
435
|
+
clearTimeout(causationTimer);
|
|
436
|
+
causationTimer=null;
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const publish=(type,payload,metadata)=>{
|
|
442
|
+
const before=Number(eventManager.eventCount??0);
|
|
443
|
+
if(typeof eventManager.instrument==='function'){
|
|
444
|
+
eventManager.instrument(type,payload,{source:'dom',...metadata});
|
|
445
|
+
}else{
|
|
446
|
+
eventManager.emit(type,payload);
|
|
447
|
+
}
|
|
448
|
+
const recorded=Number(eventManager.eventCount??0)>before
|
|
449
|
+
?eventManager.history?.[before]??null
|
|
450
|
+
:null;
|
|
451
|
+
return recorded?.type===type?recorded:null;
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const interaction=(event,observedRoot)=>{
|
|
455
|
+
let path=[];
|
|
456
|
+
try{
|
|
457
|
+
if(typeof event?.composedPath==='function')path=event.composedPath();
|
|
458
|
+
}catch{}
|
|
459
|
+
const outermostObservedRoot=path.reduce(
|
|
460
|
+
(selected,item)=>observedRoots.has(item)?item:selected,
|
|
461
|
+
null
|
|
462
|
+
);
|
|
463
|
+
if(outermostObservedRoot&&outermostObservedRoot!==observedRoot){
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const record=publish(
|
|
467
|
+
DOM_INTERACTION_EVENT,
|
|
468
|
+
interactionRecord(event,root,options),
|
|
469
|
+
{category:'interaction'}
|
|
470
|
+
);
|
|
471
|
+
if(record?.id){
|
|
472
|
+
pendingCausationId=record.id;
|
|
473
|
+
if(causationTimer!==null)clearTimeout(causationTimer);
|
|
474
|
+
causationTimer=setTimeout(()=>{
|
|
475
|
+
if(pendingCausationId===record.id)pendingCausationId=null;
|
|
476
|
+
causationTimer=null;
|
|
477
|
+
},0);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const observeRoot=target=>{
|
|
482
|
+
if(!target||observedRoots.has(target))return;
|
|
483
|
+
const registrations=new Map();
|
|
484
|
+
observedRoots.add(target);
|
|
485
|
+
listenerRegistrations.set(target,registrations);
|
|
486
|
+
try{
|
|
487
|
+
for(const type of eventTypes){
|
|
488
|
+
const handler=event=>interaction(event,target);
|
|
489
|
+
target.addEventListener(type,handler,true);
|
|
490
|
+
registrations.set(type,handler);
|
|
491
|
+
}
|
|
492
|
+
observer?.observe(target,{
|
|
493
|
+
attributes:true,
|
|
494
|
+
attributeOldValue:true,
|
|
495
|
+
characterData:true,
|
|
496
|
+
characterDataOldValue:true,
|
|
497
|
+
childList:true,
|
|
498
|
+
subtree:true
|
|
499
|
+
});
|
|
500
|
+
}catch(error){
|
|
501
|
+
for(const [type,handler] of [...registrations]){
|
|
502
|
+
const removalFailure=removeInteractionListener(target,type,handler);
|
|
503
|
+
if(!removalFailure)registrations.delete(type);
|
|
504
|
+
}
|
|
505
|
+
if(registrations.size===0){
|
|
506
|
+
listenerRegistrations.delete(target);
|
|
507
|
+
observedRoots.delete(target);
|
|
508
|
+
}else{
|
|
509
|
+
active=true;
|
|
510
|
+
cleanupPending=true;
|
|
511
|
+
}
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const mutations=records=>{
|
|
517
|
+
for(const record of records){
|
|
518
|
+
publish(DOM_MUTATION_EVENT,mutationRecord(record,root,options),{
|
|
519
|
+
category:'mutation',
|
|
520
|
+
...(pendingCausationId?{causationId:pendingCausationId}: {})
|
|
521
|
+
});
|
|
522
|
+
if(observeOpenShadowRoots&&record?.type==='childList'){
|
|
523
|
+
for(const node of record.addedNodes??[]){
|
|
524
|
+
if(node?.shadowRoot)observeRoot(node.shadowRoot);
|
|
525
|
+
for(const shadowRoot of collectOpenShadowRoots(node))observeRoot(shadowRoot);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
function start(){
|
|
532
|
+
if(cleanupPending){
|
|
533
|
+
throw new Error('DOM instrumentation cleanup is pending; retry stop() before start().');
|
|
534
|
+
}
|
|
535
|
+
if(active)return api;
|
|
536
|
+
try{
|
|
537
|
+
if(captureMutations){
|
|
538
|
+
if(typeof MutationObserverImpl!=='function'){
|
|
539
|
+
throw new TypeError('MutationObserver is required when DOM mutation capture is enabled.');
|
|
540
|
+
}
|
|
541
|
+
observer=new MutationObserverImpl(mutations);
|
|
542
|
+
}
|
|
543
|
+
observeRoot(root);
|
|
544
|
+
if(observeOpenShadowRoots){
|
|
545
|
+
for(const shadowRoot of collectOpenShadowRoots(root))observeRoot(shadowRoot);
|
|
546
|
+
}
|
|
547
|
+
active=true;
|
|
548
|
+
cleanupPending=false;
|
|
549
|
+
publish(DOM_OBSERVATION_STARTED_EVENT,{
|
|
550
|
+
root:rootDescription(root,options),
|
|
551
|
+
eventTypes:[...eventTypes],
|
|
552
|
+
captureEventDetails,
|
|
553
|
+
captureInputValues,
|
|
554
|
+
captureNodeMarkup,
|
|
555
|
+
captureMutations,
|
|
556
|
+
observeOpenShadowRoots
|
|
557
|
+
},{category:'lifecycle'});
|
|
558
|
+
lifecycleStarted=true;
|
|
559
|
+
return api;
|
|
560
|
+
}catch(error){
|
|
561
|
+
const cleanupFailure=cleanupResources();
|
|
562
|
+
if(!cleanupFailure)lifecycleStarted=false;
|
|
563
|
+
throw error;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function stop({emitLifecycle=true}={}){
|
|
568
|
+
if(typeof emitLifecycle!=='boolean')throw new TypeError('emitLifecycle must be boolean.');
|
|
569
|
+
if(!active)return api;
|
|
570
|
+
const shouldEmitLifecycle=emitLifecycle&&lifecycleStarted;
|
|
571
|
+
if(!emitLifecycle)lifecycleStarted=false;
|
|
572
|
+
const failure=cleanupResources();
|
|
573
|
+
if(failure)throw failure;
|
|
574
|
+
lifecycleStarted=false;
|
|
575
|
+
if(shouldEmitLifecycle){
|
|
576
|
+
publish(
|
|
577
|
+
DOM_OBSERVATION_STOPPED_EVENT,
|
|
578
|
+
{root:rootDescription(root,options)},
|
|
579
|
+
{category:'lifecycle'}
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
return api;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const api=Object.freeze({
|
|
586
|
+
root,
|
|
587
|
+
start,
|
|
588
|
+
stop,
|
|
589
|
+
get active(){return active;},
|
|
590
|
+
get cleanupPending(){return cleanupPending;},
|
|
591
|
+
get observedRootCount(){return observedRoots.size;}
|
|
592
|
+
});
|
|
593
|
+
return api;
|
|
594
|
+
}
|