@pb33f/cowboy-components 0.7.5 → 0.7.7

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 (64) hide show
  1. package/dist/components/auth/login-button.d.ts +3 -0
  2. package/dist/components/auth/login-button.js +29 -8
  3. package/dist/components/auth/login-panel.d.ts +2 -1
  4. package/dist/components/auth/login-panel.js +3 -2
  5. package/dist/components/auth/oauth-login.d.ts +1 -0
  6. package/dist/components/auth/oauth-login.js +11 -5
  7. package/dist/components/editor/editor-breadcrumb.css.js +1 -1
  8. package/dist/components/model-renderer/rendered-node.d.ts +2 -0
  9. package/dist/components/model-renderer/rendered-node.js +18 -0
  10. package/dist/components/model-renderer/responses.d.ts +11 -0
  11. package/dist/components/model-renderer/responses.js +46 -0
  12. package/dist/components/model-tree/tree.js +1 -1
  13. package/dist/components/paginator/paginator.css.js +1 -1
  14. package/dist/components/paginator/paginator.d.ts +2 -0
  15. package/dist/components/paginator/paginator.js +6 -6
  16. package/dist/components/problems-overview/problems-overview.js +6 -0
  17. package/dist/components/rodeo/rodeo.js +1 -1
  18. package/dist/components/the-doctor/sparks.d.ts +1 -0
  19. package/dist/components/the-doctor/sparks.js +36 -21
  20. package/dist/components/the-doctor/status-bar.css.js +10 -8
  21. package/dist/components/the-doctor/status-bar.d.ts +2 -0
  22. package/dist/components/the-doctor/status-bar.js +18 -8
  23. package/dist/components/the-doctor/the-doctor.css.js +1 -1
  24. package/dist/components/the-doctor/the-doctor.d.ts +113 -120
  25. package/dist/components/the-doctor/the-doctor.js +141 -1735
  26. package/dist/components/the-doctor/upload-archive.d.ts +1 -0
  27. package/dist/components/the-doctor/upload-archive.js +29 -12
  28. package/dist/controllers/{auth.d.ts → auth-controller.d.ts} +11 -6
  29. package/dist/controllers/auth-controller.js +165 -0
  30. package/dist/controllers/broker-controller.d.ts +22 -0
  31. package/dist/controllers/broker-controller.js +107 -0
  32. package/dist/controllers/diagnostic-controller.d.ts +6 -0
  33. package/dist/controllers/diagnostic-controller.js +262 -0
  34. package/dist/controllers/docs-controller.d.ts +8 -0
  35. package/dist/controllers/docs-controller.js +144 -0
  36. package/dist/controllers/model-controller.d.ts +8 -0
  37. package/dist/controllers/model-controller.js +87 -0
  38. package/dist/controllers/node-clicker-controller.d.ts +11 -0
  39. package/dist/controllers/node-clicker-controller.js +362 -0
  40. package/dist/controllers/problem-controller.d.ts +7 -0
  41. package/dist/controllers/problem-controller.js +46 -0
  42. package/dist/controllers/rolodex-controller.d.ts +10 -0
  43. package/dist/controllers/rolodex-controller.js +126 -0
  44. package/dist/controllers/rule-controller.d.ts +19 -0
  45. package/dist/controllers/rule-controller.js +264 -0
  46. package/dist/controllers/spec-controller.d.ts +8 -0
  47. package/dist/controllers/spec-controller.js +78 -0
  48. package/dist/controllers/state-controller.d.ts +9 -0
  49. package/dist/controllers/state-controller.js +279 -0
  50. package/dist/cowboy-components.umd.cjs +768 -736
  51. package/dist/css/pb33f-theme.css +1 -0
  52. package/dist/css/shared.css.js +5 -0
  53. package/dist/events/doctor.d.ts +12 -0
  54. package/dist/events/doctor.js +4 -0
  55. package/dist/model/api-response.d.ts +7 -0
  56. package/dist/model/api-response.js +2 -0
  57. package/dist/services/auth-service.d.ts +1 -0
  58. package/dist/services/auth-service.js +28 -0
  59. package/dist/services/linting-service.js +11 -2
  60. package/dist/services/model-service.d.ts +2 -1
  61. package/dist/services/model-service.js +31 -5
  62. package/dist/style.css +1 -1
  63. package/package.json +1 -1
  64. 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, CreditEmpty, 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,68 @@ 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));
251
+ // @ts-ignore
252
+ this.authController.addEventListener(CreditEmpty, this.creditEmpty.bind(this));
230
253
  //@ts-ignore
231
254
  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
255
  // hijack navigation buttons.
246
256
  window.addEventListener('popstate', (e) => {
247
257
  const state = e.state;
248
258
  if (state) {
249
259
  if (state.activeNode) {
250
- this.modelTreeNodeClicked(new CustomEvent(ExplorerNodeClicked, {
260
+ this.nodeClickerController.modelTreeNodeClicked(new CustomEvent(ExplorerNodeClicked, {
251
261
  detail: {
252
262
  nodeHashId: state.activeNode,
253
263
  noState: true
@@ -255,7 +265,7 @@ let TheDoctor = class TheDoctor extends LitElement {
255
265
  }));
256
266
  }
257
267
  if (state.ref) {
258
- this.documentReferenceClicked(new CustomEvent(DocumentReferenceClicked, {
268
+ this.nodeClickerController.documentReferenceClicked(new CustomEvent(DocumentReferenceClicked, {
259
269
  detail: {
260
270
  jsonPath: state.ref,
261
271
  noState: true
@@ -275,18 +285,18 @@ let TheDoctor = class TheDoctor extends LitElement {
275
285
  this.showDiagnosisButton = !config.autoDiagnose;
276
286
  // if the time vortex is not available, enable skipsState
277
287
  this.timeVortex.historyPicker.skipChanges = config.skipTimeline;
278
- if (config.applicationVersion == this.currentVersion) {
288
+ if (config.applicationVersion == this.brokerController.currentVersion) {
279
289
  return;
280
290
  }
281
- if (this.currentVersion && this.currentVersion != 'bootstrap' &&
291
+ if (this.brokerController.currentVersion && this.brokerController.currentVersion != 'bootstrap' &&
282
292
  config.applicationVersion &&
283
293
  config.applicationVersion != 'bootstrap' &&
284
- this.currentVersion != config.applicationVersion) {
285
- console.warn('version changed, reloading', this.currentVersion, config.applicationVersion);
294
+ this.brokerController.currentVersion != config.applicationVersion) {
295
+ console.warn('version changed, reloading', this.brokerController.currentVersion, config.applicationVersion);
286
296
  this.nukeWorkspaceHandler();
287
297
  }
288
298
  else {
289
- config.applicationVersion = this.currentVersion;
299
+ config.applicationVersion = this.brokerController.currentVersion;
290
300
  this.settingsBag?.set(SettingsBag, config);
291
301
  }
292
302
  }
@@ -298,15 +308,20 @@ let TheDoctor = class TheDoctor extends LitElement {
298
308
  this.requestUpdate();
299
309
  this.viewerPanel.click();
300
310
  }
301
- tickRandomly() {
302
- this.randomTicker = Math.floor(Math.random() * 9999999);
303
- }
304
- nukeWorkspaceHandler() {
311
+ nukeWorkspaceHandler(e) {
305
312
  this.bagManager.resetBags();
306
313
  localStorage.removeItem("pb33f-doctor-version");
307
- ModelService.resetWorkspace().then(() => {
308
- window.location.reload();
309
- });
314
+ if (!e || !e.detail || e.detail.resetFiles) {
315
+ ModelService.resetWorkspace().then(() => {
316
+ window.location.reload();
317
+ });
318
+ }
319
+ else {
320
+ // no work to do, just reload the page.
321
+ setTimeout(() => {
322
+ window.location.reload();
323
+ }, 50);
324
+ }
310
325
  }
311
326
  minimapToggled() {
312
327
  this.minimapVisible = !this.minimapVisible;
@@ -318,91 +333,12 @@ let TheDoctor = class TheDoctor extends LitElement {
318
333
  this.editor = this.querySelector('pb33f-editor#spec-editor');
319
334
  this.rulesetEditor = this.querySelector('pb33f-editor#ruleset-editor');
320
335
  }
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
336
  addClickTrack(node) {
346
337
  history.pushState({ activeNode: node.idHash }, "", `?view=explore&node=${node.idHash}`);
347
338
  }
348
339
  addRefTrack(ref) {
349
340
  history.pushState({ ref: ref }, "", `?view=spec&ref=${ref}`);
350
341
  }
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
342
  filterTreeModel(event) {
407
343
  this.filteredNodes = new Map();
408
344
  event.detail.graph.nodes.forEach((node) => {
@@ -411,385 +347,6 @@ let TheDoctor = class TheDoctor extends LitElement {
411
347
  this.modelTree.filteredNodes = this.filteredNodes;
412
348
  this.modelTree.renderedNodes = this.renderedNodeMap;
413
349
  }
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
350
  exportRuleset() {
794
351
  this.exportRulesetDialog.show();
795
352
  }
@@ -799,452 +356,6 @@ let TheDoctor = class TheDoctor extends LitElement {
799
356
  sendToast(toast) {
800
357
  this.toastManager.addToastManually(toast);
801
358
  }
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
359
  updateInspectorDivider() {
1249
360
  const panelState = this.panelStateBag?.get(PanelStateBag);
1250
361
  if (panelState) {
@@ -1289,15 +400,18 @@ let TheDoctor = class TheDoctor extends LitElement {
1289
400
  }
1290
401
  this.referenceMapBag?.set(ReferenceMapBag, refMap);
1291
402
  }
403
+ creditEmpty(e) {
404
+ this.platformUnavailable(e.detail.error);
405
+ }
1292
406
  platformUnavailable(error) {
1293
407
  if (!this.unavailable) {
1294
- this.loadingOverlay.hide();
1295
- this.activitySpinner.hide();
408
+ this.loadingOverlay?.hide();
409
+ this.activitySpinner?.hide();
1296
410
  this.errorBanner.errorTitle = "The doctor is out.";
1297
- if (!error) {
411
+ if (!error || !error.detail) {
1298
412
  this.errorBanner.errorMessage = "The clinic is <strong>closed!</strong> " +
1299
413
  "The doctor is currently unavailable" +
1300
- " because there is no response from the platform.";
414
+ " because there is no response (when asked) from the pb33f platform.";
1301
415
  }
1302
416
  else {
1303
417
  this.errorBanner.errorMessage = "The clinic is <strong>closed!</strong> " +
@@ -1309,716 +423,12 @@ let TheDoctor = class TheDoctor extends LitElement {
1309
423
  this.controlTabGroup.show(ActiveView.Overview);
1310
424
  }
1311
425
  }
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
426
  builtInRulesetSelected() {
1925
427
  // todo
1926
428
  }
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
429
  runDiagnostics() {
2020
430
  if (!this.autoDiagnose) {
2021
- this.lintSpec(this.editor.getValue());
431
+ this.diagnosticController.lintSpec(this.editor.getValue());
2022
432
  }
2023
433
  }
2024
434
  boostrap() {
@@ -2028,20 +438,21 @@ let TheDoctor = class TheDoctor extends LitElement {
2028
438
  if (urlParam) {
2029
439
  //this.urlInput.value = urlParam;
2030
440
  this.activeURL = urlParam;
2031
- this.lintSpec('', urlParam);
441
+ this.diagnosticController.lintSpec('', urlParam);
2032
442
  return;
2033
443
  }
444
+ // todo: this needs work man. I want to see all these settimeouts gone brother.
2034
445
  setTimeout(() => {
2035
- this.loadingOverlay.hide();
2036
446
  // check for a history
2037
447
  this.timeVortex.doctor = this;
2038
448
  this.timeVortex.checkHistory();
2039
449
  const editorValue = this.docBag?.get(DefaultDocument);
2040
450
  if (!editorValue) {
2041
451
  LintingService.bootstrapEditor().then((result) => {
452
+ this.loadingOverlay.hide();
2042
453
  this.editor.setValue(result, true);
2043
454
  this.rolodexActivePath = "";
2044
- this.specChanged(new CustomEvent(EditorUpdated, {
455
+ this.specController.specChanged(new CustomEvent(EditorUpdated, {
2045
456
  detail: {
2046
457
  content: result,
2047
458
  id: "spec"
@@ -2049,40 +460,31 @@ let TheDoctor = class TheDoctor extends LitElement {
2049
460
  }));
2050
461
  });
2051
462
  }
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;
463
+ else {
464
+ ModelService.getCurrentSpec().then((result) => {
465
+ this.loadingOverlay.hide();
466
+ this.rolodexController.queryRolodex();
467
+ if (result != editorValue) {
468
+ this.editor.setValue(result, true);
469
+ this.rolodexActivePath = "";
470
+ this.specController.specChanged(new CustomEvent(EditorUpdated, {
471
+ detail: {
472
+ content: result,
473
+ id: "spec"
474
+ }
475
+ }));
476
+ }
477
+ });
2060
478
  }
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
- });
479
+ }, 50);
2078
480
  }
2079
481
  confirmExport() {
2080
482
  switch (this.exportSelection.value) {
2081
483
  case "json":
2082
- this.exportJSON();
484
+ this.ruleController.exportJSON();
2083
485
  break;
2084
486
  case "yaml":
2085
- this.exportYAML();
487
+ this.ruleController.exportYAML();
2086
488
  break;
2087
489
  }
2088
490
  }
@@ -2276,6 +678,30 @@ let TheDoctor = class TheDoctor extends LitElement {
2276
678
  </sl-tab-group>
2277
679
  `;
2278
680
  }
681
+ const controls = html `
682
+ <div class="controls">
683
+ <sl-tooltip content="Upload zip file or tar.gz (tarball)" placement="top">
684
+ <sl-button ?disabled=${this.importDisabled} class="import-button" variant="text"
685
+ size="small" @click="${() => {
686
+ this.uploadArchive.show();
687
+ }}">
688
+ Import
689
+ </sl-button>
690
+ </sl-tooltip>
691
+ <sl-tooltip content="Nuke / reset workspace" placement="top">
692
+ <sl-icon-button class="nuke-session-button" name="radioactive" @click="${() => {
693
+ this.nukeWorkspace.show();
694
+ }}">
695
+ </sl-icon-button>
696
+ </sl-tooltip>
697
+ </div>`;
698
+ let navigatorView = html `
699
+ ${controls}
700
+ ${modelTree}
701
+ `;
702
+ if (this.authController.state && this.authController.state.authenticated) {
703
+ this.statusBar.authenticated = true;
704
+ }
2279
705
  return html `
2280
706
  ${this.uploadArchive}
2281
707
  ${this.nukeWorkspace}
@@ -2308,30 +734,11 @@ let TheDoctor = class TheDoctor extends LitElement {
2308
734
  ${this.activitySpinner}
2309
735
  ${this.errorBanner}
2310
736
  ${overlay}
2311
-
2312
737
  <sl-split-panel id="explorer-split-panel" class="split-panel-explorer" position="12"
2313
738
  @sl-reposition="${this.updateExplorerDivider}">
2314
739
  <sl-icon id="split-divider-tree" slot="divider" name="grip-vertical" class="divider-vert"></sl-icon>
2315
740
  <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}
741
+ ${navigatorView}
2335
742
  ${this.creditTicker}
2336
743
  </div>
2337
744
  <div slot="end">
@@ -2462,15 +869,17 @@ let TheDoctor = class TheDoctor extends LitElement {
2462
869
  </sl-split-panel>
2463
870
  </div>
2464
871
  </sl-split-panel>
2465
-
2466
872
  </div>`;
2467
873
  }
874
+ randomTicker() {
875
+ return crypto.randomUUID().toString();
876
+ }
2468
877
  // <iframe src="${this.doctorEndpoint}/model/scalar-template?z=${this.randomTicker}" width="100%" height="100%" style="border: none"></iframe>
2469
878
  fetchUrl(event) {
2470
879
  this.activeURL = event.detail.url;
2471
880
  this.rolodexNeedsReset = true;
2472
881
  this.rolodexActivePath = "/";
2473
- this.lintSpec('', event.detail.url);
882
+ this.diagnosticController.lintSpec('', event.detail.url);
2474
883
  }
2475
884
  hideUrlError() {
2476
885
  this.urlProblem.style.display = 'none';
@@ -2574,9 +983,6 @@ __decorate([
2574
983
  __decorate([
2575
984
  query('#inspector-split-panel')
2576
985
  ], TheDoctor.prototype, "splitPanelInspector", void 0);
2577
- __decorate([
2578
- state()
2579
- ], TheDoctor.prototype, "randomTicker", void 0);
2580
986
  __decorate([
2581
987
  state()
2582
988
  ], TheDoctor.prototype, "rulesetPulse", void 0);