@tapcolorapp/api 1.0.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/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @tapcolorapp/api
2
+
3
+ Official TypeScript/JavaScript client for the [TapColor Developer API](https://developer.tapcolor.app) —
4
+ browse coloring **categories** and **collections** over a simple REST API.
5
+
6
+ Zero dependencies. Works in Node 18+, Deno, Bun and modern browsers.
7
+
8
+ - 📚 Docs & live explorer: <https://developer.tapcolor.app>
9
+ - 🔑 Request an API key: <https://tapcolor.app/contact/>
10
+ - 🧩 OpenAPI spec: <https://api.tapcolor.app/openapi.json>
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ # npm
16
+ npm install @tapcolorapp/api
17
+
18
+ # JSR (Deno / Node / Bun)
19
+ deno add jsr:@tapcolorapp/api
20
+ npx jsr add @tapcolorapp/api
21
+ ```
22
+
23
+ ## Quickstart
24
+
25
+ ```ts
26
+ import { TapColor } from "@tapcolorapp/api";
27
+
28
+ const tc = new TapColor({ apiKey: "YOUR_API_KEY" });
29
+
30
+ // List categories
31
+ const cats = await tc.categories();
32
+ console.log(cats.total, "categories");
33
+
34
+ // List collections (filter + search + pagination)
35
+ const page = await tc.coloringPages({ category: "animals", limit: 20 });
36
+ page.items.forEach((c) => console.log(c.subtopic, "->", c.url));
37
+
38
+ // Iterate EVERY collection, auto-paginated
39
+ for await (const c of tc.allColoringPages({ category: "animals" })) {
40
+ console.log(c.subtopicSlug);
41
+ }
42
+ ```
43
+
44
+ ## API
45
+
46
+ ### `new TapColor(options)`
47
+
48
+ | Option | Type | Default | Notes |
49
+ | --------- | ------------------ | ----------------------------- | ----------------------------------- |
50
+ | `apiKey` | `string` | — | Required. Sent as the `api-key` header. |
51
+ | `baseUrl` | `string` | `https://api.tapcolor.app` | Override for testing/staging. |
52
+ | `fetch` | `typeof fetch` | global `fetch` | Custom fetch for Node <18 / tests. |
53
+
54
+ ### `tc.categories(): Promise<CategoriesResponse>`
55
+
56
+ Lists all 25 categories.
57
+
58
+ ### `tc.coloringPages(params?): Promise<ColoringPagesResponse>`
59
+
60
+ | Param | Type | Description |
61
+ | ---------- | -------- | -------------------------------------------------- |
62
+ | `category` | `string` | Category slug, e.g. `animals`. |
63
+ | `q` | `string` | Search collection names (partial, case-insensitive). |
64
+ | `limit` | `number` | 1–100 (default 20). |
65
+ | `offset` | `number` | Pagination offset. |
66
+
67
+ ### `tc.allColoringPages(params?): AsyncGenerator<Collection>`
68
+
69
+ Auto-paginates and yields every matching collection.
70
+
71
+ ## Errors
72
+
73
+ Any non-2xx response throws a `TapColorError` with `status` and `body`:
74
+
75
+ ```ts
76
+ import { TapColor, TapColorError } from "@tapcolorapp/api";
77
+
78
+ try {
79
+ await new TapColor({ apiKey: "bad" }).categories();
80
+ } catch (err) {
81
+ if (err instanceof TapColorError) {
82
+ console.error(err.status, err.message); // 401 "invalid or missing api-key"
83
+ }
84
+ }
85
+ ```
86
+
87
+ Rate limit: **120 requests / 10 minutes per key** → `429`.
88
+
89
+ ## License
90
+
91
+ MIT © TapColor
package/dist/index.cjs ADDED
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TapColor: () => TapColor,
24
+ TapColorError: () => TapColorError,
25
+ default: () => index_default
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var TapColorError = class extends Error {
29
+ status;
30
+ body;
31
+ constructor(status, message, body) {
32
+ super(message);
33
+ this.name = "TapColorError";
34
+ this.status = status;
35
+ this.body = body;
36
+ }
37
+ };
38
+ var DEFAULT_BASE = "https://api.tapcolor.app";
39
+ var TapColor = class {
40
+ #apiKey;
41
+ #baseUrl;
42
+ #fetch;
43
+ constructor(options) {
44
+ if (!options || !options.apiKey) {
45
+ throw new Error("TapColor: `apiKey` is required");
46
+ }
47
+ this.#apiKey = options.apiKey;
48
+ this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE).replace(/\/+$/, "");
49
+ const f = options.fetch ?? globalThis.fetch;
50
+ if (typeof f !== "function") {
51
+ throw new Error("TapColor: no global `fetch` found \u2014 pass options.fetch");
52
+ }
53
+ this.#fetch = f;
54
+ }
55
+ async #get(path, params) {
56
+ const url = new URL(this.#baseUrl + path);
57
+ if (params) {
58
+ for (const [k, v] of Object.entries(params)) {
59
+ if (v !== void 0 && v !== null && v !== "") {
60
+ url.searchParams.set(k, String(v));
61
+ }
62
+ }
63
+ }
64
+ const res = await this.#fetch(url.toString(), {
65
+ headers: { "api-key": this.#apiKey }
66
+ });
67
+ const text = await res.text();
68
+ let data = text;
69
+ try {
70
+ data = JSON.parse(text);
71
+ } catch {
72
+ }
73
+ if (!res.ok) {
74
+ const msg = data && typeof data === "object" && "error" in data ? String(data.error) : res.statusText || `HTTP ${res.status}`;
75
+ throw new TapColorError(res.status, msg, data);
76
+ }
77
+ return data;
78
+ }
79
+ /** List all 25 categories with collection and page counts. */
80
+ categories() {
81
+ return this.#get("/v1/categories");
82
+ }
83
+ /** List collections with optional filter, search and pagination. */
84
+ coloringPages(params = {}) {
85
+ return this.#get(
86
+ "/v1/coloring-pages",
87
+ params
88
+ );
89
+ }
90
+ /**
91
+ * Async-iterate every collection matching `params`, auto-paginating with
92
+ * `limit`/`offset` until the full result set is exhausted.
93
+ *
94
+ * ```ts
95
+ * for await (const c of tc.allColoringPages({ category: "animals" })) {
96
+ * console.log(c.subtopic, c.url);
97
+ * }
98
+ * ```
99
+ */
100
+ async *allColoringPages(params = {}) {
101
+ const limit = params.limit ?? 100;
102
+ let offset = params.offset ?? 0;
103
+ for (; ; ) {
104
+ const page = await this.coloringPages({ ...params, limit, offset });
105
+ for (const item of page.items) yield item;
106
+ offset += page.count;
107
+ if (page.count === 0 || offset >= page.total) break;
108
+ }
109
+ }
110
+ };
111
+ var index_default = TapColor;
112
+ // Annotate the CommonJS export names for ESM import in node:
113
+ 0 && (module.exports = {
114
+ TapColor,
115
+ TapColorError
116
+ });
@@ -0,0 +1,99 @@
1
+ /**
2
+ * TapColor Developer API — official TypeScript/JavaScript client.
3
+ *
4
+ * Docs: https://developer.tapcolor.app
5
+ * API: https://api.tapcolor.app
6
+ * Source: https://github.com/tapcolorapp
7
+ *
8
+ * Zero dependencies. Works in Node 18+, Deno, Bun and modern browsers
9
+ * (uses the global `fetch`).
10
+ */
11
+ /** A coloring category (one of the 25 top-level categories). */
12
+ interface Category {
13
+ /** Human-readable name, e.g. "Animals". */
14
+ name: string;
15
+ /** Slug — use it as the `category` filter, e.g. "animals". */
16
+ slug: string;
17
+ /** Number of collections in the category. */
18
+ subtopics: number;
19
+ /** Total coloring pages across the category. */
20
+ pages: number;
21
+ /** Public category page on tapcolor.app. */
22
+ url: string;
23
+ }
24
+ /** A coloring collection (subtopic). */
25
+ interface Collection {
26
+ category: string;
27
+ categorySlug: string;
28
+ subtopic: string;
29
+ subtopicSlug: string;
30
+ count: number;
31
+ url: string;
32
+ /** Cover thumbnail URL; may be null when no cover is set. */
33
+ image: string | null;
34
+ }
35
+ interface CategoriesResponse {
36
+ total: number;
37
+ items: Category[];
38
+ }
39
+ interface ColoringPagesResponse {
40
+ total: number;
41
+ limit: number;
42
+ offset: number;
43
+ count: number;
44
+ items: Collection[];
45
+ }
46
+ interface ColoringPagesParams {
47
+ /** Filter by category slug (see {@link Category.slug}). */
48
+ category?: string;
49
+ /** Search collection names (partial, case-insensitive). */
50
+ q?: string;
51
+ /** Page size, 1–100. Defaults to 20 server-side. */
52
+ limit?: number;
53
+ /** Skip the first N items for pagination. */
54
+ offset?: number;
55
+ }
56
+ interface TapColorOptions {
57
+ /** API key issued by TapColor (sent as the `api-key` header). */
58
+ apiKey: string;
59
+ /** Override the base URL. Defaults to `https://api.tapcolor.app`. */
60
+ baseUrl?: string;
61
+ /** Custom fetch implementation (e.g. for Node <18 or testing). */
62
+ fetch?: typeof fetch;
63
+ }
64
+ /** Thrown on any non-2xx response. */
65
+ declare class TapColorError extends Error {
66
+ readonly status: number;
67
+ readonly body: unknown;
68
+ constructor(status: number, message: string, body?: unknown);
69
+ }
70
+ /**
71
+ * TapColor API client.
72
+ *
73
+ * ```ts
74
+ * import { TapColor } from "@tapcolorapp/api";
75
+ * const tc = new TapColor({ apiKey: "YOUR_API_KEY" });
76
+ * const { items } = await tc.coloringPages({ category: "animals", limit: 20 });
77
+ * ```
78
+ */
79
+ declare class TapColor {
80
+ #private;
81
+ constructor(options: TapColorOptions);
82
+ /** List all 25 categories with collection and page counts. */
83
+ categories(): Promise<CategoriesResponse>;
84
+ /** List collections with optional filter, search and pagination. */
85
+ coloringPages(params?: ColoringPagesParams): Promise<ColoringPagesResponse>;
86
+ /**
87
+ * Async-iterate every collection matching `params`, auto-paginating with
88
+ * `limit`/`offset` until the full result set is exhausted.
89
+ *
90
+ * ```ts
91
+ * for await (const c of tc.allColoringPages({ category: "animals" })) {
92
+ * console.log(c.subtopic, c.url);
93
+ * }
94
+ * ```
95
+ */
96
+ allColoringPages(params?: ColoringPagesParams): AsyncGenerator<Collection, void, unknown>;
97
+ }
98
+
99
+ export { type CategoriesResponse, type Category, type Collection, type ColoringPagesParams, type ColoringPagesResponse, TapColor, TapColorError, type TapColorOptions, TapColor as default };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * TapColor Developer API — official TypeScript/JavaScript client.
3
+ *
4
+ * Docs: https://developer.tapcolor.app
5
+ * API: https://api.tapcolor.app
6
+ * Source: https://github.com/tapcolorapp
7
+ *
8
+ * Zero dependencies. Works in Node 18+, Deno, Bun and modern browsers
9
+ * (uses the global `fetch`).
10
+ */
11
+ /** A coloring category (one of the 25 top-level categories). */
12
+ interface Category {
13
+ /** Human-readable name, e.g. "Animals". */
14
+ name: string;
15
+ /** Slug — use it as the `category` filter, e.g. "animals". */
16
+ slug: string;
17
+ /** Number of collections in the category. */
18
+ subtopics: number;
19
+ /** Total coloring pages across the category. */
20
+ pages: number;
21
+ /** Public category page on tapcolor.app. */
22
+ url: string;
23
+ }
24
+ /** A coloring collection (subtopic). */
25
+ interface Collection {
26
+ category: string;
27
+ categorySlug: string;
28
+ subtopic: string;
29
+ subtopicSlug: string;
30
+ count: number;
31
+ url: string;
32
+ /** Cover thumbnail URL; may be null when no cover is set. */
33
+ image: string | null;
34
+ }
35
+ interface CategoriesResponse {
36
+ total: number;
37
+ items: Category[];
38
+ }
39
+ interface ColoringPagesResponse {
40
+ total: number;
41
+ limit: number;
42
+ offset: number;
43
+ count: number;
44
+ items: Collection[];
45
+ }
46
+ interface ColoringPagesParams {
47
+ /** Filter by category slug (see {@link Category.slug}). */
48
+ category?: string;
49
+ /** Search collection names (partial, case-insensitive). */
50
+ q?: string;
51
+ /** Page size, 1–100. Defaults to 20 server-side. */
52
+ limit?: number;
53
+ /** Skip the first N items for pagination. */
54
+ offset?: number;
55
+ }
56
+ interface TapColorOptions {
57
+ /** API key issued by TapColor (sent as the `api-key` header). */
58
+ apiKey: string;
59
+ /** Override the base URL. Defaults to `https://api.tapcolor.app`. */
60
+ baseUrl?: string;
61
+ /** Custom fetch implementation (e.g. for Node <18 or testing). */
62
+ fetch?: typeof fetch;
63
+ }
64
+ /** Thrown on any non-2xx response. */
65
+ declare class TapColorError extends Error {
66
+ readonly status: number;
67
+ readonly body: unknown;
68
+ constructor(status: number, message: string, body?: unknown);
69
+ }
70
+ /**
71
+ * TapColor API client.
72
+ *
73
+ * ```ts
74
+ * import { TapColor } from "@tapcolorapp/api";
75
+ * const tc = new TapColor({ apiKey: "YOUR_API_KEY" });
76
+ * const { items } = await tc.coloringPages({ category: "animals", limit: 20 });
77
+ * ```
78
+ */
79
+ declare class TapColor {
80
+ #private;
81
+ constructor(options: TapColorOptions);
82
+ /** List all 25 categories with collection and page counts. */
83
+ categories(): Promise<CategoriesResponse>;
84
+ /** List collections with optional filter, search and pagination. */
85
+ coloringPages(params?: ColoringPagesParams): Promise<ColoringPagesResponse>;
86
+ /**
87
+ * Async-iterate every collection matching `params`, auto-paginating with
88
+ * `limit`/`offset` until the full result set is exhausted.
89
+ *
90
+ * ```ts
91
+ * for await (const c of tc.allColoringPages({ category: "animals" })) {
92
+ * console.log(c.subtopic, c.url);
93
+ * }
94
+ * ```
95
+ */
96
+ allColoringPages(params?: ColoringPagesParams): AsyncGenerator<Collection, void, unknown>;
97
+ }
98
+
99
+ export { type CategoriesResponse, type Category, type Collection, type ColoringPagesParams, type ColoringPagesResponse, TapColor, TapColorError, type TapColorOptions, TapColor as default };
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ // src/index.ts
2
+ var TapColorError = class extends Error {
3
+ status;
4
+ body;
5
+ constructor(status, message, body) {
6
+ super(message);
7
+ this.name = "TapColorError";
8
+ this.status = status;
9
+ this.body = body;
10
+ }
11
+ };
12
+ var DEFAULT_BASE = "https://api.tapcolor.app";
13
+ var TapColor = class {
14
+ #apiKey;
15
+ #baseUrl;
16
+ #fetch;
17
+ constructor(options) {
18
+ if (!options || !options.apiKey) {
19
+ throw new Error("TapColor: `apiKey` is required");
20
+ }
21
+ this.#apiKey = options.apiKey;
22
+ this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE).replace(/\/+$/, "");
23
+ const f = options.fetch ?? globalThis.fetch;
24
+ if (typeof f !== "function") {
25
+ throw new Error("TapColor: no global `fetch` found \u2014 pass options.fetch");
26
+ }
27
+ this.#fetch = f;
28
+ }
29
+ async #get(path, params) {
30
+ const url = new URL(this.#baseUrl + path);
31
+ if (params) {
32
+ for (const [k, v] of Object.entries(params)) {
33
+ if (v !== void 0 && v !== null && v !== "") {
34
+ url.searchParams.set(k, String(v));
35
+ }
36
+ }
37
+ }
38
+ const res = await this.#fetch(url.toString(), {
39
+ headers: { "api-key": this.#apiKey }
40
+ });
41
+ const text = await res.text();
42
+ let data = text;
43
+ try {
44
+ data = JSON.parse(text);
45
+ } catch {
46
+ }
47
+ if (!res.ok) {
48
+ const msg = data && typeof data === "object" && "error" in data ? String(data.error) : res.statusText || `HTTP ${res.status}`;
49
+ throw new TapColorError(res.status, msg, data);
50
+ }
51
+ return data;
52
+ }
53
+ /** List all 25 categories with collection and page counts. */
54
+ categories() {
55
+ return this.#get("/v1/categories");
56
+ }
57
+ /** List collections with optional filter, search and pagination. */
58
+ coloringPages(params = {}) {
59
+ return this.#get(
60
+ "/v1/coloring-pages",
61
+ params
62
+ );
63
+ }
64
+ /**
65
+ * Async-iterate every collection matching `params`, auto-paginating with
66
+ * `limit`/`offset` until the full result set is exhausted.
67
+ *
68
+ * ```ts
69
+ * for await (const c of tc.allColoringPages({ category: "animals" })) {
70
+ * console.log(c.subtopic, c.url);
71
+ * }
72
+ * ```
73
+ */
74
+ async *allColoringPages(params = {}) {
75
+ const limit = params.limit ?? 100;
76
+ let offset = params.offset ?? 0;
77
+ for (; ; ) {
78
+ const page = await this.coloringPages({ ...params, limit, offset });
79
+ for (const item of page.items) yield item;
80
+ offset += page.count;
81
+ if (page.count === 0 || offset >= page.total) break;
82
+ }
83
+ }
84
+ };
85
+ var index_default = TapColor;
86
+ export {
87
+ TapColor,
88
+ TapColorError,
89
+ index_default as default
90
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@tapcolorapp/api",
3
+ "version": "1.0.0",
4
+ "description": "Official TypeScript/JavaScript client for the TapColor Developer API — browse coloring categories and collections.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": ["dist", "README.md"],
17
+ "sideEffects": false,
18
+ "engines": { "node": ">=18" },
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "tapcolor",
25
+ "coloring",
26
+ "coloring-pages",
27
+ "api",
28
+ "sdk",
29
+ "rest",
30
+ "kids",
31
+ "client"
32
+ ],
33
+ "homepage": "https://developer.tapcolor.app",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/tapcolorapp/tapcolor-js.git"
37
+ },
38
+ "bugs": { "url": "https://tapcolor.app/contact/" },
39
+ "author": "TapColor",
40
+ "license": "MIT",
41
+ "publishConfig": { "access": "public" },
42
+ "devDependencies": {
43
+ "tsup": "^8.3.0",
44
+ "typescript": "^5.6.0"
45
+ }
46
+ }