@luquimbo/bi-superpowers 3.1.0 → 3.2.0

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 (110) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.claude-plugin/skill-manifest.json +1 -1
  4. package/.plugin/plugin.json +1 -1
  5. package/README.md +2 -2
  6. package/bin/build-plugin.js +6 -6
  7. package/bin/cli.js +169 -310
  8. package/bin/commands/install.js +87 -70
  9. package/bin/commands/install.test.js +2 -2
  10. package/bin/lib/agents.js +21 -2
  11. package/bin/lib/mcp-config.js +27 -5
  12. package/bin/lib/mcp-config.test.js +1 -1
  13. package/desktop-extension/manifest.json +4 -11
  14. package/desktop-extension/server.js +34 -25
  15. package/package.json +3 -9
  16. package/skills/pbi-connect/SKILL.md +1 -1
  17. package/skills/project-kickoff/SKILL.md +1 -1
  18. package/bin/commands/add.js +0 -533
  19. package/bin/commands/add.test.js +0 -77
  20. package/bin/commands/changelog.js +0 -443
  21. package/bin/commands/pull.js +0 -287
  22. package/bin/commands/pull.test.js +0 -36
  23. package/bin/commands/push.js +0 -231
  24. package/bin/commands/push.test.js +0 -14
  25. package/bin/commands/search.js +0 -344
  26. package/bin/commands/search.test.js +0 -115
  27. package/bin/commands/setup.js +0 -545
  28. package/bin/commands/setup.test.js +0 -46
  29. package/bin/commands/sync-profile.js +0 -405
  30. package/bin/commands/sync-profile.test.js +0 -14
  31. package/bin/commands/sync-source.js +0 -418
  32. package/bin/commands/sync-source.test.js +0 -14
  33. package/bin/utils/errors.js +0 -159
  34. package/bin/utils/git.js +0 -298
  35. package/bin/utils/logger.js +0 -142
  36. package/bin/utils/pbix.js +0 -305
  37. package/bin/utils/pbix.test.js +0 -37
  38. package/bin/utils/profiles.js +0 -312
  39. package/bin/utils/projects.js +0 -169
  40. package/bin/utils/readline.js +0 -206
  41. package/bin/utils/readline.test.js +0 -47
  42. package/docs/openrouter-free-models.md +0 -92
  43. package/library/examples/README.md +0 -151
  44. package/library/examples/finance-reporting/README.md +0 -351
  45. package/library/examples/finance-reporting/data-model.md +0 -267
  46. package/library/examples/finance-reporting/measures.dax +0 -557
  47. package/library/examples/hr-analytics/README.md +0 -371
  48. package/library/examples/hr-analytics/data-model.md +0 -315
  49. package/library/examples/hr-analytics/measures.dax +0 -460
  50. package/library/examples/marketing-analytics/README.md +0 -37
  51. package/library/examples/marketing-analytics/data-model.md +0 -62
  52. package/library/examples/marketing-analytics/measures.dax +0 -110
  53. package/library/examples/retail-analytics/README.md +0 -439
  54. package/library/examples/retail-analytics/data-model.md +0 -288
  55. package/library/examples/retail-analytics/measures.dax +0 -481
  56. package/library/examples/supply-chain/README.md +0 -37
  57. package/library/examples/supply-chain/data-model.md +0 -69
  58. package/library/examples/supply-chain/measures.dax +0 -77
  59. package/library/examples/udf-library/README.md +0 -228
  60. package/library/examples/udf-library/functions.dax +0 -571
  61. package/library/snippets/dax/README.md +0 -292
  62. package/library/snippets/dax/business-domains.md +0 -576
  63. package/library/snippets/dax/calculate-patterns.md +0 -276
  64. package/library/snippets/dax/calculation-groups.md +0 -489
  65. package/library/snippets/dax/error-handling.md +0 -495
  66. package/library/snippets/dax/iterators-and-aggregations.md +0 -474
  67. package/library/snippets/dax/kpis-and-metrics.md +0 -293
  68. package/library/snippets/dax/rankings-and-topn.md +0 -235
  69. package/library/snippets/dax/security-patterns.md +0 -413
  70. package/library/snippets/dax/text-and-formatting.md +0 -316
  71. package/library/snippets/dax/time-intelligence.md +0 -196
  72. package/library/snippets/dax/user-defined-functions.md +0 -477
  73. package/library/snippets/dax/virtual-tables.md +0 -546
  74. package/library/snippets/excel-formulas/README.md +0 -84
  75. package/library/snippets/excel-formulas/aggregations.md +0 -330
  76. package/library/snippets/excel-formulas/dates-and-times.md +0 -361
  77. package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
  78. package/library/snippets/excel-formulas/lookups.md +0 -169
  79. package/library/snippets/excel-formulas/text-functions.md +0 -363
  80. package/library/snippets/governance/naming-conventions.md +0 -97
  81. package/library/snippets/governance/review-checklists.md +0 -107
  82. package/library/snippets/power-query/README.md +0 -389
  83. package/library/snippets/power-query/api-integration.md +0 -707
  84. package/library/snippets/power-query/connections.md +0 -434
  85. package/library/snippets/power-query/data-cleaning.md +0 -298
  86. package/library/snippets/power-query/error-handling.md +0 -526
  87. package/library/snippets/power-query/parameters.md +0 -350
  88. package/library/snippets/power-query/performance.md +0 -506
  89. package/library/snippets/power-query/transformations.md +0 -330
  90. package/library/snippets/report-design/accessibility.md +0 -78
  91. package/library/snippets/report-design/chart-selection.md +0 -54
  92. package/library/snippets/report-design/layout-patterns.md +0 -87
  93. package/library/templates/data-models/README.md +0 -93
  94. package/library/templates/data-models/finance-model.md +0 -627
  95. package/library/templates/data-models/retail-star-schema.md +0 -473
  96. package/library/templates/excel/README.md +0 -83
  97. package/library/templates/excel/budget-tracker.md +0 -432
  98. package/library/templates/excel/data-entry-form.md +0 -533
  99. package/library/templates/power-bi/README.md +0 -72
  100. package/library/templates/power-bi/finance-report.md +0 -449
  101. package/library/templates/power-bi/kpi-scorecard.md +0 -461
  102. package/library/templates/power-bi/sales-dashboard.md +0 -281
  103. package/library/themes/excel/README.md +0 -436
  104. package/library/themes/power-bi/README.md +0 -271
  105. package/library/themes/power-bi/accessible.json +0 -307
  106. package/library/themes/power-bi/bi-superpowers-default.json +0 -858
  107. package/library/themes/power-bi/corporate-blue.json +0 -291
  108. package/library/themes/power-bi/dark-mode.json +0 -291
  109. package/library/themes/power-bi/minimal.json +0 -292
  110. package/library/themes/power-bi/print-friendly.json +0 -309
@@ -1,495 +0,0 @@
1
- # Error Handling and Defensive DAX
2
-
3
- Patterns for handling errors, blanks, edge cases, and creating robust measures.
4
-
5
- ---
6
-
7
- ## DIVIDE Function
8
-
9
- ### Basic Safe Division
10
-
11
- ```dax
12
- -- Always use DIVIDE instead of /
13
- Profit_Margin =
14
- DIVIDE(
15
- SUM(Sales[Profit]),
16
- SUM(Sales[Revenue])
17
- )
18
- -- Returns BLANK if denominator is 0 or blank
19
- ```
20
-
21
- ### Custom Alternate Result
22
-
23
- ```dax
24
- -- Return 0 instead of BLANK
25
- Profit_Margin_Zero =
26
- DIVIDE(
27
- SUM(Sales[Profit]),
28
- SUM(Sales[Revenue]),
29
- 0 -- Return 0 when can't divide
30
- )
31
- ```
32
-
33
- ### Nested DIVIDE for Complex Calculations
34
-
35
- ```dax
36
- -- Multiple divisions with safety
37
- Efficiency_Ratio =
38
- DIVIDE(
39
- DIVIDE(SUM(Sales[Units]), SUM(Staff[Hours])),
40
- DIVIDE(SUM(Budget[Units]), SUM(Budget[Hours])),
41
- 1 -- Return 1 (100%) if comparison not possible
42
- )
43
- ```
44
-
45
- ---
46
-
47
- ## BLANK Handling
48
-
49
- ### ISBLANK / BLANK
50
-
51
- ```dax
52
- -- Check for blank and substitute
53
- Sales_Display =
54
- IF(
55
- ISBLANK(SUM(Sales[Amount])),
56
- 0,
57
- SUM(Sales[Amount])
58
- )
59
- ```
60
-
61
- ### COALESCE (DAX 2019+)
62
-
63
- ```dax
64
- -- First non-blank value
65
- Customer_Contact =
66
- COALESCE(
67
- [PreferredEmail],
68
- [WorkEmail],
69
- [PersonalEmail],
70
- "No Email"
71
- )
72
- ```
73
-
74
- ```dax
75
- -- Chain of fallback values
76
- Revenue_Display =
77
- COALESCE(
78
- [Actual Revenue],
79
- [Forecast Revenue],
80
- [Budget Revenue],
81
- 0
82
- )
83
- ```
84
-
85
- ### Handle Blank in Comparisons
86
-
87
- ```dax
88
- -- Safe comparison with potential blanks
89
- Has_Sales =
90
- VAR _Sales = SUM(Sales[Amount])
91
- RETURN
92
- IF(
93
- ISBLANK(_Sales) || _Sales = 0,
94
- "No",
95
- "Yes"
96
- )
97
- ```
98
-
99
- ---
100
-
101
- ## Error Handling
102
-
103
- ### IFERROR - Catch All Errors
104
-
105
- ```dax
106
- -- Handle any calculation error
107
- Safe_Calculation =
108
- IFERROR(
109
- [ComplexMeasure] / [AnotherMeasure],
110
- BLANK()
111
- )
112
- ```
113
-
114
- ### ISERROR - Test Before Calculate
115
-
116
- ```dax
117
- -- Test for error condition
118
- Error_Status =
119
- IF(
120
- ISERROR([RiskyCalculation]),
121
- "Error in calculation",
122
- FORMAT([RiskyCalculation], "#,##0")
123
- )
124
- ```
125
-
126
- ### Error Handling in Iterators
127
-
128
- ```dax
129
- -- Safe iteration with error handling
130
- Total_Safe =
131
- SUMX(
132
- Sales,
133
- IFERROR(
134
- Sales[Quantity] * Sales[Price] / Sales[Discount],
135
- Sales[Quantity] * Sales[Price] -- Fallback if error
136
- )
137
- )
138
- ```
139
-
140
- ---
141
-
142
- ## Null and Empty String Handling
143
-
144
- ### Distinguish BLANK from Empty String
145
-
146
- ```dax
147
- -- Check for truly empty values
148
- Has_Value =
149
- VAR _Value = SELECTEDVALUE(Products[Description])
150
- RETURN
151
- IF(
152
- ISBLANK(_Value) || _Value = "",
153
- FALSE(),
154
- TRUE()
155
- )
156
- ```
157
-
158
- ### Clean Text Input
159
-
160
- ```dax
161
- -- Handle various empty states
162
- Clean_Name =
163
- VAR _Raw = SELECTEDVALUE(Customers[Name])
164
- RETURN
165
- IF(
166
- ISBLANK(_Raw) || TRIM(_Raw) = "" || _Raw = "N/A" || _Raw = "NULL",
167
- "(Unknown)",
168
- TRIM(_Raw)
169
- )
170
- ```
171
-
172
- ---
173
-
174
- ## Context Validation
175
-
176
- ### HASONEVALUE - Single Selection Check
177
-
178
- ```dax
179
- -- Only calculate if single value selected
180
- Unit_Price_Display =
181
- IF(
182
- HASONEVALUE(Products[ProductName]),
183
- SELECTEDVALUE(Products[UnitPrice]),
184
- BLANK() -- Don't show if multiple products
185
- )
186
- ```
187
-
188
- ### HASONEFILTER - Filter Check
189
-
190
- ```dax
191
- -- Check if filter is applied
192
- Status_Message =
193
- IF(
194
- HASONEFILTER(Products[Category]),
195
- "Showing: " & SELECTEDVALUE(Products[Category]),
196
- "All Categories"
197
- )
198
- ```
199
-
200
- ### ISFILTERED / ISCROSSFILTERED
201
-
202
- ```dax
203
- -- Validate filter state
204
- Contextual_Result =
205
- SWITCH(
206
- TRUE(),
207
- NOT ISFILTERED(Products[Category]), [Total_All],
208
- ISFILTERED(Products[Category]) && NOT ISCROSSFILTERED(Regions), [Total_Category],
209
- [Total_Filtered]
210
- )
211
- ```
212
-
213
- ### SELECTEDVALUE with Fallback
214
-
215
- ```dax
216
- -- Safe single value selection
217
- Selected_Product =
218
- SELECTEDVALUE(
219
- Products[ProductName],
220
- "(Multiple Products Selected)" -- Default when not single value
221
- )
222
- ```
223
-
224
- ---
225
-
226
- ## Date Validation
227
-
228
- ### Check for Valid Dates
229
-
230
- ```dax
231
- -- Validate date exists
232
- Days_Since_Order =
233
- VAR _OrderDate = MAX(Sales[OrderDate])
234
- VAR _Today = TODAY()
235
- RETURN
236
- IF(
237
- ISBLANK(_OrderDate) || _OrderDate > _Today,
238
- BLANK(),
239
- DATEDIFF(_OrderDate, _Today, DAY)
240
- )
241
- ```
242
-
243
- ### Handle Date Range Edge Cases
244
-
245
- ```dax
246
- -- Safe date period calculation
247
- Period_Sales =
248
- VAR _StartDate = MIN('Date'[Date])
249
- VAR _EndDate = MAX('Date'[Date])
250
- RETURN
251
- IF(
252
- ISBLANK(_StartDate) || ISBLANK(_EndDate) || _StartDate > _EndDate,
253
- BLANK(),
254
- CALCULATE(
255
- SUM(Sales[Amount]),
256
- 'Date'[Date] >= _StartDate && 'Date'[Date] <= _EndDate
257
- )
258
- )
259
- ```
260
-
261
- ---
262
-
263
- ## Data Quality Measures
264
-
265
- ### Completeness Check
266
-
267
- ```dax
268
- Data_Completeness =
269
- VAR _TotalRows = COUNTROWS(Sales)
270
- VAR _CompleteRows =
271
- COUNTROWS(
272
- FILTER(
273
- Sales,
274
- NOT ISBLANK(Sales[Amount])
275
- && NOT ISBLANK(Sales[CustomerID])
276
- && NOT ISBLANK(Sales[ProductID])
277
- && NOT ISBLANK(Sales[Date])
278
- )
279
- )
280
- RETURN
281
- DIVIDE(_CompleteRows, _TotalRows)
282
- ```
283
-
284
- ### Duplicate Detection
285
-
286
- ```dax
287
- Duplicate_Orders =
288
- VAR _UniqueOrders = DISTINCTCOUNT(Sales[OrderID])
289
- VAR _TotalRows = COUNTROWS(Sales)
290
- RETURN
291
- IF(
292
- _UniqueOrders < _TotalRows,
293
- _TotalRows - _UniqueOrders,
294
- 0
295
- )
296
- ```
297
-
298
- ### Outlier Detection
299
-
300
- ```dax
301
- Outlier_Count =
302
- VAR _Avg = AVERAGE(Sales[Amount])
303
- VAR _StdDev =
304
- SQRT(
305
- AVERAGEX(Sales, POWER(Sales[Amount] - _Avg, 2))
306
- )
307
- VAR _LowerBound = _Avg - (3 * _StdDev)
308
- VAR _UpperBound = _Avg + (3 * _StdDev)
309
- RETURN
310
- COUNTROWS(
311
- FILTER(
312
- Sales,
313
- Sales[Amount] < _LowerBound || Sales[Amount] > _UpperBound
314
- )
315
- )
316
- ```
317
-
318
- ---
319
-
320
- ## Conditional Logic Patterns
321
-
322
- ### SWITCH with Catch-All
323
-
324
- ```dax
325
- -- Always include default case
326
- Region_Group =
327
- SWITCH(
328
- SELECTEDVALUE(Geography[Country]),
329
- "USA", "North America",
330
- "Canada", "North America",
331
- "Mexico", "North America",
332
- "UK", "Europe",
333
- "Germany", "Europe",
334
- "France", "Europe",
335
- "Other" -- Catch-all for unknown values
336
- )
337
- ```
338
-
339
- ### SWITCH TRUE Pattern
340
-
341
- ```dax
342
- -- Complex conditional with error handling
343
- Performance_Level =
344
- VAR _Value = [Sales_vs_Target]
345
- RETURN
346
- SWITCH(
347
- TRUE(),
348
- ISBLANK(_Value), "No Data",
349
- ISERROR(_Value), "Error",
350
- _Value >= 1.2, "Exceeds",
351
- _Value >= 1.0, "Meets",
352
- _Value >= 0.8, "Below",
353
- _Value < 0.8, "Critical",
354
- "Unknown" -- Should never reach here
355
- )
356
- ```
357
-
358
- ### Nested IF vs SWITCH
359
-
360
- ```dax
361
- -- Prefer SWITCH for multiple conditions
362
- -- Less error-prone than nested IFs
363
- Status_SWITCH =
364
- SWITCH(
365
- TRUE(),
366
- [DaysOverdue] > 90, "Critical",
367
- [DaysOverdue] > 60, "Severe",
368
- [DaysOverdue] > 30, "Warning",
369
- [DaysOverdue] > 0, "Overdue",
370
- [DaysOverdue] = 0, "Due Today",
371
- "On Track"
372
- )
373
- ```
374
-
375
- ---
376
-
377
- ## Debug Measures
378
-
379
- ### Show Calculation Context
380
-
381
- ```dax
382
- -- Debug: Show current filter context
383
- Debug_Context =
384
- "Products: " & COUNTROWS(Products) &
385
- " | Dates: " & COUNTROWS('Date') &
386
- " | Filtered: " & IF(ISFILTERED(Products), "Yes", "No")
387
- ```
388
-
389
- ### Trace Measure Calculation
390
-
391
- ```dax
392
- -- Debug: Step-by-step calculation
393
- Debug_Calculation =
394
- VAR _Step1 = SUM(Sales[Quantity])
395
- VAR _Step2 = AVERAGE(Sales[UnitPrice])
396
- VAR _Step3 = _Step1 * _Step2
397
- VAR _Step4 = DIVIDE(_Step3, 100)
398
- RETURN
399
- "Qty:" & _Step1 &
400
- " | AvgPrice:" & _Step2 &
401
- " | Product:" & _Step3 &
402
- " | Result:" & _Step4
403
- ```
404
-
405
- ### Validate Intermediate Results
406
-
407
- ```dax
408
- -- Production measure with validation
409
- Revenue_Validated =
410
- VAR _Quantity = SUM(Sales[Quantity])
411
- VAR _Price = AVERAGE(Sales[UnitPrice])
412
- VAR _Result = _Quantity * _Price
413
- RETURN
414
- IF(
415
- _Quantity < 0 || _Price < 0,
416
- ERROR("Negative values detected in Revenue calculation"),
417
- _Result
418
- )
419
- ```
420
-
421
- ---
422
-
423
- ## Best Practices
424
-
425
- ### 1. Always Use DIVIDE
426
-
427
- ```dax
428
- -- Never do this
429
- Bad_Ratio = [Sales] / [Customers]
430
-
431
- -- Always do this
432
- Good_Ratio = DIVIDE([Sales], [Customers])
433
- ```
434
-
435
- ### 2. Validate Before Calculate
436
-
437
- ```dax
438
- -- Check inputs before complex calculation
439
- Complex_Result =
440
- IF(
441
- ISBLANK([Input1]) || ISBLANK([Input2]) || [Input2] = 0,
442
- BLANK(),
443
- [ComplexCalculation]
444
- )
445
- ```
446
-
447
- ### 3. Use Variables for Intermediate Checks
448
-
449
- ```dax
450
- -- Capture values once, validate, then calculate
451
- Safe_Growth =
452
- VAR _Current = SUM(Sales[Amount])
453
- VAR _Prior = [Sales_PY]
454
- RETURN
455
- IF(
456
- ISBLANK(_Current) || ISBLANK(_Prior) || _Prior = 0,
457
- BLANK(),
458
- DIVIDE(_Current - _Prior, _Prior)
459
- )
460
- ```
461
-
462
- ### 4. Handle Edge Cases Explicitly
463
-
464
- ```dax
465
- -- Consider all scenarios
466
- Date_Status =
467
- VAR _Date = SELECTEDVALUE(Sales[DueDate])
468
- VAR _Today = TODAY()
469
- RETURN
470
- SWITCH(
471
- TRUE(),
472
- ISBLANK(_Date), "No Due Date",
473
- _Date < DATE(1900, 1, 1), "Invalid Date",
474
- _Date > DATE(2100, 12, 31), "Invalid Date",
475
- _Date < _Today, "Past Due",
476
- _Date = _Today, "Due Today",
477
- _Date <= _Today + 7, "Due This Week",
478
- "Upcoming"
479
- )
480
- ```
481
-
482
- ---
483
-
484
- ## Error Prevention Checklist
485
-
486
- | Issue | Prevention |
487
- |-------|------------|
488
- | Division by zero | Use DIVIDE() |
489
- | Blank values | ISBLANK() checks, COALESCE() |
490
- | Missing data | SELECTEDVALUE with alternate |
491
- | Invalid dates | Date range validation |
492
- | Context errors | HASONEVALUE, ISFILTERED |
493
- | Type mismatches | Explicit conversions |
494
- | Calculation errors | IFERROR wrapper |
495
- | Edge cases | SWITCH TRUE with default |