Harden sync and security; add IQ Bible, templates, password reset, backups
Build and Push Docker Image / build-and-push (push) Successful in 7m14s
Build and Push Docker Image / build-and-push (push) Successful in 7m14s
Sync and data safety - Autosave flushes on leaving a study or the tab; storage-full errors are reported - Unsynced studies, deletes and ink are queued and retried when back online - Deletes propagate across devices (with a "Keep it" undo) and no longer resurrect - Server assigns study versions (no client-clock conflicts); "Combine both" merges a conflicted study chunk by chunk - Reading-plan progress made offline is merged, not overwritten; plans, ink and marks are cleared from the device on sign-out Security - Block SSRF through the ntfy reminder URL; failure-only rate limiting per IP and account; constant-time login; TOTP replay protection; input validation; SESSION_SECRET required in production; admin user delete fixed - Forgot password: one-time code sent to the account's ntfy topic Features - IQ Bible proxy (route allow-list, text-only caching per its terms): more commentaries, lexicons, study aids, daily verse and devotional, plan calendar export, narrations, podcast background - Study templates, reading-plan catch-up, account export, daily database snapshots Tests and tooling - ESLint config; App tests split into src/test; tests for sync queues, library hook, merge, templates, IQ Bible UI and reset form Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6bb392933a
commit
f274bc9c8a
67 files changed
+4734
-1569
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { test, expect } from 'vitest';
|
||||
import { normalizeStrongs, pickIqEntries, describeRecord, listOf, iqUrl } from './iqbible.js';
|
||||
|
||||
test('Strong\'s numbers are normalised', () => {
|
||||
expect(normalizeStrongs('G0026')).toBe('G26');
|
||||
expect(normalizeStrongs('h430')).toBe('H430');
|
||||
expect(normalizeStrongs('H1647G')).toBe('H1647');
|
||||
expect(normalizeStrongs('nope')).toBeNull();
|
||||
});
|
||||
|
||||
test('commentary entries are chosen by range, then by the closest earlier verse', () => {
|
||||
const entries = [
|
||||
{ chapter: 3, verse: 0, text: 'intro' },
|
||||
{ chapter: 3, verse: 1, verse_end: 8, text: 'a' },
|
||||
{ chapter: 3, verse: 9, text: 'b' },
|
||||
{ chapter: 3, verse: 9, text: 'b2' },
|
||||
];
|
||||
expect(pickIqEntries(entries, 3, 5).map((e) => e.text)).toEqual(['a']);
|
||||
expect(pickIqEntries(entries, 3, 12).map((e) => e.text)).toEqual(['b', 'b2']);
|
||||
expect(pickIqEntries(entries, 4, 1)).toEqual([]);
|
||||
});
|
||||
|
||||
test('loosely shaped records are summarised and lists found', () => {
|
||||
expect(describeRecord({ name: 'faith', edition: 'iqbible', citation: 'John 3:16' }).title).toBe('faith');
|
||||
expect(describeRecord('x').title).toBe('x');
|
||||
expect(listOf({ status: 'ok', data: [1, 2] })).toEqual([1, 2]);
|
||||
expect(listOf({ topics: [3] })).toEqual([3]);
|
||||
expect(iqUrl('bibles/eng_kjv/reading-plan', { days: 30, books: 'MAT,MRK', format: 'ical', skip: '' })).toBe('/api/iq/bibles/eng_kjv/reading-plan?days=30&books=MAT%2CMRK&format=ical');
|
||||
});
|
||||
Reference in new issue
Block a user