fix(skills): align repository guidance with implementation (#1315)

* docs(skills): design implementation synchronization

* docs(skills): plan implementation synchronization

* fix(release): filter internal notes from public body

* docs(release): synchronize release workflow guidance

* fix(stalker): normalize catalog series flags

* fix(stalker): preserve progress with scoped episode IDs

* fix(playback): expose strict position persistence

* docs(stalker): record series position compatibility

* test(skills): validate repository skill contracts

* fix(database): keep SQL trace values private

* docs(skills): refresh Nx and SQLite ownership

* docs(skills): align provider and UI guidance

* docs(skills): tighten validated guidance

* docs(release): require exact release pushes

* style(electron): remove trailing blank line

* fix(ci): classify repository skills coverage
This commit is contained in:
4gray authored and GitHub committed 2026-07-31 08:00:59 +02:00
1 parent 99d167993d
commit 2ac0de752f
68 files changed
+7524 -793

No files matched your search

@@ -18,6 +18,10 @@ function createdObjectNames(prefix: string, statements: readonly string[]) {
}
describe('database schema statements', () => {
afterEach(() => {
jest.restoreAllMocks();
});
const {
createTableStatements,
columnMigrationStatements,
@@ -37,6 +41,26 @@ describe('database schema statements', () => {
}
).ensureDownloadsPauseResumeSchema;
it('records only the statement type for expanded main-process SQL', () => {
jest.spyOn(console, 'log').mockImplementation(() => undefined);
const secrets = [
'main-user-secret',
'main-password-secret',
'https://main-user:main-password@example.com/live?token=main-token-secret',
];
__databaseConnectionTestHooks.traceSqlStatement(
`UPDATE playlists SET username = '${secrets[0]}', password = '${secrets[1]}', url = '${secrets[2]}'`
);
const output = (console.log as jest.Mock).mock.calls.flat().join('\n');
expect(output).toContain('"statementType":"UPDATE"');
expect(output).not.toContain('UPDATE playlists');
for (const secret of secrets) {
expect(output).not.toContain(secret);
}
});
function createRebuildSqlite(legacyTableSql: string | undefined) {
const statements: string[] = [];
const transaction = jest.fn((callback: () => void) => callback);
+13 -13
View File
@@ -12,6 +12,10 @@
import Database from 'better-sqlite3';
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import {
redactSensitiveData,
summarizeSqlStatementForTrace,
} from '@iptvnator/shared/logging';
import * as schema from './schema';
import { getIptvnatorDatabasePath } from './path-utils';
@@ -49,13 +53,6 @@ function isSqlTraceEnabled(): boolean {
);
}
function compactSqlForTrace(sql: string): string {
const compactSql = sql.replace(/\s+/g, ' ').trim();
return compactSql.length <= 180
? compactSql
: `${compactSql.slice(0, 177)}...`;
}
function traceSql(scope: string, message: string, payload?: unknown): void {
if (payload === undefined) {
console.log(`[IPTVnator Trace][${scope}] ${message}`);
@@ -63,10 +60,16 @@ function traceSql(scope: string, message: string, payload?: unknown): void {
}
console.log(
`[IPTVnator Trace][${scope}] ${message} ${JSON.stringify(payload)}`
`[IPTVnator Trace][${scope}] ${message} ${JSON.stringify(
redactSensitiveData(payload)
)}`
);
}
function traceSqlStatement(sql: unknown): void {
traceSql('sql-main', 'query', summarizeSqlStatementForTrace(sql));
}
/**
* Get the database file path
*/
@@ -402,6 +405,7 @@ export const __databaseConnectionTestHooks = {
backfillEpgProgramSourceUrls,
cleanupLegacyTmdbSearchCache,
runMigrations,
traceSqlStatement,
} as const;
/**
@@ -1119,11 +1123,7 @@ export async function initDatabase(
sqlite = new Database(filePath, {
readonly,
verbose: isSqlTraceEnabled()
? (message?: unknown) => {
traceSql('sql-main', 'query', {
sql: compactSqlForTrace(String(message ?? '')),
});
}
? (message?: unknown) => traceSqlStatement(message)
: undefined,
});