mediawiki-projects-list 1.0.0 → 1.0.1

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.
@@ -0,0 +1,26 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - 'master'
7
+ paths:
8
+ - 'package.json'
9
+
10
+ jobs:
11
+ publish-npm:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - uses: actions/setup-node@v3
16
+ with:
17
+ registry-url: 'https://registry.npmjs.org'
18
+ - run: npm publish
19
+ env:
20
+ NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
21
+ - uses: actions/setup-node@v3
22
+ with:
23
+ registry-url: 'https://npm.pkg.github.com'
24
+ - run: npm publish
25
+ env:
26
+ NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
package/README.md CHANGED
@@ -1,28 +1,28 @@
1
- # List of MediaWiki projects
2
-
3
- ```json
4
- {
5
- "name": "wikipedia.org",
6
- "regex": "((?:([a-z\\d-]{1,50})\\.)?(?:m\\.)?wikipedia\\.org)",
7
- "articlePath": "/wiki/",
8
- "scriptPath": "/w/",
9
- "wikiFarm": "wikimedia",
10
- "extensions": [
11
- "CentralAuth"
12
- ]
13
- }
14
- ```
15
-
16
- ## How to check if a URL matches a wiki project
17
-
18
- * `name` - The hostname of the URL should match the `name`.
19
- * `regex` - Match the URL against the regex
20
- * `$1` - Hostname and path between `https://` and `articlePath` or `scriptPath`
21
- * `$n` - If the hostname contains multiple wikis, additional groups are used to uniquely identify them
22
- * `articlePath` - Article path of the project is `https://` + `$1` + `articlePath`
23
- * `scriptPath` - Script path of the project is `https://` + `$1` + `scriptPath`
24
- * `regexPaths` - If true, `articlePath` and `scriptPath` include group matches of `regex`
25
- * `wikiFarm` - Name of the wiki farm the project belongs to (`wikimedia`, `fandom`, `miraheze`, `wiki.gg`)
26
- * `extensions` - List of extensions providing useful API endpoints (`CentralAuth`, `Cargo`)
27
- * `urlSpaceReplacement` - Replacement for spaces in page names (default: `_`; wikihow.com: `-`)
1
+ # List of MediaWiki projects
2
+
3
+ ```json
4
+ {
5
+ "name": "wikipedia.org",
6
+ "regex": "((?:([a-z\\d-]{1,50})\\.)?(?:m\\.)?wikipedia\\.org)",
7
+ "articlePath": "/wiki/",
8
+ "scriptPath": "/w/",
9
+ "wikiFarm": "wikimedia",
10
+ "extensions": [
11
+ "CentralAuth"
12
+ ]
13
+ }
14
+ ```
15
+
16
+ ## How to check if a URL matches a wiki project
17
+
18
+ * `name` - The hostname of the URL should match the `name`.
19
+ * `regex` - Match the URL against the regex
20
+ * `$1` - Hostname and path between `https://` and `articlePath` or `scriptPath`
21
+ * `$n` - If the hostname contains multiple wikis, additional groups are used to uniquely identify them
22
+ * `articlePath` - Article path of the project is `https://` + `$1` + `articlePath`
23
+ * `scriptPath` - Script path of the project is `https://` + `$1` + `scriptPath`
24
+ * `regexPaths` - If true, `articlePath` and `scriptPath` include group matches of `regex`
25
+ * `wikiFarm` - Name of the wiki farm the project belongs to (`wikimedia`, `fandom`, `miraheze`, `wiki.gg`)
26
+ * `extensions` - List of extensions providing useful API endpoints (`CentralAuth`, `Cargo`)
27
+ * `urlSpaceReplacement` - Replacement for spaces in page names (default: `_`; wikihow.com: `-`)
28
28
  * `note` - Usage notes for the API of a specific project
package/index.js CHANGED
@@ -1,130 +1,130 @@
1
- const {properties: {wikiProjects: {items: {properties: wikiProjectSchema}}}} = require('./projects-schema.json');
2
-
3
- /**
4
- * A MediaWiki project
5
- * @typedef {object} WikiProject
6
- * @property {string} name - Hostname of the project
7
- * @property {string} regex - Regex to match the project url
8
- * @property {string} articlePath - Article path of the project
9
- * @property {string} scriptPath - Script path of the project
10
- * @property {object} [idString] - Only exists when the hostname contains multiple wikis: How to handle the id string
11
- * @property {string} idString.separator - Separator to join or split the id string on
12
- * @property {"asc"|"desc"} idString.direction - Order in which the project regex additional group matches should be chained to gain the id string
13
- * @property {string} idString.regex - Regex to match the id string
14
- * @property {string[]} idString.scriptPaths - How to turn the group matches of the id string regex into an URL to the script path, index based on group matches
15
- * @property {boolean} regexPaths - Whether the paths include matches of the regex
16
- * @property {?("wikimedia"|"fandom"|"miraheze"|"wiki.gg")} wikiFarm - Wiki farm of the project
17
- * @property {("CentralAuth"|"Cargo")[]} extensions - List of extensions providing useful API endpoints
18
- * @property {string} urlSpaceReplacement - Replacement for spaces in the article URL
19
- * @property {?string} note - Note about the specific project
20
- */
21
-
22
- /**
23
- * @type {{
24
- * inputToWikiProject: Map<string, ?{fullArticlePath: string, fullScriptPath: string, wikiProject: WikiProject}>,
25
- * urlToIdString: Map<string, ?string>,
26
- * idStringToUrl: Map<string, ?string>
27
- * }}
28
- */
29
- const functionCache = {
30
- inputToWikiProject: new Map(),
31
- urlToIdString: new Map(),
32
- idStringToUrl: new Map()
33
- };
34
-
35
- /**
36
- * List of MediaWiki projects
37
- * @type {WikiProject[]}
38
- */
39
- const wikiProjects = require('./projects.json').wikiProjects.map( wikiProject => {
40
- if ( wikiProject.idString ) {
41
- wikiProject.idString.separator ??= wikiProjectSchema.idString.properties.separator.default;
42
- wikiProject.idString.direction ??= wikiProjectSchema.idString.properties.direction.default;
43
- }
44
- wikiProject.regexPaths ??= wikiProjectSchema.regexPaths.default;
45
- wikiProject.wikiFarm ??= wikiProjectSchema.wikiFarm.default;
46
- wikiProject.extensions ??= wikiProjectSchema.extensions.default.slice();
47
- wikiProject.urlSpaceReplacement ??= wikiProjectSchema.urlSpaceReplacement.default;
48
- wikiProject.note ??= wikiProjectSchema.note.default;
49
- return wikiProject;
50
- } );
51
-
52
- /**
53
- *
54
- * @param {string} input
55
- * @returns {?{fullArticlePath: string, fullScriptPath: string, wikiProject: WikiProject}}
56
- */
57
- function inputToWikiProject(input) {
58
- if ( functionCache.inputToWikiProject.has(input) ) return structuredClone(functionCache.inputToWikiProject.get(input));
59
- let result = null;
60
- let wikiProject = wikiProjects.find( wikiProject => input.split('/').slice(0, 3).some( part => part.endsWith( wikiProject.name ) ) );
61
- if ( wikiProject ) {
62
- let articlePath = ( wikiProject.regexPaths ? '/' : wikiProject.articlePath );
63
- let scriptPath = ( wikiProject.regexPaths ? '/' : wikiProject.scriptPath );
64
- let regex = input.match( new RegExp( wikiProject.regex + `(?:${articlePath}|${scriptPath}|/?$)` ) );
65
- if ( regex ) {
66
- if ( wikiProject.regexPaths ) {
67
- scriptPath = wikiProject.scriptPath.replace( /\$(\d)/g, (match, n) => regex[n] );
68
- articlePath = wikiProject.articlePath.replace( /\$(\d)/g, (match, n) => regex[n] );
69
- }
70
- result = {
71
- fullArticlePath: 'https://' + regex[1] + articlePath,
72
- fullScriptPath: 'https://' + regex[1] + scriptPath,
73
- wikiProject: wikiProject
74
- };
75
- }
76
- }
77
- functionCache.inputToWikiProject.set(input, result);
78
- return structuredClone(result);
79
- }
80
-
81
- /**
82
- *
83
- * @param {URL} url
84
- * @returns {?string}
85
- */
86
- function urlToIdString(url) {
87
- if ( functionCache.urlToIdString.has(url.href) ) return functionCache.urlToIdString.get(url.href);
88
- let result = null;
89
- let wikiProject = wikiProjects.find( wikiProject => wikiProject.idString && url.hostname.endsWith( wikiProject.name ) );
90
- if ( wikiProject ) {
91
- let regex = url.href.match( new RegExp( wikiProject.regex ) )?.slice(2);
92
- if ( regex?.length ) {
93
- if ( wikiProject.idString.direction === 'desc' ) regex.reverse();
94
- result = regex.join(wikiProject.idString.separator);
95
- }
96
- }
97
- functionCache.urlToIdString.set(url.href, result);
98
- return result;
99
- }
100
-
101
- /**
102
- *
103
- * @param {string} idString
104
- * @param {string} projectName
105
- * @returns {?URL}
106
- */
107
- function idStringToUrl(idString, projectName) {
108
- let cacheKey = JSON.stringify([idString,projectName]);
109
- if ( functionCache.idStringToUrl.has(cacheKey) ) {
110
- let result = functionCache.idStringToUrl.get(cacheKey);
111
- return ( result ? new URL(result) : result );
112
- }
113
- let result = null;
114
- let wikiProject = wikiProjects.find( wikiProject => wikiProject.idString && wikiProject.name === projectName )?.idString;
115
- if ( wikiProject ) {
116
- let regex = idString.match( new RegExp( '^' + wikiProject.regex + '$' ) )?.[1].split(wikiProject.separator);
117
- if ( regex && regex.length <= wikiProject.scriptPaths.length ) {
118
- result = wikiProject.scriptPaths[regex.length - 1].replace( /\$(\d)/g, (match, n) => regex[n - 1] );
119
- }
120
- }
121
- functionCache.idStringToUrl.set(cacheKey, result);
122
- return ( result ? new URL(result) : result );
123
- }
124
-
125
- module.exports = {
126
- wikiProjects,
127
- inputToWikiProject,
128
- urlToIdString,
129
- idStringToUrl
1
+ const {properties: {wikiProjects: {items: {properties: wikiProjectSchema}}}} = require('./projects-schema.json');
2
+
3
+ /**
4
+ * A MediaWiki project
5
+ * @typedef {object} WikiProject
6
+ * @property {string} name - Hostname of the project
7
+ * @property {string} regex - Regex to match the project url
8
+ * @property {string} articlePath - Article path of the project
9
+ * @property {string} scriptPath - Script path of the project
10
+ * @property {object} [idString] - Only exists when the hostname contains multiple wikis: How to handle the id string
11
+ * @property {string} idString.separator - Separator to join or split the id string on
12
+ * @property {"asc"|"desc"} idString.direction - Order in which the project regex additional group matches should be chained to gain the id string
13
+ * @property {string} idString.regex - Regex to match the id string
14
+ * @property {string[]} idString.scriptPaths - How to turn the group matches of the id string regex into an URL to the script path, index based on group matches
15
+ * @property {boolean} regexPaths - Whether the paths include matches of the regex
16
+ * @property {?("wikimedia"|"fandom"|"miraheze"|"wiki.gg")} wikiFarm - Wiki farm of the project
17
+ * @property {("CentralAuth"|"Cargo")[]} extensions - List of extensions providing useful API endpoints
18
+ * @property {string} urlSpaceReplacement - Replacement for spaces in the article URL
19
+ * @property {?string} note - Note about the specific project
20
+ */
21
+
22
+ /**
23
+ * @type {{
24
+ * inputToWikiProject: Map<string, ?{fullArticlePath: string, fullScriptPath: string, wikiProject: WikiProject}>,
25
+ * urlToIdString: Map<string, ?string>,
26
+ * idStringToUrl: Map<string, ?string>
27
+ * }}
28
+ */
29
+ const functionCache = {
30
+ inputToWikiProject: new Map(),
31
+ urlToIdString: new Map(),
32
+ idStringToUrl: new Map()
33
+ };
34
+
35
+ /**
36
+ * List of MediaWiki projects
37
+ * @type {WikiProject[]}
38
+ */
39
+ const wikiProjects = require('./projects.json').wikiProjects.map( wikiProject => {
40
+ if ( wikiProject.idString ) {
41
+ wikiProject.idString.separator ??= wikiProjectSchema.idString.properties.separator.default;
42
+ wikiProject.idString.direction ??= wikiProjectSchema.idString.properties.direction.default;
43
+ }
44
+ wikiProject.regexPaths ??= wikiProjectSchema.regexPaths.default;
45
+ wikiProject.wikiFarm ??= wikiProjectSchema.wikiFarm.default;
46
+ wikiProject.extensions ??= wikiProjectSchema.extensions.default.slice();
47
+ wikiProject.urlSpaceReplacement ??= wikiProjectSchema.urlSpaceReplacement.default;
48
+ wikiProject.note ??= wikiProjectSchema.note.default;
49
+ return wikiProject;
50
+ } );
51
+
52
+ /**
53
+ *
54
+ * @param {string} input
55
+ * @returns {?{fullArticlePath: string, fullScriptPath: string, wikiProject: WikiProject}}
56
+ */
57
+ function inputToWikiProject(input) {
58
+ if ( functionCache.inputToWikiProject.has(input) ) return structuredClone(functionCache.inputToWikiProject.get(input));
59
+ let result = null;
60
+ let wikiProject = wikiProjects.find( wikiProject => input.split('/').slice(0, 3).some( part => part.endsWith( wikiProject.name ) ) );
61
+ if ( wikiProject ) {
62
+ let articlePath = ( wikiProject.regexPaths ? '/' : wikiProject.articlePath );
63
+ let scriptPath = ( wikiProject.regexPaths ? '/' : wikiProject.scriptPath );
64
+ let regex = input.match( new RegExp( wikiProject.regex + `(?:${articlePath}|${scriptPath}|/?$)` ) );
65
+ if ( regex ) {
66
+ if ( wikiProject.regexPaths ) {
67
+ scriptPath = wikiProject.scriptPath.replace( /\$(\d)/g, (match, n) => regex[n] );
68
+ articlePath = wikiProject.articlePath.replace( /\$(\d)/g, (match, n) => regex[n] );
69
+ }
70
+ result = {
71
+ fullArticlePath: 'https://' + regex[1] + articlePath,
72
+ fullScriptPath: 'https://' + regex[1] + scriptPath,
73
+ wikiProject: wikiProject
74
+ };
75
+ }
76
+ }
77
+ functionCache.inputToWikiProject.set(input, result);
78
+ return structuredClone(result);
79
+ }
80
+
81
+ /**
82
+ *
83
+ * @param {URL} url
84
+ * @returns {?string}
85
+ */
86
+ function urlToIdString(url) {
87
+ if ( functionCache.urlToIdString.has(url.href) ) return functionCache.urlToIdString.get(url.href);
88
+ let result = null;
89
+ let wikiProject = wikiProjects.find( wikiProject => wikiProject.idString && url.hostname.endsWith( wikiProject.name ) );
90
+ if ( wikiProject ) {
91
+ let regex = url.href.match( new RegExp( wikiProject.regex ) )?.slice(2);
92
+ if ( regex?.length ) {
93
+ if ( wikiProject.idString.direction === 'desc' ) regex.reverse();
94
+ result = regex.join(wikiProject.idString.separator);
95
+ }
96
+ }
97
+ functionCache.urlToIdString.set(url.href, result);
98
+ return result;
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param {string} idString
104
+ * @param {string} projectName
105
+ * @returns {?URL}
106
+ */
107
+ function idStringToUrl(idString, projectName) {
108
+ let cacheKey = JSON.stringify([idString,projectName]);
109
+ if ( functionCache.idStringToUrl.has(cacheKey) ) {
110
+ let result = functionCache.idStringToUrl.get(cacheKey);
111
+ return ( result ? new URL(result) : result );
112
+ }
113
+ let result = null;
114
+ let wikiProject = wikiProjects.find( wikiProject => wikiProject.idString && wikiProject.name === projectName )?.idString;
115
+ if ( wikiProject ) {
116
+ let regex = idString.match( new RegExp( '^' + wikiProject.regex + '$' ) )?.[1].split(wikiProject.separator);
117
+ if ( regex && regex.length <= wikiProject.scriptPaths.length ) {
118
+ result = wikiProject.scriptPaths[regex.length - 1].replace( /\$(\d)/g, (match, n) => regex[n - 1] );
119
+ }
120
+ }
121
+ functionCache.idStringToUrl.set(cacheKey, result);
122
+ return ( result ? new URL(result) : result );
123
+ }
124
+
125
+ module.exports = {
126
+ wikiProjects,
127
+ inputToWikiProject,
128
+ urlToIdString,
129
+ idStringToUrl
130
130
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "mediawiki-projects-list",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "commonjs",
5
5
  "description": "List of MediaWiki projects for use in discord-wiki-bot",
6
6
  "main": "index.js",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/Markus-Rost/mediawiki-projects-list.git"
9
+ "url": "https://github.com/Markus-Rost/mediawiki-projects-list.git"
10
10
  },
11
11
  "keywords": [
12
12
  "mediawiki"
@@ -1,130 +1,130 @@
1
- {
2
- "$schema": "https://json-schema.org/draft-07/schema",
3
- "$id": "./projects.json",
4
- "type": "object",
5
- "title": "List of MediaWiki projects",
6
- "description": "List of MediaWiki projects for use in discord-wiki-bot",
7
- "properties": {
8
- "$schema": {
9
- "type": "string",
10
- "format": "path"
11
- },
12
- "wikiProjects": {
13
- "type": "array",
14
- "description": "List of MediaWiki projects",
15
- "items": {
16
- "type": "object",
17
- "description": "A MediaWiki project",
18
- "properties": {
19
- "name": {
20
- "type": "string",
21
- "description": "Hostname of the project"
22
- },
23
- "regex": {
24
- "type": "string",
25
- "description": "Regex to match the project url"
26
- },
27
- "articlePath": {
28
- "type": "string",
29
- "description": "Article path of the project"
30
- },
31
- "scriptPath": {
32
- "type": "string",
33
- "description": "Script path of the project"
34
- },
35
- "idString": {
36
- "type": "object",
37
- "description": "Only exists when the hostname contains multiple wikis: How to handle the id string",
38
- "properties": {
39
- "separator": {
40
- "type": "string",
41
- "description": "Separator to join or split the id string on",
42
- "default": "."
43
- },
44
- "direction": {
45
- "type": "string",
46
- "description": "Order in which the project regex additional group matches should be chained to gain the id string",
47
- "enum": [
48
- "asc",
49
- "desc"
50
- ],
51
- "default": "desc"
52
- },
53
- "regex": {
54
- "type": "string",
55
- "description": "Regex to match the id string"
56
- },
57
- "scriptPaths": {
58
- "type": "array",
59
- "description": "How to turn the group matches of the id string regex into an URL to the script path, index based on group matches",
60
- "items": {
61
- "type": "string",
62
- "description": "Replacement for the id string regex match."
63
- },
64
- "contains": true,
65
- "additionalItems": false
66
- }
67
- },
68
- "required": [
69
- "regex",
70
- "scriptPaths"
71
- ],
72
- "additionalProperties": false
73
- },
74
- "regexPaths": {
75
- "type": "boolean",
76
- "description": "Whether the paths include matches of the regex",
77
- "default": false
78
- },
79
- "wikiFarm": {
80
- "type": "string",
81
- "description": "Wiki farm of the project",
82
- "enum": [
83
- "wikimedia",
84
- "fandom",
85
- "miraheze",
86
- "wiki.gg"
87
- ],
88
- "default": null
89
- },
90
- "extensions": {
91
- "type": "array",
92
- "description": "List of extensions providing useful API endpoints",
93
- "items": {
94
- "type": "string",
95
- "description": "Name of an extension providing useful API endpoints",
96
- "enum": [
97
- "CentralAuth",
98
- "Cargo"
99
- ]
100
- },
101
- "default": [],
102
- "additionalItems": false
103
- },
104
- "urlSpaceReplacement": {
105
- "type": "string",
106
- "description": "Replacement for spaces in the article URL",
107
- "default": "_"
108
- },
109
- "note": {
110
- "type": "string",
111
- "description": "Note about the specific project",
112
- "default": null
113
- }
114
- },
115
- "required": [
116
- "name",
117
- "regex",
118
- "articlePath",
119
- "scriptPath"
120
- ],
121
- "additionalProperties": false
122
- },
123
- "additionalItems": false
124
- }
125
- },
126
- "required": [
127
- "wikiProjects"
128
- ],
129
- "additionalProperties": false
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema",
3
+ "$id": "./projects.json",
4
+ "type": "object",
5
+ "title": "List of MediaWiki projects",
6
+ "description": "List of MediaWiki projects for use in discord-wiki-bot",
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "format": "path"
11
+ },
12
+ "wikiProjects": {
13
+ "type": "array",
14
+ "description": "List of MediaWiki projects",
15
+ "items": {
16
+ "type": "object",
17
+ "description": "A MediaWiki project",
18
+ "properties": {
19
+ "name": {
20
+ "type": "string",
21
+ "description": "Hostname of the project"
22
+ },
23
+ "regex": {
24
+ "type": "string",
25
+ "description": "Regex to match the project url"
26
+ },
27
+ "articlePath": {
28
+ "type": "string",
29
+ "description": "Article path of the project"
30
+ },
31
+ "scriptPath": {
32
+ "type": "string",
33
+ "description": "Script path of the project"
34
+ },
35
+ "idString": {
36
+ "type": "object",
37
+ "description": "Only exists when the hostname contains multiple wikis: How to handle the id string",
38
+ "properties": {
39
+ "separator": {
40
+ "type": "string",
41
+ "description": "Separator to join or split the id string on",
42
+ "default": "."
43
+ },
44
+ "direction": {
45
+ "type": "string",
46
+ "description": "Order in which the project regex additional group matches should be chained to gain the id string",
47
+ "enum": [
48
+ "asc",
49
+ "desc"
50
+ ],
51
+ "default": "desc"
52
+ },
53
+ "regex": {
54
+ "type": "string",
55
+ "description": "Regex to match the id string"
56
+ },
57
+ "scriptPaths": {
58
+ "type": "array",
59
+ "description": "How to turn the group matches of the id string regex into an URL to the script path, index based on group matches",
60
+ "items": {
61
+ "type": "string",
62
+ "description": "Replacement for the id string regex match."
63
+ },
64
+ "contains": true,
65
+ "additionalItems": false
66
+ }
67
+ },
68
+ "required": [
69
+ "regex",
70
+ "scriptPaths"
71
+ ],
72
+ "additionalProperties": false
73
+ },
74
+ "regexPaths": {
75
+ "type": "boolean",
76
+ "description": "Whether the paths include matches of the regex",
77
+ "default": false
78
+ },
79
+ "wikiFarm": {
80
+ "type": "string",
81
+ "description": "Wiki farm of the project",
82
+ "enum": [
83
+ "wikimedia",
84
+ "fandom",
85
+ "miraheze",
86
+ "wiki.gg"
87
+ ],
88
+ "default": null
89
+ },
90
+ "extensions": {
91
+ "type": "array",
92
+ "description": "List of extensions providing useful API endpoints",
93
+ "items": {
94
+ "type": "string",
95
+ "description": "Name of an extension providing useful API endpoints",
96
+ "enum": [
97
+ "CentralAuth",
98
+ "Cargo"
99
+ ]
100
+ },
101
+ "default": [],
102
+ "additionalItems": false
103
+ },
104
+ "urlSpaceReplacement": {
105
+ "type": "string",
106
+ "description": "Replacement for spaces in the article URL",
107
+ "default": "_"
108
+ },
109
+ "note": {
110
+ "type": "string",
111
+ "description": "Note about the specific project",
112
+ "default": null
113
+ }
114
+ },
115
+ "required": [
116
+ "name",
117
+ "regex",
118
+ "articlePath",
119
+ "scriptPath"
120
+ ],
121
+ "additionalProperties": false
122
+ },
123
+ "additionalItems": false
124
+ }
125
+ },
126
+ "required": [
127
+ "wikiProjects"
128
+ ],
129
+ "additionalProperties": false
130
130
  }