optcg-db 0.1.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.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Database connection pool using pg.
3
+ * Shared by the scraper, price fetcher, and CLI.
4
+ */
5
+ import "dotenv/config";
6
+ import pg from "pg";
7
+ export interface DbConfig {
8
+ host: string;
9
+ port: number;
10
+ user: string;
11
+ password: string;
12
+ database: string;
13
+ ssl: boolean;
14
+ }
15
+ export declare function getPool(config?: DbConfig): pg.Pool;
16
+ export declare function query<T extends pg.QueryResultRow = pg.QueryResultRow>(text: string, params?: unknown[]): Promise<pg.QueryResult<T>>;
17
+ export declare function closePool(): Promise<void>;
18
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,IAAI,CAAC;AAOpB,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd;AAuBD,wBAAgB,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,EAAE,CAAC,IAAI,CAoBlD;AAED,wBAAsB,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,cAAc,EACzE,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAG5B;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAM/C"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Database connection pool using pg.
3
+ * Shared by the scraper, price fetcher, and CLI.
4
+ */
5
+ import "dotenv/config";
6
+ import pg from "pg";
7
+ import { logger } from "../shared/logger.js";
8
+ const { Pool } = pg;
9
+ let pool = null;
10
+ /**
11
+ * Build DB config from environment variables.
12
+ * Only requires the DB_* vars — no S3/Discord/ECS needed.
13
+ */
14
+ function dbConfigFromEnv() {
15
+ const host = process.env.DB_HOST;
16
+ const user = process.env.DB_USER;
17
+ const password = process.env.DB_PASSWORD;
18
+ if (!host || !user || !password) {
19
+ throw new Error("Missing required DB env vars: DB_HOST, DB_USER, DB_PASSWORD");
20
+ }
21
+ return {
22
+ host,
23
+ port: parseInt(process.env.DB_PORT ?? "5432", 10),
24
+ user,
25
+ password,
26
+ database: process.env.DB_NAME ?? "optcg",
27
+ ssl: (process.env.DB_SSL ?? "true") === "true",
28
+ };
29
+ }
30
+ export function getPool(config) {
31
+ if (!pool) {
32
+ const c = config ?? dbConfigFromEnv();
33
+ pool = new Pool({
34
+ host: c.host,
35
+ port: c.port,
36
+ user: c.user,
37
+ password: c.password,
38
+ database: c.database,
39
+ ssl: c.ssl ? { rejectUnauthorized: false } : false,
40
+ max: 10,
41
+ idleTimeoutMillis: 30000,
42
+ connectionTimeoutMillis: 5000,
43
+ });
44
+ pool.on("error", (err) => {
45
+ logger.error("Unexpected pool error", { error: err.message });
46
+ });
47
+ }
48
+ return pool;
49
+ }
50
+ export async function query(text, params) {
51
+ const p = getPool();
52
+ return p.query(text, params);
53
+ }
54
+ export async function closePool() {
55
+ if (pool) {
56
+ await pool.end();
57
+ pool = null;
58
+ logger.info("Database pool closed");
59
+ }
60
+ }
61
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AAEpB,IAAI,IAAI,GAAmB,IAAI,CAAC;AAWhC;;;GAGG;AACH,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACzC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,EAAE,EAAE,CAAC;QACjD,IAAI;QACJ,QAAQ;QACR,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO;QACxC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,MAAM;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,MAAiB;IACvC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,CAAC,GAAG,MAAM,IAAI,eAAe,EAAE,CAAC;QACtC,IAAI,GAAG,IAAI,IAAI,CAAC;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK;YAClD,GAAG,EAAE,EAAE;YACP,iBAAiB,EAAE,KAAK;YACxB,uBAAuB,EAAE,IAAI;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,IAAY,EACZ,MAAkB;IAElB,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,CAAC,KAAK,CAAI,IAAI,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,IAAI,GAAG,IAAI,CAAC;QACZ,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Simple migration runner.
3
+ * Migrations are numbered SQL files in src/db/migrations/.
4
+ * Tracks applied migrations in a `_migrations` table.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Simple migration runner.
3
+ * Migrations are numbered SQL files in src/db/migrations/.
4
+ * Tracks applied migrations in a `_migrations` table.
5
+ */
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { query, closePool, getPool } from "./client.js";
9
+ import { logger } from "../shared/logger.js";
10
+ const MIGRATIONS_DIR = path.join(import.meta.dirname, "migrations");
11
+ async function ensureMigrationsTable() {
12
+ await query(`
13
+ CREATE TABLE IF NOT EXISTS _migrations (
14
+ id SERIAL PRIMARY KEY,
15
+ name TEXT UNIQUE NOT NULL,
16
+ applied_at TIMESTAMPTZ DEFAULT now()
17
+ )
18
+ `);
19
+ }
20
+ async function getAppliedMigrations() {
21
+ const result = await query("SELECT name FROM _migrations ORDER BY id");
22
+ return new Set(result.rows.map((r) => r.name));
23
+ }
24
+ async function getMigrationFiles() {
25
+ const files = fs.readdirSync(MIGRATIONS_DIR).filter((f) => f.endsWith(".sql"));
26
+ files.sort();
27
+ return files;
28
+ }
29
+ async function runMigration(client, name, sql) {
30
+ logger.info("Running migration", { name });
31
+ await client.query("BEGIN");
32
+ try {
33
+ await client.query(sql);
34
+ await client.query("INSERT INTO _migrations (name) VALUES ($1)", [name]);
35
+ await client.query("COMMIT");
36
+ logger.info("Migration applied", { name });
37
+ }
38
+ catch (err) {
39
+ await client.query("ROLLBACK");
40
+ throw err;
41
+ }
42
+ }
43
+ async function main() {
44
+ try {
45
+ await ensureMigrationsTable();
46
+ const applied = await getAppliedMigrations();
47
+ const files = await getMigrationFiles();
48
+ const pending = files.filter((f) => !applied.has(f));
49
+ if (pending.length === 0) {
50
+ logger.info("No pending migrations");
51
+ return;
52
+ }
53
+ logger.info("Pending migrations", { count: pending.length, files: pending });
54
+ const client = await getPool().connect();
55
+ try {
56
+ for (const file of pending) {
57
+ const sql = fs.readFileSync(path.join(MIGRATIONS_DIR, file), "utf-8");
58
+ await runMigration(client, file, sql);
59
+ }
60
+ }
61
+ finally {
62
+ client.release();
63
+ }
64
+ logger.info("All migrations applied");
65
+ }
66
+ catch (err) {
67
+ logger.error("Migration failed", {
68
+ error: err instanceof Error ? err.message : String(err),
69
+ });
70
+ process.exit(1);
71
+ }
72
+ finally {
73
+ await closePool();
74
+ }
75
+ }
76
+ main();
77
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * TypeScript types mirroring the Postgres schema.
3
+ * These are the row shapes returned by queries.
4
+ */
5
+ export interface Product {
6
+ id: string;
7
+ language: Language;
8
+ name: string;
9
+ tcgplayer_group_id: number | null;
10
+ released_at: string | null;
11
+ created_at: string;
12
+ }
13
+ export interface Card {
14
+ id: string;
15
+ card_number: string;
16
+ language: string;
17
+ product_id: string;
18
+ true_set_code: string;
19
+ name: string;
20
+ card_type: string;
21
+ rarity: string | null;
22
+ color: string[];
23
+ cost: number | null;
24
+ power: number | null;
25
+ counter: number | null;
26
+ life: number | null;
27
+ attribute: string[] | null;
28
+ types: string[];
29
+ effect: string | null;
30
+ trigger: string | null;
31
+ block: string | null;
32
+ artist: string | null;
33
+ artist_ocr: boolean;
34
+ created_at: string;
35
+ updated_at: string;
36
+ }
37
+ export interface CardImage {
38
+ id: string;
39
+ card_id: string;
40
+ product_id: string | null;
41
+ variant_index: number;
42
+ image_url: string;
43
+ source_url: string | null;
44
+ is_default: boolean;
45
+ label: string | null;
46
+ classified: boolean;
47
+ created_at: string;
48
+ }
49
+ export interface CardSource {
50
+ id: string;
51
+ card_id: string;
52
+ product_id: string;
53
+ created_at: string;
54
+ }
55
+ export interface DonCard {
56
+ id: string;
57
+ product_id: string;
58
+ character: string;
59
+ finish: string;
60
+ image_url: string | null;
61
+ created_at: string;
62
+ updated_at: string;
63
+ }
64
+ export interface Format {
65
+ id: string;
66
+ name: string;
67
+ description: string | null;
68
+ has_rotation: boolean;
69
+ created_at: string;
70
+ }
71
+ export interface FormatLegalBlock {
72
+ id: string;
73
+ format_id: string;
74
+ block: string;
75
+ legal: boolean;
76
+ rotated_at: string | null;
77
+ }
78
+ export interface FormatBan {
79
+ id: string;
80
+ format_id: string;
81
+ card_number: string;
82
+ banned_at: string;
83
+ reason: string | null;
84
+ unbanned_at: string | null;
85
+ }
86
+ export interface TcgplayerProduct {
87
+ id: string;
88
+ tcgplayer_product_id: number;
89
+ name: string;
90
+ clean_name: string | null;
91
+ sub_type: string | null;
92
+ ext_number: string | null;
93
+ ext_rarity: string | null;
94
+ group_id: number | null;
95
+ tcgplayer_url: string | null;
96
+ image_url: string | null;
97
+ card_image_id: string | null;
98
+ don_card_id: string | null;
99
+ product_type: string;
100
+ created_at: string;
101
+ updated_at: string;
102
+ }
103
+ export interface TcgplayerPrice {
104
+ id: string;
105
+ tcgplayer_product_id: number;
106
+ sub_type: string | null;
107
+ low_price: string | null;
108
+ mid_price: string | null;
109
+ high_price: string | null;
110
+ market_price: string | null;
111
+ direct_low_price: string | null;
112
+ fetched_at: string;
113
+ }
114
+ export interface ScrapeLog {
115
+ id: string;
116
+ ran_at: string;
117
+ source: string | null;
118
+ cards_added: number;
119
+ cards_updated: number;
120
+ errors: string | null;
121
+ duration_ms: number | null;
122
+ }
123
+ /** Supported languages */
124
+ export type Language = "en" | "ja" | "fr" | "zh";
125
+ /** Card types in OPTCG */
126
+ export type CardType = "Leader" | "Character" | "Event" | "Stage";
127
+ /** Rarity codes */
128
+ export type Rarity = "C" | "UC" | "R" | "SR" | "SEC" | "L" | "P" | "SP";
129
+ /** Color values */
130
+ export type Color = "Red" | "Green" | "Blue" | "Purple" | "Black" | "Yellow";
131
+ /** Attribute values */
132
+ export type Attribute = "Strike" | "Slash" | "Special" | "Wisdom" | "Ranged";
133
+ /** DON!! card finish types */
134
+ export type DonFinish = "Normal" | "Foil" | "Gold";
135
+ /** TCGPlayer product types */
136
+ export type ProductType = "card" | "sealed" | "don";
137
+ /** Label mapping from TCGPlayer suffix to card_images label */
138
+ export declare const TCGPLAYER_LABEL_MAP: Record<string, string>;
139
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * TypeScript types mirroring the Postgres schema.
3
+ * These are the row shapes returned by queries.
4
+ */
5
+ /** Label mapping from TCGPlayer suffix to card_images label */
6
+ export const TCGPLAYER_LABEL_MAP = {
7
+ "": "Standard",
8
+ "(Alternate Art)": "Alternate Art",
9
+ "(SP)": "SP Card",
10
+ "(Manga)": "Manga Art",
11
+ "(Gold)": "Gold",
12
+ "(Dash Pack)": "Dash Pack",
13
+ "(Box Topper)": "Box Topper",
14
+ "(Full Art)": "Full Art",
15
+ "(Promo)": "Promo",
16
+ "(Reprint)": "Reprint",
17
+ "(Parallel)": "Alternate Art",
18
+ };
19
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwJH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IACzD,EAAE,EAAE,UAAU;IACd,iBAAiB,EAAE,eAAe;IAClC,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,MAAM;IAChB,aAAa,EAAE,WAAW;IAC1B,cAAc,EAAE,YAAY;IAC5B,YAAY,EAAE,UAAU;IACxB,SAAS,EAAE,OAAO;IAClB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,eAAe;CAC9B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Structured JSON logger for ECS tasks.
3
+ * Writes to stdout so CloudWatch picks it up.
4
+ */
5
+ export declare const logger: {
6
+ info: (message: string, data?: Record<string, unknown>) => void;
7
+ warn: (message: string, data?: Record<string, unknown>) => void;
8
+ error: (message: string, data?: Record<string, unknown>) => void;
9
+ debug: (message: string, data?: Record<string, unknown>) => void;
10
+ };
11
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/shared/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,eAAO,MAAM,MAAM;oBACD,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBACrC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACxD,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Structured JSON logger for ECS tasks.
3
+ * Writes to stdout so CloudWatch picks it up.
4
+ */
5
+ function log(level, message, data) {
6
+ const entry = {
7
+ timestamp: new Date().toISOString(),
8
+ level,
9
+ message,
10
+ ...data,
11
+ };
12
+ const stream = level === "error" ? process.stderr : process.stdout;
13
+ stream.write(JSON.stringify(entry) + "\n");
14
+ }
15
+ export const logger = {
16
+ info: (message, data) => log("info", message, data),
17
+ warn: (message, data) => log("warn", message, data),
18
+ error: (message, data) => log("error", message, data),
19
+ debug: (message, data) => log("debug", message, data),
20
+ };
21
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/shared/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,SAAS,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;IAC3E,MAAM,KAAK,GAAG;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,KAAK;QACL,OAAO;QACP,GAAG,IAAI;KACR,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACnE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;IACrF,IAAI,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;IACrF,KAAK,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;IACvF,KAAK,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;CACxF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "optcg-db",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Shared database layer (schema, migrations, connection pool) for the OPTCG Scryfall project",
6
+ "exports": {
7
+ "./db/client.js": "./dist/db/client.js",
8
+ "./db/schema.js": "./dist/db/schema.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "migrate": "tsx src/db/migrate.ts",
16
+ "prepublishOnly": "tsc",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "dependencies": {
20
+ "dotenv": "^17.3.1",
21
+ "pg": "^8.13.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.11.0",
25
+ "@types/pg": "^8.11.0",
26
+ "tsx": "^4.7.0",
27
+ "typescript": "^5.5.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=20"
31
+ }
32
+ }