@mrtimeey/everybodycodes-data 1.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.
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/everybodycodes-data.d.ts +58 -0
- package/dist/index.cjs +158 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tim Kruse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @mrtimeey/everybodycodes-data
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@mrtimeey/everybodycodes-data)
|
|
4
|
+
[](https://provenancebadge.vercel.app)
|
|
5
|
+
[](https://github.com/mrtimeey/everybodycodes-data/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/mrtimeey/everybodycodes-data/actions/workflows/release.yml)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
> 🧩 A lightweight Node.js client for fetching and decrypting **Everybody Codes** inputs automatically.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
14
|
+
|
|
15
|
+
- Fetch and decrypt puzzle input data from [everybody.codes](https://everybody.codes/)
|
|
16
|
+
- Fully typed API (TypeScript)
|
|
17
|
+
- Built-in test mocks via `undici.MockAgent`
|
|
18
|
+
- Automatically validated examples and README code blocks in CI
|
|
19
|
+
- API-stable and documented via [API Extractor](https://api-extractor.com/)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🚀 Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @mrtimeey/everybodycodes-data
|
|
27
|
+
# or
|
|
28
|
+
pnpm add @mrtimeey/everybodycodes-data
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📘 Usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { EverybodyCodesClient } from "@mrtimeey/everybodycodes-data";
|
|
37
|
+
|
|
38
|
+
const client = new EverybodyCodesClient("your-everybody-codes-session-cookie");
|
|
39
|
+
|
|
40
|
+
// Fetch and decrypt full quest data
|
|
41
|
+
const data = await client.getEventData("2025", 1);
|
|
42
|
+
console.log(data); // { 1: "input text part 1", 2: "...", 3: "..." }
|
|
43
|
+
|
|
44
|
+
// Fetch only a single part
|
|
45
|
+
const part1 = await client.getEventPartData("2025", 1, 1);
|
|
46
|
+
console.log(part1);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> 💡 Your `session-cookie` must match the `everybody-codes` cookie from your logged-in browser.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 🧩 API Reference
|
|
54
|
+
|
|
55
|
+
The public API surface is automatically tracked by [API Extractor](https://api-extractor.com/).
|
|
56
|
+
See [`etc/everybodycodes-data.api.md`](./etc/everybodycodes-data.api.md) for the latest exported types.
|
|
57
|
+
|
|
58
|
+
The public API is also documented through [Typedoc on GH-Pages](https://mrtimeey.github.io/everybodycodes-data/).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🧱 Development
|
|
63
|
+
|
|
64
|
+
### Prerequisites
|
|
65
|
+
|
|
66
|
+
- Node.js ≥ 18.17
|
|
67
|
+
- npm ≥ 9 or pnpm ≥ 8
|
|
68
|
+
|
|
69
|
+
### Setup
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm ci
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Run all checks
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm test
|
|
79
|
+
npm run build
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Lint & Format
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run lint
|
|
86
|
+
npm run format
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🔄 Release Workflow
|
|
92
|
+
|
|
93
|
+
This repository uses [release-please](https://github.com/google-github-actions/release-please-action):
|
|
94
|
+
|
|
95
|
+
1. Conventional commits (`fix:`, `feat:`) update changelog and version bump via PR.
|
|
96
|
+
2. Merge the release PR → Git tag is created.
|
|
97
|
+
3. GitHub Actions publish the package to npm with provenance.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🛡️ Security
|
|
102
|
+
|
|
103
|
+
See [SECURITY.md](./SECURITY.md) for responsible disclosure guidelines.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🤝 Contributing
|
|
108
|
+
|
|
109
|
+
Contributions, issues, and pull requests are welcome!
|
|
110
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 📄 License
|
|
115
|
+
|
|
116
|
+
MIT © [Tim Kruse](https://github.com/mrtimeey)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for request against the EverybodyCodes API.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export declare class EverybodyCodesClient {
|
|
7
|
+
private readonly cookie;
|
|
8
|
+
private seed?;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new EverybodyCodesClient.
|
|
11
|
+
* @param sessionCookie - Value of the `everybody-codes` cookie.
|
|
12
|
+
*/
|
|
13
|
+
constructor(sessionCookie: string);
|
|
14
|
+
private makeUrl;
|
|
15
|
+
private cookieHeader;
|
|
16
|
+
private getSeed;
|
|
17
|
+
private getEncryptedInputs;
|
|
18
|
+
private getDataInternal;
|
|
19
|
+
private getKeys;
|
|
20
|
+
/**
|
|
21
|
+
* Fetches data for a specific event and quest.
|
|
22
|
+
* @param event - Event ID
|
|
23
|
+
* @param quest - Quest ID
|
|
24
|
+
* @returns Data for the event and quest
|
|
25
|
+
*/
|
|
26
|
+
getEventData(event: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetches data for a specific story and quest.
|
|
29
|
+
* @param story - Story ID
|
|
30
|
+
* @param quest - Quest ID
|
|
31
|
+
* @returns Data for the story and quest
|
|
32
|
+
*/
|
|
33
|
+
getStoryData(story: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
34
|
+
/**
|
|
35
|
+
* Fetches data for a specific event, quest, and part.
|
|
36
|
+
* @param event - Event ID
|
|
37
|
+
* @param quest - Quest ID
|
|
38
|
+
* @param part - Part number (1, 2, or 3)
|
|
39
|
+
* @returns Data for the event, quest, and part
|
|
40
|
+
*/
|
|
41
|
+
getEventPartData(event: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Fetches data for a specific story, quest, and part.
|
|
44
|
+
* @param story - Story ID
|
|
45
|
+
* @param quest - Quest ID
|
|
46
|
+
* @param part - Part number (1, 2, or 3)
|
|
47
|
+
* @returns Data for the story, quest, and part
|
|
48
|
+
*/
|
|
49
|
+
getStoryPartData(story: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Part number for quests.
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export declare type PartNumber = 1 | 2 | 3;
|
|
57
|
+
|
|
58
|
+
export { }
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
8
|
+
|
|
9
|
+
// src/helper/http.ts
|
|
10
|
+
async function http(url, init) {
|
|
11
|
+
const res = await fetch(url, {
|
|
12
|
+
...init,
|
|
13
|
+
headers: {
|
|
14
|
+
accept: "application/json",
|
|
15
|
+
"user-agent": "everybodycodes-data/0.1 (+github.com/you/repo)",
|
|
16
|
+
...init?.headers ?? {}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const body = await res.text().catch(() => "");
|
|
21
|
+
throw new Error(
|
|
22
|
+
`HTTP ${res.status} ${res.statusText} :: ${url} :: ${body}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const ct = res.headers.get("content-type") || "";
|
|
26
|
+
if (ct.includes("application/json")) {
|
|
27
|
+
return await res.json();
|
|
28
|
+
}
|
|
29
|
+
return await res.text();
|
|
30
|
+
}
|
|
31
|
+
function aesDecryptHexWithKey(key, hexCipher) {
|
|
32
|
+
const keyBytes = Buffer.from(key, "utf8");
|
|
33
|
+
const iv = Buffer.from(key.substring(0, 16), "utf8");
|
|
34
|
+
const cipherBytes = Buffer.from(hexCipher, "hex");
|
|
35
|
+
const algo = keyBytes.length === 16 ? "aes-128-cbc" : keyBytes.length === 24 ? "aes-192-cbc" : keyBytes.length === 32 ? "aes-256-cbc" : "aes-256-cbc";
|
|
36
|
+
const dec = crypto__default.default.createDecipheriv(algo, keyBytes, iv);
|
|
37
|
+
const out = Buffer.concat([dec.update(cipherBytes), dec.final()]);
|
|
38
|
+
return out.toString("utf8");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/client.ts
|
|
42
|
+
var API_BASE = "https://everybody.codes/api";
|
|
43
|
+
var CDN_BASE = "https://everybody-codes.b-cdn.net/assets";
|
|
44
|
+
var EverybodyCodesClient = class {
|
|
45
|
+
cookie;
|
|
46
|
+
seed;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new EverybodyCodesClient.
|
|
49
|
+
* @param sessionCookie - Value of the `everybody-codes` cookie.
|
|
50
|
+
*/
|
|
51
|
+
constructor(sessionCookie) {
|
|
52
|
+
if (!sessionCookie || !sessionCookie.trim()) {
|
|
53
|
+
throw new Error("EverybodyCodesClient: sessionCookie is required.");
|
|
54
|
+
}
|
|
55
|
+
this.cookie = sessionCookie.trim();
|
|
56
|
+
}
|
|
57
|
+
makeUrl(type, ...parts) {
|
|
58
|
+
return [
|
|
59
|
+
API_BASE,
|
|
60
|
+
type,
|
|
61
|
+
...parts.map((p) => encodeURIComponent(String(p)))
|
|
62
|
+
].join("/");
|
|
63
|
+
}
|
|
64
|
+
cookieHeader() {
|
|
65
|
+
return { Cookie: `everybody-codes=${this.cookie}` };
|
|
66
|
+
}
|
|
67
|
+
async getSeed() {
|
|
68
|
+
if (this.seed) return this.seed;
|
|
69
|
+
const url = `${API_BASE}/user/me`;
|
|
70
|
+
const data = await http(url, {
|
|
71
|
+
headers: this.cookieHeader()
|
|
72
|
+
});
|
|
73
|
+
const raw = data?.seed;
|
|
74
|
+
const num = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : NaN;
|
|
75
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Invalid seed value (0). This usually means your session has expired or the 'everybody-codes' cookie is invalid. Log in again at https://everybody.codes, copy the cookie value exactly (no spaces or quotes), and try again."
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
this.seed = String(num);
|
|
81
|
+
return this.seed;
|
|
82
|
+
}
|
|
83
|
+
async getEncryptedInputs(event, quest, seed) {
|
|
84
|
+
const url = `${CDN_BASE}/${encodeURIComponent(String(event))}/${encodeURIComponent(
|
|
85
|
+
String(quest)
|
|
86
|
+
)}/input/${encodeURIComponent(seed)}.json`;
|
|
87
|
+
return await http(url, {
|
|
88
|
+
headers: this.cookieHeader()
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async getDataInternal(type, event, quest) {
|
|
92
|
+
const seed = await this.getSeed();
|
|
93
|
+
const encrypted = await this.getEncryptedInputs(event, quest, seed);
|
|
94
|
+
const keys = await this.getKeys(type, event, quest);
|
|
95
|
+
const out = {};
|
|
96
|
+
["1", "2", "3"].forEach((p) => {
|
|
97
|
+
const hex = encrypted[p];
|
|
98
|
+
const key = keys[p];
|
|
99
|
+
if (hex && key)
|
|
100
|
+
out[Number(p)] = aesDecryptHexWithKey(key, hex);
|
|
101
|
+
});
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
async getKeys(type, event, quest) {
|
|
105
|
+
const url = this.makeUrl(type, event, "quest", quest);
|
|
106
|
+
const data = await http(url, { headers: this.cookieHeader() });
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const p of ["1", "2", "3"]) {
|
|
109
|
+
const flat = data?.[`key${p}`];
|
|
110
|
+
if (flat) out[p] = String(flat);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Fetches data for a specific event and quest.
|
|
116
|
+
* @param event - Event ID
|
|
117
|
+
* @param quest - Quest ID
|
|
118
|
+
* @returns Data for the event and quest
|
|
119
|
+
*/
|
|
120
|
+
async getEventData(event, quest) {
|
|
121
|
+
return this.getDataInternal("event" /* Event */, event, quest);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Fetches data for a specific story and quest.
|
|
125
|
+
* @param story - Story ID
|
|
126
|
+
* @param quest - Quest ID
|
|
127
|
+
* @returns Data for the story and quest
|
|
128
|
+
*/
|
|
129
|
+
async getStoryData(story, quest) {
|
|
130
|
+
return this.getDataInternal("story" /* Story */, story, quest);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Fetches data for a specific event, quest, and part.
|
|
134
|
+
* @param event - Event ID
|
|
135
|
+
* @param quest - Quest ID
|
|
136
|
+
* @param part - Part number (1, 2, or 3)
|
|
137
|
+
* @returns Data for the event, quest, and part
|
|
138
|
+
*/
|
|
139
|
+
async getEventPartData(event, quest, part) {
|
|
140
|
+
const data = await this.getDataInternal("event" /* Event */, event, quest);
|
|
141
|
+
return data[part];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Fetches data for a specific story, quest, and part.
|
|
145
|
+
* @param story - Story ID
|
|
146
|
+
* @param quest - Quest ID
|
|
147
|
+
* @param part - Part number (1, 2, or 3)
|
|
148
|
+
* @returns Data for the story, quest, and part
|
|
149
|
+
*/
|
|
150
|
+
async getStoryPartData(story, quest, part) {
|
|
151
|
+
const data = await this.getDataInternal("story" /* Story */, story, quest);
|
|
152
|
+
return data[part];
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
exports.EverybodyCodesClient = EverybodyCodesClient;
|
|
157
|
+
//# sourceMappingURL=index.cjs.map
|
|
158
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helper/http.ts","../src/crypto.ts","../src/client.ts"],"names":["crypto"],"mappings":"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","file":"index.cjs","sourcesContent":["export async function http<T = unknown>(\n url: string,\n init?: RequestInit,\n): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: {\n accept: \"application/json\",\n \"user-agent\": \"everybodycodes-data/0.1 (+github.com/you/repo)\",\n ...(init?.headers ?? {}),\n },\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(\n `HTTP ${res.status} ${res.statusText} :: ${url} :: ${body}`,\n );\n }\n const ct = res.headers.get(\"content-type\") || \"\";\n if (ct.includes(\"application/json\")) {\n return (await res.json()) as T;\n }\n return (await res.text()) as unknown as T;\n}\n","import crypto from \"node:crypto\";\n\nexport function aesDecryptHexWithKey(key: string, hexCipher: string): string {\n const keyBytes = Buffer.from(key, \"utf8\");\n const iv = Buffer.from(key.substring(0, 16), \"utf8\");\n const cipherBytes = Buffer.from(hexCipher, \"hex\");\n\n const algo =\n keyBytes.length === 16\n ? \"aes-128-cbc\"\n : keyBytes.length === 24\n ? \"aes-192-cbc\"\n : keyBytes.length === 32\n ? \"aes-256-cbc\"\n : \"aes-256-cbc\";\n\n const dec = crypto.createDecipheriv(algo, keyBytes, iv);\n const out = Buffer.concat([dec.update(cipherBytes), dec.final()]);\n return out.toString(\"utf8\");\n}\n","import { http } from \"./helper/http\";\nimport { aesDecryptHexWithKey } from \"./crypto.js\";\n\nconst API_BASE = \"https://everybody.codes/api\";\nconst CDN_BASE = \"https://everybody-codes.b-cdn.net/assets\";\n\nenum ChallengeType {\n Event = \"event\",\n Story = \"story\",\n}\n\n/**\n * Part number for quests.\n * @public\n */\nexport type PartNumber = 1 | 2 | 3;\n\n/**\n * Client for request against the EverybodyCodes API.\n *\n * @public\n */\nexport class EverybodyCodesClient {\n private readonly cookie: string;\n private seed?: string;\n\n /**\n * Creates a new EverybodyCodesClient.\n * @param sessionCookie - Value of the `everybody-codes` cookie.\n */\n constructor(sessionCookie: string) {\n if (!sessionCookie || !sessionCookie.trim()) {\n throw new Error(\"EverybodyCodesClient: sessionCookie is required.\");\n }\n this.cookie = sessionCookie.trim();\n }\n\n private makeUrl(type: ChallengeType, ...parts: (string | number)[]): string {\n return [\n API_BASE,\n type,\n ...parts.map((p) => encodeURIComponent(String(p))),\n ].join(\"/\");\n }\n\n private cookieHeader(): HeadersInit {\n return { Cookie: `everybody-codes=${this.cookie}` };\n }\n\n private async getSeed(): Promise<string> {\n if (this.seed) return this.seed;\n\n const url = `${API_BASE}/user/me`;\n const data = await http<{ seed?: number | string }>(url, {\n headers: this.cookieHeader(),\n });\n\n const raw = data?.seed;\n const num =\n typeof raw === \"number\"\n ? raw\n : typeof raw === \"string\" && raw.trim() !== \"\"\n ? Number(raw)\n : NaN;\n\n if (!Number.isFinite(num) || num <= 0) {\n throw new Error(\n \"Invalid seed value (0). This usually means your session has expired or the 'everybody-codes' cookie is invalid. \" +\n \"Log in again at https://everybody.codes, copy the cookie value exactly (no spaces or quotes), and try again.\",\n );\n }\n\n this.seed = String(num);\n return this.seed;\n }\n\n private async getEncryptedInputs(\n event: string | number,\n quest: string | number,\n seed: string,\n ) {\n const url = `${CDN_BASE}/${encodeURIComponent(String(event))}/${encodeURIComponent(\n String(quest),\n )}/input/${encodeURIComponent(seed)}.json`;\n return await http<Record<\"1\" | \"2\" | \"3\", string>>(url, {\n headers: this.cookieHeader(),\n });\n }\n\n private async getDataInternal(\n type: ChallengeType,\n event: string | number,\n quest: string | number,\n ) {\n const seed = await this.getSeed();\n const encrypted = await this.getEncryptedInputs(event, quest, seed);\n const keys = await this.getKeys(type, event, quest);\n\n const out: Partial<Record<1 | 2 | 3, string>> = {};\n ([\"1\", \"2\", \"3\"] as const).forEach((p) => {\n const hex = encrypted[p];\n const key = keys[p];\n if (hex && key)\n out[Number(p) as 1 | 2 | 3] = aesDecryptHexWithKey(key, hex);\n });\n return out;\n }\n\n private async getKeys(\n type: ChallengeType,\n event: string | number,\n quest: string | number,\n ) {\n const url = this.makeUrl(type, event, \"quest\", quest);\n\n type KeyResponse = {\n key1?: string;\n key2?: string;\n key3?: string;\n };\n\n const data = await http<KeyResponse>(url, { headers: this.cookieHeader() });\n\n const out: Partial<Record<\"1\" | \"2\" | \"3\", string>> = {};\n for (const p of [\"1\", \"2\", \"3\"] as const) {\n const flat = data?.[`key${p}`];\n if (flat) out[p] = String(flat);\n }\n return out;\n }\n\n /**\n * Fetches data for a specific event and quest.\n * @param event - Event ID\n * @param quest - Quest ID\n * @returns Data for the event and quest\n */\n async getEventData(event: string | number, quest: string | number) {\n return this.getDataInternal(ChallengeType.Event, event, quest);\n }\n\n /**\n * Fetches data for a specific story and quest.\n * @param story - Story ID\n * @param quest - Quest ID\n * @returns Data for the story and quest\n */\n async getStoryData(story: string | number, quest: string | number) {\n return this.getDataInternal(ChallengeType.Story, story, quest);\n }\n\n /**\n * Fetches data for a specific event, quest, and part.\n * @param event - Event ID\n * @param quest - Quest ID\n * @param part - Part number (1, 2, or 3)\n * @returns Data for the event, quest, and part\n */\n async getEventPartData(\n event: string | number,\n quest: string | number,\n part: PartNumber,\n ) {\n const data = await this.getDataInternal(ChallengeType.Event, event, quest);\n return data[part];\n }\n\n /**\n * Fetches data for a specific story, quest, and part.\n * @param story - Story ID\n * @param quest - Quest ID\n * @param part - Part number (1, 2, or 3)\n * @returns Data for the story, quest, and part\n */\n async getStoryPartData(\n story: string | number,\n quest: string | number,\n part: PartNumber,\n ) {\n const data = await this.getDataInternal(ChallengeType.Story, story, quest);\n return data[part];\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Part number for quests.
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
type PartNumber = 1 | 2 | 3;
|
|
6
|
+
/**
|
|
7
|
+
* Client for request against the EverybodyCodes API.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
declare class EverybodyCodesClient {
|
|
12
|
+
private readonly cookie;
|
|
13
|
+
private seed?;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new EverybodyCodesClient.
|
|
16
|
+
* @param sessionCookie - Value of the `everybody-codes` cookie.
|
|
17
|
+
*/
|
|
18
|
+
constructor(sessionCookie: string);
|
|
19
|
+
private makeUrl;
|
|
20
|
+
private cookieHeader;
|
|
21
|
+
private getSeed;
|
|
22
|
+
private getEncryptedInputs;
|
|
23
|
+
private getDataInternal;
|
|
24
|
+
private getKeys;
|
|
25
|
+
/**
|
|
26
|
+
* Fetches data for a specific event and quest.
|
|
27
|
+
* @param event - Event ID
|
|
28
|
+
* @param quest - Quest ID
|
|
29
|
+
* @returns Data for the event and quest
|
|
30
|
+
*/
|
|
31
|
+
getEventData(event: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetches data for a specific story and quest.
|
|
34
|
+
* @param story - Story ID
|
|
35
|
+
* @param quest - Quest ID
|
|
36
|
+
* @returns Data for the story and quest
|
|
37
|
+
*/
|
|
38
|
+
getStoryData(story: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetches data for a specific event, quest, and part.
|
|
41
|
+
* @param event - Event ID
|
|
42
|
+
* @param quest - Quest ID
|
|
43
|
+
* @param part - Part number (1, 2, or 3)
|
|
44
|
+
* @returns Data for the event, quest, and part
|
|
45
|
+
*/
|
|
46
|
+
getEventPartData(event: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* Fetches data for a specific story, quest, and part.
|
|
49
|
+
* @param story - Story ID
|
|
50
|
+
* @param quest - Quest ID
|
|
51
|
+
* @param part - Part number (1, 2, or 3)
|
|
52
|
+
* @returns Data for the story, quest, and part
|
|
53
|
+
*/
|
|
54
|
+
getStoryPartData(story: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { EverybodyCodesClient, type PartNumber };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Part number for quests.
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
type PartNumber = 1 | 2 | 3;
|
|
6
|
+
/**
|
|
7
|
+
* Client for request against the EverybodyCodes API.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
declare class EverybodyCodesClient {
|
|
12
|
+
private readonly cookie;
|
|
13
|
+
private seed?;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new EverybodyCodesClient.
|
|
16
|
+
* @param sessionCookie - Value of the `everybody-codes` cookie.
|
|
17
|
+
*/
|
|
18
|
+
constructor(sessionCookie: string);
|
|
19
|
+
private makeUrl;
|
|
20
|
+
private cookieHeader;
|
|
21
|
+
private getSeed;
|
|
22
|
+
private getEncryptedInputs;
|
|
23
|
+
private getDataInternal;
|
|
24
|
+
private getKeys;
|
|
25
|
+
/**
|
|
26
|
+
* Fetches data for a specific event and quest.
|
|
27
|
+
* @param event - Event ID
|
|
28
|
+
* @param quest - Quest ID
|
|
29
|
+
* @returns Data for the event and quest
|
|
30
|
+
*/
|
|
31
|
+
getEventData(event: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetches data for a specific story and quest.
|
|
34
|
+
* @param story - Story ID
|
|
35
|
+
* @param quest - Quest ID
|
|
36
|
+
* @returns Data for the story and quest
|
|
37
|
+
*/
|
|
38
|
+
getStoryData(story: string | number, quest: string | number): Promise<Partial<Record<2 | 1 | 3, string>>>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetches data for a specific event, quest, and part.
|
|
41
|
+
* @param event - Event ID
|
|
42
|
+
* @param quest - Quest ID
|
|
43
|
+
* @param part - Part number (1, 2, or 3)
|
|
44
|
+
* @returns Data for the event, quest, and part
|
|
45
|
+
*/
|
|
46
|
+
getEventPartData(event: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* Fetches data for a specific story, quest, and part.
|
|
49
|
+
* @param story - Story ID
|
|
50
|
+
* @param quest - Quest ID
|
|
51
|
+
* @param part - Part number (1, 2, or 3)
|
|
52
|
+
* @returns Data for the story, quest, and part
|
|
53
|
+
*/
|
|
54
|
+
getStoryPartData(story: string | number, quest: string | number, part: PartNumber): Promise<string | undefined>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { EverybodyCodesClient, type PartNumber };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/helper/http.ts
|
|
4
|
+
async function http(url, init) {
|
|
5
|
+
const res = await fetch(url, {
|
|
6
|
+
...init,
|
|
7
|
+
headers: {
|
|
8
|
+
accept: "application/json",
|
|
9
|
+
"user-agent": "everybodycodes-data/0.1 (+github.com/you/repo)",
|
|
10
|
+
...init?.headers ?? {}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
if (!res.ok) {
|
|
14
|
+
const body = await res.text().catch(() => "");
|
|
15
|
+
throw new Error(
|
|
16
|
+
`HTTP ${res.status} ${res.statusText} :: ${url} :: ${body}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const ct = res.headers.get("content-type") || "";
|
|
20
|
+
if (ct.includes("application/json")) {
|
|
21
|
+
return await res.json();
|
|
22
|
+
}
|
|
23
|
+
return await res.text();
|
|
24
|
+
}
|
|
25
|
+
function aesDecryptHexWithKey(key, hexCipher) {
|
|
26
|
+
const keyBytes = Buffer.from(key, "utf8");
|
|
27
|
+
const iv = Buffer.from(key.substring(0, 16), "utf8");
|
|
28
|
+
const cipherBytes = Buffer.from(hexCipher, "hex");
|
|
29
|
+
const algo = keyBytes.length === 16 ? "aes-128-cbc" : keyBytes.length === 24 ? "aes-192-cbc" : keyBytes.length === 32 ? "aes-256-cbc" : "aes-256-cbc";
|
|
30
|
+
const dec = crypto.createDecipheriv(algo, keyBytes, iv);
|
|
31
|
+
const out = Buffer.concat([dec.update(cipherBytes), dec.final()]);
|
|
32
|
+
return out.toString("utf8");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/client.ts
|
|
36
|
+
var API_BASE = "https://everybody.codes/api";
|
|
37
|
+
var CDN_BASE = "https://everybody-codes.b-cdn.net/assets";
|
|
38
|
+
var EverybodyCodesClient = class {
|
|
39
|
+
cookie;
|
|
40
|
+
seed;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a new EverybodyCodesClient.
|
|
43
|
+
* @param sessionCookie - Value of the `everybody-codes` cookie.
|
|
44
|
+
*/
|
|
45
|
+
constructor(sessionCookie) {
|
|
46
|
+
if (!sessionCookie || !sessionCookie.trim()) {
|
|
47
|
+
throw new Error("EverybodyCodesClient: sessionCookie is required.");
|
|
48
|
+
}
|
|
49
|
+
this.cookie = sessionCookie.trim();
|
|
50
|
+
}
|
|
51
|
+
makeUrl(type, ...parts) {
|
|
52
|
+
return [
|
|
53
|
+
API_BASE,
|
|
54
|
+
type,
|
|
55
|
+
...parts.map((p) => encodeURIComponent(String(p)))
|
|
56
|
+
].join("/");
|
|
57
|
+
}
|
|
58
|
+
cookieHeader() {
|
|
59
|
+
return { Cookie: `everybody-codes=${this.cookie}` };
|
|
60
|
+
}
|
|
61
|
+
async getSeed() {
|
|
62
|
+
if (this.seed) return this.seed;
|
|
63
|
+
const url = `${API_BASE}/user/me`;
|
|
64
|
+
const data = await http(url, {
|
|
65
|
+
headers: this.cookieHeader()
|
|
66
|
+
});
|
|
67
|
+
const raw = data?.seed;
|
|
68
|
+
const num = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : NaN;
|
|
69
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Invalid seed value (0). This usually means your session has expired or the 'everybody-codes' cookie is invalid. Log in again at https://everybody.codes, copy the cookie value exactly (no spaces or quotes), and try again."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
this.seed = String(num);
|
|
75
|
+
return this.seed;
|
|
76
|
+
}
|
|
77
|
+
async getEncryptedInputs(event, quest, seed) {
|
|
78
|
+
const url = `${CDN_BASE}/${encodeURIComponent(String(event))}/${encodeURIComponent(
|
|
79
|
+
String(quest)
|
|
80
|
+
)}/input/${encodeURIComponent(seed)}.json`;
|
|
81
|
+
return await http(url, {
|
|
82
|
+
headers: this.cookieHeader()
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async getDataInternal(type, event, quest) {
|
|
86
|
+
const seed = await this.getSeed();
|
|
87
|
+
const encrypted = await this.getEncryptedInputs(event, quest, seed);
|
|
88
|
+
const keys = await this.getKeys(type, event, quest);
|
|
89
|
+
const out = {};
|
|
90
|
+
["1", "2", "3"].forEach((p) => {
|
|
91
|
+
const hex = encrypted[p];
|
|
92
|
+
const key = keys[p];
|
|
93
|
+
if (hex && key)
|
|
94
|
+
out[Number(p)] = aesDecryptHexWithKey(key, hex);
|
|
95
|
+
});
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
async getKeys(type, event, quest) {
|
|
99
|
+
const url = this.makeUrl(type, event, "quest", quest);
|
|
100
|
+
const data = await http(url, { headers: this.cookieHeader() });
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const p of ["1", "2", "3"]) {
|
|
103
|
+
const flat = data?.[`key${p}`];
|
|
104
|
+
if (flat) out[p] = String(flat);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Fetches data for a specific event and quest.
|
|
110
|
+
* @param event - Event ID
|
|
111
|
+
* @param quest - Quest ID
|
|
112
|
+
* @returns Data for the event and quest
|
|
113
|
+
*/
|
|
114
|
+
async getEventData(event, quest) {
|
|
115
|
+
return this.getDataInternal("event" /* Event */, event, quest);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fetches data for a specific story and quest.
|
|
119
|
+
* @param story - Story ID
|
|
120
|
+
* @param quest - Quest ID
|
|
121
|
+
* @returns Data for the story and quest
|
|
122
|
+
*/
|
|
123
|
+
async getStoryData(story, quest) {
|
|
124
|
+
return this.getDataInternal("story" /* Story */, story, quest);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fetches data for a specific event, quest, and part.
|
|
128
|
+
* @param event - Event ID
|
|
129
|
+
* @param quest - Quest ID
|
|
130
|
+
* @param part - Part number (1, 2, or 3)
|
|
131
|
+
* @returns Data for the event, quest, and part
|
|
132
|
+
*/
|
|
133
|
+
async getEventPartData(event, quest, part) {
|
|
134
|
+
const data = await this.getDataInternal("event" /* Event */, event, quest);
|
|
135
|
+
return data[part];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Fetches data for a specific story, quest, and part.
|
|
139
|
+
* @param story - Story ID
|
|
140
|
+
* @param quest - Quest ID
|
|
141
|
+
* @param part - Part number (1, 2, or 3)
|
|
142
|
+
* @returns Data for the story, quest, and part
|
|
143
|
+
*/
|
|
144
|
+
async getStoryPartData(story, quest, part) {
|
|
145
|
+
const data = await this.getDataInternal("story" /* Story */, story, quest);
|
|
146
|
+
return data[part];
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export { EverybodyCodesClient };
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helper/http.ts","../src/crypto.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export async function http<T = unknown>(\n url: string,\n init?: RequestInit,\n): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: {\n accept: \"application/json\",\n \"user-agent\": \"everybodycodes-data/0.1 (+github.com/you/repo)\",\n ...(init?.headers ?? {}),\n },\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(\n `HTTP ${res.status} ${res.statusText} :: ${url} :: ${body}`,\n );\n }\n const ct = res.headers.get(\"content-type\") || \"\";\n if (ct.includes(\"application/json\")) {\n return (await res.json()) as T;\n }\n return (await res.text()) as unknown as T;\n}\n","import crypto from \"node:crypto\";\n\nexport function aesDecryptHexWithKey(key: string, hexCipher: string): string {\n const keyBytes = Buffer.from(key, \"utf8\");\n const iv = Buffer.from(key.substring(0, 16), \"utf8\");\n const cipherBytes = Buffer.from(hexCipher, \"hex\");\n\n const algo =\n keyBytes.length === 16\n ? \"aes-128-cbc\"\n : keyBytes.length === 24\n ? \"aes-192-cbc\"\n : keyBytes.length === 32\n ? \"aes-256-cbc\"\n : \"aes-256-cbc\";\n\n const dec = crypto.createDecipheriv(algo, keyBytes, iv);\n const out = Buffer.concat([dec.update(cipherBytes), dec.final()]);\n return out.toString(\"utf8\");\n}\n","import { http } from \"./helper/http\";\nimport { aesDecryptHexWithKey } from \"./crypto.js\";\n\nconst API_BASE = \"https://everybody.codes/api\";\nconst CDN_BASE = \"https://everybody-codes.b-cdn.net/assets\";\n\nenum ChallengeType {\n Event = \"event\",\n Story = \"story\",\n}\n\n/**\n * Part number for quests.\n * @public\n */\nexport type PartNumber = 1 | 2 | 3;\n\n/**\n * Client for request against the EverybodyCodes API.\n *\n * @public\n */\nexport class EverybodyCodesClient {\n private readonly cookie: string;\n private seed?: string;\n\n /**\n * Creates a new EverybodyCodesClient.\n * @param sessionCookie - Value of the `everybody-codes` cookie.\n */\n constructor(sessionCookie: string) {\n if (!sessionCookie || !sessionCookie.trim()) {\n throw new Error(\"EverybodyCodesClient: sessionCookie is required.\");\n }\n this.cookie = sessionCookie.trim();\n }\n\n private makeUrl(type: ChallengeType, ...parts: (string | number)[]): string {\n return [\n API_BASE,\n type,\n ...parts.map((p) => encodeURIComponent(String(p))),\n ].join(\"/\");\n }\n\n private cookieHeader(): HeadersInit {\n return { Cookie: `everybody-codes=${this.cookie}` };\n }\n\n private async getSeed(): Promise<string> {\n if (this.seed) return this.seed;\n\n const url = `${API_BASE}/user/me`;\n const data = await http<{ seed?: number | string }>(url, {\n headers: this.cookieHeader(),\n });\n\n const raw = data?.seed;\n const num =\n typeof raw === \"number\"\n ? raw\n : typeof raw === \"string\" && raw.trim() !== \"\"\n ? Number(raw)\n : NaN;\n\n if (!Number.isFinite(num) || num <= 0) {\n throw new Error(\n \"Invalid seed value (0). This usually means your session has expired or the 'everybody-codes' cookie is invalid. \" +\n \"Log in again at https://everybody.codes, copy the cookie value exactly (no spaces or quotes), and try again.\",\n );\n }\n\n this.seed = String(num);\n return this.seed;\n }\n\n private async getEncryptedInputs(\n event: string | number,\n quest: string | number,\n seed: string,\n ) {\n const url = `${CDN_BASE}/${encodeURIComponent(String(event))}/${encodeURIComponent(\n String(quest),\n )}/input/${encodeURIComponent(seed)}.json`;\n return await http<Record<\"1\" | \"2\" | \"3\", string>>(url, {\n headers: this.cookieHeader(),\n });\n }\n\n private async getDataInternal(\n type: ChallengeType,\n event: string | number,\n quest: string | number,\n ) {\n const seed = await this.getSeed();\n const encrypted = await this.getEncryptedInputs(event, quest, seed);\n const keys = await this.getKeys(type, event, quest);\n\n const out: Partial<Record<1 | 2 | 3, string>> = {};\n ([\"1\", \"2\", \"3\"] as const).forEach((p) => {\n const hex = encrypted[p];\n const key = keys[p];\n if (hex && key)\n out[Number(p) as 1 | 2 | 3] = aesDecryptHexWithKey(key, hex);\n });\n return out;\n }\n\n private async getKeys(\n type: ChallengeType,\n event: string | number,\n quest: string | number,\n ) {\n const url = this.makeUrl(type, event, \"quest\", quest);\n\n type KeyResponse = {\n key1?: string;\n key2?: string;\n key3?: string;\n };\n\n const data = await http<KeyResponse>(url, { headers: this.cookieHeader() });\n\n const out: Partial<Record<\"1\" | \"2\" | \"3\", string>> = {};\n for (const p of [\"1\", \"2\", \"3\"] as const) {\n const flat = data?.[`key${p}`];\n if (flat) out[p] = String(flat);\n }\n return out;\n }\n\n /**\n * Fetches data for a specific event and quest.\n * @param event - Event ID\n * @param quest - Quest ID\n * @returns Data for the event and quest\n */\n async getEventData(event: string | number, quest: string | number) {\n return this.getDataInternal(ChallengeType.Event, event, quest);\n }\n\n /**\n * Fetches data for a specific story and quest.\n * @param story - Story ID\n * @param quest - Quest ID\n * @returns Data for the story and quest\n */\n async getStoryData(story: string | number, quest: string | number) {\n return this.getDataInternal(ChallengeType.Story, story, quest);\n }\n\n /**\n * Fetches data for a specific event, quest, and part.\n * @param event - Event ID\n * @param quest - Quest ID\n * @param part - Part number (1, 2, or 3)\n * @returns Data for the event, quest, and part\n */\n async getEventPartData(\n event: string | number,\n quest: string | number,\n part: PartNumber,\n ) {\n const data = await this.getDataInternal(ChallengeType.Event, event, quest);\n return data[part];\n }\n\n /**\n * Fetches data for a specific story, quest, and part.\n * @param story - Story ID\n * @param quest - Quest ID\n * @param part - Part number (1, 2, or 3)\n * @returns Data for the story, quest, and part\n */\n async getStoryPartData(\n story: string | number,\n quest: string | number,\n part: PartNumber,\n ) {\n const data = await this.getDataInternal(ChallengeType.Story, story, quest);\n return data[part];\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mrtimeey/everybodycodes-data",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Simple client for fetching data and submitting answers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"private": false,
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"clean": "rimraf dist",
|
|
32
|
+
"docs": "typedoc",
|
|
33
|
+
"docs:clean": "rimraf docs",
|
|
34
|
+
"format": "prettier --write .",
|
|
35
|
+
"format:check": "prettier --check .",
|
|
36
|
+
"lint": "eslint . --max-warnings=0",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"dev:test": "vitest",
|
|
39
|
+
"prepublishOnly": "npm run build && npm run docs",
|
|
40
|
+
"api:extract": "npm run build && api-extractor run --local",
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"postbuild": "api-extractor run --verbose"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.39.1",
|
|
46
|
+
"@microsoft/api-extractor": "^7.54.0",
|
|
47
|
+
"@microsoft/tsdoc": "^0.15.1",
|
|
48
|
+
"@types/node": "^24.10.0",
|
|
49
|
+
"eslint": "^9.39.1",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"globals": "^16.5.0",
|
|
52
|
+
"prettier": "^3.6.2",
|
|
53
|
+
"prettier-plugin-organize-imports": "^4.3.0",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
55
|
+
"ts-node": "^10.9.2",
|
|
56
|
+
"tsup": "^8.3.0",
|
|
57
|
+
"typedoc": "^0.28.14",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"typescript-eslint": "^8.46.3",
|
|
60
|
+
"undici": "^7.16.0",
|
|
61
|
+
"vitest": "^4.0.8"
|
|
62
|
+
},
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+https://github.com/MrTimeey/everybodycodes-data.git"
|
|
66
|
+
},
|
|
67
|
+
"keywords": [
|
|
68
|
+
"everybody-codes"
|
|
69
|
+
],
|
|
70
|
+
"author": "MrTimeey",
|
|
71
|
+
"license": "MIT",
|
|
72
|
+
"bugs": {
|
|
73
|
+
"url": "https://github.com/MrTimeey/everybodycodes-data/issues"
|
|
74
|
+
},
|
|
75
|
+
"homepage": "https://github.com/MrTimeey/everybodycodes-data#readme"
|
|
76
|
+
}
|