arcane-os 0.1.0-dev.5
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 +154 -0
- package/COMMERCIAL-LICENSE.md +13 -0
- package/LICENSE +661 -0
- package/NOTICE +70 -0
- package/README.md +448 -0
- package/bin/arcane-test.mjs +741 -0
- package/bin/arcane.mjs +5 -0
- package/docs/architecture.md +234 -0
- package/docs/compatibility.md +36 -0
- package/docs/event-manager.md +166 -0
- package/docs/platform-targets.md +108 -0
- package/docs/publishing.md +203 -0
- package/docs/reference/README.md +117 -0
- package/docs/reference/arcane-ollama.md +288 -0
- package/docs/reference/availability-and-normalization.md +123 -0
- package/docs/reference/behavioral-testing.md +86 -0
- package/docs/reference/cli.md +569 -0
- package/docs/reference/core/README.md +62 -0
- package/docs/reference/core/arcane-ai-contracts.md +873 -0
- package/docs/reference/core/arcane-api.md +601 -0
- package/docs/reference/core/arcane-entities.md +65 -0
- package/docs/reference/core/arcane-events.md +134 -0
- package/docs/reference/core/ollama-module.md +181 -0
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
- package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
- package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
- package/docs/reference/event-manager.md +957 -0
- package/docs/reference/inventory/package-api.json +2632 -0
- package/docs/reference/inventory/runtime-components.json +934 -0
- package/docs/reference/inventory/runtime-entities.json +26 -0
- package/docs/reference/inventory/runtime-modules.json +1249 -0
- package/docs/reference/protocols.md +242 -0
- package/docs/reference/runtime-components.md +1098 -0
- package/docs/reference/runtime-entities.md +303 -0
- package/docs/reference/runtime-modules.md +2010 -0
- package/docs/reference/sdk-api.md +4901 -0
- package/docs/roadmap.md +79 -0
- package/docs/work-amplification.md +124 -0
- package/node_modules/event-pubsub/CHANGELOG.md +55 -0
- package/node_modules/event-pubsub/MIGRATION.md +70 -0
- package/node_modules/event-pubsub/README.md +363 -0
- package/node_modules/event-pubsub/SECURITY.md +37 -0
- package/node_modules/event-pubsub/index.js +141 -0
- package/node_modules/event-pubsub/licence +21 -0
- package/node_modules/event-pubsub/package.json +59 -0
- package/node_modules/strong-type/README.md +408 -0
- package/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/strong-type/index.js +1151 -0
- package/node_modules/strong-type/licence +21 -0
- package/node_modules/strong-type/node.js +125 -0
- package/node_modules/strong-type/package.json +61 -0
- package/package.json +95 -0
- package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
- package/runtime/arcane/components/app-bar.html +468 -0
- package/runtime/arcane/components/assistant-panel.html +715 -0
- package/runtime/arcane/components/calculator.html +8 -0
- package/runtime/arcane/components/chart.html +655 -0
- package/runtime/arcane/components/chat.html +1225 -0
- package/runtime/arcane/components/conversation-view.html +13 -0
- package/runtime/arcane/components/dashboard-config.html +341 -0
- package/runtime/arcane/components/data-maintenance.html +112 -0
- package/runtime/arcane/components/data-view.html +92 -0
- package/runtime/arcane/components/directory-picker.html +197 -0
- package/runtime/arcane/components/document-inspector.html +252 -0
- package/runtime/arcane/components/file-drop.html +264 -0
- package/runtime/arcane/components/file-inspector.html +293 -0
- package/runtime/arcane/components/file-manager.html +1715 -0
- package/runtime/arcane/components/header.html +142 -0
- package/runtime/arcane/components/integration-settings.html +14 -0
- package/runtime/arcane/components/local-ai-status.html +360 -0
- package/runtime/arcane/components/markdown-document.html +1048 -0
- package/runtime/arcane/components/markdown-editor.html +360 -0
- package/runtime/arcane/components/media-embed.html +8 -0
- package/runtime/arcane/components/modal.html +402 -0
- package/runtime/arcane/components/output-panel.html +259 -0
- package/runtime/arcane/components/preferences-form.html +135 -0
- package/runtime/arcane/components/record-timeline.html +105 -0
- package/runtime/arcane/components/relationship-board.html +116 -0
- package/runtime/arcane/components/screen-capture.html +8 -0
- package/runtime/arcane/components/source-code-viewer.html +441 -0
- package/runtime/arcane/components/source-explanation.html +124 -0
- package/runtime/arcane/components/speech.html +365 -0
- package/runtime/arcane/components/summary-strip.html +177 -0
- package/runtime/arcane/components/table.html +77 -0
- package/runtime/arcane/components/task-progress.html +282 -0
- package/runtime/arcane/components/terminal-workspace.html +65 -0
- package/runtime/arcane/components/theme-editor.html +41 -0
- package/runtime/arcane/components/theme-switcher.html +46 -0
- package/runtime/arcane/components/unified-inbox.html +20 -0
- package/runtime/arcane/components/voice-transcription.html +476 -0
- package/runtime/arcane/components/weather-widget.html +8 -0
- package/runtime/arcane/components/web-navigator.html +239 -0
- package/runtime/arcane/css/communications.css +1 -0
- package/runtime/arcane/css/dashboard-config.css +45 -0
- package/runtime/arcane/css/document-site.css +981 -0
- package/runtime/arcane/css/layout.css +438 -0
- package/runtime/arcane/css/primitives.css +321 -0
- package/runtime/arcane/css/theme.css +112 -0
- package/runtime/arcane/css/utility-workspace.css +1 -0
- package/runtime/arcane/entities/ApiModelRecord.js +20 -0
- package/runtime/arcane/entities/Calculation.js +13 -0
- package/runtime/arcane/entities/Chat.js +581 -0
- package/runtime/arcane/entities/CommunicationMessage.js +29 -0
- package/runtime/arcane/entities/CommunicationThread.js +21 -0
- package/runtime/arcane/entities/Document.js +10 -0
- package/runtime/arcane/entities/File.js +143 -0
- package/runtime/arcane/entities/Image.js +135 -0
- package/runtime/arcane/entities/IntentEnvelope.js +834 -0
- package/runtime/arcane/entities/Preference.js +83 -0
- package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
- package/runtime/arcane/entities/TerminalSession.js +46 -0
- package/runtime/arcane/entities/Theme.js +107 -0
- package/runtime/arcane/entities/User.js +1046 -0
- package/runtime/arcane/entities/Weather.js +23 -0
- package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
- package/runtime/arcane/img/arrow-left.png +0 -0
- package/runtime/arcane/img/arrow-right.png +0 -0
- package/runtime/arcane/img/doc.svg +5 -0
- package/runtime/arcane/img/folder.svg +4 -0
- package/runtime/arcane/img/image.svg +5 -0
- package/runtime/arcane/img/refresh.png +0 -0
- package/runtime/arcane/img/send.svg +9 -0
- package/runtime/arcane/img/trash.svg +5 -0
- package/runtime/arcane/img/upload.svg +5 -0
- package/runtime/arcane/modules/AI.js +2048 -0
- package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
- package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
- package/runtime/arcane/modules/AIResponseLength.js +42 -0
- package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
- package/runtime/arcane/modules/AnsiText.js +53 -0
- package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
- package/runtime/arcane/modules/AppDataScope.js +245 -0
- package/runtime/arcane/modules/AppearancePreferences.js +28 -0
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
- package/runtime/arcane/modules/AsyncBoundary.js +161 -0
- package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
- package/runtime/arcane/modules/CalculatorEngine.js +20 -0
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
- package/runtime/arcane/modules/ChartLibrary.js +35 -0
- package/runtime/arcane/modules/ChatRecords.js +13 -0
- package/runtime/arcane/modules/CommunicationAppController.js +43 -0
- package/runtime/arcane/modules/CommunicationHub.js +16 -0
- package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
- package/runtime/arcane/modules/ComponentContracts.js +586 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
- package/runtime/arcane/modules/ConversationActionItems.js +488 -0
- package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
- package/runtime/arcane/modules/ConversationTimebox.js +527 -0
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
- package/runtime/arcane/modules/DBLS.js +171 -0
- package/runtime/arcane/modules/DBOPFS.js +1154 -0
- package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
- package/runtime/arcane/modules/DataMaintenance.js +91 -0
- package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
- package/runtime/arcane/modules/DirectoryPicker.js +109 -0
- package/runtime/arcane/modules/DocumentNavigation.js +223 -0
- package/runtime/arcane/modules/Errors.js +1025 -0
- package/runtime/arcane/modules/GifEncoder.js +29 -0
- package/runtime/arcane/modules/HTMLImport.js +114 -0
- package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
- package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
- package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
- package/runtime/arcane/modules/MD.js +111 -0
- package/runtime/arcane/modules/Mail.js +352 -0
- package/runtime/arcane/modules/MailTransport.mjs +180 -0
- package/runtime/arcane/modules/Marked.min.js +71 -0
- package/runtime/arcane/modules/MemoryRecords.js +44 -0
- package/runtime/arcane/modules/MessageAdvisory.js +38 -0
- package/runtime/arcane/modules/ModelDefinition.js +189 -0
- package/runtime/arcane/modules/Ollama.js +74 -0
- package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
- package/runtime/arcane/modules/OllamaSettings.js +24 -0
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
- package/runtime/arcane/modules/PreferenceStore.js +109 -0
- package/runtime/arcane/modules/QRCode.min.js +1 -0
- package/runtime/arcane/modules/Questionnaire.js +61 -0
- package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
- package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
- package/runtime/arcane/modules/RecordReviewStore.js +94 -0
- package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
- package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
- package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
- package/runtime/arcane/modules/ScreenCapture.js +20 -0
- package/runtime/arcane/modules/SpeechPlayback.js +581 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
- package/runtime/arcane/modules/SystemAppearance.js +20 -0
- package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
- package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
- package/runtime/arcane/modules/TerminalClient.js +52 -0
- package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
- package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
- package/runtime/arcane/modules/ThemeManager.js +131 -0
- package/runtime/arcane/modules/TimeGuard.js +149 -0
- package/runtime/arcane/modules/ToolCallRouter.js +83 -0
- package/runtime/arcane/modules/WaitForComponent.js +102 -0
- package/runtime/arcane/modules/YouTubeMedia.js +16 -0
- package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
- package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
- package/runtime/arcane/modules/uPlot.min.css +1 -0
- package/runtime/arcane/security/arcane-network-policy.json +6 -0
- package/runtime/strong-type/index.js +352 -0
- package/runtime/strong-type/licence +21 -0
- package/runtime/strong-type/package.json +45 -0
- package/schemas/arcane-app-bundle.schema.json +125 -0
- package/schemas/arcane-app.schema.json +329 -0
- package/schemas/arcane-lock.schema.json +86 -0
- package/schemas/arcane-package.schema.json +224 -0
- package/schemas/cli-event.schema.json +122 -0
- package/schemas/event-stack.schema.json +152 -0
- package/schemas/native-build-plan.schema.json +176 -0
- package/schemas/target-adapter.schema.json +117 -0
- package/src/app-descriptor.mjs +500 -0
- package/src/cli/main.mjs +561 -0
- package/src/constants.mjs +31 -0
- package/src/dev-server.mjs +718 -0
- package/src/doctor.mjs +315 -0
- package/src/dom-event-instrumentation.mjs +594 -0
- package/src/errors.mjs +75 -0
- package/src/event-manager.mjs +1342 -0
- package/src/event-queue.mjs +138 -0
- package/src/events.mjs +219 -0
- package/src/index.mjs +177 -0
- package/src/integrated-provider-loader.mjs +432 -0
- package/src/native-plan.mjs +698 -0
- package/src/native-provider-loader.mjs +1126 -0
- package/src/packager/core.mjs +2691 -0
- package/src/process.mjs +353 -0
- package/src/release-bundle.mjs +2523 -0
- package/src/repository.mjs +90 -0
- package/src/runtime.mjs +452 -0
- package/src/scaffold.mjs +380 -0
- package/src/targets/index.mjs +436 -0
- package/src/templates/assets/app-icon.png +0 -0
- package/src/templates/workspace-template.mjs +388 -0
- package/src/testing-loader.mjs +9 -0
- package/src/testing.mjs +427 -0
- package/src/toolchain.mjs +1335 -0
- package/src/update-check.mjs +307 -0
- package/src/workspace.mjs +449 -0
|
@@ -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
|
+
}
|
package/src/errors.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const ERROR_CODES=Object.freeze({
|
|
2
|
+
usage:'ARCANE_USAGE',
|
|
3
|
+
workspaceInvalid:'ARCANE_WORKSPACE_INVALID',
|
|
4
|
+
prerequisiteMissing:'ARCANE_PREREQUISITE_MISSING',
|
|
5
|
+
targetUnavailable:'ARCANE_TARGET_UNAVAILABLE',
|
|
6
|
+
targetDeferred:'ARCANE_TARGET_DEFERRED',
|
|
7
|
+
nativeRunUnsupported:'ARCANE_NATIVE_RUN_UNSUPPORTED',
|
|
8
|
+
updateCheckFailed:'ARCANE_UPDATE_CHECK_FAILED',
|
|
9
|
+
policyDenied:'ARCANE_POLICY_DENIED',
|
|
10
|
+
integrityFailed:'ARCANE_INTEGRITY_FAILED',
|
|
11
|
+
operationFailed:'ARCANE_OPERATION_FAILED',
|
|
12
|
+
cancelled:'ARCANE_CANCELLED'
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export class ArcaneError extends Error{
|
|
16
|
+
constructor(code,message,{details,cause,exitCode}={}){
|
|
17
|
+
super(message,{cause});
|
|
18
|
+
this.name='ArcaneError';
|
|
19
|
+
this.code=code||ERROR_CODES.operationFailed;
|
|
20
|
+
this.details=details;
|
|
21
|
+
this.exitCode=Number.isInteger(exitCode)?exitCode:1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function fail(code,message,options){
|
|
26
|
+
throw new ArcaneError(code,message,options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function throwIfAborted(signal){
|
|
30
|
+
if(signal?.aborted){
|
|
31
|
+
throw new ArcaneError(
|
|
32
|
+
ERROR_CODES.cancelled,
|
|
33
|
+
'The Arcane operation was cancelled.',
|
|
34
|
+
{cause:signal.reason,exitCode:130}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizeError(error,fallbackCode=ERROR_CODES.operationFailed){
|
|
40
|
+
if(error instanceof ArcaneError){
|
|
41
|
+
return error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if(error?.name==='AbortError'||error?.code==='ABORT_ERR'
|
|
45
|
+
||error?.code===ERROR_CODES.cancelled){
|
|
46
|
+
return new ArcaneError(
|
|
47
|
+
ERROR_CODES.cancelled,
|
|
48
|
+
'The Arcane operation was cancelled.',
|
|
49
|
+
{cause:error,exitCode:130}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const reportedCode=typeof error?.code==='string'&&/^ARCANE_[A-Z0-9_]+$/u.test(error.code)
|
|
54
|
+
?error.code
|
|
55
|
+
:fallbackCode;
|
|
56
|
+
|
|
57
|
+
return new ArcaneError(
|
|
58
|
+
reportedCode,
|
|
59
|
+
error instanceof Error?error.message:String(error??'The Arcane operation failed.'),
|
|
60
|
+
{
|
|
61
|
+
cause:error,
|
|
62
|
+
details:error?.details,
|
|
63
|
+
exitCode:Number.isInteger(error?.exitCode)?error.exitCode:undefined
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function errorRecord(error){
|
|
69
|
+
const normalized=normalizeError(error);
|
|
70
|
+
return {
|
|
71
|
+
code:normalized.code,
|
|
72
|
+
message:normalized.message,
|
|
73
|
+
...(normalized.details===undefined?{}:{details:normalized.details})
|
|
74
|
+
};
|
|
75
|
+
}
|