@datawheel/bespoke 0.2.0 → 0.2.2

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 (3) hide show
  1. package/dist/index.js +158 -109
  2. package/dist/server.js +394 -885
  3. package/package.json +8 -8
package/dist/server.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { initAuth0 } from '@auth0/nextjs-auth0';
2
+ import auth0 from 'auth0';
2
3
  import * as pg from 'pg';
3
4
  import { Sequelize, Error as Error$1, fn, col, Op, DataTypes, QueryTypes, Model } from 'sequelize';
4
5
  import yn3 from 'yn';
@@ -19,7 +20,6 @@ import FlickrSDK from 'flickr-sdk';
19
20
  import sharp from 'sharp';
20
21
  import { createApi } from 'unsplash-js';
21
22
  import fs from 'fs';
22
- import auth0 from 'auth0';
23
23
  import NextCors from 'nextjs-cors';
24
24
  import formidable from 'formidable';
25
25
  import toposort from 'toposort';
@@ -66,6 +66,232 @@ var handleAuth_default = getAuth_default.handleAuth({
66
66
  }
67
67
  });
68
68
 
69
+ // api/lib.ts
70
+ var BackendError = class extends Error {
71
+ constructor(code, message) {
72
+ super(message);
73
+ this.code = code;
74
+ }
75
+ toJSON() {
76
+ return failureResult(this);
77
+ }
78
+ static is(obj) {
79
+ return Object.prototype.hasOwnProperty.call(obj, "code") && typeof obj.code === "number";
80
+ }
81
+ };
82
+ function successResult(result) {
83
+ return { ok: true, status: 200, data: result };
84
+ }
85
+ function failureResult(err) {
86
+ return { ok: false, status: BackendError.is(err) ? err.code : 500, error: err.message };
87
+ }
88
+ function resultWrapper(method) {
89
+ return (param) => method(param).then(successResult, failureResult);
90
+ }
91
+ function pickMethod(api, operation, entity) {
92
+ const name = `${operation}${capitalize(entity)}`;
93
+ return api[name];
94
+ }
95
+ function capitalize(str) {
96
+ return str[0].toUpperCase() + str.slice(1);
97
+ }
98
+
99
+ // types/auth.ts
100
+ var CMS_ROLES = {
101
+ ADMIN: "Admin",
102
+ EDITOR: "Editor",
103
+ WRITER: "Writer"
104
+ };
105
+ var CMS_ROLES_TYPES = {
106
+ APP: "App",
107
+ USER: "User"
108
+ };
109
+ var addRoleTypes = (roles) => {
110
+ const systemRoles = Object.values(CMS_ROLES);
111
+ return roles ? roles.map((r) => {
112
+ return {
113
+ ...r,
114
+ type: systemRoles.includes(r.name) ? CMS_ROLES_TYPES.APP : CMS_ROLES_TYPES.USER
115
+ };
116
+ }) : [];
117
+ };
118
+
119
+ // api/db/users/auth0.ts
120
+ var auth0ConfigObject = {
121
+ domain: new URL(process.env.AUTH0_ISSUER_BASE_URL || "").host,
122
+ clientId: process.env.AUTH0_CLIENT_ID,
123
+ clientSecret: process.env.AUTH0_CLIENT_SECRET
124
+ };
125
+ var managementClient = new auth0.ManagementClient(auth0ConfigObject);
126
+ function dbSearchRole() {
127
+ return async () => {
128
+ try {
129
+ const roles = await managementClient.roles.getAll({
130
+ include_totals: true,
131
+ per_page: 100
132
+ });
133
+ return {
134
+ roles: addRoleTypes(roles.roles)
135
+ };
136
+ } catch (err) {
137
+ console.error("AuthZeroError", err);
138
+ throw new BackendError(err.response?.status || 500, err.message);
139
+ }
140
+ };
141
+ }
142
+ function dbSearchUser() {
143
+ return async (filters) => {
144
+ try {
145
+ let users;
146
+ if (filters) {
147
+ const pagination = {
148
+ include_totals: true,
149
+ per_page: 10,
150
+ page: filters.page || 0
151
+ };
152
+ if (filters.role_id && filters.role_id !== "") {
153
+ const paramsByRole = {
154
+ ...pagination,
155
+ id: filters.role_id
156
+ };
157
+ users = await managementClient.roles.getUsers(paramsByRole);
158
+ } else {
159
+ const paramsSearch = {
160
+ ...pagination,
161
+ q: filters.query
162
+ };
163
+ users = await managementClient.users.getAll(paramsSearch);
164
+ }
165
+ }
166
+ return users;
167
+ } catch (err) {
168
+ console.error("AuthZeroError", err);
169
+ throw new BackendError(err.response?.status || 500, err.message);
170
+ }
171
+ };
172
+ }
173
+ function dbReadUser() {
174
+ return async (userId) => {
175
+ try {
176
+ if (userId) {
177
+ const userData = await Promise.all([
178
+ managementClient.users.get({ id: userId }),
179
+ managementClient.users.getRoles({ id: userId })
180
+ ]);
181
+ return {
182
+ ...userData[0],
183
+ roles: userData[1]
184
+ };
185
+ } else {
186
+ throw new BackendError(404, "User ID required");
187
+ }
188
+ } catch (err) {
189
+ console.error("AuthZeroError", err);
190
+ throw new BackendError(err.response?.status || 500, err.message);
191
+ }
192
+ };
193
+ }
194
+ function dbUpdateUser() {
195
+ return async (user) => {
196
+ try {
197
+ if (user.user_id) {
198
+ if (user.app_metadata) {
199
+ await updateUserMetadata(user.user_id, user.app_metadata);
200
+ }
201
+ if (user.roles) {
202
+ const currentRoles = await managementClient.users.getRoles({ id: user.user_id });
203
+ const oldRoles = currentRoles.map((r) => r.id);
204
+ const newRoles = user.roles.map((r) => r.id);
205
+ const rolesToAssign = newRoles.filter((role) => !oldRoles.includes(role));
206
+ const rolesToDelete = oldRoles.filter((role) => !newRoles.includes(role));
207
+ if (rolesToAssign.length > 0) {
208
+ await managementClient.users.assignRoles({ id: user.user_id }, { roles: rolesToAssign });
209
+ }
210
+ if (rolesToDelete.length > 0) {
211
+ await managementClient.users.removeRoles({ id: user.user_id }, { roles: rolesToDelete });
212
+ }
213
+ }
214
+ return "User updated!";
215
+ } else {
216
+ throw new BackendError(404, "user_id is required");
217
+ }
218
+ } catch (err) {
219
+ console.error("AuthZeroError", err);
220
+ throw new BackendError(err.response?.status || 500, err.message);
221
+ }
222
+ };
223
+ }
224
+ function dbAddNewReportToCurrentUser() {
225
+ return async (params) => {
226
+ try {
227
+ return "NOT USED SERVICE";
228
+ } catch (err) {
229
+ console.error("AuthZeroError", err);
230
+ throw new BackendError(err.response?.status || 500, err.message);
231
+ }
232
+ };
233
+ }
234
+ async function updateUserMetadata(userId, newMetadata) {
235
+ return await managementClient.users.updateAppMetadata({ id: userId }, newMetadata);
236
+ }
237
+ async function searchUsersByMetadata(key, value) {
238
+ try {
239
+ const users = await managementClient.getUsers({
240
+ search_engine: "v3",
241
+ q: `app_metadata.${key}:"${value}"`
242
+ });
243
+ for (let index = 0; index < users.length; index++) {
244
+ const u = users[index];
245
+ u.bespoke_app_metadata = u.app_metadata || {};
246
+ delete u.app_metadata;
247
+ u.bespoke_roles = (await managementClient.users.getRoles({ id: u.user_id })).map((r) => r.name);
248
+ }
249
+ return users;
250
+ } catch (error) {
251
+ console.error(error);
252
+ }
253
+ }
254
+
255
+ // api/auth/updateCurrentUserMetadata.tsx
256
+ var updateCurrentUserMetadata = (req, res, newMetadata) => {
257
+ return new Promise((resolve, reject) => {
258
+ const updateMetadata = async () => {
259
+ try {
260
+ const session = await getAuth_default.getSession(req, res);
261
+ if (!session || !session.user) {
262
+ throw new Error("not_authenticated");
263
+ } else {
264
+ const { user } = session;
265
+ const oldMetadata = session.user.bespoke_app_metadata || {};
266
+ const latestMetadata = {
267
+ ...oldMetadata,
268
+ ...newMetadata
269
+ };
270
+ await updateUserMetadata(session.user.sub, latestMetadata);
271
+ await getAuth_default.updateSession(req, res, {
272
+ ...session,
273
+ user: {
274
+ ...user,
275
+ bespoke_app_metadata: latestMetadata
276
+ }
277
+ });
278
+ resolve({
279
+ error: "",
280
+ description: `Settings updated for user ${session.user.email}`
281
+ });
282
+ }
283
+ } catch (error) {
284
+ reject({
285
+ error: error.message,
286
+ description: "The user does not have an active session or is not authenticated"
287
+ });
288
+ }
289
+ };
290
+ updateMetadata();
291
+ });
292
+ };
293
+ var updateCurrentUserMetadata_default = updateCurrentUserMetadata;
294
+
69
295
  // libs/js/arrayUtils.ts
70
296
  var keyDiver = (obj, str) => !str ? obj : typeof str === "string" ? str.split(".").reduce((o, i) => o[i], obj) : obj;
71
297
  function arrContainsAnyItem(arr1, arr2) {
@@ -95,6 +321,32 @@ var withApiRoleAuthRequired = (apiRoute, allowedRoles) => {
95
321
  };
96
322
  };
97
323
  var withApiRoleAuthRequired_default = withApiRoleAuthRequired;
324
+
325
+ // api/auth/searchUsersByMetadata.tsx
326
+ var searchUsersByMetadata2 = (key, value) => {
327
+ return new Promise((resolve, reject) => {
328
+ const searchUsers = async () => {
329
+ try {
330
+ if (key && value && key !== "" && value !== "") {
331
+ const users = await searchUsersByMetadata(key, value);
332
+ resolve(users);
333
+ } else {
334
+ reject({
335
+ error: "empty_params",
336
+ description: "key or value params are empty"
337
+ });
338
+ }
339
+ } catch (error) {
340
+ reject({
341
+ error: error.message,
342
+ description: "Error "
343
+ });
344
+ }
345
+ };
346
+ searchUsers();
347
+ });
348
+ };
349
+ var searchUsersByMetadata_default = searchUsersByMetadata2;
98
350
  var getLogging_default = (env = process.env) => yn3(env.REPORTS_LOGGING);
99
351
  var BlockModel = class extends Model {
100
352
  getContent(locale) {
@@ -448,687 +700,62 @@ var libraries = {
448
700
  titleCase
449
701
  },
450
702
  stats: stats_default
451
- };
452
- var declaredFormatters = {};
453
- function declareFormatter(manifest) {
454
- const func = manifest.logicFactory(libraries);
455
- declaredFormatters[manifest.name] = {
456
- description: manifest.description,
457
- inputType: manifest.inputType,
458
- logic: stringifyFn(func),
459
- testValue: manifest.testValue,
460
- type: manifest.type || "formatter"
461
- };
462
- return func;
463
- }
464
- function stringifyFn(func) {
465
- const strFunc = func.toString().trim();
466
- if (strFunc.startsWith("function")) {
467
- return strFunc.replace(/^function(?:\s\w+)?\([^\)]\)/, "").trim().replace(/^{([\s\S]+)\}$/, "$1");
468
- }
469
- const noArgs = strFunc.replace(/^.+?=>/, "").trim();
470
- return noArgs.startsWith("{") ? noArgs.replace(/^{([\s\S]+)\}$/, "$1") : `return ${noArgs}`;
471
- }
472
- var REPORT_TYPES = {
473
- REPORT: "report",
474
- STORY: "story"
475
- };
476
- var REPORT_MODES = {
477
- UNILATERAL: "uni",
478
- MULTILATERAL: "multi"
479
- };
480
- var SELECTOR_TYPES = {
481
- SINGLE: "single",
482
- MULTI: "multi"
483
- };
484
- var REPORT_FIELDS = {
485
- TITLE: "title",
486
- SUBTITLE: "subtitle",
487
- LABEL: "label"
488
- };
489
- ({
490
- [REPORT_TYPES.REPORT]: Object.values(REPORT_FIELDS),
491
- [REPORT_TYPES.STORY]: Object.values(REPORT_FIELDS)
492
- });
493
- var BLOCK_CONTENT_TYPES = {
494
- // AUTHOR: "author",
495
- // FOOTNOTE: "footnote",
496
- IMAGE: "image",
497
- PARAGRAPH: "paragraph",
498
- SELECTOR: "selector",
499
- // todo1.0 - move to logic types
500
- STAT: "stat",
501
- SUBTITLE: "subtitle",
502
- TITLE: "title",
503
- NAV: "nav",
504
- // todo1.0, how to put custom blocks in here?
505
- RESET_BUTTON: "reset_button"
506
- };
507
- var BLOCK_LOGIC_TYPES = {
508
- GENERATOR: "generator",
509
- RELATED: "related",
510
- VIZ: "visualization"
511
- };
512
- var BLOCK_LOGIC_LOCALE = "logic";
513
- var BLOCK_TYPES = { ...BLOCK_CONTENT_TYPES, ...BLOCK_LOGIC_TYPES };
514
- var FORMATTER_TYPES = {
515
- FORMATTER: "formatter",
516
- FUNCTION: "function"
517
- };
518
- var FORMATTER_INPUT_TYPES = {
519
- STRING: "string",
520
- NUMBER: "number",
521
- OBJECT: "object",
522
- DATE: "date"
523
- };
524
-
525
- // libs/formatters/index.ts
526
- var formatters_exports = {};
527
- __export(formatters_exports, {
528
- abbreviate: () => abbreviate_default,
529
- abs: () => abs_default,
530
- bucket: () => bucket_default,
531
- commas: () => commas_default,
532
- date: () => date_default,
533
- dollar: () => dollar_default,
534
- formatFieldName: () => formatFieldName_default,
535
- grewTo: () => grewTo_default,
536
- grewWord: () => grewWord_default,
537
- growing: () => growing_default,
538
- growth: () => growth_default,
539
- growthPct: () => growthPct_default,
540
- growthWord: () => growthWord_default,
541
- highWord: () => highWord_default,
542
- increaseWord: () => increaseWord_default,
543
- increasedWord: () => increasedWord_default,
544
- increasing: () => increasing_default,
545
- largerThan: () => largerThan_default,
546
- list: () => list_default,
547
- longWord: () => longWord_default,
548
- lowerCaseFirst: () => lowerCaseFirst_default,
549
- moreFewerWord: () => moreFewerWord_default,
550
- moreLess: () => moreLess_default,
551
- moreWord: () => moreWord_default,
552
- olderWord: () => olderWord_default,
553
- olderYounger: () => olderYounger_default,
554
- plural: () => plural_default,
555
- pxToInt: () => pxToInt_default,
556
- salary: () => salary_default,
557
- stripEntities: () => stripEntities_default,
558
- stripHTML: () => stripHTML_default,
559
- stripOL: () => stripOL_default,
560
- stripP: () => stripP_default,
561
- stripUL: () => stripUL_default,
562
- toKebabCase: () => toKebabCase_default,
563
- toSpacedCase: () => toSpacedCase_default,
564
- upperCaseFirst: () => upperCaseFirst_default
565
- });
566
-
567
- // libs/formatters/abbreviate.js
568
- var abbreviate_default = declareFormatter({
569
- name: "abbreviate",
570
- description: "Abbreviates a number into a smaller more human-readable number.",
571
- logicFactory(libs) {
572
- return (n) => libs.d3plus.formatAbbreviate(n);
573
- },
574
- inputType: "number",
575
- testValue: "123456",
576
- type: "formatter"
577
- });
578
-
579
- // libs/formatters/abs.js
580
- var abs_default = declareFormatter({
581
- name: "abs",
582
- description: "Simple Absolute Value.",
583
- logicFactory(libs) {
584
- return (n) => Math.abs(n);
585
- },
586
- inputType: "number",
587
- testValue: "-123",
588
- type: "formatter"
589
- });
590
-
591
- // libs/formatters/bucket.js
592
- var logicFactory = () => function bucket(n) {
593
- const re = new RegExp(/([\$0-9\,]+)[A-z\-\s\&]*([\$0-9\,]*)/g);
594
- let nums = re.exec(n);
595
- if (nums) {
596
- nums = nums.slice(1).filter((d) => d.length).map((d) => {
597
- if (d.includes(",")) {
598
- if (d.indexOf(",") === d.length - 4) {
599
- d = d.replace(/,000$/g, "k").replace(/([0-9]+)(,999$)/g, (n2) => `${parseInt(n2) + 1}k`);
600
- } else if (d.indexOf(",") === d.length - 8) {
601
- d = d.replace(/,000,000$/g, "M").replace(/([0-9]+)(,999,999$)/g, (n2) => `${parseInt(n2) + 1}M`);
602
- }
603
- }
604
- return d;
605
- });
606
- if (nums.length === 2)
607
- return nums.join(" - ");
608
- if (n.toLowerCase().match(/under|less|\</g))
609
- return `< ${nums[0]}`;
610
- if (n.toLowerCase().match(/over|more|\+|\>/g))
611
- return `${nums[0]}+`;
612
- return `${nums[0]}`;
613
- }
614
- return "None";
615
- };
616
- var bucket_default = declareFormatter({
617
- name: "bucket",
618
- description: "Sanitizes bucket strings to '< n', 'n1 - n2', and 'n+'",
619
- logicFactory,
620
- inputType: "number",
621
- testValue: "1234",
622
- type: "formatter"
623
- });
624
-
625
- // libs/formatters/commas.js
626
- var commas_default = declareFormatter({
627
- name: "commas",
628
- description: "* Rounds to nearest whole number and adds commas.",
629
- logicFactory(libs) {
630
- return (n) => libs.d3.format(",")(Math.round(n));
631
- },
632
- inputType: "number",
633
- testValue: "1234.6",
634
- type: "formatter"
635
- });
636
-
637
- // libs/formatters/date.js
638
- var date_default = declareFormatter({
639
- name: "date",
640
- description: "Formats a date into '%B %d, %Y' format.",
641
- logicFactory(libs) {
642
- return (n) => {
643
- if (typeof n === "string") {
644
- n = libs.d3plus.date(n);
645
- }
646
- return libs.d3.timeFormat("%B %d, %Y")(n);
647
- };
648
- },
649
- inputType: "date",
650
- testValue: "2022-01-01",
651
- type: "formatter"
652
- });
653
-
654
- // libs/formatters/dollar.js
655
- var dollar_default = declareFormatter({
656
- name: "dollar",
657
- description: "Adds a US dollar sign to the beginning of a String or Number.",
658
- logicFactory(libs) {
659
- return (n) => {
660
- if (typeof n === "number")
661
- n = libs.d3plus.formatAbbreviate(n);
662
- return n.charAt(0) === "-" ? n.replace("-", "-$") : `$${n}`;
663
- };
664
- },
665
- inputType: "number",
666
- testValue: "123",
667
- type: "formatter"
668
- });
669
-
670
- // libs/formatters/formatFieldName.js
671
- var formatFieldName_default = declareFormatter({
672
- name: "formatFieldName",
673
- description: "Based on field name returns a string",
674
- logicFactory(libs) {
675
- return (n) => {
676
- if (n.contentType.toLowerCase() === "stat") {
677
- if (n.field === "title")
678
- return "Stat label";
679
- if (n.field === "value")
680
- return "Stat value";
681
- if (n.field === "subtitle")
682
- return "Stat subtitle";
683
- if (n.field === "tooltip")
684
- return "Tooltip text";
685
- }
686
- if (n.contentType.toLowerCase() === "section") {
687
- if (n.field === "short")
688
- return "Short title";
689
- }
690
- if (n.contentType.toLowerCase() === "description") {
691
- if (n.field === "description")
692
- return "Paragraph";
693
- }
694
- return n.field;
695
- };
696
- },
697
- inputType: "object",
698
- testValue: '{"field":"title","contentType":"stat"}',
699
- type: "formatter"
700
- });
701
-
702
- // libs/formatters/grewTo.js
703
- var grewTo_default = declareFormatter({
704
- name: "grewTo",
705
- description: "Returns the text with grow direction: 'declined from', 'grew to' o 'stayed at'",
706
- logicFactory(libs) {
707
- return (n) => n < 0 ? "declined from" : n > 0 ? "grew to" : "stayed at";
708
- },
709
- inputType: "number",
710
- testValue: "-1234",
711
- type: "formatter"
712
- });
713
-
714
- // libs/formatters/grewWord.js
715
- var grewWord_default = declareFormatter({
716
- name: "grewWord",
717
- description: "Returns either 'grew', 'declined', or 'stayed' depending on the provided number's sign.",
718
- logicFactory(libs) {
719
- return (n) => n < 0 ? "declined" : n > 0 ? "grew" : "stayed";
720
- },
721
- inputType: "number",
722
- testValue: "-1234",
723
- type: "formatter"
724
- });
725
-
726
- // libs/formatters/growing.js
727
- var growing_default = declareFormatter({
728
- name: "growing",
729
- description: "Returns either 'declining', 'growing' depending on the provided number's sign.",
730
- logicFactory(libs) {
731
- return (n) => n < 0 ? "declining" : "growing";
732
- },
733
- inputType: "number",
734
- testValue: "-1234",
735
- type: "formatter"
736
- });
737
-
738
- // libs/formatters/growth.js
739
- var logicFactory2 = () => function growth(n) {
740
- const {
741
- curr,
742
- currMoe = 0,
743
- prev,
744
- prevMoe = 0
745
- } = n;
746
- let value;
747
- if (currMoe || prevMoe) {
748
- const f1 = (-prev / curr ** 2) ** 2 * currMoe ** 2;
749
- const f2 = (1 / curr) ** 2 * prevMoe ** 2;
750
- value = Math.sqrt(f1 + f2);
751
- } else
752
- value = (curr - prev) / prev;
753
- return value * 100;
754
- };
755
- var growth_default = declareFormatter({
756
- name: "growth",
757
- // eslint-disable-next-line max-len
758
- description: "Calculates the growth percentage between two numbers provided the following object format: {curr, prev}. Also supports calculating the growth between two margin of errors using this format: {curr, currMoe, prev, prevMoe}",
759
- logicFactory: logicFactory2,
760
- inputType: "object",
761
- testValue: '{"curr":10,"currMoe":0,"prev":13,"prevMoe":0}',
762
- type: "formatter"
763
- });
764
-
765
- // libs/formatters/growthPct.js
766
- var growthPct_default = declareFormatter({
767
- name: "growthPct",
768
- description: "Abbreviates a growth value, turns it absolute, and adds a percent sign.",
769
- logicFactory(libs) {
770
- return (n) => `${libs.d3plus.formatAbbreviate(Math.abs(n))}%`;
771
- },
772
- inputType: "number",
773
- testValue: "12345",
774
- type: "formatter"
775
- });
776
-
777
- // libs/formatters/growthWord.js
778
- var growthWord_default = declareFormatter({
779
- name: "growthWord",
780
- description: "Returns either 'growth' or 'decline' depending on the provided number's sign.",
781
- logicFactory(libs) {
782
- return (n) => n < 0 ? "decline" : "growth";
783
- },
784
- inputType: "number",
785
- testValue: "-1234",
786
- type: "formatter"
787
- });
788
-
789
- // libs/formatters/highWord.js
790
- var highWord_default = declareFormatter({
791
- name: "highWord",
792
- // eslint-disable-next-line max-len
793
- description: "Returns either 'higher than', 'lower than', or 'approximately the same as' depending on the provided number's sign.",
794
- logicFactory(libs) {
795
- return (n) => n < 0 ? "lower than" : n > 0 ? "higher than" : "approximately the same as";
796
- },
797
- inputType: "number",
798
- testValue: "0",
799
- type: "formatter"
800
- });
801
-
802
- // libs/formatters/increaseWord.js
803
- var increaseWord_default = declareFormatter({
804
- name: "increaseWord",
805
- // eslint-disable-next-line max-len
806
- description: "Returns either 'increase', 'decrease', or 'change' depending on the provided number's sign.",
807
- logicFactory(libs) {
808
- return (n) => n < 0 ? "decrease" : n > 0 ? "increase" : "change";
809
- },
810
- inputType: "number",
811
- testValue: "1234",
812
- type: "formatter"
813
- });
814
-
815
- // libs/formatters/increasedWord.js
816
- var increasedWord_default = declareFormatter({
817
- name: "increasedWord",
818
- // eslint-disable-next-line max-len
819
- description: "Returns either 'increased', 'decreased', or 'remained the same' depending on the provided number's sign.",
820
- logicFactory(libs) {
821
- return (n) => n < 0 ? "decreased" : n > 0 ? "increased" : "remained the same";
822
- },
823
- inputType: "number",
824
- testValue: "0",
825
- type: "formatter"
826
- });
827
-
828
- // libs/formatters/increasing.js
829
- var increasing_default = declareFormatter({
830
- name: "increasing",
831
- description: "Returns either 'decreasing', 'increasing' or 'maintaining' depending on the provided number's sign.",
832
- logicFactory(libs) {
833
- return (n) => n < 0 ? "decreasing" : n > 0 ? "increasing" : "maintaining";
834
- },
835
- inputType: "number",
836
- testValue: "-1234",
837
- type: "formatter"
838
- });
839
-
840
- // libs/formatters/largerThan.js
841
- var largerThan_default = declareFormatter({
842
- name: "largerThan",
843
- // eslint-disable-next-line max-len
844
- description: "Returns either 'higher than', 'lower than', or 'approximately the same as' depending on the provided number's sign",
845
- logicFactory(libs) {
846
- return (n) => n < 0 ? "smaller than" : n > 0 ? "larger than" : "the same as";
847
- },
848
- inputType: "number",
849
- testValue: "-1234",
850
- type: "formatter"
851
- });
852
-
853
- // libs/formatters/list.js
854
- var list_default = declareFormatter({
855
- name: "list",
856
- // eslint-disable-next-line max-len
857
- description: "Joins an array of strings together, adding commas and 'and' when necessary.",
858
- logicFactory(libs) {
859
- return (n) => n.reduce((str, item, i) => {
860
- if (!i)
861
- str += item;
862
- else if (i === n.length - 1 && i === 1)
863
- str += ` and ${item}`;
864
- else if (i === n.length - 1)
865
- str += `, and ${item}`;
866
- else
867
- str += `, ${item}`;
868
- return str;
869
- }, "");
870
- },
871
- inputType: "object",
872
- testValue: '["foo","bar","bee"]',
873
- type: "formatter"
874
- });
875
-
876
- // libs/formatters/longWord.js
877
- var longWord_default = declareFormatter({
878
- name: "longWord",
879
- description: "",
880
- logicFactory(libs) {
881
- return (n) => n < 0 ? "shorter" : n > 0 ? "longer" : "similar";
882
- },
883
- inputType: "number",
884
- testValue: "5",
885
- type: "formatter"
886
- });
887
-
888
- // libs/formatters/lowerCaseFirst.js
889
- var lowerCaseFirst_default = declareFormatter({
890
- name: "lowerCaseFirst",
891
- description: "Converts the first letter of a string to lowercase",
892
- logicFactory(libs) {
893
- return (n) => typeof n === "string" ? n.charAt(0).toLowerCase() + n.slice(1) : n;
894
- },
895
- inputType: "string",
896
- testValue: "foo",
897
- type: "formatter"
898
- });
899
-
900
- // libs/formatters/moreFewerWord.js
901
- var moreFewerWord_default = declareFormatter({
902
- name: "moreFewerWord",
903
- description: "",
904
- logicFactory(libs) {
905
- return (n) => n < 0 ? "fewer" : "more";
906
- },
907
- inputType: "number",
908
- testValue: "-5",
909
- type: "formatter"
910
- });
911
-
912
- // libs/formatters/moreLess.js
913
- var moreLess_default = declareFormatter({
914
- name: "moreLess",
915
- description: "Returns either 'more' or 'less' depending on the provided number's sign.",
916
- logicFactory(libs) {
917
- return (n) => n < 0 ? "less" : "more";
918
- },
919
- inputType: "number",
920
- testValue: "-5",
921
- type: "formatter"
922
- });
923
-
924
- // libs/formatters/moreWord.js
925
- var moreWord_default = declareFormatter({
926
- name: "moreLess",
927
- // eslint-disable-next-line max-len
928
- description: "Returns either 'more than', 'less than', or 'approximately the same' depending on the provided number's sign.",
929
- logicFactory(libs) {
930
- return (n) => n < 0 ? "less than" : n > 0 ? "more than" : "approximately the same";
931
- },
932
- inputType: "number",
933
- testValue: "-5",
934
- type: "formatter"
935
- });
936
-
937
- // libs/formatters/olderWord.js
938
- var olderWord_default = declareFormatter({
939
- name: "olderWord",
940
- description: "",
941
- logicFactory(libs) {
942
- return (n) => n < 0 ? "getting younger" : n > 0 ? "getting older" : "staying the same age";
943
- },
944
- inputType: "number",
945
- testValue: "-5",
946
- type: "formatter"
947
- });
948
-
949
- // libs/formatters/olderYounger.js
950
- var olderYounger_default = declareFormatter({
951
- name: "olderYounger",
952
- description: "",
953
- logicFactory(libs) {
954
- return (n) => n < 0 ? "younger than" : n > 0 ? "older than" : "the same age as";
955
- },
956
- inputType: "number",
957
- testValue: "-5",
958
- type: "formatter"
959
- });
960
-
961
- // libs/formatters/plural.js
962
- var plural_default = declareFormatter({
963
- name: "plural",
964
- description: "Pluralizes a word.",
965
- logicFactory(libs) {
966
- return (n) => n.replace(/\w$/g, (chr) => chr === "y" ? "ies" : `${chr}s`);
967
- },
968
- inputType: "string",
969
- testValue: "Foo",
970
- type: "formatter"
971
- });
972
-
973
- // libs/formatters/pxToInt.js
974
- var pxToInt_default = declareFormatter({
975
- name: "pxToInt",
976
- description: "Takes a pixel value and converts it to an integer",
977
- logicFactory(libs) {
978
- return (n) => parseInt(n.replace(/\D+/g, ""), 10);
979
- },
980
- inputType: "string",
981
- testValue: "12px",
982
- type: "formatter"
983
- });
984
-
985
- // libs/formatters/salary.js
986
- var salary_default = declareFormatter({
987
- name: "salary",
988
- description: "Displays salary values with proper precision (ie. '$74,200' instead of '$74.2k')",
989
- logicFactory(libs) {
990
- return (n) => {
991
- let str;
992
- if (n < 1e6) {
993
- str = libs.d3.format(",")(n.toFixed(0));
994
- } else
995
- str = libs.d3plus.formatAbbreviate(n);
996
- return `$${str}`;
997
- };
998
- },
999
- inputType: "number",
1000
- testValue: "1000000",
1001
- type: "formatter"
1002
- });
1003
-
1004
- // libs/formatters/stripEntities.js
1005
- var stripEntities_default = declareFormatter({
1006
- name: "stripEntities",
1007
- description: "Removes non breaking spaces & other html entities from a string",
1008
- logicFactory(libs) {
1009
- return (n) => typeof n === "string" ? String(n).replace(/&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});/ig, " ") : n;
1010
- },
1011
- inputType: "string",
1012
- testValue: "<b>MyText</b>",
1013
- type: "formatter"
1014
- });
1015
-
1016
- // libs/formatters/stripHTML.js
1017
- var stripHTML_default = declareFormatter({
1018
- name: "stripHTML",
1019
- description: "Converts html tags to spaces, then removes redundant spaces",
1020
- logicFactory(libs) {
1021
- return (n) => {
1022
- const entities = {
1023
- "&amp;": "&",
1024
- "&lt;": "<",
1025
- "&gt;": ">",
1026
- "&quot;": '"',
1027
- "&#x27;": "'",
1028
- "&#x60;": "`",
1029
- "&nbsp;": ""
1030
- };
1031
- const source = `(?:${Object.keys(entities).join("|")})`;
1032
- const testRegexp = RegExp(source);
1033
- const replaceRegexp = RegExp(source, "g");
1034
- const s = String(n).replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
1035
- return testRegexp.test(s) ? s.replace(replaceRegexp, (match) => entities[match]) : s;
1036
- };
1037
- },
1038
- inputType: "string",
1039
- testValue: "<b>My text</b>",
1040
- type: "formatter"
1041
- });
1042
-
1043
- // libs/formatters/stripOL.js
1044
- var stripOL_default = declareFormatter({
1045
- name: "stripOL",
1046
- description: "Removes ordered list wrapper tags from a string.",
1047
- logicFactory(libs) {
1048
- return (n) => n.replace(/<ol>/g, "").replace(/<\/ol>/g, "");
1049
- },
1050
- inputType: "string",
1051
- testValue: "<ol><li>My list</li></ol>",
1052
- type: "formatter"
1053
- });
1054
-
1055
- // libs/formatters/stripP.js
1056
- var stripP_default = declareFormatter({
1057
- name: "stripP",
1058
- description: "Removes all paragraph tags from a string.",
1059
- logicFactory(libs) {
1060
- return (n) => n.replace(/<p>/g, "").replace(/<\/p>/g, "");
1061
- },
1062
- inputType: "string",
1063
- testValue: "<p>My p</p>",
1064
- type: "formatter"
1065
- });
1066
-
1067
- // libs/formatters/stripUL.js
1068
- var stripUL_default = declareFormatter({
1069
- name: "stripUL",
1070
- description: "Removes unordered list wrapper tags from a string.",
1071
- logicFactory(libs) {
1072
- return (n) => n.replace(/<ul>/g, "").replace(/<\/ul>/g, "");
1073
- },
1074
- inputType: "string",
1075
- testValue: "<ul><li>My list</li></ul>",
1076
- type: "formatter"
1077
- });
1078
-
1079
- // libs/formatters/toKebabCase.js
1080
- var toKebabCase_default = declareFormatter({
1081
- name: "toKebabCase",
1082
- description: "Takes a camelCase or PascalCase string and converts it to kebab-case.",
1083
- logicFactory(libs) {
1084
- return (n) => {
1085
- if (typeof n === "string") {
1086
- n = n.charAt(0).toLowerCase() + n.substring(1);
1087
- return n.replace(/([A-Z])/g, "-$1").toLowerCase();
1088
- }
1089
- return "invalid string passed to toKebabCase()";
1090
- };
1091
- },
1092
- inputType: "string",
1093
- testValue: "camelCaseText",
1094
- type: "formatter"
1095
- });
1096
-
1097
- // libs/formatters/toSpacedCase.js
1098
- var toSpacedCase_default = declareFormatter({
1099
- name: "toSpacedCase",
1100
- // eslint-disable-next-line max-len
1101
- description: "Takes a camelCase or PascalCase string and adds spaces (I know Spaced Case isn't a thing, deal with it).",
1102
- logicFactory(libs) {
1103
- return (n) => {
1104
- if (typeof n === "string") {
1105
- return n.replace(/([A-Z])/g, " $1").toLowerCase().trim();
1106
- }
1107
- return "invalid string passed to toSpacedCase()";
1108
- };
1109
- },
1110
- inputType: "string",
1111
- testValue: "camelCaseText",
1112
- type: "formatter"
1113
- });
1114
-
1115
- // libs/formatters/upperCaseFirst.js
1116
- var upperCaseFirst_default = declareFormatter({
1117
- name: "upperCaseFirst",
1118
- // eslint-disable-next-line max-len
1119
- description: "Converts the first letter of a string to uppercase",
1120
- logicFactory(libs) {
1121
- return (n) => n.charAt(0).toUpperCase() + n.slice(1);
1122
- },
1123
- inputType: "string",
1124
- testValue: "my name",
1125
- type: "formatter"
703
+ };
704
+ var declaredFormatters = {};
705
+ var REPORT_TYPES = {
706
+ REPORT: "report",
707
+ STORY: "story"
708
+ };
709
+ var REPORT_MODES = {
710
+ UNILATERAL: "uni",
711
+ MULTILATERAL: "multi"
712
+ };
713
+ var SELECTOR_TYPES = {
714
+ SINGLE: "single",
715
+ MULTI: "multi"
716
+ };
717
+ var REPORT_FIELDS = {
718
+ TITLE: "title",
719
+ SUBTITLE: "subtitle",
720
+ LABEL: "label"
721
+ };
722
+ ({
723
+ [REPORT_TYPES.REPORT]: Object.values(REPORT_FIELDS),
724
+ [REPORT_TYPES.STORY]: Object.values(REPORT_FIELDS)
1126
725
  });
726
+ var BLOCK_CONTENT_TYPES = {
727
+ // AUTHOR: "author",
728
+ // FOOTNOTE: "footnote",
729
+ IMAGE: "image",
730
+ PARAGRAPH: "paragraph",
731
+ SELECTOR: "selector",
732
+ // todo1.0 - move to logic types
733
+ STAT: "stat",
734
+ SUBTITLE: "subtitle",
735
+ TITLE: "title",
736
+ NAV: "nav",
737
+ // todo1.0, how to put custom blocks in here?
738
+ RESET_BUTTON: "reset_button"
739
+ };
740
+ var BLOCK_LOGIC_TYPES = {
741
+ GENERATOR: "generator",
742
+ RELATED: "related",
743
+ VIZ: "visualization"
744
+ };
745
+ var BLOCK_LOGIC_LOCALE = "logic";
746
+ var BLOCK_TYPES = { ...BLOCK_CONTENT_TYPES, ...BLOCK_LOGIC_TYPES };
747
+ var FORMATTER_TYPES = {
748
+ FORMATTER: "formatter",
749
+ FUNCTION: "function"
750
+ };
751
+ var FORMATTER_INPUT_TYPES = {
752
+ STRING: "string",
753
+ NUMBER: "number",
754
+ OBJECT: "object",
755
+ DATE: "date"
756
+ };
1127
757
 
1128
758
  // db/models/formatter.ts
1129
- var verbose = getLogging_default();
1130
- if (verbose)
1131
- console.log(`Loaded formatters: ${Object.keys(formatters_exports)}`);
1132
759
  var FormatterModel = class extends Model {
1133
760
  };
1134
761
  function initModel4(sequelize) {
@@ -1654,7 +1281,7 @@ var modelFactoryMap = {
1654
1281
  };
1655
1282
 
1656
1283
  // db/index.ts
1657
- var verbose2 = getLogging_default();
1284
+ var verbose = getLogging_default();
1658
1285
  var connectionString = process.env.REPORTS_DB_CONNECTION || "";
1659
1286
  var shouldWipe = yn3(process.env.REPORTS_DB_WIPE);
1660
1287
  if (!connectionString) {
@@ -1665,7 +1292,7 @@ if (!connectionString) {
1665
1292
  }
1666
1293
  var getDB = () => {
1667
1294
  if (!global.sequelize) {
1668
- if (verbose2)
1295
+ if (verbose)
1669
1296
  console.log("\u{1F511} INITIALIZING DB CONNECTION");
1670
1297
  let sequelize;
1671
1298
  try {
@@ -1708,42 +1335,12 @@ var getDB = () => {
1708
1335
  })
1709
1336
  );
1710
1337
  } else {
1711
- if (verbose2)
1338
+ if (verbose)
1712
1339
  console.log("\u267B\uFE0F RE-UTILIZING DB CONNECTION");
1713
1340
  }
1714
1341
  return global.sequelize;
1715
1342
  };
1716
1343
 
1717
- // api/lib.ts
1718
- var BackendError = class extends Error {
1719
- constructor(code, message) {
1720
- super(message);
1721
- this.code = code;
1722
- }
1723
- toJSON() {
1724
- return failureResult(this);
1725
- }
1726
- static is(obj) {
1727
- return Object.prototype.hasOwnProperty.call(obj, "code") && typeof obj.code === "number";
1728
- }
1729
- };
1730
- function successResult(result) {
1731
- return { ok: true, status: 200, data: result };
1732
- }
1733
- function failureResult(err) {
1734
- return { ok: false, status: BackendError.is(err) ? err.code : 500, error: err.message };
1735
- }
1736
- function resultWrapper(method) {
1737
- return (param) => method(param).then(successResult, failureResult);
1738
- }
1739
- function pickMethod(api, operation, entity) {
1740
- const name = `${operation}${capitalize(entity)}`;
1741
- return api[name];
1742
- }
1743
- function capitalize(str) {
1744
- return str[0].toUpperCase() + str.slice(1);
1745
- }
1746
-
1747
1344
  // libs/configs/getLocales.ts
1748
1345
  var getLocales_default = () => {
1749
1346
  const localeDefault11 = process.env.NEXT_PUBLIC_REPORTS_LOCALE_DEFAULT || "en";
@@ -2137,25 +1734,25 @@ var whitelist = [
2137
1734
  "vi",
2138
1735
  "zh"
2139
1736
  ];
2140
- var verbose3 = getLogging_default();
1737
+ var verbose2 = getLogging_default();
2141
1738
  var { locales } = getLocales_default();
2142
1739
  var enabledLocales = locales.filter((d) => whitelist.includes(d));
2143
1740
  var initializing = false;
2144
1741
  var getSearchIndexByLocale = async (db, forceRegenerate = false) => {
2145
1742
  if (forceRegenerate) {
2146
- if (verbose3)
1743
+ if (verbose2)
2147
1744
  console.log("0\uFE0F\u20E3 CLEARING SEARCH INDEX");
2148
1745
  global.lunrsearch = void 0;
2149
1746
  initializing = false;
2150
1747
  }
2151
1748
  if (global.lunrsearch) {
2152
- if (verbose3)
1749
+ if (verbose2)
2153
1750
  console.log("\u{1F501} RE-UTILIZING SEARCH INDEX");
2154
1751
  return global.lunrsearch;
2155
1752
  }
2156
1753
  try {
2157
1754
  if (!initializing) {
2158
- if (verbose3)
1755
+ if (verbose2)
2159
1756
  console.log("1\uFE0F\u20E3 INITIALIZING SEARCH INDEX");
2160
1757
  initializing = true;
2161
1758
  global.lunrsearch = await newSearchIndex(db);
@@ -2178,7 +1775,7 @@ async function newSearchIndex(db) {
2178
1775
  const lunrLang = (await import(`lunr-languages/lunr.${locale}`)).default;
2179
1776
  lunrLang(lunr);
2180
1777
  lunrStemmer(lunr);
2181
- if (verbose3)
1778
+ if (verbose2)
2182
1779
  console.log(`\u{1F3F3}\uFE0F INITIALIZING SEARCH ${locale} LOCALE`);
2183
1780
  }
2184
1781
  return [
@@ -3041,26 +2638,6 @@ function normalizeList(value) {
3041
2638
  return value.toString().split(",");
3042
2639
  }
3043
2640
 
3044
- // types/auth.ts
3045
- var CMS_ROLES = {
3046
- ADMIN: "Admin",
3047
- EDITOR: "Editor",
3048
- WRITER: "Writer"
3049
- };
3050
- var CMS_ROLES_TYPES = {
3051
- APP: "App",
3052
- USER: "User"
3053
- };
3054
- var addRoleTypes = (roles) => {
3055
- const systemRoles = Object.values(CMS_ROLES);
3056
- return roles ? roles.map((r) => {
3057
- return {
3058
- ...r,
3059
- type: systemRoles.includes(r.name) ? CMS_ROLES_TYPES.APP : CMS_ROLES_TYPES.USER
3060
- };
3061
- }) : [];
3062
- };
3063
-
3064
2641
  // api/endpoints/member.ts
3065
2642
  var { localeDefault: localeDefault3 } = getLocales_default();
3066
2643
  function endpointMemberFactory(operations) {
@@ -3725,120 +3302,6 @@ function dbReadPrivateBlocksFactory(db) {
3725
3302
  };
3726
3303
  }
3727
3304
  }
3728
- var auth0ConfigObject = {
3729
- domain: new URL(process.env.AUTH0_ISSUER_BASE_URL || "").host,
3730
- clientId: process.env.AUTH0_CLIENT_ID,
3731
- clientSecret: process.env.AUTH0_CLIENT_SECRET
3732
- };
3733
- var managementClient = new auth0.ManagementClient(auth0ConfigObject);
3734
- function dbSearchRole() {
3735
- return async () => {
3736
- try {
3737
- const roles = await managementClient.roles.getAll({
3738
- include_totals: true,
3739
- per_page: 100
3740
- });
3741
- return {
3742
- roles: addRoleTypes(roles.roles)
3743
- };
3744
- } catch (err) {
3745
- console.error("AuthZeroError", err);
3746
- throw new BackendError(err.response?.status || 500, err.message);
3747
- }
3748
- };
3749
- }
3750
- function dbSearchUser() {
3751
- return async (filters) => {
3752
- try {
3753
- let users;
3754
- if (filters) {
3755
- const pagination = {
3756
- include_totals: true,
3757
- per_page: 10,
3758
- page: filters.page || 0
3759
- };
3760
- if (filters.role_id && filters.role_id !== "") {
3761
- const paramsByRole = {
3762
- ...pagination,
3763
- id: filters.role_id
3764
- };
3765
- users = await managementClient.roles.getUsers(paramsByRole);
3766
- } else {
3767
- const paramsSearch = {
3768
- ...pagination,
3769
- q: filters.query
3770
- };
3771
- users = await managementClient.users.getAll(paramsSearch);
3772
- }
3773
- }
3774
- return users;
3775
- } catch (err) {
3776
- console.error("AuthZeroError", err);
3777
- throw new BackendError(err.response?.status || 500, err.message);
3778
- }
3779
- };
3780
- }
3781
- function dbReadUser() {
3782
- return async (userId) => {
3783
- try {
3784
- if (userId) {
3785
- const userData = await Promise.all([
3786
- managementClient.users.get({ id: userId }),
3787
- managementClient.users.getRoles({ id: userId })
3788
- ]);
3789
- return {
3790
- ...userData[0],
3791
- roles: userData[1]
3792
- };
3793
- } else {
3794
- throw new BackendError(404, "User ID required");
3795
- }
3796
- } catch (err) {
3797
- console.error("AuthZeroError", err);
3798
- throw new BackendError(err.response?.status || 500, err.message);
3799
- }
3800
- };
3801
- }
3802
- function dbUpdateUser() {
3803
- return async (user) => {
3804
- try {
3805
- if (user.user_id) {
3806
- if (user.app_metadata) {
3807
- await managementClient.users.updateAppMetadata({ id: user.user_id }, user.app_metadata);
3808
- }
3809
- if (user.roles) {
3810
- const currentRoles = await managementClient.users.getRoles({ id: user.user_id });
3811
- const oldRoles = currentRoles.map((r) => r.id);
3812
- const newRoles = user.roles.map((r) => r.id);
3813
- const rolesToAssign = newRoles.filter((role) => !oldRoles.includes(role));
3814
- const rolesToDelete = oldRoles.filter((role) => !newRoles.includes(role));
3815
- if (rolesToAssign.length > 0) {
3816
- await managementClient.users.assignRoles({ id: user.user_id }, { roles: rolesToAssign });
3817
- }
3818
- if (rolesToDelete.length > 0) {
3819
- await managementClient.users.removeRoles({ id: user.user_id }, { roles: rolesToDelete });
3820
- }
3821
- }
3822
- return "User updated!";
3823
- } else {
3824
- throw new BackendError(404, "user_id is required");
3825
- }
3826
- } catch (err) {
3827
- console.error("AuthZeroError", err);
3828
- throw new BackendError(err.response?.status || 500, err.message);
3829
- }
3830
- };
3831
- }
3832
- function dbAddNewReportToCurrentUser() {
3833
- return async (params) => {
3834
- try {
3835
- return "NOT USED SERVICE";
3836
- } catch (err) {
3837
- console.error("AuthZeroError", err);
3838
- throw new BackendError(err.response?.status || 500, err.message);
3839
- }
3840
- };
3841
- }
3842
3305
 
3843
3306
  // api/db/index.ts
3844
3307
  function useDatabaseApi(_, __, api) {
@@ -3988,7 +3451,6 @@ function parseSearchReportParams(query) {
3988
3451
  }
3989
3452
 
3990
3453
  // api/endpoints/urlProxy.ts
3991
- getLogging_default();
3992
3454
  function endpointUrlProxyFactory(operations) {
3993
3455
  const { urlProxy: urlProxy3 } = operations;
3994
3456
  return endpoint("GET", "url/proxy", (req) => {
@@ -4196,7 +3658,7 @@ function endpointAddNewReportToCurrentUserFactory(operations) {
4196
3658
  }
4197
3659
 
4198
3660
  // api/endpoints/index.ts
4199
- var verbose5 = getLogging_default();
3661
+ var verbose3 = getLogging_default();
4200
3662
  function endpointKey(method, path) {
4201
3663
  return `${method.toUpperCase()} ${path.toLowerCase()}`;
4202
3664
  }
@@ -4273,7 +3735,7 @@ async function endpointNextJsHandlerFactory(req, res) {
4273
3735
  }
4274
3736
  }
4275
3737
  }).catch((err) => {
4276
- if (verbose5)
3738
+ if (verbose3)
4277
3739
  console.trace("Backend Error", err);
4278
3740
  res.status(err.code).json({ error: err.message });
4279
3741
  });
@@ -4466,7 +3928,7 @@ function parseBlockContext(context) {
4466
3928
  locale: context.locale || localeDefault6
4467
3929
  };
4468
3930
  }
4469
- var verbose6 = yn3(process.env.REPORTS_LOGGING);
3931
+ var verbose4 = yn3(process.env.REPORTS_LOGGING);
4470
3932
  var mortarEval_default = mortarEval;
4471
3933
  function mortarEval(varInnerName, varOuterValue, logic, formatterFunctions, attributes = void 0, blockContext) {
4472
3934
  const vars = {};
@@ -4479,7 +3941,7 @@ function mortarEval(varInnerName, varOuterValue, logic, formatterFunctions, attr
4479
3941
  if (isInSet(prop, "log", "debug", "info", "warn", "error")) {
4480
3942
  return (...args) => {
4481
3943
  log.push(`${prop} ${Date.now()} - ${args.join(" ")}`);
4482
- if (verbose6)
3944
+ if (verbose4)
4483
3945
  target[prop](...args);
4484
3946
  };
4485
3947
  }
@@ -4679,7 +4141,6 @@ var getRootBlocksForSection_default = getRootBlocksForSection;
4679
4141
 
4680
4142
  // libs/consts.ts
4681
4143
  var apiSeparator = "|||";
4682
- var verbose7 = getLogging_default();
4683
4144
  var debug = yn3(process.env.NEXT_PUBLIC_REPORTS_DEBUG);
4684
4145
  var ORIGIN = process.env.REPORTS_ORIGIN || "";
4685
4146
  axios5.interceptors.request.use((config) => ({
@@ -4700,12 +4161,15 @@ var { urlProxy } = apiFactory2("/api/cms");
4700
4161
  function isResult(response) {
4701
4162
  return response.ok !== void 0;
4702
4163
  }
4164
+ function ApiFetchException(message) {
4165
+ this.message = message;
4166
+ this.name = "ApiFetchException";
4167
+ }
4168
+ var apiFetchTimeout = process.env.NEXT_PUBLIC_REPORTS_API_FETCH_TIMEOUT || 0;
4703
4169
  async function apiFetch(url, settings, readMemberFn, locale = "en") {
4704
4170
  const start = Date.now();
4705
4171
  let finalUrl = url;
4706
4172
  let finalData;
4707
- if (verbose7)
4708
- console.log("apiFetch", url);
4709
4173
  const parsedUrl = new URL(url);
4710
4174
  const parsedQuery = parsedUrl.searchParams;
4711
4175
  if (parsedQuery.has("bespoke_slugs")) {
@@ -4714,11 +4178,26 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
4714
4178
  }
4715
4179
  const useProxy = settings && settings.useProxy && settings.useProxy === "true" ? true : false;
4716
4180
  if (typeof window === "object" && useProxy) {
4717
- const result = await urlProxy({ url: finalUrl });
4718
- finalData = result.ok === true ? result.data : result.error;
4181
+ const result = await urlProxy({ url: finalUrl, timeout: apiFetchTimeout });
4182
+ if (result.ok === true) {
4183
+ finalData = result.data;
4184
+ } else {
4185
+ throw new ApiFetchException(result?.error);
4186
+ }
4719
4187
  } else {
4720
- const response = await axios5({ url: finalUrl });
4721
- finalData = response.data;
4188
+ try {
4189
+ const response = await axios5({
4190
+ url: finalUrl,
4191
+ timeout: apiFetchTimeout
4192
+ });
4193
+ if (response.status === 200) {
4194
+ finalData = response.data;
4195
+ } else {
4196
+ throw new ApiFetchException("No response");
4197
+ }
4198
+ } catch (e) {
4199
+ throw new ApiFetchException(e.message);
4200
+ }
4722
4201
  }
4723
4202
  if (parsedQuery.has("bespoke_slugs")) {
4724
4203
  const slugStrings = parsedQuery.get("bespoke_slugs");
@@ -4772,6 +4251,10 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
4772
4251
  requestDuration: Date.now() - start
4773
4252
  };
4774
4253
  }
4254
+ function BlockException(message) {
4255
+ this.name = "BlockException";
4256
+ this.message = message;
4257
+ }
4775
4258
  async function runSingleBlock(block, formatterFunctions, blockContext, readMemberFn) {
4776
4259
  const { locale, variables, query } = parseBlockContext(blockContext);
4777
4260
  const allowed = isBlockAllowed(block, blockContext, variables, formatterFunctions);
@@ -4803,18 +4286,30 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4803
4286
  block.settings,
4804
4287
  readMemberFn,
4805
4288
  locale
4806
- ).then((result) => {
4807
- if (verbose7)
4808
- console.log("Variable loaded:", apiUrl, "response time: ", result.requestDuration);
4809
- return result;
4810
- }, (e) => {
4811
- if (verbose7)
4812
- console.error(`Error fetching ${apiUrl} block ${block.id}: ${e.message}`);
4813
- return { data: {}, requestDuration: 0 };
4814
- })
4289
+ ).catch(
4290
+ (e) => {
4291
+ throw new BlockException(` Excecution of ${block.type}-${block.id} in section ${block.section_id} in report ${variables.report_name} (${variables.report_id}) failed.
4292
+ Error fetching ${apiUrl}.
4293
+ Message: ${e.message}.
4294
+ `);
4295
+ }
4296
+ )
4815
4297
  );
4816
4298
  }
4817
- const apiResponses = await Promise.all(apiPromisesList);
4299
+ const apiResponses = await Promise.all(apiPromisesList).catch((e) => {
4300
+ return e.message;
4301
+ });
4302
+ if (typeof apiResponses === "string") {
4303
+ if (apiResponses) {
4304
+ return {
4305
+ outputVariables: {},
4306
+ renderVariables: {},
4307
+ status: {
4308
+ error: apiResponses
4309
+ }
4310
+ };
4311
+ }
4312
+ }
4818
4313
  resp = apiResponses.length === 1 ? apiResponses[0].data : apiResponses.map((r) => r.data);
4819
4314
  responseSize = apiResponses.reduce((acc, r) => acc + JSON.stringify(r.data).length / 1024, 0);
4820
4315
  duration = Math.max(...apiResponses.map((ar) => ar.requestDuration));
@@ -5062,6 +4557,16 @@ var runConsumersV2 = async (blocks, sections, bid, formatterFunctions, blockCont
5062
4557
  },
5063
4558
  readMemberFn
5064
4559
  ).then(({ outputVariables, status }) => {
4560
+ if (status.error) {
4561
+ if (typeof window === "undefined") {
4562
+ throw new Error(`Run consumers failed with message: ${status.error}`);
4563
+ } else {
4564
+ statusById[bid2] = {
4565
+ allowed: false,
4566
+ error: status.error
4567
+ };
4568
+ }
4569
+ }
5065
4570
  if (
5066
4571
  // store output variables for block that:
5067
4572
  (block.consumers.length > 0 || [BLOCK_TYPES.GENERATOR, BLOCK_TYPES.RELATED].includes(block.type)) && status.allowed && Object.keys(outputVariables).length > 0
@@ -5855,17 +5360,21 @@ function recalculateVariables(resource, params) {
5855
5360
  };
5856
5361
  const section = state.records.entities.section[sid];
5857
5362
  const readMemberFn = async (innerParams) => dispatch(readMember(innerParams));
5858
- const data = await runConsumersV2(
5859
- blocks,
5860
- section && [section],
5861
- bid,
5862
- formatterFunctions,
5863
- blockContext,
5864
- void 0,
5865
- readMemberFn
5866
- );
5867
- const { variables, status } = data;
5868
- dispatch(variablesActions.setVariableChange({ attributes, variables, status }));
5363
+ try {
5364
+ const data = await runConsumersV2(
5365
+ blocks,
5366
+ section && [section],
5367
+ bid,
5368
+ formatterFunctions,
5369
+ blockContext,
5370
+ void 0,
5371
+ readMemberFn
5372
+ );
5373
+ const { variables, status } = data;
5374
+ dispatch(variablesActions.setVariableChange({ attributes, variables, status }));
5375
+ } catch (error) {
5376
+ console.log("Error in runConsumers client side: recalculateVariables", error);
5377
+ }
5869
5378
  };
5870
5379
  }
5871
5380
  function readMetadata(filters) {
@@ -6194,4 +5703,4 @@ function getSlugSegments(slugs = []) {
6194
5703
  };
6195
5704
  }
6196
5705
 
6197
- export { getSession_default as BespokeGetSession, handleAuth_default as BespokeHandleAuth, BespokeManagerServerSideProps, BespokeRendererStaticPaths, BespokeRendererStaticProps, withApiRoleAuthRequired_default as BespokeWithApiRoleAuthRequired, crosswalk_default as ReportCrosswalkHandler, apiFactory as dbApiFactory, endpointKey, endpointNextJsHandlerFactory, getDB, useDatabaseApi };
5706
+ export { getSession_default as BespokeGetSession, handleAuth_default as BespokeHandleAuth, BespokeManagerServerSideProps, BespokeRendererStaticPaths, BespokeRendererStaticProps, searchUsersByMetadata_default as BespokeSearchUsersByMetadata, updateCurrentUserMetadata_default as BespokeUpdateCurrentUserMetadata, withApiRoleAuthRequired_default as BespokeWithApiRoleAuthRequired, crosswalk_default as ReportCrosswalkHandler, apiFactory as dbApiFactory, endpointKey, endpointNextJsHandlerFactory, getDB, useDatabaseApi };