AppView in a box as a Vite plugin thing hatk.dev
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: fix lint warnings and format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+189 -127
+5 -1
packages/hatk/src/cli.ts
··· 2011 2011 const configDir2 = resolve('.') 2012 2012 const lexicons2 = loadLexicons(resolve(configDir2, 'lexicons')) 2013 2013 const collections2 = config.collections.length > 0 ? config.collections : discoverCollections(lexicons2) 2014 - const { schemas: schemas2, ddlStatements: ddl2 } = buildSchemas(lexicons2, collections2, getDialect(config.databaseEngine)) 2014 + const { schemas: schemas2, ddlStatements: ddl2 } = buildSchemas( 2015 + lexicons2, 2016 + collections2, 2017 + getDialect(config.databaseEngine), 2018 + ) 2015 2019 2016 2020 if (config.database !== ':memory:') { 2017 2021 mkdirSync(dirname(config.database), { recursive: true })
+1 -1
packages/hatk/src/config.ts
··· 96 96 relay: env.RELAY || parsed.relay || 'ws://localhost:2583', 97 97 plc: env.DID_PLC_URL || parsed.plc || 'https://plc.directory', 98 98 port: parseInt(env.PORT || '') || parsed.port || 3000, 99 - databaseEngine: ((env.DATABASE_ENGINE || parsed.databaseEngine || 'sqlite') as HatkConfig['databaseEngine']), 99 + databaseEngine: (env.DATABASE_ENGINE || parsed.databaseEngine || 'sqlite') as HatkConfig['databaseEngine'], 100 100 database: database ? resolve(configDir, database) : ':memory:', 101 101 publicDir: parsed.publicDir === null ? null : resolve(configDir, parsed.publicDir || './public'), 102 102 collections: parsed.collections || [],
+1 -5
packages/hatk/src/database/adapters/duckdb-search.ts
··· 4 4 export class DuckDBSearchPort implements SearchPort { 5 5 constructor(private port: DatabasePort) {} 6 6 7 - async buildIndex( 8 - shadowTable: string, 9 - sourceQuery: string, 10 - searchColumns: string[], 11 - ): Promise<void> { 7 + async buildIndex(shadowTable: string, sourceQuery: string, searchColumns: string[]): Promise<void> { 12 8 // Create shadow table 13 9 await this.port.execute(`CREATE OR REPLACE TABLE ${shadowTable} AS ${sourceQuery}`, []) 14 10
+22 -6
packages/hatk/src/database/adapters/duckdb.ts
··· 17 17 } 18 18 19 19 close(): void { 20 - try { this.readCon?.closeSync() } catch {} 21 - try { this.writeCon?.closeSync() } catch {} 22 - try { this.instance?.closeSync() } catch {} 20 + try { 21 + this.readCon?.closeSync() 22 + } catch {} 23 + try { 24 + this.writeCon?.closeSync() 25 + } catch {} 26 + try { 27 + this.instance?.closeSync() 28 + } catch {} 23 29 } 24 30 25 31 async query<T = Record<string, unknown>>(sql: string, params: unknown[] = []): Promise<T[]> { ··· 73 79 }) 74 80 } 75 81 76 - async createBulkInserter(table: string, columns: string[], _options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }): Promise<BulkInserter> { 82 + async createBulkInserter( 83 + table: string, 84 + _columns: string[], 85 + _options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }, 86 + ): Promise<BulkInserter> { 77 87 const appender = await this.writeCon.createAppender(table.replace(/"/g, '')) 78 88 return { 79 89 append(values: unknown[]) { ··· 114 124 private enqueue<T>(queue: 'read' | 'write', fn: () => Promise<T>): Promise<T> { 115 125 if (queue === 'write') { 116 126 const p = this.writeQueue.then(fn) 117 - this.writeQueue = p.then(() => {}, () => {}) 127 + this.writeQueue = p.then( 128 + () => {}, 129 + () => {}, 130 + ) 118 131 return p 119 132 } else { 120 133 const p = this.readQueue.then(fn) 121 - this.readQueue = p.then(() => {}, () => {}) 134 + this.readQueue = p.then( 135 + () => {}, 136 + () => {}, 137 + ) 122 138 return p 123 139 } 124 140 }
+1 -5
packages/hatk/src/database/adapters/sqlite-search.ts
··· 10 10 export class SQLiteSearchPort implements SearchPort { 11 11 constructor(private port: DatabasePort) {} 12 12 13 - async buildIndex( 14 - shadowTable: string, 15 - sourceQuery: string, 16 - searchColumns: string[], 17 - ): Promise<void> { 13 + async buildIndex(shadowTable: string, sourceQuery: string, searchColumns: string[]): Promise<void> { 18 14 // Drop existing FTS table and data table 19 15 await this.port.execute(`DROP TABLE IF EXISTS ${shadowTable}_fts`, []) 20 16 await this.port.execute(`DROP TABLE IF EXISTS ${shadowTable}`, [])
+16 -5
packages/hatk/src/database/adapters/sqlite.ts
··· 32 32 } 33 33 34 34 close(): void { 35 - try { this.db?.close() } catch {} 35 + try { 36 + this.db?.close() 37 + } catch {} 36 38 } 37 39 38 40 async query<T = Record<string, unknown>>(sql: string, params: unknown[] = []): Promise<T[]> { ··· 63 65 this.db.exec('ROLLBACK') 64 66 } 65 67 66 - async createBulkInserter(table: string, columns: string[], options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }): Promise<BulkInserter> { 68 + async createBulkInserter( 69 + table: string, 70 + columns: string[], 71 + options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }, 72 + ): Promise<BulkInserter> { 67 73 const placeholders = columns.map(() => '?').join(', ') 68 - const conflict = options?.onConflict === 'ignore' ? ' OR IGNORE' : options?.onConflict === 'replace' ? ' OR REPLACE' : '' 74 + const conflict = 75 + options?.onConflict === 'ignore' ? ' OR IGNORE' : options?.onConflict === 'replace' ? ' OR REPLACE' : '' 69 76 const sql = `INSERT${conflict} INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})` 70 77 const stmt = this.db.prepare(sql) 71 78 const buffer: unknown[][] = [] ··· 89 96 buffer.push(values) 90 97 if (buffer.length >= batchSize) flush() 91 98 }, 92 - async flush() { flush() }, 93 - async close() { flush() }, 99 + async flush() { 100 + flush() 101 + }, 102 + async close() { 103 + flush() 104 + }, 94 105 } 95 106 } 96 107 }
+74 -56
packages/hatk/src/database/db.ts
··· 1 1 import { type TableSchema, toSnakeCase } from './schema.ts' 2 2 import type { Row } from '../lex-types.ts' 3 - import { getSearchColumns, stripStopWords, hasSearchPort, getSearchPort } from './fts.ts' 3 + import { getSearchColumns, stripStopWords, getSearchPort } from './fts.ts' 4 4 import { emit, timer } from '../logger.ts' 5 5 import { OAUTH_DDL } from '../oauth/db.ts' 6 6 import type { DatabasePort } from './ports.ts' ··· 10 10 let dialect: SqlDialect 11 11 const schemas = new Map<string, TableSchema>() 12 12 13 - export function getDatabasePort(): DatabasePort { return port } 14 - export function getSqlDialect(): SqlDialect { return dialect } 13 + export function getDatabasePort(): DatabasePort { 14 + return port 15 + } 16 + export function getSqlDialect(): SqlDialect { 17 + return dialect 18 + } 15 19 16 20 export function closeDatabase(): void { 17 21 port?.close() ··· 135 139 if (upper === 'VARCHAR' || upper === 'CHARACTER VARYING') return 'TEXT' 136 140 if (upper === 'TIMESTAMP WITH TIME ZONE') return 'TIMESTAMPTZ' 137 141 if (upper === 'BOOLEAN' || upper === 'BOOL') return 'BOOLEAN' 138 - if (upper === 'INT' || upper === 'INT4' || upper === 'INT8' || upper === 'BIGINT' || upper === 'SMALLINT') return 'INTEGER' 142 + if (upper === 'INT' || upper === 'INT4' || upper === 'INT8' || upper === 'BIGINT' || upper === 'SMALLINT') 143 + return 'INTEGER' 139 144 return upper 140 145 } 141 146 ··· 316 321 break 317 322 } 318 323 } catch (err: any) { 319 - console.warn(`[migration] failed to ${change.action} column "${change.column}" on "${change.table}": ${err.message}`) 320 - emit('migration', 'error', { action: change.action, table: change.table, column: change.column, error: err.message }) 324 + console.warn( 325 + `[migration] failed to ${change.action} column "${change.column}" on "${change.table}": ${err.message}`, 326 + ) 327 + emit('migration', 'error', { 328 + action: change.action, 329 + table: change.table, 330 + column: change.column, 331 + error: err.message, 332 + }) 321 333 } 322 334 } 323 335 } ··· 382 394 383 395 export async function listRetryEligibleRepos(maxRetries: number): Promise<string[]> { 384 396 const now = Math.floor(Date.now() / 1000) 385 - const rows = await all( 386 - `SELECT did FROM _repos WHERE status = 'failed' AND retry_after <= $1 AND retry_count < $2`, 387 - [now, maxRetries], 388 - ) 397 + const rows = await all(`SELECT did FROM _repos WHERE status = 'failed' AND retry_after <= $1 AND retry_count < $2`, [ 398 + now, 399 + maxRetries, 400 + ]) 389 401 return rows.map((r: any) => r.did) 390 402 } 391 403 ··· 550 562 } 551 563 } 552 564 553 - await run( 554 - `INSERT INTO ${child.tableName} (${colNames.join(', ')}) VALUES (${placeholders.join(', ')})`, 555 - values, 556 - ) 565 + await run(`INSERT INTO ${child.tableName} (${colNames.join(', ')}) VALUES (${placeholders.join(', ')})`, values) 557 566 } 558 567 } 559 568 ··· 615 624 values.push(raw) 616 625 } 617 626 } 618 - await run( 619 - `INSERT INTO ${branch.tableName} (${colNames.join(', ')}) VALUES (${placeholders.join(', ')})`, 620 - values, 621 - ) 627 + await run(`INSERT INTO ${branch.tableName} (${colNames.join(', ')}) VALUES (${placeholders.join(', ')})`, values) 622 628 } 623 629 } 624 630 } ··· 658 664 ) 659 665 if (!label.neg && existing.length > 0) continue 660 666 661 - await run( 662 - `INSERT INTO _labels (src, uri, val, neg, cts, exp) VALUES ($1, $2, $3, $4, $5, $6)`, 663 - [label.src, label.uri, label.val, label.neg || false, label.cts || new Date().toISOString(), label.exp || null], 664 - ) 667 + await run(`INSERT INTO _labels (src, uri, val, neg, cts, exp) VALUES ($1, $2, $3, $4, $5, $6)`, [ 668 + label.src, 669 + label.uri, 670 + label.val, 671 + label.neg || false, 672 + label.cts || new Date().toISOString(), 673 + label.exp || null, 674 + ]) 665 675 } 666 676 } 667 677 ··· 916 926 } 917 927 918 928 /** Extract a column value from a record, handling strongRef expansion and type coercion for bulk insert */ 919 - function resolveColumnValue(col: { name: string; originalName: string; sqlType: string; isRef: boolean }, record: Record<string, any>): unknown { 929 + function resolveColumnValue( 930 + col: { name: string; originalName: string; sqlType: string; isRef: boolean }, 931 + record: Record<string, any>, 932 + ): unknown { 920 933 let rawValue = record[col.originalName] 921 934 if (rawValue && typeof rawValue === 'object' && col.name.endsWith('_uri') && col.isRef) { 922 935 rawValue = rawValue.uri ··· 1143 1156 if (!schema) throw new Error(`Unknown collection: ${collection}`) 1144 1157 1145 1158 const elapsed = timer() 1146 - const { limit = 20, cursor, fuzzy = true } = opts 1159 + const { limit = 20, fuzzy = true } = opts 1147 1160 const textCols = schema.columns.filter((c) => c.sqlType === 'TEXT') 1148 1161 1149 1162 // Also check if FTS has indexed any columns (including derived JSON columns) ··· 1154 1167 1155 1168 // FTS shadow table name (dots replaced with underscores) 1156 1169 const safeName = '_fts_' + collection.replace(/\./g, '_') 1157 - const ftsSchema = `fts_main_${safeName}` 1158 1170 1159 1171 const phaseErrors: string[] = [] 1160 1172 const phasesUsed: string[] = [] ··· 1162 1174 // Phase 1: BM25 ranked search via SearchPort 1163 1175 let bm25Results: any[] = [] 1164 1176 const sp = getSearchPort() 1165 - if (sp) try { 1166 - const ftsQuery = stripStopWords(query) 1167 - const ftsSearchColNames = getSearchColumns(collection) 1177 + if (sp) 1178 + try { 1179 + const ftsQuery = stripStopWords(query) 1180 + const ftsSearchColNames = getSearchColumns(collection) 1168 1181 1169 - // Get ranked URIs from the search port 1170 - const hits = await sp.search(safeName, ftsQuery, ftsSearchColNames, limit + 1, 0) 1171 - if (hits.length > 0) { 1172 - const uriList = hits.map((h) => h.uri) 1173 - const scoreMap = new Map(hits.map((h) => [h.uri, h.score])) 1182 + // Get ranked URIs from the search port 1183 + const hits = await sp.search(safeName, ftsQuery, ftsSearchColNames, limit + 1, 0) 1184 + if (hits.length > 0) { 1185 + const uriList = hits.map((h) => h.uri) 1186 + const scoreMap = new Map(hits.map((h) => [h.uri, h.score])) 1174 1187 1175 - // Fetch full records for matched URIs 1176 - const placeholders = uriList.map((_, i) => `$${i + 1}`).join(', ') 1177 - const rows = await all( 1178 - `SELECT m.* FROM ${schema.tableName} m 1188 + // Fetch full records for matched URIs 1189 + const placeholders = uriList.map((_, i) => `$${i + 1}`).join(', ') 1190 + const rows = await all( 1191 + `SELECT m.* FROM ${schema.tableName} m 1179 1192 LEFT JOIN _repos r ON m.did = r.did 1180 1193 WHERE m.uri IN (${placeholders}) 1181 1194 AND (r.status IS NULL OR r.status != 'takendown')`, 1182 - uriList, 1183 - ) 1195 + uriList, 1196 + ) 1184 1197 1185 - // Re-attach scores and sort 1186 - bm25Results = rows 1187 - .map((r: any) => ({ ...r, score: scoreMap.get(r.uri) ?? 0 })) 1188 - .sort((a: any, b: any) => b.score - a.score) 1198 + // Re-attach scores and sort 1199 + bm25Results = rows 1200 + .map((r: any) => ({ ...r, score: scoreMap.get(r.uri) ?? 0 })) 1201 + .sort((a: any, b: any) => b.score - a.score) 1202 + } 1203 + phasesUsed.push('bm25') 1204 + } catch (err: any) { 1205 + phaseErrors.push(`bm25: ${err.message}`) 1189 1206 } 1190 - phasesUsed.push('bm25') 1191 - } catch (err: any) { 1192 - phaseErrors.push(`bm25: ${err.message}`) 1193 - } 1194 1207 1195 1208 const bm25Count = bm25Results.length 1196 1209 const hasMore = bm25Results.length > limit ··· 1359 1372 return run(sql, params) 1360 1373 } 1361 1374 1362 - export async function createBulkInserterSQL(table: string, columns: string[], options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }): Promise<import('./ports.ts').BulkInserter> { 1375 + export async function createBulkInserterSQL( 1376 + table: string, 1377 + columns: string[], 1378 + options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }, 1379 + ): Promise<import('./ports.ts').BulkInserter> { 1363 1380 return port.createBulkInserter(table, columns, options) 1364 1381 } 1365 1382 ··· 1605 1622 } 1606 1623 1607 1624 export async function queryLabelsByDid(did: string): Promise<any[]> { 1608 - return all( 1609 - `SELECT * FROM _labels WHERE uri LIKE $1 AND neg = false AND (exp IS NULL OR exp > CURRENT_TIMESTAMP)`, 1610 - [`at://${did}/%`], 1611 - ) 1625 + return all(`SELECT * FROM _labels WHERE uri LIKE $1 AND neg = false AND (exp IS NULL OR exp > CURRENT_TIMESTAMP)`, [ 1626 + `at://${did}/%`, 1627 + ]) 1612 1628 } 1613 1629 1614 1630 export async function searchAccounts(query: string, limit: number = 20): Promise<any[]> { ··· 1655 1671 } 1656 1672 1657 1673 export async function putPreference(did: string, key: string, value: any): Promise<void> { 1658 - await run( 1659 - `INSERT OR REPLACE INTO _preferences (did, key, value, updated_at) VALUES ($1, $2, $3, $4)`, 1660 - [did, key, JSON.stringify(value), new Date().toISOString()], 1661 - ) 1674 + await run(`INSERT OR REPLACE INTO _preferences (did, key, value, updated_at) VALUES ($1, $2, $3, $4)`, [ 1675 + did, 1676 + key, 1677 + JSON.stringify(value), 1678 + new Date().toISOString(), 1679 + ]) 1662 1680 } 1663 1681 1664 1682 export async function filterTakendownDids(dids: string[]): Promise<Set<string>> {
+7 -5
packages/hatk/src/database/dialect.ts
··· 121 121 jaroWinklerSimilarity: null, 122 122 stringAgg: (col, sep) => `group_concat(${col}, ${sep})`, 123 123 supportsSequences: false, 124 - introspectColumnsQuery: (tableName) => 125 - `PRAGMA table_info("${tableName}")`, 124 + introspectColumnsQuery: (tableName) => `PRAGMA table_info("${tableName}")`, 126 125 } 127 126 128 127 export function getDialect(dialect: Dialect): SqlDialect { 129 128 switch (dialect) { 130 - case 'duckdb': return DUCKDB_DIALECT 131 - case 'sqlite': return SQLITE_DIALECT 132 - case 'postgres': throw new Error('PostgreSQL adapter not yet implemented') 129 + case 'duckdb': 130 + return DUCKDB_DIALECT 131 + case 'sqlite': 132 + return SQLITE_DIALECT 133 + case 'postgres': 134 + throw new Error('PostgreSQL adapter not yet implemented') 133 135 } 134 136 }
+8 -7
packages/hatk/src/database/fts.ts
··· 22 22 * Given a JSON column and its lexicon property definition, produce 23 23 * search column expressions that extract searchable text. 24 24 */ 25 - function jsonSearchColumns(colName: string, prop: any, lexicon: any, dialect: import('./dialect.ts').SqlDialect): SearchColumn[] { 25 + function jsonSearchColumns( 26 + colName: string, 27 + prop: any, 28 + lexicon: any, 29 + dialect: import('./dialect.ts').SqlDialect, 30 + ): SearchColumn[] { 26 31 const columns: SearchColumn[] = [] 27 32 // Strip table qualifier (e.g. "t.artists" → "artists") for use in aliases 28 33 const aliasBase = colName.includes('.') ? colName.split('.').pop()! : colName ··· 149 154 if (col.sqlType === 'TEXT') { 150 155 const alias = `${child.fieldName}_${col.name}` 151 156 const agg = dialect.stringAgg(`c.${col.name}`, "' '") 152 - selectExprs.push( 153 - `(SELECT ${agg} FROM ${child.tableName} c WHERE c.parent_uri = t.uri) AS ${alias}`, 154 - ) 157 + selectExprs.push(`(SELECT ${agg} FROM ${child.tableName} c WHERE c.parent_uri = t.uri) AS ${alias}`) 155 158 searchColNames.push(alias) 156 159 } 157 160 } ··· 164 167 if (col.sqlType === 'TEXT') { 165 168 const alias = `${union.fieldName}_${branch.branchName}_${col.name}` 166 169 const agg = dialect.stringAgg(`c.${col.name}`, "' '") 167 - selectExprs.push( 168 - `(SELECT ${agg} FROM ${branch.tableName} c WHERE c.parent_uri = t.uri) AS ${alias}`, 169 - ) 170 + selectExprs.push(`(SELECT ${agg} FROM ${branch.tableName} c WHERE c.parent_uri = t.uri) AS ${alias}`) 170 171 searchColNames.push(alias) 171 172 } 172 173 }
+6 -6
packages/hatk/src/database/ports.ts
··· 29 29 rollback(): Promise<void> 30 30 31 31 /** Create a bulk inserter for high-throughput writes */ 32 - createBulkInserter(table: string, columns: string[], options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }): Promise<BulkInserter> 32 + createBulkInserter( 33 + table: string, 34 + columns: string[], 35 + options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }, 36 + ): Promise<BulkInserter> 33 37 } 34 38 35 39 export interface BulkInserter { ··· 45 49 46 50 export interface SearchPort { 47 51 /** Build/rebuild an FTS index for a table */ 48 - buildIndex( 49 - shadowTable: string, 50 - sourceQuery: string, 51 - searchColumns: string[], 52 - ): Promise<void> 52 + buildIndex(shadowTable: string, sourceQuery: string, searchColumns: string[]): Promise<void> 53 53 54 54 /** Search a table, returning URIs with scores */ 55 55 search(
+6 -1
packages/hatk/src/database/schema.ts
··· 255 255 } 256 256 257 257 // Generate a TableSchema from a lexicon record definition 258 - export function generateTableSchema(nsid: string, lexicon: any, lexicons?: Map<string, any>, dialect: SqlDialect = DUCKDB_DIALECT): TableSchema { 258 + export function generateTableSchema( 259 + nsid: string, 260 + lexicon: any, 261 + lexicons?: Map<string, any>, 262 + dialect: SqlDialect = DUCKDB_DIALECT, 263 + ): TableSchema { 259 264 const mainDef = lexicon.defs?.main 260 265 if (!mainDef || mainDef.type !== 'record') { 261 266 throw new Error(`Lexicon ${nsid} does not define a record type`)
+8 -1
packages/hatk/src/indexer.ts
··· 1 1 import { cborDecode } from './cbor.ts' 2 2 import { parseCarFrame } from './car.ts' 3 - import { insertRecord, deleteRecord, setCursor, setRepoStatus, getRepoRetryInfo, listAllRepoStatuses } from './database/db.ts' 3 + import { 4 + insertRecord, 5 + deleteRecord, 6 + setCursor, 7 + setRepoStatus, 8 + getRepoRetryInfo, 9 + listAllRepoStatuses, 10 + } from './database/db.ts' 4 11 import { backfillRepo } from './backfill.ts' 5 12 import { rebuildAllIndexes } from './database/fts.ts' 6 13 import { log, emit, timer } from './logger.ts'
+4 -7
packages/hatk/src/main.ts
··· 3 3 import { dirname, resolve } from 'node:path' 4 4 import { log } from './logger.ts' 5 5 import { loadConfig } from './config.ts' 6 - import { 7 - loadLexicons, 8 - storeLexicons, 9 - discoverCollections, 10 - buildSchemas, 11 - } from './database/schema.ts' 6 + import { loadLexicons, storeLexicons, discoverCollections, buildSchemas } from './database/schema.ts' 12 7 import { discoverViews } from './views.ts' 13 8 import { initDatabase, getCursor, querySQL, getSqlDialect, getSchemaDump, migrateSchema } from './database/db.ts' 14 9 import { createAdapter } from './database/adapter-factory.ts' ··· 88 83 setSearchPort(searchPort) 89 84 await initDatabase(adapter, config.database, schemas, ddlStatements) 90 85 logMemory('after-db-init') 91 - log(`[main] Database initialized (${config.databaseEngine}, ${config.database === ':memory:' ? 'in-memory' : config.database})`) 86 + log( 87 + `[main] Database initialized (${config.databaseEngine}, ${config.database === ':memory:' ? 'in-memory' : config.database})`, 88 + ) 92 89 93 90 // Auto-migrate schema if lexicons changed 94 91 const migrationChanges = await migrateSchema(schemas)
+13 -12
packages/hatk/src/oauth/db.ts
··· 73 73 } 74 74 75 75 export async function storeServerKey(kid: string, privateKey: string, publicKey: string): Promise<void> { 76 - await runSQL( 77 - 'INSERT OR REPLACE INTO _oauth_keys (kid, private_key, public_key) VALUES ($1, $2, $3)', 78 - [kid, privateKey, publicKey], 79 - ) 76 + await runSQL('INSERT OR REPLACE INTO _oauth_keys (kid, private_key, public_key) VALUES ($1, $2, $3)', [ 77 + kid, 78 + privateKey, 79 + publicKey, 80 + ]) 80 81 } 81 82 82 83 // --- OAuth Request Storage --- ··· 138 139 // --- Authorization Codes --- 139 140 140 141 export async function storeAuthCode(code: string, requestUri: string): Promise<void> { 141 - await runSQL( 142 - 'INSERT INTO _oauth_codes (code, request_uri, created_at) VALUES ($1, $2, $3)', 143 - [code, requestUri, Math.floor(Date.now() / 1000)], 144 - ) 142 + await runSQL('INSERT INTO _oauth_codes (code, request_uri, created_at) VALUES ($1, $2, $3)', [ 143 + code, 144 + requestUri, 145 + Math.floor(Date.now() / 1000), 146 + ]) 145 147 } 146 148 147 149 export async function consumeAuthCode(code: string): Promise<string | null> { ··· 223 225 await runSQL('DELETE FROM _oauth_dpop_jtis WHERE expires_at < $1', [now]) 224 226 await runSQL('DELETE FROM _oauth_requests WHERE expires_at < $1', [now]) 225 227 await runSQL('DELETE FROM _oauth_codes WHERE created_at < $1', [now - 600]) 226 - await runSQL( 227 - 'DELETE FROM _oauth_refresh_tokens WHERE revoked = 1 OR (expires_at IS NOT NULL AND expires_at < $1)', 228 - [now], 229 - ) 228 + await runSQL('DELETE FROM _oauth_refresh_tokens WHERE revoked = 1 OR (expires_at IS NOT NULL AND expires_at < $1)', [ 229 + now, 230 + ]) 230 231 }
+5 -1
packages/hatk/src/setup.ts
··· 34 34 query: (sql: string, params?: any[]) => Promise<any[]> 35 35 run: (sql: string, params?: any[]) => Promise<void> 36 36 runBatch: (operations: Array<{ sql: string; params: any[] }>) => Promise<void> 37 - createBulkInserter: (table: string, columns: string[], options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }) => Promise<BulkInserter> 37 + createBulkInserter: ( 38 + table: string, 39 + columns: string[], 40 + options?: { onConflict?: 'ignore' | 'replace'; batchSize?: number }, 41 + ) => Promise<BulkInserter> 38 42 } 39 43 } 40 44
+12 -8
packages/hatk/src/test.ts
··· 141 141 if (Array.isArray(records)) { 142 142 for (const rec of records) { 143 143 const row = interpolateHelpers(rec) 144 - await runSQL( 145 - `INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, 146 - [row.did, row.status || 'active', row.handle || row.did.split(':').pop() + '.test', new Date().toISOString()], 147 - ) 144 + await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, [ 145 + row.did, 146 + row.status || 'active', 147 + row.handle || row.did.split(':').pop() + '.test', 148 + new Date().toISOString(), 149 + ]) 148 150 } 149 151 } 150 152 } ··· 189 191 // Auto-register DID in _repos if not already present 190 192 if (!seenDids.has(did)) { 191 193 seenDids.add(did) 192 - await runSQL( 193 - `INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, 194 - [did, 'active', did.split(':').pop() + '.test', new Date().toISOString()], 195 - ) 194 + await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, [ 195 + did, 196 + 'active', 197 + did.split(':').pop() + '.test', 198 + new Date().toISOString(), 199 + ]) 196 200 } 197 201 await insertRecord(tableName, uri, cid, did, fields) 198 202 }