@newcms/database 0.0.1 → 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.
- package/README.md +168 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/repositories/meta-repository.d.ts +71 -0
- package/dist/repositories/meta-repository.d.ts.map +1 -0
- package/dist/repositories/meta-repository.js +176 -0
- package/dist/repositories/meta-repository.js.map +1 -0
- package/dist/repositories/post-repository.d.ts +121 -0
- package/dist/repositories/post-repository.d.ts.map +1 -0
- package/dist/repositories/post-repository.js +241 -0
- package/dist/repositories/post-repository.js.map +1 -0
- package/dist/repositories/revision-repository.d.ts +50 -0
- package/dist/repositories/revision-repository.d.ts.map +1 -0
- package/dist/repositories/revision-repository.js +149 -0
- package/dist/repositories/revision-repository.js.map +1 -0
- package/dist/repositories/taxonomy-repository.d.ts +63 -0
- package/dist/repositories/taxonomy-repository.d.ts.map +1 -0
- package/dist/repositories/taxonomy-repository.js +268 -0
- package/dist/repositories/taxonomy-repository.js.map +1 -0
- package/dist/schema/terms.d.ts.map +1 -1
- package/dist/schema/terms.js +2 -1
- package/dist/schema/terms.js.map +1 -1
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @newcms/database
|
|
2
|
+
|
|
3
|
+
Database schema, Redis object cache, and repositories for [NewCMS](https://github.com/durvs/newcms) — a modern, TypeScript-first content management system.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @newcms/database
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Requirements:** PostgreSQL 17+ and Redis 7+.
|
|
12
|
+
|
|
13
|
+
## Database Schema
|
|
14
|
+
|
|
15
|
+
Type-safe [Drizzle ORM](https://orm.drizzle.team/) schema with 14 tables covering the full CMS data model:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
posts, postmeta,
|
|
20
|
+
users, usermeta,
|
|
21
|
+
comments, commentmeta,
|
|
22
|
+
terms, termTaxonomy, termRelationships, termmeta,
|
|
23
|
+
options,
|
|
24
|
+
links,
|
|
25
|
+
sessions,
|
|
26
|
+
scheduledEvents,
|
|
27
|
+
} from '@newcms/database/schema';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Tables
|
|
31
|
+
|
|
32
|
+
| Table | Purpose |
|
|
33
|
+
|-------|---------|
|
|
34
|
+
| `posts` | All content types (posts, pages, attachments, custom types) |
|
|
35
|
+
| `postmeta` | Post metadata (EAV with JSONB support) |
|
|
36
|
+
| `users` | User accounts |
|
|
37
|
+
| `usermeta` | User metadata |
|
|
38
|
+
| `comments` | Threaded comments |
|
|
39
|
+
| `commentmeta` | Comment metadata |
|
|
40
|
+
| `terms` | Taxonomy terms |
|
|
41
|
+
| `term_taxonomy` | Term-taxonomy relationships with hierarchy |
|
|
42
|
+
| `term_relationships` | Object-term assignments |
|
|
43
|
+
| `termmeta` | Term metadata |
|
|
44
|
+
| `options` | Site settings (with autoload and JSONB) |
|
|
45
|
+
| `links` | Blogroll / link manager |
|
|
46
|
+
| `sessions` | User sessions (Redis-backed with DB fallback) |
|
|
47
|
+
| `scheduled_events` | Cron-like scheduled tasks (BullMQ-backed) |
|
|
48
|
+
|
|
49
|
+
### Key Design Decisions
|
|
50
|
+
|
|
51
|
+
- **JSONB columns** on all meta tables — queryable structured data instead of opaque serialized text
|
|
52
|
+
- **GIN indexes** on JSONB columns for efficient structured queries
|
|
53
|
+
- **Composite indexes** on frequently queried combinations (e.g., `post_type + status + date`)
|
|
54
|
+
- **Dedicated tables** for sessions and scheduled events (instead of serialized blobs in options/usermeta)
|
|
55
|
+
|
|
56
|
+
## Connection
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { createConnection } from '@newcms/database';
|
|
60
|
+
|
|
61
|
+
// From environment variables (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD)
|
|
62
|
+
const { db, client } = createConnection();
|
|
63
|
+
|
|
64
|
+
// Or with explicit config
|
|
65
|
+
const { db, client } = createConnection({
|
|
66
|
+
host: 'localhost',
|
|
67
|
+
port: 5432,
|
|
68
|
+
database: 'newcms',
|
|
69
|
+
user: 'newcms',
|
|
70
|
+
password: 'your-secure-password',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Use Drizzle query builder
|
|
74
|
+
const allPosts = await db.select().from(posts);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Note:** `DB_PASSWORD` is required — there is no default. Set it in your environment or `.env` file.
|
|
78
|
+
|
|
79
|
+
## Object Cache
|
|
80
|
+
|
|
81
|
+
Redis-backed object cache with group isolation, TTL, and multisite support:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { ObjectCache } from '@newcms/database';
|
|
85
|
+
|
|
86
|
+
const cache = new ObjectCache({
|
|
87
|
+
host: 'localhost',
|
|
88
|
+
port: 6379,
|
|
89
|
+
});
|
|
90
|
+
await cache.connect();
|
|
91
|
+
|
|
92
|
+
// Basic get/set
|
|
93
|
+
await cache.set('my_key', { hello: 'world' }, 'my_group');
|
|
94
|
+
const value = await cache.get('my_key', 'my_group');
|
|
95
|
+
|
|
96
|
+
// TTL (in seconds)
|
|
97
|
+
await cache.set('temp', 'data', 'default', 300); // expires in 5 min
|
|
98
|
+
|
|
99
|
+
// Batch operations (uses Redis pipeline)
|
|
100
|
+
await cache.setMultiple({ a: 1, b: 2, c: 3 }, 'counters');
|
|
101
|
+
const results = await cache.getMultiple(['a', 'b', 'c'], 'counters');
|
|
102
|
+
|
|
103
|
+
// Increment/decrement
|
|
104
|
+
await cache.set('views', 0);
|
|
105
|
+
await cache.incr('views'); // => 1
|
|
106
|
+
|
|
107
|
+
// Group-level TTL
|
|
108
|
+
cache.setGroupTtl('transients', 3600); // 1 hour for all transients
|
|
109
|
+
|
|
110
|
+
// Multisite: global groups shared across sites
|
|
111
|
+
cache.addGlobalGroups(['users', 'site-options']);
|
|
112
|
+
cache.setSiteId(2); // switch site context
|
|
113
|
+
|
|
114
|
+
// Flush
|
|
115
|
+
await cache.flushGroup('posts'); // flush one group
|
|
116
|
+
await cache.flushAll(); // flush everything
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Options Repository
|
|
120
|
+
|
|
121
|
+
CRUD for site options with integrated Redis cache:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { createConnection, ObjectCache, OptionsRepository } from '@newcms/database';
|
|
125
|
+
|
|
126
|
+
const { db } = createConnection();
|
|
127
|
+
const cache = new ObjectCache({ host: 'localhost', port: 6379 });
|
|
128
|
+
await cache.connect();
|
|
129
|
+
|
|
130
|
+
const options = new OptionsRepository(db, cache);
|
|
131
|
+
|
|
132
|
+
// Read (with cache — hits DB only on first read)
|
|
133
|
+
const siteName = await options.getOption('blogname');
|
|
134
|
+
const perPage = await options.getOption('posts_per_page', 10); // with default
|
|
135
|
+
|
|
136
|
+
// Write (invalidates cache automatically)
|
|
137
|
+
await options.updateOption('blogname', 'My New Site');
|
|
138
|
+
await options.addOption('custom_setting', { enabled: true, threshold: 50 });
|
|
139
|
+
|
|
140
|
+
// Delete
|
|
141
|
+
await options.deleteOption('old_setting');
|
|
142
|
+
|
|
143
|
+
// Bootstrap: pre-load all autoloaded options into cache
|
|
144
|
+
const allOptions = await options.loadAutoloadedOptions();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Cache Behavior
|
|
148
|
+
|
|
149
|
+
- **Autoloaded options** are bulk-loaded into Redis on bootstrap
|
|
150
|
+
- **Cache-aside pattern** — reads check cache first, fall back to DB
|
|
151
|
+
- **Not-found caching** — missing keys are cached to prevent repeated DB misses
|
|
152
|
+
- **Granular invalidation** — writes invalidate only the affected key
|
|
153
|
+
- **JSONB** — complex values (objects, arrays) stored in queryable JSONB column
|
|
154
|
+
|
|
155
|
+
## Environment Variables
|
|
156
|
+
|
|
157
|
+
| Variable | Required | Default | Description |
|
|
158
|
+
|----------|----------|---------|-------------|
|
|
159
|
+
| `DB_HOST` | No | `localhost` | PostgreSQL host |
|
|
160
|
+
| `DB_PORT` | No | `5432` | PostgreSQL port |
|
|
161
|
+
| `DB_NAME` | No | `newcms` | Database name |
|
|
162
|
+
| `DB_USER` | No | `newcms` | Database user |
|
|
163
|
+
| `DB_PASSWORD` | **Yes** | — | Database password |
|
|
164
|
+
| `DB_MAX_CONNECTIONS` | No | `10` | Connection pool size |
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
GPL-2.0-or-later
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,11 @@ export * from './schema/index.js';
|
|
|
4
4
|
export { ObjectCache } from './cache/index.js';
|
|
5
5
|
export type { ObjectCacheConfig, CacheGroupConfig } from './cache/index.js';
|
|
6
6
|
export { OptionsRepository } from './repositories/options-repository.js';
|
|
7
|
+
export { PostRepository } from './repositories/post-repository.js';
|
|
8
|
+
export type { CreatePostInput, UpdatePostInput, PostRow } from './repositories/post-repository.js';
|
|
9
|
+
export { MetaRepository } from './repositories/meta-repository.js';
|
|
10
|
+
export type { MetaTableColumns, MetaColumnNames, MetaEntry } from './repositories/meta-repository.js';
|
|
11
|
+
export { TaxonomyRepository } from './repositories/taxonomy-repository.js';
|
|
12
|
+
export type { CreateTermInput, TermRow } from './repositories/taxonomy-repository.js';
|
|
13
|
+
export { RevisionRepository } from './repositories/revision-repository.js';
|
|
7
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAChE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAChE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,8 @@ export { createConnection } from './connection.js';
|
|
|
2
2
|
export * from './schema/index.js';
|
|
3
3
|
export { ObjectCache } from './cache/index.js';
|
|
4
4
|
export { OptionsRepository } from './repositories/options-repository.js';
|
|
5
|
+
export { PostRepository } from './repositories/post-repository.js';
|
|
6
|
+
export { MetaRepository } from './repositories/meta-repository.js';
|
|
7
|
+
export { TaxonomyRepository } from './repositories/taxonomy-repository.js';
|
|
8
|
+
export { RevisionRepository } from './repositories/revision-repository.js';
|
|
5
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAEnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { PgTable, PgColumn } from 'drizzle-orm/pg-core';
|
|
2
|
+
import type { Database } from '../connection.js';
|
|
3
|
+
/**
|
|
4
|
+
* Column references needed for a meta table.
|
|
5
|
+
*/
|
|
6
|
+
export interface MetaTableColumns {
|
|
7
|
+
metaId: PgColumn;
|
|
8
|
+
objectId: PgColumn;
|
|
9
|
+
metaKey: PgColumn;
|
|
10
|
+
metaValue: PgColumn;
|
|
11
|
+
metaValueJson: PgColumn;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Column name mapping for raw SQL operations and result parsing.
|
|
15
|
+
*
|
|
16
|
+
* `sql` = names as in the database (for INSERT/UPDATE raw SQL)
|
|
17
|
+
* `ts` = names as in the Drizzle schema definition (for reading select results)
|
|
18
|
+
*/
|
|
19
|
+
export interface MetaColumnNames {
|
|
20
|
+
table: string;
|
|
21
|
+
/** SQL column names (for raw queries) */
|
|
22
|
+
sql: {
|
|
23
|
+
metaId: string;
|
|
24
|
+
objectId: string;
|
|
25
|
+
metaKey: string;
|
|
26
|
+
metaValue: string;
|
|
27
|
+
metaValueJson: string;
|
|
28
|
+
};
|
|
29
|
+
/** TypeScript property names (for reading Drizzle select results) */
|
|
30
|
+
ts: {
|
|
31
|
+
metaId: string;
|
|
32
|
+
objectId: string;
|
|
33
|
+
metaKey: string;
|
|
34
|
+
metaValue: string;
|
|
35
|
+
metaValueJson: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface MetaEntry {
|
|
39
|
+
metaId: number;
|
|
40
|
+
objectId: number;
|
|
41
|
+
metaKey: string | null;
|
|
42
|
+
metaValue: string | null;
|
|
43
|
+
metaValueJson: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generic metadata repository — works with any meta table (postmeta, usermeta,
|
|
47
|
+
* commentmeta, termmeta). Follows the entity-attribute-value pattern with
|
|
48
|
+
* JSONB support for structured queries.
|
|
49
|
+
*/
|
|
50
|
+
export declare class MetaRepository {
|
|
51
|
+
private db;
|
|
52
|
+
private table;
|
|
53
|
+
private columns;
|
|
54
|
+
private colNames;
|
|
55
|
+
constructor(db: Database, table: PgTable, columns: MetaTableColumns, colNames: MetaColumnNames);
|
|
56
|
+
get<T = unknown>(objectId: number, key: string): Promise<T | undefined>;
|
|
57
|
+
getAll<T = unknown>(objectId: number, key: string): Promise<T[]>;
|
|
58
|
+
getAllForObject(objectId: number): Promise<Map<string, unknown[]>>;
|
|
59
|
+
batchLoad(objectIds: number[]): Promise<Map<number, Map<string, unknown[]>>>;
|
|
60
|
+
add(objectId: number, key: string, value: unknown): Promise<number>;
|
|
61
|
+
update(objectId: number, key: string, value: unknown): Promise<boolean>;
|
|
62
|
+
delete(objectId: number, key: string, value?: unknown): Promise<number>;
|
|
63
|
+
deleteAllForObject(objectId: number): Promise<number>;
|
|
64
|
+
/**
|
|
65
|
+
* Convert a raw DB row to MetaEntry using column name mapping.
|
|
66
|
+
*/
|
|
67
|
+
private toMetaEntry;
|
|
68
|
+
private serialize;
|
|
69
|
+
private deserialize;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=meta-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-repository.d.ts","sourceRoot":"","sources":["../../src/repositories/meta-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC;IAClB,SAAS,EAAE,QAAQ,CAAC;IACpB,aAAa,EAAE,QAAQ,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACrG,qEAAqE;IACrE,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACpG;AAED,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IAIzB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,OAAO;IALhB,OAAO,CAAC,QAAQ,CAAkB;gBAGzB,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,EACjC,QAAQ,EAAE,eAAe;IAKpB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAWvE,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAShE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAiBlE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAwB5E,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBnE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BvE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBvE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ3D;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,WAAW;CAWnB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { eq, and, inArray, sql } from 'drizzle-orm';
|
|
2
|
+
/**
|
|
3
|
+
* Generic metadata repository — works with any meta table (postmeta, usermeta,
|
|
4
|
+
* commentmeta, termmeta). Follows the entity-attribute-value pattern with
|
|
5
|
+
* JSONB support for structured queries.
|
|
6
|
+
*/
|
|
7
|
+
export class MetaRepository {
|
|
8
|
+
db;
|
|
9
|
+
table;
|
|
10
|
+
columns;
|
|
11
|
+
colNames;
|
|
12
|
+
constructor(db, table, columns, colNames) {
|
|
13
|
+
this.db = db;
|
|
14
|
+
this.table = table;
|
|
15
|
+
this.columns = columns;
|
|
16
|
+
this.colNames = colNames;
|
|
17
|
+
}
|
|
18
|
+
async get(objectId, key) {
|
|
19
|
+
const rows = await this.db
|
|
20
|
+
.select()
|
|
21
|
+
.from(this.table)
|
|
22
|
+
.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))
|
|
23
|
+
.limit(1);
|
|
24
|
+
if (rows.length === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
return this.deserialize(this.toMetaEntry(rows[0]));
|
|
27
|
+
}
|
|
28
|
+
async getAll(objectId, key) {
|
|
29
|
+
const rows = await this.db
|
|
30
|
+
.select()
|
|
31
|
+
.from(this.table)
|
|
32
|
+
.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)));
|
|
33
|
+
return rows.map((r) => this.deserialize(this.toMetaEntry(r)));
|
|
34
|
+
}
|
|
35
|
+
async getAllForObject(objectId) {
|
|
36
|
+
const rows = await this.db
|
|
37
|
+
.select()
|
|
38
|
+
.from(this.table)
|
|
39
|
+
.where(eq(this.columns.objectId, objectId));
|
|
40
|
+
const result = new Map();
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
const entry = this.toMetaEntry(row);
|
|
43
|
+
const key = entry.metaKey ?? '';
|
|
44
|
+
const values = result.get(key) ?? [];
|
|
45
|
+
values.push(this.deserialize(entry));
|
|
46
|
+
result.set(key, values);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
async batchLoad(objectIds) {
|
|
51
|
+
if (objectIds.length === 0)
|
|
52
|
+
return new Map();
|
|
53
|
+
const rows = await this.db
|
|
54
|
+
.select()
|
|
55
|
+
.from(this.table)
|
|
56
|
+
.where(inArray(this.columns.objectId, objectIds));
|
|
57
|
+
const result = new Map();
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
const rawRow = row;
|
|
60
|
+
const entry = this.toMetaEntry(rawRow);
|
|
61
|
+
if (!result.has(entry.objectId)) {
|
|
62
|
+
result.set(entry.objectId, new Map());
|
|
63
|
+
}
|
|
64
|
+
const objectMeta = result.get(entry.objectId);
|
|
65
|
+
const key = entry.metaKey ?? '';
|
|
66
|
+
const values = objectMeta.get(key) ?? [];
|
|
67
|
+
values.push(this.deserialize(entry));
|
|
68
|
+
objectMeta.set(key, values);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
async add(objectId, key, value) {
|
|
73
|
+
const { textValue, jsonValue } = this.serialize(value);
|
|
74
|
+
const jsonStr = jsonValue !== null ? JSON.stringify(jsonValue) : null;
|
|
75
|
+
const s = this.colNames.sql;
|
|
76
|
+
const t = this.colNames.table;
|
|
77
|
+
const rows = await this.db.execute(sql `
|
|
78
|
+
INSERT INTO ${sql.raw(`"${t}"`)} (${sql.raw(`"${s.objectId}"`)}, ${sql.raw(`"${s.metaKey}"`)}, ${sql.raw(`"${s.metaValue}"`)}, ${sql.raw(`"${s.metaValueJson}"`)})
|
|
79
|
+
VALUES (${objectId}, ${key}, ${textValue}, ${jsonStr}::jsonb)
|
|
80
|
+
RETURNING ${sql.raw(`"${s.metaId}"`)}
|
|
81
|
+
`);
|
|
82
|
+
const returnedRows = rows;
|
|
83
|
+
return returnedRows[0][s.metaId];
|
|
84
|
+
}
|
|
85
|
+
async update(objectId, key, value) {
|
|
86
|
+
const { textValue, jsonValue } = this.serialize(value);
|
|
87
|
+
const existing = await this.db
|
|
88
|
+
.select({ metaId: this.columns.metaId })
|
|
89
|
+
.from(this.table)
|
|
90
|
+
.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))
|
|
91
|
+
.limit(1);
|
|
92
|
+
if (existing.length === 0) {
|
|
93
|
+
await this.add(objectId, key, value);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
const metaId = existing[0].metaId;
|
|
97
|
+
const jsonStr = jsonValue !== null ? JSON.stringify(jsonValue) : null;
|
|
98
|
+
const s = this.colNames.sql;
|
|
99
|
+
const t = this.colNames.table;
|
|
100
|
+
await this.db.execute(sql `
|
|
101
|
+
UPDATE ${sql.raw(`"${t}"`)}
|
|
102
|
+
SET ${sql.raw(`"${s.metaValue}"`)} = ${textValue}, ${sql.raw(`"${s.metaValueJson}"`)} = ${jsonStr}::jsonb
|
|
103
|
+
WHERE ${sql.raw(`"${s.metaId}"`)} = ${metaId}
|
|
104
|
+
`);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
async delete(objectId, key, value) {
|
|
108
|
+
if (value !== undefined) {
|
|
109
|
+
const { textValue } = this.serialize(value);
|
|
110
|
+
const result = await this.db
|
|
111
|
+
.delete(this.table)
|
|
112
|
+
.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key), eq(this.columns.metaValue, textValue)))
|
|
113
|
+
.returning({ metaId: this.columns.metaId });
|
|
114
|
+
return result.length;
|
|
115
|
+
}
|
|
116
|
+
const result = await this.db
|
|
117
|
+
.delete(this.table)
|
|
118
|
+
.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))
|
|
119
|
+
.returning({ metaId: this.columns.metaId });
|
|
120
|
+
return result.length;
|
|
121
|
+
}
|
|
122
|
+
async deleteAllForObject(objectId) {
|
|
123
|
+
const result = await this.db
|
|
124
|
+
.delete(this.table)
|
|
125
|
+
.where(eq(this.columns.objectId, objectId))
|
|
126
|
+
.returning({ metaId: this.columns.metaId });
|
|
127
|
+
return result.length;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Convert a raw DB row to MetaEntry using column name mapping.
|
|
131
|
+
*/
|
|
132
|
+
toMetaEntry(row) {
|
|
133
|
+
const ts = this.colNames.ts;
|
|
134
|
+
return {
|
|
135
|
+
metaId: row[ts.metaId],
|
|
136
|
+
objectId: row[ts.objectId],
|
|
137
|
+
metaKey: row[ts.metaKey],
|
|
138
|
+
metaValue: row[ts.metaValue],
|
|
139
|
+
metaValueJson: row[ts.metaValueJson],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
serialize(value) {
|
|
143
|
+
if (value === null || value === undefined) {
|
|
144
|
+
return { textValue: '', jsonValue: null };
|
|
145
|
+
}
|
|
146
|
+
if (typeof value === 'string') {
|
|
147
|
+
try {
|
|
148
|
+
const parsed = JSON.parse(value);
|
|
149
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
150
|
+
return { textValue: value, jsonValue: parsed };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch { /* plain string */ }
|
|
154
|
+
return { textValue: value, jsonValue: null };
|
|
155
|
+
}
|
|
156
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
157
|
+
return { textValue: String(value), jsonValue: null };
|
|
158
|
+
}
|
|
159
|
+
const textValue = JSON.stringify(value);
|
|
160
|
+
return { textValue, jsonValue: value };
|
|
161
|
+
}
|
|
162
|
+
deserialize(entry) {
|
|
163
|
+
if (entry.metaValueJson !== null && entry.metaValueJson !== undefined) {
|
|
164
|
+
return entry.metaValueJson;
|
|
165
|
+
}
|
|
166
|
+
if (entry.metaValue === null)
|
|
167
|
+
return '';
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(entry.metaValue);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return entry.metaValue;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=meta-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-repository.js","sourceRoot":"","sources":["../../src/repositories/meta-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Database } from '../connection.js';
|
|
2
|
+
import { MetaRepository } from './meta-repository.js';
|
|
3
|
+
export interface CreatePostInput {
|
|
4
|
+
postAuthor: number;
|
|
5
|
+
postTitle: string;
|
|
6
|
+
postContent?: string;
|
|
7
|
+
postExcerpt?: string;
|
|
8
|
+
postStatus?: string;
|
|
9
|
+
postName?: string;
|
|
10
|
+
postType?: string;
|
|
11
|
+
postParent?: number;
|
|
12
|
+
postPassword?: string;
|
|
13
|
+
commentStatus?: string;
|
|
14
|
+
pingStatus?: string;
|
|
15
|
+
postMimeType?: string;
|
|
16
|
+
menuOrder?: number;
|
|
17
|
+
guid?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface UpdatePostInput {
|
|
20
|
+
postTitle?: string;
|
|
21
|
+
postContent?: string;
|
|
22
|
+
postExcerpt?: string;
|
|
23
|
+
postStatus?: string;
|
|
24
|
+
postName?: string;
|
|
25
|
+
postType?: string;
|
|
26
|
+
postParent?: number;
|
|
27
|
+
postPassword?: string;
|
|
28
|
+
commentStatus?: string;
|
|
29
|
+
pingStatus?: string;
|
|
30
|
+
menuOrder?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface PostRow {
|
|
33
|
+
id: number;
|
|
34
|
+
postAuthor: number;
|
|
35
|
+
postDate: Date;
|
|
36
|
+
postDateGmt: Date;
|
|
37
|
+
postContent: string;
|
|
38
|
+
postTitle: string;
|
|
39
|
+
postExcerpt: string;
|
|
40
|
+
postStatus: string;
|
|
41
|
+
commentStatus: string;
|
|
42
|
+
pingStatus: string;
|
|
43
|
+
postPassword: string;
|
|
44
|
+
postName: string;
|
|
45
|
+
toPing: string;
|
|
46
|
+
pinged: string;
|
|
47
|
+
postModified: Date;
|
|
48
|
+
postModifiedGmt: Date;
|
|
49
|
+
postContentFiltered: string;
|
|
50
|
+
postParent: number;
|
|
51
|
+
guid: string;
|
|
52
|
+
menuOrder: number;
|
|
53
|
+
postType: string;
|
|
54
|
+
postMimeType: string;
|
|
55
|
+
commentCount: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Repository for posts (all content types).
|
|
59
|
+
*
|
|
60
|
+
* Handles CRUD, slug generation, sticky posts, and provides
|
|
61
|
+
* a MetaRepository instance for post metadata operations.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PostRepository {
|
|
64
|
+
private db;
|
|
65
|
+
readonly meta: MetaRepository;
|
|
66
|
+
constructor(db: Database);
|
|
67
|
+
/**
|
|
68
|
+
* Create a new post.
|
|
69
|
+
*/
|
|
70
|
+
create(input: CreatePostInput): Promise<PostRow>;
|
|
71
|
+
/**
|
|
72
|
+
* Get a post by ID.
|
|
73
|
+
*/
|
|
74
|
+
getById(id: number): Promise<PostRow | undefined>;
|
|
75
|
+
/**
|
|
76
|
+
* Get a post by slug and type.
|
|
77
|
+
*/
|
|
78
|
+
getBySlug(slug: string, postType?: string): Promise<PostRow | undefined>;
|
|
79
|
+
/**
|
|
80
|
+
* Update a post. Returns the updated row or undefined if not found.
|
|
81
|
+
*/
|
|
82
|
+
update(id: number, input: UpdatePostInput): Promise<PostRow | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Trash a post (move to trash status, preserving original status in meta).
|
|
85
|
+
*/
|
|
86
|
+
trash(id: number): Promise<PostRow | undefined>;
|
|
87
|
+
/**
|
|
88
|
+
* Restore a trashed post to its original status.
|
|
89
|
+
*/
|
|
90
|
+
untrash(id: number): Promise<PostRow | undefined>;
|
|
91
|
+
/**
|
|
92
|
+
* Permanently delete a post and all its metadata.
|
|
93
|
+
*/
|
|
94
|
+
deletePermanently(id: number): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Get sticky post IDs.
|
|
97
|
+
*/
|
|
98
|
+
getStickyIds(): Promise<number[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Set a post as sticky or not.
|
|
101
|
+
*/
|
|
102
|
+
setSticky(id: number, sticky: boolean): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Check if a post is sticky.
|
|
105
|
+
*/
|
|
106
|
+
isSticky(id: number): Promise<boolean>;
|
|
107
|
+
/**
|
|
108
|
+
* Count posts by type and status.
|
|
109
|
+
*/
|
|
110
|
+
countByStatus(postType?: string): Promise<Record<string, number>>;
|
|
111
|
+
/**
|
|
112
|
+
* Generate a URL-safe slug from a title.
|
|
113
|
+
*/
|
|
114
|
+
private generateSlug;
|
|
115
|
+
/**
|
|
116
|
+
* Ensure a slug is unique within a post type.
|
|
117
|
+
* Appends -2, -3, etc. if needed.
|
|
118
|
+
*/
|
|
119
|
+
private ensureUniqueSlug;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=post-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-repository.d.ts","sourceRoot":"","sources":["../../src/repositories/post-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAA+C,MAAM,sBAAsB,CAAC;AAEnG,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,IAAI,CAAC;IACnB,eAAe,EAAE,IAAI,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IAGd,OAAO,CAAC,EAAE;IAFtB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAEV,EAAE,EAAE,QAAQ;IAgBhC;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAmCtD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAKvD;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAe,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAStF;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAsC9E;;OAEG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAWrD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAUvD;;OAEG;IACG,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYrD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IASvC;;OAEG;IACG,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3D;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK5C;;OAEG;IACG,aAAa,CAClB,QAAQ,GAAE,MAAe,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAiBlC;;OAEG;IACH,OAAO,CAAC,YAAY;IAYpB;;;OAGG;YACW,gBAAgB;CAyB9B"}
|