@takaro/db 0.4.10 → 0.4.11

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,4 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
4
+ //# sourceMappingURL=20251220000000-inventory-diff-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20251220000000-inventory-diff-storage.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20251220000000-inventory-diff-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA0JlD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpD"}
@@ -0,0 +1,147 @@
1
+ export async function up(knex) {
2
+ // Create playerInventoryBaseline table - full inventory snapshots every hour
3
+ await knex.raw(`
4
+ CREATE TABLE "playerInventoryBaseline" (
5
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
6
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
7
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
8
+ domain VARCHAR(255) NOT NULL,
9
+ "playerId" UUID NOT NULL,
10
+ "baselineId" UUID NOT NULL,
11
+ "itemId" UUID NOT NULL,
12
+ quantity INTEGER NOT NULL,
13
+ quality VARCHAR(255),
14
+ CONSTRAINT "playerInventoryBaseline_pkey" PRIMARY KEY ("createdAt", "baselineId", "itemId"),
15
+ CONSTRAINT "playerInventoryBaseline_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
16
+ CONSTRAINT "playerInventoryBaseline_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE,
17
+ CONSTRAINT "playerInventoryBaseline_itemId_foreign" FOREIGN KEY ("itemId") REFERENCES items(id) ON DELETE CASCADE,
18
+ CONSTRAINT "playerInventoryBaseline_quantity_check" CHECK (quantity >= 0)
19
+ ) PARTITION BY RANGE ("createdAt")
20
+ `);
21
+ // Create playerInventoryDiff table - individual item changes between baselines
22
+ await knex.raw(`
23
+ CREATE TABLE "playerInventoryDiff" (
24
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
25
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
26
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
27
+ domain VARCHAR(255) NOT NULL,
28
+ "playerId" UUID NOT NULL,
29
+ "itemId" UUID NOT NULL,
30
+ "changeType" VARCHAR(10) NOT NULL,
31
+ "previousQuantity" INTEGER,
32
+ "newQuantity" INTEGER,
33
+ "previousQuality" VARCHAR(255),
34
+ "newQuality" VARCHAR(255),
35
+ CONSTRAINT "playerInventoryDiff_pkey" PRIMARY KEY ("createdAt", "playerId", "itemId"),
36
+ CONSTRAINT "playerInventoryDiff_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
37
+ CONSTRAINT "playerInventoryDiff_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE,
38
+ CONSTRAINT "playerInventoryDiff_itemId_foreign" FOREIGN KEY ("itemId") REFERENCES items(id) ON DELETE CASCADE,
39
+ CONSTRAINT "playerInventoryDiff_changeType_check" CHECK ("changeType" IN ('added', 'removed', 'changed'))
40
+ ) PARTITION BY RANGE ("createdAt")
41
+ `);
42
+ // Create partition function for playerInventoryBaseline
43
+ await knex.raw(`
44
+ CREATE OR REPLACE FUNCTION ensure_player_inventory_baseline_partition(date_param VARCHAR DEFAULT NULL)
45
+ RETURNS VOID AS $$
46
+ DECLARE
47
+ current_day_start DATE;
48
+ next_day_start DATE;
49
+ partition_name TEXT;
50
+ partition_exists BOOLEAN;
51
+ target_date DATE;
52
+ BEGIN
53
+ IF date_param IS NOT NULL THEN
54
+ target_date := DATE(date_param::TIMESTAMP);
55
+ ELSE
56
+ target_date := CURRENT_DATE;
57
+ END IF;
58
+
59
+ current_day_start := DATE_TRUNC('day', target_date);
60
+ next_day_start := current_day_start + INTERVAL '1 day';
61
+
62
+ partition_name := 'playerInventoryBaseline_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
63
+
64
+ SELECT EXISTS (
65
+ SELECT 1 FROM pg_class c
66
+ JOIN pg_namespace n ON n.oid = c.relnamespace
67
+ WHERE c.relname = partition_name
68
+ AND n.nspname = 'public'
69
+ ) INTO partition_exists;
70
+
71
+ IF NOT partition_exists THEN
72
+ EXECUTE format(
73
+ 'CREATE TABLE %I PARTITION OF "playerInventoryBaseline"
74
+ FOR VALUES FROM (%L) TO (%L)',
75
+ partition_name,
76
+ current_day_start,
77
+ next_day_start
78
+ );
79
+
80
+ RAISE NOTICE 'Created partition: % for date: %',
81
+ partition_name, current_day_start;
82
+ END IF;
83
+ END;
84
+ $$ LANGUAGE plpgsql;
85
+ `);
86
+ // Create partition function for playerInventoryDiff
87
+ await knex.raw(`
88
+ CREATE OR REPLACE FUNCTION ensure_player_inventory_diff_partition(date_param VARCHAR DEFAULT NULL)
89
+ RETURNS VOID AS $$
90
+ DECLARE
91
+ current_day_start DATE;
92
+ next_day_start DATE;
93
+ partition_name TEXT;
94
+ partition_exists BOOLEAN;
95
+ target_date DATE;
96
+ BEGIN
97
+ IF date_param IS NOT NULL THEN
98
+ target_date := DATE(date_param::TIMESTAMP);
99
+ ELSE
100
+ target_date := CURRENT_DATE;
101
+ END IF;
102
+
103
+ current_day_start := DATE_TRUNC('day', target_date);
104
+ next_day_start := current_day_start + INTERVAL '1 day';
105
+
106
+ partition_name := 'playerInventoryDiff_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
107
+
108
+ SELECT EXISTS (
109
+ SELECT 1 FROM pg_class c
110
+ JOIN pg_namespace n ON n.oid = c.relnamespace
111
+ WHERE c.relname = partition_name
112
+ AND n.nspname = 'public'
113
+ ) INTO partition_exists;
114
+
115
+ IF NOT partition_exists THEN
116
+ EXECUTE format(
117
+ 'CREATE TABLE %I PARTITION OF "playerInventoryDiff"
118
+ FOR VALUES FROM (%L) TO (%L)',
119
+ partition_name,
120
+ current_day_start,
121
+ next_day_start
122
+ );
123
+
124
+ RAISE NOTICE 'Created partition: % for date: %',
125
+ partition_name, current_day_start;
126
+ END IF;
127
+ END;
128
+ $$ LANGUAGE plpgsql;
129
+ `);
130
+ // Create indexes for playerInventoryBaseline
131
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_domain_createdAt_idx" ON "playerInventoryBaseline" (domain, "createdAt")`);
132
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_playerId_idx" ON "playerInventoryBaseline" ("playerId")`);
133
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_playerId_createdAt_idx" ON "playerInventoryBaseline" ("playerId", "createdAt" DESC)`);
134
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_baselineId_idx" ON "playerInventoryBaseline" ("baselineId")`);
135
+ // Create indexes for playerInventoryDiff
136
+ await knex.raw(`CREATE INDEX "playerInventoryDiff_domain_createdAt_idx" ON "playerInventoryDiff" (domain, "createdAt")`);
137
+ await knex.raw(`CREATE INDEX "playerInventoryDiff_playerId_idx" ON "playerInventoryDiff" ("playerId")`);
138
+ await knex.raw(`CREATE INDEX "playerInventoryDiff_playerId_createdAt_idx" ON "playerInventoryDiff" ("playerId", "createdAt" DESC)`);
139
+ await knex.raw(`CREATE INDEX "playerInventoryDiff_itemId_createdAt_idx" ON "playerInventoryDiff" ("itemId", "createdAt" DESC)`);
140
+ }
141
+ export async function down(knex) {
142
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_inventory_baseline_partition(VARCHAR)');
143
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_inventory_diff_partition(VARCHAR)');
144
+ await knex.raw('DROP TABLE IF EXISTS "playerInventoryBaseline" CASCADE');
145
+ await knex.raw('DROP TABLE IF EXISTS "playerInventoryDiff" CASCADE');
146
+ }
147
+ //# sourceMappingURL=20251220000000-inventory-diff-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20251220000000-inventory-diff-storage.js","sourceRoot":"","sources":["../../../src/migrations/sql/20251220000000-inventory-diff-storage.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,6EAA6E;IAC7E,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;GAiBd,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;GAmBd,CAAC,CAAC;IAEH,wDAAwD;IACxD,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0Cd,CAAC,CAAC;IAEH,oDAAoD;IACpD,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0Cd,CAAC,CAAC;IAEH,6CAA6C;IAC7C,MAAM,IAAI,CAAC,GAAG,CACZ,gHAAgH,CACjH,CAAC;IACF,MAAM,IAAI,CAAC,GAAG,CAAC,+FAA+F,CAAC,CAAC;IAChH,MAAM,IAAI,CAAC,GAAG,CACZ,2HAA2H,CAC5H,CAAC;IACF,MAAM,IAAI,CAAC,GAAG,CAAC,mGAAmG,CAAC,CAAC;IAEpH,yCAAyC;IACzC,MAAM,IAAI,CAAC,GAAG,CACZ,wGAAwG,CACzG,CAAC;IACF,MAAM,IAAI,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACxG,MAAM,IAAI,CAAC,GAAG,CACZ,mHAAmH,CACpH,CAAC;IACF,MAAM,IAAI,CAAC,GAAG,CACZ,+GAA+G,CAChH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,MAAM,IAAI,CAAC,GAAG,CAAC,6EAA6E,CAAC,CAAC;IAC9F,MAAM,IAAI,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IAC1F,MAAM,IAAI,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACzE,MAAM,IAAI,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;AACvE,CAAC"}
package/package.json CHANGED
@@ -1,16 +1,27 @@
1
1
  {
2
2
  "name": "@takaro/db",
3
- "version": "0.4.10",
4
3
  "description": "An opinionated data layer",
4
+ "version": "0.4.11",
5
+ "author": "",
6
+ "dependencies": {
7
+ "class-validator": "0.14.2",
8
+ "knex": "3.1.0",
9
+ "ms": "2.1.3",
10
+ "objection": "3.1.5",
11
+ "redis": "4.7.0"
12
+ },
13
+ "keywords": [],
14
+ "license": "ISC",
5
15
  "main": "dist/main.js",
6
- "types": "dist/main.d.ts",
7
- "type": "module",
16
+ "peerDependencies": {
17
+ "@takaro/config": "*",
18
+ "@takaro/util": "*"
19
+ },
8
20
  "scripts": {
9
- "start:dev": "tsc --watch --preserveWatchOutput -p ./tsconfig.build.json",
10
21
  "build": "tsc -p ./tsconfig.build.json",
11
- "migrate:create": "run(){ touch ./src/migrations/sql/$(date -u +\"%Y%m%d%H%M%S\")-$1.ts; }; run"
22
+ "migrate:create": "run(){ touch ./src/migrations/sql/$(date -u +\"%Y%m%d%H%M%S\")-$1.ts; }; run",
23
+ "start:dev": ":"
12
24
  },
13
- "keywords": [],
14
- "author": "",
15
- "license": "ISC"
25
+ "type": "module",
26
+ "types": "dist/main.d.ts"
16
27
  }
@@ -0,0 +1,164 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Create playerInventoryBaseline table - full inventory snapshots every hour
5
+ await knex.raw(`
6
+ CREATE TABLE "playerInventoryBaseline" (
7
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
8
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
9
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
10
+ domain VARCHAR(255) NOT NULL,
11
+ "playerId" UUID NOT NULL,
12
+ "baselineId" UUID NOT NULL,
13
+ "itemId" UUID NOT NULL,
14
+ quantity INTEGER NOT NULL,
15
+ quality VARCHAR(255),
16
+ CONSTRAINT "playerInventoryBaseline_pkey" PRIMARY KEY ("createdAt", "baselineId", "itemId"),
17
+ CONSTRAINT "playerInventoryBaseline_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
18
+ CONSTRAINT "playerInventoryBaseline_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE,
19
+ CONSTRAINT "playerInventoryBaseline_itemId_foreign" FOREIGN KEY ("itemId") REFERENCES items(id) ON DELETE CASCADE,
20
+ CONSTRAINT "playerInventoryBaseline_quantity_check" CHECK (quantity >= 0)
21
+ ) PARTITION BY RANGE ("createdAt")
22
+ `);
23
+
24
+ // Create playerInventoryDiff table - individual item changes between baselines
25
+ await knex.raw(`
26
+ CREATE TABLE "playerInventoryDiff" (
27
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
28
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
29
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
30
+ domain VARCHAR(255) NOT NULL,
31
+ "playerId" UUID NOT NULL,
32
+ "itemId" UUID NOT NULL,
33
+ "changeType" VARCHAR(10) NOT NULL,
34
+ "previousQuantity" INTEGER,
35
+ "newQuantity" INTEGER,
36
+ "previousQuality" VARCHAR(255),
37
+ "newQuality" VARCHAR(255),
38
+ CONSTRAINT "playerInventoryDiff_pkey" PRIMARY KEY ("createdAt", "playerId", "itemId"),
39
+ CONSTRAINT "playerInventoryDiff_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
40
+ CONSTRAINT "playerInventoryDiff_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE,
41
+ CONSTRAINT "playerInventoryDiff_itemId_foreign" FOREIGN KEY ("itemId") REFERENCES items(id) ON DELETE CASCADE,
42
+ CONSTRAINT "playerInventoryDiff_changeType_check" CHECK ("changeType" IN ('added', 'removed', 'changed'))
43
+ ) PARTITION BY RANGE ("createdAt")
44
+ `);
45
+
46
+ // Create partition function for playerInventoryBaseline
47
+ await knex.raw(`
48
+ CREATE OR REPLACE FUNCTION ensure_player_inventory_baseline_partition(date_param VARCHAR DEFAULT NULL)
49
+ RETURNS VOID AS $$
50
+ DECLARE
51
+ current_day_start DATE;
52
+ next_day_start DATE;
53
+ partition_name TEXT;
54
+ partition_exists BOOLEAN;
55
+ target_date DATE;
56
+ BEGIN
57
+ IF date_param IS NOT NULL THEN
58
+ target_date := DATE(date_param::TIMESTAMP);
59
+ ELSE
60
+ target_date := CURRENT_DATE;
61
+ END IF;
62
+
63
+ current_day_start := DATE_TRUNC('day', target_date);
64
+ next_day_start := current_day_start + INTERVAL '1 day';
65
+
66
+ partition_name := 'playerInventoryBaseline_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
67
+
68
+ SELECT EXISTS (
69
+ SELECT 1 FROM pg_class c
70
+ JOIN pg_namespace n ON n.oid = c.relnamespace
71
+ WHERE c.relname = partition_name
72
+ AND n.nspname = 'public'
73
+ ) INTO partition_exists;
74
+
75
+ IF NOT partition_exists THEN
76
+ EXECUTE format(
77
+ 'CREATE TABLE %I PARTITION OF "playerInventoryBaseline"
78
+ FOR VALUES FROM (%L) TO (%L)',
79
+ partition_name,
80
+ current_day_start,
81
+ next_day_start
82
+ );
83
+
84
+ RAISE NOTICE 'Created partition: % for date: %',
85
+ partition_name, current_day_start;
86
+ END IF;
87
+ END;
88
+ $$ LANGUAGE plpgsql;
89
+ `);
90
+
91
+ // Create partition function for playerInventoryDiff
92
+ await knex.raw(`
93
+ CREATE OR REPLACE FUNCTION ensure_player_inventory_diff_partition(date_param VARCHAR DEFAULT NULL)
94
+ RETURNS VOID AS $$
95
+ DECLARE
96
+ current_day_start DATE;
97
+ next_day_start DATE;
98
+ partition_name TEXT;
99
+ partition_exists BOOLEAN;
100
+ target_date DATE;
101
+ BEGIN
102
+ IF date_param IS NOT NULL THEN
103
+ target_date := DATE(date_param::TIMESTAMP);
104
+ ELSE
105
+ target_date := CURRENT_DATE;
106
+ END IF;
107
+
108
+ current_day_start := DATE_TRUNC('day', target_date);
109
+ next_day_start := current_day_start + INTERVAL '1 day';
110
+
111
+ partition_name := 'playerInventoryDiff_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
112
+
113
+ SELECT EXISTS (
114
+ SELECT 1 FROM pg_class c
115
+ JOIN pg_namespace n ON n.oid = c.relnamespace
116
+ WHERE c.relname = partition_name
117
+ AND n.nspname = 'public'
118
+ ) INTO partition_exists;
119
+
120
+ IF NOT partition_exists THEN
121
+ EXECUTE format(
122
+ 'CREATE TABLE %I PARTITION OF "playerInventoryDiff"
123
+ FOR VALUES FROM (%L) TO (%L)',
124
+ partition_name,
125
+ current_day_start,
126
+ next_day_start
127
+ );
128
+
129
+ RAISE NOTICE 'Created partition: % for date: %',
130
+ partition_name, current_day_start;
131
+ END IF;
132
+ END;
133
+ $$ LANGUAGE plpgsql;
134
+ `);
135
+
136
+ // Create indexes for playerInventoryBaseline
137
+ await knex.raw(
138
+ `CREATE INDEX "playerInventoryBaseline_domain_createdAt_idx" ON "playerInventoryBaseline" (domain, "createdAt")`,
139
+ );
140
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_playerId_idx" ON "playerInventoryBaseline" ("playerId")`);
141
+ await knex.raw(
142
+ `CREATE INDEX "playerInventoryBaseline_playerId_createdAt_idx" ON "playerInventoryBaseline" ("playerId", "createdAt" DESC)`,
143
+ );
144
+ await knex.raw(`CREATE INDEX "playerInventoryBaseline_baselineId_idx" ON "playerInventoryBaseline" ("baselineId")`);
145
+
146
+ // Create indexes for playerInventoryDiff
147
+ await knex.raw(
148
+ `CREATE INDEX "playerInventoryDiff_domain_createdAt_idx" ON "playerInventoryDiff" (domain, "createdAt")`,
149
+ );
150
+ await knex.raw(`CREATE INDEX "playerInventoryDiff_playerId_idx" ON "playerInventoryDiff" ("playerId")`);
151
+ await knex.raw(
152
+ `CREATE INDEX "playerInventoryDiff_playerId_createdAt_idx" ON "playerInventoryDiff" ("playerId", "createdAt" DESC)`,
153
+ );
154
+ await knex.raw(
155
+ `CREATE INDEX "playerInventoryDiff_itemId_createdAt_idx" ON "playerInventoryDiff" ("itemId", "createdAt" DESC)`,
156
+ );
157
+ }
158
+
159
+ export async function down(knex: Knex): Promise<void> {
160
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_inventory_baseline_partition(VARCHAR)');
161
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_inventory_diff_partition(VARCHAR)');
162
+ await knex.raw('DROP TABLE IF EXISTS "playerInventoryBaseline" CASCADE');
163
+ await knex.raw('DROP TABLE IF EXISTS "playerInventoryDiff" CASCADE');
164
+ }
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
3
  "compilerOptions": {
4
+ "composite": true,
4
5
  "rootDir": "./src",
5
6
  "outDir": "dist"
6
7
  },
7
8
  "include": ["src/**/*"],
8
- "exclude": ["src/**/*.test.ts"]
9
+ "exclude": ["src/**/*.test.ts"],
10
+ "references": [
11
+ { "path": "../lib-config/tsconfig.build.json" },
12
+ { "path": "../lib-util/tsconfig.build.json" }
13
+ ]
9
14
  }