proca 0.8.1 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1079 -28
- package/package.json +94 -101
- package/proca-cli +8 -0
- package/src/commands/action/count.mjs +61 -0
- package/src/commands/action/list.mjs +204 -0
- package/src/commands/action/replay.mjs +54 -0
- package/src/commands/campaign/add.mjs +101 -0
- package/src/commands/campaign/delete.mjs +61 -0
- package/src/commands/campaign/get.mjs +107 -0
- package/src/commands/campaign/list.mjs +156 -0
- package/src/commands/campaign/queries.graphql +19 -0
- package/src/commands/config/add.mjs +101 -0
- package/src/commands/config/get.mjs +33 -0
- package/src/commands/config/set.mjs +103 -0
- package/src/commands/config/token.mjs +34 -0
- package/src/commands/config/user.mjs +76 -0
- package/src/commands/org/add.mjs +66 -0
- package/src/commands/org/crm.mjs +88 -0
- package/src/commands/org/delete.mjs +48 -0
- package/src/commands/org/get.mjs +97 -0
- package/src/commands/org/join.mjs +77 -0
- package/src/commands/supporter/count.mjs +96 -0
- package/src/commands/user/get.mjs +91 -0
- package/src/commands/user/leave.mjs +52 -0
- package/src/commands/user/list.mjs +71 -0
- package/src/commands/widget/add.mjs +101 -0
- package/src/commands/widget/cache.mjs +29 -0
- package/src/commands/widget/get.mjs +59 -0
- package/src/commands/widget/list.mjs +116 -0
- package/src/config.mjs +36 -0
- package/src/generated/schema.json +10677 -0
- package/src/hooks/help.mjs +14 -0
- package/src/hooks/init.mjs +20 -0
- package/src/index.mjs +1 -0
- package/src/procaCommand.mjs +242 -0
- package/src/queries/campaign.mjs +21 -0
- package/src/queries/widget.mjs +12 -0
- package/src/urql.mjs +60 -0
- package/src/util/twitter.mjs +23 -0
- package/theme.json +29 -0
- package/LICENSE +0 -661
- package/dist/App.css +0 -22
- package/dist/App.js +0 -25
- package/dist/App.test.js +0 -8
- package/dist/Wizard.js +0 -32
- package/dist/components/Alert.js +0 -49
- package/dist/components/Clickify.js +0 -26
- package/dist/components/Consent.js +0 -120
- package/dist/components/Country.js +0 -94
- package/dist/components/Dialog.js +0 -84
- package/dist/components/Disabled.js +0 -3
- package/dist/components/Emoji.js +0 -12
- package/dist/components/FAB.js +0 -70
- package/dist/components/Html.js +0 -56
- package/dist/components/Loader.js +0 -14
- package/dist/components/Openletter.js +0 -443
- package/dist/components/Organisation.js +0 -181
- package/dist/components/Petition.js +0 -16
- package/dist/components/ProcaRoot.js +0 -23
- package/dist/components/ProcaStyle.js +0 -94
- package/dist/components/ProgressCounter.js +0 -64
- package/dist/components/Register.js +0 -345
- package/dist/components/Share.js +0 -177
- package/dist/components/TextField.js +0 -58
- package/dist/components/Twitter.js +0 -127
- package/dist/components/TwitterAction.js +0 -111
- package/dist/components/TwitterList.js +0 -25
- package/dist/components/TwitterText.js +0 -42
- package/dist/components/Widget.js +0 -266
- package/dist/components/bespoke/Download.js +0 -49
- package/dist/components/bespoke/Register-CH.js +0 -505
- package/dist/data/countries.json +0 -114
- package/dist/hooks/useConfig.js +0 -217
- package/dist/hooks/useCount.js +0 -95
- package/dist/hooks/useData.js +0 -59
- package/dist/hooks/useElementWidth.js +0 -46
- package/dist/hooks/useGeoLocation.js +0 -45
- package/dist/hooks/useLayout.js +0 -63
- package/dist/images/Twitter.js +0 -16
- package/dist/images/Twitter.svg +0 -1
- package/dist/index.css +0 -13
- package/dist/index.js +0 -114
- package/dist/lib/domparser.js +0 -12
- package/dist/lib/i18n.js +0 -40
- package/dist/lib/init.js +0 -7
- package/dist/lib/server.js +0 -331
- package/dist/lib/urlparser.js +0 -65
- package/dist/lib/uuid.js +0 -30
- package/dist/locales/README.md +0 -37
- package/dist/locales/common.json +0 -189
- package/dist/locales/de/common.json +0 -46
- package/dist/locales/de/index.js +0 -0
- package/dist/locales/en/common.json +0 -45
- package/dist/locales/fr/common.json +0 -51
- package/dist/locales/index.js +0 -27
- package/dist/locales/it/common.json +0 -46
- package/dist/logo.svg +0 -1
- package/dist/module.js +0 -14
- package/dist/slingshot.xcf +0 -0
- package/dist/tmp.config/0.json +0 -21
- package/dist/tmp.config/2.json +0 -11
- package/dist/tmp.config/20.json +0 -29
- package/dist/tmp.config/28.json +0 -14
- package/dist/tmp.config/32.json +0 -34
- package/dist/tmp.config/35.json +0 -14
- package/dist/tmp.config/null.json +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Config, Help } from "@oclif/core";
|
|
2
|
+
|
|
3
|
+
export default class CustomHelp extends Help {
|
|
4
|
+
showCommandHelp(command) {
|
|
5
|
+
// console.log("This will be displayed in single-command CLIs");
|
|
6
|
+
super.showCommandHelp(command);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async showHelp(argv) {
|
|
10
|
+
// console.log('This will be displayed in multi-command CLIs')
|
|
11
|
+
// Always include --all flag to show all commands
|
|
12
|
+
await super.showHelp([...argv, "--all"]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import oclif from "@oclif/core";
|
|
2
|
+
import { config as dotenv } from "dotenv";
|
|
3
|
+
import { getFilename, load } from "#src/config.mjs";
|
|
4
|
+
import CustomHelp from "./help.mjs";
|
|
5
|
+
|
|
6
|
+
const hook = async (opts) => {
|
|
7
|
+
const config = load(opts.config.configDir);
|
|
8
|
+
if (config) {
|
|
9
|
+
opts.config.procaConfig = config;
|
|
10
|
+
} else {
|
|
11
|
+
const file = getFilename(opts.config.configDir);
|
|
12
|
+
this.warn("missing config", file);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//console.log(opts);
|
|
16
|
+
//console.log(opts.config.helpClass, CustomHelp);
|
|
17
|
+
// opts.config.helpClass = CustomHelp;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default hook;
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run, Args, Flags } from "@oclif/core";
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { Args, Command, Flags, ux } from "@oclif/core";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
import { parse as dxid, id } from "dxid";
|
|
4
|
+
import Table from "easy-table";
|
|
5
|
+
import fastcsv from "fast-csv";
|
|
6
|
+
import initHook from "#src/hooks/init.mjs";
|
|
7
|
+
import { createClient } from "#src/urql.mjs";
|
|
8
|
+
|
|
9
|
+
class ProcaCommand extends Command {
|
|
10
|
+
static enableJsonFlag = true;
|
|
11
|
+
procaConfig = { url: "https://api.proca.app/api" };
|
|
12
|
+
format = "human"; // the default formatting
|
|
13
|
+
flags = {};
|
|
14
|
+
|
|
15
|
+
static baseFlags = {
|
|
16
|
+
human: Flags.boolean({
|
|
17
|
+
helpGroup: "OUTPUT", // Optional, groups it under a specific help section if desired
|
|
18
|
+
description: "Format output to be read on screen by a human [default]",
|
|
19
|
+
default: true,
|
|
20
|
+
}),
|
|
21
|
+
json: Flags.boolean({
|
|
22
|
+
helpGroup: "OUTPUT", // Optional, groups it under a specific help section if desired
|
|
23
|
+
description: "Format output as json",
|
|
24
|
+
exclusive: ["human", "csv"],
|
|
25
|
+
}),
|
|
26
|
+
csv: Flags.boolean({
|
|
27
|
+
description: "Format output as csv",
|
|
28
|
+
helpGroup: "OUTPUT", // Optional, groups it under a specific help section if desired
|
|
29
|
+
}),
|
|
30
|
+
simplify: Flags.boolean({
|
|
31
|
+
helpGroup: "OUTPUT",
|
|
32
|
+
description:
|
|
33
|
+
"flatten and filter to output only the most important attributes, mostly relevant for json",
|
|
34
|
+
allowNo: true,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static multiid() {
|
|
39
|
+
const args = {
|
|
40
|
+
id_name_dxid: Args.string({
|
|
41
|
+
ignoreStdin: true,
|
|
42
|
+
hidden: true,
|
|
43
|
+
description:
|
|
44
|
+
"it's better to use -i <id> or -x <dxid> or -n <name>, but you can for convenience",
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
return args;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static flagify(params = {}) {
|
|
51
|
+
const flags = Object.assign({}, ProcaCommand.baseFlags);
|
|
52
|
+
if (params.multiid) {
|
|
53
|
+
flags.id = Flags.string({
|
|
54
|
+
char: "i",
|
|
55
|
+
parse: (input) => Number.parseInt(input, 10),
|
|
56
|
+
exclusive: ["name", "dxid"],
|
|
57
|
+
});
|
|
58
|
+
flags.dxid = Flags.string({
|
|
59
|
+
char: "x",
|
|
60
|
+
description: "dxid",
|
|
61
|
+
});
|
|
62
|
+
flags.name = Flags.string({
|
|
63
|
+
char: "n",
|
|
64
|
+
charAliases: ["o"],
|
|
65
|
+
description: "name",
|
|
66
|
+
helpValue: "<the_short_name>",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return flags;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async parse() {
|
|
73
|
+
const parsed = await super.parse();
|
|
74
|
+
const maybe = parsed.args.id_name_dxid;
|
|
75
|
+
if (maybe) {
|
|
76
|
+
const d = dxid(maybe, false);
|
|
77
|
+
if (d) parsed.flags.id = d;
|
|
78
|
+
else parsed.flags.name = maybe;
|
|
79
|
+
}
|
|
80
|
+
if (parsed.flags.dxid) {
|
|
81
|
+
parsed.flags.id = dxid(parsed.flags.dxid);
|
|
82
|
+
}
|
|
83
|
+
return parsed;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static hooks = {
|
|
87
|
+
init: async (options) => {
|
|
88
|
+
console.log("init hook called", options);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
async init() {
|
|
93
|
+
await super.init();
|
|
94
|
+
const { argv, flags } = await this.parse();
|
|
95
|
+
this.flags = flags;
|
|
96
|
+
if (flags.json) this.format = "json";
|
|
97
|
+
if (flags.csv) this.format = "csv";
|
|
98
|
+
|
|
99
|
+
this.debug = debug("proca");
|
|
100
|
+
initHook({ config: this.config });
|
|
101
|
+
this.procaConfig = this.config.procaConfig; // set up from the hooks/init
|
|
102
|
+
// await this.config.runHook('init', { config: this.config });
|
|
103
|
+
createClient(this.procaConfig);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async catch(err) {
|
|
107
|
+
// Check if the error was caused by a missing flag or wrong argument format
|
|
108
|
+
try {
|
|
109
|
+
this.error(err.toString());
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.log(e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
flatten = (obj, prefix = "", result = {}) => {
|
|
116
|
+
Object.entries(obj).forEach(([k, v]) => {
|
|
117
|
+
const newKey = Object.hasOwn(result, k) && prefix ? `${prefix}-${k}` : k;
|
|
118
|
+
|
|
119
|
+
if (v?.constructor === Object) {
|
|
120
|
+
this.flatten(v, newKey, result);
|
|
121
|
+
} else {
|
|
122
|
+
result[newKey] = v;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
simplify = (d) => {
|
|
129
|
+
const r = {};
|
|
130
|
+
for (const [key, value] of Object.entries(d)) {
|
|
131
|
+
if (key === "__typename") continue;
|
|
132
|
+
if (key === "config" && typeof value === "string") continue; // it's just a giant mess if not processed, let's skipt
|
|
133
|
+
if (value === null) continue;
|
|
134
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
135
|
+
r[key] = value;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof value === "object") {
|
|
140
|
+
if (value?.name) r[key] = value.name;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
r[key] = value;
|
|
144
|
+
}
|
|
145
|
+
return r;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
tlog(color, ...msg) {
|
|
149
|
+
const coloredMsg = msg.map((d) => ux.colorize(this.config.theme[color], d));
|
|
150
|
+
this.log(...coloredMsg);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
info(...msg) {
|
|
154
|
+
this.tlog("info", msg);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
prettyJson(obj) {
|
|
158
|
+
if (typeof obj === "string") {
|
|
159
|
+
obj = JSON.parse(obj);
|
|
160
|
+
}
|
|
161
|
+
this.log(ux.colorizeJson(obj, { theme: this.config.theme.json }));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
warn(...msg) {
|
|
165
|
+
this.tlog("warn", ...msg);
|
|
166
|
+
}
|
|
167
|
+
error(msg, options = {}) {
|
|
168
|
+
const colouredMessage = ux.colorize(this.config.theme.error, msg);
|
|
169
|
+
super.error(colouredMessage, options);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async csv(data) {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
let d = null;
|
|
175
|
+
const format = this.flags.simplify
|
|
176
|
+
? this.simplify
|
|
177
|
+
: (d) => this.flatten(d, "");
|
|
178
|
+
if (Array.isArray(data)) {
|
|
179
|
+
d = data.map(format);
|
|
180
|
+
} else {
|
|
181
|
+
d = [format(data)];
|
|
182
|
+
}
|
|
183
|
+
const stream = fastcsv
|
|
184
|
+
.write(d, { headers: true })
|
|
185
|
+
.on("finish", () => {
|
|
186
|
+
console.log();
|
|
187
|
+
resolve();
|
|
188
|
+
})
|
|
189
|
+
.on("error", reject);
|
|
190
|
+
|
|
191
|
+
stream.pipe(process.stdout);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
table(data, transformRow, print = (table) => table.toString()) {
|
|
196
|
+
if (!transformRow) {
|
|
197
|
+
if (this.flags.simplify !== false) {
|
|
198
|
+
transformRow = (d, cell) => {
|
|
199
|
+
for (const [key, value] of Object.entries(this.simplify(d))) {
|
|
200
|
+
cell(key, value);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
} else {
|
|
204
|
+
transformRow = (d, cell) => {
|
|
205
|
+
for (const [key, value] of Object.entries(this.flatten(d))) {
|
|
206
|
+
cell(key, value);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const theme = this.config.theme;
|
|
212
|
+
Table.prototype.pushDelimeter = function (cols) {
|
|
213
|
+
// hack to change the formatting of the header
|
|
214
|
+
cols = cols || this.columns();
|
|
215
|
+
cols.forEach(function (col) {
|
|
216
|
+
this.cell(
|
|
217
|
+
col,
|
|
218
|
+
undefined,
|
|
219
|
+
Table.leftPadder(ux.colorize(theme.flagSeparator, "-")),
|
|
220
|
+
);
|
|
221
|
+
}, this);
|
|
222
|
+
return this.newRow();
|
|
223
|
+
};
|
|
224
|
+
this.log(Table.print(data, transformRow, print));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async output(data) {
|
|
228
|
+
if (this.format === "json") {
|
|
229
|
+
if (this.flags.simplify)
|
|
230
|
+
return data?.map(this.simplify) || this.simplify(data);
|
|
231
|
+
const isDirectCall = process.argv.join(":").includes(this.id);
|
|
232
|
+
return data;
|
|
233
|
+
}
|
|
234
|
+
if (this.format === "csv") {
|
|
235
|
+
return this.csv(data);
|
|
236
|
+
}
|
|
237
|
+
return this.table(data);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { ProcaCommand as Command, Args, Flags };
|
|
242
|
+
export default ProcaCommand;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { gql } from "#src/urql.mjs";
|
|
2
|
+
|
|
3
|
+
export const FragmentSummary = gql`fragment Summary on Campaign {id name title externalId status}`;
|
|
4
|
+
|
|
5
|
+
export const FragmentOrg = gql`
|
|
6
|
+
fragment Org on Campaign {
|
|
7
|
+
org {
|
|
8
|
+
name
|
|
9
|
+
title
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
export const FragmentStats = gql`
|
|
15
|
+
fragment Stats on Campaign {
|
|
16
|
+
stats {
|
|
17
|
+
supporterCount
|
|
18
|
+
actionCount {actionType count}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
package/src/urql.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { authExchange } from "@urql/exchange-auth";
|
|
2
|
+
import {
|
|
3
|
+
createClient as _createClient,
|
|
4
|
+
cacheExchange,
|
|
5
|
+
fetchExchange,
|
|
6
|
+
gql,
|
|
7
|
+
} from "urql";
|
|
8
|
+
|
|
9
|
+
import { GraphQLError, formatError } from "graphql";
|
|
10
|
+
|
|
11
|
+
export let client = {
|
|
12
|
+
query: () => {
|
|
13
|
+
throw new Error("urql graphql not initialised, call init first");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Create a URQL client with your GraphQL API endpoint
|
|
18
|
+
export const createClient = (config) => {
|
|
19
|
+
if (!config) {
|
|
20
|
+
console.error("config missing on createClient");
|
|
21
|
+
}
|
|
22
|
+
client = _createClient({
|
|
23
|
+
url: config?.url || "https://api.proca.app/api",
|
|
24
|
+
exchanges: [
|
|
25
|
+
// cacheExchange, // Handles caching
|
|
26
|
+
authExchange(async (utils) => {
|
|
27
|
+
const token = config.token;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
addAuthToOperation(operation) {
|
|
31
|
+
if (!token) return operation;
|
|
32
|
+
return utils.appendHeaders(operation, {
|
|
33
|
+
Authorization: `Bearer ${token}`,
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
38
|
+
fetchExchange, // Handles fetching
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const query = async (query, payload) => {
|
|
44
|
+
const result = await client.query(query, payload).toPromise();
|
|
45
|
+
if (result.error) {
|
|
46
|
+
//console.log(result.error);
|
|
47
|
+
throw result.error;
|
|
48
|
+
}
|
|
49
|
+
return result.data;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const mutation = async (mutation, payload) => {
|
|
53
|
+
const result = await client.mutation(mutation, payload).toPromise();
|
|
54
|
+
if (result.error) {
|
|
55
|
+
throw result.error;
|
|
56
|
+
}
|
|
57
|
+
return result.data;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { gql };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const getTwitter = async (org) => {
|
|
2
|
+
const orgName = org.config.twitter?.screen_name || org.name;
|
|
3
|
+
try {
|
|
4
|
+
const res = await fetch(
|
|
5
|
+
`https://twitter.proca.app/?screen_name=${orgName}`,
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
if (res.status >= 400) {
|
|
9
|
+
throw new Error("Bad response from twitter.proca.app");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const twitter = await res.json();
|
|
13
|
+
twitter.picture = twitter.profile_image_url_https;
|
|
14
|
+
twitter.profile_image_url_https = undefined;
|
|
15
|
+
if (twitter) org.config.twitter = twitter;
|
|
16
|
+
if (!org.config.description) org.config.description = twitter.description;
|
|
17
|
+
if (!org.config.location) org.config.location = twitter.location;
|
|
18
|
+
if (!org.config.url) org.config.url = twitter.url;
|
|
19
|
+
if (!org.title) org.title = twitter.name;
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(err);
|
|
22
|
+
}
|
|
23
|
+
};
|
package/theme.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"info": "cyan",
|
|
3
|
+
"warn": "#FFAB40",
|
|
4
|
+
"error": "red",
|
|
5
|
+
"bin": "block",
|
|
6
|
+
"command": "cyan",
|
|
7
|
+
"commandSummary": "black",
|
|
8
|
+
"dollarSign": "white",
|
|
9
|
+
"flag": "blackBright",
|
|
10
|
+
"flagDefaultValue": "blue",
|
|
11
|
+
"flagOptions": "white",
|
|
12
|
+
"flagRequired": "red",
|
|
13
|
+
"flagSeparator": "white",
|
|
14
|
+
"json": {
|
|
15
|
+
"brace": "magenta",
|
|
16
|
+
"bracket": "magenta",
|
|
17
|
+
"colon": "dim",
|
|
18
|
+
"comma": "dim",
|
|
19
|
+
"key": "yellow",
|
|
20
|
+
"string": "green",
|
|
21
|
+
"number": "green",
|
|
22
|
+
"boolean": "green",
|
|
23
|
+
"null": "red"
|
|
24
|
+
},
|
|
25
|
+
"sectionDescription": "blackBright",
|
|
26
|
+
"sectionHeader": "underline",
|
|
27
|
+
"topic": "cyan",
|
|
28
|
+
"version": "white"
|
|
29
|
+
}
|