"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.sessions = exports.messageBoardTopics = exports.messageBoardReplies = exports.legacyCredentialsStaging = exports.inventoryItems = exports.characters = exports.auditLog = exports.accounts = void 0; var _sqliteCore = await jitiImport("drizzle-orm/sqlite-core"); // Source: Phase 3 SDOC-03/04 + plan 03-06 + D-13 + D-14. // // Hand-authored Drizzle schema (sqlite dialect, drizzle-orm 0.45.2). Phase 4 // SRV-01..03 imports these tables verbatim — schema drift here = Phase 4 // redesign loop. Phase 4 wires the better-sqlite3 runtime + migration runner; // Phase 3's job is to lock the shape. // // Per D-14 ("cite, don't preserve"): every column comment cites either the // originating .bnu/.bnb field (filename_pattern + GML field name from // docs/extracted-server/save-formats.json) OR a D-### decision row in // .planning/phases/03-server-documentation-schemas/03-CONTEXT.md OR a // script:line citation under extracted/server-5-4/. The legacy text-line // shape is NOT preserved — only the data semantics are carried forward, // normalized into modern SQL. // // Per D-22 / WARNING 7 the per-column SOURCE-comment lint runs deterministically // (scripts/check-source-comments.mjs) — every column declaration line MUST be // followed by a `// SOURCE:` comment. // ============================================================================= // accounts — production credentials, post-migration // ============================================================================= // Phase 4 SRV-01..03 owns the runtime. Better-Auth Drizzle adapter lives in // Phase 4 packages/server (Assumption A5 — column-name compatibility verified // at adapter-wire time). Plaintext passwords NEVER land here; argon2id only, // per CLAUDE.md hard rule #2. const accounts = exports.accounts = (0, _sqliteCore.sqliteTable)('accounts', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 + Better-Auth users-table convention; CUID2 string id. username: (0, _sqliteCore.text)('username').notNull().unique(), // SOURCE: User_DBUpdated.bnu (save-formats.json filename_pattern) — username field carried verbatim from legacy account namespace. passwordHash: (0, _sqliteCore.text)('password_hash').notNull(), // SOURCE: CLAUDE.md hard rule #2 — argon2id encoded string (~95 bytes typical, $argon2id$...). email: (0, _sqliteCore.text)('email'), // SOURCE: D-13 — nullable; legacy-only accounts have no email until first password reset. role: (0, _sqliteCore.text)('role').notNull().default('player'), // SOURCE: D-13 — 'player' | 'mod' | 'admin'. Phase 7 PAR-07 admin UI authorizes by role. createdAt: (0, _sqliteCore.integer)('created_at', { mode: 'timestamp' }).notNull(), // SOURCE: D-13 audit field — account creation epoch. lastLoginAt: (0, _sqliteCore.integer)('last_login_at', { mode: 'timestamp' }), // SOURCE: D-13 audit field — nullable until first successful login. forceReset: (0, _sqliteCore.integer)('force_reset', { mode: 'boolean' }).notNull().default(false) // SOURCE: D-04 staging-pipeline trigger — set true when legacyCredentialsStaging.algorithm in {'plaintext','bcrypt-weak'} forces password reset on first login. }); // ============================================================================= // legacy_credentials_staging — read-once-then-purged migration buffer (D-04) // ============================================================================= // Plaintext from legacy/servers/enlyzeam-current/localList.txt lands here ONLY, // NEVER in `accounts`. Phase 4 SRV-09/10/11 owns the rehash + force-reset flow; // Phase 5 RESTORE.md repeats the read-once-then-purge step. Schema verbatim // per D-04 spec lines in 03-CONTEXT.md. const legacyCredentialsStaging = exports.legacyCredentialsStaging = (0, _sqliteCore.sqliteTable)('legacy_credentials_staging', { username: (0, _sqliteCore.text)('username').primaryKey(), // SOURCE: D-04 staging-table spec verbatim — primary key matches User_DBUpdated.bnu#username for migration join. legacyHash: (0, _sqliteCore.blob)('legacy_hash'), // SOURCE: D-04 staging-table spec verbatim — legacy hash bytes (or plaintext for localList.txt rows); nullable when only username is known. algorithm: (0, _sqliteCore.text)('algorithm').notNull(), // SOURCE: D-04 staging-table spec verbatim — 'plaintext' | 'bcrypt-weak' | ...; drives forceReset flag. forceReset: (0, _sqliteCore.integer)('force_reset', { mode: 'boolean' }).notNull(), // SOURCE: D-04 staging-table spec verbatim — 1 if algorithm in {'plaintext','bcrypt-weak'}; copied to accounts.forceReset on rehash. legacySource: (0, _sqliteCore.text)('legacy_source').notNull(), // SOURCE: D-04 staging-table spec verbatim — provenance like 'enlyzeam-current/localList.txt:LINE'; audit trail for migration. importedAt: (0, _sqliteCore.integer)('imported_at', { mode: 'timestamp' }).notNull() // SOURCE: D-04 staging-table spec verbatim — epoch seconds the row was loaded; informs purge cadence. }); // ============================================================================= // characters — per-account world state (.bnu HXB equivalent, normalized) // ============================================================================= // Derived from extracted/server-5-4/scripts/0359-server_receive.gml case 5 // (movement) lines 142-143 (p_x/p_y readint) + 95-100 (p_spr broadcast). // Source filename UserData/HXB/Bridges_.bnu (save-formats.json // filename_pattern, mvp=true). D-14 modernization: original text-line layout // discarded; we keep one row per character, integer-indexed, sprite variants // JSON-encoded so the table schema doesn't fan out per visual slot. const characters = exports.characters = (0, _sqliteCore.sqliteTable)('characters', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 — CUID2 character id; not the legacy GML uid. accountId: (0, _sqliteCore.text)('account_id').notNull().references(() => accounts.id), // SOURCE: D-13 — FK to accounts.id; one account may have multiple characters in v1+. roomId: (0, _sqliteCore.integer)('room_id').notNull(), // SOURCE: extracted/server-5-4/scripts/0359-server_receive.gml:71 (global.p_room[pid] = tempint) — current room number. x: (0, _sqliteCore.integer)('x').notNull(), // SOURCE: extracted/server-5-4/scripts/0359-server_receive.gml:142 (global.p_x[upid] = readint()) — world x in pixels. y: (0, _sqliteCore.integer)('y').notNull(), // SOURCE: extracted/server-5-4/scripts/0359-server_receive.gml:143 (global.p_y[upid] = readint()) — world y in pixels. spriteId: (0, _sqliteCore.integer)('sprite_id').notNull(), // SOURCE: extracted/server-5-4/scripts/0359-server_receive.gml:128 (global.p_spr[upid,0] = readint()) — base sprite index from p_spr[uid,0]. spriteVariants: (0, _sqliteCore.text)('sprite_variants', { mode: 'json' }).$type(), // SOURCE: p_spr[uid,1..5] 5-element string array per 0359-server_receive.gml:131 (global.p_spr[upid,tempint] = readstring()); D-13/D-14 normalization (JSON-encoded, not 5 columns). createdAt: (0, _sqliteCore.integer)('created_at', { mode: 'timestamp' }).notNull(), // SOURCE: D-13 audit field — character creation epoch. lastSavedAt: (0, _sqliteCore.integer)('last_saved_at', { mode: 'timestamp' }).notNull() // SOURCE: D-13 audit field — last persistence write; mirrors UserData/HXB/Bridges_.bnu mtime in legacy snapshot. }); // ============================================================================= // inventory_items — per-account inventory (UserData/Inv/Inventory_.bnu) // ============================================================================= // Derived from extracted/server-5-4/scripts/0376-uinv_backup.gml (write side) // + 0377-uinv_restore.gml (read side). Original .bnu format is a per-category // nested loop (iv_totalcats outer, uinv_get(uid,cat,0) inner count). D-14 // modernization: each inventory entry becomes one row keyed by (account_id, // category, slot) — composite primary key — discarding the count-then-loop // shape. Phase 4 SRV-10/11 import maps the legacy file once. const inventoryItems = exports.inventoryItems = (0, _sqliteCore.sqliteTable)( 'inventory_items', { accountId: (0, _sqliteCore.text)('account_id').notNull().references(() => accounts.id), // SOURCE: UserData/Inv/Inventory_.bnu — argument0 (uid) maps to accounts.id post-migration; 0376-uinv_backup.gml:1. category: (0, _sqliteCore.integer)('category').notNull(), // SOURCE: UserData/Inv/Inventory_.bnu — outer loop index lu over global.iv_totalcats (0376-uinv_backup.gml:6 for(lu=0; lu.bnu — inner loop index li over uinv_get(argument0,lu,0) per 0376-uinv_backup.gml:11; 1-based in legacy, preserved here. itemId: (0, _sqliteCore.text)('item_id').notNull() // SOURCE: UserData/Inv/Inventory_.bnu — uinv_get(argument0,lu,li) string written via file_text_write_string at 0376-uinv_backup.gml:13. }, (t) => ({ pk: (0, _sqliteCore.primaryKey)({ columns: [t.accountId, t.category, t.slot] }) }) ); // ============================================================================= // message_board_topics — MB_Log.bnb @TOPIC section // ============================================================================= // Derived from extracted/server-5-4/scripts/0365-mb_backup.gml:14-18. // Original MB_Log.bnb @TOPIC section: per-topic 3-field block { mb_topic[i,0] // (string title/body), mb_topic[i,1] (real reply count), mb_topic[i,2] (real // flag) }. Boards (mb_board[i] preceding @TOPIC) are denormalized into // boardId here per D-13. const messageBoardTopics = exports.messageBoardTopics = (0, _sqliteCore.sqliteTable)('message_board_topics', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 — CUID2 topic id; not derived from legacy positional index. boardId: (0, _sqliteCore.integer)('board_id').notNull(), // SOURCE: MB_Log.bnb pre-@TOPIC section: global.mb_board[i] roster (0365-mb_backup.gml:6 for(i=0; i accounts.id), // SOURCE: D-13 — modernized: original embedded author in title string; new schema FKs to accounts.id; nullable for legacy rows where author can't be resolved. createdAt: (0, _sqliteCore.integer)('created_at', { mode: 'timestamp' }).notNull() // SOURCE: D-13 audit field — topic creation epoch; legacy MB_Log.bnb has no per-topic timestamp, so legacy rows get import time. }); // ============================================================================= // message_board_replies — MB_Log.bnb @REPLY section // ============================================================================= // Derived from extracted/server-5-4/scripts/0365-mb_backup.gml:24-30. Original // @REPLY section: nested loop per topic, j < mb_topic[i,1] inner; per-reply // single string mb_reply[i,j]. const messageBoardReplies = exports.messageBoardReplies = (0, _sqliteCore.sqliteTable)('message_board_replies', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 — CUID2 reply id. topicId: (0, _sqliteCore.text)('topic_id').notNull().references(() => messageBoardTopics.id), // SOURCE: MB_Log.bnb @REPLY section nested-loop outer index i mapping to mb_topic[i,*]; modernized to FK on message_board_topics.id. body: (0, _sqliteCore.text)('body').notNull(), // SOURCE: MB_Log.bnb @REPLY section field global.mb_reply[i,j] (0365-mb_backup.gml:30 file_text_write_string ... mb_reply[i,j]). authorAccountId: (0, _sqliteCore.text)('author_account_id').references(() => accounts.id), // SOURCE: D-13 — modernized: legacy reply body embeds author inline; new schema FKs to accounts.id; nullable for legacy rows. createdAt: (0, _sqliteCore.integer)('created_at', { mode: 'timestamp' }).notNull() // SOURCE: D-13 audit field — reply creation epoch; legacy rows take import time. }); // ============================================================================= // audit_log — Phase 7 PAR-07 admin actions (seeded EMPTY in Phase 3) // ============================================================================= // Per D-13 + RESEARCH §Open Question 1, the column set is locked NOW even // though no rows write until Phase 7 — avoids a Phase 7 schema migration. The // payloadJson column is the ESCAPE HATCH (D-13): per-action shape lives in JSON // so new admin actions can ship without DDL changes. Baseline migration // includes the CREATE TABLE but no INSERT INTO audit_log statements. const auditLog = exports.auditLog = (0, _sqliteCore.sqliteTable)('audit_log', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 audit_log spec; seeded EMPTY in Phase 3, Phase 7 PAR-07 writes; CUID2. actorAccountId: (0, _sqliteCore.text)('actor_account_id').notNull().references(() => accounts.id), // SOURCE: D-13 audit_log spec — admin/mod who performed the action; FK to accounts.id. action: (0, _sqliteCore.text)('action').notNull(), // SOURCE: D-13 audit_log spec — action verb (e.g., 'kick', 'mute', 'ban', 'mb-moderate'); enumerated in 03-PATTERNS modernized admin command surface. targetAccountId: (0, _sqliteCore.text)('target_account_id').references(() => accounts.id), // SOURCE: D-13 audit_log spec — nullable; not all admin actions target an account (e.g., world broadcast). payloadJson: (0, _sqliteCore.text)('payload_json', { mode: 'json' }).$type(), // SOURCE: ESCAPE HATCH per D-13 + RESEARCH §Open Question 1 — per-action payload (reason strings, durations, MB topic ids); no Phase 7 schema migration required to add new admin actions. createdAt: (0, _sqliteCore.integer)('created_at', { mode: 'timestamp' }).notNull() // SOURCE: D-13 audit_log spec — action epoch; baseline migration emits no INSERT, so no rows in 0001_baseline.sql. }); // ============================================================================= // sessions — Phase 4 SRV-06 reconnection grace // ============================================================================= // Per D-13 sessions spec. Phase 4 SRV-06 uses these rows to grant a // reconnection window (heartbeatAt diff) before treating the player as // disconnected. Not Better-Auth sessions (those live in a Better-Auth-managed // table per its Drizzle adapter) — this is the in-game world session. const sessions = exports.sessions = (0, _sqliteCore.sqliteTable)('sessions', { id: (0, _sqliteCore.text)('id').primaryKey(), // SOURCE: D-13 sessions spec — CUID2 session id; rotated per reconnection. accountId: (0, _sqliteCore.text)('account_id').notNull().references(() => accounts.id), // SOURCE: D-13 sessions spec — FK to accounts.id; one active session per account in v1. roomId: (0, _sqliteCore.integer)('room_id').notNull(), // SOURCE: D-13 sessions spec — current room; mirrors characters.roomId at session-end snapshot for reconnection-without-character-load fast path. connectedAt: (0, _sqliteCore.integer)('connected_at', { mode: 'timestamp' }).notNull(), // SOURCE: D-13 sessions spec — initial connection epoch; immutable for the session. heartbeatAt: (0, _sqliteCore.integer)('heartbeat_at', { mode: 'timestamp' }).notNull() // SOURCE: D-13 sessions spec; Phase 4 SRV-06 reconnection-grace bound — last received client heartbeat. }); /* v9-d97bafa7fc796023 */