@pb33f/cowboy-components 0.7.5 → 0.7.6

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 (53) hide show
  1. package/dist/components/auth/login-button.d.ts +2 -0
  2. package/dist/components/auth/login-button.js +15 -3
  3. package/dist/components/auth/login-panel.js +1 -1
  4. package/dist/components/auth/oauth-login.d.ts +1 -0
  5. package/dist/components/auth/oauth-login.js +11 -5
  6. package/dist/components/editor/editor-breadcrumb.css.js +1 -1
  7. package/dist/components/model-renderer/rendered-node.d.ts +2 -0
  8. package/dist/components/model-renderer/rendered-node.js +18 -0
  9. package/dist/components/model-renderer/responses.d.ts +11 -0
  10. package/dist/components/model-renderer/responses.js +46 -0
  11. package/dist/components/model-tree/tree.js +1 -1
  12. package/dist/components/rodeo/rodeo.js +1 -1
  13. package/dist/components/the-doctor/the-doctor.css.js +1 -1
  14. package/dist/components/the-doctor/the-doctor.d.ts +112 -120
  15. package/dist/components/the-doctor/the-doctor.js +134 -1735
  16. package/dist/components/the-doctor/upload-archive.d.ts +1 -0
  17. package/dist/components/the-doctor/upload-archive.js +29 -12
  18. package/dist/controllers/auth-controller.d.ts +25 -0
  19. package/dist/controllers/auth-controller.js +154 -0
  20. package/dist/controllers/broker-controller.d.ts +22 -0
  21. package/dist/controllers/broker-controller.js +107 -0
  22. package/dist/controllers/diagnostic-controller.d.ts +6 -0
  23. package/dist/controllers/diagnostic-controller.js +262 -0
  24. package/dist/controllers/docs-controller.d.ts +8 -0
  25. package/dist/controllers/docs-controller.js +143 -0
  26. package/dist/controllers/model-controller.d.ts +8 -0
  27. package/dist/controllers/model-controller.js +87 -0
  28. package/dist/controllers/node-clicker-controller.d.ts +11 -0
  29. package/dist/controllers/node-clicker-controller.js +362 -0
  30. package/dist/controllers/problem-controller.d.ts +7 -0
  31. package/dist/controllers/problem-controller.js +46 -0
  32. package/dist/controllers/rolodex-controller.d.ts +10 -0
  33. package/dist/controllers/rolodex-controller.js +126 -0
  34. package/dist/controllers/rule-controller.d.ts +19 -0
  35. package/dist/controllers/rule-controller.js +264 -0
  36. package/dist/controllers/spec-controller.d.ts +8 -0
  37. package/dist/controllers/spec-controller.js +78 -0
  38. package/dist/controllers/state-controller.d.ts +9 -0
  39. package/dist/controllers/state-controller.js +279 -0
  40. package/dist/cowboy-components.umd.cjs +1152 -1121
  41. package/dist/css/shared.css.js +5 -0
  42. package/dist/events/doctor.d.ts +10 -0
  43. package/dist/events/doctor.js +2 -0
  44. package/dist/model/api-response.d.ts +7 -0
  45. package/dist/model/api-response.js +2 -0
  46. package/dist/services/auth-service.d.ts +1 -0
  47. package/dist/services/auth-service.js +28 -0
  48. package/dist/services/linting-service.js +11 -2
  49. package/dist/services/model-service.d.ts +2 -1
  50. package/dist/services/model-service.js +31 -5
  51. package/package.json +1 -1
  52. package/dist/controllers/auth.d.ts +0 -20
  53. package/dist/controllers/auth.js +0 -101
@@ -16,12 +16,11 @@ import '@shoelace-style/shoelace/dist/components/avatar/avatar.js';
16
16
  import { customElement, property, query, state } from "lit/decorators.js";
17
17
  import { html, LitElement } from "lit";
18
18
  import { SpecEditor } from "../editor/editor.js";
19
- import { ActiveView, AddToast, ArchiveURLRequested, BuiltInRulesetChanged, CustomRulesetEnabled, DocumentReferenceClicked, EditorClicked, EditorUpdated, ExplorerEqualizerChanged, ExplorerEqualizerFiltered, ExplorerNodeClicked, ExportRuleset, LoadRenderedNodeIntoInspector, ModelTreeNodeClicked, NodeReferenceClicked, NukeWorkspaceEvent, OpenProblemDrawer, OpenSettings, ProblemClicked, ProblemRuleFilterChangedManual, RolodexRootFileSelected, RolodexTreeNodeClicked, RuleClicked, RulesetSaved, RuleViolationClicked, } from "../../events/doctor.js";
20
- import { ProblemDetailsDrawer, ProblemDrawerEventType } from "../problem-list/details-drawer.js";
19
+ import { ActiveView, AddToast, ArchiveURLRequested, BuiltInRulesetChanged, CustomRulesetEnabled, DocumentReferenceClicked, EditorClicked, EditorUpdated, ExplorerEqualizerChanged, ExplorerEqualizerFiltered, ExplorerNodeClicked, ExportRuleset, LoadRenderedNodeIntoInspector, ModelTreeNodeClicked, NodeReferenceClicked, NukeWorkspaceEvent, OpenProblemDrawer, OpenSettings, ProblemClicked, RolodexRootFileSelected, RolodexTreeNodeClicked, RuleClicked, RulesetSaved, RuleViolationClicked, StartSessionFailed, } from "../../events/doctor.js";
20
+ import { ProblemDetailsDrawer } from "../problem-list/details-drawer.js";
21
21
  import { CreateBagManager } from "@pb33f/saddlebag";
22
22
  import { LintingService } from "../../services/linting-service.js";
23
23
  import { ProblemList } from "../problem-list/problem-list.js";
24
- import { Problem } from "../../model/problem.js";
25
24
  import { ProblemsOverview } from "../problems-overview/problems-overview.js";
26
25
  import { ErrorBanner } from "../error-banner/error-banner.js";
27
26
  import { StatusBar } from "./status-bar.js";
@@ -38,29 +37,35 @@ import { ToastType } from "../../model/toast.js";
38
37
  import dialogCss from "../../css/dialog.css.js";
39
38
  import buttonCss from "../../css/button.css.js";
40
39
  import radioGroupsCss from "../../css/radiogroups.css.js";
41
- import { MarkerSeverity } from "monaco-editor";
42
40
  import { ModelService } from "../../services/model-service.js";
43
- import { SearchNodeTreeForPath } from "../../model/graph.js";
44
41
  import { ModelTree } from "../model-tree/tree.js";
45
42
  import { ExplorerComponent } from "../visualizer/explorer.js";
46
43
  import { RenderedNodeComponent } from "../model-renderer/rendered-node.js";
47
44
  import tabsCss from "../../css/tabs.css.js";
48
45
  import { CreateBus } from "@pb33f/ranch";
49
- import { Command, CreditStreamChannel, DoctorServiceChannel, isBrokerResponse, QueuePrefix, SpecStreamChannel } from "../../model/channels.js";
50
46
  import formsCss from "../../css/forms.css.js";
51
47
  import spinnerCss from "../../css/spinner.css.js";
52
48
  import { UploadArchiveComponent } from "./upload-archive.js";
53
- import { NodeType } from "../../model/node_type.js";
54
49
  import tooltipCss from "../../css/tooltip.css.js";
55
50
  import { NukeWorkspaceComponent } from "./nuke-workspace.js";
56
51
  import { CreditTicker } from "../credit-ticker/credit-ticker.js";
57
- import { AuthController } from "../../controllers/auth.js";
52
+ import { AuthController } from "../../controllers/auth-controller";
58
53
  import { AuthService } from "../../services/auth-service.js";
59
54
  import { TimeVortex } from "../time-vortex/time-vortex.js";
60
55
  import { TimelineService } from "../../services/timeline-service.js";
61
56
  import { DoctorSettings } from "./settings.js";
62
57
  import panelsCss from "../../css/panels.css.js";
63
- import nukeCss from "../../css/nuke.css";
58
+ import nukeCss from "../../css/nuke.css.js";
59
+ import { NodeClickerController } from '../../controllers/node-clicker-controller.js';
60
+ import { BrokerController } from '../../controllers/broker-controller.js';
61
+ import { RolodexController } from '../../controllers/rolodex-controller.js';
62
+ import { SpecController } from '../../controllers/spec-controller.js';
63
+ import { RuleController } from '../../controllers/rule-controller.js';
64
+ import { ProblemController } from '../../controllers/problem-controller.js';
65
+ import { DocsController } from '../../controllers/docs-controller.js';
66
+ import { ModelController } from '../../controllers/model-controller.js';
67
+ import { DiagnosticController } from '../../controllers/diagnostic-controller.js';
68
+ import { StateController } from '../../controllers/state-controller.js';
64
69
  export const GraphBag = "pb33f-doctor-graph";
65
70
  export const PanelStateBag = "pb33f-doctor-panel-state";
66
71
  export const RolodexResponseBag = "pb33f-doctor-rolodex-response";
@@ -91,10 +96,9 @@ let TheDoctor = class TheDoctor extends LitElement {
91
96
  this.debounceTime = 1000;
92
97
  this.debounceTimeRuleset = 900;
93
98
  this.bounceId = 0;
94
- this.useTLS = false;
99
+ this.firstRun = true;
95
100
  // rolodex divider state (because it may not exist)
96
101
  this.rolodexDividerPosition = 40;
97
- this._firstRun = true;
98
102
  // extract the doctor endpoint from session storage.
99
103
  const sessionEndpoint = sessionStorage.getItem(DoctorEndpoint);
100
104
  if (sessionEndpoint) {
@@ -110,21 +114,23 @@ let TheDoctor = class TheDoctor extends LitElement {
110
114
  AuthService.doctorEndpoint = this.doctorEndpoint;
111
115
  TimelineService.doctorEndpoint = this.doctorEndpoint;
112
116
  this.timeVortex = new TimeVortex();
113
- //this.authController = new AuthController(this)
114
117
  // bus it up
115
118
  this.bus = CreateBus();
116
- this.doctorServiceChannel = this.bus.createChannel(DoctorServiceChannel);
117
- this.specStreamChannel = this.bus.createChannel(SpecStreamChannel);
118
- this.creditStreamChannel = this.bus.createChannel(CreditStreamChannel);
119
- this.bus.mapChannelToBrokerDestination(QueuePrefix + DoctorServiceChannel, DoctorServiceChannel);
120
- // this.bus.mapChannelToBrokerDestination(QueuePrefix + SpecStreamChannel, SpecStreamChannel);
121
- // this.bus.mapChannelToBrokerDestination(QueuePrefix + CreditStreamChannel, CreditStreamChannel);
122
- this.doctorChannelSubscription = this.doctorServiceChannel.subscribe(this.doctorServiceHandler());
123
- // this.specChannelSubscription = this.specStreamChannel.subscribe(this.specStreamHandler());
124
- // this.creditChannelSubscription = this.creditStreamChannel.subscribe(this.creditStreamHandler());
125
- // create a stateful bag manager
126
119
  this.bagManager = CreateBagManager(true);
127
- this.bagManager.loadStatefulBags().then(this.loadState.bind(this));
120
+ this.graphBag = this.bagManager.getBag(GraphBag);
121
+ this.docBag = this.bagManager.getBag(DoctorDocumentBag);
122
+ this.rolodexResponseBag = this.bagManager.getBag(RolodexResponseBag);
123
+ this.rolodexFilesBag = this.bagManager.getBag(RolodexFilesBag);
124
+ this.rolodexStateBag = this.bagManager.getBag(RolodexStateBag);
125
+ this.referenceMapBag = this.bagManager.getBag(ReferenceMapBag);
126
+ this.docExpirationBag = this.bagManager.getBag(DocumentationExpirationBag);
127
+ this.panelStateBag = this.bagManager.getBag(PanelStateBag);
128
+ this.settingsBag = this.bagManager.getBag(SettingsBag);
129
+ // create a stateful bag manager
130
+ this.modelController = new ModelController(this);
131
+ this.stateController = new StateController(this);
132
+ // boot state.
133
+ this.bagManager.loadStatefulBags().then(this.stateController.loadState.bind(this.stateController));
128
134
  this.editor = new SpecEditor();
129
135
  this.uploadArchive = new UploadArchiveComponent();
130
136
  this.nukeWorkspace = new NukeWorkspaceComponent();
@@ -141,6 +147,15 @@ let TheDoctor = class TheDoctor extends LitElement {
141
147
  this.manageRuleset = new ManageRuleset();
142
148
  this.toastManager = new ToastManager();
143
149
  this.creditTicker = new CreditTicker();
150
+ // roll the controllers!
151
+ this.nodeClickerController = new NodeClickerController(this);
152
+ this.brokerController = new BrokerController(this);
153
+ this.rolodexController = new RolodexController(this);
154
+ this.specController = new SpecController(this);
155
+ this.ruleController = new RuleController(this);
156
+ this.problemController = new ProblemController(this);
157
+ this.docsController = new DocsController(this);
158
+ this.diagnosticController = new DiagnosticController(this);
144
159
  this.settingsComponent = new DoctorSettings();
145
160
  this.editorMap = new Map();
146
161
  this.editorMap.set("spec", this.editor);
@@ -172,7 +187,6 @@ let TheDoctor = class TheDoctor extends LitElement {
172
187
  this.minimapIconVisible = true;
173
188
  this.showDiagnosisButton = false;
174
189
  this.nodeLimit = 150;
175
- this.randomTicker = 0;
176
190
  // session call back for when we're online.
177
191
  const sessionCallback = (session) => {
178
192
  if (session.creditsRemaining) {
@@ -182,72 +196,66 @@ let TheDoctor = class TheDoctor extends LitElement {
182
196
  this.creditTicker.credits = session.creditsRemaining;
183
197
  }
184
198
  this.creditTicker.visible = true;
185
- this.connectToBroker();
199
+ this.brokerController.connectToBroker();
186
200
  };
187
201
  // create auth controller
188
202
  this.authController = new AuthController(this, sessionCallback, true);
203
+ // listen for auth state changes.
189
204
  //@ts-ignore
190
- this.addEventListener(EditorUpdated, this.specChanged);
205
+ this.addEventListener(EditorUpdated, this.specController.specChanged.bind(this.specController));
191
206
  //@ts-ignore
192
- this.addEventListener(EditorClicked, this.specClicked);
207
+ this.addEventListener(EditorClicked, this.specController.specClicked.bind(this.specController));
193
208
  //@ts-ignore
194
- this.addEventListener(ProblemClicked, this.problemClicked);
209
+ this.addEventListener(ProblemClicked, this.problemController.problemClicked.bind(this.problemController));
195
210
  // @ts-ignore
196
- this.addEventListener(OpenProblemDrawer, this.ruleDocsClicked);
211
+ this.addEventListener(OpenProblemDrawer, this.docsController.ruleDocsClicked.bind(this.docsController));
197
212
  // @ts-ignore
198
- this.addEventListener(RuleViolationClicked, this.ruleGroupClicked);
213
+ this.addEventListener(RuleViolationClicked, this.ruleController.ruleGroupClicked.bind(this.ruleController));
199
214
  // @ts-ignore
200
- this.addEventListener(CustomRulesetEnabled, this.customRulesetEnabled);
215
+ this.addEventListener(CustomRulesetEnabled, this.ruleController.customRulesetEnabled.bind(this.ruleController));
201
216
  // @ts-ignore
202
- this.addEventListener(RulesetSaved, this.rulesetSaved);
217
+ this.addEventListener(RulesetSaved, this.ruleController.rulesetSaved.bind(this.ruleController));
203
218
  // @ts-ignore
204
219
  this.addEventListener(AddToast, this.addToastEvent);
205
220
  this.addEventListener(ExportRuleset, this.exportRuleset);
206
221
  // @ts-ignore
207
- this.addEventListener(RuleClicked, this.ruleClicked);
222
+ this.addEventListener(RuleClicked, this.ruleController.ruleClicked.bind(this.ruleController));
208
223
  // @ts-ignore
209
224
  this.addEventListener(BuiltInRulesetChanged, this.builtInRulesetSelected);
210
225
  // @ts-ignore
211
- this.addEventListener(ModelTreeNodeClicked, this.modelTreeNodeClicked);
226
+ this.addEventListener(ModelTreeNodeClicked, this.nodeClickerController.modelTreeNodeClicked.bind(this.nodeClickerController));
212
227
  // @ts-ignore
213
- this.addEventListener(RolodexTreeNodeClicked, this.rolodexTreeNodeClicked);
228
+ this.addEventListener(RolodexTreeNodeClicked, this.nodeClickerController.rolodexTreeNodeClicked.bind(this.nodeClickerController));
214
229
  // @ts-ignore
215
- this.addEventListener(RolodexRootFileSelected, this.rolodexRootFileSelected);
230
+ this.addEventListener(NodeReferenceClicked, this.nodeClickerController.explorerReferenceClicked.bind(this.nodeClickerController));
216
231
  // @ts-ignore
217
- this.addEventListener(NodeReferenceClicked, this.explorerReferenceClicked);
232
+ this.addEventListener(ExplorerNodeClicked, this.nodeClickerController.explorerNodeClicked.bind(this.nodeClickerController));
218
233
  // @ts-ignore
219
- this.addEventListener(ExplorerNodeClicked, this.explorerNodeClicked.bind(this));
234
+ this.addEventListener(DocumentReferenceClicked, this.nodeClickerController.documentReferenceClicked.bind(this.nodeClickerController));
220
235
  // @ts-ignore
221
- this.addEventListener(ArchiveURLRequested, this.fetchUrl.bind(this));
236
+ this.addEventListener(RolodexRootFileSelected, this.rolodexController.rolodexRootFileSelected.bind(this.rolodexController));
222
237
  // @ts-ignore
223
- this.addEventListener(DocumentReferenceClicked, this.documentReferenceClicked.bind(this));
238
+ this.addEventListener(ArchiveURLRequested, this.fetchUrl.bind(this));
224
239
  //@ts-ignore
225
240
  this.explorer.equalizer.addEventListener(ExplorerEqualizerChanged, this.filterTreeModel.bind(this));
226
241
  //@ts-ignore
227
242
  this.explorer.equalizer.addEventListener(ExplorerEqualizerFiltered, this.filterTreeModel.bind(this));
243
+ // @ts-ignore
228
244
  this.nukeWorkspace.addEventListener(NukeWorkspaceEvent, this.nukeWorkspaceHandler.bind(this));
245
+ // on logout, nuke it all.
246
+ // @ts-ignore
247
+ this.authController.addEventListener(NukeWorkspaceEvent, this.nukeWorkspaceHandler.bind(this));
248
+ // @ts-ignore
249
+ this.authController.addEventListener(StartSessionFailed, this.platformUnavailable.bind(this));
229
250
  this.timeVortex.historyPicker.addEventListener(OpenSettings, this.openSettings.bind(this));
230
251
  //@ts-ignore
231
252
  this.addEventListener(LoadRenderedNodeIntoInspector, this.loadRenderedNodeIntoInspector.bind(this));
232
- // extract port from session storage.
233
- this.busPort = sessionStorage.getItem("pb33f-doctor-port");
234
- this.busHost = sessionStorage.getItem("pb33f-doctor-host");
235
- if (!this.busPort) {
236
- this.busPort = "443"; // default port
237
- }
238
- if (!this.busHost) {
239
- this.busHost = "doctor.pb33f.io"; // default host
240
- }
241
- const useTLS = sessionStorage.getItem("pb33f-doctor-tls");
242
- if (useTLS && useTLS == 'true') {
243
- this.useTLS = true;
244
- }
245
253
  // hijack navigation buttons.
246
254
  window.addEventListener('popstate', (e) => {
247
255
  const state = e.state;
248
256
  if (state) {
249
257
  if (state.activeNode) {
250
- this.modelTreeNodeClicked(new CustomEvent(ExplorerNodeClicked, {
258
+ this.nodeClickerController.modelTreeNodeClicked(new CustomEvent(ExplorerNodeClicked, {
251
259
  detail: {
252
260
  nodeHashId: state.activeNode,
253
261
  noState: true
@@ -255,7 +263,7 @@ let TheDoctor = class TheDoctor extends LitElement {
255
263
  }));
256
264
  }
257
265
  if (state.ref) {
258
- this.documentReferenceClicked(new CustomEvent(DocumentReferenceClicked, {
266
+ this.nodeClickerController.documentReferenceClicked(new CustomEvent(DocumentReferenceClicked, {
259
267
  detail: {
260
268
  jsonPath: state.ref,
261
269
  noState: true
@@ -275,18 +283,18 @@ let TheDoctor = class TheDoctor extends LitElement {
275
283
  this.showDiagnosisButton = !config.autoDiagnose;
276
284
  // if the time vortex is not available, enable skipsState
277
285
  this.timeVortex.historyPicker.skipChanges = config.skipTimeline;
278
- if (config.applicationVersion == this.currentVersion) {
286
+ if (config.applicationVersion == this.brokerController.currentVersion) {
279
287
  return;
280
288
  }
281
- if (this.currentVersion && this.currentVersion != 'bootstrap' &&
289
+ if (this.brokerController.currentVersion && this.brokerController.currentVersion != 'bootstrap' &&
282
290
  config.applicationVersion &&
283
291
  config.applicationVersion != 'bootstrap' &&
284
- this.currentVersion != config.applicationVersion) {
285
- console.warn('version changed, reloading', this.currentVersion, config.applicationVersion);
292
+ this.brokerController.currentVersion != config.applicationVersion) {
293
+ console.warn('version changed, reloading', this.brokerController.currentVersion, config.applicationVersion);
286
294
  this.nukeWorkspaceHandler();
287
295
  }
288
296
  else {
289
- config.applicationVersion = this.currentVersion;
297
+ config.applicationVersion = this.brokerController.currentVersion;
290
298
  this.settingsBag?.set(SettingsBag, config);
291
299
  }
292
300
  }
@@ -298,15 +306,20 @@ let TheDoctor = class TheDoctor extends LitElement {
298
306
  this.requestUpdate();
299
307
  this.viewerPanel.click();
300
308
  }
301
- tickRandomly() {
302
- this.randomTicker = Math.floor(Math.random() * 9999999);
303
- }
304
- nukeWorkspaceHandler() {
309
+ nukeWorkspaceHandler(e) {
305
310
  this.bagManager.resetBags();
306
311
  localStorage.removeItem("pb33f-doctor-version");
307
- ModelService.resetWorkspace().then(() => {
308
- window.location.reload();
309
- });
312
+ if (!e || !e.detail || e.detail.resetFiles) {
313
+ ModelService.resetWorkspace().then(() => {
314
+ window.location.reload();
315
+ });
316
+ }
317
+ else {
318
+ // no work to do, just reload the page.
319
+ setTimeout(() => {
320
+ window.location.reload();
321
+ }, 50);
322
+ }
310
323
  }
311
324
  minimapToggled() {
312
325
  this.minimapVisible = !this.minimapVisible;
@@ -318,91 +331,12 @@ let TheDoctor = class TheDoctor extends LitElement {
318
331
  this.editor = this.querySelector('pb33f-editor#spec-editor');
319
332
  this.rulesetEditor = this.querySelector('pb33f-editor#ruleset-editor');
320
333
  }
321
- whoAmI() {
322
- this.bus.publish({
323
- destination: "/p/q/" + DoctorServiceChannel,
324
- body: JSON.stringify({ request: Command.WhoAmI }),
325
- });
326
- }
327
- connectToBroker() {
328
- let protocol = "ws://";
329
- if (this.useTLS) {
330
- protocol = "wss://";
331
- }
332
- // configure wiretap broker.
333
- const config = {
334
- brokerURL: protocol + this.busHost + ':' + this.busPort + '/ranch',
335
- heartbeatIncoming: 0,
336
- heartbeatOutgoing: 0,
337
- onConnect: () => {
338
- console.log("💊 Connected to the %cOpenAPI Doctor%c, we are ready to communicate.", 'background: #0d1117; color: #62C4FFFF; font-weight: bold', 'color: default');
339
- this.bus.mapChannels();
340
- this.whoAmI();
341
- }
342
- };
343
- this.bus.connectToBroker(config);
344
- }
345
334
  addClickTrack(node) {
346
335
  history.pushState({ activeNode: node.idHash }, "", `?view=explore&node=${node.idHash}`);
347
336
  }
348
337
  addRefTrack(ref) {
349
338
  history.pushState({ ref: ref }, "", `?view=spec&ref=${ref}`);
350
339
  }
351
- doctorServiceHandler() {
352
- return (msg) => {
353
- if (msg.payload?.payload != null) {
354
- if (isBrokerResponse(msg.payload.payload)) {
355
- this.brokerConnectionId = msg.payload.payload.broker;
356
- this.authController.associateBroker(this.brokerConnectionId).then(() => {
357
- // check version from the server
358
- const version = msg.payload.payload.version;
359
- if (version !== "") {
360
- this.currentVersion = version;
361
- }
362
- const memVersion = localStorage.getItem("pb33f-doctor-version");
363
- if (memVersion) {
364
- if (this.currentVersion != memVersion) {
365
- this.nukeWorkspaceHandler();
366
- return;
367
- }
368
- }
369
- else {
370
- localStorage.setItem("pb33f-doctor-version", this.currentVersion);
371
- }
372
- console.log("💊 Welcome to the clinic, the %cdoctor %cis ready to see you. [" + "v" + version + "]", 'color: #62C4FFFF; font-weight: bold', 'color: default');
373
- this.startTheDoctor();
374
- });
375
- }
376
- }
377
- };
378
- }
379
- startTheDoctor() {
380
- this.specChannelSubscription = this.specStreamChannel.subscribe(this.specStreamHandler());
381
- this.creditChannelSubscription = this.creditStreamChannel.subscribe(this.creditStreamHandler());
382
- this.bus.mapChannelToBrokerDestination(QueuePrefix + SpecStreamChannel, SpecStreamChannel);
383
- this.bus.mapChannelToBrokerDestination(QueuePrefix + CreditStreamChannel, CreditStreamChannel);
384
- this.boostrap();
385
- }
386
- specStreamHandler() {
387
- return (msg) => {
388
- if (msg.payload?.payload != null) {
389
- // base64 decode the payload and update the editor!
390
- const decoded = atob(msg.payload.payload);
391
- if (this.docBag) {
392
- this.docBag.set(DefaultDocument, decoded);
393
- }
394
- this.editor?.setValue(decoded, true);
395
- this.requestUpdate();
396
- }
397
- };
398
- }
399
- creditStreamHandler() {
400
- return (msg) => {
401
- if (msg.payload?.payload != null) {
402
- this.creditTicker.credits = parseInt(msg.payload.payload);
403
- }
404
- };
405
- }
406
340
  filterTreeModel(event) {
407
341
  this.filteredNodes = new Map();
408
342
  event.detail.graph.nodes.forEach((node) => {
@@ -411,385 +345,6 @@ let TheDoctor = class TheDoctor extends LitElement {
411
345
  this.modelTree.filteredNodes = this.filteredNodes;
412
346
  this.modelTree.renderedNodes = this.renderedNodeMap;
413
347
  }
414
- documentReferenceClicked(evt) {
415
- let location = '';
416
- let line, col = 1;
417
- let path = evt.detail.jsonPath;
418
- let fullPath = '';
419
- let file = '';
420
- let rolodexId = '';
421
- if (evt.detail.jsonPath.includes('||')) {
422
- location = evt.detail.jsonPath.split('||')[1];
423
- path = evt.detail.jsonPath.split('||')[0];
424
- // location is formatted line:col-endCol
425
- line = parseInt(location.split(':')[0]);
426
- col = parseInt(location.split(':')[1]);
427
- fullPath = evt.detail.jsonPath.split('||')[2];
428
- if (fullPath != '') {
429
- file = fullPath.split('#')[0];
430
- }
431
- rolodexId = evt.detail.jsonPath.split('||')[3];
432
- }
433
- this.editor.editor?.setPosition({ lineNumber: line, column: col });
434
- this.editor.editor?.revealLineInCenter(line);
435
- if (this.nodeIdMap.has(path)) {
436
- const node = this.nodeIdMap.get(path);
437
- if (node) {
438
- if (this.nodeIdHashMap.has(node.idHash)) {
439
- const renderedNode = this.renderedNodeMap.get(node.id);
440
- if (renderedNode) {
441
- this.renderedNode.node = renderedNode;
442
- this.modelTree.explorerClicked(node.id);
443
- // add a ref click
444
- if (!evt.detail.noState) {
445
- this.addRefTrack(evt.detail.jsonPath);
446
- }
447
- }
448
- }
449
- }
450
- else {
451
- this.toastManager.addToastManually({
452
- id: crypto.randomUUID(),
453
- type: ToastType.INFO,
454
- title: "Reference not found",
455
- body: `Something went wrong, '${path}' not found`
456
- });
457
- }
458
- }
459
- else {
460
- if (this.rolodexActivePath != undefined && this.rolodexActivePath != file) {
461
- ModelService.queryRolodex(rolodexId, file).then((result) => {
462
- this.editor.showBreadcrumb = true;
463
- this.rolodexActivePath = file;
464
- this.editor.clearDecorations();
465
- this.editor.setValue(result.rolodexRoot.instance, true);
466
- this.editor.editor?.setPosition({ lineNumber: line, column: 1 });
467
- this.editor.editor?.revealLineInCenter(line);
468
- this.rolodexTree.explorerClicked(rolodexId);
469
- this.editor.setCurrentPath(file);
470
- this.fetchRefMap(file);
471
- //this.lintSpec(result.rolodexRoot.instance);
472
- // apply problems
473
- if (this.rolodexProblemMap.has(this.rolodexActivePath)) {
474
- const probs = this.rolodexProblemMap.get(this.rolodexActivePath);
475
- if (probs) {
476
- this.editor.setMarkers(probs);
477
- }
478
- }
479
- }).catch((e) => {
480
- console.error('rolodex query failed', e);
481
- });
482
- }
483
- }
484
- }
485
- explorerReferenceClicked(evt) {
486
- let nodeId = '';
487
- this.explorer.nodeComponents.forEach((node) => {
488
- if (node.body.node.nodePath === evt.detail.nodePath) {
489
- const renderedNode = this.renderedNodeMap.get(node.body.node.id);
490
- if (renderedNode) {
491
- this.editor.editor?.setPosition({ lineNumber: renderedNode.keyLine, column: 0 });
492
- this.editor.editor?.revealLineInCenter(renderedNode.keyLine);
493
- this.renderedNode.node = renderedNode;
494
- nodeId = node.body.node.id;
495
- this.explorer.moveToNode(node.body.node);
496
- this.activeNode = node.body.node;
497
- this.explorer.activeNode = node.body.node;
498
- this.explorer.equalizer.activeNode = node.body.node;
499
- }
500
- node.body.active = true;
501
- }
502
- else {
503
- node.body.active = false;
504
- }
505
- });
506
- if (nodeId) {
507
- this.modelTree.explorerClicked(nodeId);
508
- this.explorer.requestUpdate();
509
- this.viewerPanel.click();
510
- }
511
- if (evt.detail.nodePath) {
512
- let p = evt.detail.nodePath;
513
- if (!p.startsWith('/') && !p.includes('#')) {
514
- const dir = this.rolodexRootPath.substring(0, this.rolodexRootPath.lastIndexOf('/') + 1);
515
- p = dir + p;
516
- }
517
- if (!p.startsWith('#/')) {
518
- this.rolodexTreeNodeClicked(new CustomEvent(RolodexTreeNodeClicked, {
519
- detail: {
520
- path: p,
521
- }
522
- }));
523
- }
524
- }
525
- }
526
- rolodexTreeNodeClicked(evt) {
527
- const fileBag = this.rolodexFilesBag?.get(RolodexFilesBag);
528
- const handleRolodexResponse = (result) => {
529
- this.rolodexActivePath = evt.detail.path;
530
- this.rolodexActiveHash = evt.detail.nodeHashId;
531
- this.editor.setValue(result.instance, true);
532
- this.editor.setCurrentPath(this.rolodexActivePath);
533
- this.fetchRefMap(this.rolodexActivePath);
534
- const probs = this.rolodexProblemMap.get(this.rolodexActivePath);
535
- if (probs) {
536
- this.editor.setMarkers(probs);
537
- }
538
- // set the editor file type
539
- let language = 'yaml';
540
- const ext = evt.detail?.path?.split('.').pop();
541
- if (ext) {
542
- switch (ext) {
543
- case NodeType.JSON:
544
- language = NodeType.JSON;
545
- break;
546
- case NodeType.GO:
547
- language = NodeType.GO;
548
- break;
549
- case NodeType.PY:
550
- language = 'python';
551
- break;
552
- case NodeType.JS:
553
- language = 'javascript';
554
- break;
555
- case NodeType.TS:
556
- language = 'typescript';
557
- break;
558
- case NodeType.PHP:
559
- language = NodeType.PHP;
560
- break;
561
- case NodeType.XML:
562
- language = NodeType.XML;
563
- break;
564
- case NodeType.JAVA:
565
- language = NodeType.JAVA;
566
- break;
567
- case NodeType.RB:
568
- language = 'ruby';
569
- break;
570
- case NodeType.RS:
571
- language = 'rust';
572
- break;
573
- case NodeType.C:
574
- language = NodeType.C;
575
- break;
576
- case NodeType.CPP:
577
- language = NodeType.CPP;
578
- break;
579
- case NodeType.CS:
580
- language = 'csharp';
581
- break;
582
- case NodeType.MD:
583
- language = 'markdown';
584
- break;
585
- }
586
- }
587
- const existingState = this.rolodexStateBag?.get(RolodexStateBag);
588
- if (existingState) {
589
- existingState.activePath = evt.detail.path;
590
- existingState.activeHash = evt.detail.nodeHashId;
591
- existingState.activeLanguage = language;
592
- existingState.activeNode = result;
593
- existingState.rootPath = this.rolodexRootPath;
594
- this.rolodexStateBag?.set(RolodexStateBag, existingState);
595
- }
596
- else {
597
- const existingState = {
598
- activePath: evt.detail.path,
599
- activeHash: evt.detail.nodeHashId,
600
- activeLanguage: language,
601
- activeNode: result,
602
- rootHash: this.rolodexRootHash,
603
- rootPath: this.rolodexRootPath
604
- };
605
- this.rolodexStateBag?.set(RolodexStateBag, existingState);
606
- }
607
- this.editor.switchLanguage(language);
608
- let line = 1;
609
- let col = 0;
610
- if (evt.detail.line) {
611
- line = evt.detail.line;
612
- }
613
- if (evt.detail.column) {
614
- col = evt.detail.column;
615
- }
616
- this.editor.editor?.setPosition({ lineNumber: line, column: col });
617
- this.editor.editor?.revealLineInCenter(line, col);
618
- //this.lintSpec(result.instance);
619
- if (evt.detail.path) {
620
- this.rolodexTree.openNodeByPath(evt.detail.path);
621
- }
622
- return language;
623
- };
624
- ModelService.queryRolodex(evt.detail.nodeHashId, evt.detail.path).then((result) => {
625
- this.editor.showBreadcrumb = true;
626
- // clear all references
627
- this.referenceMapBag?.reset();
628
- handleRolodexResponse(result.rolodexRoot);
629
- if (evt.detail.path) {
630
- if (fileBag && !fileBag.files[evt.detail.path]) {
631
- fileBag.files[evt.detail.path] = result.rolodexRoot;
632
- this.rolodexFilesBag?.set(RolodexFilesBag, fileBag);
633
- }
634
- if (!fileBag) {
635
- const rf = {
636
- files: { [evt.detail.path]: result.rolodexRoot }
637
- };
638
- this.rolodexFilesBag?.set(RolodexFilesBag, rf);
639
- }
640
- }
641
- });
642
- }
643
- rolodexRootFileSelected(evt) {
644
- this.rolodexActivePath = evt.detail.path;
645
- this.rolodexActiveHash = evt.detail.nodeHashId;
646
- this.rolodexRootPath = evt.detail.path;
647
- this.importDisabled = true;
648
- const existingState = this.rolodexStateBag?.get(RolodexStateBag);
649
- if (existingState) {
650
- existingState.activePath = evt.detail.path;
651
- existingState.activeHash = evt.detail.nodeHashId;
652
- existingState.rootPath = evt.detail.path;
653
- existingState.rootHash = evt.detail.nodeHashId;
654
- this.rolodexStateBag?.set(RolodexStateBag, existingState);
655
- }
656
- else {
657
- const existingState = {
658
- activePath: evt.detail.path,
659
- activeHash: evt.detail.nodeHashId,
660
- rootPath: evt.detail.path,
661
- rootHash: evt.detail.nodeHashId
662
- };
663
- this.rolodexStateBag?.set(RolodexStateBag, existingState);
664
- }
665
- if (evt.detail.nodeHashId) {
666
- this.rolodexRootHash = evt.detail.nodeHashId;
667
- }
668
- if (evt.detail.content) {
669
- this.rolodexNeedsReset = true;
670
- this.editor.setValue(evt.detail.content, true);
671
- this.lintSpec(evt.detail.content);
672
- }
673
- }
674
- modelTreeNodeClicked(evt) {
675
- let node = this.nodeIdMap.get(evt.detail.nodeId);
676
- if (!node) {
677
- node = this.nodeIdHashMap.get(evt.detail.nodeId);
678
- }
679
- if (node) {
680
- if (node.origin && node.origin != this.rolodexActivePath && (node.origin != '/root.yaml' && node.origin != 'root.yaml')) {
681
- // extract the rolodex id using the path
682
- let hashId = '';
683
- const fb = this.rolodexFilesBag?.get(RolodexFilesBag);
684
- if (fb) {
685
- if (fb.files[node.origin]) {
686
- hashId = fb.files[node.origin].idHash;
687
- }
688
- }
689
- // we need to fire a rolodex tree node change event.
690
- this.rolodexTreeNodeClicked(new CustomEvent(RolodexTreeNodeClicked, {
691
- detail: {
692
- nodeHashId: hashId,
693
- path: node.origin
694
- }
695
- }));
696
- return;
697
- }
698
- // don't jump to the root in the editor if the root is selected
699
- if (node.idHash !== 'root') {
700
- this.editor.editor?.setPosition({ lineNumber: node.keyLine, column: 0 });
701
- this.editor.editor?.revealLineInCenter(node.keyLine);
702
- }
703
- if (this.explorerVisible) {
704
- this.explorer.moveToNode(node, evt.detail.first);
705
- }
706
- this.activeNode = node;
707
- // add changes to active node.
708
- if (evt.detail.changes) {
709
- node.timeline = evt.detail.changes;
710
- }
711
- this.explorer.activeNode = node;
712
- this.explorer.equalizer.activeNode = node;
713
- const renderedNode = structuredClone(this.renderedNodeMap.get(node.id));
714
- if (renderedNode) {
715
- if (evt.detail.changes) {
716
- renderedNode.timeline = evt.detail.changes;
717
- }
718
- this.renderedNode.node = renderedNode;
719
- }
720
- else {
721
- alert('no id found');
722
- }
723
- if (!evt.detail.noState) {
724
- this.addClickTrack(node);
725
- evt.detail.noState = true;
726
- }
727
- this.explorerNodeClicked(evt, false);
728
- if (this._firstRun) {
729
- this._firstRun = false;
730
- }
731
- else {
732
- this.viewerPanel.click();
733
- }
734
- }
735
- else {
736
- this.sendToast({
737
- id: crypto.randomUUID(),
738
- title: 'Unable to load change model',
739
- type: ToastType.WARNING,
740
- body: 'There is no-longer a model with the path `' + evt.detail.nodeId + '` in the document',
741
- });
742
- console.error('cannot navigate to node, path not found', evt.detail.nodeId);
743
- }
744
- }
745
- explorerNodeClicked(evt, replaceRenderedNode = true) {
746
- let found = false;
747
- let foundRenderedNode;
748
- this.explorer.nodeComponents.forEach((node) => {
749
- if (node.id === evt.detail.nodeId) {
750
- let renderedNode;
751
- if (replaceRenderedNode) {
752
- renderedNode = this.renderedNodeMap.get(node.id);
753
- if (renderedNode && !this._firstRun) {
754
- this.renderedNode.node = renderedNode;
755
- }
756
- }
757
- this.selectedNodeHashId = node.body.node.idHash;
758
- node.body.active = true;
759
- this.activeNode = node.body.node;
760
- this.explorer.activeNode = node.body.node;
761
- found = true;
762
- if (replaceRenderedNode) {
763
- foundRenderedNode = renderedNode;
764
- }
765
- if (!evt.detail.noState) {
766
- this.addClickTrack(node.body.node);
767
- }
768
- }
769
- else {
770
- node.body.active = false;
771
- }
772
- });
773
- if (!found) {
774
- // might be filtered, check the filtered nodes
775
- const node = this.nodeIdMap.get(evt.detail.nodeId);
776
- if (node) {
777
- this.selectedNodeHashId = node.idHash;
778
- this.activeNode = node;
779
- this.explorer.activeNode = node;
780
- this.explorer.equalizer.activeNode = node;
781
- }
782
- }
783
- if (foundRenderedNode) {
784
- // when jumping back to the spec view, we need to set the line
785
- this.pendingLine = foundRenderedNode.keyLine;
786
- }
787
- this.modelTree.explorerClicked(evt.detail.nodeId);
788
- if (!this._firstRun) {
789
- this.viewerPanel.click();
790
- }
791
- this.explorer.requestUpdate();
792
- }
793
348
  exportRuleset() {
794
349
  this.exportRulesetDialog.show();
795
350
  }
@@ -799,452 +354,6 @@ let TheDoctor = class TheDoctor extends LitElement {
799
354
  sendToast(toast) {
800
355
  this.toastManager.addToastManually(toast);
801
356
  }
802
- ruleGroupClicked(event) {
803
- this.problemsPanel.focus();
804
- const simEvent = new CustomEvent(ProblemRuleFilterChangedManual, {
805
- bubbles: true,
806
- composed: true,
807
- detail: {
808
- rule: event.detail.rule
809
- }
810
- });
811
- this.problemsPanel.focus();
812
- this.problemList.dispatchEvent(simEvent);
813
- history.pushState({ rule: event.detail.rule }, "", `/${ActiveView.Problems}?rule=${event.detail.rule}`);
814
- this.controlTabGroup.show(ActiveView.Problems);
815
- }
816
- ruleDocsClicked(event) {
817
- const ruleId = event.detail.rule;
818
- if (ruleId) {
819
- switch (event.detail.type) {
820
- case ProblemDrawerEventType.RULE_DOCS:
821
- const ruleDoc = this.ruleDocsBag?.get(ruleId);
822
- if (ruleDoc) {
823
- event.detail.body = ruleDoc.body;
824
- this.detailsDrawer.open(event.detail);
825
- }
826
- else {
827
- this.sendToast({
828
- id: crypto.randomUUID(),
829
- type: ToastType.INFO,
830
- title: "Rule documentation unavailable",
831
- body: `Documentation for '${ruleId}' not available`
832
- });
833
- }
834
- break;
835
- case ProblemDrawerEventType.HOW_TO_FIX:
836
- const howToFix = this.howToFixBag?.get(ruleId);
837
- if (howToFix) {
838
- event.detail.body = howToFix.howToFix;
839
- this.detailsDrawer.open(event.detail);
840
- }
841
- else {
842
- this.sendToast({
843
- id: crypto.randomUUID(),
844
- type: ToastType.INFO,
845
- title: "How to fix unavailable",
846
- body: `Information on how to fix '${ruleId}' not available`
847
- });
848
- }
849
- break;
850
- case ProblemDrawerEventType.FUNCTION_DOCS:
851
- const funcDocs = this.functionDocsBag?.get(ruleId);
852
- if (funcDocs) {
853
- event.detail.body = funcDocs.body;
854
- this.detailsDrawer.open(event.detail);
855
- }
856
- else {
857
- this.sendToast({
858
- id: crypto.randomUUID(),
859
- type: ToastType.INFO,
860
- title: "Function documentation unavailable",
861
- body: `Documentation for '${ruleId}' not available`
862
- });
863
- }
864
- break;
865
- }
866
- }
867
- else {
868
- switch (event.detail.type) {
869
- case ProblemDrawerEventType.RENDERED_EXAMPLE:
870
- case ProblemDrawerEventType.MARKDOWN:
871
- this.detailsDrawer.open(event.detail);
872
- }
873
- }
874
- }
875
- rulesetSaved(evt) {
876
- // lint spec
877
- if (this.docBag) {
878
- const doc = this.docBag.get(DefaultDocument);
879
- const config = this.ruleConfigBag?.get(RuleConfigurationBag);
880
- if (evt.detail.rules) {
881
- this.customRulesetBag?.set(CustomRulesetBag, evt.detail.rules);
882
- // get ruleset config and disable everything,
883
- // then enable the custom rules.
884
- if (config) {
885
- // extract config from the event
886
- const newConfig = evt.detail.config;
887
- if (newConfig && this.ruleConfigBag) {
888
- newConfig.returnedRuleset = evt.detail.returnedRules;
889
- this.ruleConfigBag?.set(RuleConfigurationBag, newConfig);
890
- }
891
- }
892
- RulesetService.getSessionRulesetAsYAML().then((yaml) => {
893
- if (this.selectedEditorTab == "spec") {
894
- this.rulesetPulse = true;
895
- }
896
- this.rulesetEditor.setValue(yaml, true);
897
- this.fetchRulesetMap();
898
- });
899
- // lint the spec
900
- this.lintSpec(doc, '');
901
- }
902
- }
903
- }
904
- buildRolodexResultMap(results) {
905
- // TODO: optimize this, it's a bit slow.
906
- this.rolodexProblemMap.clear();
907
- results.forEach((problem) => {
908
- // check if the source location is '/root.yaml'
909
- if (problem.sourceLocation === '/root.yaml' || problem.sourceLocation === 'root.yaml') {
910
- problem.sourceLocation = "root";
911
- }
912
- if (this.rolodexProblemMap.has(problem.sourceLocation)) {
913
- const problems = this.rolodexProblemMap.get(problem.sourceLocation);
914
- if (problems) {
915
- problems.push(problem);
916
- if (problem.sourceLocation) {
917
- this.rolodexProblemMap.set(problem.sourceLocation, problems);
918
- }
919
- else {
920
- if (!this.rolodexRootPath) {
921
- this.rolodexProblemMap.set("root", problems);
922
- }
923
- else {
924
- this.rolodexProblemMap.set(this.rolodexRootPath, problems);
925
- }
926
- }
927
- }
928
- }
929
- else {
930
- if (problem.sourceLocation) {
931
- this.rolodexProblemMap.set(problem.sourceLocation, [problem]);
932
- }
933
- else {
934
- if (!this.rolodexRootPath) {
935
- const currProblems = this.rolodexProblemMap.get("root");
936
- if (currProblems) {
937
- currProblems.push(problem);
938
- this.rolodexProblemMap.set("root", currProblems);
939
- }
940
- else {
941
- this.rolodexProblemMap.set("root", [problem]);
942
- }
943
- }
944
- else {
945
- const currProblems = this.rolodexProblemMap.get(this.rolodexRootPath);
946
- if (currProblems) {
947
- currProblems.push(problem);
948
- this.rolodexProblemMap.set(this.rolodexRootPath, currProblems);
949
- }
950
- else {
951
- this.rolodexProblemMap.set(this.rolodexRootPath, [problem]);
952
- }
953
- }
954
- }
955
- }
956
- });
957
- return this.rolodexProblemMap;
958
- }
959
- fetchRefMap(currentPath = '') {
960
- ModelService.fetchReferenceMap().then((result) => {
961
- this.editor.clearDecorations();
962
- this.references = result;
963
- this.editor.links = result;
964
- this.editor.applyLinkDecorations();
965
- // add references to our stateful bag
966
- this.updateRefmapBag(currentPath, result);
967
- }).catch(() => {
968
- this.editor.clearDecorations();
969
- this.editor.clearAllMarkers();
970
- this.toastManager.addToastManually({
971
- id: crypto.randomUUID(),
972
- type: ToastType.INFO,
973
- title: "File not analyzed",
974
- body: "This file is not a JSON or YAML file, it has not been analyzed."
975
- });
976
- });
977
- }
978
- /* LINT SPEC <---------------------------------------------------------------------------------------------
979
- *
980
- * This is our main function, man we need to clean this all up, it's bloated to fuck
981
- *
982
- * <--------------------------------------------------------------------------------------------------------
983
- */
984
- lintSpec(value, url) {
985
- this.activitySpinner.show();
986
- this.editor.breadcumb.isInvalid = false;
987
- if (url) {
988
- if (url === 'root') {
989
- url = '';
990
- }
991
- else {
992
- this.urlProblem.style.display = 'none';
993
- this.urlOverlay.style.display = "block";
994
- this.urlSpinner.style.display = "block";
995
- this.referenceMapBag?.reset(); // wipe out refs.
996
- }
997
- }
998
- else {
999
- this.urlProblem.style.display = 'none';
1000
- this.urlOverlay.style.display = "none";
1001
- this.urlSpinner.style.display = "none";
1002
- }
1003
- let replaceResults = false;
1004
- let currentPath = this.rolodexActivePath;
1005
- if (this.rolodexActivePath === this.rolodexRootPath || this.rolodexActivePath === 'root') {
1006
- replaceResults = true;
1007
- currentPath = '';
1008
- this.editor.showBreadcrumb = false;
1009
- }
1010
- if (url && url != '') {
1011
- try {
1012
- const parsedUrl = new URL(url);
1013
- if (parsedUrl) {
1014
- currentPath = parsedUrl.pathname;
1015
- this.rolodexActivePath = currentPath;
1016
- this.rolodexRootPath = currentPath;
1017
- replaceResults = true;
1018
- }
1019
- }
1020
- catch (e) {
1021
- // do nothing for now.
1022
- }
1023
- }
1024
- let revive = true;
1025
- LintingService.lintFile(value, this.brokerConnectionId, url, currentPath, this.skipTimeline).then((result) => {
1026
- //if (replaceResults) {
1027
- this.activitySpinner.hide();
1028
- const map = this.buildRolodexResultMap(result);
1029
- if (url) {
1030
- this.urlOverlay.style.display = "none";
1031
- this.urlSpinner.style.display = "none";
1032
- this.urlProblem.style.display = 'none';
1033
- }
1034
- // extract empty location problems as we are replacing the root results
1035
- let rootProblems = this.rolodexProblemMap.get("root");
1036
- if (!this.rolodexRoot) {
1037
- rootProblems = [];
1038
- }
1039
- if (result && !Array.isArray(result)) {
1040
- const r = [result];
1041
- if (this.rolodexProblemMap.size > 0 && this.rolodexProblemMap.has(this.rolodexActivePath)) {
1042
- const probs = this.rolodexProblemMap.get(this.rolodexActivePath);
1043
- if (probs) {
1044
- if (rootProblems) {
1045
- probs.push(...rootProblems);
1046
- }
1047
- this.editor.setMarkers(probs);
1048
- }
1049
- else {
1050
- if (rootProblems) {
1051
- r.push(...rootProblems);
1052
- }
1053
- this.editor.setMarkers(r);
1054
- }
1055
- }
1056
- else {
1057
- if (rootProblems) {
1058
- r.push(...rootProblems);
1059
- }
1060
- this.editor.setMarkers(r);
1061
- }
1062
- this.problemBag?.set(DocumentProblems, r);
1063
- this.problemList.problems = r;
1064
- this.problems = r;
1065
- this.problemsOverview.problems = this.problemList.problemItems;
1066
- }
1067
- if (result && Array.isArray(result)) {
1068
- if (result.length == 0) {
1069
- this.editor.clearAllMarkers();
1070
- this.editor.breadcumb.isInvalid = false;
1071
- this.editor.showBreadcrumb = false;
1072
- revive = true;
1073
- this.problemList.isInvalid = false;
1074
- }
1075
- // check if this problem is 'unable to parse'
1076
- if (result.length == 1 && result[0].message.startsWith('unable to parse')) {
1077
- // short circuit, we are dead.
1078
- this.editor.clearDecorations();
1079
- this.editor.clearAllMarkers();
1080
- this.editor.setMarkers(result);
1081
- this.editor.breadcumb.isInvalid = true;
1082
- this.editor.showBreadcrumb = true;
1083
- this.editor.dead();
1084
- this.problemList.isInvalid = true;
1085
- revive = false;
1086
- }
1087
- // extract empty location problems as we are replacing the root results
1088
- if (revive && this.rolodexProblemMap.size > 0 && this.rolodexProblemMap.has(this.rolodexActivePath)) {
1089
- const probs = this.rolodexProblemMap.get(this.rolodexActivePath);
1090
- if (probs) {
1091
- if (rootProblems) {
1092
- probs.push(...rootProblems);
1093
- }
1094
- this.editor.setMarkers(probs);
1095
- }
1096
- else {
1097
- if (rootProblems) {
1098
- result.push(...rootProblems);
1099
- }
1100
- this.editor.setMarkers(result);
1101
- }
1102
- }
1103
- else {
1104
- if (replaceResults) {
1105
- this.editor.setMarkers(result);
1106
- }
1107
- else {
1108
- this.fetchRefMap(currentPath);
1109
- return;
1110
- }
1111
- }
1112
- this.problemBag?.set(DocumentProblems, result);
1113
- this.problemList.problems = result;
1114
- this.problems = result;
1115
- this.problemsOverview.problems = this.problemList.problemItems;
1116
- if (this.problemsOverview.statistics) {
1117
- if (result.length == 1) {
1118
- this.problemsOverview.statistics.statistics.totalErrors = 1;
1119
- this.problemsOverview.statistics.statistics.overallScore = 0;
1120
- this.problemsOverview.statistics.evaluation = 'Useless';
1121
- this.problemsOverview.statistics.diagnosis = '<strong>Specification cannot be used</strong>: <br/><br/>' + result[0].message;
1122
- }
1123
- }
1124
- }
1125
- // enable pb33f theme.
1126
- if (revive) {
1127
- this.editor.revive();
1128
- }
1129
- else {
1130
- return;
1131
- }
1132
- // fetch graph
1133
- if ((this.rolodexRootPath == this.rolodexActivePath || this.rolodexActivePath == 'root') || url) {
1134
- ModelService.fetchLatestGraph().then((result) => {
1135
- this.extractGraph(result);
1136
- });
1137
- }
1138
- // TODO: rolodex query should happen elsewhere too!
1139
- if (this.rolodexNeedsReset) {
1140
- ModelService.queryRolodex().then((result) => {
1141
- this.fetchRefMap(currentPath);
1142
- this.importDisabled = true;
1143
- this.editor.showBreadcrumb = true;
1144
- this.rolodexNeedsReset = false;
1145
- result.rolodexRoot.treeExpanded = true;
1146
- this.rolodexTree.isRolodex = true;
1147
- this.rolodexTree.node = result.rolodexRoot;
1148
- this.rolodexRoot = result.rolodexRoot;
1149
- if (this.rolodexActiveHash) {
1150
- this.rolodexTree.pendingNavigationHash = this.rolodexActiveHash;
1151
- }
1152
- if (this.rolodexActivePath) {
1153
- this.rolodexTree.pendingNavigationPath = this.rolodexActivePath;
1154
- }
1155
- this.rolodexResponseBag?.set(RolodexResponseBag, result);
1156
- const existingState = this.rolodexStateBag?.get(RolodexStateBag);
1157
- if (!existingState) {
1158
- const existingState = {
1159
- activePath: this.rolodexActivePath,
1160
- rootPath: this.rolodexRootPath,
1161
- };
1162
- this.rolodexStateBag?.set(RolodexStateBag, existingState);
1163
- }
1164
- this.requestUpdate();
1165
- });
1166
- }
1167
- else {
1168
- this.fetchRefMap(currentPath);
1169
- this.timeVortex.tardisControl.fetchHistory();
1170
- }
1171
- // update the overview statistics
1172
- LintingService.fetchStatistics().then((result) => {
1173
- let oldScore = 0;
1174
- if (this.problemsOverview.statistics) {
1175
- oldScore = this.problemsOverview.statistics.statistics.overallScore;
1176
- }
1177
- this.diagnosticBag?.set(DiagnosticBag, result);
1178
- this.activitySpinner.hide();
1179
- if (result?.remainingCredit <= 10) {
1180
- this.statusBar.callsRemaining = result.remainingCredit;
1181
- this.statusBar.visible = true;
1182
- console.warn("You are running low on credit, you will need to authenticate soon. " +
1183
- "" + result.remainingCredit + " credits remaining.");
1184
- }
1185
- // determine if the score went up or down and toast it!
1186
- this.problemsOverview.statistics = result;
1187
- this.tickRandomly(); // tick randomly to refresh docs iframe.
1188
- // TODO: re-evaluate these toasts, they get annoying, fucking fast.
1189
- // if (this.problemsOverview.statistics) {
1190
- // const newScore = result.statistics.overallScore;
1191
- // if (oldScore > newScore) {
1192
- // this.sendToast({
1193
- // id: crypto.randomUUID(),
1194
- // type: ToastType.SCOREDOWN,
1195
- // body: "Your score has decreased. It is now " + newScore + "%",
1196
- // title: "Score went down by " + (oldScore - newScore) + "%"
1197
- // });
1198
- // }
1199
- // if (oldScore < newScore) {
1200
- // this.sendToast({
1201
- // id: crypto.randomUUID(),
1202
- // type: ToastType.SCOREUP,
1203
- // body: "Your score has increased to " + newScore + "%",
1204
- // title: "Score went up by " + (newScore - oldScore) + "%"
1205
- // });
1206
- // }
1207
- // }
1208
- }).catch((e) => {
1209
- console.error("statistics service is down", e);
1210
- this.platformUnavailable(e);
1211
- });
1212
- if (!this.explorerBooted) {
1213
- this.explorer.equalizer.initializeEqualizer();
1214
- this.explorerBooted = true;
1215
- }
1216
- }).catch((e) => {
1217
- this.activitySpinner.hide();
1218
- if (!url) {
1219
- //this.platformUnavailable(e);
1220
- console.error("so sorry, the doctor cannot see you right now, the clinic is closed.");
1221
- if (e) {
1222
- console.error(e.detail);
1223
- if (e.instance === 'https://pb33f.io/errors/no-credit-remaining') {
1224
- this.statusBar.callsRemaining = 0;
1225
- this.statusBar.visible = true;
1226
- this.sendToast({
1227
- id: crypto.randomUUID(),
1228
- type: ToastType.ERROR,
1229
- body: "Run out of credit, please authenticate for more or wait 24 hours.",
1230
- title: "Credit exhausted!",
1231
- });
1232
- }
1233
- else {
1234
- this.sendToast({
1235
- id: crypto.randomUUID(),
1236
- type: ToastType.ERROR,
1237
- body: e.detail,
1238
- title: "Platform Error",
1239
- });
1240
- }
1241
- }
1242
- }
1243
- else {
1244
- this.showUrlError(e);
1245
- }
1246
- });
1247
- }
1248
357
  updateInspectorDivider() {
1249
358
  const panelState = this.panelStateBag?.get(PanelStateBag);
1250
359
  if (panelState) {
@@ -1291,13 +400,13 @@ let TheDoctor = class TheDoctor extends LitElement {
1291
400
  }
1292
401
  platformUnavailable(error) {
1293
402
  if (!this.unavailable) {
1294
- this.loadingOverlay.hide();
1295
- this.activitySpinner.hide();
403
+ this.loadingOverlay?.hide();
404
+ this.activitySpinner?.hide();
1296
405
  this.errorBanner.errorTitle = "The doctor is out.";
1297
- if (!error) {
406
+ if (!error || !error.detail) {
1298
407
  this.errorBanner.errorMessage = "The clinic is <strong>closed!</strong> " +
1299
408
  "The doctor is currently unavailable" +
1300
- " because there is no response from the platform.";
409
+ " because there is no response (when asked) from the pb33f platform.";
1301
410
  }
1302
411
  else {
1303
412
  this.errorBanner.errorMessage = "The clinic is <strong>closed!</strong> " +
@@ -1309,716 +418,12 @@ let TheDoctor = class TheDoctor extends LitElement {
1309
418
  this.controlTabGroup.show(ActiveView.Overview);
1310
419
  }
1311
420
  }
1312
- specClicked(event) {
1313
- this.detailsDrawer.close();
1314
- if (this.selectedEditorTab == "spec") {
1315
- let problemFound = false;
1316
- for (let i = 0; i < this.problems?.length; i++) {
1317
- if (this.problems[i].startLineNumber === event.detail.line) {
1318
- this.problemList.lineClicked(event.detail.line, true);
1319
- this.controlTabGroup.show(ActiveView.Problems);
1320
- if (this.sidebarClosed) {
1321
- this.toggleSidebar();
1322
- }
1323
- problemFound = true;
1324
- break;
1325
- }
1326
- }
1327
- // use the graph lookup to find the node, use the graphResponse, if available if not, pull from bag
1328
- const graph = this.graphBag?.get(GraphBag);
1329
- if (graph) {
1330
- if (graph.graphMap && event.detail.line) {
1331
- const nodeHash = graph.graphMap[event.detail.line];
1332
- if (nodeHash) {
1333
- this.modelTree.explorerClicked(nodeHash);
1334
- const rendered = this.renderedNodeMap.get(nodeHash);
1335
- if (rendered) {
1336
- this.renderedNode.node = rendered;
1337
- }
1338
- if (!problemFound) {
1339
- this.viewerPanel.click();
1340
- }
1341
- const n = this.nodeIdHashMap.get(nodeHash);
1342
- if (n) {
1343
- this.explorer.activeNode = n;
1344
- this.explorer.equalizer.activeNode = n;
1345
- }
1346
- }
1347
- }
1348
- }
1349
- }
1350
- if (this.selectedEditorTab == ActiveView.Ruleset) {
1351
- // check ruleset map for the line number
1352
- const rulesetMap = this.sessionRulesetMapBag?.get(SessionRulesetMapBag);
1353
- if (rulesetMap?.rulesMap) {
1354
- for (let i = 0; i < rulesetMap.rulesMap.length; i++) {
1355
- if (rulesetMap.rulesMap[i].line === event.detail.line) {
1356
- this.controlTabGroup.show(ActiveView.Ruleset);
1357
- if (this.sidebarClosed) {
1358
- this.toggleSidebar();
1359
- }
1360
- break;
1361
- }
1362
- }
1363
- }
1364
- }
1365
- }
1366
- problemClicked(event) {
1367
- if (this.selectedEditorTab != "spec") {
1368
- this.editorTabGroup.show("spec");
1369
- this.selectedEditorTab = "spec";
1370
- }
1371
- let sourceLocation = event.detail.problem.problemObject.sourceLocation;
1372
- // no source? use root
1373
- if (sourceLocation == '' || sourceLocation == undefined || sourceLocation == 'root') {
1374
- sourceLocation = this.rolodexRootPath;
1375
- }
1376
- // locate the node in rolodex root
1377
- const node = SearchNodeTreeForPath(this.rolodexRoot, sourceLocation);
1378
- if (node) {
1379
- // trigger a rolodex load
1380
- this.rolodexTreeNodeClicked(new CustomEvent(RolodexTreeNodeClicked, {
1381
- detail: {
1382
- nodeHashId: node.idHash,
1383
- path: sourceLocation,
1384
- line: event.detail.problem.problemObject.startLineNumber,
1385
- column: event.detail.problem.problemObject.startColumn
1386
- }
1387
- }));
1388
- this.detailsDrawer.close();
1389
- // make sure we select the file in the rolodex
1390
- this.rolodexTree.openNodeByPath(sourceLocation);
1391
- return;
1392
- }
1393
- this.controlTabGroup.show(ActiveView.Problems);
1394
- this.editor.editor?.revealLineInCenter(event.detail.problem.line);
1395
- this.editor.editor?.setPosition({
1396
- lineNumber: event.detail.problem.line,
1397
- column: event.detail.problem.column
1398
- });
1399
- this.detailsDrawer.close();
1400
- if (!event.detail.launchedFromProblems) {
1401
- this.problemList.lineClicked(event.detail.problem.problemObject.startLineNumber);
1402
- }
1403
- }
1404
- loadState() {
1405
- this.graphBag = this.bagManager.getBag(GraphBag);
1406
- this.docBag = this.bagManager.getBag(DoctorDocumentBag);
1407
- this.rolodexResponseBag = this.bagManager.getBag(RolodexResponseBag);
1408
- this.rolodexFilesBag = this.bagManager.getBag(RolodexFilesBag);
1409
- this.rolodexStateBag = this.bagManager.getBag(RolodexStateBag);
1410
- this.referenceMapBag = this.bagManager.getBag(ReferenceMapBag);
1411
- this.docExpirationBag = this.bagManager.getBag(DocumentationExpirationBag);
1412
- this.panelStateBag = this.bagManager.getBag(PanelStateBag);
1413
- this.settingsBag = this.bagManager.getBag(SettingsBag);
1414
- // register a handler for settings bag changes.
1415
- this.settingsBag?.onAllChanges(this.settingsChanged.bind(this));
1416
- this.settingsComponent.settingsBag = this.settingsBag;
1417
- if (this.settingsBag) {
1418
- this.settingsChanged('', this.settingsBag.get(SettingsBag));
1419
- }
1420
- this.settingsComponent.bootstrap();
1421
- // load the rolodex
1422
- if (this.rolodexResponseBag) {
1423
- const response = this.rolodexResponseBag.get(RolodexResponseBag);
1424
- if (response) {
1425
- this.rolodexRoot = response.rolodexRoot;
1426
- this.rolodexTree.isRolodex = true;
1427
- this.rolodexTree.node = response.rolodexRoot;
1428
- response.rolodexRoot.treeExpanded = true;
1429
- this.requestUpdate();
1430
- }
1431
- }
1432
- let skipDocBag = false;
1433
- const checkRefMap = (key) => {
1434
- let refMap = this.referenceMapBag?.get(ReferenceMapBag);
1435
- if (refMap) {
1436
- // TODO: replace with scehduled task to refresh the reference map
1437
- setTimeout(() => {
1438
- let refs = refMap?.references[key];
1439
- if (!refs) {
1440
- refs = refMap?.references[""]; // single file
1441
- }
1442
- if (refs) {
1443
- this.references = refs;
1444
- this.editor.links = refs;
1445
- this.editor.applyLinkDecorations();
1446
- }
1447
- }, 5); // editor has not loaded yet, give it a second to breathe.
1448
- }
1449
- else {
1450
- ModelService.fetchReferenceMap().then((result) => {
1451
- this.references = result;
1452
- this.editor.links = result;
1453
- this.editor.applyLinkDecorations();
1454
- }).catch(() => {
1455
- this.editor.clearDecorations();
1456
- this.editor.clearAllMarkers();
1457
- });
1458
- }
1459
- };
1460
- if (this.rolodexStateBag) {
1461
- const state = this.rolodexStateBag.get(RolodexStateBag);
1462
- if (state) {
1463
- this.rolodexActivePath = state.activePath;
1464
- this.rolodexActiveHash = state.activeHash;
1465
- // load the root.
1466
- if (state.rootHash) {
1467
- this.rolodexTree.pendingNavigationHash = state.rootHash;
1468
- }
1469
- if (state.rootPath) {
1470
- this.rolodexRootPath = state.rootPath;
1471
- if (state.activePath) {
1472
- checkRefMap(state.activePath);
1473
- }
1474
- // ensure the session has a root selected
1475
- ModelService.selectRootPath(state.rootPath).then(() => {
1476
- // nothing to do.
1477
- }).catch((e) => {
1478
- console.error('could not select the root path on boot bro', e);
1479
- });
1480
- }
1481
- if (state.rootHash)
1482
- this.rolodexRootHash = state.rootHash;
1483
- if (state.activePath) {
1484
- this.rolodexTree.pendingNavigationPath = state.activePath;
1485
- this.rolodexTree.requestUpdate();
1486
- }
1487
- if (state.activeNode) {
1488
- // load the content of the active node into the editor
1489
- if (state.activeLanguage) {
1490
- this.editor.switchLanguage(state.activeLanguage);
1491
- }
1492
- this.editor.setValue(state.activeNode.instance, true);
1493
- skipDocBag = true;
1494
- }
1495
- }
1496
- else {
1497
- this.rolodexActivePath = "root";
1498
- // refresh the refmap.
1499
- checkRefMap(this.rolodexActivePath);
1500
- }
1501
- }
1502
- if (this.docBag && !skipDocBag) {
1503
- const doc = this.docBag.get(DefaultDocument);
1504
- if (doc) {
1505
- this.editor.setValue(doc, true);
1506
- }
1507
- }
1508
- this.problemBag = this.bagManager.getBag(DocumentProblems);
1509
- if (this.problemBag) {
1510
- const markers = this.problemBag.get(DocumentProblems);
1511
- if (markers) {
1512
- this.problems = markers;
1513
- for (let i = 0; i < markers.length; i++) {
1514
- markers[i] = Problem.reconstruct(markers[i]);
1515
- }
1516
- this.buildRolodexResultMap(markers);
1517
- if (this.rolodexProblemMap.size > 0 && this.rolodexProblemMap.has(this.rolodexActivePath)) {
1518
- const probs = this.rolodexProblemMap.get(this.rolodexActivePath);
1519
- if (probs) {
1520
- this.editor.setMarkers(probs);
1521
- }
1522
- }
1523
- this.problemList.problems = markers;
1524
- this.problemsOverview.problems = this.problemList.problemItems;
1525
- }
1526
- }
1527
- this.howToFixBag = this.bagManager.getBag(HowToFixBag);
1528
- this.diagnosticBag = this.bagManager.getBag(DiagnosticBag);
1529
- if (this.diagnosticBag) {
1530
- const stats = this.diagnosticBag.get(DiagnosticBag);
1531
- if (stats) {
1532
- this.problemsOverview.statistics = stats;
1533
- }
1534
- }
1535
- // extract graph
1536
- if (this.graphBag) {
1537
- const graph = this.graphBag.get(GraphBag);
1538
- if (graph) {
1539
- this.extractGraph(graph);
1540
- }
1541
- }
1542
- // extract panel state
1543
- if (this.panelStateBag) {
1544
- const panelState = this.panelStateBag.get(PanelStateBag);
1545
- if (panelState && panelState.explorerPanel) {
1546
- this.splitPanelExplorer.position = panelState.explorerPanel;
1547
- }
1548
- if (panelState && panelState.rolodexPanel) {
1549
- if (this.splitPanelRolodex) {
1550
- this.splitPanelRolodex.position = panelState.rolodexPanel;
1551
- }
1552
- this.rolodexDividerPosition = panelState.rolodexPanel;
1553
- }
1554
- if (panelState && panelState.inspectorPanel) {
1555
- this.splitPanelInspector.position = panelState.inspectorPanel;
1556
- }
1557
- }
1558
- if (!this.rolodexRoot) {
1559
- this.importDisabled = false;
1560
- }
1561
- else {
1562
- this.importDisabled = true;
1563
- }
1564
- // extract rulesets from bags
1565
- const promises = [];
1566
- this.defaultRulesetBag = this.bagManager.getBag(DefaultRulesetBag);
1567
- if (this.defaultRulesetBag) {
1568
- const ruleset = this.defaultRulesetBag.get(DefaultRulesetBag);
1569
- if (ruleset) {
1570
- this.defaultRuleset = ruleset;
1571
- }
1572
- else {
1573
- promises.push(this.fetchDefaultRuleset());
1574
- }
1575
- }
1576
- // OWASP ruleset
1577
- this.OWASPRulesetBag = this.bagManager.getBag(OWASPRulesetBag);
1578
- if (this.OWASPRulesetBag) {
1579
- const ruleset = this.OWASPRulesetBag.get(OWASPRulesetBag);
1580
- if (ruleset) {
1581
- this.OWASPRuleset = ruleset;
1582
- }
1583
- else {
1584
- promises.push(this.fetchOWASPRuleset());
1585
- }
1586
- }
1587
- // All ruleset
1588
- this.allRulesetBag = this.bagManager.getBag(AllRulesetBag);
1589
- if (this.allRulesetBag) {
1590
- const ruleset = this.allRulesetBag.get(AllRulesetBag);
1591
- if (ruleset) {
1592
- this.allRuleset = ruleset;
1593
- }
1594
- else {
1595
- promises.push(this.fetchAllRuleset());
1596
- }
1597
- }
1598
- // functions
1599
- this.functionsBag = this.bagManager.getBag(FunctionsBag);
1600
- this.functionSchemaBag = this.bagManager.getBag(FunctionsSchemaBag);
1601
- if (this.functionsBag) {
1602
- const functions = this.functionsBag.get(FunctionsBag);
1603
- if (functions) {
1604
- this.functions = functions;
1605
- }
1606
- else {
1607
- promises.push(this.fetchFunctions());
1608
- }
1609
- }
1610
- // custom ruleset
1611
- this.customRulesetBag = this.bagManager.getBag(CustomRulesetBag);
1612
- if (this.customRulesetBag) {
1613
- const ruleset = this.customRulesetBag.get(CustomRulesetBag);
1614
- if (ruleset) {
1615
- this.customRuleset = ruleset;
1616
- }
1617
- }
1618
- // create rule configuration bag
1619
- this.ruleConfigBag = this.bagManager.getBag(RuleConfigurationBag);
1620
- // create rule configuration bag
1621
- this.sessionRulesetMapBag = this.bagManager.getBag(SessionRulesetMapBag);
1622
- // fire off all network requests, then configure the ruleset management.
1623
- Promise.all(promises).then(() => {
1624
- // configure rule management
1625
- this.manageRuleset.defaultRuleset = this.defaultRuleset;
1626
- this.manageRuleset.owaspRuleset = this.OWASPRuleset;
1627
- this.manageRuleset.allRuleset = this.allRuleset;
1628
- this.manageRuleset.functions = this.functions;
1629
- const config = this.ruleConfigBag?.get(RuleConfigurationBag);
1630
- if (config) {
1631
- this.manageRuleset.rulesetConfig = config;
1632
- }
1633
- else {
1634
- this.manageRuleset.rulesetConfig = { ruleMapping: new Map(), allRulesSwitch: true };
1635
- }
1636
- setTimeout(() => {
1637
- this.manageRuleset.buildRulesets();
1638
- if (this.customRuleset && this.customRuleset.rules.size > 0) {
1639
- this.manageRuleset.customRuleset = this.customRuleset;
1640
- }
1641
- this.fetchSessionRulesetAsYaml().then((result) => {
1642
- this.rulesetEditor?.setValue(result, true);
1643
- this.fetchRulesetMap();
1644
- }).catch((e) => {
1645
- this.platformUnavailable(e);
1646
- });
1647
- }, 50);
1648
- });
1649
- // refresh state for how to fix.
1650
- this.fetchDocs();
1651
- }
1652
- extractGraph(graph) {
1653
- // handled stripped nodes
1654
- if (graph.stripped) {
1655
- this.explorer.nodeLimitExceeded = true;
1656
- this.modelTree.nodeLimitExceeded = true;
1657
- if (graph.strippedCount) {
1658
- this.explorer.nodeLimit = graph.strippedCount;
1659
- this.modelTree.nodeLimit = graph.strippedCount;
1660
- }
1661
- }
1662
- else {
1663
- this.explorer.nodeLimitExceeded = false;
1664
- this.modelTree.nodeLimitExceeded = false;
1665
- }
1666
- this.explorer.renderedNodeMap.clear();
1667
- this.nodeMap.clear();
1668
- this.nodeIdMap.clear();
1669
- this.nodeIdHashMap.clear();
1670
- const renderedNodes = new Map();
1671
- graph?.nodesRendered?.forEach((node) => {
1672
- this.explorer.renderedNodeMap.set(node.id, node);
1673
- renderedNodes.set(node.id, node);
1674
- });
1675
- for (let i = 0; i < graph?.nodesRendered?.length; i++) {
1676
- this.renderedNodeMap.set(graph.nodesRendered[i].id, graph.nodesRendered[i]);
1677
- }
1678
- graph?.nodes?.forEach((node) => {
1679
- this.nodeMap.set(node.id, node);
1680
- if (node.idHash.includes('root')) {
1681
- this.nodeIdMap.set('root', node);
1682
- this.nodeIdHashMap.set(node.id, node);
1683
- }
1684
- else {
1685
- this.nodeIdMap.set(node.id, node);
1686
- this.nodeIdHashMap.set(node.idHash, node);
1687
- }
1688
- if (node.filtered) {
1689
- this.filteredNodes.set(node.id, node);
1690
- }
1691
- const renderedNode = this.renderedNodeMap.get(node.id);
1692
- if (this.activeNode?.id === node.id && this.renderedNodeMap.get(node.id)) {
1693
- if (this.renderedNode && renderedNode) {
1694
- this.renderedNode.node = renderedNode;
1695
- }
1696
- }
1697
- });
1698
- this.graphBag?.set(GraphBag, graph);
1699
- this.explorer.updateGraphResponse(graph);
1700
- if (graph && graph.nodes) {
1701
- this.modelTree.node = graph?.nodesRendered[0]; // update tree
1702
- }
1703
- if (!this.explorer.equalizer.isInitialized()) {
1704
- this.explorer.equalizer.initializeEqualizer();
1705
- }
1706
- else {
1707
- this.explorer.equalizer.runEQ(true);
1708
- }
1709
- // sync timeline
1710
- this.timeVortex.checkHistory();
1711
- }
1712
- customRulesetEnabled(event) {
1713
- const customRS = { rules: new Map() };
1714
- customRS.id = crypto.randomUUID();
1715
- customRS.owner = this.authController.session.sessionId;
1716
- event.detail.rules.forEach((rule) => {
1717
- if (rule.rule.id != rule.ruleId) {
1718
- rule.rule.id = rule.ruleId;
1719
- }
1720
- customRS.rules.set(rule.ruleId, rule.rule);
1721
- });
1722
- // write state
1723
- this.customRulesetBag?.set(CustomRulesetBag, customRS);
1724
- if (event.detail.ruleConfig) {
1725
- this.ruleConfigBag?.set(RuleConfigurationBag, event.detail.ruleConfig);
1726
- }
1727
- else {
1728
- this.ruleConfigBag?.reset();
1729
- // reset session ruleset
1730
- RulesetService.resetSessionRuleset(event.detail.rulesetReset).then(() => {
1731
- this.sendToast({
1732
- id: crypto.randomUUID(),
1733
- type: ToastType.INFO,
1734
- body: "Ruleset has been reset to the default",
1735
- title: "Ruleset reset",
1736
- });
1737
- if (this.docBag) {
1738
- const doc = this.docBag.get(DefaultDocument);
1739
- let path = this.rolodexActivePath;
1740
- if (path == 'root') {
1741
- path = "";
1742
- }
1743
- this.lintSpec(doc, this.rolodexActivePath);
1744
- }
1745
- this.fetchSessionRulesetAsYaml().then((result) => {
1746
- this.rulesetEditor?.setValue(result, true);
1747
- });
1748
- }).catch((e) => {
1749
- this.sendToast({
1750
- id: crypto.randomUUID(),
1751
- type: ToastType.ERROR,
1752
- body: e.detail,
1753
- title: "Cannot reset ruleset",
1754
- });
1755
- });
1756
- }
1757
- }
1758
- async fetchSessionRulesetAsYaml() {
1759
- return new Promise(async (resolve, reject) => {
1760
- RulesetService.getSessionRulesetAsYAML().then((result) => {
1761
- resolve(result);
1762
- }).catch((e) => {
1763
- console.error("cannot fetch session ruleset: ", e.title);
1764
- reject(e);
1765
- });
1766
- });
1767
- }
1768
- async fetchFunctions() {
1769
- return new Promise(async (resolve) => {
1770
- RulesetService.getFunctions().then((result) => {
1771
- this.functions = result;
1772
- this.functionsBag?.set(FunctionsBag, result);
1773
- // extract schemas for each function
1774
- const promises = [];
1775
- result.forEach((functionId) => {
1776
- promises.push(this.fetchFunctionSchema(functionId));
1777
- });
1778
- Promise.all(promises).then(() => {
1779
- resolve(result);
1780
- });
1781
- });
1782
- });
1783
- }
1784
- async fetchFunctionSchema(functionId) {
1785
- return new Promise(async (resolve) => {
1786
- RulesetService.getFunctionSchema(functionId).then((result) => {
1787
- let m = this.functionSchemaBag?.get(FunctionsSchemaBag);
1788
- if (!m) {
1789
- m = new Map();
1790
- }
1791
- m.set(functionId, result);
1792
- this.functionSchemaBag?.set(FunctionsSchemaBag, m);
1793
- resolve(result);
1794
- });
1795
- });
1796
- }
1797
- async fetchDefaultRuleset() {
1798
- return new Promise(async (resolve) => {
1799
- RulesetService.getDefaultRuleset().then((result) => {
1800
- this.defaultRuleset = result;
1801
- this.defaultRulesetBag?.set(DefaultRulesetBag, result);
1802
- resolve(result);
1803
- });
1804
- });
1805
- }
1806
- async fetchOWASPRuleset() {
1807
- return new Promise(async (resolve) => {
1808
- RulesetService.getOWASPRuleset().then((result) => {
1809
- this.OWASPRuleset = result;
1810
- this.OWASPRulesetBag?.set(OWASPRulesetBag, result);
1811
- resolve(result);
1812
- });
1813
- });
1814
- }
1815
- async fetchAllRuleset() {
1816
- return new Promise(async (resolve) => {
1817
- RulesetService.getAllRuleset().then((result) => {
1818
- this.allRuleset = result;
1819
- this.allRulesetBag?.set(AllRulesetBag, result);
1820
- resolve(result);
1821
- });
1822
- });
1823
- }
1824
- fetchDocs() {
1825
- this.activitySpinner.show();
1826
- const url = new URL(window.location.href);
1827
- const urlParam = url.searchParams.get('url');
1828
- if (urlParam) {
1829
- //this.urlInput.value = urlParam;
1830
- this.activeURL = urlParam;
1831
- this.lintSpec('', urlParam);
1832
- }
1833
- LintingService.fetchAllHowToFix().then((result) => {
1834
- if (result) {
1835
- result.forEach((howToFix) => {
1836
- this.howToFixBag?.set(howToFix.ruleId, howToFix);
1837
- });
1838
- }
1839
- }).catch((e) => {
1840
- this.platformUnavailable(e);
1841
- console.error("documentation service is down");
1842
- });
1843
- this.ruleDocsBag = this.bagManager.getBag(RuleDocumentationBag);
1844
- this.functionDocsBag = this.bagManager.getBag(FunctionDocumentationBag);
1845
- // populate docs via worker.
1846
- this.ruleDocsWorker.addEventListener("message", (event) => {
1847
- const data = event.data;
1848
- if (data) {
1849
- data.forEach((doc) => {
1850
- if (doc.ruleId) {
1851
- this.ruleDocsBag?.set(doc.ruleId, doc);
1852
- }
1853
- if (doc.functionId) {
1854
- this.functionDocsBag?.set(doc.functionId, doc);
1855
- }
1856
- });
1857
- this.activitySpinner.hide();
1858
- }
1859
- });
1860
- // let ruleDocs: string[] = [];
1861
- // let functionDocs: string[] = [];
1862
- // if (this.ruleDocsBag) {
1863
- // ruleDocs = Array.from(this.ruleDocsBag.export().keys());
1864
- // }
1865
- // if (this.functionDocsBag) {
1866
- // functionDocs = Array.from(this.functionDocsBag.export().keys());
1867
- // }
1868
- const fetchDocs = () => {
1869
- this.ruleDocsWorker.postMessage({
1870
- start: true,
1871
- endpoint: this.doctorEndpoint,
1872
- existingRules: [],
1873
- existingFunctions: []
1874
- });
1875
- };
1876
- // check expiration
1877
- if (this.docExpirationBag) {
1878
- const expiration = this.docExpirationBag.get(DocumentationExpirationBag);
1879
- // if the docs are older than 15 days, refresh them.
1880
- if (expiration) {
1881
- const now = new Date().getTime();
1882
- const then = new Date(expiration).getTime();
1883
- // if (now - then > 1296000000) {
1884
- // fetchDocs();
1885
- // } else {
1886
- // this.activitySpinner.hide();
1887
- // }
1888
- fetchDocs(); // always fetch for now.
1889
- }
1890
- else {
1891
- fetchDocs();
1892
- }
1893
- }
1894
- else {
1895
- fetchDocs();
1896
- }
1897
- }
1898
- fetchRulesetMap() {
1899
- RulesetService.getSessionRulesetMap().then((result) => {
1900
- this.sessionRulesetMapBag?.set(SessionRulesetMapBag, result);
1901
- }).catch(() => {
1902
- console.warn("map is empty, or unavailable, Did you submit an empty ruleset?");
1903
- this.sendToast({
1904
- id: crypto.randomUUID(),
1905
- type: ToastType.WARNING,
1906
- body: "map is empty, or unavailable, Did you submit an empty ruleset?",
1907
- title: "Issue fetching ruleset map"
1908
- });
1909
- });
1910
- }
1911
- ruleClicked(evt) {
1912
- // get rule map from bag
1913
- const map = this.sessionRulesetMapBag?.get(SessionRulesetMapBag);
1914
- if (map) {
1915
- const rule = map.rulesMap.find((r) => r.ruleId === evt.detail.ruleId);
1916
- if (rule) {
1917
- this.selectedEditorTab = "ruleset";
1918
- this.editorTabGroup.show(ActiveView.Ruleset);
1919
- this.rulesetEditor.editor?.setPosition({ lineNumber: rule.line, column: rule.start });
1920
- this.rulesetEditor.editor?.revealLineInCenter(rule.line);
1921
- }
1922
- }
1923
- }
1924
421
  builtInRulesetSelected() {
1925
422
  // todo
1926
423
  }
1927
- rulesetManuallyChanged() {
1928
- clearTimeout(this.bounceId);
1929
- this.bounceId = window.setTimeout(() => {
1930
- const ruleset = this.rulesetEditor.editor?.getValue();
1931
- RulesetService.validateRuleset(ruleset).then((result) => {
1932
- // clear any problems
1933
- this.manageRuleset.clearRuleProblems();
1934
- // create a new custom ruleset with a map of rules
1935
- let customRS = { rules: new Map() };
1936
- customRS.id = crypto.randomUUID();
1937
- customRS.owner = this.authController.session.sessionId;
1938
- customRS.rules = new Map(Object.entries(result.rules));
1939
- customRS.rules.forEach((rule, id) => {
1940
- rule.id = id; // set the id to the key
1941
- });
1942
- // write state
1943
- this.customRulesetBag?.set(CustomRulesetBag, customRS);
1944
- this.customRuleset = customRS;
1945
- if (customRS.rules.size > 0) {
1946
- // rebuild the ruleset management
1947
- this.manageRuleset.customRulesetManual = customRS;
1948
- }
1949
- this.selectedEditorTab = ActiveView.Ruleset;
1950
- this.controlTabGroup.show(ActiveView.Ruleset);
1951
- this.rulesetEditor.setMarkers([]);
1952
- this.fetchRulesetMap();
1953
- }).catch((e) => {
1954
- this.sendToast({
1955
- id: crypto.randomUUID(),
1956
- type: ToastType.ERROR,
1957
- body: e.detail,
1958
- title: e.title
1959
- });
1960
- console.error('unable to validate ruleset', e);
1961
- // process bad rules
1962
- const ruleErrors = e.body;
1963
- const markers = [];
1964
- ruleErrors?.forEach((err) => {
1965
- markers.push({
1966
- severity: MarkerSeverity.Error,
1967
- message: err.ruleError,
1968
- startLineNumber: err.line,
1969
- startColumn: err.startCol,
1970
- endLineNumber: err.line,
1971
- endColumn: err.endCol
1972
- });
1973
- });
1974
- // convert to JSON
1975
- RulesetService.convertToJSON(ruleset).then((json) => {
1976
- // create a new custom ruleset with a map of rules
1977
- let customRS = { rules: new Map() };
1978
- customRS.id = crypto.randomUUID();
1979
- customRS.owner = this.authController.session.sessionId;
1980
- customRS.rules = new Map(Object.entries(json.rules));
1981
- customRS.rules.forEach((rule, id) => {
1982
- if (rule.id != id)
1983
- rule.id = id; // set the id to the key
1984
- });
1985
- this.customRuleset = customRS;
1986
- if (customRS.rules.size > 0) {
1987
- // rebuild the ruleset management
1988
- this.manageRuleset.customRulesetManual = customRS;
1989
- }
1990
- this.rulesetEditor.setMarkers([]);
1991
- // clear any problems
1992
- this.manageRuleset.clearRuleProblems();
1993
- // set problems.
1994
- this.rulesetEditor.setMarkers(markers);
1995
- this.manageRuleset.processBadRules(ruleErrors);
1996
- this.controlTabGroup.show(ActiveView.Ruleset);
1997
- }).catch((e) => {
1998
- console.error("unable to convert ruleset to JSON", e);
1999
- });
2000
- });
2001
- }, this.debounceTimeRuleset);
2002
- }
2003
- specChanged(event) {
2004
- const editor = this.editorMap.get(event.detail.id);
2005
- if (editor && event.detail.id === 'ruleset-editor') {
2006
- this.rulesetManuallyChanged();
2007
- return;
2008
- }
2009
- if (this.docBag) {
2010
- this.docBag.set(DefaultDocument, event.detail.content);
2011
- }
2012
- if (this.autoDiagnose) {
2013
- clearTimeout(this.bounceId);
2014
- this.bounceId = window.setTimeout(() => {
2015
- this.lintSpec(event.detail.content);
2016
- }, this.debounceTime);
2017
- }
2018
- }
2019
424
  runDiagnostics() {
2020
425
  if (!this.autoDiagnose) {
2021
- this.lintSpec(this.editor.getValue());
426
+ this.diagnosticController.lintSpec(this.editor.getValue());
2022
427
  }
2023
428
  }
2024
429
  boostrap() {
@@ -2028,20 +433,20 @@ let TheDoctor = class TheDoctor extends LitElement {
2028
433
  if (urlParam) {
2029
434
  //this.urlInput.value = urlParam;
2030
435
  this.activeURL = urlParam;
2031
- this.lintSpec('', urlParam);
436
+ this.diagnosticController.lintSpec('', urlParam);
2032
437
  return;
2033
438
  }
2034
439
  setTimeout(() => {
2035
- this.loadingOverlay.hide();
2036
440
  // check for a history
2037
441
  this.timeVortex.doctor = this;
2038
442
  this.timeVortex.checkHistory();
2039
443
  const editorValue = this.docBag?.get(DefaultDocument);
2040
444
  if (!editorValue) {
2041
445
  LintingService.bootstrapEditor().then((result) => {
446
+ this.loadingOverlay.hide();
2042
447
  this.editor.setValue(result, true);
2043
448
  this.rolodexActivePath = "";
2044
- this.specChanged(new CustomEvent(EditorUpdated, {
449
+ this.specController.specChanged(new CustomEvent(EditorUpdated, {
2045
450
  detail: {
2046
451
  content: result,
2047
452
  id: "spec"
@@ -2049,40 +454,31 @@ let TheDoctor = class TheDoctor extends LitElement {
2049
454
  }));
2050
455
  });
2051
456
  }
2052
- }, 200);
2053
- }
2054
- exportJSON() {
2055
- let jsonRS;
2056
- if (this.ruleConfigBag) {
2057
- const config = this.ruleConfigBag.get(RuleConfigurationBag);
2058
- if (config && config.returnedRuleset) {
2059
- jsonRS = config.returnedRuleset;
457
+ else {
458
+ ModelService.getCurrentSpec().then((result) => {
459
+ this.loadingOverlay.hide();
460
+ this.rolodexController.queryRolodex();
461
+ if (result != editorValue) {
462
+ this.editor.setValue(result, true);
463
+ this.rolodexActivePath = "";
464
+ this.specController.specChanged(new CustomEvent(EditorUpdated, {
465
+ detail: {
466
+ content: result,
467
+ id: "spec"
468
+ }
469
+ }));
470
+ }
471
+ });
2060
472
  }
2061
- }
2062
- if (jsonRS) {
2063
- let reportData = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonRS, null, 2));
2064
- this.downloadRulesetLink.download = "ruleset.json";
2065
- this.downloadRulesetLink.href = reportData;
2066
- this.downloadRulesetLink.click();
2067
- this.exportRulesetDialog.hide();
2068
- }
2069
- }
2070
- exportYAML() {
2071
- RulesetService.getSessionRulesetAsYAML().then((yaml) => {
2072
- let reportData = "data:text/yaml;charset=utf-8," + encodeURIComponent(yaml);
2073
- this.downloadRulesetLink.download = "ruleset.yaml";
2074
- this.downloadRulesetLink.href = reportData;
2075
- this.downloadRulesetLink.click();
2076
- this.exportRulesetDialog.hide();
2077
- });
473
+ }, 50);
2078
474
  }
2079
475
  confirmExport() {
2080
476
  switch (this.exportSelection.value) {
2081
477
  case "json":
2082
- this.exportJSON();
478
+ this.ruleController.exportJSON();
2083
479
  break;
2084
480
  case "yaml":
2085
- this.exportYAML();
481
+ this.ruleController.exportYAML();
2086
482
  break;
2087
483
  }
2088
484
  }
@@ -2276,6 +672,29 @@ let TheDoctor = class TheDoctor extends LitElement {
2276
672
  </sl-tab-group>
2277
673
  `;
2278
674
  }
675
+ const controls = html `
676
+ <div class="controls">
677
+ <sl-tooltip content="Upload zip file or tar.gz (tarball)" placement="top">
678
+ <sl-button ?disabled=${this.importDisabled} class="import-button" variant="text"
679
+ size="small" @click="${() => {
680
+ this.uploadArchive.show();
681
+ }}">
682
+ Import
683
+ </sl-button>
684
+ </sl-tooltip>
685
+ <sl-tooltip content="Nuke / reset workspace" placement="top">
686
+ <sl-icon-button class="nuke-session-button" name="radioactive" @click="${() => {
687
+ this.nukeWorkspace.show();
688
+ }}">
689
+ </sl-icon-button>
690
+ </sl-tooltip>
691
+ </div>`;
692
+ let navigatorView = html `
693
+ ${controls}
694
+ ${modelTree}
695
+ `;
696
+ if (this.authController.state && this.authController.state.authenticated) {
697
+ }
2279
698
  return html `
2280
699
  ${this.uploadArchive}
2281
700
  ${this.nukeWorkspace}
@@ -2308,30 +727,11 @@ let TheDoctor = class TheDoctor extends LitElement {
2308
727
  ${this.activitySpinner}
2309
728
  ${this.errorBanner}
2310
729
  ${overlay}
2311
-
2312
730
  <sl-split-panel id="explorer-split-panel" class="split-panel-explorer" position="12"
2313
731
  @sl-reposition="${this.updateExplorerDivider}">
2314
732
  <sl-icon id="split-divider-tree" slot="divider" name="grip-vertical" class="divider-vert"></sl-icon>
2315
733
  <div slot="start" class="model-tree">
2316
- <div class="controls">
2317
-
2318
- <sl-tooltip content="Upload zip file or tar.gz (tarball)" placement="top">
2319
- <sl-button ?disabled=${this.importDisabled} class="import-button" variant="text"
2320
- size="small" @click="${() => {
2321
- this.uploadArchive.show();
2322
- }}">
2323
- Import
2324
- </sl-button>
2325
- </sl-tooltip>
2326
- <sl-tooltip content="Nuke / reset workspace" placement="top">
2327
- <sl-icon-button class="nuke-session-button" name="radioactive" @click="${() => {
2328
- this.nukeWorkspace.show();
2329
- }}">
2330
- </sl-icon-button>
2331
- </sl-tooltip>
2332
-
2333
- </div>
2334
- ${modelTree}
734
+ ${navigatorView}
2335
735
  ${this.creditTicker}
2336
736
  </div>
2337
737
  <div slot="end">
@@ -2462,15 +862,17 @@ let TheDoctor = class TheDoctor extends LitElement {
2462
862
  </sl-split-panel>
2463
863
  </div>
2464
864
  </sl-split-panel>
2465
-
2466
865
  </div>`;
2467
866
  }
867
+ randomTicker() {
868
+ return crypto.randomUUID().toString();
869
+ }
2468
870
  // <iframe src="${this.doctorEndpoint}/model/scalar-template?z=${this.randomTicker}" width="100%" height="100%" style="border: none"></iframe>
2469
871
  fetchUrl(event) {
2470
872
  this.activeURL = event.detail.url;
2471
873
  this.rolodexNeedsReset = true;
2472
874
  this.rolodexActivePath = "/";
2473
- this.lintSpec('', event.detail.url);
875
+ this.diagnosticController.lintSpec('', event.detail.url);
2474
876
  }
2475
877
  hideUrlError() {
2476
878
  this.urlProblem.style.display = 'none';
@@ -2574,9 +976,6 @@ __decorate([
2574
976
  __decorate([
2575
977
  query('#inspector-split-panel')
2576
978
  ], TheDoctor.prototype, "splitPanelInspector", void 0);
2577
- __decorate([
2578
- state()
2579
- ], TheDoctor.prototype, "randomTicker", void 0);
2580
979
  __decorate([
2581
980
  state()
2582
981
  ], TheDoctor.prototype, "rulesetPulse", void 0);