@ix-xs/twitch.api 1.0.5 → 2.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.
package/dist/node.cjs ADDED
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ var promises = require('fs/promises');
4
+ var path = require('path');
5
+
6
+ // src/node.ts
7
+ var FileTokenStore = class {
8
+ path;
9
+ #cache;
10
+ #queue = Promise.resolve();
11
+ constructor(path$1 = "./tokens.json") {
12
+ this.path = path.resolve(path$1);
13
+ }
14
+ async get(userId) {
15
+ return (await this.#read())[userId];
16
+ }
17
+ async set(userId, token) {
18
+ const tokens = await this.#read();
19
+ tokens[userId] = token;
20
+ await this.#write();
21
+ }
22
+ async delete(userId) {
23
+ const tokens = await this.#read();
24
+ if (!(userId in tokens)) return;
25
+ delete tokens[userId];
26
+ await this.#write();
27
+ }
28
+ async list() {
29
+ return Object.keys(await this.#read());
30
+ }
31
+ #read() {
32
+ return this.#cache ??= (async () => {
33
+ try {
34
+ const parsed = JSON.parse(await promises.readFile(this.path, "utf8"));
35
+ return parsed && typeof parsed === "object" ? parsed : {};
36
+ } catch (error) {
37
+ if (error.code === "ENOENT") return {};
38
+ this.#cache = void 0;
39
+ throw error;
40
+ }
41
+ })();
42
+ }
43
+ #write() {
44
+ const run = this.#queue.then(async () => {
45
+ const temporary = `${this.path}.${process.pid}.tmp`;
46
+ await promises.mkdir(path.dirname(this.path), { recursive: true });
47
+ await promises.writeFile(temporary, `${JSON.stringify(await this.#read(), null, 2)}
48
+ `, { mode: 384 });
49
+ await promises.rename(temporary, this.path);
50
+ });
51
+ this.#queue = run.catch(() => {
52
+ });
53
+ return run;
54
+ }
55
+ };
56
+
57
+ exports.FileTokenStore = FileTokenStore;
@@ -0,0 +1,36 @@
1
+ import { T as TokenStore, U as UserToken } from './tokens-DlOL0kjk.cjs';
2
+
3
+ /**
4
+ * Persists user tokens in a JSON file. Writes are atomic (a temporary file
5
+ * renamed into place) and serialised, so a crash or two refreshes at once
6
+ * never leave a broken file behind. The file is created with owner-only
7
+ * permissions where the platform supports them.
8
+ *
9
+ * Add the file to your `.gitignore`: it holds credentials.
10
+ *
11
+ * @example
12
+ * import { Twitch } from "@ix-xs/twitch.api";
13
+ * import { FileTokenStore } from "@ix-xs/twitch.api/node";
14
+ *
15
+ * const twitch = new Twitch({
16
+ * clientId,
17
+ * clientSecret,
18
+ * tokens: new FileTokenStore("./tokens.json"),
19
+ * });
20
+ */
21
+ declare class FileTokenStore implements TokenStore {
22
+ #private;
23
+ /** Absolute path of the file. */
24
+ readonly path: string;
25
+ constructor(path?: string);
26
+ /** Returns the token saved for `userId`, if any. */
27
+ get(userId: string): Promise<UserToken | undefined>;
28
+ /** Saves or replaces the token of `userId`, then writes the file. */
29
+ set(userId: string, token: UserToken): Promise<void>;
30
+ /** Forgets the token of `userId`, then writes the file. */
31
+ delete(userId: string): Promise<void>;
32
+ /** Returns every saved user ID. */
33
+ list(): Promise<string[]>;
34
+ }
35
+
36
+ export { FileTokenStore };
package/dist/node.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { T as TokenStore, U as UserToken } from './tokens-DlOL0kjk.js';
2
+
3
+ /**
4
+ * Persists user tokens in a JSON file. Writes are atomic (a temporary file
5
+ * renamed into place) and serialised, so a crash or two refreshes at once
6
+ * never leave a broken file behind. The file is created with owner-only
7
+ * permissions where the platform supports them.
8
+ *
9
+ * Add the file to your `.gitignore`: it holds credentials.
10
+ *
11
+ * @example
12
+ * import { Twitch } from "@ix-xs/twitch.api";
13
+ * import { FileTokenStore } from "@ix-xs/twitch.api/node";
14
+ *
15
+ * const twitch = new Twitch({
16
+ * clientId,
17
+ * clientSecret,
18
+ * tokens: new FileTokenStore("./tokens.json"),
19
+ * });
20
+ */
21
+ declare class FileTokenStore implements TokenStore {
22
+ #private;
23
+ /** Absolute path of the file. */
24
+ readonly path: string;
25
+ constructor(path?: string);
26
+ /** Returns the token saved for `userId`, if any. */
27
+ get(userId: string): Promise<UserToken | undefined>;
28
+ /** Saves or replaces the token of `userId`, then writes the file. */
29
+ set(userId: string, token: UserToken): Promise<void>;
30
+ /** Forgets the token of `userId`, then writes the file. */
31
+ delete(userId: string): Promise<void>;
32
+ /** Returns every saved user ID. */
33
+ list(): Promise<string[]>;
34
+ }
35
+
36
+ export { FileTokenStore };
package/dist/node.js ADDED
@@ -0,0 +1,55 @@
1
+ import { readFile, mkdir, writeFile, rename } from 'fs/promises';
2
+ import { resolve, dirname } from 'path';
3
+
4
+ // src/node.ts
5
+ var FileTokenStore = class {
6
+ path;
7
+ #cache;
8
+ #queue = Promise.resolve();
9
+ constructor(path = "./tokens.json") {
10
+ this.path = resolve(path);
11
+ }
12
+ async get(userId) {
13
+ return (await this.#read())[userId];
14
+ }
15
+ async set(userId, token) {
16
+ const tokens = await this.#read();
17
+ tokens[userId] = token;
18
+ await this.#write();
19
+ }
20
+ async delete(userId) {
21
+ const tokens = await this.#read();
22
+ if (!(userId in tokens)) return;
23
+ delete tokens[userId];
24
+ await this.#write();
25
+ }
26
+ async list() {
27
+ return Object.keys(await this.#read());
28
+ }
29
+ #read() {
30
+ return this.#cache ??= (async () => {
31
+ try {
32
+ const parsed = JSON.parse(await readFile(this.path, "utf8"));
33
+ return parsed && typeof parsed === "object" ? parsed : {};
34
+ } catch (error) {
35
+ if (error.code === "ENOENT") return {};
36
+ this.#cache = void 0;
37
+ throw error;
38
+ }
39
+ })();
40
+ }
41
+ #write() {
42
+ const run = this.#queue.then(async () => {
43
+ const temporary = `${this.path}.${process.pid}.tmp`;
44
+ await mkdir(dirname(this.path), { recursive: true });
45
+ await writeFile(temporary, `${JSON.stringify(await this.#read(), null, 2)}
46
+ `, { mode: 384 });
47
+ await rename(temporary, this.path);
48
+ });
49
+ this.#queue = run.catch(() => {
50
+ });
51
+ return run;
52
+ }
53
+ };
54
+
55
+ export { FileTokenStore };
@@ -0,0 +1,97 @@
1
+ /** A value or a promise of it. */
2
+ type Awaitable<T> = T | PromiseLike<T>;
3
+ type Listener<A extends unknown[]> = (...args: A) => void;
4
+ /** Minimal typed event emitter. A listener that throws never breaks the emitter. */
5
+ declare class Emitter<Events extends {
6
+ [K in keyof Events]: unknown[];
7
+ }> {
8
+ #private;
9
+ /** Registers a listener. Returns a function that removes it. */
10
+ on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
11
+ /** Registers a listener that runs once. */
12
+ once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
13
+ /** Removes a listener. */
14
+ off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
15
+ /** Calls every listener of `event`. Returns whether there was at least one. */
16
+ emit<K extends keyof Events>(event: K, ...args: Events[K]): boolean;
17
+ /** Whether `event` has at least one listener. */
18
+ has(event: keyof Events): boolean;
19
+ /** Removes every listener. */
20
+ clear(): void;
21
+ }
22
+
23
+ /** A user access token and what is known about it. Plain JSON, safe to persist. */
24
+ interface UserToken {
25
+ /** ID of the user who granted the token. */
26
+ userId: string;
27
+ /** Login name of that user, when known. */
28
+ login?: string;
29
+ /** The access token itself. */
30
+ accessToken: string;
31
+ /** Refresh token. Without it the access token cannot be renewed once it expires. */
32
+ refreshToken?: string;
33
+ /** Scopes granted to the token. */
34
+ scopes: string[];
35
+ /** Epoch milliseconds at which the access token expires. Absent when unknown. */
36
+ expiresAt?: number;
37
+ }
38
+ /** What you hand to `twitch.auth.addUser()`. Only the access token is mandatory. */
39
+ interface UserTokenInput {
40
+ /** The access token. */
41
+ accessToken: string;
42
+ /** Refresh token, needed for automatic renewal. */
43
+ refreshToken?: string;
44
+ /** Scopes of the token. Looked up from Twitch when omitted. */
45
+ scopes?: readonly string[];
46
+ /** Expiry as epoch milliseconds or a `Date`. */
47
+ expiresAt?: number | Date;
48
+ /** Lifetime in seconds, as returned by Twitch. Used when `expiresAt` is absent. */
49
+ expiresIn?: number;
50
+ /** Owner of the token. Looked up from Twitch when omitted. */
51
+ userId?: string;
52
+ /** Login of the owner. */
53
+ login?: string;
54
+ }
55
+ /** Result of validating a token against Twitch. */
56
+ interface TokenInfo {
57
+ /** Client ID the token was issued to. */
58
+ clientId: string;
59
+ /** Owner of the token. `null` for an app access token. */
60
+ userId: string | null;
61
+ /** Login of the owner. `null` for an app access token. */
62
+ login: string | null;
63
+ /** Scopes granted to the token. */
64
+ scopes: string[];
65
+ /** Seconds left before the token expires. `0` means it never expires. */
66
+ expiresIn: number;
67
+ /** Epoch milliseconds at which the token expires. Absent when it never expires. */
68
+ expiresAt?: number;
69
+ }
70
+ /**
71
+ * Where user tokens are persisted between runs. Implement it on top of a
72
+ * database, a KV store or a file. Every method may be sync or async.
73
+ */
74
+ interface TokenStore {
75
+ /** Returns the token saved for `userId`, if any. */
76
+ get(userId: string): Awaitable<UserToken | undefined>;
77
+ /** Saves or replaces the token of `userId`. Called after every refresh. */
78
+ set(userId: string, token: UserToken): Awaitable<void>;
79
+ /** Forgets the token of `userId`. */
80
+ delete(userId: string): Awaitable<void>;
81
+ /** Returns every saved user ID. When present, tokens are restored on first use. */
82
+ list?(): Awaitable<string[]>;
83
+ }
84
+ /** Keeps tokens in memory. The default store: nothing survives a restart. */
85
+ declare class MemoryTokenStore implements TokenStore {
86
+ #private;
87
+ /** Returns the token saved for `userId`, if any. */
88
+ get(userId: string): UserToken | undefined;
89
+ /** Saves or replaces the token of `userId`. */
90
+ set(userId: string, token: UserToken): void;
91
+ /** Forgets the token of `userId`. */
92
+ delete(userId: string): void;
93
+ /** Returns every saved user ID. */
94
+ list(): string[];
95
+ }
96
+
97
+ export { type Awaitable as A, Emitter as E, MemoryTokenStore as M, type TokenStore as T, type UserToken as U, type UserTokenInput as a, type TokenInfo as b };
@@ -0,0 +1,97 @@
1
+ /** A value or a promise of it. */
2
+ type Awaitable<T> = T | PromiseLike<T>;
3
+ type Listener<A extends unknown[]> = (...args: A) => void;
4
+ /** Minimal typed event emitter. A listener that throws never breaks the emitter. */
5
+ declare class Emitter<Events extends {
6
+ [K in keyof Events]: unknown[];
7
+ }> {
8
+ #private;
9
+ /** Registers a listener. Returns a function that removes it. */
10
+ on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
11
+ /** Registers a listener that runs once. */
12
+ once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
13
+ /** Removes a listener. */
14
+ off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
15
+ /** Calls every listener of `event`. Returns whether there was at least one. */
16
+ emit<K extends keyof Events>(event: K, ...args: Events[K]): boolean;
17
+ /** Whether `event` has at least one listener. */
18
+ has(event: keyof Events): boolean;
19
+ /** Removes every listener. */
20
+ clear(): void;
21
+ }
22
+
23
+ /** A user access token and what is known about it. Plain JSON, safe to persist. */
24
+ interface UserToken {
25
+ /** ID of the user who granted the token. */
26
+ userId: string;
27
+ /** Login name of that user, when known. */
28
+ login?: string;
29
+ /** The access token itself. */
30
+ accessToken: string;
31
+ /** Refresh token. Without it the access token cannot be renewed once it expires. */
32
+ refreshToken?: string;
33
+ /** Scopes granted to the token. */
34
+ scopes: string[];
35
+ /** Epoch milliseconds at which the access token expires. Absent when unknown. */
36
+ expiresAt?: number;
37
+ }
38
+ /** What you hand to `twitch.auth.addUser()`. Only the access token is mandatory. */
39
+ interface UserTokenInput {
40
+ /** The access token. */
41
+ accessToken: string;
42
+ /** Refresh token, needed for automatic renewal. */
43
+ refreshToken?: string;
44
+ /** Scopes of the token. Looked up from Twitch when omitted. */
45
+ scopes?: readonly string[];
46
+ /** Expiry as epoch milliseconds or a `Date`. */
47
+ expiresAt?: number | Date;
48
+ /** Lifetime in seconds, as returned by Twitch. Used when `expiresAt` is absent. */
49
+ expiresIn?: number;
50
+ /** Owner of the token. Looked up from Twitch when omitted. */
51
+ userId?: string;
52
+ /** Login of the owner. */
53
+ login?: string;
54
+ }
55
+ /** Result of validating a token against Twitch. */
56
+ interface TokenInfo {
57
+ /** Client ID the token was issued to. */
58
+ clientId: string;
59
+ /** Owner of the token. `null` for an app access token. */
60
+ userId: string | null;
61
+ /** Login of the owner. `null` for an app access token. */
62
+ login: string | null;
63
+ /** Scopes granted to the token. */
64
+ scopes: string[];
65
+ /** Seconds left before the token expires. `0` means it never expires. */
66
+ expiresIn: number;
67
+ /** Epoch milliseconds at which the token expires. Absent when it never expires. */
68
+ expiresAt?: number;
69
+ }
70
+ /**
71
+ * Where user tokens are persisted between runs. Implement it on top of a
72
+ * database, a KV store or a file. Every method may be sync or async.
73
+ */
74
+ interface TokenStore {
75
+ /** Returns the token saved for `userId`, if any. */
76
+ get(userId: string): Awaitable<UserToken | undefined>;
77
+ /** Saves or replaces the token of `userId`. Called after every refresh. */
78
+ set(userId: string, token: UserToken): Awaitable<void>;
79
+ /** Forgets the token of `userId`. */
80
+ delete(userId: string): Awaitable<void>;
81
+ /** Returns every saved user ID. When present, tokens are restored on first use. */
82
+ list?(): Awaitable<string[]>;
83
+ }
84
+ /** Keeps tokens in memory. The default store: nothing survives a restart. */
85
+ declare class MemoryTokenStore implements TokenStore {
86
+ #private;
87
+ /** Returns the token saved for `userId`, if any. */
88
+ get(userId: string): UserToken | undefined;
89
+ /** Saves or replaces the token of `userId`. */
90
+ set(userId: string, token: UserToken): void;
91
+ /** Forgets the token of `userId`. */
92
+ delete(userId: string): void;
93
+ /** Returns every saved user ID. */
94
+ list(): string[];
95
+ }
96
+
97
+ export { type Awaitable as A, Emitter as E, MemoryTokenStore as M, type TokenStore as T, type UserToken as U, type UserTokenInput as a, type TokenInfo as b };
package/package.json CHANGED
@@ -1,20 +1,101 @@
1
- {
2
- "name": "@ix-xs/twitch.api",
3
- "version": "1.0.5",
4
- "description": "Twitch API JS",
5
- "main": "twitchapi.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "github.com/ix-xs/twitch.api"
12
- },
13
- "keywords": [
14
- "Twitch",
15
- "API",
16
- "JS"
17
- ],
18
- "author": "ix-xs",
19
- "license": "ISC"
20
- }
1
+ {
2
+ "name": "@ix-xs/twitch.api",
3
+ "version": "2.0.1",
4
+ "description": "The whole Twitch API, typed. A zero-dependency client for JavaScript and TypeScript: all 149 Helix endpoints and 83 EventSub events, with tokens, rate limits, pagination and reconnects handled for you.",
5
+ "keywords": [
6
+ "twitch",
7
+ "twitch-api",
8
+ "helix",
9
+ "eventsub",
10
+ "twitch-bot",
11
+ "chat-bot",
12
+ "oauth",
13
+ "websocket",
14
+ "webhook",
15
+ "typescript",
16
+ "esm",
17
+ "zero-dependency",
18
+ "api-client"
19
+ ],
20
+ "author": "ix-xs.dev <contact@ix-xs.dev>",
21
+ "license": "MIT",
22
+ "homepage": "https://ix-xs.github.io/twitch.api/",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/ix-xs/twitch.api.git"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/ix-xs/twitch.api/issues"
29
+ },
30
+ "type": "module",
31
+ "sideEffects": false,
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "import": {
38
+ "types": "./dist/index.d.ts",
39
+ "default": "./dist/index.js"
40
+ },
41
+ "require": {
42
+ "types": "./dist/index.d.cts",
43
+ "default": "./dist/index.cjs"
44
+ }
45
+ },
46
+ "./node": {
47
+ "import": {
48
+ "types": "./dist/node.d.ts",
49
+ "default": "./dist/node.js"
50
+ },
51
+ "require": {
52
+ "types": "./dist/node.d.cts",
53
+ "default": "./dist/node.cjs"
54
+ }
55
+ },
56
+ "./package.json": "./package.json"
57
+ },
58
+ "typesVersions": {
59
+ "*": {
60
+ "node": [
61
+ "./dist/node.d.ts"
62
+ ]
63
+ }
64
+ },
65
+ "files": [
66
+ "dist",
67
+ "README.md",
68
+ "CHANGELOG.md",
69
+ "LICENSE"
70
+ ],
71
+ "engines": {
72
+ "node": ">=22.0.0"
73
+ },
74
+ "scripts": {
75
+ "build": "tsup",
76
+ "dev": "tsup --watch",
77
+ "typecheck": "tsc --noEmit",
78
+ "test": "vitest run",
79
+ "test:watch": "vitest",
80
+ "test:types": "tsc -p tsconfig.test.json",
81
+ "attw": "attw --pack .",
82
+ "check": "npm run typecheck && npm run test:types && npm run check:jsdoc && npm run build && npm test && npm run docs:check",
83
+ "docs": "npm --prefix docs install && npm --prefix docs run dev",
84
+ "docs:build": "npm --prefix docs install && npm --prefix docs run build",
85
+ "docs:reference": "node docs/scripts/reference.mjs",
86
+ "docs:check": "node docs/scripts/reference.mjs --check && node docs/scripts/check-snippets.mjs",
87
+ "prepublishOnly": "npm run check",
88
+ "check:jsdoc": "node scripts/check-jsdoc.mjs"
89
+ },
90
+ "devDependencies": {
91
+ "@arethetypeswrong/cli": "^0.18.0",
92
+ "@types/node": "^22.10.0",
93
+ "tsup": "^8.3.5",
94
+ "tsx": "^4.23.15",
95
+ "typescript": "^6.0.3",
96
+ "vitest": "^5.0.3"
97
+ },
98
+ "publishConfig": {
99
+ "access": "public"
100
+ }
101
+ }
package/Token.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "access_token": "",
3
- "expires_in": 0,
4
- "token_type": "",
5
- "created_at": 0
6
- }