@newcms/database 0.1.1 → 0.3.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/dist/index.cjs +1682 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2763 -0
- package/dist/index.d.ts +2763 -14
- package/dist/index.js +1682 -8
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/dist/cache/index.d.ts +0 -3
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/index.js +0 -2
- package/dist/cache/index.js.map +0 -1
- package/dist/cache/object-cache.d.ts +0 -139
- package/dist/cache/object-cache.d.ts.map +0 -1
- package/dist/cache/object-cache.js +0 -321
- package/dist/cache/object-cache.js.map +0 -1
- package/dist/connection.d.ts +0 -18
- package/dist/connection.d.ts.map +0 -1
- package/dist/connection.js +0 -32
- package/dist/connection.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/repositories/meta-repository.d.ts +0 -71
- package/dist/repositories/meta-repository.d.ts.map +0 -1
- package/dist/repositories/meta-repository.js +0 -176
- package/dist/repositories/meta-repository.js.map +0 -1
- package/dist/repositories/options-repository.d.ts +0 -59
- package/dist/repositories/options-repository.d.ts.map +0 -1
- package/dist/repositories/options-repository.js +0 -222
- package/dist/repositories/options-repository.js.map +0 -1
- package/dist/repositories/post-repository.d.ts +0 -121
- package/dist/repositories/post-repository.d.ts.map +0 -1
- package/dist/repositories/post-repository.js +0 -241
- package/dist/repositories/post-repository.js.map +0 -1
- package/dist/repositories/revision-repository.d.ts +0 -50
- package/dist/repositories/revision-repository.d.ts.map +0 -1
- package/dist/repositories/revision-repository.js +0 -149
- package/dist/repositories/revision-repository.js.map +0 -1
- package/dist/repositories/taxonomy-repository.d.ts +0 -63
- package/dist/repositories/taxonomy-repository.d.ts.map +0 -1
- package/dist/repositories/taxonomy-repository.js +0 -268
- package/dist/repositories/taxonomy-repository.js.map +0 -1
- package/dist/schema/comments.d.ts +0 -369
- package/dist/schema/comments.d.ts.map +0 -1
- package/dist/schema/comments.js +0 -47
- package/dist/schema/comments.js.map +0 -1
- package/dist/schema/index.d.ts +0 -9
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -9
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/links.d.ts +0 -245
- package/dist/schema/links.d.ts.map +0 -1
- package/dist/schema/links.js +0 -17
- package/dist/schema/links.js.map +0 -1
- package/dist/schema/options.d.ts +0 -95
- package/dist/schema/options.d.ts.map +0 -1
- package/dist/schema/options.js +0 -12
- package/dist/schema/options.js.map +0 -1
- package/dist/schema/posts.d.ts +0 -509
- package/dist/schema/posts.d.ts.map +0 -1
- package/dist/schema/posts.js +0 -51
- package/dist/schema/posts.js.map +0 -1
- package/dist/schema/scheduled-events.d.ts +0 -156
- package/dist/schema/scheduled-events.d.ts.map +0 -1
- package/dist/schema/scheduled-events.js +0 -22
- package/dist/schema/scheduled-events.js.map +0 -1
- package/dist/schema/sessions.d.ts +0 -157
- package/dist/schema/sessions.d.ts.map +0 -1
- package/dist/schema/sessions.js +0 -26
- package/dist/schema/sessions.js.map +0 -1
- package/dist/schema/terms.d.ts +0 -343
- package/dist/schema/terms.d.ts.map +0 -1
- package/dist/schema/terms.js +0 -46
- package/dist/schema/terms.js.map +0 -1
- package/dist/schema/users.d.ts +0 -288
- package/dist/schema/users.d.ts.map +0 -1
- package/dist/schema/users.js +0 -30
- package/dist/schema/users.js.map +0 -1
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;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"}
|
|
1
|
+
{"version":3,"sources":["../src/connection.ts","../src/schema/index.ts","../src/schema/posts.ts","../src/schema/users.ts","../src/schema/comments.ts","../src/schema/terms.ts","../src/schema/options.ts","../src/schema/links.ts","../src/schema/sessions.ts","../src/schema/scheduled-events.ts","../src/cache/object-cache.ts","../src/repositories/options-repository.ts","../src/repositories/post-repository.ts","../src/repositories/meta-repository.ts","../src/repositories/taxonomy-repository.ts","../src/repositories/revision-repository.ts","../src/repositories/comment-repository.ts"],"sourcesContent":["import { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport * as schema from './schema/index';\n\nexport interface DatabaseConfig {\n\thost: string;\n\tport: number;\n\tdatabase: string;\n\tuser: string;\n\tpassword: string;\n\tmaxConnections?: number;\n}\n\nfunction getConfigFromEnv(): DatabaseConfig {\n\tconst password = process.env['DB_PASSWORD'];\n\tif (!password) {\n\t\tthrow new Error(\n\t\t\t'DB_PASSWORD environment variable is required. ' +\n\t\t\t\t'Set it in your .env file or environment.',\n\t\t);\n\t}\n\n\treturn {\n\t\thost: process.env['DB_HOST'] ?? 'localhost',\n\t\tport: parseInt(process.env['DB_PORT'] ?? '5432', 10),\n\t\tdatabase: process.env['DB_NAME'] ?? 'newcms',\n\t\tuser: process.env['DB_USER'] ?? 'newcms',\n\t\tpassword,\n\t\tmaxConnections: parseInt(process.env['DB_MAX_CONNECTIONS'] ?? '10', 10),\n\t};\n}\n\nexport function createConnection(config?: DatabaseConfig) {\n\tconst cfg = config ?? getConfigFromEnv();\n\n\tconst client = postgres({\n\t\thost: cfg.host,\n\t\tport: cfg.port,\n\t\tdatabase: cfg.database,\n\t\tuser: cfg.user,\n\t\tpassword: cfg.password,\n\t\tmax: cfg.maxConnections ?? 10,\n\t});\n\n\tconst db = drizzle(client, { schema });\n\n\treturn { db, client };\n}\n\nexport type Database = ReturnType<typeof createConnection>['db'];\n","export { posts, postmeta } from './posts';\nexport { users, usermeta } from './users';\nexport { comments, commentmeta } from './comments';\nexport { terms, termTaxonomy, termRelationships, termmeta } from './terms';\nexport { options } from './options';\nexport { links } from './links';\nexport { sessions } from './sessions';\nexport { scheduledEvents } from './scheduled-events';\n","import {\n\tpgTable,\n\tserial,\n\tbigint,\n\ttext,\n\ttimestamp,\n\tvarchar,\n\tindex,\n\tjsonb,\n} from 'drizzle-orm/pg-core';\nimport { users } from './users';\n\nexport const posts = pgTable(\n\t'posts',\n\t{\n\t\tid: serial('id').primaryKey(),\n\t\tpostAuthor: bigint('post_author', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.default(0)\n\t\t\t.references(() => users.id),\n\t\tpostDate: timestamp('post_date', { withTimezone: true }).notNull().defaultNow(),\n\t\tpostDateGmt: timestamp('post_date_gmt', { withTimezone: true }).notNull().defaultNow(),\n\t\tpostContent: text('post_content').notNull().default(''),\n\t\tpostTitle: text('post_title').notNull().default(''),\n\t\tpostExcerpt: text('post_excerpt').notNull().default(''),\n\t\tpostStatus: varchar('post_status', { length: 20 }).notNull().default('publish'),\n\t\tcommentStatus: varchar('comment_status', { length: 20 }).notNull().default('open'),\n\t\tpingStatus: varchar('ping_status', { length: 20 }).notNull().default('open'),\n\t\tpostPassword: varchar('post_password', { length: 255 }).notNull().default(''),\n\t\tpostName: varchar('post_name', { length: 200 }).notNull().default(''),\n\t\ttoPing: text('to_ping').notNull().default(''),\n\t\tpinged: text('pinged').notNull().default(''),\n\t\tpostModified: timestamp('post_modified', { withTimezone: true }).notNull().defaultNow(),\n\t\tpostModifiedGmt: timestamp('post_modified_gmt', { withTimezone: true })\n\t\t\t.notNull()\n\t\t\t.defaultNow(),\n\t\tpostContentFiltered: text('post_content_filtered').notNull().default(''),\n\t\tpostParent: bigint('post_parent', { mode: 'number' }).notNull().default(0),\n\t\tguid: varchar('guid', { length: 255 }).notNull().default(''),\n\t\tmenuOrder: bigint('menu_order', { mode: 'number' }).notNull().default(0),\n\t\tpostType: varchar('post_type', { length: 20 }).notNull().default('post'),\n\t\tpostMimeType: varchar('post_mime_type', { length: 100 }).notNull().default(''),\n\t\tcommentCount: bigint('comment_count', { mode: 'number' }).notNull().default(0),\n\t},\n\t(table) => [\n\t\tindex('idx_post_name').on(table.postName),\n\t\tindex('idx_post_type_status_date').on(table.postType, table.postStatus, table.postDate),\n\t\tindex('idx_post_parent').on(table.postParent),\n\t\tindex('idx_post_author').on(table.postAuthor),\n\t],\n);\n\nexport const postmeta = pgTable(\n\t'postmeta',\n\t{\n\t\tmetaId: serial('meta_id').primaryKey(),\n\t\tpostId: bigint('post_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => posts.id, { onDelete: 'cascade' }),\n\t\tmetaKey: varchar('meta_key', { length: 255 }),\n\t\tmetaValue: text('meta_value'),\n\t\tmetaValueJson: jsonb('meta_value_json'),\n\t},\n\t(table) => [\n\t\tindex('idx_postmeta_post_id').on(table.postId),\n\t\tindex('idx_postmeta_meta_key').on(table.metaKey),\n\t\tindex('idx_postmeta_post_key').on(table.postId, table.metaKey),\n\t],\n);\n","import {\n\tpgTable,\n\tserial,\n\ttext,\n\ttimestamp,\n\tvarchar,\n\tindex,\n\tjsonb,\n} from 'drizzle-orm/pg-core';\n\nexport const users = pgTable(\n\t'users',\n\t{\n\t\tid: serial('id').primaryKey(),\n\t\tuserLogin: varchar('user_login', { length: 60 }).notNull().unique(),\n\t\tuserPass: varchar('user_pass', { length: 255 }).notNull().default(''),\n\t\tuserNicename: varchar('user_nicename', { length: 50 }).notNull().default(''),\n\t\tuserEmail: varchar('user_email', { length: 100 }).notNull().unique(),\n\t\tuserUrl: varchar('user_url', { length: 100 }).notNull().default(''),\n\t\tuserRegistered: timestamp('user_registered', { withTimezone: true }).notNull().defaultNow(),\n\t\tuserActivationKey: varchar('user_activation_key', { length: 255 }).notNull().default(''),\n\t\tuserStatus: varchar('user_status', { length: 20 }).notNull().default('active'),\n\t\tdisplayName: varchar('display_name', { length: 250 }).notNull().default(''),\n\t},\n\t(table) => [\n\t\tindex('idx_user_login').on(table.userLogin),\n\t\tindex('idx_user_nicename').on(table.userNicename),\n\t\tindex('idx_user_email').on(table.userEmail),\n\t],\n);\n\nexport const usermeta = pgTable(\n\t'usermeta',\n\t{\n\t\tumetaId: serial('umeta_id').primaryKey(),\n\t\tuserId: serial('user_id')\n\t\t\t.notNull()\n\t\t\t.references(() => users.id, { onDelete: 'cascade' }),\n\t\tmetaKey: varchar('meta_key', { length: 255 }),\n\t\tmetaValue: text('meta_value'),\n\t\tmetaValueJson: jsonb('meta_value_json'),\n\t},\n\t(table) => [\n\t\tindex('idx_usermeta_user_id').on(table.userId),\n\t\tindex('idx_usermeta_meta_key').on(table.metaKey),\n\t],\n);\n","import {\n\tpgTable,\n\tserial,\n\tbigint,\n\ttext,\n\ttimestamp,\n\tvarchar,\n\tindex,\n\tjsonb,\n} from 'drizzle-orm/pg-core';\nimport { posts } from './posts';\nimport { users } from './users';\n\nexport const comments = pgTable(\n\t'comments',\n\t{\n\t\tcommentId: serial('comment_id').primaryKey(),\n\t\tcommentPostId: bigint('comment_post_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.default(0)\n\t\t\t.references(() => posts.id, { onDelete: 'cascade' }),\n\t\tcommentAuthor: text('comment_author').notNull().default(''),\n\t\tcommentAuthorEmail: varchar('comment_author_email', { length: 100 })\n\t\t\t.notNull()\n\t\t\t.default(''),\n\t\tcommentAuthorUrl: varchar('comment_author_url', { length: 200 }).notNull().default(''),\n\t\tcommentAuthorIp: varchar('comment_author_ip', { length: 100 }).notNull().default(''),\n\t\tcommentDate: timestamp('comment_date', { withTimezone: true }).notNull().defaultNow(),\n\t\tcommentDateGmt: timestamp('comment_date_gmt', { withTimezone: true }).notNull().defaultNow(),\n\t\tcommentContent: text('comment_content').notNull().default(''),\n\t\tcommentKarma: bigint('comment_karma', { mode: 'number' }).notNull().default(0),\n\t\tcommentApproved: varchar('comment_approved', { length: 20 }).notNull().default('1'),\n\t\tcommentAgent: varchar('comment_agent', { length: 255 }).notNull().default(''),\n\t\tcommentType: varchar('comment_type', { length: 20 }).notNull().default('comment'),\n\t\tcommentParent: bigint('comment_parent', { mode: 'number' }).notNull().default(0),\n\t\tuserId: bigint('user_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.default(0)\n\t\t\t.references(() => users.id),\n\t},\n\t(table) => [\n\t\tindex('idx_comment_post_id').on(table.commentPostId),\n\t\tindex('idx_comment_approved_date_gmt').on(table.commentApproved, table.commentDateGmt),\n\t\tindex('idx_comment_date_gmt').on(table.commentDateGmt),\n\t\tindex('idx_comment_parent').on(table.commentParent),\n\t\tindex('idx_comment_author_email').on(table.commentAuthorEmail),\n\t],\n);\n\nexport const commentmeta = pgTable(\n\t'commentmeta',\n\t{\n\t\tmetaId: serial('meta_id').primaryKey(),\n\t\tcommentId: bigint('comment_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => comments.commentId, { onDelete: 'cascade' }),\n\t\tmetaKey: varchar('meta_key', { length: 255 }),\n\t\tmetaValue: text('meta_value'),\n\t\tmetaValueJson: jsonb('meta_value_json'),\n\t},\n\t(table) => [\n\t\tindex('idx_commentmeta_comment_id').on(table.commentId),\n\t\tindex('idx_commentmeta_meta_key').on(table.metaKey),\n\t],\n);\n","import {\n\tpgTable,\n\tserial,\n\tbigint,\n\ttext,\n\tvarchar,\n\tindex,\n\tjsonb,\n\tuniqueIndex,\n\tprimaryKey,\n} from 'drizzle-orm/pg-core';\nimport { posts } from './posts';\n\nexport const terms = pgTable(\n\t'terms',\n\t{\n\t\ttermId: serial('term_id').primaryKey(),\n\t\tname: varchar('name', { length: 200 }).notNull().default(''),\n\t\tslug: varchar('slug', { length: 200 }).notNull().default(''),\n\t\ttermGroup: bigint('term_group', { mode: 'number' }).notNull().default(0),\n\t},\n\t(table) => [index('idx_term_slug').on(table.slug), index('idx_term_name').on(table.name)],\n);\n\nexport const termTaxonomy = pgTable(\n\t'term_taxonomy',\n\t{\n\t\ttermTaxonomyId: serial('term_taxonomy_id').primaryKey(),\n\t\ttermId: bigint('term_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => terms.termId, { onDelete: 'cascade' }),\n\t\ttaxonomy: varchar('taxonomy', { length: 32 }).notNull().default(''),\n\t\tdescription: text('description').notNull().default(''),\n\t\tparent: bigint('parent', { mode: 'number' }).notNull().default(0),\n\t\tcount: bigint('count', { mode: 'number' }).notNull().default(0),\n\t},\n\t(table) => [\n\t\tuniqueIndex('idx_term_id_taxonomy').on(table.termId, table.taxonomy),\n\t\tindex('idx_taxonomy').on(table.taxonomy),\n\t],\n);\n\nexport const termRelationships = pgTable(\n\t'term_relationships',\n\t{\n\t\tobjectId: bigint('object_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => posts.id, { onDelete: 'cascade' }),\n\t\ttermTaxonomyId: bigint('term_taxonomy_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => termTaxonomy.termTaxonomyId, { onDelete: 'cascade' }),\n\t\ttermOrder: bigint('term_order', { mode: 'number' }).notNull().default(0),\n\t},\n\t(table) => [\n\t\tprimaryKey({ columns: [table.objectId, table.termTaxonomyId] }),\n\t\tindex('idx_term_taxonomy_id').on(table.termTaxonomyId),\n\t],\n);\n\nexport const termmeta = pgTable(\n\t'termmeta',\n\t{\n\t\tmetaId: serial('meta_id').primaryKey(),\n\t\ttermId: bigint('term_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => terms.termId, { onDelete: 'cascade' }),\n\t\tmetaKey: varchar('meta_key', { length: 255 }),\n\t\tmetaValue: text('meta_value'),\n\t\tmetaValueJson: jsonb('meta_value_json'),\n\t},\n\t(table) => [\n\t\tindex('idx_termmeta_term_id').on(table.termId),\n\t\tindex('idx_termmeta_meta_key').on(table.metaKey),\n\t],\n);\n","import {\n\tpgTable,\n\tserial,\n\ttext,\n\tvarchar,\n\tboolean,\n\tjsonb,\n\tuniqueIndex,\n\tindex,\n} from 'drizzle-orm/pg-core';\n\nexport const options = pgTable(\n\t'options',\n\t{\n\t\toptionId: serial('option_id').primaryKey(),\n\t\toptionName: varchar('option_name', { length: 191 }).notNull().unique(),\n\t\toptionValue: text('option_value').notNull().default(''),\n\t\toptionValueJson: jsonb('option_value_json'),\n\t\tautoload: boolean('autoload').notNull().default(true),\n\t},\n\t(table) => [\n\t\tuniqueIndex('idx_option_name').on(table.optionName),\n\t\tindex('idx_autoload').on(table.autoload),\n\t],\n);\n","import {\n\tpgTable,\n\tserial,\n\ttext,\n\ttimestamp,\n\tvarchar,\n\tbigint,\n\tindex,\n} from 'drizzle-orm/pg-core';\n\nexport const links = pgTable(\n\t'links',\n\t{\n\t\tlinkId: serial('link_id').primaryKey(),\n\t\tlinkUrl: varchar('link_url', { length: 255 }).notNull().default(''),\n\t\tlinkName: varchar('link_name', { length: 255 }).notNull().default(''),\n\t\tlinkImage: varchar('link_image', { length: 255 }).notNull().default(''),\n\t\tlinkTarget: varchar('link_target', { length: 25 }).notNull().default(''),\n\t\tlinkDescription: varchar('link_description', { length: 255 }).notNull().default(''),\n\t\tlinkVisible: varchar('link_visible', { length: 20 }).notNull().default('Y'),\n\t\tlinkOwner: bigint('link_owner', { mode: 'number' }).notNull().default(1),\n\t\tlinkRating: bigint('link_rating', { mode: 'number' }).notNull().default(0),\n\t\tlinkUpdated: timestamp('link_updated', { withTimezone: true }).notNull().defaultNow(),\n\t\tlinkRel: varchar('link_rel', { length: 255 }).notNull().default(''),\n\t\tlinkNotes: text('link_notes').notNull().default(''),\n\t\tlinkRss: varchar('link_rss', { length: 255 }).notNull().default(''),\n\t},\n\t(table) => [index('idx_link_visible').on(table.linkVisible)],\n);\n","import { pgTable, serial, bigint, varchar, timestamp, jsonb, index } from 'drizzle-orm/pg-core';\nimport { users } from './users';\n\n/**\n * Dedicated sessions table — replaces serialized session tokens\n * stored as user metadata in the original system.\n *\n * Note: Primary session storage is in Redis for O(1) access.\n * This table serves as persistent backup and for admin visibility.\n */\nexport const sessions = pgTable(\n\t'sessions',\n\t{\n\t\tid: serial('id').primaryKey(),\n\t\tuserId: bigint('user_id', { mode: 'number' })\n\t\t\t.notNull()\n\t\t\t.references(() => users.id, { onDelete: 'cascade' }),\n\t\ttokenHash: varchar('token_hash', { length: 255 }).notNull().unique(),\n\t\tcreatedAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n\t\texpiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\t\tip: varchar('ip', { length: 45 }).notNull().default(''),\n\t\tuserAgent: varchar('user_agent', { length: 500 }).notNull().default(''),\n\t\tdata: jsonb('data'),\n\t},\n\t(table) => [\n\t\tindex('idx_session_user_id').on(table.userId),\n\t\tindex('idx_session_token_hash').on(table.tokenHash),\n\t\tindex('idx_session_expires_at').on(table.expiresAt),\n\t],\n);\n","import { pgTable, serial, varchar, timestamp, jsonb, bigint, index } from 'drizzle-orm/pg-core';\n\n/**\n * Dedicated scheduled_events table — replaces the serialized cron\n * array stored in the options table in the original system.\n *\n * This is a persistent record. Actual scheduling is managed by BullMQ.\n */\nexport const scheduledEvents = pgTable(\n\t'scheduled_events',\n\t{\n\t\tid: serial('id').primaryKey(),\n\t\thook: varchar('hook', { length: 255 }).notNull(),\n\t\targs: jsonb('args').notNull().default('[]'),\n\t\tschedule: varchar('schedule', { length: 50 }),\n\t\tintervalSeconds: bigint('interval_seconds', { mode: 'number' }),\n\t\tnextRunAt: timestamp('next_run_at', { withTimezone: true }).notNull(),\n\t\tcreatedAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n\t\tstatus: varchar('status', { length: 20 }).notNull().default('pending'),\n\t},\n\t(table) => [\n\t\tindex('idx_scheduled_hook').on(table.hook),\n\t\tindex('idx_scheduled_next_run').on(table.nextRunAt),\n\t\tindex('idx_scheduled_status').on(table.status),\n\t],\n);\n","import Redis from 'ioredis';\n\nexport interface ObjectCacheConfig {\n\thost: string;\n\tport: number;\n\tkeyPrefix?: string;\n\tdefaultTtl?: number;\n}\n\nexport interface CacheGroupConfig {\n\t/** TTL in seconds. 0 = no expiry */\n\tttl: number;\n\t/** Whether this group is global (shared across sites in multisite) */\n\tglobal: boolean;\n}\n\n/**\n * Redis-backed object cache implementing the CMS cache API.\n *\n * Features:\n * - Group-based key namespacing\n * - Global vs per-site groups (multisite support)\n * - Configurable TTL per group\n * - Batch get/set operations via pipeline\n * - Flush by group or total flush\n * - Increment/decrement operations\n */\nexport class ObjectCache {\n\tprivate redis: Redis;\n\tprivate siteId: number = 1;\n\tprivate keyPrefix: string;\n\tprivate defaultTtl: number;\n\n\t/** Groups that are shared across all sites (multisite) */\n\tprivate globalGroups: Set<string> = new Set();\n\n\t/** Per-group TTL overrides (in seconds) */\n\tprivate groupTtl: Map<string, number> = new Map();\n\n\t/** Local in-memory cache for the current request (non-persistent) */\n\tprivate localCache: Map<string, unknown> = new Map();\n\n\tconstructor(config: ObjectCacheConfig) {\n\t\tthis.redis = new Redis({\n\t\t\thost: config.host,\n\t\t\tport: config.port,\n\t\t\tlazyConnect: true,\n\t\t\tmaxRetriesPerRequest: 3,\n\t\t});\n\t\tthis.keyPrefix = config.keyPrefix ?? 'cache';\n\t\tthis.defaultTtl = config.defaultTtl ?? 0;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tawait this.redis.connect();\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tawait this.redis.quit();\n\t}\n\n\t/**\n\t * Set the current site ID (for multisite per-site cache isolation).\n\t */\n\tsetSiteId(siteId: number): void {\n\t\tthis.siteId = siteId;\n\t}\n\n\t/**\n\t * Register groups as global (shared across sites in multisite).\n\t */\n\taddGlobalGroups(groups: string[]): void {\n\t\tfor (const group of groups) {\n\t\t\tthis.globalGroups.add(group);\n\t\t}\n\t}\n\n\t/**\n\t * Set TTL for a specific cache group.\n\t */\n\tsetGroupTtl(group: string, ttlSeconds: number): void {\n\t\tthis.groupTtl.set(group, ttlSeconds);\n\t}\n\n\t/**\n\t * Build the Redis key for a cache entry.\n\t */\n\tprivate buildKey(key: string, group: string = 'default'): string {\n\t\tif (this.globalGroups.has(group)) {\n\t\t\treturn `${this.keyPrefix}:global:${group}:${key}`;\n\t\t}\n\t\treturn `${this.keyPrefix}:site:${this.siteId}:${group}:${key}`;\n\t}\n\n\t/**\n\t * Build a local cache key (in-memory, for current request).\n\t */\n\tprivate buildLocalKey(key: string, group: string = 'default'): string {\n\t\tif (this.globalGroups.has(group)) {\n\t\t\treturn `global:${group}:${key}`;\n\t\t}\n\t\treturn `site:${this.siteId}:${group}:${key}`;\n\t}\n\n\t/**\n\t * Get TTL for a group in seconds.\n\t */\n\tprivate getTtl(group: string): number {\n\t\treturn this.groupTtl.get(group) ?? this.defaultTtl;\n\t}\n\n\t/**\n\t * Get a cached value.\n\t *\n\t * @returns The cached value, or undefined if not found\n\t */\n\tasync get<T = unknown>(key: string, group: string = 'default'): Promise<T | undefined> {\n\t\t// Check local cache first\n\t\tconst localKey = this.buildLocalKey(key, group);\n\t\tif (this.localCache.has(localKey)) {\n\t\t\treturn this.localCache.get(localKey) as T;\n\t\t}\n\n\t\tconst redisKey = this.buildKey(key, group);\n\t\tconst raw = await this.redis.get(redisKey);\n\n\t\tif (raw === null) return undefined;\n\n\t\ttry {\n\t\t\tconst value = JSON.parse(raw) as T;\n\t\t\tthis.localCache.set(localKey, value);\n\t\t\treturn value;\n\t\t} catch {\n\t\t\treturn raw as T;\n\t\t}\n\t}\n\n\t/**\n\t * Set a cached value.\n\t *\n\t * @param key - Cache key\n\t * @param value - Value to cache (will be JSON serialized)\n\t * @param group - Cache group\n\t * @param ttl - TTL in seconds (overrides group default). 0 = no expiry.\n\t * @returns true on success\n\t */\n\tasync set(\n\t\tkey: string,\n\t\tvalue: unknown,\n\t\tgroup: string = 'default',\n\t\tttl?: number,\n\t): Promise<boolean> {\n\t\tconst redisKey = this.buildKey(key, group);\n\t\tconst serialized = JSON.stringify(value);\n\t\tconst effectiveTtl = ttl ?? this.getTtl(group);\n\n\t\tif (effectiveTtl > 0) {\n\t\t\tawait this.redis.setex(redisKey, effectiveTtl, serialized);\n\t\t} else {\n\t\t\tawait this.redis.set(redisKey, serialized);\n\t\t}\n\n\t\tconst localKey = this.buildLocalKey(key, group);\n\t\tthis.localCache.set(localKey, value);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Add a cached value only if it doesn't already exist.\n\t *\n\t * @returns true if the value was added, false if key already exists\n\t */\n\tasync add(\n\t\tkey: string,\n\t\tvalue: unknown,\n\t\tgroup: string = 'default',\n\t\tttl?: number,\n\t): Promise<boolean> {\n\t\tconst redisKey = this.buildKey(key, group);\n\t\tconst serialized = JSON.stringify(value);\n\t\tconst effectiveTtl = ttl ?? this.getTtl(group);\n\n\t\tlet result: string | null;\n\t\tif (effectiveTtl > 0) {\n\t\t\tresult = await this.redis.set(redisKey, serialized, 'EX', effectiveTtl, 'NX');\n\t\t} else {\n\t\t\tresult = await this.redis.set(redisKey, serialized, 'NX');\n\t\t}\n\n\t\tif (result === 'OK') {\n\t\t\tconst localKey = this.buildLocalKey(key, group);\n\t\t\tthis.localCache.set(localKey, value);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Delete a cached value.\n\t *\n\t * @returns true if the key existed and was deleted\n\t */\n\tasync delete(key: string, group: string = 'default'): Promise<boolean> {\n\t\tconst redisKey = this.buildKey(key, group);\n\t\tconst count = await this.redis.del(redisKey);\n\n\t\tconst localKey = this.buildLocalKey(key, group);\n\t\tthis.localCache.delete(localKey);\n\n\t\treturn count > 0;\n\t}\n\n\t/**\n\t * Increment a numeric cached value.\n\t *\n\t * @returns The new value, or false if key doesn't exist\n\t */\n\tasync incr(key: string, group: string = 'default', offset: number = 1): Promise<number | false> {\n\t\tconst redisKey = this.buildKey(key, group);\n\t\tconst keyExists = await this.redis.exists(redisKey);\n\t\tif (!keyExists) return false;\n\n\t\tconst current = await this.get<number>(key, group);\n\t\tif (current === undefined || typeof current !== 'number') return false;\n\n\t\tconst newValue = current + offset;\n\t\tawait this.set(key, newValue, group);\n\t\treturn newValue;\n\t}\n\n\t/**\n\t * Decrement a numeric cached value.\n\t *\n\t * @returns The new value, or false if key doesn't exist\n\t */\n\tasync decr(key: string, group: string = 'default', offset: number = 1): Promise<number | false> {\n\t\treturn this.incr(key, group, -offset);\n\t}\n\n\t/**\n\t * Get multiple cached values at once.\n\t *\n\t * @returns Map of key -> value (missing keys are not included)\n\t */\n\tasync getMultiple(\n\t\tkeys: string[],\n\t\tgroup: string = 'default',\n\t): Promise<Map<string, unknown>> {\n\t\tconst result = new Map<string, unknown>();\n\t\tif (keys.length === 0) return result;\n\n\t\tconst redisKeys = keys.map((k) => this.buildKey(k, group));\n\t\tconst values = await this.redis.mget(...redisKeys);\n\n\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\tconst raw = values[i];\n\t\t\tif (raw !== null) {\n\t\t\t\ttry {\n\t\t\t\t\tconst value = JSON.parse(raw);\n\t\t\t\t\tresult.set(keys[i], value);\n\t\t\t\t\tconst localKey = this.buildLocalKey(keys[i], group);\n\t\t\t\t\tthis.localCache.set(localKey, value);\n\t\t\t\t} catch {\n\t\t\t\t\tresult.set(keys[i], raw);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Set multiple cached values at once.\n\t */\n\tasync setMultiple(\n\t\tentries: Map<string, unknown> | Record<string, unknown>,\n\t\tgroup: string = 'default',\n\t\tttl?: number,\n\t): Promise<boolean> {\n\t\tconst items = entries instanceof Map ? entries : new Map(Object.entries(entries));\n\t\tif (items.size === 0) return true;\n\n\t\tconst effectiveTtl = ttl ?? this.getTtl(group);\n\t\tconst pipeline = this.redis.pipeline();\n\n\t\tfor (const [key, value] of items) {\n\t\t\tconst redisKey = this.buildKey(key, group);\n\t\t\tconst serialized = JSON.stringify(value);\n\n\t\t\tif (effectiveTtl > 0) {\n\t\t\t\tpipeline.setex(redisKey, effectiveTtl, serialized);\n\t\t\t} else {\n\t\t\t\tpipeline.set(redisKey, serialized);\n\t\t\t}\n\n\t\t\tconst localKey = this.buildLocalKey(key, group);\n\t\t\tthis.localCache.set(localKey, value);\n\t\t}\n\n\t\tawait pipeline.exec();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Flush all keys in a specific group using SCAN + pipeline DEL.\n\t *\n\t * @returns Number of keys deleted\n\t */\n\tasync flushGroup(group: string): Promise<number> {\n\t\tconst pattern = this.globalGroups.has(group)\n\t\t\t? `${this.keyPrefix}:global:${group}:*`\n\t\t\t: `${this.keyPrefix}:site:${this.siteId}:${group}:*`;\n\n\t\tlet deleted = 0;\n\t\tlet cursor = '0';\n\n\t\tdo {\n\t\t\tconst [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);\n\t\t\tcursor = nextCursor;\n\n\t\t\tif (keys.length > 0) {\n\t\t\t\tconst pipeline = this.redis.pipeline();\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\tpipeline.del(key);\n\t\t\t\t}\n\t\t\t\tawait pipeline.exec();\n\t\t\t\tdeleted += keys.length;\n\t\t\t}\n\t\t} while (cursor !== '0');\n\n\t\t// Clear local cache for this group\n\t\tfor (const localKey of this.localCache.keys()) {\n\t\t\tif (localKey.startsWith(`${group}:`)) {\n\t\t\t\tthis.localCache.delete(localKey);\n\t\t\t}\n\t\t}\n\n\t\treturn deleted;\n\t}\n\n\t/**\n\t * Flush all cached data (all groups, all sites).\n\t * Uses SCAN + DEL to only clear cache keys (not other Redis data).\n\t *\n\t * @returns Number of keys deleted\n\t */\n\tasync flushAll(): Promise<number> {\n\t\tconst pattern = `${this.keyPrefix}:*`;\n\t\tlet deleted = 0;\n\t\tlet cursor = '0';\n\n\t\tdo {\n\t\t\tconst [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);\n\t\t\tcursor = nextCursor;\n\n\t\t\tif (keys.length > 0) {\n\t\t\t\tconst pipeline = this.redis.pipeline();\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\tpipeline.del(key);\n\t\t\t\t}\n\t\t\t\tawait pipeline.exec();\n\t\t\t\tdeleted += keys.length;\n\t\t\t}\n\t\t} while (cursor !== '0');\n\n\t\tthis.localCache.clear();\n\t\treturn deleted;\n\t}\n\n\t/**\n\t * Clear only the local in-memory cache (for request boundary cleanup).\n\t */\n\tclearLocalCache(): void {\n\t\tthis.localCache.clear();\n\t}\n\n\t/**\n\t * Check if a key exists in cache.\n\t */\n\tasync exists(key: string, group: string = 'default'): Promise<boolean> {\n\t\tconst localKey = this.buildLocalKey(key, group);\n\t\tif (this.localCache.has(localKey)) return true;\n\n\t\tconst redisKey = this.buildKey(key, group);\n\t\treturn (await this.redis.exists(redisKey)) === 1;\n\t}\n\n\t/**\n\t * Get the underlying Redis instance (for advanced operations).\n\t */\n\tgetRedis(): Redis {\n\t\treturn this.redis;\n\t}\n}\n","import { eq } from 'drizzle-orm';\nimport type { Database } from '../connection';\nimport { options } from '../schema/options';\nimport type { ObjectCache } from '../cache/object-cache';\n\nconst CACHE_GROUP = 'options';\nconst AUTOLOAD_CACHE_KEY = '__autoloaded';\nconst NOT_FOUND_GROUP = 'options_nf';\n\n/**\n * Options repository — CRUD for site options with integrated Redis cache.\n *\n * Behaviors per spec:\n * - Options marked as autoload=true are pre-loaded into cache together\n * - Cache of \"not found\" keys prevents repeated DB misses\n * - Complex values (objects/arrays) stored as JSONB in addition to text\n * - Write operations invalidate cache granularly\n */\nexport class OptionsRepository {\n\tconstructor(\n\t\tprivate db: Database,\n\t\tprivate cache: ObjectCache,\n\t) {}\n\n\t/**\n\t * Get an option value.\n\t *\n\t * Lookup order:\n\t * 1. Redis cache (group \"options\")\n\t * 2. \"Not found\" cache (avoids repeated DB queries for missing keys)\n\t * 3. Database\n\t */\n\tasync getOption<T = string>(name: string, defaultValue?: T): Promise<T | undefined> {\n\t\t// 1. Check cache\n\t\tconst cached = await this.cache.get<T>(name, CACHE_GROUP);\n\t\tif (cached !== undefined) return cached;\n\n\t\t// 2. Check \"not found\" cache\n\t\tconst isNotFound = await this.cache.get(name, NOT_FOUND_GROUP);\n\t\tif (isNotFound !== undefined) return defaultValue;\n\n\t\t// 3. Query database\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(options)\n\t\t\t.where(eq(options.optionName, name))\n\t\t\t.limit(1);\n\n\t\tif (rows.length === 0) {\n\t\t\t// Cache as \"not found\" to avoid future DB queries\n\t\t\tawait this.cache.set(name, true, NOT_FOUND_GROUP, 3600);\n\t\t\treturn defaultValue;\n\t\t}\n\n\t\tconst row = rows[0];\n\t\tconst value = this.deserializeValue<T>(row);\n\n\t\t// Cache the value\n\t\tawait this.cache.set(name, value, CACHE_GROUP);\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Add a new option. Fails if option already exists.\n\t *\n\t * @returns true if the option was created\n\t */\n\tasync addOption(\n\t\tname: string,\n\t\tvalue: unknown,\n\t\tautoload: boolean = true,\n\t): Promise<boolean> {\n\t\t// Check if already exists\n\t\tconst existing = await this.db\n\t\t\t.select({ optionId: options.optionId })\n\t\t\t.from(options)\n\t\t\t.where(eq(options.optionName, name))\n\t\t\t.limit(1);\n\n\t\tif (existing.length > 0) return false;\n\n\t\tconst { textValue, jsonValue } = this.serializeValue(value);\n\n\t\tawait this.db.insert(options).values({\n\t\t\toptionName: name,\n\t\t\toptionValue: textValue,\n\t\t\toptionValueJson: jsonValue,\n\t\t\tautoload,\n\t\t});\n\n\t\t// Update cache\n\t\tawait this.cache.set(name, value, CACHE_GROUP);\n\t\tawait this.cache.delete(name, NOT_FOUND_GROUP);\n\n\t\t// Invalidate autoload cache if this is an autoloaded option\n\t\tif (autoload) {\n\t\t\tawait this.cache.delete(AUTOLOAD_CACHE_KEY, CACHE_GROUP);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Update an existing option, or create it if it doesn't exist.\n\t *\n\t * @returns true if the value was changed\n\t */\n\tasync updateOption(\n\t\tname: string,\n\t\tvalue: unknown,\n\t\tautoload?: boolean,\n\t): Promise<boolean> {\n\t\tconst { textValue, jsonValue } = this.serializeValue(value);\n\n\t\t// Try to update\n\t\tconst existing = await this.db\n\t\t\t.select()\n\t\t\t.from(options)\n\t\t\t.where(eq(options.optionName, name))\n\t\t\t.limit(1);\n\n\t\tif (existing.length === 0) {\n\t\t\t// Option doesn't exist — create it\n\t\t\treturn this.addOption(name, value, autoload ?? true);\n\t\t}\n\n\t\tconst row = existing[0];\n\n\t\t// Check if value actually changed\n\t\tif (row.optionValue === textValue && autoload === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst updateData: Record<string, unknown> = {\n\t\t\toptionValue: textValue,\n\t\t\toptionValueJson: jsonValue,\n\t\t};\n\n\t\tif (autoload !== undefined) {\n\t\t\tupdateData['autoload'] = autoload;\n\t\t}\n\n\t\tawait this.db\n\t\t\t.update(options)\n\t\t\t.set(updateData)\n\t\t\t.where(eq(options.optionName, name));\n\n\t\t// Update cache\n\t\tawait this.cache.set(name, value, CACHE_GROUP);\n\t\tawait this.cache.delete(name, NOT_FOUND_GROUP);\n\n\t\t// Invalidate autoload cache\n\t\tawait this.cache.delete(AUTOLOAD_CACHE_KEY, CACHE_GROUP);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete an option.\n\t *\n\t * @returns true if the option existed and was deleted\n\t */\n\tasync deleteOption(name: string): Promise<boolean> {\n\t\tconst result = await this.db\n\t\t\t.delete(options)\n\t\t\t.where(eq(options.optionName, name))\n\t\t\t.returning({ optionId: options.optionId });\n\n\t\tif (result.length === 0) return false;\n\n\t\t// Remove from cache\n\t\tawait this.cache.delete(name, CACHE_GROUP);\n\t\tawait this.cache.delete(name, NOT_FOUND_GROUP);\n\t\tawait this.cache.delete(AUTOLOAD_CACHE_KEY, CACHE_GROUP);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Load all autoloaded options into cache at once.\n\t * Called during bootstrap to pre-warm the cache.\n\t */\n\tasync loadAutoloadedOptions(): Promise<Map<string, unknown>> {\n\t\t// Check if already loaded\n\t\tconst cached = await this.cache.get<Record<string, unknown>>(\n\t\t\tAUTOLOAD_CACHE_KEY,\n\t\t\tCACHE_GROUP,\n\t\t);\n\n\t\tif (cached) {\n\t\t\tconst result = new Map(Object.entries(cached));\n\t\t\t// Populate individual option caches\n\t\t\tfor (const [key, value] of result) {\n\t\t\t\tawait this.cache.set(key, value, CACHE_GROUP);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// Query all autoloaded options\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(options)\n\t\t\t.where(eq(options.autoload, true));\n\n\t\tconst result = new Map<string, unknown>();\n\t\tconst autoloadMap: Record<string, unknown> = {};\n\n\t\tfor (const row of rows) {\n\t\t\tconst value = this.deserializeValue(row);\n\t\t\tresult.set(row.optionName, value);\n\t\t\tautoloadMap[row.optionName] = value;\n\t\t\tawait this.cache.set(row.optionName, value, CACHE_GROUP);\n\t\t}\n\n\t\t// Cache the complete autoload map\n\t\tawait this.cache.set(AUTOLOAD_CACHE_KEY, autoloadMap, CACHE_GROUP);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Serialize a value for storage.\n\t * Complex types (objects, arrays) are stored in both text and JSONB columns.\n\t */\n\tprivate serializeValue(value: unknown): { textValue: string; jsonValue: unknown } {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn { textValue: '', jsonValue: null };\n\t\t}\n\n\t\tif (typeof value === 'string') {\n\t\t\t// Try to detect if it's JSON\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\t\treturn { textValue: value, jsonValue: parsed };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Not JSON, store as plain text\n\t\t\t}\n\t\t\treturn { textValue: value, jsonValue: null };\n\t\t}\n\n\t\tif (typeof value === 'number' || typeof value === 'boolean') {\n\t\t\treturn { textValue: String(value), jsonValue: null };\n\t\t}\n\n\t\t// Objects and arrays → both text (JSON string) and JSONB\n\t\tconst textValue = JSON.stringify(value);\n\t\treturn { textValue, jsonValue: value };\n\t}\n\n\t/**\n\t * Deserialize a value from the database row.\n\t * Prefers JSONB column when available (already parsed).\n\t */\n\tprivate deserializeValue<T>(row: {\n\t\toptionValue: string;\n\t\toptionValueJson: unknown;\n\t}): T {\n\t\t// Prefer JSONB if available\n\t\tif (row.optionValueJson !== null && row.optionValueJson !== undefined) {\n\t\t\treturn row.optionValueJson as T;\n\t\t}\n\n\t\t// Try to parse as JSON\n\t\ttry {\n\t\t\treturn JSON.parse(row.optionValue) as T;\n\t\t} catch {\n\t\t\treturn row.optionValue as T;\n\t\t}\n\t}\n}\n","import { eq, and, sql } from 'drizzle-orm';\nimport type { Database } from '../connection';\nimport { posts, postmeta } from '../schema/index';\nimport { MetaRepository, type MetaTableColumns, type MetaColumnNames } from './meta-repository';\n\nexport interface CreatePostInput {\n\tpostAuthor: number;\n\tpostTitle: string;\n\tpostContent?: string;\n\tpostExcerpt?: string;\n\tpostStatus?: string;\n\tpostName?: string;\n\tpostType?: string;\n\tpostParent?: number;\n\tpostPassword?: string;\n\tcommentStatus?: string;\n\tpingStatus?: string;\n\tpostMimeType?: string;\n\tmenuOrder?: number;\n\tguid?: string;\n}\n\nexport interface UpdatePostInput {\n\tpostTitle?: string;\n\tpostContent?: string;\n\tpostExcerpt?: string;\n\tpostStatus?: string;\n\tpostName?: string;\n\tpostType?: string;\n\tpostParent?: number;\n\tpostPassword?: string;\n\tcommentStatus?: string;\n\tpingStatus?: string;\n\tmenuOrder?: number;\n}\n\nexport interface PostRow {\n\tid: number;\n\tpostAuthor: number;\n\tpostDate: Date;\n\tpostDateGmt: Date;\n\tpostContent: string;\n\tpostTitle: string;\n\tpostExcerpt: string;\n\tpostStatus: string;\n\tcommentStatus: string;\n\tpingStatus: string;\n\tpostPassword: string;\n\tpostName: string;\n\ttoPing: string;\n\tpinged: string;\n\tpostModified: Date;\n\tpostModifiedGmt: Date;\n\tpostContentFiltered: string;\n\tpostParent: number;\n\tguid: string;\n\tmenuOrder: number;\n\tpostType: string;\n\tpostMimeType: string;\n\tcommentCount: number;\n}\n\n/**\n * Repository for posts (all content types).\n *\n * Handles CRUD, slug generation, sticky posts, and provides\n * a MetaRepository instance for post metadata operations.\n */\nexport class PostRepository {\n\treadonly meta: MetaRepository;\n\n\tconstructor(private db: Database) {\n\t\tconst metaCols: MetaTableColumns = {\n\t\t\tmetaId: postmeta.metaId,\n\t\t\tobjectId: postmeta.postId,\n\t\t\tmetaKey: postmeta.metaKey,\n\t\t\tmetaValue: postmeta.metaValue,\n\t\t\tmetaValueJson: postmeta.metaValueJson,\n\t\t};\n\t\tconst colNames: MetaColumnNames = {\n\t\t\ttable: 'postmeta',\n\t\t\tsql: { metaId: 'meta_id', objectId: 'post_id', metaKey: 'meta_key', metaValue: 'meta_value', metaValueJson: 'meta_value_json' },\n\t\t\tts: { metaId: 'metaId', objectId: 'postId', metaKey: 'metaKey', metaValue: 'metaValue', metaValueJson: 'metaValueJson' },\n\t\t};\n\t\tthis.meta = new MetaRepository(db, postmeta, metaCols, colNames);\n\t}\n\n\t/**\n\t * Create a new post.\n\t */\n\tasync create(input: CreatePostInput): Promise<PostRow> {\n\t\tconst postName = input.postName || this.generateSlug(input.postTitle);\n\t\tconst uniqueSlug = await this.ensureUniqueSlug(\n\t\t\tpostName,\n\t\t\tinput.postType ?? 'post',\n\t\t);\n\n\t\tconst now = new Date();\n\t\tconst [row] = await this.db\n\t\t\t.insert(posts)\n\t\t\t.values({\n\t\t\t\tpostAuthor: input.postAuthor,\n\t\t\t\tpostTitle: input.postTitle,\n\t\t\t\tpostContent: input.postContent ?? '',\n\t\t\t\tpostExcerpt: input.postExcerpt ?? '',\n\t\t\t\tpostStatus: input.postStatus ?? 'draft',\n\t\t\t\tpostName: uniqueSlug,\n\t\t\t\tpostType: input.postType ?? 'post',\n\t\t\t\tpostParent: input.postParent ?? 0,\n\t\t\t\tpostPassword: input.postPassword ?? '',\n\t\t\t\tcommentStatus: input.commentStatus ?? 'open',\n\t\t\t\tpingStatus: input.pingStatus ?? 'open',\n\t\t\t\tpostMimeType: input.postMimeType ?? '',\n\t\t\t\tmenuOrder: input.menuOrder ?? 0,\n\t\t\t\tguid: input.guid ?? '',\n\t\t\t\tpostDate: now,\n\t\t\t\tpostDateGmt: now,\n\t\t\t\tpostModified: now,\n\t\t\t\tpostModifiedGmt: now,\n\t\t\t})\n\t\t\t.returning();\n\n\t\treturn row as PostRow;\n\t}\n\n\t/**\n\t * Get a post by ID.\n\t */\n\tasync getById(id: number): Promise<PostRow | undefined> {\n\t\tconst rows = await this.db.select().from(posts).where(eq(posts.id, id)).limit(1);\n\t\treturn rows[0] as PostRow | undefined;\n\t}\n\n\t/**\n\t * Get a post by slug and type.\n\t */\n\tasync getBySlug(slug: string, postType: string = 'post'): Promise<PostRow | undefined> {\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(posts)\n\t\t\t.where(and(eq(posts.postName, slug), eq(posts.postType, postType)))\n\t\t\t.limit(1);\n\t\treturn rows[0] as PostRow | undefined;\n\t}\n\n\t/**\n\t * Update a post. Returns the updated row or undefined if not found.\n\t */\n\tasync update(id: number, input: UpdatePostInput): Promise<PostRow | undefined> {\n\t\tconst existing = await this.getById(id);\n\t\tif (!existing) return undefined;\n\n\t\tconst now = new Date();\n\t\tconst updateData: Record<string, unknown> = {\n\t\t\tpostModified: now,\n\t\t\tpostModifiedGmt: now,\n\t\t};\n\n\t\tif (input.postTitle !== undefined) updateData['postTitle'] = input.postTitle;\n\t\tif (input.postContent !== undefined) updateData['postContent'] = input.postContent;\n\t\tif (input.postExcerpt !== undefined) updateData['postExcerpt'] = input.postExcerpt;\n\t\tif (input.postStatus !== undefined) updateData['postStatus'] = input.postStatus;\n\t\tif (input.postType !== undefined) updateData['postType'] = input.postType;\n\t\tif (input.postParent !== undefined) updateData['postParent'] = input.postParent;\n\t\tif (input.postPassword !== undefined) updateData['postPassword'] = input.postPassword;\n\t\tif (input.commentStatus !== undefined) updateData['commentStatus'] = input.commentStatus;\n\t\tif (input.pingStatus !== undefined) updateData['pingStatus'] = input.pingStatus;\n\t\tif (input.menuOrder !== undefined) updateData['menuOrder'] = input.menuOrder;\n\n\t\tif (input.postName !== undefined) {\n\t\t\tupdateData['postName'] = await this.ensureUniqueSlug(\n\t\t\t\tinput.postName,\n\t\t\t\tinput.postType ?? existing.postType,\n\t\t\t\tid,\n\t\t\t);\n\t\t}\n\n\t\tconst [row] = await this.db\n\t\t\t.update(posts)\n\t\t\t.set(updateData)\n\t\t\t.where(eq(posts.id, id))\n\t\t\t.returning();\n\n\t\treturn row as PostRow;\n\t}\n\n\t/**\n\t * Trash a post (move to trash status, preserving original status in meta).\n\t */\n\tasync trash(id: number): Promise<PostRow | undefined> {\n\t\tconst existing = await this.getById(id);\n\t\tif (!existing) return undefined;\n\t\tif (existing.postStatus === 'trash') return existing;\n\n\t\t// Save original status so it can be restored\n\t\tawait this.meta.update(id, '_trash_meta_status', existing.postStatus);\n\n\t\treturn this.update(id, { postStatus: 'trash' });\n\t}\n\n\t/**\n\t * Restore a trashed post to its original status.\n\t */\n\tasync untrash(id: number): Promise<PostRow | undefined> {\n\t\tconst existing = await this.getById(id);\n\t\tif (!existing || existing.postStatus !== 'trash') return existing;\n\n\t\tconst originalStatus = await this.meta.get<string>(id, '_trash_meta_status');\n\t\tawait this.meta.delete(id, '_trash_meta_status');\n\n\t\treturn this.update(id, { postStatus: originalStatus ?? 'draft' });\n\t}\n\n\t/**\n\t * Permanently delete a post and all its metadata.\n\t */\n\tasync deletePermanently(id: number): Promise<boolean> {\n\t\t// Delete meta first (cascade should handle it but be explicit)\n\t\tawait this.meta.deleteAllForObject(id);\n\n\t\tconst result = await this.db\n\t\t\t.delete(posts)\n\t\t\t.where(eq(posts.id, id))\n\t\t\t.returning({ id: posts.id });\n\n\t\treturn result.length > 0;\n\t}\n\n\t/**\n\t * Get sticky post IDs.\n\t */\n\tasync getStickyIds(): Promise<number[]> {\n\t\tconst rows = await this.db\n\t\t\t.select({ postId: postmeta.postId })\n\t\t\t.from(postmeta)\n\t\t\t.where(and(eq(postmeta.metaKey, '_sticky'), eq(postmeta.metaValue, '1')));\n\n\t\treturn rows.map((r) => r.postId);\n\t}\n\n\t/**\n\t * Set a post as sticky or not.\n\t */\n\tasync setSticky(id: number, sticky: boolean): Promise<void> {\n\t\tif (sticky) {\n\t\t\tawait this.meta.update(id, '_sticky', '1');\n\t\t} else {\n\t\t\tawait this.meta.delete(id, '_sticky');\n\t\t}\n\t}\n\n\t/**\n\t * Check if a post is sticky.\n\t */\n\tasync isSticky(id: number): Promise<boolean> {\n\t\tconst value = await this.meta.get(id, '_sticky');\n\t\treturn value === '1' || value === 1;\n\t}\n\n\t/**\n\t * Count posts by type and status.\n\t */\n\tasync countByStatus(\n\t\tpostType: string = 'post',\n\t): Promise<Record<string, number>> {\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\tstatus: posts.postStatus,\n\t\t\t\tcount: sql<number>`count(*)::int`,\n\t\t\t})\n\t\t\t.from(posts)\n\t\t\t.where(eq(posts.postType, postType))\n\t\t\t.groupBy(posts.postStatus);\n\n\t\tconst result: Record<string, number> = {};\n\t\tfor (const row of rows) {\n\t\t\tresult[row.status] = row.count;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Generate a URL-safe slug from a title.\n\t */\n\tprivate generateSlug(title: string): string {\n\t\treturn title\n\t\t\t.toLowerCase()\n\t\t\t.normalize('NFD')\n\t\t\t.replace(/[\\u0300-\\u036f]/g, '') // remove diacritics\n\t\t\t.replace(/[^a-z0-9_\\s-]/g, '')\n\t\t\t.replace(/\\s+/g, '-')\n\t\t\t.replace(/-+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 200);\n\t}\n\n\t/**\n\t * Ensure a slug is unique within a post type.\n\t * Appends -2, -3, etc. if needed.\n\t */\n\tprivate async ensureUniqueSlug(\n\t\tslug: string,\n\t\tpostType: string,\n\t\texcludeId?: number,\n\t): Promise<string> {\n\t\tlet candidate = slug;\n\t\tlet suffix = 2;\n\n\t\twhile (true) {\n\t\t\tconst conditions = [eq(posts.postName, candidate), eq(posts.postType, postType)];\n\t\t\tif (excludeId !== undefined) {\n\t\t\t\tconditions.push(sql`${posts.id} != ${excludeId}`);\n\t\t\t}\n\n\t\t\tconst existing = await this.db\n\t\t\t\t.select({ id: posts.id })\n\t\t\t\t.from(posts)\n\t\t\t\t.where(and(...conditions))\n\t\t\t\t.limit(1);\n\n\t\t\tif (existing.length === 0) return candidate;\n\t\t\tcandidate = `${slug}-${suffix}`;\n\t\t\tsuffix++;\n\t\t}\n\t}\n}\n","import { eq, and, inArray, sql } from 'drizzle-orm';\nimport type { PgTable, PgColumn } from 'drizzle-orm/pg-core';\nimport type { Database } from '../connection';\n\n/**\n * Column references needed for a meta table.\n */\nexport interface MetaTableColumns {\n\tmetaId: PgColumn;\n\tobjectId: PgColumn;\n\tmetaKey: PgColumn;\n\tmetaValue: PgColumn;\n\tmetaValueJson: PgColumn;\n}\n\n/**\n * Column name mapping for raw SQL operations and result parsing.\n *\n * `sql` = names as in the database (for INSERT/UPDATE raw SQL)\n * `ts` = names as in the Drizzle schema definition (for reading select results)\n */\nexport interface MetaColumnNames {\n\ttable: string;\n\t/** SQL column names (for raw queries) */\n\tsql: { metaId: string; objectId: string; metaKey: string; metaValue: string; metaValueJson: string };\n\t/** TypeScript property names (for reading Drizzle select results) */\n\tts: { metaId: string; objectId: string; metaKey: string; metaValue: string; metaValueJson: string };\n}\n\nexport interface MetaEntry {\n\tmetaId: number;\n\tobjectId: number;\n\tmetaKey: string | null;\n\tmetaValue: string | null;\n\tmetaValueJson: unknown;\n}\n\n/**\n * Generic metadata repository — works with any meta table (postmeta, usermeta,\n * commentmeta, termmeta). Follows the entity-attribute-value pattern with\n * JSONB support for structured queries.\n */\nexport class MetaRepository {\n\tprivate colNames: MetaColumnNames;\n\n\tconstructor(\n\t\tprivate db: Database,\n\t\tprivate table: PgTable,\n\t\tprivate columns: MetaTableColumns,\n\t\tcolNames: MetaColumnNames,\n\t) {\n\t\tthis.colNames = colNames;\n\t}\n\n\tasync get<T = unknown>(objectId: number, key: string): Promise<T | undefined> {\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(this.table)\n\t\t\t.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))\n\t\t\t.limit(1);\n\n\t\tif (rows.length === 0) return undefined;\n\t\treturn this.deserialize<T>(this.toMetaEntry(rows[0] as Record<string, unknown>));\n\t}\n\n\tasync getAll<T = unknown>(objectId: number, key: string): Promise<T[]> {\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(this.table)\n\t\t\t.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)));\n\n\t\treturn rows.map((r) => this.deserialize<T>(this.toMetaEntry(r as Record<string, unknown>)));\n\t}\n\n\tasync getAllForObject(objectId: number): Promise<Map<string, unknown[]>> {\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(this.table)\n\t\t\t.where(eq(this.columns.objectId, objectId));\n\n\t\tconst result = new Map<string, unknown[]>();\n\t\tfor (const row of rows) {\n\t\t\tconst entry = this.toMetaEntry(row as Record<string, unknown>);\n\t\t\tconst key = entry.metaKey ?? '';\n\t\t\tconst values = result.get(key) ?? [];\n\t\t\tvalues.push(this.deserialize(entry));\n\t\t\tresult.set(key, values);\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync batchLoad(objectIds: number[]): Promise<Map<number, Map<string, unknown[]>>> {\n\t\tif (objectIds.length === 0) return new Map();\n\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(this.table)\n\t\t\t.where(inArray(this.columns.objectId, objectIds));\n\n\t\tconst result = new Map<number, Map<string, unknown[]>>();\n\t\tfor (const row of rows) {\n\t\t\tconst rawRow = row as Record<string, unknown>;\n\t\t\tconst entry = this.toMetaEntry(rawRow);\n\t\t\tif (!result.has(entry.objectId)) {\n\t\t\t\tresult.set(entry.objectId, new Map());\n\t\t\t}\n\t\t\tconst objectMeta = result.get(entry.objectId)!;\n\t\t\tconst key = entry.metaKey ?? '';\n\t\t\tconst values = objectMeta.get(key) ?? [];\n\t\t\tvalues.push(this.deserialize(entry));\n\t\t\tobjectMeta.set(key, values);\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync add(objectId: number, key: string, value: unknown): Promise<number> {\n\t\tconst { textValue, jsonValue } = this.serialize(value);\n\t\tconst jsonStr = jsonValue !== null ? JSON.stringify(jsonValue) : null;\n\t\tconst s = this.colNames.sql;\n\t\tconst t = this.colNames.table;\n\n\t\tconst rows = await this.db.execute(sql`\n\t\t\tINSERT INTO ${sql.raw(`\"${t}\"`)} (${sql.raw(`\"${s.objectId}\"`)}, ${sql.raw(`\"${s.metaKey}\"`)}, ${sql.raw(`\"${s.metaValue}\"`)}, ${sql.raw(`\"${s.metaValueJson}\"`)})\n\t\t\tVALUES (${objectId}, ${key}, ${textValue}, ${jsonStr}::jsonb)\n\t\t\tRETURNING ${sql.raw(`\"${s.metaId}\"`)}\n\t\t`);\n\n\t\tconst returnedRows = rows as unknown as Array<Record<string, number>>;\n\t\treturn returnedRows[0][s.metaId];\n\t}\n\n\tasync update(objectId: number, key: string, value: unknown): Promise<boolean> {\n\t\tconst { textValue, jsonValue } = this.serialize(value);\n\n\t\tconst existing = await this.db\n\t\t\t.select({ metaId: this.columns.metaId })\n\t\t\t.from(this.table)\n\t\t\t.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))\n\t\t\t.limit(1);\n\n\t\tif (existing.length === 0) {\n\t\t\tawait this.add(objectId, key, value);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst metaId = (existing[0] as { metaId: number }).metaId;\n\t\tconst jsonStr = jsonValue !== null ? JSON.stringify(jsonValue) : null;\n\t\tconst s = this.colNames.sql;\n\t\tconst t = this.colNames.table;\n\n\t\tawait this.db.execute(sql`\n\t\t\tUPDATE ${sql.raw(`\"${t}\"`)}\n\t\t\tSET ${sql.raw(`\"${s.metaValue}\"`)} = ${textValue}, ${sql.raw(`\"${s.metaValueJson}\"`)} = ${jsonStr}::jsonb\n\t\t\tWHERE ${sql.raw(`\"${s.metaId}\"`)} = ${metaId}\n\t\t`);\n\n\t\treturn true;\n\t}\n\n\tasync delete(objectId: number, key: string, value?: unknown): Promise<number> {\n\t\tif (value !== undefined) {\n\t\t\tconst { textValue } = this.serialize(value);\n\t\t\tconst result = await this.db\n\t\t\t\t.delete(this.table)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(this.columns.objectId, objectId),\n\t\t\t\t\t\teq(this.columns.metaKey, key),\n\t\t\t\t\t\teq(this.columns.metaValue, textValue),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.returning({ metaId: this.columns.metaId });\n\t\t\treturn result.length;\n\t\t}\n\n\t\tconst result = await this.db\n\t\t\t.delete(this.table)\n\t\t\t.where(and(eq(this.columns.objectId, objectId), eq(this.columns.metaKey, key)))\n\t\t\t.returning({ metaId: this.columns.metaId });\n\n\t\treturn result.length;\n\t}\n\n\tasync deleteAllForObject(objectId: number): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.delete(this.table)\n\t\t\t.where(eq(this.columns.objectId, objectId))\n\t\t\t.returning({ metaId: this.columns.metaId });\n\t\treturn result.length;\n\t}\n\n\t/**\n\t * Convert a raw DB row to MetaEntry using column name mapping.\n\t */\n\tprivate toMetaEntry(row: Record<string, unknown>): MetaEntry {\n\t\tconst ts = this.colNames.ts;\n\t\treturn {\n\t\t\tmetaId: row[ts.metaId] as number,\n\t\t\tobjectId: row[ts.objectId] as number,\n\t\t\tmetaKey: row[ts.metaKey] as string | null,\n\t\t\tmetaValue: row[ts.metaValue] as string | null,\n\t\t\tmetaValueJson: row[ts.metaValueJson],\n\t\t};\n\t}\n\n\tprivate serialize(value: unknown): { textValue: string; jsonValue: unknown } {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn { textValue: '', jsonValue: null };\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\t\treturn { textValue: value, jsonValue: parsed };\n\t\t\t\t}\n\t\t\t} catch { /* plain string */ }\n\t\t\treturn { textValue: value, jsonValue: null };\n\t\t}\n\t\tif (typeof value === 'number' || typeof value === 'boolean') {\n\t\t\treturn { textValue: String(value), jsonValue: null };\n\t\t}\n\t\tconst textValue = JSON.stringify(value);\n\t\treturn { textValue, jsonValue: value };\n\t}\n\n\tprivate deserialize<T>(entry: MetaEntry): T {\n\t\tif (entry.metaValueJson !== null && entry.metaValueJson !== undefined) {\n\t\t\treturn entry.metaValueJson as T;\n\t\t}\n\t\tif (entry.metaValue === null) return '' as T;\n\t\ttry {\n\t\t\treturn JSON.parse(entry.metaValue) as T;\n\t\t} catch {\n\t\t\treturn entry.metaValue as T;\n\t\t}\n\t}\n}\n","import { eq, and, sql, inArray } from 'drizzle-orm';\nimport type { Database } from '../connection';\nimport {\n\tterms,\n\ttermTaxonomy,\n\ttermRelationships,\n\ttermmeta,\n} from '../schema/index';\nimport { MetaRepository, type MetaTableColumns, type MetaColumnNames } from './meta-repository';\n\nexport interface CreateTermInput {\n\tname: string;\n\tslug?: string;\n\ttaxonomy: string;\n\tdescription?: string;\n\tparent?: number;\n}\n\nexport interface TermRow {\n\ttermId: number;\n\tname: string;\n\tslug: string;\n\ttermGroup: number;\n\ttermTaxonomyId: number;\n\ttaxonomy: string;\n\tdescription: string;\n\tparent: number;\n\tcount: number;\n}\n\n/**\n * Repository for taxonomy terms (categories, tags, custom taxonomies).\n */\nexport class TaxonomyRepository {\n\treadonly meta: MetaRepository;\n\n\tconstructor(private db: Database) {\n\t\tconst metaCols: MetaTableColumns = {\n\t\t\tmetaId: termmeta.metaId,\n\t\t\tobjectId: termmeta.termId,\n\t\t\tmetaKey: termmeta.metaKey,\n\t\t\tmetaValue: termmeta.metaValue,\n\t\t\tmetaValueJson: termmeta.metaValueJson,\n\t\t};\n\t\tconst colNames: MetaColumnNames = {\n\t\t\ttable: 'termmeta',\n\t\t\tsql: { metaId: 'meta_id', objectId: 'term_id', metaKey: 'meta_key', metaValue: 'meta_value', metaValueJson: 'meta_value_json' },\n\t\t\tts: { metaId: 'metaId', objectId: 'termId', metaKey: 'metaKey', metaValue: 'metaValue', metaValueJson: 'metaValueJson' },\n\t\t};\n\t\tthis.meta = new MetaRepository(db, termmeta, metaCols, colNames);\n\t}\n\n\t/**\n\t * Create a new term in a taxonomy.\n\t */\n\tasync createTerm(input: CreateTermInput): Promise<TermRow> {\n\t\tconst slug = input.slug || this.generateSlug(input.name);\n\t\tconst uniqueSlug = await this.ensureUniqueSlug(slug);\n\n\t\tconst [term] = await this.db\n\t\t\t.insert(terms)\n\t\t\t.values({\n\t\t\t\tname: input.name,\n\t\t\t\tslug: uniqueSlug,\n\t\t\t})\n\t\t\t.returning();\n\n\t\tconst [tt] = await this.db\n\t\t\t.insert(termTaxonomy)\n\t\t\t.values({\n\t\t\t\ttermId: term.termId,\n\t\t\t\ttaxonomy: input.taxonomy,\n\t\t\t\tdescription: input.description ?? '',\n\t\t\t\tparent: input.parent ?? 0,\n\t\t\t\tcount: 0,\n\t\t\t})\n\t\t\t.returning();\n\n\t\treturn {\n\t\t\ttermId: term.termId,\n\t\t\tname: term.name,\n\t\t\tslug: term.slug,\n\t\t\ttermGroup: term.termGroup,\n\t\t\ttermTaxonomyId: tt.termTaxonomyId,\n\t\t\ttaxonomy: tt.taxonomy,\n\t\t\tdescription: tt.description,\n\t\t\tparent: tt.parent,\n\t\t\tcount: tt.count,\n\t\t};\n\t}\n\n\t/**\n\t * Get a term by ID and taxonomy.\n\t */\n\tasync getTermById(termId: number, taxonomy: string): Promise<TermRow | undefined> {\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\ttermId: terms.termId,\n\t\t\t\tname: terms.name,\n\t\t\t\tslug: terms.slug,\n\t\t\t\ttermGroup: terms.termGroup,\n\t\t\t\ttermTaxonomyId: termTaxonomy.termTaxonomyId,\n\t\t\t\ttaxonomy: termTaxonomy.taxonomy,\n\t\t\t\tdescription: termTaxonomy.description,\n\t\t\t\tparent: termTaxonomy.parent,\n\t\t\t\tcount: termTaxonomy.count,\n\t\t\t})\n\t\t\t.from(terms)\n\t\t\t.innerJoin(termTaxonomy, eq(terms.termId, termTaxonomy.termId))\n\t\t\t.where(and(eq(terms.termId, termId), eq(termTaxonomy.taxonomy, taxonomy)))\n\t\t\t.limit(1);\n\n\t\treturn rows[0] as TermRow | undefined;\n\t}\n\n\t/**\n\t * Get a term by slug and taxonomy.\n\t */\n\tasync getTermBySlug(slug: string, taxonomy: string): Promise<TermRow | undefined> {\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\ttermId: terms.termId,\n\t\t\t\tname: terms.name,\n\t\t\t\tslug: terms.slug,\n\t\t\t\ttermGroup: terms.termGroup,\n\t\t\t\ttermTaxonomyId: termTaxonomy.termTaxonomyId,\n\t\t\t\ttaxonomy: termTaxonomy.taxonomy,\n\t\t\t\tdescription: termTaxonomy.description,\n\t\t\t\tparent: termTaxonomy.parent,\n\t\t\t\tcount: termTaxonomy.count,\n\t\t\t})\n\t\t\t.from(terms)\n\t\t\t.innerJoin(termTaxonomy, eq(terms.termId, termTaxonomy.termId))\n\t\t\t.where(and(eq(terms.slug, slug), eq(termTaxonomy.taxonomy, taxonomy)))\n\t\t\t.limit(1);\n\n\t\treturn rows[0] as TermRow | undefined;\n\t}\n\n\t/**\n\t * Get all terms in a taxonomy.\n\t */\n\tasync getTerms(taxonomy: string, parentId?: number): Promise<TermRow[]> {\n\t\tconst conditions = [eq(termTaxonomy.taxonomy, taxonomy)];\n\t\tif (parentId !== undefined) {\n\t\t\tconditions.push(eq(termTaxonomy.parent, parentId));\n\t\t}\n\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\ttermId: terms.termId,\n\t\t\t\tname: terms.name,\n\t\t\t\tslug: terms.slug,\n\t\t\t\ttermGroup: terms.termGroup,\n\t\t\t\ttermTaxonomyId: termTaxonomy.termTaxonomyId,\n\t\t\t\ttaxonomy: termTaxonomy.taxonomy,\n\t\t\t\tdescription: termTaxonomy.description,\n\t\t\t\tparent: termTaxonomy.parent,\n\t\t\t\tcount: termTaxonomy.count,\n\t\t\t})\n\t\t\t.from(terms)\n\t\t\t.innerJoin(termTaxonomy, eq(terms.termId, termTaxonomy.termId))\n\t\t\t.where(and(...conditions))\n\t\t\t.orderBy(terms.name);\n\n\t\treturn rows as TermRow[];\n\t}\n\n\t/**\n\t * Assign terms to an object (post).\n\t */\n\tasync setObjectTerms(objectId: number, termTaxonomyIds: number[]): Promise<void> {\n\t\t// Remove existing relationships for these taxonomies\n\t\tconst existingTtIds = termTaxonomyIds.length > 0\n\t\t\t? await this.db\n\t\t\t\t\t.select({ taxonomy: termTaxonomy.taxonomy })\n\t\t\t\t\t.from(termTaxonomy)\n\t\t\t\t\t.where(inArray(termTaxonomy.termTaxonomyId, termTaxonomyIds))\n\t\t\t: [];\n\n\t\tconst taxonomies = [...new Set(existingTtIds.map((r) => r.taxonomy))];\n\n\t\tif (taxonomies.length > 0) {\n\t\t\t// Get all term_taxonomy_ids for these taxonomies\n\t\t\tconst allTtIds = await this.db\n\t\t\t\t.select({ termTaxonomyId: termTaxonomy.termTaxonomyId })\n\t\t\t\t.from(termTaxonomy)\n\t\t\t\t.where(inArray(termTaxonomy.taxonomy, taxonomies));\n\n\t\t\tconst allTtIdValues = allTtIds.map((r) => r.termTaxonomyId);\n\n\t\t\tif (allTtIdValues.length > 0) {\n\t\t\t\tawait this.db\n\t\t\t\t\t.delete(termRelationships)\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(\n\t\t\t\t\t\t\teq(termRelationships.objectId, objectId),\n\t\t\t\t\t\t\tinArray(termRelationships.termTaxonomyId, allTtIdValues),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Insert new relationships\n\t\tif (termTaxonomyIds.length > 0) {\n\t\t\tawait this.db.insert(termRelationships).values(\n\t\t\t\ttermTaxonomyIds.map((ttId, i) => ({\n\t\t\t\t\tobjectId,\n\t\t\t\t\ttermTaxonomyId: ttId,\n\t\t\t\t\ttermOrder: i,\n\t\t\t\t})),\n\t\t\t).onConflictDoNothing();\n\t\t}\n\n\t\t// Update counts\n\t\tawait this.recountTerms(termTaxonomyIds);\n\t}\n\n\t/**\n\t * Get terms assigned to an object.\n\t */\n\tasync getObjectTerms(objectId: number, taxonomy?: string): Promise<TermRow[]> {\n\t\tconst conditions = [eq(termRelationships.objectId, objectId)];\n\t\tif (taxonomy) {\n\t\t\tconditions.push(eq(termTaxonomy.taxonomy, taxonomy));\n\t\t}\n\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\ttermId: terms.termId,\n\t\t\t\tname: terms.name,\n\t\t\t\tslug: terms.slug,\n\t\t\t\ttermGroup: terms.termGroup,\n\t\t\t\ttermTaxonomyId: termTaxonomy.termTaxonomyId,\n\t\t\t\ttaxonomy: termTaxonomy.taxonomy,\n\t\t\t\tdescription: termTaxonomy.description,\n\t\t\t\tparent: termTaxonomy.parent,\n\t\t\t\tcount: termTaxonomy.count,\n\t\t\t})\n\t\t\t.from(termRelationships)\n\t\t\t.innerJoin(\n\t\t\t\ttermTaxonomy,\n\t\t\t\teq(termRelationships.termTaxonomyId, termTaxonomy.termTaxonomyId),\n\t\t\t)\n\t\t\t.innerJoin(terms, eq(termTaxonomy.termId, terms.termId))\n\t\t\t.where(and(...conditions))\n\t\t\t.orderBy(termRelationships.termOrder);\n\n\t\treturn rows as TermRow[];\n\t}\n\n\t/**\n\t * Delete a term and all its relationships.\n\t */\n\tasync deleteTerm(termId: number, taxonomy: string): Promise<boolean> {\n\t\tconst term = await this.getTermById(termId, taxonomy);\n\t\tif (!term) return false;\n\n\t\t// Delete relationships\n\t\tawait this.db\n\t\t\t.delete(termRelationships)\n\t\t\t.where(eq(termRelationships.termTaxonomyId, term.termTaxonomyId));\n\n\t\t// Delete term_taxonomy\n\t\tawait this.db\n\t\t\t.delete(termTaxonomy)\n\t\t\t.where(eq(termTaxonomy.termTaxonomyId, term.termTaxonomyId));\n\n\t\t// Delete term (if not used by another taxonomy)\n\t\tconst otherUsages = await this.db\n\t\t\t.select({ termTaxonomyId: termTaxonomy.termTaxonomyId })\n\t\t\t.from(termTaxonomy)\n\t\t\t.where(eq(termTaxonomy.termId, termId))\n\t\t\t.limit(1);\n\n\t\tif (otherUsages.length === 0) {\n\t\t\tawait this.db.delete(terms).where(eq(terms.termId, termId));\n\t\t}\n\n\t\t// Delete meta\n\t\tawait this.meta.deleteAllForObject(termId);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Recount the number of objects assigned to terms.\n\t */\n\tasync recountTerms(termTaxonomyIds: number[]): Promise<void> {\n\t\tfor (const ttId of termTaxonomyIds) {\n\t\t\tconst [result] = await this.db\n\t\t\t\t.select({ count: sql<number>`count(*)::int` })\n\t\t\t\t.from(termRelationships)\n\t\t\t\t.where(eq(termRelationships.termTaxonomyId, ttId));\n\n\t\t\tawait this.db\n\t\t\t\t.update(termTaxonomy)\n\t\t\t\t.set({ count: result.count })\n\t\t\t\t.where(eq(termTaxonomy.termTaxonomyId, ttId));\n\t\t}\n\t}\n\n\tprivate generateSlug(name: string): string {\n\t\treturn name\n\t\t\t.toLowerCase()\n\t\t\t.normalize('NFD')\n\t\t\t.replace(/[\\u0300-\\u036f]/g, '')\n\t\t\t.replace(/[^a-z0-9_\\s-]/g, '')\n\t\t\t.replace(/\\s+/g, '-')\n\t\t\t.replace(/-+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 200);\n\t}\n\n\tprivate async ensureUniqueSlug(slug: string, excludeId?: number): Promise<string> {\n\t\tlet candidate = slug;\n\t\tlet suffix = 2;\n\n\t\twhile (true) {\n\t\t\tconst conditions = [eq(terms.slug, candidate)];\n\t\t\tif (excludeId !== undefined) {\n\t\t\t\tconditions.push(sql`${terms.termId} != ${excludeId}`);\n\t\t\t}\n\n\t\t\tconst existing = await this.db\n\t\t\t\t.select({ termId: terms.termId })\n\t\t\t\t.from(terms)\n\t\t\t\t.where(and(...conditions))\n\t\t\t\t.limit(1);\n\n\t\t\tif (existing.length === 0) return candidate;\n\t\t\tcandidate = `${slug}-${suffix}`;\n\t\t\tsuffix++;\n\t\t}\n\t}\n}\n","import { eq, and, desc, sql } from 'drizzle-orm';\nimport type { Database } from '../connection';\nimport { posts } from '../schema/index';\nimport type { PostRow } from './post-repository';\n\n/**\n * Fields that are tracked for revision diffing.\n * If none of these change, no revision is created.\n */\nconst REVISION_FIELDS = [\n\t'postTitle',\n\t'postContent',\n\t'postExcerpt',\n] as const;\n\n/**\n * Repository for post revisions.\n *\n * Revisions are stored as posts with type='revision' and\n * postParent pointing to the original post.\n */\nexport class RevisionRepository {\n\tconstructor(private db: Database) {}\n\n\t/**\n\t * Create a revision snapshot of a post.\n\t * Returns undefined if nothing changed since the last revision.\n\t *\n\t * @param post - The current state of the post (before or after update)\n\t * @param authorId - The user who made the change\n\t */\n\tasync createRevision(post: PostRow, authorId: number): Promise<PostRow | undefined> {\n\t\t// Check if anything actually changed\n\t\tconst lastRevision = await this.getLatest(post.id);\n\t\tif (lastRevision && !this.hasChanges(post, lastRevision)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst now = new Date();\n\t\tconst [revision] = await this.db\n\t\t\t.insert(posts)\n\t\t\t.values({\n\t\t\t\tpostAuthor: authorId,\n\t\t\t\tpostDate: now,\n\t\t\t\tpostDateGmt: now,\n\t\t\t\tpostModified: now,\n\t\t\t\tpostModifiedGmt: now,\n\t\t\t\tpostTitle: post.postTitle,\n\t\t\t\tpostContent: post.postContent,\n\t\t\t\tpostExcerpt: post.postExcerpt,\n\t\t\t\tpostStatus: 'inherit',\n\t\t\t\tpostName: `${post.id}-revision-v1`,\n\t\t\t\tpostType: 'revision',\n\t\t\t\tpostParent: post.id,\n\t\t\t})\n\t\t\t.returning();\n\n\t\treturn revision as PostRow;\n\t}\n\n\t/**\n\t * Get all revisions for a post, newest first.\n\t */\n\tasync getRevisions(postId: number, limit?: number): Promise<PostRow[]> {\n\t\tlet query = this.db\n\t\t\t.select()\n\t\t\t.from(posts)\n\t\t\t.where(and(eq(posts.postParent, postId), eq(posts.postType, 'revision')))\n\t\t\t.orderBy(desc(posts.postDate))\n\t\t\t.$dynamic();\n\n\t\tif (limit !== undefined) {\n\t\t\tquery = query.limit(limit);\n\t\t}\n\n\t\treturn (await query) as PostRow[];\n\t}\n\n\t/**\n\t * Get the most recent revision for a post.\n\t */\n\tasync getLatest(postId: number): Promise<PostRow | undefined> {\n\t\tconst rows = await this.getRevisions(postId, 1);\n\t\treturn rows[0];\n\t}\n\n\t/**\n\t * Get a specific revision by ID.\n\t */\n\tasync getById(revisionId: number): Promise<PostRow | undefined> {\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(posts)\n\t\t\t.where(and(eq(posts.id, revisionId), eq(posts.postType, 'revision')))\n\t\t\t.limit(1);\n\t\treturn rows[0] as PostRow | undefined;\n\t}\n\n\t/**\n\t * Restore a post to a specific revision.\n\t * Returns the updated post.\n\t */\n\tasync restore(postId: number, revisionId: number): Promise<PostRow | undefined> {\n\t\tconst revision = await this.getById(revisionId);\n\t\tif (!revision || revision.postParent !== postId) return undefined;\n\n\t\tconst now = new Date();\n\t\tconst [updated] = await this.db\n\t\t\t.update(posts)\n\t\t\t.set({\n\t\t\t\tpostTitle: revision.postTitle,\n\t\t\t\tpostContent: revision.postContent,\n\t\t\t\tpostExcerpt: revision.postExcerpt,\n\t\t\t\tpostModified: now,\n\t\t\t\tpostModifiedGmt: now,\n\t\t\t})\n\t\t\t.where(eq(posts.id, postId))\n\t\t\t.returning();\n\n\t\treturn updated as PostRow;\n\t}\n\n\t/**\n\t * Delete old revisions beyond the keep limit.\n\t */\n\tasync cleanup(postId: number, keepCount: number): Promise<number> {\n\t\tconst revisions = await this.getRevisions(postId);\n\t\tif (revisions.length <= keepCount) return 0;\n\n\t\tconst toDelete = revisions.slice(keepCount);\n\t\tconst idsToDelete = toDelete.map((r) => r.id);\n\n\t\tif (idsToDelete.length === 0) return 0;\n\n\t\tconst result = await this.db\n\t\t\t.delete(posts)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tsql`${posts.id} IN (${sql.join(\n\t\t\t\t\t\tidsToDelete.map((id) => sql`${id}`),\n\t\t\t\t\t\tsql`, `,\n\t\t\t\t\t)})`,\n\t\t\t\t\teq(posts.postType, 'revision'),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.returning({ id: posts.id });\n\n\t\treturn result.length;\n\t}\n\n\t/**\n\t * Count revisions for a post.\n\t */\n\tasync count(postId: number): Promise<number> {\n\t\tconst [result] = await this.db\n\t\t\t.select({ count: sql<number>`count(*)::int` })\n\t\t\t.from(posts)\n\t\t\t.where(and(eq(posts.postParent, postId), eq(posts.postType, 'revision')));\n\n\t\treturn result.count;\n\t}\n\n\t/**\n\t * Check if any tracked field changed between two post states.\n\t */\n\tprivate hasChanges(current: PostRow, previous: PostRow): boolean {\n\t\tfor (const field of REVISION_FIELDS) {\n\t\t\tif (current[field] !== previous[field]) return true;\n\t\t}\n\t\treturn false;\n\t}\n}\n","import { eq, and, sql } from 'drizzle-orm';\nimport type { Database } from '../connection';\nimport { comments, commentmeta, posts } from '../schema/index';\nimport { MetaRepository, type MetaTableColumns, type MetaColumnNames } from './meta-repository';\n\nexport interface CreateCommentInput {\n\tcommentPostId: number;\n\tcommentAuthor: string;\n\tcommentAuthorEmail?: string;\n\tcommentAuthorUrl?: string;\n\tcommentAuthorIp?: string;\n\tcommentContent: string;\n\tcommentType?: string;\n\tcommentParent?: number;\n\tuserId?: number;\n\tcommentApproved?: string;\n}\n\nexport interface CommentRow {\n\tcommentId: number;\n\tcommentPostId: number;\n\tcommentAuthor: string;\n\tcommentAuthorEmail: string;\n\tcommentAuthorUrl: string;\n\tcommentAuthorIp: string;\n\tcommentDate: Date;\n\tcommentDateGmt: Date;\n\tcommentContent: string;\n\tcommentKarma: number;\n\tcommentApproved: string;\n\tcommentAgent: string;\n\tcommentType: string;\n\tcommentParent: number;\n\tuserId: number;\n}\n\nexport interface CommentModerationRules {\n\tblockedWords?: string[];\n\tmoderationWords?: string[];\n\tmaxLinks?: number;\n\tfloodSeconds?: number;\n}\n\nexport class CommentRepository {\n\treadonly meta: MetaRepository;\n\n\tconstructor(private db: Database) {\n\t\tconst metaCols: MetaTableColumns = {\n\t\t\tmetaId: commentmeta.metaId,\n\t\t\tobjectId: commentmeta.commentId,\n\t\t\tmetaKey: commentmeta.metaKey,\n\t\t\tmetaValue: commentmeta.metaValue,\n\t\t\tmetaValueJson: commentmeta.metaValueJson,\n\t\t};\n\t\tconst colNames: MetaColumnNames = {\n\t\t\ttable: 'commentmeta',\n\t\t\tsql: { metaId: 'meta_id', objectId: 'comment_id', metaKey: 'meta_key', metaValue: 'meta_value', metaValueJson: 'meta_value_json' },\n\t\t\tts: { metaId: 'metaId', objectId: 'commentId', metaKey: 'metaKey', metaValue: 'metaValue', metaValueJson: 'metaValueJson' },\n\t\t};\n\t\tthis.meta = new MetaRepository(db, commentmeta, metaCols, colNames);\n\t}\n\n\tasync create(input: CreateCommentInput): Promise<CommentRow> {\n\t\tconst now = new Date();\n\t\tconst [row] = await this.db\n\t\t\t.insert(comments)\n\t\t\t.values({\n\t\t\t\tcommentPostId: input.commentPostId,\n\t\t\t\tcommentAuthor: input.commentAuthor,\n\t\t\t\tcommentAuthorEmail: input.commentAuthorEmail ?? '',\n\t\t\t\tcommentAuthorUrl: input.commentAuthorUrl ?? '',\n\t\t\t\tcommentAuthorIp: input.commentAuthorIp ?? '',\n\t\t\t\tcommentContent: input.commentContent,\n\t\t\t\tcommentType: input.commentType ?? 'comment',\n\t\t\t\tcommentParent: input.commentParent ?? 0,\n\t\t\t\tuserId: input.userId ?? 0,\n\t\t\t\tcommentApproved: input.commentApproved ?? '1',\n\t\t\t\tcommentDate: now,\n\t\t\t\tcommentDateGmt: now,\n\t\t\t})\n\t\t\t.returning();\n\n\t\t// Update comment count on post\n\t\tawait this.updatePostCommentCount(input.commentPostId);\n\n\t\treturn row as CommentRow;\n\t}\n\n\tasync getById(id: number): Promise<CommentRow | undefined> {\n\t\tconst rows = await this.db.select().from(comments).where(eq(comments.commentId, id)).limit(1);\n\t\treturn rows[0] as CommentRow | undefined;\n\t}\n\n\tasync getByPost(postId: number, status?: string): Promise<CommentRow[]> {\n\t\tconst conditions = [eq(comments.commentPostId, postId)];\n\t\tif (status) conditions.push(eq(comments.commentApproved, status));\n\n\t\treturn (await this.db\n\t\t\t.select()\n\t\t\t.from(comments)\n\t\t\t.where(and(...conditions))\n\t\t\t.orderBy(comments.commentDate)) as CommentRow[];\n\t}\n\n\t/**\n\t * Get threaded comments — returns flat list, client builds the tree\n\t * using commentParent references.\n\t */\n\tasync getThreaded(postId: number, _depth: number = 5): Promise<CommentRow[]> {\n\t\treturn this.getByPost(postId, '1');\n\t}\n\n\tasync approve(id: number): Promise<boolean> {\n\t\tconst comment = await this.getById(id);\n\t\tif (!comment) return false;\n\t\tawait this.db.update(comments).set({ commentApproved: '1' }).where(eq(comments.commentId, id));\n\t\tawait this.updatePostCommentCount(comment.commentPostId);\n\t\treturn true;\n\t}\n\n\tasync unapprove(id: number): Promise<boolean> {\n\t\tconst comment = await this.getById(id);\n\t\tif (!comment) return false;\n\t\tawait this.db.update(comments).set({ commentApproved: '0' }).where(eq(comments.commentId, id));\n\t\tawait this.updatePostCommentCount(comment.commentPostId);\n\t\treturn true;\n\t}\n\n\tasync spam(id: number): Promise<boolean> {\n\t\tconst comment = await this.getById(id);\n\t\tif (!comment) return false;\n\t\tawait this.db.update(comments).set({ commentApproved: 'spam' }).where(eq(comments.commentId, id));\n\t\tawait this.updatePostCommentCount(comment.commentPostId);\n\t\treturn true;\n\t}\n\n\tasync trash(id: number): Promise<boolean> {\n\t\tconst comment = await this.getById(id);\n\t\tif (!comment) return false;\n\t\tawait this.meta.update(id, '_trash_status', comment.commentApproved);\n\t\tawait this.db.update(comments).set({ commentApproved: 'trash' }).where(eq(comments.commentId, id));\n\t\tawait this.updatePostCommentCount(comment.commentPostId);\n\t\treturn true;\n\t}\n\n\tasync deletePermanently(id: number): Promise<boolean> {\n\t\tconst comment = await this.getById(id);\n\t\tif (!comment) return false;\n\t\tawait this.meta.deleteAllForObject(id);\n\t\tconst result = await this.db.delete(comments).where(eq(comments.commentId, id)).returning({ commentId: comments.commentId });\n\t\tif (result.length > 0) await this.updatePostCommentCount(comment.commentPostId);\n\t\treturn result.length > 0;\n\t}\n\n\t/**\n\t * Check moderation rules before approving a comment.\n\t */\n\tmoderate(content: string, _authorEmail: string, rules: CommentModerationRules): 'approve' | 'hold' | 'spam' {\n\t\tconst lower = content.toLowerCase();\n\n\t\t// Blocked words → spam\n\t\tif (rules.blockedWords) {\n\t\t\tfor (const word of rules.blockedWords) {\n\t\t\t\tif (lower.includes(word.toLowerCase())) return 'spam';\n\t\t\t}\n\t\t}\n\n\t\t// Moderation words → hold\n\t\tif (rules.moderationWords) {\n\t\t\tfor (const word of rules.moderationWords) {\n\t\t\t\tif (lower.includes(word.toLowerCase())) return 'hold';\n\t\t\t}\n\t\t}\n\n\t\t// Too many links → hold\n\t\tif (rules.maxLinks !== undefined) {\n\t\t\tconst linkCount = (content.match(/https?:\\/\\//g) ?? []).length;\n\t\t\tif (linkCount > rules.maxLinks) return 'hold';\n\t\t}\n\n\t\treturn 'approve';\n\t}\n\n\t/**\n\t * Check for duplicate comment.\n\t */\n\tasync isDuplicate(postId: number, authorEmail: string, content: string): Promise<boolean> {\n\t\tconst rows = await this.db\n\t\t\t.select({ commentId: comments.commentId })\n\t\t\t.from(comments)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(comments.commentPostId, postId),\n\t\t\t\t\teq(comments.commentAuthorEmail, authorEmail),\n\t\t\t\t\teq(comments.commentContent, content),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.limit(1);\n\t\treturn rows.length > 0;\n\t}\n\n\t/**\n\t * Check flood protection — has user commented too recently?\n\t */\n\tasync isFlooding(authorIp: string, floodSeconds: number): Promise<boolean> {\n\t\tconst cutoff = new Date(Date.now() - floodSeconds * 1000);\n\t\tconst rows = await this.db\n\t\t\t.select({ commentId: comments.commentId })\n\t\t\t.from(comments)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(comments.commentAuthorIp, authorIp),\n\t\t\t\t\tsql`${comments.commentDate} > ${cutoff}`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.limit(1);\n\t\treturn rows.length > 0;\n\t}\n\n\tasync countByStatus(postId?: number): Promise<Record<string, number>> {\n\t\tconst conditions = postId !== undefined ? [eq(comments.commentPostId, postId)] : [];\n\t\tconst rows = await this.db\n\t\t\t.select({ status: comments.commentApproved, count: sql<number>`count(*)::int` })\n\t\t\t.from(comments)\n\t\t\t.where(conditions.length > 0 ? and(...conditions) : undefined)\n\t\t\t.groupBy(comments.commentApproved);\n\n\t\tconst result: Record<string, number> = {};\n\t\tfor (const row of rows) result[row.status] = row.count;\n\t\treturn result;\n\t}\n\n\tprivate async updatePostCommentCount(postId: number): Promise<void> {\n\t\tconst [result] = await this.db\n\t\t\t.select({ count: sql<number>`count(*)::int` })\n\t\t\t.from(comments)\n\t\t\t.where(and(eq(comments.commentPostId, postId), eq(comments.commentApproved, '1')));\n\n\t\tawait this.db\n\t\t\t.update(posts)\n\t\t\t.set({ commentCount: result.count })\n\t\t\t.where(eq(posts.id, postId));\n\t}\n}\n"],"mappings":";;;;;;;AAAA,SAAS,eAAe;AACxB,OAAO,cAAc;;;ACDrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA,EACC,WAAAA;AAAA,EACA,UAAAC;AAAA,EACA;AAAA,EACA,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,OACM;;;ACTP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEA,IAAM,QAAQ;AAAA,EACpB;AAAA,EACA;AAAA,IACC,IAAI,OAAO,IAAI,EAAE,WAAW;AAAA,IAC5B,WAAW,QAAQ,cAAc,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,OAAO;AAAA,IAClE,UAAU,QAAQ,aAAa,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACpE,cAAc,QAAQ,iBAAiB,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC3E,WAAW,QAAQ,cAAc,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO;AAAA,IACnE,SAAS,QAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClE,gBAAgB,UAAU,mBAAmB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IAC1F,mBAAmB,QAAQ,uBAAuB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACvF,YAAY,QAAQ,eAAe,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,QAAQ;AAAA,IAC7E,aAAa,QAAQ,gBAAgB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,EAC3E;AAAA,EACA,CAAC,UAAU;AAAA,IACV,MAAM,gBAAgB,EAAE,GAAG,MAAM,SAAS;AAAA,IAC1C,MAAM,mBAAmB,EAAE,GAAG,MAAM,YAAY;AAAA,IAChD,MAAM,gBAAgB,EAAE,GAAG,MAAM,SAAS;AAAA,EAC3C;AACD;AAEO,IAAM,WAAW;AAAA,EACvB;AAAA,EACA;AAAA,IACC,SAAS,OAAO,UAAU,EAAE,WAAW;AAAA,IACvC,QAAQ,OAAO,SAAS,EACtB,QAAQ,EACR,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA,IACpD,SAAS,QAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5C,WAAW,KAAK,YAAY;AAAA,IAC5B,eAAe,MAAM,iBAAiB;AAAA,EACvC;AAAA,EACA,CAAC,UAAU;AAAA,IACV,MAAM,sBAAsB,EAAE,GAAG,MAAM,MAAM;AAAA,IAC7C,MAAM,uBAAuB,EAAE,GAAG,MAAM,OAAO;AAAA,EAChD;AACD;;;ADlCO,IAAM,QAAQC;AAAA,EACpB;AAAA,EACA;AAAA,IACC,IAAIC,QAAO,IAAI,EAAE,WAAW;AAAA,IAC5B,YAAY,OAAO,eAAe,EAAE,MAAM,SAAS,CAAC,EAClD,QAAQ,EACR,QAAQ,CAAC,EACT,WAAW,MAAM,MAAM,EAAE;AAAA,IAC3B,UAAUC,WAAU,aAAa,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IAC9E,aAAaA,WAAU,iBAAiB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IACrF,aAAaC,MAAK,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtD,WAAWA,MAAK,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClD,aAAaA,MAAK,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtD,YAAYC,SAAQ,eAAe,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,SAAS;AAAA,IAC9E,eAAeA,SAAQ,kBAAkB,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,MAAM;AAAA,IACjF,YAAYA,SAAQ,eAAe,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,MAAM;AAAA,IAC3E,cAAcA,SAAQ,iBAAiB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC5E,UAAUA,SAAQ,aAAa,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACpE,QAAQD,MAAK,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC5C,QAAQA,MAAK,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC3C,cAAcD,WAAU,iBAAiB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IACtF,iBAAiBA,WAAU,qBAAqB,EAAE,cAAc,KAAK,CAAC,EACpE,QAAQ,EACR,WAAW;AAAA,IACb,qBAAqBC,MAAK,uBAAuB,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACvE,YAAY,OAAO,eAAe,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACzE,MAAMC,SAAQ,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC3D,WAAW,OAAO,cAAc,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACvE,UAAUA,SAAQ,aAAa,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,MAAM;AAAA,IACvE,cAAcA,SAAQ,kBAAkB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC7E,cAAc,OAAO,iBAAiB,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,EAC9E;AAAA,EACA,CAAC,UAAU;AAAA,IACVC,OAAM,eAAe,EAAE,GAAG,MAAM,QAAQ;AAAA,IACxCA,OAAM,2BAA2B,EAAE,GAAG,MAAM,UAAU,MAAM,YAAY,MAAM,QAAQ;AAAA,IACtFA,OAAM,iBAAiB,EAAE,GAAG,MAAM,UAAU;AAAA,IAC5CA,OAAM,iBAAiB,EAAE,GAAG,MAAM,UAAU;AAAA,EAC7C;AACD;AAEO,IAAM,WAAWL;AAAA,EACvB;AAAA,EACA;AAAA,IACC,QAAQC,QAAO,SAAS,EAAE,WAAW;AAAA,IACrC,QAAQ,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EAC1C,QAAQ,EACR,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA,IACpD,SAASG,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5C,WAAWD,MAAK,YAAY;AAAA,IAC5B,eAAeG,OAAM,iBAAiB;AAAA,EACvC;AAAA,EACA,CAAC,UAAU;AAAA,IACVD,OAAM,sBAAsB,EAAE,GAAG,MAAM,MAAM;AAAA,IAC7CA,OAAM,uBAAuB,EAAE,GAAG,MAAM,OAAO;AAAA,IAC/CA,OAAM,uBAAuB,EAAE,GAAG,MAAM,QAAQ,MAAM,OAAO;AAAA,EAC9D;AACD;;;AEpEA;AAAA,EACC,WAAAE;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,OACM;AAIA,IAAM,WAAWC;AAAA,EACvB;AAAA,EACA;AAAA,IACC,WAAWC,QAAO,YAAY,EAAE,WAAW;AAAA,IAC3C,eAAeC,QAAO,mBAAmB,EAAE,MAAM,SAAS,CAAC,EACzD,QAAQ,EACR,QAAQ,CAAC,EACT,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA,IACpD,eAAeC,MAAK,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC1D,oBAAoBC,SAAQ,wBAAwB,EAAE,QAAQ,IAAI,CAAC,EACjE,QAAQ,EACR,QAAQ,EAAE;AAAA,IACZ,kBAAkBA,SAAQ,sBAAsB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACrF,iBAAiBA,SAAQ,qBAAqB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACnF,aAAaC,WAAU,gBAAgB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IACpF,gBAAgBA,WAAU,oBAAoB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IAC3F,gBAAgBF,MAAK,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC5D,cAAcD,QAAO,iBAAiB,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IAC7E,iBAAiBE,SAAQ,oBAAoB,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG;AAAA,IAClF,cAAcA,SAAQ,iBAAiB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC5E,aAAaA,SAAQ,gBAAgB,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,SAAS;AAAA,IAChF,eAAeF,QAAO,kBAAkB,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IAC/E,QAAQA,QAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EAC1C,QAAQ,EACR,QAAQ,CAAC,EACT,WAAW,MAAM,MAAM,EAAE;AAAA,EAC5B;AAAA,EACA,CAAC,UAAU;AAAA,IACVI,OAAM,qBAAqB,EAAE,GAAG,MAAM,aAAa;AAAA,IACnDA,OAAM,+BAA+B,EAAE,GAAG,MAAM,iBAAiB,MAAM,cAAc;AAAA,IACrFA,OAAM,sBAAsB,EAAE,GAAG,MAAM,cAAc;AAAA,IACrDA,OAAM,oBAAoB,EAAE,GAAG,MAAM,aAAa;AAAA,IAClDA,OAAM,0BAA0B,EAAE,GAAG,MAAM,kBAAkB;AAAA,EAC9D;AACD;AAEO,IAAM,cAAcN;AAAA,EAC1B;AAAA,EACA;AAAA,IACC,QAAQC,QAAO,SAAS,EAAE,WAAW;AAAA,IACrC,WAAWC,QAAO,cAAc,EAAE,MAAM,SAAS,CAAC,EAChD,QAAQ,EACR,WAAW,MAAM,SAAS,WAAW,EAAE,UAAU,UAAU,CAAC;AAAA,IAC9D,SAASE,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5C,WAAWD,MAAK,YAAY;AAAA,IAC5B,eAAeI,OAAM,iBAAiB;AAAA,EACvC;AAAA,EACA,CAAC,UAAU;AAAA,IACVD,OAAM,4BAA4B,EAAE,GAAG,MAAM,SAAS;AAAA,IACtDA,OAAM,0BAA0B,EAAE,GAAG,MAAM,OAAO;AAAA,EACnD;AACD;;;AChEA;AAAA,EACC,WAAAE;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAGA,IAAM,QAAQC;AAAA,EACpB;AAAA,EACA;AAAA,IACC,QAAQC,QAAO,SAAS,EAAE,WAAW;AAAA,IACrC,MAAMC,SAAQ,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC3D,MAAMA,SAAQ,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC3D,WAAWC,QAAO,cAAc,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,EACxE;AAAA,EACA,CAAC,UAAU,CAACC,OAAM,eAAe,EAAE,GAAG,MAAM,IAAI,GAAGA,OAAM,eAAe,EAAE,GAAG,MAAM,IAAI,CAAC;AACzF;AAEO,IAAM,eAAeJ;AAAA,EAC3B;AAAA,EACA;AAAA,IACC,gBAAgBC,QAAO,kBAAkB,EAAE,WAAW;AAAA,IACtD,QAAQE,QAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EAC1C,QAAQ,EACR,WAAW,MAAM,MAAM,QAAQ,EAAE,UAAU,UAAU,CAAC;AAAA,IACxD,UAAUD,SAAQ,YAAY,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClE,aAAaG,MAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACrD,QAAQF,QAAO,UAAU,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IAChE,OAAOA,QAAO,SAAS,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,EAC/D;AAAA,EACA,CAAC,UAAU;AAAA,IACV,YAAY,sBAAsB,EAAE,GAAG,MAAM,QAAQ,MAAM,QAAQ;AAAA,IACnEC,OAAM,cAAc,EAAE,GAAG,MAAM,QAAQ;AAAA,EACxC;AACD;AAEO,IAAM,oBAAoBJ;AAAA,EAChC;AAAA,EACA;AAAA,IACC,UAAUG,QAAO,aAAa,EAAE,MAAM,SAAS,CAAC,EAC9C,QAAQ,EACR,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA,IACpD,gBAAgBA,QAAO,oBAAoB,EAAE,MAAM,SAAS,CAAC,EAC3D,QAAQ,EACR,WAAW,MAAM,aAAa,gBAAgB,EAAE,UAAU,UAAU,CAAC;AAAA,IACvE,WAAWA,QAAO,cAAc,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,EACxE;AAAA,EACA,CAAC,UAAU;AAAA,IACV,WAAW,EAAE,SAAS,CAAC,MAAM,UAAU,MAAM,cAAc,EAAE,CAAC;AAAA,IAC9DC,OAAM,sBAAsB,EAAE,GAAG,MAAM,cAAc;AAAA,EACtD;AACD;AAEO,IAAM,WAAWJ;AAAA,EACvB;AAAA,EACA;AAAA,IACC,QAAQC,QAAO,SAAS,EAAE,WAAW;AAAA,IACrC,QAAQE,QAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EAC1C,QAAQ,EACR,WAAW,MAAM,MAAM,QAAQ,EAAE,UAAU,UAAU,CAAC;AAAA,IACxD,SAASD,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5C,WAAWG,MAAK,YAAY;AAAA,IAC5B,eAAeC,OAAM,iBAAiB;AAAA,EACvC;AAAA,EACA,CAAC,UAAU;AAAA,IACVF,OAAM,sBAAsB,EAAE,GAAG,MAAM,MAAM;AAAA,IAC7CA,OAAM,uBAAuB,EAAE,GAAG,MAAM,OAAO;AAAA,EAChD;AACD;;;AC1EA;AAAA,EACC,WAAAG;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA;AAAA,EACA,SAAAC;AAAA,EACA,eAAAC;AAAA,EACA,SAAAC;AAAA,OACM;AAEA,IAAM,UAAUN;AAAA,EACtB;AAAA,EACA;AAAA,IACC,UAAUC,QAAO,WAAW,EAAE,WAAW;AAAA,IACzC,YAAYE,SAAQ,eAAe,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO;AAAA,IACrE,aAAaD,MAAK,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtD,iBAAiBE,OAAM,mBAAmB;AAAA,IAC1C,UAAU,QAAQ,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACrD;AAAA,EACA,CAAC,UAAU;AAAA,IACVC,aAAY,iBAAiB,EAAE,GAAG,MAAM,UAAU;AAAA,IAClDC,OAAM,cAAc,EAAE,GAAG,MAAM,QAAQ;AAAA,EACxC;AACD;;;ACxBA;AAAA,EACC,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC;AAAA,OACM;AAEA,IAAM,QAAQN;AAAA,EACpB;AAAA,EACA;AAAA,IACC,QAAQC,QAAO,SAAS,EAAE,WAAW;AAAA,IACrC,SAASG,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClE,UAAUA,SAAQ,aAAa,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACpE,WAAWA,SAAQ,cAAc,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtE,YAAYA,SAAQ,eAAe,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACvE,iBAAiBA,SAAQ,oBAAoB,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClF,aAAaA,SAAQ,gBAAgB,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG;AAAA,IAC1E,WAAWC,QAAO,cAAc,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACvE,YAAYA,QAAO,eAAe,EAAE,MAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACzE,aAAaF,WAAU,gBAAgB,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IACpF,SAASC,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClE,WAAWF,MAAK,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAClD,SAASE,SAAQ,YAAY,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,EACnE;AAAA,EACA,CAAC,UAAU,CAACE,OAAM,kBAAkB,EAAE,GAAG,MAAM,WAAW,CAAC;AAC5D;;;AC5BA,SAAS,WAAAC,UAAS,UAAAC,SAAQ,UAAAC,SAAQ,WAAAC,UAAS,aAAAC,YAAW,SAAAC,QAAO,SAAAC,cAAa;AAUnE,IAAM,WAAWC;AAAA,EACvB;AAAA,EACA;AAAA,IACC,IAAIC,QAAO,IAAI,EAAE,WAAW;AAAA,IAC5B,QAAQC,QAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EAC1C,QAAQ,EACR,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA,IACpD,WAAWC,SAAQ,cAAc,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO;AAAA,IACnE,WAAWC,WAAU,cAAc,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IAChF,WAAWA,WAAU,cAAc,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ;AAAA,IACnE,IAAID,SAAQ,MAAM,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtD,WAAWA,SAAQ,cAAc,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IACtE,MAAME,OAAM,MAAM;AAAA,EACnB;AAAA,EACA,CAAC,UAAU;AAAA,IACVC,OAAM,qBAAqB,EAAE,GAAG,MAAM,MAAM;AAAA,IAC5CA,OAAM,wBAAwB,EAAE,GAAG,MAAM,SAAS;AAAA,IAClDA,OAAM,wBAAwB,EAAE,GAAG,MAAM,SAAS;AAAA,EACnD;AACD;;;AC7BA,SAAS,WAAAC,UAAS,UAAAC,SAAQ,WAAAC,UAAS,aAAAC,YAAW,SAAAC,QAAO,UAAAC,SAAQ,SAAAC,cAAa;AAQnE,IAAM,kBAAkBN;AAAA,EAC9B;AAAA,EACA;AAAA,IACC,IAAIC,QAAO,IAAI,EAAE,WAAW;AAAA,IAC5B,MAAMC,SAAQ,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAQ;AAAA,IAC/C,MAAME,OAAM,MAAM,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAC1C,UAAUF,SAAQ,YAAY,EAAE,QAAQ,GAAG,CAAC;AAAA,IAC5C,iBAAiBG,QAAO,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAAA,IAC9D,WAAWF,WAAU,eAAe,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ;AAAA,IACpE,WAAWA,WAAU,cAAc,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW;AAAA,IAChF,QAAQD,SAAQ,UAAU,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,SAAS;AAAA,EACtE;AAAA,EACA,CAAC,UAAU;AAAA,IACVI,OAAM,oBAAoB,EAAE,GAAG,MAAM,IAAI;AAAA,IACzCA,OAAM,wBAAwB,EAAE,GAAG,MAAM,SAAS;AAAA,IAClDA,OAAM,sBAAsB,EAAE,GAAG,MAAM,MAAM;AAAA,EAC9C;AACD;;;ATZA,SAAS,mBAAmC;AAC3C,QAAM,WAAW,QAAQ,IAAI,aAAa;AAC1C,MAAI,CAAC,UAAU;AACd,UAAM,IAAI;AAAA,MACT;AAAA,IAED;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM,QAAQ,IAAI,SAAS,KAAK;AAAA,IAChC,MAAM,SAAS,QAAQ,IAAI,SAAS,KAAK,QAAQ,EAAE;AAAA,IACnD,UAAU,QAAQ,IAAI,SAAS,KAAK;AAAA,IACpC,MAAM,QAAQ,IAAI,SAAS,KAAK;AAAA,IAChC;AAAA,IACA,gBAAgB,SAAS,QAAQ,IAAI,oBAAoB,KAAK,MAAM,EAAE;AAAA,EACvE;AACD;AAEO,SAAS,iBAAiB,QAAyB;AACzD,QAAM,MAAM,UAAU,iBAAiB;AAEvC,QAAM,SAAS,SAAS;AAAA,IACvB,MAAM,IAAI;AAAA,IACV,MAAM,IAAI;AAAA,IACV,UAAU,IAAI;AAAA,IACd,MAAM,IAAI;AAAA,IACV,UAAU,IAAI;AAAA,IACd,KAAK,IAAI,kBAAkB;AAAA,EAC5B,CAAC;AAED,QAAM,KAAK,QAAQ,QAAQ,EAAE,uBAAO,CAAC;AAErC,SAAO,EAAE,IAAI,OAAO;AACrB;;;AU/CA,OAAO,WAAW;AA2BX,IAAM,cAAN,MAAkB;AAAA,EAChB;AAAA,EACA,SAAiB;AAAA,EACjB;AAAA,EACA;AAAA;AAAA,EAGA,eAA4B,oBAAI,IAAI;AAAA;AAAA,EAGpC,WAAgC,oBAAI,IAAI;AAAA;AAAA,EAGxC,aAAmC,oBAAI,IAAI;AAAA,EAEnD,YAAY,QAA2B;AACtC,SAAK,QAAQ,IAAI,MAAM;AAAA,MACtB,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,aAAa;AAAA,MACb,sBAAsB;AAAA,IACvB,CAAC;AACD,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,aAAa,OAAO,cAAc;AAAA,EACxC;AAAA,EAEA,MAAM,UAAyB;AAC9B,UAAM,KAAK,MAAM,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,aAA4B;AACjC,UAAM,KAAK,MAAM,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,QAAsB;AAC/B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,QAAwB;AACvC,eAAW,SAAS,QAAQ;AAC3B,WAAK,aAAa,IAAI,KAAK;AAAA,IAC5B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAAe,YAA0B;AACpD,SAAK,SAAS,IAAI,OAAO,UAAU;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,KAAa,QAAgB,WAAmB;AAChE,QAAI,KAAK,aAAa,IAAI,KAAK,GAAG;AACjC,aAAO,GAAG,KAAK,SAAS,WAAW,KAAK,IAAI,GAAG;AAAA,IAChD;AACA,WAAO,GAAG,KAAK,SAAS,SAAS,KAAK,MAAM,IAAI,KAAK,IAAI,GAAG;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,KAAa,QAAgB,WAAmB;AACrE,QAAI,KAAK,aAAa,IAAI,KAAK,GAAG;AACjC,aAAO,UAAU,KAAK,IAAI,GAAG;AAAA,IAC9B;AACA,WAAO,QAAQ,KAAK,MAAM,IAAI,KAAK,IAAI,GAAG;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAO,OAAuB;AACrC,WAAO,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAiB,KAAa,QAAgB,WAAmC;AAEtF,UAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,QAAI,KAAK,WAAW,IAAI,QAAQ,GAAG;AAClC,aAAO,KAAK,WAAW,IAAI,QAAQ;AAAA,IACpC;AAEA,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,UAAM,MAAM,MAAM,KAAK,MAAM,IAAI,QAAQ;AAEzC,QAAI,QAAQ,KAAM,QAAO;AAEzB,QAAI;AACH,YAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,WAAK,WAAW,IAAI,UAAU,KAAK;AACnC,aAAO;AAAA,IACR,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,IACL,KACA,OACA,QAAgB,WAChB,KACmB;AACnB,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,UAAM,aAAa,KAAK,UAAU,KAAK;AACvC,UAAM,eAAe,OAAO,KAAK,OAAO,KAAK;AAE7C,QAAI,eAAe,GAAG;AACrB,YAAM,KAAK,MAAM,MAAM,UAAU,cAAc,UAAU;AAAA,IAC1D,OAAO;AACN,YAAM,KAAK,MAAM,IAAI,UAAU,UAAU;AAAA,IAC1C;AAEA,UAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,SAAK,WAAW,IAAI,UAAU,KAAK;AAEnC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACL,KACA,OACA,QAAgB,WAChB,KACmB;AACnB,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,UAAM,aAAa,KAAK,UAAU,KAAK;AACvC,UAAM,eAAe,OAAO,KAAK,OAAO,KAAK;AAE7C,QAAI;AACJ,QAAI,eAAe,GAAG;AACrB,eAAS,MAAM,KAAK,MAAM,IAAI,UAAU,YAAY,MAAM,cAAc,IAAI;AAAA,IAC7E,OAAO;AACN,eAAS,MAAM,KAAK,MAAM,IAAI,UAAU,YAAY,IAAI;AAAA,IACzD;AAEA,QAAI,WAAW,MAAM;AACpB,YAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,WAAK,WAAW,IAAI,UAAU,KAAK;AACnC,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,KAAa,QAAgB,WAA6B;AACtE,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,UAAM,QAAQ,MAAM,KAAK,MAAM,IAAI,QAAQ;AAE3C,UAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,SAAK,WAAW,OAAO,QAAQ;AAE/B,WAAO,QAAQ;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,KAAa,QAAgB,WAAW,SAAiB,GAA4B;AAC/F,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,UAAM,YAAY,MAAM,KAAK,MAAM,OAAO,QAAQ;AAClD,QAAI,CAAC,UAAW,QAAO;AAEvB,UAAM,UAAU,MAAM,KAAK,IAAY,KAAK,KAAK;AACjD,QAAI,YAAY,UAAa,OAAO,YAAY,SAAU,QAAO;AAEjE,UAAM,WAAW,UAAU;AAC3B,UAAM,KAAK,IAAI,KAAK,UAAU,KAAK;AACnC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,KAAa,QAAgB,WAAW,SAAiB,GAA4B;AAC/F,WAAO,KAAK,KAAK,KAAK,OAAO,CAAC,MAAM;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YACL,MACA,QAAgB,WACgB;AAChC,UAAM,SAAS,oBAAI,IAAqB;AACxC,QAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,UAAM,YAAY,KAAK,IAAI,CAAC,MAAM,KAAK,SAAS,GAAG,KAAK,CAAC;AACzD,UAAM,SAAS,MAAM,KAAK,MAAM,KAAK,GAAG,SAAS;AAEjD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACrC,YAAM,MAAM,OAAO,CAAC;AACpB,UAAI,QAAQ,MAAM;AACjB,YAAI;AACH,gBAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,iBAAO,IAAI,KAAK,CAAC,GAAG,KAAK;AACzB,gBAAM,WAAW,KAAK,cAAc,KAAK,CAAC,GAAG,KAAK;AAClD,eAAK,WAAW,IAAI,UAAU,KAAK;AAAA,QACpC,QAAQ;AACP,iBAAO,IAAI,KAAK,CAAC,GAAG,GAAG;AAAA,QACxB;AAAA,MACD;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACL,SACA,QAAgB,WAChB,KACmB;AACnB,UAAM,QAAQ,mBAAmB,MAAM,UAAU,IAAI,IAAI,OAAO,QAAQ,OAAO,CAAC;AAChF,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,eAAe,OAAO,KAAK,OAAO,KAAK;AAC7C,UAAM,WAAW,KAAK,MAAM,SAAS;AAErC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO;AACjC,YAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,YAAM,aAAa,KAAK,UAAU,KAAK;AAEvC,UAAI,eAAe,GAAG;AACrB,iBAAS,MAAM,UAAU,cAAc,UAAU;AAAA,MAClD,OAAO;AACN,iBAAS,IAAI,UAAU,UAAU;AAAA,MAClC;AAEA,YAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,WAAK,WAAW,IAAI,UAAU,KAAK;AAAA,IACpC;AAEA,UAAM,SAAS,KAAK;AACpB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,OAAgC;AAChD,UAAM,UAAU,KAAK,aAAa,IAAI,KAAK,IACxC,GAAG,KAAK,SAAS,WAAW,KAAK,OACjC,GAAG,KAAK,SAAS,SAAS,KAAK,MAAM,IAAI,KAAK;AAEjD,QAAI,UAAU;AACd,QAAI,SAAS;AAEb,OAAG;AACF,YAAM,CAAC,YAAY,IAAI,IAAI,MAAM,KAAK,MAAM,KAAK,QAAQ,SAAS,SAAS,SAAS,GAAG;AACvF,eAAS;AAET,UAAI,KAAK,SAAS,GAAG;AACpB,cAAM,WAAW,KAAK,MAAM,SAAS;AACrC,mBAAW,OAAO,MAAM;AACvB,mBAAS,IAAI,GAAG;AAAA,QACjB;AACA,cAAM,SAAS,KAAK;AACpB,mBAAW,KAAK;AAAA,MACjB;AAAA,IACD,SAAS,WAAW;AAGpB,eAAW,YAAY,KAAK,WAAW,KAAK,GAAG;AAC9C,UAAI,SAAS,WAAW,GAAG,KAAK,GAAG,GAAG;AACrC,aAAK,WAAW,OAAO,QAAQ;AAAA,MAChC;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAA4B;AACjC,UAAM,UAAU,GAAG,KAAK,SAAS;AACjC,QAAI,UAAU;AACd,QAAI,SAAS;AAEb,OAAG;AACF,YAAM,CAAC,YAAY,IAAI,IAAI,MAAM,KAAK,MAAM,KAAK,QAAQ,SAAS,SAAS,SAAS,GAAG;AACvF,eAAS;AAET,UAAI,KAAK,SAAS,GAAG;AACpB,cAAM,WAAW,KAAK,MAAM,SAAS;AACrC,mBAAW,OAAO,MAAM;AACvB,mBAAS,IAAI,GAAG;AAAA,QACjB;AACA,cAAM,SAAS,KAAK;AACpB,mBAAW,KAAK;AAAA,MACjB;AAAA,IACD,SAAS,WAAW;AAEpB,SAAK,WAAW,MAAM;AACtB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAwB;AACvB,SAAK,WAAW,MAAM;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,KAAa,QAAgB,WAA6B;AACtE,UAAM,WAAW,KAAK,cAAc,KAAK,KAAK;AAC9C,QAAI,KAAK,WAAW,IAAI,QAAQ,EAAG,QAAO;AAE1C,UAAM,WAAW,KAAK,SAAS,KAAK,KAAK;AACzC,WAAQ,MAAM,KAAK,MAAM,OAAO,QAAQ,MAAO;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAkB;AACjB,WAAO,KAAK;AAAA,EACb;AACD;;;AC3YA,SAAS,UAAU;AAKnB,IAAM,cAAc;AACpB,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AAWjB,IAAM,oBAAN,MAAwB;AAAA,EAC9B,YACS,IACA,OACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,MAAM,UAAsB,MAAc,cAA0C;AAEnF,UAAM,SAAS,MAAM,KAAK,MAAM,IAAO,MAAM,WAAW;AACxD,QAAI,WAAW,OAAW,QAAO;AAGjC,UAAM,aAAa,MAAM,KAAK,MAAM,IAAI,MAAM,eAAe;AAC7D,QAAI,eAAe,OAAW,QAAO;AAGrC,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,OAAO,EACZ,MAAM,GAAG,QAAQ,YAAY,IAAI,CAAC,EAClC,MAAM,CAAC;AAET,QAAI,KAAK,WAAW,GAAG;AAEtB,YAAM,KAAK,MAAM,IAAI,MAAM,MAAM,iBAAiB,IAAI;AACtD,aAAO;AAAA,IACR;AAEA,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,QAAQ,KAAK,iBAAoB,GAAG;AAG1C,UAAM,KAAK,MAAM,IAAI,MAAM,OAAO,WAAW;AAE7C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UACL,MACA,OACA,WAAoB,MACD;AAEnB,UAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,UAAU,QAAQ,SAAS,CAAC,EACrC,KAAK,OAAO,EACZ,MAAM,GAAG,QAAQ,YAAY,IAAI,CAAC,EAClC,MAAM,CAAC;AAET,QAAI,SAAS,SAAS,EAAG,QAAO;AAEhC,UAAM,EAAE,WAAW,UAAU,IAAI,KAAK,eAAe,KAAK;AAE1D,UAAM,KAAK,GAAG,OAAO,OAAO,EAAE,OAAO;AAAA,MACpC,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB;AAAA,IACD,CAAC;AAGD,UAAM,KAAK,MAAM,IAAI,MAAM,OAAO,WAAW;AAC7C,UAAM,KAAK,MAAM,OAAO,MAAM,eAAe;AAG7C,QAAI,UAAU;AACb,YAAM,KAAK,MAAM,OAAO,oBAAoB,WAAW;AAAA,IACxD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACL,MACA,OACA,UACmB;AACnB,UAAM,EAAE,WAAW,UAAU,IAAI,KAAK,eAAe,KAAK;AAG1D,UAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EACP,KAAK,OAAO,EACZ,MAAM,GAAG,QAAQ,YAAY,IAAI,CAAC,EAClC,MAAM,CAAC;AAET,QAAI,SAAS,WAAW,GAAG;AAE1B,aAAO,KAAK,UAAU,MAAM,OAAO,YAAY,IAAI;AAAA,IACpD;AAEA,UAAM,MAAM,SAAS,CAAC;AAGtB,QAAI,IAAI,gBAAgB,aAAa,aAAa,QAAW;AAC5D,aAAO;AAAA,IACR;AAEA,UAAM,aAAsC;AAAA,MAC3C,aAAa;AAAA,MACb,iBAAiB;AAAA,IAClB;AAEA,QAAI,aAAa,QAAW;AAC3B,iBAAW,UAAU,IAAI;AAAA,IAC1B;AAEA,UAAM,KAAK,GACT,OAAO,OAAO,EACd,IAAI,UAAU,EACd,MAAM,GAAG,QAAQ,YAAY,IAAI,CAAC;AAGpC,UAAM,KAAK,MAAM,IAAI,MAAM,OAAO,WAAW;AAC7C,UAAM,KAAK,MAAM,OAAO,MAAM,eAAe;AAG7C,UAAM,KAAK,MAAM,OAAO,oBAAoB,WAAW;AAEvD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAa,MAAgC;AAClD,UAAM,SAAS,MAAM,KAAK,GACxB,OAAO,OAAO,EACd,MAAM,GAAG,QAAQ,YAAY,IAAI,CAAC,EAClC,UAAU,EAAE,UAAU,QAAQ,SAAS,CAAC;AAE1C,QAAI,OAAO,WAAW,EAAG,QAAO;AAGhC,UAAM,KAAK,MAAM,OAAO,MAAM,WAAW;AACzC,UAAM,KAAK,MAAM,OAAO,MAAM,eAAe;AAC7C,UAAM,KAAK,MAAM,OAAO,oBAAoB,WAAW;AAEvD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,wBAAuD;AAE5D,UAAM,SAAS,MAAM,KAAK,MAAM;AAAA,MAC/B;AAAA,MACA;AAAA,IACD;AAEA,QAAI,QAAQ;AACX,YAAMC,UAAS,IAAI,IAAI,OAAO,QAAQ,MAAM,CAAC;AAE7C,iBAAW,CAAC,KAAK,KAAK,KAAKA,SAAQ;AAClC,cAAM,KAAK,MAAM,IAAI,KAAK,OAAO,WAAW;AAAA,MAC7C;AACA,aAAOA;AAAA,IACR;AAGA,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,OAAO,EACZ,MAAM,GAAG,QAAQ,UAAU,IAAI,CAAC;AAElC,UAAM,SAAS,oBAAI,IAAqB;AACxC,UAAM,cAAuC,CAAC;AAE9C,eAAW,OAAO,MAAM;AACvB,YAAM,QAAQ,KAAK,iBAAiB,GAAG;AACvC,aAAO,IAAI,IAAI,YAAY,KAAK;AAChC,kBAAY,IAAI,UAAU,IAAI;AAC9B,YAAM,KAAK,MAAM,IAAI,IAAI,YAAY,OAAO,WAAW;AAAA,IACxD;AAGA,UAAM,KAAK,MAAM,IAAI,oBAAoB,aAAa,WAAW;AAEjE,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,OAA2D;AACjF,QAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,aAAO,EAAE,WAAW,IAAI,WAAW,KAAK;AAAA,IACzC;AAEA,QAAI,OAAO,UAAU,UAAU;AAE9B,UAAI;AACH,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,YAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AAClD,iBAAO,EAAE,WAAW,OAAO,WAAW,OAAO;AAAA,QAC9C;AAAA,MACD,QAAQ;AAAA,MAER;AACA,aAAO,EAAE,WAAW,OAAO,WAAW,KAAK;AAAA,IAC5C;AAEA,QAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC5D,aAAO,EAAE,WAAW,OAAO,KAAK,GAAG,WAAW,KAAK;AAAA,IACpD;AAGA,UAAM,YAAY,KAAK,UAAU,KAAK;AACtC,WAAO,EAAE,WAAW,WAAW,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAoB,KAGtB;AAEL,QAAI,IAAI,oBAAoB,QAAQ,IAAI,oBAAoB,QAAW;AACtE,aAAO,IAAI;AAAA,IACZ;AAGA,QAAI;AACH,aAAO,KAAK,MAAM,IAAI,WAAW;AAAA,IAClC,QAAQ;AACP,aAAO,IAAI;AAAA,IACZ;AAAA,EACD;AACD;;;AChRA,SAAS,MAAAC,KAAI,OAAAC,MAAK,OAAAC,YAAW;;;ACA7B,SAAS,MAAAC,KAAI,KAAK,SAAS,WAAW;AA0C/B,IAAM,iBAAN,MAAqB;AAAA,EAG3B,YACS,IACA,OACA,SACR,UACC;AAJO;AACA;AACA;AAGR,SAAK,WAAW;AAAA,EACjB;AAAA,EANS;AAAA,EACA;AAAA,EACA;AAAA,EALD;AAAA,EAWR,MAAM,IAAiB,UAAkB,KAAqC;AAC7E,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,KAAK,EACf,MAAM,IAAIA,IAAG,KAAK,QAAQ,UAAU,QAAQ,GAAGA,IAAG,KAAK,QAAQ,SAAS,GAAG,CAAC,CAAC,EAC7E,MAAM,CAAC;AAET,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,WAAO,KAAK,YAAe,KAAK,YAAY,KAAK,CAAC,CAA4B,CAAC;AAAA,EAChF;AAAA,EAEA,MAAM,OAAoB,UAAkB,KAA2B;AACtE,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,KAAK,EACf,MAAM,IAAIA,IAAG,KAAK,QAAQ,UAAU,QAAQ,GAAGA,IAAG,KAAK,QAAQ,SAAS,GAAG,CAAC,CAAC;AAE/E,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,YAAe,KAAK,YAAY,CAA4B,CAAC,CAAC;AAAA,EAC3F;AAAA,EAEA,MAAM,gBAAgB,UAAmD;AACxE,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,KAAK,EACf,MAAMA,IAAG,KAAK,QAAQ,UAAU,QAAQ,CAAC;AAE3C,UAAM,SAAS,oBAAI,IAAuB;AAC1C,eAAW,OAAO,MAAM;AACvB,YAAM,QAAQ,KAAK,YAAY,GAA8B;AAC7D,YAAM,MAAM,MAAM,WAAW;AAC7B,YAAM,SAAS,OAAO,IAAI,GAAG,KAAK,CAAC;AACnC,aAAO,KAAK,KAAK,YAAY,KAAK,CAAC;AACnC,aAAO,IAAI,KAAK,MAAM;AAAA,IACvB;AACA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,UAAU,WAAmE;AAClF,QAAI,UAAU,WAAW,EAAG,QAAO,oBAAI,IAAI;AAE3C,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,KAAK,EACf,MAAM,QAAQ,KAAK,QAAQ,UAAU,SAAS,CAAC;AAEjD,UAAM,SAAS,oBAAI,IAAoC;AACvD,eAAW,OAAO,MAAM;AACvB,YAAM,SAAS;AACf,YAAM,QAAQ,KAAK,YAAY,MAAM;AACrC,UAAI,CAAC,OAAO,IAAI,MAAM,QAAQ,GAAG;AAChC,eAAO,IAAI,MAAM,UAAU,oBAAI,IAAI,CAAC;AAAA,MACrC;AACA,YAAM,aAAa,OAAO,IAAI,MAAM,QAAQ;AAC5C,YAAM,MAAM,MAAM,WAAW;AAC7B,YAAM,SAAS,WAAW,IAAI,GAAG,KAAK,CAAC;AACvC,aAAO,KAAK,KAAK,YAAY,KAAK,CAAC;AACnC,iBAAW,IAAI,KAAK,MAAM;AAAA,IAC3B;AACA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,IAAI,UAAkB,KAAa,OAAiC;AACzE,UAAM,EAAE,WAAW,UAAU,IAAI,KAAK,UAAU,KAAK;AACrD,UAAM,UAAU,cAAc,OAAO,KAAK,UAAU,SAAS,IAAI;AACjE,UAAM,IAAI,KAAK,SAAS;AACxB,UAAM,IAAI,KAAK,SAAS;AAExB,UAAM,OAAO,MAAM,KAAK,GAAG,QAAQ;AAAA,iBACpB,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,QAAQ,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,OAAO,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,SAAS,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,aAAa,GAAG,CAAC;AAAA,aACtJ,QAAQ,KAAK,GAAG,KAAK,SAAS,KAAK,OAAO;AAAA,eACxC,IAAI,IAAI,IAAI,EAAE,MAAM,GAAG,CAAC;AAAA,GACpC;AAED,UAAM,eAAe;AACrB,WAAO,aAAa,CAAC,EAAE,EAAE,MAAM;AAAA,EAChC;AAAA,EAEA,MAAM,OAAO,UAAkB,KAAa,OAAkC;AAC7E,UAAM,EAAE,WAAW,UAAU,IAAI,KAAK,UAAU,KAAK;AAErD,UAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC,EACtC,KAAK,KAAK,KAAK,EACf,MAAM,IAAIA,IAAG,KAAK,QAAQ,UAAU,QAAQ,GAAGA,IAAG,KAAK,QAAQ,SAAS,GAAG,CAAC,CAAC,EAC7E,MAAM,CAAC;AAET,QAAI,SAAS,WAAW,GAAG;AAC1B,YAAM,KAAK,IAAI,UAAU,KAAK,KAAK;AACnC,aAAO;AAAA,IACR;AAEA,UAAM,SAAU,SAAS,CAAC,EAAyB;AACnD,UAAM,UAAU,cAAc,OAAO,KAAK,UAAU,SAAS,IAAI;AACjE,UAAM,IAAI,KAAK,SAAS;AACxB,UAAM,IAAI,KAAK,SAAS;AAExB,UAAM,KAAK,GAAG,QAAQ;AAAA,YACZ,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC;AAAA,SACpB,IAAI,IAAI,IAAI,EAAE,SAAS,GAAG,CAAC,MAAM,SAAS,KAAK,IAAI,IAAI,IAAI,EAAE,aAAa,GAAG,CAAC,MAAM,OAAO;AAAA,WACzF,IAAI,IAAI,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM;AAAA,GAC5C;AAED,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,OAAO,UAAkB,KAAa,OAAkC;AAC7E,QAAI,UAAU,QAAW;AACxB,YAAM,EAAE,UAAU,IAAI,KAAK,UAAU,KAAK;AAC1C,YAAMC,UAAS,MAAM,KAAK,GACxB,OAAO,KAAK,KAAK,EACjB;AAAA,QACA;AAAA,UACCD,IAAG,KAAK,QAAQ,UAAU,QAAQ;AAAA,UAClCA,IAAG,KAAK,QAAQ,SAAS,GAAG;AAAA,UAC5BA,IAAG,KAAK,QAAQ,WAAW,SAAS;AAAA,QACrC;AAAA,MACD,EACC,UAAU,EAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC;AAC3C,aAAOC,QAAO;AAAA,IACf;AAEA,UAAM,SAAS,MAAM,KAAK,GACxB,OAAO,KAAK,KAAK,EACjB,MAAM,IAAID,IAAG,KAAK,QAAQ,UAAU,QAAQ,GAAGA,IAAG,KAAK,QAAQ,SAAS,GAAG,CAAC,CAAC,EAC7E,UAAU,EAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC;AAE3C,WAAO,OAAO;AAAA,EACf;AAAA,EAEA,MAAM,mBAAmB,UAAmC;AAC3D,UAAM,SAAS,MAAM,KAAK,GACxB,OAAO,KAAK,KAAK,EACjB,MAAMA,IAAG,KAAK,QAAQ,UAAU,QAAQ,CAAC,EACzC,UAAU,EAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC;AAC3C,WAAO,OAAO;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,KAAyC;AAC5D,UAAM,KAAK,KAAK,SAAS;AACzB,WAAO;AAAA,MACN,QAAQ,IAAI,GAAG,MAAM;AAAA,MACrB,UAAU,IAAI,GAAG,QAAQ;AAAA,MACzB,SAAS,IAAI,GAAG,OAAO;AAAA,MACvB,WAAW,IAAI,GAAG,SAAS;AAAA,MAC3B,eAAe,IAAI,GAAG,aAAa;AAAA,IACpC;AAAA,EACD;AAAA,EAEQ,UAAU,OAA2D;AAC5E,QAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,aAAO,EAAE,WAAW,IAAI,WAAW,KAAK;AAAA,IACzC;AACA,QAAI,OAAO,UAAU,UAAU;AAC9B,UAAI;AACH,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,YAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AAClD,iBAAO,EAAE,WAAW,OAAO,WAAW,OAAO;AAAA,QAC9C;AAAA,MACD,QAAQ;AAAA,MAAqB;AAC7B,aAAO,EAAE,WAAW,OAAO,WAAW,KAAK;AAAA,IAC5C;AACA,QAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC5D,aAAO,EAAE,WAAW,OAAO,KAAK,GAAG,WAAW,KAAK;AAAA,IACpD;AACA,UAAM,YAAY,KAAK,UAAU,KAAK;AACtC,WAAO,EAAE,WAAW,WAAW,MAAM;AAAA,EACtC;AAAA,EAEQ,YAAe,OAAqB;AAC3C,QAAI,MAAM,kBAAkB,QAAQ,MAAM,kBAAkB,QAAW;AACtE,aAAO,MAAM;AAAA,IACd;AACA,QAAI,MAAM,cAAc,KAAM,QAAO;AACrC,QAAI;AACH,aAAO,KAAK,MAAM,MAAM,SAAS;AAAA,IAClC,QAAQ;AACP,aAAO,MAAM;AAAA,IACd;AAAA,EACD;AACD;;;ADxKO,IAAM,iBAAN,MAAqB;AAAA,EAG3B,YAAoB,IAAc;AAAd;AACnB,UAAM,WAA6B;AAAA,MAClC,QAAQ,SAAS;AAAA,MACjB,UAAU,SAAS;AAAA,MACnB,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,MACpB,eAAe,SAAS;AAAA,IACzB;AACA,UAAM,WAA4B;AAAA,MACjC,OAAO;AAAA,MACP,KAAK,EAAE,QAAQ,WAAW,UAAU,WAAW,SAAS,YAAY,WAAW,cAAc,eAAe,kBAAkB;AAAA,MAC9H,IAAI,EAAE,QAAQ,UAAU,UAAU,UAAU,SAAS,WAAW,WAAW,aAAa,eAAe,gBAAgB;AAAA,IACxH;AACA,SAAK,OAAO,IAAI,eAAe,IAAI,UAAU,UAAU,QAAQ;AAAA,EAChE;AAAA,EAdoB;AAAA,EAFX;AAAA;AAAA;AAAA;AAAA,EAqBT,MAAM,OAAO,OAA0C;AACtD,UAAM,WAAW,MAAM,YAAY,KAAK,aAAa,MAAM,SAAS;AACpE,UAAM,aAAa,MAAM,KAAK;AAAA,MAC7B;AAAA,MACA,MAAM,YAAY;AAAA,IACnB;AAEA,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,CAAC,GAAG,IAAI,MAAM,KAAK,GACvB,OAAO,KAAK,EACZ,OAAO;AAAA,MACP,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,aAAa,MAAM,eAAe;AAAA,MAClC,aAAa,MAAM,eAAe;AAAA,MAClC,YAAY,MAAM,cAAc;AAAA,MAChC,UAAU;AAAA,MACV,UAAU,MAAM,YAAY;AAAA,MAC5B,YAAY,MAAM,cAAc;AAAA,MAChC,cAAc,MAAM,gBAAgB;AAAA,MACpC,eAAe,MAAM,iBAAiB;AAAA,MACtC,YAAY,MAAM,cAAc;AAAA,MAChC,cAAc,MAAM,gBAAgB;AAAA,MACpC,WAAW,MAAM,aAAa;AAAA,MAC9B,MAAM,MAAM,QAAQ;AAAA,MACpB,UAAU;AAAA,MACV,aAAa;AAAA,MACb,cAAc;AAAA,MACd,iBAAiB;AAAA,IAClB,CAAC,EACA,UAAU;AAEZ,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,IAA0C;AACvD,UAAM,OAAO,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,KAAK,EAAE,MAAME,IAAG,MAAM,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC;AAC/E,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAc,WAAmB,QAAsC;AACtF,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,EACV,MAAMC,KAAID,IAAG,MAAM,UAAU,IAAI,GAAGA,IAAG,MAAM,UAAU,QAAQ,CAAC,CAAC,EACjE,MAAM,CAAC;AACT,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,IAAY,OAAsD;AAC9E,UAAM,WAAW,MAAM,KAAK,QAAQ,EAAE;AACtC,QAAI,CAAC,SAAU,QAAO;AAEtB,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,aAAsC;AAAA,MAC3C,cAAc;AAAA,MACd,iBAAiB;AAAA,IAClB;AAEA,QAAI,MAAM,cAAc,OAAW,YAAW,WAAW,IAAI,MAAM;AACnE,QAAI,MAAM,gBAAgB,OAAW,YAAW,aAAa,IAAI,MAAM;AACvE,QAAI,MAAM,gBAAgB,OAAW,YAAW,aAAa,IAAI,MAAM;AACvE,QAAI,MAAM,eAAe,OAAW,YAAW,YAAY,IAAI,MAAM;AACrE,QAAI,MAAM,aAAa,OAAW,YAAW,UAAU,IAAI,MAAM;AACjE,QAAI,MAAM,eAAe,OAAW,YAAW,YAAY,IAAI,MAAM;AACrE,QAAI,MAAM,iBAAiB,OAAW,YAAW,cAAc,IAAI,MAAM;AACzE,QAAI,MAAM,kBAAkB,OAAW,YAAW,eAAe,IAAI,MAAM;AAC3E,QAAI,MAAM,eAAe,OAAW,YAAW,YAAY,IAAI,MAAM;AACrE,QAAI,MAAM,cAAc,OAAW,YAAW,WAAW,IAAI,MAAM;AAEnE,QAAI,MAAM,aAAa,QAAW;AACjC,iBAAW,UAAU,IAAI,MAAM,KAAK;AAAA,QACnC,MAAM;AAAA,QACN,MAAM,YAAY,SAAS;AAAA,QAC3B;AAAA,MACD;AAAA,IACD;AAEA,UAAM,CAAC,GAAG,IAAI,MAAM,KAAK,GACvB,OAAO,KAAK,EACZ,IAAI,UAAU,EACd,MAAMA,IAAG,MAAM,IAAI,EAAE,CAAC,EACtB,UAAU;AAEZ,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,IAA0C;AACrD,UAAM,WAAW,MAAM,KAAK,QAAQ,EAAE;AACtC,QAAI,CAAC,SAAU,QAAO;AACtB,QAAI,SAAS,eAAe,QAAS,QAAO;AAG5C,UAAM,KAAK,KAAK,OAAO,IAAI,sBAAsB,SAAS,UAAU;AAEpE,WAAO,KAAK,OAAO,IAAI,EAAE,YAAY,QAAQ,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,IAA0C;AACvD,UAAM,WAAW,MAAM,KAAK,QAAQ,EAAE;AACtC,QAAI,CAAC,YAAY,SAAS,eAAe,QAAS,QAAO;AAEzD,UAAM,iBAAiB,MAAM,KAAK,KAAK,IAAY,IAAI,oBAAoB;AAC3E,UAAM,KAAK,KAAK,OAAO,IAAI,oBAAoB;AAE/C,WAAO,KAAK,OAAO,IAAI,EAAE,YAAY,kBAAkB,QAAQ,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,IAA8B;AAErD,UAAM,KAAK,KAAK,mBAAmB,EAAE;AAErC,UAAM,SAAS,MAAM,KAAK,GACxB,OAAO,KAAK,EACZ,MAAMA,IAAG,MAAM,IAAI,EAAE,CAAC,EACtB,UAAU,EAAE,IAAI,MAAM,GAAG,CAAC;AAE5B,WAAO,OAAO,SAAS;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAkC;AACvC,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,QAAQ,SAAS,OAAO,CAAC,EAClC,KAAK,QAAQ,EACb,MAAMC,KAAID,IAAG,SAAS,SAAS,SAAS,GAAGA,IAAG,SAAS,WAAW,GAAG,CAAC,CAAC;AAEzE,WAAO,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,IAAY,QAAgC;AAC3D,QAAI,QAAQ;AACX,YAAM,KAAK,KAAK,OAAO,IAAI,WAAW,GAAG;AAAA,IAC1C,OAAO;AACN,YAAM,KAAK,KAAK,OAAO,IAAI,SAAS;AAAA,IACrC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,IAA8B;AAC5C,UAAM,QAAQ,MAAM,KAAK,KAAK,IAAI,IAAI,SAAS;AAC/C,WAAO,UAAU,OAAO,UAAU;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACL,WAAmB,QACe;AAClC,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,OAAOE;AAAA,IACR,CAAC,EACA,KAAK,KAAK,EACV,MAAMF,IAAG,MAAM,UAAU,QAAQ,CAAC,EAClC,QAAQ,MAAM,UAAU;AAE1B,UAAM,SAAiC,CAAC;AACxC,eAAW,OAAO,MAAM;AACvB,aAAO,IAAI,MAAM,IAAI,IAAI;AAAA,IAC1B;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAuB;AAC3C,WAAO,MACL,YAAY,EACZ,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,kBAAkB,EAAE,EAC5B,QAAQ,QAAQ,GAAG,EACnB,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,UAAU,GAAG,GAAG;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBACb,MACA,UACA,WACkB;AAClB,QAAI,YAAY;AAChB,QAAI,SAAS;AAEb,WAAO,MAAM;AACZ,YAAM,aAAa,CAACA,IAAG,MAAM,UAAU,SAAS,GAAGA,IAAG,MAAM,UAAU,QAAQ,CAAC;AAC/E,UAAI,cAAc,QAAW;AAC5B,mBAAW,KAAKE,OAAM,MAAM,EAAE,OAAO,SAAS,EAAE;AAAA,MACjD;AAEA,YAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,IAAI,MAAM,GAAG,CAAC,EACvB,KAAK,KAAK,EACV,MAAMD,KAAI,GAAG,UAAU,CAAC,EACxB,MAAM,CAAC;AAET,UAAI,SAAS,WAAW,EAAG,QAAO;AAClC,kBAAY,GAAG,IAAI,IAAI,MAAM;AAC7B;AAAA,IACD;AAAA,EACD;AACD;;;AErUA,SAAS,MAAAE,KAAI,OAAAC,MAAK,OAAAC,MAAK,WAAAC,gBAAe;AAiC/B,IAAM,qBAAN,MAAyB;AAAA,EAG/B,YAAoB,IAAc;AAAd;AACnB,UAAM,WAA6B;AAAA,MAClC,QAAQ,SAAS;AAAA,MACjB,UAAU,SAAS;AAAA,MACnB,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,MACpB,eAAe,SAAS;AAAA,IACzB;AACA,UAAM,WAA4B;AAAA,MACjC,OAAO;AAAA,MACP,KAAK,EAAE,QAAQ,WAAW,UAAU,WAAW,SAAS,YAAY,WAAW,cAAc,eAAe,kBAAkB;AAAA,MAC9H,IAAI,EAAE,QAAQ,UAAU,UAAU,UAAU,SAAS,WAAW,WAAW,aAAa,eAAe,gBAAgB;AAAA,IACxH;AACA,SAAK,OAAO,IAAI,eAAe,IAAI,UAAU,UAAU,QAAQ;AAAA,EAChE;AAAA,EAdoB;AAAA,EAFX;AAAA;AAAA;AAAA;AAAA,EAqBT,MAAM,WAAW,OAA0C;AAC1D,UAAM,OAAO,MAAM,QAAQ,KAAK,aAAa,MAAM,IAAI;AACvD,UAAM,aAAa,MAAM,KAAK,iBAAiB,IAAI;AAEnD,UAAM,CAAC,IAAI,IAAI,MAAM,KAAK,GACxB,OAAO,KAAK,EACZ,OAAO;AAAA,MACP,MAAM,MAAM;AAAA,MACZ,MAAM;AAAA,IACP,CAAC,EACA,UAAU;AAEZ,UAAM,CAAC,EAAE,IAAI,MAAM,KAAK,GACtB,OAAO,YAAY,EACnB,OAAO;AAAA,MACP,QAAQ,KAAK;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM,eAAe;AAAA,MAClC,QAAQ,MAAM,UAAU;AAAA,MACxB,OAAO;AAAA,IACR,CAAC,EACA,UAAU;AAEZ,WAAO;AAAA,MACN,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,gBAAgB,GAAG;AAAA,MACnB,UAAU,GAAG;AAAA,MACb,aAAa,GAAG;AAAA,MAChB,QAAQ,GAAG;AAAA,MACX,OAAO,GAAG;AAAA,IACX;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAAgB,UAAgD;AACjF,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,gBAAgB,aAAa;AAAA,MAC7B,UAAU,aAAa;AAAA,MACvB,aAAa,aAAa;AAAA,MAC1B,QAAQ,aAAa;AAAA,MACrB,OAAO,aAAa;AAAA,IACrB,CAAC,EACA,KAAK,KAAK,EACV,UAAU,cAAcC,IAAG,MAAM,QAAQ,aAAa,MAAM,CAAC,EAC7D,MAAMC,KAAID,IAAG,MAAM,QAAQ,MAAM,GAAGA,IAAG,aAAa,UAAU,QAAQ,CAAC,CAAC,EACxE,MAAM,CAAC;AAET,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,MAAc,UAAgD;AACjF,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,gBAAgB,aAAa;AAAA,MAC7B,UAAU,aAAa;AAAA,MACvB,aAAa,aAAa;AAAA,MAC1B,QAAQ,aAAa;AAAA,MACrB,OAAO,aAAa;AAAA,IACrB,CAAC,EACA,KAAK,KAAK,EACV,UAAU,cAAcA,IAAG,MAAM,QAAQ,aAAa,MAAM,CAAC,EAC7D,MAAMC,KAAID,IAAG,MAAM,MAAM,IAAI,GAAGA,IAAG,aAAa,UAAU,QAAQ,CAAC,CAAC,EACpE,MAAM,CAAC;AAET,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,UAAkB,UAAuC;AACvE,UAAM,aAAa,CAACA,IAAG,aAAa,UAAU,QAAQ,CAAC;AACvD,QAAI,aAAa,QAAW;AAC3B,iBAAW,KAAKA,IAAG,aAAa,QAAQ,QAAQ,CAAC;AAAA,IAClD;AAEA,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,gBAAgB,aAAa;AAAA,MAC7B,UAAU,aAAa;AAAA,MACvB,aAAa,aAAa;AAAA,MAC1B,QAAQ,aAAa;AAAA,MACrB,OAAO,aAAa;AAAA,IACrB,CAAC,EACA,KAAK,KAAK,EACV,UAAU,cAAcA,IAAG,MAAM,QAAQ,aAAa,MAAM,CAAC,EAC7D,MAAMC,KAAI,GAAG,UAAU,CAAC,EACxB,QAAQ,MAAM,IAAI;AAEpB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,UAAkB,iBAA0C;AAEhF,UAAM,gBAAgB,gBAAgB,SAAS,IAC5C,MAAM,KAAK,GACV,OAAO,EAAE,UAAU,aAAa,SAAS,CAAC,EAC1C,KAAK,YAAY,EACjB,MAAMC,SAAQ,aAAa,gBAAgB,eAAe,CAAC,IAC5D,CAAC;AAEJ,UAAM,aAAa,CAAC,GAAG,IAAI,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEpE,QAAI,WAAW,SAAS,GAAG;AAE1B,YAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,gBAAgB,aAAa,eAAe,CAAC,EACtD,KAAK,YAAY,EACjB,MAAMA,SAAQ,aAAa,UAAU,UAAU,CAAC;AAElD,YAAM,gBAAgB,SAAS,IAAI,CAAC,MAAM,EAAE,cAAc;AAE1D,UAAI,cAAc,SAAS,GAAG;AAC7B,cAAM,KAAK,GACT,OAAO,iBAAiB,EACxB;AAAA,UACAD;AAAA,YACCD,IAAG,kBAAkB,UAAU,QAAQ;AAAA,YACvCE,SAAQ,kBAAkB,gBAAgB,aAAa;AAAA,UACxD;AAAA,QACD;AAAA,MACF;AAAA,IACD;AAGA,QAAI,gBAAgB,SAAS,GAAG;AAC/B,YAAM,KAAK,GAAG,OAAO,iBAAiB,EAAE;AAAA,QACvC,gBAAgB,IAAI,CAAC,MAAM,OAAO;AAAA,UACjC;AAAA,UACA,gBAAgB;AAAA,UAChB,WAAW;AAAA,QACZ,EAAE;AAAA,MACH,EAAE,oBAAoB;AAAA,IACvB;AAGA,UAAM,KAAK,aAAa,eAAe;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,UAAkB,UAAuC;AAC7E,UAAM,aAAa,CAACF,IAAG,kBAAkB,UAAU,QAAQ,CAAC;AAC5D,QAAI,UAAU;AACb,iBAAW,KAAKA,IAAG,aAAa,UAAU,QAAQ,CAAC;AAAA,IACpD;AAEA,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,gBAAgB,aAAa;AAAA,MAC7B,UAAU,aAAa;AAAA,MACvB,aAAa,aAAa;AAAA,MAC1B,QAAQ,aAAa;AAAA,MACrB,OAAO,aAAa;AAAA,IACrB,CAAC,EACA,KAAK,iBAAiB,EACtB;AAAA,MACA;AAAA,MACAA,IAAG,kBAAkB,gBAAgB,aAAa,cAAc;AAAA,IACjE,EACC,UAAU,OAAOA,IAAG,aAAa,QAAQ,MAAM,MAAM,CAAC,EACtD,MAAMC,KAAI,GAAG,UAAU,CAAC,EACxB,QAAQ,kBAAkB,SAAS;AAErC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,QAAgB,UAAoC;AACpE,UAAM,OAAO,MAAM,KAAK,YAAY,QAAQ,QAAQ;AACpD,QAAI,CAAC,KAAM,QAAO;AAGlB,UAAM,KAAK,GACT,OAAO,iBAAiB,EACxB,MAAMD,IAAG,kBAAkB,gBAAgB,KAAK,cAAc,CAAC;AAGjE,UAAM,KAAK,GACT,OAAO,YAAY,EACnB,MAAMA,IAAG,aAAa,gBAAgB,KAAK,cAAc,CAAC;AAG5D,UAAM,cAAc,MAAM,KAAK,GAC7B,OAAO,EAAE,gBAAgB,aAAa,eAAe,CAAC,EACtD,KAAK,YAAY,EACjB,MAAMA,IAAG,aAAa,QAAQ,MAAM,CAAC,EACrC,MAAM,CAAC;AAET,QAAI,YAAY,WAAW,GAAG;AAC7B,YAAM,KAAK,GAAG,OAAO,KAAK,EAAE,MAAMA,IAAG,MAAM,QAAQ,MAAM,CAAC;AAAA,IAC3D;AAGA,UAAM,KAAK,KAAK,mBAAmB,MAAM;AAEzC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,iBAA0C;AAC5D,eAAW,QAAQ,iBAAiB;AACnC,YAAM,CAAC,MAAM,IAAI,MAAM,KAAK,GAC1B,OAAO,EAAE,OAAOG,oBAA2B,CAAC,EAC5C,KAAK,iBAAiB,EACtB,MAAMH,IAAG,kBAAkB,gBAAgB,IAAI,CAAC;AAElD,YAAM,KAAK,GACT,OAAO,YAAY,EACnB,IAAI,EAAE,OAAO,OAAO,MAAM,CAAC,EAC3B,MAAMA,IAAG,aAAa,gBAAgB,IAAI,CAAC;AAAA,IAC9C;AAAA,EACD;AAAA,EAEQ,aAAa,MAAsB;AAC1C,WAAO,KACL,YAAY,EACZ,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,kBAAkB,EAAE,EAC5B,QAAQ,QAAQ,GAAG,EACnB,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,UAAU,GAAG,GAAG;AAAA,EACnB;AAAA,EAEA,MAAc,iBAAiB,MAAc,WAAqC;AACjF,QAAI,YAAY;AAChB,QAAI,SAAS;AAEb,WAAO,MAAM;AACZ,YAAM,aAAa,CAACA,IAAG,MAAM,MAAM,SAAS,CAAC;AAC7C,UAAI,cAAc,QAAW;AAC5B,mBAAW,KAAKG,OAAM,MAAM,MAAM,OAAO,SAAS,EAAE;AAAA,MACrD;AAEA,YAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,QAAQ,MAAM,OAAO,CAAC,EAC/B,KAAK,KAAK,EACV,MAAMF,KAAI,GAAG,UAAU,CAAC,EACxB,MAAM,CAAC;AAET,UAAI,SAAS,WAAW,EAAG,QAAO;AAClC,kBAAY,GAAG,IAAI,IAAI,MAAM;AAC7B;AAAA,IACD;AAAA,EACD;AACD;;;AC/UA,SAAS,MAAAG,KAAI,OAAAC,MAAK,MAAM,OAAAC,YAAW;AASnC,IAAM,kBAAkB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACD;AAQO,IAAM,qBAAN,MAAyB;AAAA,EAC/B,YAAoB,IAAc;AAAd;AAAA,EAAe;AAAA,EAAf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,MAAM,eAAe,MAAe,UAAgD;AAEnF,UAAM,eAAe,MAAM,KAAK,UAAU,KAAK,EAAE;AACjD,QAAI,gBAAgB,CAAC,KAAK,WAAW,MAAM,YAAY,GAAG;AACzD,aAAO;AAAA,IACR;AAEA,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,CAAC,QAAQ,IAAI,MAAM,KAAK,GAC5B,OAAO,KAAK,EACZ,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,aAAa;AAAA,MACb,cAAc;AAAA,MACd,iBAAiB;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK;AAAA,MAClB,YAAY;AAAA,MACZ,UAAU,GAAG,KAAK,EAAE;AAAA,MACpB,UAAU;AAAA,MACV,YAAY,KAAK;AAAA,IAClB,CAAC,EACA,UAAU;AAEZ,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,QAAgB,OAAoC;AACtE,QAAI,QAAQ,KAAK,GACf,OAAO,EACP,KAAK,KAAK,EACV,MAAMC,KAAIC,IAAG,MAAM,YAAY,MAAM,GAAGA,IAAG,MAAM,UAAU,UAAU,CAAC,CAAC,EACvE,QAAQ,KAAK,MAAM,QAAQ,CAAC,EAC5B,SAAS;AAEX,QAAI,UAAU,QAAW;AACxB,cAAQ,MAAM,MAAM,KAAK;AAAA,IAC1B;AAEA,WAAQ,MAAM;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,QAA8C;AAC7D,UAAM,OAAO,MAAM,KAAK,aAAa,QAAQ,CAAC;AAC9C,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,YAAkD;AAC/D,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,KAAK,EACV,MAAMD,KAAIC,IAAG,MAAM,IAAI,UAAU,GAAGA,IAAG,MAAM,UAAU,UAAU,CAAC,CAAC,EACnE,MAAM,CAAC;AACT,WAAO,KAAK,CAAC;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,QAAgB,YAAkD;AAC/E,UAAM,WAAW,MAAM,KAAK,QAAQ,UAAU;AAC9C,QAAI,CAAC,YAAY,SAAS,eAAe,OAAQ,QAAO;AAExD,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,CAAC,OAAO,IAAI,MAAM,KAAK,GAC3B,OAAO,KAAK,EACZ,IAAI;AAAA,MACJ,WAAW,SAAS;AAAA,MACpB,aAAa,SAAS;AAAA,MACtB,aAAa,SAAS;AAAA,MACtB,cAAc;AAAA,MACd,iBAAiB;AAAA,IAClB,CAAC,EACA,MAAMA,IAAG,MAAM,IAAI,MAAM,CAAC,EAC1B,UAAU;AAEZ,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,QAAgB,WAAoC;AACjE,UAAM,YAAY,MAAM,KAAK,aAAa,MAAM;AAChD,QAAI,UAAU,UAAU,UAAW,QAAO;AAE1C,UAAM,WAAW,UAAU,MAAM,SAAS;AAC1C,UAAM,cAAc,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE;AAE5C,QAAI,YAAY,WAAW,EAAG,QAAO;AAErC,UAAM,SAAS,MAAM,KAAK,GACxB,OAAO,KAAK,EACZ;AAAA,MACAD;AAAA,QACCE,OAAM,MAAM,EAAE,QAAQA,KAAI;AAAA,UACzB,YAAY,IAAI,CAAC,OAAOA,OAAM,EAAE,EAAE;AAAA,UAClCA;AAAA,QACD,CAAC;AAAA,QACDD,IAAG,MAAM,UAAU,UAAU;AAAA,MAC9B;AAAA,IACD,EACC,UAAU,EAAE,IAAI,MAAM,GAAG,CAAC;AAE5B,WAAO,OAAO;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,QAAiC;AAC5C,UAAM,CAAC,MAAM,IAAI,MAAM,KAAK,GAC1B,OAAO,EAAE,OAAOC,oBAA2B,CAAC,EAC5C,KAAK,KAAK,EACV,MAAMF,KAAIC,IAAG,MAAM,YAAY,MAAM,GAAGA,IAAG,MAAM,UAAU,UAAU,CAAC,CAAC;AAEzE,WAAO,OAAO;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,SAAkB,UAA4B;AAChE,eAAW,SAAS,iBAAiB;AACpC,UAAI,QAAQ,KAAK,MAAM,SAAS,KAAK,EAAG,QAAO;AAAA,IAChD;AACA,WAAO;AAAA,EACR;AACD;;;AC3KA,SAAS,MAAAE,KAAI,OAAAC,MAAK,OAAAC,YAAW;AA2CtB,IAAM,oBAAN,MAAwB;AAAA,EAG9B,YAAoB,IAAc;AAAd;AACnB,UAAM,WAA6B;AAAA,MAClC,QAAQ,YAAY;AAAA,MACpB,UAAU,YAAY;AAAA,MACtB,SAAS,YAAY;AAAA,MACrB,WAAW,YAAY;AAAA,MACvB,eAAe,YAAY;AAAA,IAC5B;AACA,UAAM,WAA4B;AAAA,MACjC,OAAO;AAAA,MACP,KAAK,EAAE,QAAQ,WAAW,UAAU,cAAc,SAAS,YAAY,WAAW,cAAc,eAAe,kBAAkB;AAAA,MACjI,IAAI,EAAE,QAAQ,UAAU,UAAU,aAAa,SAAS,WAAW,WAAW,aAAa,eAAe,gBAAgB;AAAA,IAC3H;AACA,SAAK,OAAO,IAAI,eAAe,IAAI,aAAa,UAAU,QAAQ;AAAA,EACnE;AAAA,EAdoB;AAAA,EAFX;AAAA,EAkBT,MAAM,OAAO,OAAgD;AAC5D,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,CAAC,GAAG,IAAI,MAAM,KAAK,GACvB,OAAO,QAAQ,EACf,OAAO;AAAA,MACP,eAAe,MAAM;AAAA,MACrB,eAAe,MAAM;AAAA,MACrB,oBAAoB,MAAM,sBAAsB;AAAA,MAChD,kBAAkB,MAAM,oBAAoB;AAAA,MAC5C,iBAAiB,MAAM,mBAAmB;AAAA,MAC1C,gBAAgB,MAAM;AAAA,MACtB,aAAa,MAAM,eAAe;AAAA,MAClC,eAAe,MAAM,iBAAiB;AAAA,MACtC,QAAQ,MAAM,UAAU;AAAA,MACxB,iBAAiB,MAAM,mBAAmB;AAAA,MAC1C,aAAa;AAAA,MACb,gBAAgB;AAAA,IACjB,CAAC,EACA,UAAU;AAGZ,UAAM,KAAK,uBAAuB,MAAM,aAAa;AAErD,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,QAAQ,IAA6C;AAC1D,UAAM,OAAO,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAMC,IAAG,SAAS,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC;AAC5F,WAAO,KAAK,CAAC;AAAA,EACd;AAAA,EAEA,MAAM,UAAU,QAAgB,QAAwC;AACvE,UAAM,aAAa,CAACA,IAAG,SAAS,eAAe,MAAM,CAAC;AACtD,QAAI,OAAQ,YAAW,KAAKA,IAAG,SAAS,iBAAiB,MAAM,CAAC;AAEhE,WAAQ,MAAM,KAAK,GACjB,OAAO,EACP,KAAK,QAAQ,EACb,MAAMC,KAAI,GAAG,UAAU,CAAC,EACxB,QAAQ,SAAS,WAAW;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,QAAgB,SAAiB,GAA0B;AAC5E,WAAO,KAAK,UAAU,QAAQ,GAAG;AAAA,EAClC;AAAA,EAEA,MAAM,QAAQ,IAA8B;AAC3C,UAAM,UAAU,MAAM,KAAK,QAAQ,EAAE;AACrC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,KAAK,GAAG,OAAO,QAAQ,EAAE,IAAI,EAAE,iBAAiB,IAAI,CAAC,EAAE,MAAMD,IAAG,SAAS,WAAW,EAAE,CAAC;AAC7F,UAAM,KAAK,uBAAuB,QAAQ,aAAa;AACvD,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,UAAU,IAA8B;AAC7C,UAAM,UAAU,MAAM,KAAK,QAAQ,EAAE;AACrC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,KAAK,GAAG,OAAO,QAAQ,EAAE,IAAI,EAAE,iBAAiB,IAAI,CAAC,EAAE,MAAMA,IAAG,SAAS,WAAW,EAAE,CAAC;AAC7F,UAAM,KAAK,uBAAuB,QAAQ,aAAa;AACvD,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,KAAK,IAA8B;AACxC,UAAM,UAAU,MAAM,KAAK,QAAQ,EAAE;AACrC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,KAAK,GAAG,OAAO,QAAQ,EAAE,IAAI,EAAE,iBAAiB,OAAO,CAAC,EAAE,MAAMA,IAAG,SAAS,WAAW,EAAE,CAAC;AAChG,UAAM,KAAK,uBAAuB,QAAQ,aAAa;AACvD,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,MAAM,IAA8B;AACzC,UAAM,UAAU,MAAM,KAAK,QAAQ,EAAE;AACrC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,KAAK,KAAK,OAAO,IAAI,iBAAiB,QAAQ,eAAe;AACnE,UAAM,KAAK,GAAG,OAAO,QAAQ,EAAE,IAAI,EAAE,iBAAiB,QAAQ,CAAC,EAAE,MAAMA,IAAG,SAAS,WAAW,EAAE,CAAC;AACjG,UAAM,KAAK,uBAAuB,QAAQ,aAAa;AACvD,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,IAA8B;AACrD,UAAM,UAAU,MAAM,KAAK,QAAQ,EAAE;AACrC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,KAAK,KAAK,mBAAmB,EAAE;AACrC,UAAM,SAAS,MAAM,KAAK,GAAG,OAAO,QAAQ,EAAE,MAAMA,IAAG,SAAS,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,SAAS,UAAU,CAAC;AAC3H,QAAI,OAAO,SAAS,EAAG,OAAM,KAAK,uBAAuB,QAAQ,aAAa;AAC9E,WAAO,OAAO,SAAS;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,SAAiB,cAAsB,OAA4D;AAC3G,UAAM,QAAQ,QAAQ,YAAY;AAGlC,QAAI,MAAM,cAAc;AACvB,iBAAW,QAAQ,MAAM,cAAc;AACtC,YAAI,MAAM,SAAS,KAAK,YAAY,CAAC,EAAG,QAAO;AAAA,MAChD;AAAA,IACD;AAGA,QAAI,MAAM,iBAAiB;AAC1B,iBAAW,QAAQ,MAAM,iBAAiB;AACzC,YAAI,MAAM,SAAS,KAAK,YAAY,CAAC,EAAG,QAAO;AAAA,MAChD;AAAA,IACD;AAGA,QAAI,MAAM,aAAa,QAAW;AACjC,YAAM,aAAa,QAAQ,MAAM,cAAc,KAAK,CAAC,GAAG;AACxD,UAAI,YAAY,MAAM,SAAU,QAAO;AAAA,IACxC;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAAgB,aAAqB,SAAmC;AACzF,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,WAAW,SAAS,UAAU,CAAC,EACxC,KAAK,QAAQ,EACb;AAAA,MACAC;AAAA,QACCD,IAAG,SAAS,eAAe,MAAM;AAAA,QACjCA,IAAG,SAAS,oBAAoB,WAAW;AAAA,QAC3CA,IAAG,SAAS,gBAAgB,OAAO;AAAA,MACpC;AAAA,IACD,EACC,MAAM,CAAC;AACT,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,UAAkB,cAAwC;AAC1E,UAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAI;AACxD,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,WAAW,SAAS,UAAU,CAAC,EACxC,KAAK,QAAQ,EACb;AAAA,MACAC;AAAA,QACCD,IAAG,SAAS,iBAAiB,QAAQ;AAAA,QACrCE,OAAM,SAAS,WAAW,MAAM,MAAM;AAAA,MACvC;AAAA,IACD,EACC,MAAM,CAAC;AACT,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA,EAEA,MAAM,cAAc,QAAkD;AACrE,UAAM,aAAa,WAAW,SAAY,CAACF,IAAG,SAAS,eAAe,MAAM,CAAC,IAAI,CAAC;AAClF,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,QAAQ,SAAS,iBAAiB,OAAOE,oBAA2B,CAAC,EAC9E,KAAK,QAAQ,EACb,MAAM,WAAW,SAAS,IAAID,KAAI,GAAG,UAAU,IAAI,MAAS,EAC5D,QAAQ,SAAS,eAAe;AAElC,UAAM,SAAiC,CAAC;AACxC,eAAW,OAAO,KAAM,QAAO,IAAI,MAAM,IAAI,IAAI;AACjD,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,uBAAuB,QAA+B;AACnE,UAAM,CAAC,MAAM,IAAI,MAAM,KAAK,GAC1B,OAAO,EAAE,OAAOC,oBAA2B,CAAC,EAC5C,KAAK,QAAQ,EACb,MAAMD,KAAID,IAAG,SAAS,eAAe,MAAM,GAAGA,IAAG,SAAS,iBAAiB,GAAG,CAAC,CAAC;AAElF,UAAM,KAAK,GACT,OAAO,KAAK,EACZ,IAAI,EAAE,cAAc,OAAO,MAAM,CAAC,EAClC,MAAMA,IAAG,MAAM,IAAI,MAAM,CAAC;AAAA,EAC7B;AACD;","names":["pgTable","serial","text","timestamp","varchar","index","jsonb","pgTable","serial","timestamp","text","varchar","index","jsonb","pgTable","serial","bigint","text","timestamp","varchar","index","jsonb","pgTable","serial","bigint","text","varchar","timestamp","index","jsonb","pgTable","serial","bigint","text","varchar","index","jsonb","pgTable","serial","varchar","bigint","index","text","jsonb","pgTable","serial","text","varchar","jsonb","uniqueIndex","index","pgTable","serial","text","timestamp","varchar","bigint","index","pgTable","serial","bigint","varchar","timestamp","jsonb","index","pgTable","serial","bigint","varchar","timestamp","jsonb","index","pgTable","serial","varchar","timestamp","jsonb","bigint","index","result","eq","and","sql","eq","result","eq","and","sql","eq","and","sql","inArray","eq","and","inArray","sql","eq","and","sql","and","eq","sql","eq","and","sql","eq","and","sql"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newcms/database",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Database schema, object cache, and repositories for NewCMS",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -18,14 +18,13 @@
|
|
|
18
18
|
"cache",
|
|
19
19
|
"newcms"
|
|
20
20
|
],
|
|
21
|
+
"main": "./dist/index.cjs",
|
|
22
|
+
"module": "./dist/index.js",
|
|
21
23
|
"exports": {
|
|
22
24
|
".": {
|
|
23
25
|
"types": "./dist/index.d.ts",
|
|
24
|
-
"import": "./dist/index.js"
|
|
25
|
-
|
|
26
|
-
"./schema": {
|
|
27
|
-
"types": "./dist/schema/index.d.ts",
|
|
28
|
-
"import": "./dist/schema/index.js"
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
|
+
"require": "./dist/index.cjs"
|
|
29
28
|
}
|
|
30
29
|
},
|
|
31
30
|
"files": [
|
|
@@ -41,14 +40,15 @@
|
|
|
41
40
|
"@types/node": "^22.15.3",
|
|
42
41
|
"dotenv": "^16.5.0",
|
|
43
42
|
"drizzle-kit": "^0.31.1",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.19.4",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"vitest": "^3.2.4",
|
|
47
47
|
"@newcms/config": "0.0.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
-
"build": "
|
|
51
|
-
"dev": "
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"dev": "tsup --watch",
|
|
52
52
|
"typecheck": "tsc --noEmit",
|
|
53
53
|
"test": "vitest run",
|
|
54
54
|
"test:watch": "vitest",
|
package/dist/cache/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/cache/index.js
DELETED
package/dist/cache/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import Redis from 'ioredis';
|
|
2
|
-
export interface ObjectCacheConfig {
|
|
3
|
-
host: string;
|
|
4
|
-
port: number;
|
|
5
|
-
keyPrefix?: string;
|
|
6
|
-
defaultTtl?: number;
|
|
7
|
-
}
|
|
8
|
-
export interface CacheGroupConfig {
|
|
9
|
-
/** TTL in seconds. 0 = no expiry */
|
|
10
|
-
ttl: number;
|
|
11
|
-
/** Whether this group is global (shared across sites in multisite) */
|
|
12
|
-
global: boolean;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Redis-backed object cache implementing the CMS cache API.
|
|
16
|
-
*
|
|
17
|
-
* Features:
|
|
18
|
-
* - Group-based key namespacing
|
|
19
|
-
* - Global vs per-site groups (multisite support)
|
|
20
|
-
* - Configurable TTL per group
|
|
21
|
-
* - Batch get/set operations via pipeline
|
|
22
|
-
* - Flush by group or total flush
|
|
23
|
-
* - Increment/decrement operations
|
|
24
|
-
*/
|
|
25
|
-
export declare class ObjectCache {
|
|
26
|
-
private redis;
|
|
27
|
-
private siteId;
|
|
28
|
-
private keyPrefix;
|
|
29
|
-
private defaultTtl;
|
|
30
|
-
/** Groups that are shared across all sites (multisite) */
|
|
31
|
-
private globalGroups;
|
|
32
|
-
/** Per-group TTL overrides (in seconds) */
|
|
33
|
-
private groupTtl;
|
|
34
|
-
/** Local in-memory cache for the current request (non-persistent) */
|
|
35
|
-
private localCache;
|
|
36
|
-
constructor(config: ObjectCacheConfig);
|
|
37
|
-
connect(): Promise<void>;
|
|
38
|
-
disconnect(): Promise<void>;
|
|
39
|
-
/**
|
|
40
|
-
* Set the current site ID (for multisite per-site cache isolation).
|
|
41
|
-
*/
|
|
42
|
-
setSiteId(siteId: number): void;
|
|
43
|
-
/**
|
|
44
|
-
* Register groups as global (shared across sites in multisite).
|
|
45
|
-
*/
|
|
46
|
-
addGlobalGroups(groups: string[]): void;
|
|
47
|
-
/**
|
|
48
|
-
* Set TTL for a specific cache group.
|
|
49
|
-
*/
|
|
50
|
-
setGroupTtl(group: string, ttlSeconds: number): void;
|
|
51
|
-
/**
|
|
52
|
-
* Build the Redis key for a cache entry.
|
|
53
|
-
*/
|
|
54
|
-
private buildKey;
|
|
55
|
-
/**
|
|
56
|
-
* Build a local cache key (in-memory, for current request).
|
|
57
|
-
*/
|
|
58
|
-
private buildLocalKey;
|
|
59
|
-
/**
|
|
60
|
-
* Get TTL for a group in seconds.
|
|
61
|
-
*/
|
|
62
|
-
private getTtl;
|
|
63
|
-
/**
|
|
64
|
-
* Get a cached value.
|
|
65
|
-
*
|
|
66
|
-
* @returns The cached value, or undefined if not found
|
|
67
|
-
*/
|
|
68
|
-
get<T = unknown>(key: string, group?: string): Promise<T | undefined>;
|
|
69
|
-
/**
|
|
70
|
-
* Set a cached value.
|
|
71
|
-
*
|
|
72
|
-
* @param key - Cache key
|
|
73
|
-
* @param value - Value to cache (will be JSON serialized)
|
|
74
|
-
* @param group - Cache group
|
|
75
|
-
* @param ttl - TTL in seconds (overrides group default). 0 = no expiry.
|
|
76
|
-
* @returns true on success
|
|
77
|
-
*/
|
|
78
|
-
set(key: string, value: unknown, group?: string, ttl?: number): Promise<boolean>;
|
|
79
|
-
/**
|
|
80
|
-
* Add a cached value only if it doesn't already exist.
|
|
81
|
-
*
|
|
82
|
-
* @returns true if the value was added, false if key already exists
|
|
83
|
-
*/
|
|
84
|
-
add(key: string, value: unknown, group?: string, ttl?: number): Promise<boolean>;
|
|
85
|
-
/**
|
|
86
|
-
* Delete a cached value.
|
|
87
|
-
*
|
|
88
|
-
* @returns true if the key existed and was deleted
|
|
89
|
-
*/
|
|
90
|
-
delete(key: string, group?: string): Promise<boolean>;
|
|
91
|
-
/**
|
|
92
|
-
* Increment a numeric cached value.
|
|
93
|
-
*
|
|
94
|
-
* @returns The new value, or false if key doesn't exist
|
|
95
|
-
*/
|
|
96
|
-
incr(key: string, group?: string, offset?: number): Promise<number | false>;
|
|
97
|
-
/**
|
|
98
|
-
* Decrement a numeric cached value.
|
|
99
|
-
*
|
|
100
|
-
* @returns The new value, or false if key doesn't exist
|
|
101
|
-
*/
|
|
102
|
-
decr(key: string, group?: string, offset?: number): Promise<number | false>;
|
|
103
|
-
/**
|
|
104
|
-
* Get multiple cached values at once.
|
|
105
|
-
*
|
|
106
|
-
* @returns Map of key -> value (missing keys are not included)
|
|
107
|
-
*/
|
|
108
|
-
getMultiple(keys: string[], group?: string): Promise<Map<string, unknown>>;
|
|
109
|
-
/**
|
|
110
|
-
* Set multiple cached values at once.
|
|
111
|
-
*/
|
|
112
|
-
setMultiple(entries: Map<string, unknown> | Record<string, unknown>, group?: string, ttl?: number): Promise<boolean>;
|
|
113
|
-
/**
|
|
114
|
-
* Flush all keys in a specific group using SCAN + pipeline DEL.
|
|
115
|
-
*
|
|
116
|
-
* @returns Number of keys deleted
|
|
117
|
-
*/
|
|
118
|
-
flushGroup(group: string): Promise<number>;
|
|
119
|
-
/**
|
|
120
|
-
* Flush all cached data (all groups, all sites).
|
|
121
|
-
* Uses SCAN + DEL to only clear cache keys (not other Redis data).
|
|
122
|
-
*
|
|
123
|
-
* @returns Number of keys deleted
|
|
124
|
-
*/
|
|
125
|
-
flushAll(): Promise<number>;
|
|
126
|
-
/**
|
|
127
|
-
* Clear only the local in-memory cache (for request boundary cleanup).
|
|
128
|
-
*/
|
|
129
|
-
clearLocalCache(): void;
|
|
130
|
-
/**
|
|
131
|
-
* Check if a key exists in cache.
|
|
132
|
-
*/
|
|
133
|
-
exists(key: string, group?: string): Promise<boolean>;
|
|
134
|
-
/**
|
|
135
|
-
* Get the underlying Redis instance (for advanced operations).
|
|
136
|
-
*/
|
|
137
|
-
getRedis(): Redis;
|
|
138
|
-
}
|
|
139
|
-
//# sourceMappingURL=object-cache.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"object-cache.d.ts","sourceRoot":"","sources":["../../src/cache/object-cache.ts"],"names":[],"mappings":"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"}
|