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.
Files changed (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,1342 @@
1
+ import EventPubSub from 'event-pubsub';
2
+ import {createDOMInstrumentation} from './dom-event-instrumentation.mjs';
3
+
4
+ export {
5
+ createDOMInstrumentation,
6
+ DEFAULT_DOM_EVENT_TYPES,
7
+ describeDOMTarget,
8
+ DOM_INTERACTION_EVENT,
9
+ DOM_MUTATION_EVENT,
10
+ DOM_OBSERVATION_STARTED_EVENT,
11
+ DOM_OBSERVATION_STOPPED_EVENT,
12
+ domSelector
13
+ } from './dom-event-instrumentation.mjs';
14
+
15
+ export const ARCANE_EVENT_STACK_PROTOCOL='arcane-event-stack/1';
16
+ export const TIME_TRAVEL_SEEK_EVENT='arcane.time-travel.seek';
17
+ export const PLAYBACK_STARTED_EVENT='arcane.time-travel.playback.started';
18
+ export const PLAYBACK_RECORD_EVENT='arcane.time-travel.playback.record';
19
+ export const PLAYBACK_COMPLETED_EVENT='arcane.time-travel.playback.completed';
20
+ export const PLAYBACK_CANCELLED_EVENT='arcane.time-travel.playback.cancelled';
21
+ export const PLAYBACK_FAILED_EVENT='arcane.time-travel.playback.failed';
22
+ export const TIME_TRAVEL_OVERFLOW_EVENT='arcane.time-travel.overflow';
23
+
24
+ const SENSITIVE_KEY_PATTERN=/(?:authorization|cookie|credential|pass(?:word|phrase)?|private.?key|secret|session.?token|token|api.?key)/iu;
25
+ const PRIVATE_CONTENT_KEY_PATTERN=/^(?:data|detail|key)$/iu;
26
+ const INTERNAL_STACK_PATTERN=/(?:EventManager\.|#dispatch|event-manager\.mjs)/u;
27
+ const URL_PATTERN=/\b(?:blob|data|file|ftp|ftps|http|https|ws|wss):[^\s<>'"\])}]+/giu;
28
+ const DEFAULT_MAX_EVENTS=10_000;
29
+ const DEFAULT_MAX_SNAPSHOT_DEPTH=50;
30
+ const DEFAULT_MAX_SNAPSHOT_ENTRIES=1_000;
31
+ const DEFAULT_MAX_SNAPSHOT_STRING_LENGTH=10_000;
32
+ const MIN_SNAPSHOT_STRING_LENGTH=64;
33
+ const DOCUMENT_KEYS=Object.freeze(['protocol','sessionId','createdAt','events']);
34
+ const RECORD_KEYS=Object.freeze([
35
+ 'protocol','sessionId','id','sequence','timestamp','monotonicMs','type','source',
36
+ 'category','correlationId','causationId','parentSequence','depth','stack','payload',
37
+ 'metadata','status','completedAt','durationMs','error'
38
+ ]);
39
+ const EVENT_STATUSES=new Set(['dispatching','completed','failed']);
40
+ const DATE_TO_ISO=Date.prototype.toISOString;
41
+ const REGEXP_SOURCE_GETTER=Object.getOwnPropertyDescriptor(RegExp.prototype,'source')?.get;
42
+ const REGEXP_FLAG_GETTERS=Object.freeze([
43
+ ['d',Object.getOwnPropertyDescriptor(RegExp.prototype,'hasIndices')?.get],
44
+ ['g',Object.getOwnPropertyDescriptor(RegExp.prototype,'global')?.get],
45
+ ['i',Object.getOwnPropertyDescriptor(RegExp.prototype,'ignoreCase')?.get],
46
+ ['m',Object.getOwnPropertyDescriptor(RegExp.prototype,'multiline')?.get],
47
+ ['s',Object.getOwnPropertyDescriptor(RegExp.prototype,'dotAll')?.get],
48
+ ['u',Object.getOwnPropertyDescriptor(RegExp.prototype,'unicode')?.get],
49
+ ['v',Object.getOwnPropertyDescriptor(RegExp.prototype,'unicodeSets')?.get],
50
+ ['y',Object.getOwnPropertyDescriptor(RegExp.prototype,'sticky')?.get]
51
+ ]);
52
+ const MAP_ENTRIES=Map.prototype.entries;
53
+ const MAP_SIZE_GETTER=Object.getOwnPropertyDescriptor(Map.prototype,'size')?.get;
54
+ const SET_VALUES=Set.prototype.values;
55
+ const SET_SIZE_GETTER=Object.getOwnPropertyDescriptor(Set.prototype,'size')?.get;
56
+ const TYPED_ARRAY_PROTOTYPE=Object.getPrototypeOf(Uint8Array.prototype);
57
+ const TYPED_ARRAY_LENGTH_GETTER=
58
+ Object.getOwnPropertyDescriptor(TYPED_ARRAY_PROTOTYPE,'length')?.get;
59
+ const TYPED_ARRAY_VALUES=TYPED_ARRAY_PROTOTYPE.values;
60
+ const TYPED_ARRAY_TAG_GETTER=
61
+ Object.getOwnPropertyDescriptor(TYPED_ARRAY_PROTOTYPE,Symbol.toStringTag)?.get;
62
+ const DATA_VIEW_BUFFER_GETTER=Object.getOwnPropertyDescriptor(DataView.prototype,'buffer')?.get;
63
+ const DATA_VIEW_BYTE_LENGTH_GETTER=
64
+ Object.getOwnPropertyDescriptor(DataView.prototype,'byteLength')?.get;
65
+ const DATA_VIEW_BYTE_OFFSET_GETTER=
66
+ Object.getOwnPropertyDescriptor(DataView.prototype,'byteOffset')?.get;
67
+
68
+ function dataObject(entries=[]){
69
+ const result=Object.create(null);
70
+ for(const [key,value] of entries){
71
+ Object.defineProperty(result,key,{
72
+ value,
73
+ enumerable:true,
74
+ configurable:true,
75
+ writable:true
76
+ });
77
+ }
78
+ return result;
79
+ }
80
+
81
+ function snapshotObject(entries=[]){
82
+ const result={};
83
+ for(const [key,value] of entries){
84
+ Object.defineProperty(result,key,{
85
+ value,
86
+ enumerable:true,
87
+ configurable:true,
88
+ writable:true
89
+ });
90
+ }
91
+ return result;
92
+ }
93
+
94
+ function taggedSnapshot(type,entries=[]){
95
+ return snapshotObject([['$type',type],...entries]);
96
+ }
97
+
98
+ function dataProperty(value,key,{inherited=false}={}){
99
+ let current=value;
100
+ while(current!==null&&(typeof current==='object'||typeof current==='function')){
101
+ const descriptor=Object.getOwnPropertyDescriptor(current,key);
102
+ if(descriptor){
103
+ return 'value' in descriptor
104
+ ?{found:true,readable:true,value:descriptor.value}
105
+ :{found:true,readable:false,value:undefined};
106
+ }
107
+ if(!inherited)break;
108
+ current=Object.getPrototypeOf(current);
109
+ }
110
+ return {found:false,readable:false,value:undefined};
111
+ }
112
+
113
+ function safeDataString(value,key,{fallback='',inherited=false}={}){
114
+ try{
115
+ const property=dataProperty(value,key,{inherited});
116
+ if(property.found&&!property.readable)return '[UNREADABLE]';
117
+ if(property.readable&&typeof property.value==='string')return property.value;
118
+ }catch{}
119
+ return fallback;
120
+ }
121
+
122
+ function regexpFlags(value){
123
+ let result='';
124
+ for(const [flag,getter] of REGEXP_FLAG_GETTERS){
125
+ if(typeof getter==='function'&&getter.call(value))result+=flag;
126
+ }
127
+ return result;
128
+ }
129
+
130
+ function boundedString(value,maxLength,{redactSensitive=true}={}){
131
+ let result=String(value);
132
+ if(redactSensitive)result=result.replace(URL_PATTERN,'[REDACTED URL]');
133
+ return result.length<=maxLength?result:`${result.slice(0,maxLength)}…`;
134
+ }
135
+
136
+ function safeErrorText(error,maxLength,{redactSensitive=true}={}){
137
+ let text='Snapshot capture failed.';
138
+ try{
139
+ if(error!==null&&(typeof error==='object'||typeof error==='function')){
140
+ const message=safeDataString(error,'message');
141
+ if(message)text=message;
142
+ }else if(error!==undefined){
143
+ text=String(error);
144
+ }
145
+ }catch{}
146
+ try{return boundedString(text,maxLength,{redactSensitive});}
147
+ catch{return 'Snapshot capture failed.';}
148
+ }
149
+
150
+ function sessionIdentifier(){
151
+ if(typeof globalThis.crypto?.randomUUID==='function')return globalThis.crypto.randomUUID();
152
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
153
+ }
154
+
155
+ function clockDate(clock){
156
+ const value=clock();
157
+ const result=value instanceof Date?new Date(value.getTime()):new Date(value);
158
+ if(Number.isNaN(result.getTime()))throw new TypeError('The event clock returned an invalid timestamp.');
159
+ return result;
160
+ }
161
+
162
+ function monotonicValue(now){
163
+ const value=Number(now());
164
+ if(!Number.isFinite(value)||value<0){
165
+ throw new TypeError('The monotonic event clock returned an invalid value.');
166
+ }
167
+ return value;
168
+ }
169
+
170
+ function defaultMonotonicClock(){
171
+ return typeof globalThis.performance?.now==='function'
172
+ ?globalThis.performance.now()
173
+ :Date.now();
174
+ }
175
+
176
+ function sourceStack(){
177
+ const stack=new Error('Arcane event source').stack;
178
+ if(typeof stack!=='string')return null;
179
+ const lines=stack.split(/\r?\n/u);
180
+ const filtered=[lines[0],...lines.slice(1).filter(line=>!INTERNAL_STACK_PATTERN.test(line))];
181
+ return filtered.join('\n');
182
+ }
183
+
184
+ function snapshot(value,{
185
+ redactSensitive=true,
186
+ captureStacks=false,
187
+ maxDepth=DEFAULT_MAX_SNAPSHOT_DEPTH,
188
+ maxEntries=DEFAULT_MAX_SNAPSHOT_ENTRIES,
189
+ maxStringLength=DEFAULT_MAX_SNAPSHOT_STRING_LENGTH,
190
+ key='',
191
+ path='$',
192
+ depth=0,
193
+ seen=new Map()
194
+ }={}){
195
+ if(redactSensitive&&key
196
+ &&(SENSITIVE_KEY_PATTERN.test(key)||PRIVATE_CONTENT_KEY_PATTERN.test(key))){
197
+ return '[REDACTED]';
198
+ }
199
+ if(value===null||value===undefined||typeof value==='boolean')return value;
200
+ if(typeof value==='string')return boundedString(value,maxStringLength,{redactSensitive});
201
+ if(typeof value==='number')return Number.isFinite(value)
202
+ ?value
203
+ :taggedSnapshot('number',[['value',String(value)]]);
204
+ if(typeof value==='bigint')return taggedSnapshot('bigint',[[
205
+ 'value',boundedString(value.toString(),maxStringLength,{redactSensitive})
206
+ ]]);
207
+ if(typeof value==='symbol')return taggedSnapshot('symbol',[[
208
+ 'value',value.description===undefined
209
+ ?null
210
+ :boundedString(value.description,maxStringLength,{redactSensitive})
211
+ ]]);
212
+ if(typeof value==='function'){
213
+ const name=safeDataString(value,'name');
214
+ return taggedSnapshot('function',[[
215
+ 'name',name?boundedString(name,maxStringLength,{redactSensitive}):null
216
+ ]]);
217
+ }
218
+ if(depth>=maxDepth)return taggedSnapshot('depth-limit',[[
219
+ 'path',boundedString(path,maxStringLength,{redactSensitive})
220
+ ]]);
221
+ if(seen.has(value))return snapshotObject([[
222
+ '$ref',boundedString(seen.get(value),maxStringLength,{redactSensitive})
223
+ ]]);
224
+ seen.set(value,path);
225
+
226
+ const next=(item,itemKey,itemPath)=>{
227
+ try{
228
+ return snapshot(item,{
229
+ redactSensitive,captureStacks,maxDepth,maxEntries,maxStringLength,
230
+ key:itemKey,
231
+ path:boundedString(itemPath,maxStringLength,{redactSensitive}),
232
+ depth:depth+1,
233
+ seen
234
+ });
235
+ }catch(error){
236
+ return snapshotFailure(error,{
237
+ redactSensitive,maxStringLength,
238
+ path:itemPath
239
+ });
240
+ }
241
+ };
242
+ if(value instanceof Date)return taggedSnapshot('date',[['value',DATE_TO_ISO.call(value)]]);
243
+ if(value instanceof RegExp)return taggedSnapshot('regexp',[
244
+ ['source',boundedString(
245
+ REGEXP_SOURCE_GETTER.call(value),maxStringLength,{redactSensitive}
246
+ )],
247
+ ['flags',regexpFlags(value)]
248
+ ]);
249
+ if(value instanceof Error){
250
+ const name=safeDataString(value,'name',{fallback:'Error',inherited:true});
251
+ const message=safeDataString(value,'message');
252
+ const stack=safeDataString(value,'stack');
253
+ const entries=[
254
+ ['name',boundedString(name||'Error',maxStringLength,{redactSensitive})],
255
+ ['message',boundedString(message,maxStringLength,{redactSensitive})],
256
+ ['stack',captureStacks&&stack
257
+ ?boundedString(stack,maxStringLength,{redactSensitive})
258
+ :null]
259
+ ];
260
+ let causeDescriptor;
261
+ try{causeDescriptor=Object.getOwnPropertyDescriptor(value,'cause');}catch{}
262
+ if(causeDescriptor){
263
+ entries.push(['cause','value' in causeDescriptor
264
+ ?next(causeDescriptor.value,'cause',`${path}.cause`)
265
+ :taggedSnapshot('unreadable',[['error','Accessor properties are not evaluated.']])]);
266
+ }
267
+ return taggedSnapshot('error',entries);
268
+ }
269
+ if(Array.isArray(value)){
270
+ const result=[];
271
+ const limit=Math.min(value.length,maxEntries);
272
+ for(let index=0;index<limit;index+=1){
273
+ const descriptor=Object.getOwnPropertyDescriptor(value,String(index));
274
+ const item=descriptor&&'value' in descriptor
275
+ ?descriptor.value
276
+ :taggedSnapshot('unreadable',[['error','Accessor properties are not evaluated.']]);
277
+ result.push(next(item,String(index),`${path}[${index}]`));
278
+ }
279
+ if(value.length>maxEntries){
280
+ result.push(taggedSnapshot('entries-truncated',[
281
+ ['omitted',value.length-maxEntries]
282
+ ]));
283
+ }
284
+ return result;
285
+ }
286
+ if(value instanceof Map){
287
+ const entries=[];
288
+ let index=0;
289
+ for(const [mapKey,item] of MAP_ENTRIES.call(value)){
290
+ if(index>=maxEntries)break;
291
+ entries.push([
292
+ next(mapKey,'mapKey',`${path}.entries[${index}].key`),
293
+ next(item,'value',`${path}.entries[${index}].value`)
294
+ ]);
295
+ index+=1;
296
+ }
297
+ const size=MAP_SIZE_GETTER.call(value);
298
+ return taggedSnapshot('map',[
299
+ ['entries',entries],
300
+ ...(size>index?[['omitted',size-index]]:[])
301
+ ]);
302
+ }
303
+ if(value instanceof Set){
304
+ const values=[];
305
+ let index=0;
306
+ for(const item of SET_VALUES.call(value)){
307
+ if(index>=maxEntries)break;
308
+ values.push(next(item,String(index),`${path}.values[${index}]`));
309
+ index+=1;
310
+ }
311
+ const size=SET_SIZE_GETTER.call(value);
312
+ return taggedSnapshot('set',[
313
+ ['values',values],
314
+ ...(size>index?[['omitted',size-index]]:[])
315
+ ]);
316
+ }
317
+ if(ArrayBuffer.isView(value)){
318
+ const dataView=value instanceof DataView;
319
+ const length=dataView
320
+ ?DATA_VIEW_BYTE_LENGTH_GETTER.call(value)
321
+ :TYPED_ARRAY_LENGTH_GETTER.call(value);
322
+ let values;
323
+ let type='DataView';
324
+ if(dataView){
325
+ const buffer=DATA_VIEW_BUFFER_GETTER.call(value);
326
+ const byteOffset=DATA_VIEW_BYTE_OFFSET_GETTER.call(value);
327
+ values=Array.from(new Uint8Array(buffer,byteOffset,Math.min(length,maxEntries)));
328
+ }else{
329
+ type=TYPED_ARRAY_TAG_GETTER.call(value)??'TypedArray';
330
+ values=[];
331
+ const iterator=TYPED_ARRAY_VALUES.call(value);
332
+ while(values.length<Math.min(length,maxEntries)){
333
+ const step=iterator.next();
334
+ if(step.done)break;
335
+ values.push(step.value);
336
+ }
337
+ }
338
+ return taggedSnapshot(type,[
339
+ ['values',values],
340
+ ...(length>maxEntries?[['omitted',length-maxEntries]]:[])
341
+ ]);
342
+ }
343
+ if(value instanceof ArrayBuffer){
344
+ const bytes=new Uint8Array(value);
345
+ return taggedSnapshot('ArrayBuffer',[
346
+ ['values',Array.from(bytes.subarray(0,maxEntries))],
347
+ ...(bytes.length>maxEntries?[['omitted',bytes.length-maxEntries]]:[])
348
+ ]);
349
+ }
350
+
351
+ const result=snapshotObject();
352
+ const properties=Reflect.ownKeys(value).filter(property=>{
353
+ if(typeof property!=='string')return false;
354
+ const descriptor=Object.getOwnPropertyDescriptor(value,property);
355
+ return descriptor?.enumerable===true;
356
+ });
357
+ for(let index=0;index<Math.min(properties.length,maxEntries);index+=1){
358
+ const property=properties[index];
359
+ const descriptor=Object.getOwnPropertyDescriptor(value,property);
360
+ const item=descriptor&&'value' in descriptor
361
+ ?descriptor.value
362
+ :taggedSnapshot('unreadable',[['error','Accessor properties are not evaluated.']]);
363
+ let outputKey=boundedString(property,maxStringLength,{redactSensitive:false});
364
+ if(Object.hasOwn(result,outputKey))outputKey=`$arcaneCollision:${String(index)}`;
365
+ Object.defineProperty(result,outputKey,{
366
+ value:next(item,property,`${path}.${property}`),
367
+ enumerable:true,
368
+ configurable:true,
369
+ writable:true
370
+ });
371
+ }
372
+ if(properties.length>maxEntries){
373
+ Object.defineProperty(result,'$arcaneTruncated',{
374
+ value:snapshotObject([['omitted',properties.length-maxEntries]]),
375
+ enumerable:true,
376
+ configurable:true,
377
+ writable:true
378
+ });
379
+ }
380
+ return result;
381
+ }
382
+
383
+ function snapshotFailure(error,{
384
+ redactSensitive=true,
385
+ maxStringLength=DEFAULT_MAX_SNAPSHOT_STRING_LENGTH,
386
+ path='$'
387
+ }={}){
388
+ const name=safeDataString(error,'name',{fallback:'Error',inherited:true})||'Error';
389
+ return taggedSnapshot('snapshot-failed',[
390
+ ['name',boundedString(name,maxStringLength,{redactSensitive})],
391
+ ['message',safeErrorText(error,maxStringLength,{redactSensitive})],
392
+ ['path',boundedString(path,maxStringLength,{redactSensitive})]
393
+ ]);
394
+ }
395
+
396
+ function deepFreeze(value,seen=new WeakSet()){
397
+ if(!value||typeof value!=='object'||seen.has(value))return value;
398
+ seen.add(value);
399
+ for(const property of Reflect.ownKeys(value)){
400
+ const descriptor=Object.getOwnPropertyDescriptor(value,property);
401
+ if(descriptor&&'value' in descriptor)deepFreeze(descriptor.value,seen);
402
+ }
403
+ return Object.freeze(value);
404
+ }
405
+
406
+ function frozenSnapshot(value,options){
407
+ try{return deepFreeze(snapshot(value,options));}
408
+ catch(error){return deepFreeze(snapshotFailure(error,options));}
409
+ }
410
+
411
+ function frozenErrorSnapshot(value,options){
412
+ const captured=frozenSnapshot(value,options);
413
+ if(captured&&typeof captured==='object'&&!Array.isArray(captured))return captured;
414
+ return deepFreeze(taggedSnapshot('thrown',[['value',captured]]));
415
+ }
416
+
417
+ function normalizeMetadata(metadata){
418
+ if(metadata===undefined)return {};
419
+ if(metadata===null||typeof metadata!=='object'||Array.isArray(metadata)){
420
+ throw new TypeError('Event instrumentation metadata must be an object.');
421
+ }
422
+ return metadata;
423
+ }
424
+
425
+ function abortError(reason){
426
+ if(reason instanceof Error)return reason;
427
+ const error=new Error(reason===undefined?'Playback was aborted.':String(reason));
428
+ error.name='AbortError';
429
+ return error;
430
+ }
431
+
432
+ function wait(milliseconds,signal){
433
+ if(milliseconds<=0)return Promise.resolve();
434
+ return new Promise((resolve,reject)=>{
435
+ if(signal?.aborted){
436
+ reject(abortError(signal.reason));
437
+ return;
438
+ }
439
+ const timer=setTimeout(done,milliseconds);
440
+ function done(){
441
+ signal?.removeEventListener?.('abort',cancel);
442
+ resolve();
443
+ }
444
+ function cancel(){
445
+ clearTimeout(timer);
446
+ signal?.removeEventListener?.('abort',cancel);
447
+ reject(abortError(signal.reason));
448
+ }
449
+ signal?.addEventListener?.('abort',cancel,{once:true});
450
+ });
451
+ }
452
+
453
+ function invalidStack(label){
454
+ throw new TypeError(`${label} is invalid.`);
455
+ }
456
+
457
+ function exactDataObject(value,expectedKeys,label){
458
+ try{
459
+ if(!value||typeof value!=='object'||Array.isArray(value))invalidStack(label);
460
+ const prototype=Object.getPrototypeOf(value);
461
+ if(prototype!==Object.prototype&&prototype!==null)invalidStack(label);
462
+ const keys=Reflect.ownKeys(value);
463
+ if(keys.length!==expectedKeys.length||keys.some(key=>typeof key!=='string')){
464
+ invalidStack(label);
465
+ }
466
+ const expected=new Set(expectedKeys);
467
+ if(keys.some(key=>!expected.has(key)))invalidStack(label);
468
+ const result=dataObject();
469
+ for(const key of expectedKeys){
470
+ const descriptor=Object.getOwnPropertyDescriptor(value,key);
471
+ if(!descriptor||descriptor.enumerable!==true||!('value' in descriptor)){
472
+ invalidStack(label);
473
+ }
474
+ Object.defineProperty(result,key,{
475
+ value:descriptor.value,
476
+ enumerable:true,
477
+ configurable:true,
478
+ writable:true
479
+ });
480
+ }
481
+ return result;
482
+ }catch(error){
483
+ if(error instanceof TypeError&&error.message===`${label} is invalid.`)throw error;
484
+ throw new TypeError(`${label} is invalid.`,{cause:error});
485
+ }
486
+ }
487
+
488
+ function denseArrayValues(value,label,{maxLength}={}){
489
+ try{
490
+ if(!Array.isArray(value)||!Number.isSafeInteger(value.length)||value.length<0){
491
+ invalidStack(label);
492
+ }
493
+ if(maxLength!==undefined&&value.length>maxLength)invalidStack(label);
494
+ const keys=Reflect.ownKeys(value);
495
+ if(keys.some(key=>typeof key!=='string'))invalidStack(label);
496
+ const expected=new Set(['length']);
497
+ for(let index=0;index<value.length;index+=1)expected.add(String(index));
498
+ if(keys.length!==expected.size||keys.some(key=>!expected.has(key)))invalidStack(label);
499
+ const result=[];
500
+ for(let index=0;index<value.length;index+=1){
501
+ const descriptor=Object.getOwnPropertyDescriptor(value,String(index));
502
+ if(!descriptor||descriptor.enumerable!==true||!('value' in descriptor)){
503
+ invalidStack(label);
504
+ }
505
+ result.push(descriptor.value);
506
+ }
507
+ return result;
508
+ }catch(error){
509
+ if(error instanceof TypeError&&error.message===`${label} is invalid.`)throw error;
510
+ throw new TypeError(`${label} is invalid.`,{cause:error});
511
+ }
512
+ }
513
+
514
+ function cloneImportedValue(value,{
515
+ maxDepth,
516
+ maxEntries,
517
+ maxStringLength,
518
+ path='$',
519
+ depth=0,
520
+ seen=new WeakSet()
521
+ }){
522
+ if(value===null||typeof value==='boolean')return value;
523
+ if(typeof value==='number'){
524
+ if(!Number.isFinite(value))invalidStack(`Event stack value at ${path}`);
525
+ return value;
526
+ }
527
+ if(typeof value==='string'){
528
+ if(value.length>maxStringLength+1
529
+ ||(value.length===maxStringLength+1&&!value.endsWith('…'))){
530
+ invalidStack(`Event stack value at ${path}`);
531
+ }
532
+ return value;
533
+ }
534
+ if(!value||typeof value!=='object')invalidStack(`Event stack value at ${path}`);
535
+ if(depth>maxDepth||seen.has(value))invalidStack(`Event stack value at ${path}`);
536
+ seen.add(value);
537
+
538
+ if(Array.isArray(value)){
539
+ const items=denseArrayValues(value,`Event stack array at ${path}`,{
540
+ maxLength:maxEntries+1
541
+ });
542
+ return items.map((item,index)=>cloneImportedValue(item,{
543
+ maxDepth,maxEntries,maxStringLength,
544
+ path:`${path}[${String(index)}]`,
545
+ depth:depth+1,
546
+ seen
547
+ }));
548
+ }
549
+
550
+ let prototype;
551
+ let keys;
552
+ try{
553
+ prototype=Object.getPrototypeOf(value);
554
+ keys=Reflect.ownKeys(value);
555
+ }catch(error){
556
+ throw new TypeError(`Event stack value at ${path} is invalid.`,{cause:error});
557
+ }
558
+ if((prototype!==Object.prototype&&prototype!==null)
559
+ ||keys.some(key=>typeof key!=='string')
560
+ ||keys.length>maxEntries+1){
561
+ invalidStack(`Event stack value at ${path}`);
562
+ }
563
+ const result=dataObject();
564
+ for(const key of keys){
565
+ if(key.length>maxStringLength+1
566
+ ||(key.length===maxStringLength+1&&!key.endsWith('…'))){
567
+ invalidStack(`Event stack value at ${path}`);
568
+ }
569
+ let descriptor;
570
+ try{descriptor=Object.getOwnPropertyDescriptor(value,key);}catch(error){
571
+ throw new TypeError(`Event stack value at ${path} is invalid.`,{cause:error});
572
+ }
573
+ if(!descriptor||descriptor.enumerable!==true||!('value' in descriptor)){
574
+ invalidStack(`Event stack value at ${path}`);
575
+ }
576
+ Object.defineProperty(result,key,{
577
+ value:cloneImportedValue(descriptor.value,{
578
+ maxDepth,maxEntries,maxStringLength,
579
+ path:`${path}.${key}`,
580
+ depth:depth+1,
581
+ seen
582
+ }),
583
+ enumerable:true,
584
+ configurable:true,
585
+ writable:true
586
+ });
587
+ }
588
+ return result;
589
+ }
590
+
591
+ function canonicalTimestamp(value,label){
592
+ if(typeof value!=='string')invalidStack(label);
593
+ const milliseconds=Date.parse(value);
594
+ if(!Number.isFinite(milliseconds)||new Date(milliseconds).toISOString()!==value){
595
+ invalidStack(label);
596
+ }
597
+ return milliseconds;
598
+ }
599
+
600
+ function boundedRecordString(value,label,maxStringLength,{nullable=false,empty=true}={}){
601
+ if(nullable&&value===null)return null;
602
+ if(typeof value!=='string'||(!empty&&!value)
603
+ ||value.length>maxStringLength+1
604
+ ||(value.length===maxStringLength+1&&!value.endsWith('…'))){
605
+ invalidStack(label);
606
+ }
607
+ return value;
608
+ }
609
+
610
+ function validateRecord(record,index,{
611
+ sessionId,
612
+ maxDepth,
613
+ maxEntries,
614
+ maxStringLength
615
+ }){
616
+ const label=`Event stack record ${String(index)}`;
617
+ const fields=exactDataObject(record,RECORD_KEYS,label);
618
+ if(fields.protocol!==ARCANE_EVENT_STACK_PROTOCOL||fields.sessionId!==sessionId){
619
+ invalidStack(label);
620
+ }
621
+ if(!Number.isSafeInteger(fields.sequence)||fields.sequence<1
622
+ ||fields.id!==`${sessionId}:${String(fields.sequence)}`){
623
+ invalidStack(label);
624
+ }
625
+ const timestampMs=canonicalTimestamp(fields.timestamp,`${label} timestamp`);
626
+ if(typeof fields.monotonicMs!=='number'||!Number.isFinite(fields.monotonicMs)
627
+ ||fields.monotonicMs<0){
628
+ invalidStack(`${label} monotonic timing`);
629
+ }
630
+ boundedRecordString(fields.type,`${label} type`,maxStringLength);
631
+ boundedRecordString(fields.source,`${label} source`,maxStringLength,{empty:false});
632
+ boundedRecordString(fields.category,`${label} category`,maxStringLength,{
633
+ nullable:true,empty:false
634
+ });
635
+ boundedRecordString(fields.correlationId,`${label} correlation`,maxStringLength,{
636
+ nullable:true,empty:false
637
+ });
638
+ boundedRecordString(fields.causationId,`${label} causation`,maxStringLength,{
639
+ nullable:true,empty:false
640
+ });
641
+ if(fields.parentSequence!==null
642
+ &&(!Number.isSafeInteger(fields.parentSequence)||fields.parentSequence<1
643
+ ||fields.parentSequence>=fields.sequence)){
644
+ invalidStack(`${label} parent`);
645
+ }
646
+ if(!Number.isSafeInteger(fields.depth)||fields.depth<0
647
+ ||(fields.parentSequence===null)!==(fields.depth===0)){
648
+ invalidStack(`${label} depth`);
649
+ }
650
+ boundedRecordString(fields.stack,`${label} stack`,maxStringLength,{nullable:true});
651
+ if(!EVENT_STATUSES.has(fields.status))invalidStack(`${label} status`);
652
+
653
+ const payloadValues=denseArrayValues(fields.payload,`${label} payload`,{
654
+ maxLength:maxEntries+1
655
+ });
656
+ const payload=payloadValues.map((item,payloadIndex)=>cloneImportedValue(item,{
657
+ maxDepth,maxEntries,maxStringLength,
658
+ path:`$.events[${String(index)}].payload[${String(payloadIndex)}]`,
659
+ depth:1,
660
+ seen:new WeakSet()
661
+ }));
662
+ const metadata=cloneImportedValue(fields.metadata,{
663
+ maxDepth,maxEntries,maxStringLength,
664
+ path:`$.events[${String(index)}].metadata`,
665
+ depth:0,
666
+ seen:new WeakSet()
667
+ });
668
+ if(!metadata||typeof metadata!=='object'||Array.isArray(metadata)){
669
+ invalidStack(`${label} metadata`);
670
+ }
671
+
672
+ let completedAt=null;
673
+ let durationMs=null;
674
+ let error=null;
675
+ if(fields.status==='dispatching'){
676
+ if(fields.completedAt!==null||fields.durationMs!==null||fields.error!==null){
677
+ invalidStack(`${label} dispatch timing`);
678
+ }
679
+ }else{
680
+ const completedAtMs=canonicalTimestamp(fields.completedAt,`${label} completion timestamp`);
681
+ if(completedAtMs<timestampMs
682
+ ||typeof fields.durationMs!=='number'||!Number.isFinite(fields.durationMs)
683
+ ||fields.durationMs<0){
684
+ invalidStack(`${label} completion timing`);
685
+ }
686
+ completedAt=fields.completedAt;
687
+ durationMs=fields.durationMs;
688
+ if(fields.status==='completed'){
689
+ if(fields.error!==null)invalidStack(`${label} error`);
690
+ }else{
691
+ error=cloneImportedValue(fields.error,{
692
+ maxDepth,maxEntries,maxStringLength,
693
+ path:`$.events[${String(index)}].error`,
694
+ depth:0,
695
+ seen:new WeakSet()
696
+ });
697
+ if(!error||typeof error!=='object'||Array.isArray(error)){
698
+ invalidStack(`${label} error`);
699
+ }
700
+ }
701
+ }
702
+
703
+ return dataObject([
704
+ ['protocol',ARCANE_EVENT_STACK_PROTOCOL],
705
+ ['sessionId',sessionId],
706
+ ['id',fields.id],
707
+ ['sequence',fields.sequence],
708
+ ['timestamp',fields.timestamp],
709
+ ['monotonicMs',fields.monotonicMs],
710
+ ['type',fields.type],
711
+ ['source',fields.source],
712
+ ['category',fields.category],
713
+ ['correlationId',fields.correlationId],
714
+ ['causationId',fields.causationId],
715
+ ['parentSequence',fields.parentSequence],
716
+ ['depth',fields.depth],
717
+ ['stack',fields.stack],
718
+ ['payload',payload],
719
+ ['metadata',metadata],
720
+ ['status',fields.status],
721
+ ['completedAt',completedAt],
722
+ ['durationMs',durationMs],
723
+ ['error',error]
724
+ ]);
725
+ }
726
+
727
+ export function parseEventStack(source,{
728
+ maxEvents=DEFAULT_MAX_EVENTS,
729
+ maxSnapshotDepth=DEFAULT_MAX_SNAPSHOT_DEPTH,
730
+ maxSnapshotEntries=DEFAULT_MAX_SNAPSHOT_ENTRIES,
731
+ maxSnapshotStringLength=DEFAULT_MAX_SNAPSHOT_STRING_LENGTH
732
+ }={}){
733
+ if(!Number.isSafeInteger(maxEvents)||maxEvents<1
734
+ ||!Number.isSafeInteger(maxSnapshotDepth)||maxSnapshotDepth<1
735
+ ||!Number.isSafeInteger(maxSnapshotEntries)||maxSnapshotEntries<1
736
+ ||!Number.isSafeInteger(maxSnapshotStringLength)
737
+ ||maxSnapshotStringLength<MIN_SNAPSHOT_STRING_LENGTH){
738
+ throw new RangeError(
739
+ 'Event stack import limits must be positive safe integers and the string limit must be at least 64.'
740
+ );
741
+ }
742
+ let document=source;
743
+ if(typeof source==='string'){
744
+ try{document=JSON.parse(source);}catch(error){
745
+ throw new TypeError('The event stack is not valid JSON.',{cause:error});
746
+ }
747
+ }
748
+ const fields=exactDataObject(document,DOCUMENT_KEYS,'The event stack document');
749
+ if(fields.protocol!==ARCANE_EVENT_STACK_PROTOCOL
750
+ ||typeof fields.sessionId!=='string'||!fields.sessionId
751
+ ||fields.sessionId.length>256){
752
+ invalidStack('The event stack document');
753
+ }
754
+ canonicalTimestamp(fields.createdAt,'The event stack creation timestamp');
755
+ const sourceEvents=denseArrayValues(fields.events,'The event stack events',{
756
+ maxLength:maxEvents+1
757
+ });
758
+ let previous=0;
759
+ const bySequence=new Map();
760
+ const events=sourceEvents.map((record,index)=>{
761
+ const validated=validateRecord(record,index,{
762
+ sessionId:fields.sessionId,
763
+ maxDepth:maxSnapshotDepth,
764
+ maxEntries:maxSnapshotEntries,
765
+ maxStringLength:maxSnapshotStringLength
766
+ });
767
+ if(validated.sequence<=previous){
768
+ throw new TypeError('Event stack sequences must be strictly increasing.');
769
+ }
770
+ if(validated.parentSequence!==null){
771
+ const parent=bySequence.get(validated.parentSequence);
772
+ if(!parent||validated.depth!==parent.depth+1||validated.causationId===null){
773
+ invalidStack(`Event stack record ${String(index)} parent`);
774
+ }
775
+ }
776
+ previous=validated.sequence;
777
+ bySequence.set(validated.sequence,validated);
778
+ return validated;
779
+ });
780
+ const overflowIndexes=[];
781
+ for(let index=0;index<events.length;index+=1){
782
+ if(events[index].type===TIME_TRAVEL_OVERFLOW_EVENT)overflowIndexes.push(index);
783
+ }
784
+ if(overflowIndexes.length>1
785
+ ||(overflowIndexes.length===1&&overflowIndexes[0]!==events.length-1)
786
+ ||(events.length>maxEvents
787
+ &&(events.length!==maxEvents+1||overflowIndexes[0]!==events.length-1))){
788
+ invalidStack('The event stack overflow history');
789
+ }
790
+ if(overflowIndexes.length===1){
791
+ const overflow=events.at(-1);
792
+ const payload=denseArrayValues(
793
+ overflow.payload,'The event stack overflow payload',{maxLength:1}
794
+ );
795
+ if(payload.length!==1)invalidStack('The event stack overflow payload');
796
+ const counts=exactDataObject(
797
+ payload[0],['maxEvents','retainedEvents'],'The event stack overflow payload counts'
798
+ );
799
+ const metadata=exactDataObject(
800
+ overflow.metadata,['maxEvents'],'The event stack overflow metadata'
801
+ );
802
+ const retainedEvents=events.length-1;
803
+ if(overflow.source!=='event-manager'||overflow.category!=='overflow'
804
+ ||overflow.correlationId!==null||overflow.causationId!==null
805
+ ||overflow.parentSequence!==null||overflow.depth!==0||overflow.stack!==null
806
+ ||overflow.status!=='completed'||overflow.completedAt!==overflow.timestamp
807
+ ||overflow.durationMs!==0||overflow.error!==null
808
+ ||!Number.isSafeInteger(counts.maxEvents)||counts.maxEvents<1
809
+ ||!Number.isSafeInteger(counts.retainedEvents)||counts.retainedEvents<1
810
+ ||counts.maxEvents!==counts.retainedEvents
811
+ ||metadata.maxEvents!==counts.maxEvents
812
+ ||retainedEvents!==counts.retainedEvents
813
+ ||overflow.sequence!==retainedEvents+1
814
+ ||counts.maxEvents>maxEvents){
815
+ invalidStack('The event stack overflow record');
816
+ }
817
+ }
818
+ return deepFreeze(dataObject([
819
+ ['protocol',ARCANE_EVENT_STACK_PROTOCOL],
820
+ ['sessionId',fields.sessionId],
821
+ ['createdAt',fields.createdAt],
822
+ ['events',events]
823
+ ]));
824
+ }
825
+
826
+ export class EventManager{
827
+ #activeDispatch=[];
828
+ #bus=new EventPubSub();
829
+ #captureStacks;
830
+ #clock;
831
+ #cursor=0;
832
+ #domInstrumentation=null;
833
+ #history=[];
834
+ #maxEvents;
835
+ #maxSnapshotDepth;
836
+ #maxSnapshotEntries;
837
+ #maxSnapshotStringLength;
838
+ #now;
839
+ #overflowed=false;
840
+ #redactSensitive;
841
+ #replaying=false;
842
+ #sequence=0;
843
+ #sessionId;
844
+ #timeTravelEnabled=false;
845
+
846
+ constructor({
847
+ timeTravel=false,
848
+ dom=null,
849
+ captureStacks=false,
850
+ redactSensitive=true,
851
+ maxEvents=DEFAULT_MAX_EVENTS,
852
+ maxSnapshotDepth=DEFAULT_MAX_SNAPSHOT_DEPTH,
853
+ maxSnapshotEntries=DEFAULT_MAX_SNAPSHOT_ENTRIES,
854
+ maxSnapshotStringLength=DEFAULT_MAX_SNAPSHOT_STRING_LENGTH,
855
+ clock=()=>new Date(),
856
+ now=defaultMonotonicClock,
857
+ sessionId=sessionIdentifier()
858
+ }={}){
859
+ if(typeof timeTravel!=='boolean'||typeof captureStacks!=='boolean'
860
+ ||typeof redactSensitive!=='boolean'){
861
+ throw new TypeError('EventManager flags must be boolean values.');
862
+ }
863
+ if(typeof clock!=='function'||typeof now!=='function'){
864
+ throw new TypeError('EventManager clocks must be functions.');
865
+ }
866
+ if(!Number.isSafeInteger(maxEvents)||maxEvents<1
867
+ ||!Number.isSafeInteger(maxSnapshotDepth)||maxSnapshotDepth<1
868
+ ||!Number.isSafeInteger(maxSnapshotEntries)||maxSnapshotEntries<1
869
+ ||!Number.isSafeInteger(maxSnapshotStringLength)
870
+ ||maxSnapshotStringLength<MIN_SNAPSHOT_STRING_LENGTH){
871
+ throw new RangeError(
872
+ 'EventManager retention limits must be positive safe integers and the string limit must be at least 64.'
873
+ );
874
+ }
875
+ if(typeof sessionId!=='string'||!sessionId||sessionId.length>256){
876
+ throw new TypeError('EventManager sessionId must be a non-empty string.');
877
+ }
878
+ this.#captureStacks=captureStacks;
879
+ this.#redactSensitive=redactSensitive;
880
+ this.#maxEvents=maxEvents;
881
+ this.#maxSnapshotDepth=maxSnapshotDepth;
882
+ this.#maxSnapshotEntries=maxSnapshotEntries;
883
+ this.#maxSnapshotStringLength=maxSnapshotStringLength;
884
+ this.#clock=clock;
885
+ this.#now=now;
886
+ this.#sessionId=sessionId;
887
+ this.#timeTravelEnabled=timeTravel;
888
+ if(dom)this.attachDOM(dom?.root??dom,dom?.root?dom:{});
889
+ }
890
+
891
+ get list(){return this.#bus.list;}
892
+ get sessionId(){return this.#sessionId;}
893
+ get timeTravelEnabled(){return this.#timeTravelEnabled;}
894
+ get replaying(){return this.#replaying;}
895
+ get cursor(){return this.#cursor;}
896
+ get eventCount(){return this.#history.length;}
897
+ get maxEvents(){return this.#maxEvents;}
898
+ get overflowed(){return this.#overflowed;}
899
+ get history(){return Object.freeze([...this.#history]);}
900
+ get domInstrumentation(){return this.#domInstrumentation;}
901
+
902
+ on(type,handler,once=false){
903
+ this.#bus.on(type,handler,once);
904
+ return this;
905
+ }
906
+
907
+ once(type,handler){
908
+ this.#bus.once(type,handler);
909
+ return this;
910
+ }
911
+
912
+ off(type='*',handler='*'){
913
+ this.#bus.off(type,handler);
914
+ return this;
915
+ }
916
+
917
+ reset(){
918
+ this.#bus.reset();
919
+ return this;
920
+ }
921
+
922
+ emit(type,...payload){
923
+ return this.#dispatch(type,payload,{});
924
+ }
925
+
926
+ instrument(type,payload,metadata={}){
927
+ return this.#dispatch(type,[payload],normalizeMetadata(metadata));
928
+ }
929
+
930
+ forward(event,metadata={}){
931
+ if(!event||typeof event!=='object'||Array.isArray(event)||typeof event.type!=='string'){
932
+ throw new TypeError('Forwarded events must be objects with a string type.');
933
+ }
934
+ return this.instrument(event.type,event,metadata);
935
+ }
936
+
937
+ #dispatch(type,payload,metadata){
938
+ if(typeof type!=='string'){
939
+ this.#bus.emit(type,...payload);
940
+ return this;
941
+ }
942
+ let recording=this.#timeTravelEnabled&&!this.#replaying;
943
+ if(recording&&this.#history.length>=this.#maxEvents){
944
+ this.#recordOverflow();
945
+ recording=false;
946
+ }
947
+ let draft=null;
948
+ let startedMonotonic=null;
949
+ if(recording){
950
+ try{
951
+ const timestamp=clockDate(this.#clock).toISOString();
952
+ startedMonotonic=monotonicValue(this.#now);
953
+ const snapshotOptions={
954
+ redactSensitive:this.#redactSensitive,
955
+ captureStacks:this.#captureStacks,
956
+ maxDepth:this.#maxSnapshotDepth,
957
+ maxEntries:this.#maxSnapshotEntries,
958
+ maxStringLength:this.#maxSnapshotStringLength
959
+ };
960
+ const payloadSnapshot=frozenSnapshot(payload,snapshotOptions);
961
+ const metadataSnapshot=frozenSnapshot(metadata,snapshotOptions);
962
+ const safeMetadata=metadataSnapshot&&typeof metadataSnapshot==='object'
963
+ &&!Array.isArray(metadataSnapshot)
964
+ ?metadataSnapshot
965
+ :snapshotObject();
966
+ const sequence=this.#sequence+1;
967
+ const parentSequence=this.#activeDispatch.at(-1)??null;
968
+ let stack=null;
969
+ if(this.#captureStacks){
970
+ try{
971
+ stack=boundedString(sourceStack()??'',this.#maxSnapshotStringLength,{
972
+ redactSensitive:this.#redactSensitive
973
+ });
974
+ }catch{
975
+ stack='[STACK CAPTURE FAILED]';
976
+ }
977
+ }
978
+ draft={
979
+ protocol:ARCANE_EVENT_STACK_PROTOCOL,
980
+ sessionId:this.#sessionId,
981
+ id:`${this.#sessionId}:${sequence}`,
982
+ sequence,
983
+ timestamp,
984
+ monotonicMs:startedMonotonic,
985
+ type:boundedString(type,this.#maxSnapshotStringLength,{
986
+ redactSensitive:this.#redactSensitive
987
+ }),
988
+ source:typeof safeMetadata.source==='string'&&safeMetadata.source
989
+ ?boundedString(safeMetadata.source,this.#maxSnapshotStringLength,{
990
+ redactSensitive:this.#redactSensitive
991
+ })
992
+ :'application',
993
+ category:typeof safeMetadata.category==='string'&&safeMetadata.category
994
+ ?boundedString(safeMetadata.category,this.#maxSnapshotStringLength,{
995
+ redactSensitive:this.#redactSensitive
996
+ })
997
+ :null,
998
+ correlationId:typeof safeMetadata.correlationId==='string'
999
+ &&safeMetadata.correlationId
1000
+ ?boundedString(safeMetadata.correlationId,this.#maxSnapshotStringLength,{
1001
+ redactSensitive:this.#redactSensitive
1002
+ }):null,
1003
+ causationId:typeof safeMetadata.causationId==='string'
1004
+ &&safeMetadata.causationId
1005
+ ?boundedString(safeMetadata.causationId,this.#maxSnapshotStringLength,{
1006
+ redactSensitive:this.#redactSensitive
1007
+ }):(parentSequence===null?null:`${this.#sessionId}:${parentSequence}`),
1008
+ parentSequence,
1009
+ depth:this.#activeDispatch.length,
1010
+ stack,
1011
+ payload:Array.isArray(payloadSnapshot)
1012
+ ?payloadSnapshot
1013
+ :deepFreeze([payloadSnapshot]),
1014
+ metadata:safeMetadata,
1015
+ status:'dispatching',
1016
+ completedAt:null,
1017
+ durationMs:null,
1018
+ error:null
1019
+ };
1020
+ this.#history.push(deepFreeze({...draft}));
1021
+ this.#sequence=sequence;
1022
+ this.#cursor=sequence;
1023
+ this.#activeDispatch.push(sequence);
1024
+ }catch{
1025
+ draft=null;
1026
+ startedMonotonic=null;
1027
+ }
1028
+ }
1029
+
1030
+ let listenerFailed=false;
1031
+ let listenerError;
1032
+ try{
1033
+ this.#bus.emit(type,...payload);
1034
+ }catch(error){
1035
+ listenerFailed=true;
1036
+ listenerError=error;
1037
+ }finally{
1038
+ if(draft){
1039
+ this.#finalize(
1040
+ draft,
1041
+ startedMonotonic,
1042
+ listenerFailed?'failed':'completed',
1043
+ listenerFailed?listenerError:null
1044
+ );
1045
+ this.#activeDispatch.pop();
1046
+ }
1047
+ }
1048
+ if(listenerFailed)throw listenerError;
1049
+ return this;
1050
+ }
1051
+
1052
+ #recordOverflow(){
1053
+ this.#overflowed=true;
1054
+ this.#timeTravelEnabled=false;
1055
+ try{this.#domInstrumentation?.stop({emitLifecycle:false});}catch{}
1056
+ let timestamp;
1057
+ let monotonicMs;
1058
+ try{timestamp=clockDate(this.#clock).toISOString();}
1059
+ catch{timestamp=new Date().toISOString();}
1060
+ try{monotonicMs=monotonicValue(this.#now);}
1061
+ catch{monotonicMs=Math.max(0,Number(this.#history.at(-1)?.monotonicMs??0));}
1062
+ const sequence=++this.#sequence;
1063
+ const payload=deepFreeze([snapshotObject([
1064
+ ['maxEvents',this.#maxEvents],
1065
+ ['retainedEvents',this.#history.length]
1066
+ ])]);
1067
+ const record=deepFreeze({
1068
+ protocol:ARCANE_EVENT_STACK_PROTOCOL,
1069
+ sessionId:this.#sessionId,
1070
+ id:`${this.#sessionId}:${sequence}`,
1071
+ sequence,
1072
+ timestamp,
1073
+ monotonicMs,
1074
+ type:TIME_TRAVEL_OVERFLOW_EVENT,
1075
+ source:'event-manager',
1076
+ category:'overflow',
1077
+ correlationId:null,
1078
+ causationId:null,
1079
+ parentSequence:null,
1080
+ depth:0,
1081
+ stack:null,
1082
+ payload,
1083
+ metadata:deepFreeze(snapshotObject([['maxEvents',this.#maxEvents]])),
1084
+ status:'completed',
1085
+ completedAt:timestamp,
1086
+ durationMs:0,
1087
+ error:null
1088
+ });
1089
+ this.#history.push(record);
1090
+ this.#cursor=sequence;
1091
+ }
1092
+
1093
+ #finalize(draft,startedMonotonic,status,error){
1094
+ try{
1095
+ const index=this.#history.findIndex(record=>record.sequence===draft.sequence);
1096
+ if(index<0)return;
1097
+ let completedAt=draft.timestamp;
1098
+ let durationMs=0;
1099
+ try{
1100
+ const completion=clockDate(this.#clock);
1101
+ if(completion.getTime()>=Date.parse(draft.timestamp)){
1102
+ completedAt=completion.toISOString();
1103
+ }
1104
+ }catch{}
1105
+ try{
1106
+ durationMs=Math.max(0,monotonicValue(this.#now)-startedMonotonic);
1107
+ }catch{}
1108
+ this.#history[index]=deepFreeze({
1109
+ ...draft,
1110
+ status,
1111
+ completedAt,
1112
+ durationMs,
1113
+ error:error===null?null:frozenErrorSnapshot(error,{
1114
+ redactSensitive:this.#redactSensitive,
1115
+ captureStacks:this.#captureStacks,
1116
+ maxDepth:this.#maxSnapshotDepth,
1117
+ maxEntries:this.#maxSnapshotEntries,
1118
+ maxStringLength:this.#maxSnapshotStringLength
1119
+ })
1120
+ });
1121
+ }catch{}
1122
+ }
1123
+
1124
+ enableTimeTravel({dom}={}){
1125
+ if(this.#overflowed){
1126
+ throw new Error('Clear the overflowed event history before enabling time travel again.');
1127
+ }
1128
+ const wasEnabled=this.#timeTravelEnabled;
1129
+ this.#timeTravelEnabled=true;
1130
+ try{
1131
+ if(dom)this.attachDOM(dom?.root??dom,dom?.root?dom:{});
1132
+ else this.#domInstrumentation?.start();
1133
+ }catch(error){
1134
+ this.#timeTravelEnabled=wasEnabled;
1135
+ throw error;
1136
+ }
1137
+ return this;
1138
+ }
1139
+
1140
+ disableTimeTravel(){
1141
+ try{return this.#domInstrumentation?.stop(),this;}
1142
+ finally{this.#timeTravelEnabled=false;}
1143
+ }
1144
+
1145
+ attachDOM(root=globalThis.document,options={}){
1146
+ const previous=this.#domInstrumentation;
1147
+ const replacement=createDOMInstrumentation({
1148
+ ...options,
1149
+ root,
1150
+ eventManager:this
1151
+ });
1152
+ if(previous&&previous!==replacement){
1153
+ previous.stop();
1154
+ if(this.#domInstrumentation===previous)this.#domInstrumentation=null;
1155
+ }
1156
+ try{
1157
+ if(this.#timeTravelEnabled)replacement.start();
1158
+ if(!this.#timeTravelEnabled||this.#overflowed){
1159
+ replacement.stop({emitLifecycle:false});
1160
+ }
1161
+ }catch(error){
1162
+ try{replacement.stop({emitLifecycle:false});}catch{}
1163
+ if(replacement.active||replacement.cleanupPending){
1164
+ this.#domInstrumentation=replacement;
1165
+ try{
1166
+ if(error&&(typeof error==='object'||typeof error==='function')){
1167
+ Object.defineProperty(error,'domInstrumentation',{
1168
+ value:replacement,
1169
+ enumerable:false,
1170
+ configurable:true
1171
+ });
1172
+ }
1173
+ }catch{}
1174
+ }
1175
+ throw error;
1176
+ }
1177
+ this.#domInstrumentation=replacement;
1178
+ return replacement;
1179
+ }
1180
+
1181
+ detachDOM(){
1182
+ const current=this.#domInstrumentation;
1183
+ try{current?.stop();}
1184
+ finally{
1185
+ if(this.#domInstrumentation===current
1186
+ &&!current?.active&&!current?.cleanupPending){
1187
+ this.#domInstrumentation=null;
1188
+ }
1189
+ }
1190
+ return this;
1191
+ }
1192
+
1193
+ clearHistory({newSession=true}={}){
1194
+ if(typeof newSession!=='boolean')throw new TypeError('newSession must be boolean.');
1195
+ if(this.#activeDispatch.length||this.#replaying){
1196
+ throw new Error('Event history cannot be cleared during dispatch or playback.');
1197
+ }
1198
+ this.#history=[];
1199
+ this.#sequence=0;
1200
+ this.#cursor=0;
1201
+ this.#activeDispatch=[];
1202
+ this.#overflowed=false;
1203
+ if(newSession)this.#sessionId=sessionIdentifier();
1204
+ return this;
1205
+ }
1206
+
1207
+ getEventStack({fromSequence=1,toSequence=Number.MAX_SAFE_INTEGER,type=null}={}){
1208
+ if(!Number.isSafeInteger(fromSequence)||fromSequence<1
1209
+ ||!Number.isSafeInteger(toSequence)||toSequence<fromSequence
1210
+ ||(type!==null&&typeof type!=='string')){
1211
+ throw new TypeError('The event stack range is invalid.');
1212
+ }
1213
+ return Object.freeze(this.#history.filter(record=>
1214
+ record.sequence>=fromSequence&&record.sequence<=toSequence
1215
+ &&(type===null||record.type===type)
1216
+ ));
1217
+ }
1218
+
1219
+ exportStack({space=2}={}){
1220
+ if(!Number.isSafeInteger(space)||space<0||space>10){
1221
+ throw new RangeError('Event stack JSON indentation must be from 0 through 10.');
1222
+ }
1223
+ return JSON.stringify({
1224
+ protocol:ARCANE_EVENT_STACK_PROTOCOL,
1225
+ sessionId:this.#sessionId,
1226
+ createdAt:clockDate(this.#clock).toISOString(),
1227
+ events:this.#history
1228
+ },null,space);
1229
+ }
1230
+
1231
+ seek(sequence){
1232
+ if(!Number.isSafeInteger(sequence)||sequence<0||sequence>this.#sequence){
1233
+ throw new RangeError('The time-travel sequence is outside this event stack.');
1234
+ }
1235
+ this.#cursor=sequence;
1236
+ const record=sequence===0?null:this.#history.find(item=>item.sequence===sequence)??null;
1237
+ this.#bus.emit(TIME_TRAVEL_SEEK_EVENT,{sessionId:this.#sessionId,sequence,record});
1238
+ return record;
1239
+ }
1240
+
1241
+ async playback({
1242
+ stack=null,
1243
+ fromSequence=1,
1244
+ toSequence=Number.MAX_SAFE_INTEGER,
1245
+ speed=0,
1246
+ mode='review',
1247
+ signal,
1248
+ onRecord
1249
+ }={}){
1250
+ if(!Number.isSafeInteger(fromSequence)||fromSequence<1
1251
+ ||!Number.isSafeInteger(toSequence)||toSequence<fromSequence){
1252
+ throw new TypeError('The playback range is invalid.');
1253
+ }
1254
+ if(typeof speed!=='number'||!Number.isFinite(speed)||speed<0){
1255
+ throw new RangeError('Playback speed must be zero or a positive finite number.');
1256
+ }
1257
+ if(!['review','events','none'].includes(mode)){
1258
+ throw new TypeError('Playback mode must be review, events, or none.');
1259
+ }
1260
+ if(onRecord!==undefined&&typeof onRecord!=='function'){
1261
+ throw new TypeError('onRecord must be a function.');
1262
+ }
1263
+ if(this.#replaying)throw new Error('Event playback is already active.');
1264
+ const document=stack===null
1265
+ ?{protocol:ARCANE_EVENT_STACK_PROTOCOL,sessionId:this.#sessionId,events:this.#history}
1266
+ :parseEventStack(stack,{
1267
+ maxEvents:this.#maxEvents,
1268
+ maxSnapshotDepth:this.#maxSnapshotDepth,
1269
+ maxSnapshotEntries:this.#maxSnapshotEntries,
1270
+ maxSnapshotStringLength:this.#maxSnapshotStringLength
1271
+ });
1272
+ const records=document.events.filter(record=>
1273
+ record.sequence>=fromSequence&&record.sequence<=toSequence
1274
+ );
1275
+ this.#replaying=true;
1276
+ let delivered=0;
1277
+ let previousTimestamp=null;
1278
+ let previousMonotonic=null;
1279
+ try{
1280
+ if(signal?.aborted)throw abortError(signal.reason);
1281
+ this.#bus.emit(PLAYBACK_STARTED_EVENT,{
1282
+ sessionId:document.sessionId,
1283
+ count:records.length,
1284
+ fromSequence,
1285
+ toSequence,
1286
+ speed,
1287
+ mode
1288
+ });
1289
+ for(const record of records){
1290
+ if(signal?.aborted)throw abortError(signal.reason);
1291
+ if(speed>0&&(previousTimestamp!==null||previousMonotonic!==null)){
1292
+ const monotonic=Number(record.monotonicMs);
1293
+ const difference=Number.isFinite(monotonic)&&previousMonotonic!==null
1294
+ ?Math.max(0,monotonic-previousMonotonic)
1295
+ :Math.max(0,Date.parse(record.timestamp)-previousTimestamp);
1296
+ await wait(difference/speed,signal);
1297
+ }
1298
+ previousTimestamp=Date.parse(record.timestamp);
1299
+ previousMonotonic=Number.isFinite(Number(record.monotonicMs))
1300
+ ?Number(record.monotonicMs)
1301
+ :null;
1302
+ if(mode==='review')this.#bus.emit(PLAYBACK_RECORD_EVENT,record);
1303
+ else if(mode==='events')this.#bus.emit(record.type,...record.payload);
1304
+ await onRecord?.(record);
1305
+ this.#cursor=record.sequence;
1306
+ delivered+=1;
1307
+ }
1308
+ const result=Object.freeze({
1309
+ sessionId:document.sessionId,
1310
+ delivered,
1311
+ cursor:this.#cursor,
1312
+ completed:true
1313
+ });
1314
+ this.#bus.emit(PLAYBACK_COMPLETED_EVENT,result);
1315
+ return result;
1316
+ }catch(error){
1317
+ const cancelled=signal?.aborted||error?.name==='AbortError';
1318
+ this.#bus.emit(cancelled?PLAYBACK_CANCELLED_EVENT:PLAYBACK_FAILED_EVENT,Object.freeze({
1319
+ sessionId:document.sessionId,
1320
+ delivered,
1321
+ cursor:this.#cursor,
1322
+ completed:false,
1323
+ error:frozenSnapshot(error,{
1324
+ redactSensitive:this.#redactSensitive,
1325
+ captureStacks:this.#captureStacks,
1326
+ maxDepth:this.#maxSnapshotDepth,
1327
+ maxEntries:this.#maxSnapshotEntries,
1328
+ maxStringLength:this.#maxSnapshotStringLength
1329
+ })
1330
+ }));
1331
+ throw error;
1332
+ }finally{
1333
+ this.#replaying=false;
1334
+ }
1335
+ }
1336
+ }
1337
+
1338
+ export function createEventManager(options){
1339
+ return new EventManager(options);
1340
+ }
1341
+
1342
+ export const arcaneEvents=new EventManager();