diff --git a/packages/core/__mocks__/node-storage.mock.ts b/packages/core/__mocks__/node-storage.mock.ts index 88d2dcfe7..455b2febf 100644 --- a/packages/core/__mocks__/node-storage.mock.ts +++ b/packages/core/__mocks__/node-storage.mock.ts @@ -24,6 +24,12 @@ export class NodeStorageInterface implements IStorage { storage = {}; crypto = new NNCrypto(); + async removeMulti(keys: string[]): Promise { + for (const key of keys) { + this.remove(key); + } + } + async write(key: string, data: T): Promise { this.storage[key] = data; } diff --git a/packages/core/__tests__/lookup.test.js b/packages/core/__tests__/lookup.test.js index b556e35b2..e5a19e6b7 100644 --- a/packages/core/__tests__/lookup.test.js +++ b/packages/core/__tests__/lookup.test.js @@ -31,7 +31,7 @@ test("search notes", () => content: content }).then(async ({ db }) => { await db.notes.add(TEST_NOTE); - let filtered = await db.lookup.notes("note the world"); + let filtered = await db.lookup.notes("note of the world").ids(); expect(filtered).toHaveLength(1); })); @@ -42,8 +42,8 @@ test("search notes with a locked note", () => const noteId = await db.notes.add(TEST_NOTE); await db.vault.create("password"); await db.vault.add(noteId); - expect(await db.lookup.notes("note the world")).toHaveLength(1); - expect(await db.lookup.notes("format")).toHaveLength(0); + expect(await db.lookup.notes("note of the world").ids()).toHaveLength(1); + expect(await db.lookup.notes("format").ids()).toHaveLength(0); })); test("search notes with an empty note", () => @@ -54,16 +54,13 @@ test("search notes with an empty note", () => title: "world is a heavy tune", content: { type: "tiptap", data: "


" } }); - let filtered = await db.lookup.notes("heavy tune"); + let filtered = await db.lookup.notes("heavy tune").ids(); expect(filtered).toHaveLength(1); })); test("search notebooks", () => notebookTest().then(async ({ db }) => { await db.notebooks.add(TEST_NOTEBOOK2); - let filtered = db.lookup.notebooks( - await db.notebooks.all.items(), - "Description" - ); + let filtered = await db.lookup.notebooks("Description").ids(); expect(filtered.length).toBeGreaterThan(0); })); diff --git a/packages/core/__tests__/migrations.test.ts b/packages/core/__tests__/migrations.test.ts index 1c7fd4e5f..e62094b88 100644 --- a/packages/core/__tests__/migrations.test.ts +++ b/packages/core/__tests__/migrations.test.ts @@ -171,7 +171,7 @@ test("[5.6] move pins to shortcuts", () => expect(await migrateItem(item, 5.6, 5.7, "settings", db, "local")).toBe( true ); - const shortcuts = await db.shortcuts.all.items(); + const shortcuts = db.shortcuts.all; expect(item.pins).toBeUndefined(); expect( shortcuts.find((s) => s.itemType === "notebook" && s.itemId === "hello") @@ -546,7 +546,7 @@ test("[5.9] move attachments.noteIds to relations", () => .get(); expect(attachment.noteIds).toBeUndefined(); expect(linkedNotes).toHaveLength(1); - expect(linkedNotes[0]).toBe("HELLO_NOTE_ID"); + expect(linkedNotes[0].toId).toBe("HELLO_NOTE_ID"); })); test.todo("[5.9] flatten attachment object", () => @@ -584,8 +584,8 @@ describe("[5.9] move topics out of notebooks & use relations", () => { .get(); expect(notebook.topics).toBeUndefined(); expect(linkedNotebooks).toHaveLength(2); - expect(linkedNotebooks.some((a) => a === "topics1")).toBeTruthy(); - expect(linkedNotebooks.some((a) => a === "topics2")).toBeTruthy(); + expect(linkedNotebooks.some((a) => a.toId === "topics1")).toBeTruthy(); + expect(linkedNotebooks.some((a) => a.toId === "topics2")).toBeTruthy(); expect(await db.notebooks.all.count()).toBe(2); expect(await db.notebooks.notebook("topics1")).toBeDefined(); expect(await db.notebooks.notebook("topics2")).toBeDefined(); @@ -621,8 +621,8 @@ describe("[5.9] move topics out of notebooks & use relations", () => { .get(); expect(note.notebooks).toBeUndefined(); expect(linkedNotebooks).toHaveLength(2); - expect(linkedNotebooks.some((a) => a === "topic1")).toBeTruthy(); - expect(linkedNotebooks.some((a) => a === "topic2")).toBeTruthy(); + expect(linkedNotebooks.some((a) => a.fromId === "topic1")).toBeTruthy(); + expect(linkedNotebooks.some((a) => a.fromId === "topic2")).toBeTruthy(); })); }); diff --git a/packages/core/__tests__/note-history.test.js b/packages/core/__tests__/note-history.test.js index e8793fa8b..c42f0f3bf 100644 --- a/packages/core/__tests__/note-history.test.js +++ b/packages/core/__tests__/note-history.test.js @@ -33,7 +33,7 @@ import { test, expect } from "vitest"; test("new history session should be automatically created on note save", () => noteTest({ ...TEST_NOTE, sessionId: Date.now() }).then(async ({ db, id }) => { - const sessions = await db.noteHistory.get(id); + const sessions = await db.noteHistory.get(id).items(); expect(sessions).toHaveLength(1); await expect(db.noteHistory.content(sessions[0].id)).resolves.toMatchObject( TEST_NOTE.content @@ -53,7 +53,9 @@ test("editing the same note should create multiple history sessions", () => sessionId: Date.now() + 10000 }); - const sessions = await db.noteHistory.get(id); + const sessions = await db.noteHistory + .get(id) + .items(undefined, { sortBy: "dateModified", sortDirection: "desc" }); expect(sessions).toHaveLength(2); await expect(db.noteHistory.content(sessions[0].id)).resolves.toMatchObject( @@ -77,7 +79,9 @@ test("restoring an old session should replace note's content", () => sessionId: Date.now() + 10000 }); - const [, firstVersion] = await db.noteHistory.get(id); + const [, firstVersion] = await db.noteHistory + .get(id) + .items(undefined, { sortBy: "dateModified", sortDirection: "desc" }); await db.noteHistory.restore(firstVersion.id); const contentId = (await db.notes.note(id)).contentId; @@ -86,7 +90,9 @@ test("restoring an old session should replace note's content", () => test("date created of session should not change on edit", () => noteTest({ ...TEST_NOTE, sessionId: "session" }).then(async ({ db, id }) => { - const [{ dateCreated, dateModified }] = await db.noteHistory.get(id); + const [{ dateCreated, dateModified }] = await db.noteHistory + .get(id) + .items(undefined, { sortBy: "dateModified", sortDirection: "desc" }); let editedContent = { data: TEST_NOTE.content.data + "

Some new content

", @@ -102,7 +108,9 @@ test("date created of session should not change on edit", () => }); const [{ dateCreated: newDateCreated, dateModified: newDateModified }] = - await db.noteHistory.get(id); + await db.noteHistory + .get(id) + .items(undefined, { sortBy: "dateModified", sortDirection: "desc" }); expect(newDateCreated).toBe(dateCreated); expect(newDateModified).toBeGreaterThan(dateModified); })); @@ -110,26 +118,22 @@ test("date created of session should not change on edit", () => test("clear a note's sessions", () => noteTest({ ...TEST_NOTE, sessionId: "session" }).then(async ({ db, id }) => { await db.noteHistory.clearSessions(id); - - let history = await db.noteHistory.get(id); - expect(history).toHaveLength(0); + expect(await db.noteHistory.get(id).count()).toBe(0); })); test("remove a single session by sessionId", () => noteTest({ ...TEST_NOTE, sessionId: "iamasession" }).then( async ({ db, id }) => { - const [{ id: sessionId }] = await db.noteHistory.get(id); + const [{ id: sessionId }] = await db.noteHistory.get(id).items(); await db.noteHistory.remove(sessionId); - let history = await db.noteHistory.get(sessionId); - expect(history).toHaveLength(0); + expect(await db.noteHistory.get(sessionId).count()).toBe(0); } )); test("return empty array if no history available", () => noteTest().then(async ({ db, id }) => { - let history = await db.noteHistory.get(id); - expect(history).toHaveLength(0); + expect(await db.noteHistory.get(id).count()).toBe(0); })); test("auto clear sessions if they exceed the limit", () => @@ -142,15 +146,14 @@ test("auto clear sessions if they exceed the limit", () => await db.notes.add({ id: id, content: editedContent, - sessionId: Date.now() + 10000 + sessionId: `${Date.now() + 10000}` }); - let sessions = await db.noteHistory.get(id); - expect(sessions).toHaveLength(2); + expect(await db.noteHistory.get(id).count()).toBe(2); await db.noteHistory.cleanup(id, 1); - sessions = await db.noteHistory.get(id); + const sessions = await db.noteHistory.get(id).items(); expect(sessions).toHaveLength(1); const content = await db.noteHistory.content(sessions[0].id); @@ -170,7 +173,7 @@ test("save a locked note should add a locked session to note history", () => sessionId: "lockedsession" }); - const sessions = await db.noteHistory.get(id); + const sessions = await db.noteHistory.get(id).items(); expect(sessions).toHaveLength(1); const lockedContent = await db.noteHistory.content(sessions[0].id); @@ -187,7 +190,6 @@ test("locking an old note should clear its history", () => await db.vault.create("password"); await db.vault.add(id); - const sessions = await db.noteHistory.get(id); - expect(sessions).toHaveLength(0); + expect(await db.noteHistory.get(id).count()).toBe(0); } )); diff --git a/packages/core/__tests__/notes.test.ts b/packages/core/__tests__/notes.test.ts index 132ce6140..a1a065215 100644 --- a/packages/core/__tests__/notes.test.ts +++ b/packages/core/__tests__/notes.test.ts @@ -399,15 +399,12 @@ test("get grouped notes by abc", () => sortBy: "title" }); - expect((await grouping.item(grouping.ids[0]))?.group?.title).toBe( - "Conflicted" - ); - expect((await grouping.item(grouping.ids[1]))?.group?.title).toBe("Pinned"); + expect((await grouping.item(0))?.group?.title).toBe("Conflicted"); + expect((await grouping.item(1))?.group?.title).toBe("Pinned"); for (let i = 0; i < alphabet.length; ++i) { - expect( - (await grouping.item(grouping.ids[i * alphabet.length + 2]))?.group - ?.title - ).toBe(alphabet[i]); + expect((await grouping.item(i * alphabet.length + 2))?.group?.title).toBe( + alphabet[i] + ); } })); @@ -430,9 +427,9 @@ test("get grouped notes by month", () => }); for (let month = 11; month >= 0; --month) { - expect( - (await grouping.item(grouping.ids[(11 - month) * 5]))?.group?.title - ).toBe(MONTHS_FULL[month]); + expect((await grouping.item((11 - month) * 5))?.group?.title).toContain( + MONTHS_FULL[month] + ); } })); @@ -455,9 +452,9 @@ test("get grouped notes by year", () => }); for (let year = 2020; year <= 2025; ++year) { - expect( - (await grouping.item(grouping.ids[(2025 - year) * 5]))?.group?.title - ).toBe(year.toString()); + expect((await grouping.item((2025 - year) * 5))?.group?.title).toBe( + year.toString() + ); } })); @@ -493,9 +490,7 @@ test("get grouped notes by week", () => "20 - 26 Feb, 2023" ]; for (let i = 1; i <= 5; ++i) { - expect((await grouping.item(grouping.ids[i * 4]))?.group?.title).toBe( - weeks[i - 1] - ); + expect((await grouping.item(i * 4))?.group?.title).toBe(weeks[i - 1]); } })); @@ -526,9 +521,7 @@ test("get grouped notes default", () => let i = 0; for (const key in ranges) { - expect((await grouping.item(grouping.ids[i * 7]))?.group?.title).toBe( - key - ); + expect((await grouping.item(i * 7))?.group?.title).toBe(key); ++i; } })); diff --git a/packages/core/__tests__/shortcuts.test.js b/packages/core/__tests__/shortcuts.test.js index 294288a2e..0fd8e1a2c 100644 --- a/packages/core/__tests__/shortcuts.test.js +++ b/packages/core/__tests__/shortcuts.test.js @@ -30,8 +30,8 @@ test("create a shortcut of an invalid item should throw", () => test("create a shortcut of notebook", () => notebookTest().then(async ({ db, id }) => { await db.shortcuts.add({ itemType: "notebook", itemId: id }); - expect(await db.shortcuts.exists(id)).toBe(true); - expect(await db.shortcuts.all.has(id)).toBe(true); + expect(db.shortcuts.exists(id)).toBe(true); + expect(db.shortcuts.all.find((s) => s.id === id)).toBeDefined(); })); test("create a duplicate shortcut of notebook", () => @@ -39,8 +39,8 @@ test("create a duplicate shortcut of notebook", () => await db.shortcuts.add({ itemType: "notebook", itemId: id }); await db.shortcuts.add({ itemType: "notebook", itemId: id }); - expect(await db.shortcuts.all.count()).toBe(1); - expect(await db.shortcuts.all.has(id)).toBe(true); + expect(db.shortcuts.all).toHaveLength(1); + expect(db.shortcuts.all.find((s) => s.id === id)).toBeDefined(); })); test("pin a tag", () => @@ -48,8 +48,8 @@ test("pin a tag", () => const tagId = await db.tags.add({ title: "HELLO!" }); await db.shortcuts.add({ itemType: "tag", itemId: tagId }); - expect(await db.shortcuts.all.count()).toBe(1); - expect(await db.shortcuts.all.has(tagId)).toBe(true); + expect(db.shortcuts.all).toHaveLength(1); + expect(db.shortcuts.all.find((s) => s.id === tagId)).toBeDefined(); })); test("remove shortcut", () => @@ -60,8 +60,8 @@ test("remove shortcut", () => itemId: tagId }); - expect(await db.shortcuts.all.count()).toBe(1); + expect(db.shortcuts.all).toHaveLength(1); await db.shortcuts.remove(shortcutId); - expect(await db.shortcuts.all.count()).toBe(0); + expect(db.shortcuts.all).toHaveLength(0); })); diff --git a/packages/core/__tests__/trash.test.ts b/packages/core/__tests__/trash.test.ts index ebba17b45..614db9c31 100644 --- a/packages/core/__tests__/trash.test.ts +++ b/packages/core/__tests__/trash.test.ts @@ -41,19 +41,17 @@ test("permanently delete a note", () => const note = await db.notes.note(noteId); if (!note) throw new Error("Could not find note."); - let sessions = await db.noteHistory.get(noteId); - expect(sessions).toHaveLength(1); + expect(await db.noteHistory.get(noteId).count()).toBe(1); await db.notes.moveToTrash(noteId); expect(await db.trash.all()).toHaveLength(1); expect(await db.content.get(note.contentId)).toBeDefined(); - await db.trash.delete({ id: noteId, type: "note" }); + await db.trash.delete(noteId); expect(await db.trash.all()).toHaveLength(0); expect(await db.content.get(note.contentId)).toBeUndefined(); - sessions = await db.noteHistory.get(noteId); - expect(sessions).toHaveLength(0); + expect(await db.noteHistory.get(noteId).count()).toBe(0); })); test("restore a deleted note that was in a notebook", () => @@ -68,7 +66,7 @@ test("restore a deleted note that was in a notebook", () => await db.notes.addToNotebook(subNotebookId, id); await db.notes.moveToTrash(id); - await db.trash.restore({ type: "note", id }); + await db.trash.restore(id); expect(await db.trash.all()).toHaveLength(0); const note = await db.notes.note(id); @@ -102,7 +100,7 @@ test("restore a deleted locked note", () => await db.notes.moveToTrash(id); expect(await db.trash.all()).toHaveLength(1); expect(await db.content.get(note.contentId)).toBeDefined(); - await db.trash.restore({ type: "note", id }); + await db.trash.restore(id); note = await db.notes.note(id); expect(await db.trash.all()).toHaveLength(0); @@ -117,7 +115,7 @@ test("restore a deleted note that's in a deleted notebook", () => await db.notes.moveToTrash(id); await db.notebooks.moveToTrash(notebookId); - await db.trash.restore({ type: "note", id }); + await db.trash.restore(id); const note = await db.notes.note(id); expect(note).toBeDefined(); expect( @@ -144,7 +142,7 @@ test("restore a deleted notebook", () => await db.notes.addToNotebook(id, noteId); await db.notebooks.moveToTrash(id); - await db.trash.restore({ type: "notebook", id }); + await db.trash.restore(id); const notebook = db.notebooks.notebook(id); expect(notebook).toBeDefined(); @@ -164,7 +162,7 @@ test("restore a notebook that has deleted notes", () => await db.notebooks.moveToTrash(id); await db.notes.moveToTrash(noteId); - await db.trash.restore({ type: "notebook", id }); + await db.trash.restore(id); const notebook = db.notebooks.notebook(id); expect(notebook).toBeDefined(); @@ -227,8 +225,7 @@ test("clear trash should delete note content", () => const notebookId = await db.notebooks.add(TEST_NOTEBOOK); - let sessions = await db.noteHistory.get(noteId); - expect(sessions).toHaveLength(1); + expect(await db.noteHistory.get(noteId).count()).toBe(1); const note = { ...(await db.notes.note(noteId)) }; @@ -244,6 +241,5 @@ test("clear trash should delete note content", () => const content = note.contentId && (await db.content.get(note.contentId)); expect(content).toBeUndefined(); - sessions = await db.noteHistory.get(noteId); - expect(sessions).toHaveLength(0); + expect(await db.noteHistory.get(noteId).count()).toBe(0); })); diff --git a/packages/core/__tests__/utils/index.ts b/packages/core/__tests__/utils/index.ts index 5bfeecfb6..2fb852112 100644 --- a/packages/core/__tests__/utils/index.ts +++ b/packages/core/__tests__/utils/index.ts @@ -49,8 +49,10 @@ function databaseTest() { fs: FS, compressor: Compressor, sqliteOptions: { - dialect: new SqliteDialect({ database: BetterSQLite3("db.sql") }) - } + dialect: (name) => + new SqliteDialect({ database: BetterSQLite3(":memory:") }) + }, + batchSize: 500 }); return db.init().then(() => db); } diff --git a/packages/core/__tests__/vault.test.js b/packages/core/__tests__/vault.test.js index 956ba86d6..e23a7c9c7 100644 --- a/packages/core/__tests__/vault.test.js +++ b/packages/core/__tests__/vault.test.js @@ -97,7 +97,7 @@ test("unlock a note permanently", () => expect(note.headline).not.toBe(""); const content = await db.content.get(note.contentId); expect(content.data).toBeDefined(); - expect(typeof content.data).toBe("object"); + expect(typeof content.data).toBe("string"); })); test("save a locked note", () => diff --git a/packages/core/src/collections/content.ts b/packages/core/src/collections/content.ts index 54d84679e..698f6893d 100644 --- a/packages/core/src/collections/content.ts +++ b/packages/core/src/collections/content.ts @@ -78,6 +78,18 @@ export class Content implements ICollection { "Please use db.content.merge for merging remote content." ); + if (content.noteId && !content.id) { + // find content from noteId + content.id = ( + await this.db + .sql() + .selectFrom("content") + .where("noteId", "==", content.noteId) + .select("id") + .executeTakeFirst() + )?.id; + } + const id = content.id || getId(); const encryptedData = isCipher(content.data) ? content.data : undefined; @@ -107,12 +119,13 @@ export class Content implements ICollection { ...contentData }); - if (content.sessionId && contentData && content.type && content.noteId) + if (content.sessionId && contentData && content.type && content.noteId) { await this.db.noteHistory.add(content.sessionId, { noteId: content.noteId, type: content.type, ...contentData }); + } } else if (content.noteId) { const contentItem: ContentItem = { type: "tiptap", @@ -186,6 +199,14 @@ export class Content implements ICollection { }) .execute(); } + + // async findByNoteId(noteId: string) { + // await this.db + // .sql() + // .selectFrom("content") + // .where("noteId", "==", noteId) + // .execute(); + // } // multi(ids: string[]) { // return this.collection.getItems(ids); // } diff --git a/packages/core/src/collections/note-history.ts b/packages/core/src/collections/note-history.ts index 006f3c37e..0312b44ad 100644 --- a/packages/core/src/collections/note-history.ts +++ b/packages/core/src/collections/note-history.ts @@ -103,7 +103,7 @@ export class NoteHistory implements ICollection { .sql() .selectFrom("notehistory") .where("noteId", "==", noteId) - .orderBy(`dateModified asc`) + .orderBy(`dateModified desc`) .select(["id", "sessionContentId"]) .offset(limit) .limit(10) diff --git a/packages/core/src/collections/session-content.ts b/packages/core/src/collections/session-content.ts index af6924578..78ec482b3 100644 --- a/packages/core/src/collections/session-content.ts +++ b/packages/core/src/collections/session-content.ts @@ -57,7 +57,6 @@ export class SessionContent implements ICollection { // locked || isCipher(content.data) // ? content.data // : await this.db.compressor().compress(content.data); - await this.collection.upsert({ type: "sessioncontent", id: makeSessionContentId(sessionId), diff --git a/packages/core/src/content-types/__tests__/__snapshots__/tiptap.test.js.snap b/packages/core/src/content-types/__tests__/__snapshots__/tiptap.test.js.snap index 5fa67c899..bcbd7fd3b 100644 --- a/packages/core/src/content-types/__tests__/__snapshots__/tiptap.test.js.snap +++ b/packages/core/src/content-types/__tests__/__snapshots__/tiptap.test.js.snap @@ -9,7 +9,7 @@ hello `; exports[`convert HTML to markdown with codeblocks > html-to-md-codeblocks.md 1`] = ` -"Typescript is one of those languages that appear to be very simple. It's often described as \\"Javascript with types\\" and it fits that name very well. However, what many don't realize starting out with Typescript is that Typescript*is *a language and like all other languages it has it's own \\"secrets\\", it's own set of quirks. +"Typescript is one of those languages that appear to be very simple. It's often described as "Javascript with types" and it fits that name very well. However, what many don't realize starting out with Typescript is that Typescript*is *a language and like all other languages it has it's own "secrets", it's own set of quirks. When I started out with Typescript a few years back, I absolutely hated it. It was unnecessary, a box of clutter, making me write code that would never actually run. I hated defining interfaces, typing out all my functions, and thinking in terms I was not used to as a Javascript developer. Before Javascript, I had coded in C# and I had never really liked C# (I still don't) mostly for the huge amounts of boilerplate and magic involved. Typescript is heavily inspired by C# and seeing that contaminate the Javascript ecosystem irked me no end. @@ -26,12 +26,12 @@ Think of a container that can take any type of item as long as it is not a circl \`\`\`javascript var container = []; function putIntoContainer(item) { - if (item.type === \\"round\\") throw new Error(\\"Rounded items not supported.\\") + if (item.type === "round") throw new Error("Rounded items not supported.") container.push(item); } -var square = {type: \\"square\\"} -var circle = {type: \\"round\\"} +var square = {type: "square"} +var circle = {type: "round"} putIntoContainer(square) putIntoContainer(circle) // ERROR! Rounded items not supported! \`\`\` @@ -46,11 +46,11 @@ In Typescript, this will be solved much more succinctly: // than square or round. // This gives us nice auto completion and safety // against typos. -type ItemTypes = \\"square\\" | \\"round\\"; +type ItemTypes = "square" | "round"; // Define a generic item that can be of any type // defined in ItemTypes. -// i.e. Item<\\"triangle\\"> will give an error. +// i.e. Item<"triangle"> will give an error. type Item = { type?: TItemType; width: number; @@ -58,15 +58,15 @@ type Item = { }; // This is just syntax sugar to increase readability. -type Square = Item<\\"square\\">; -type Circle = Item<\\"round\\">; +type Square = Item<"square">; +type Circle = Item<"round">; // Our container is just a simple wrapper around an array // that accepts items of only a specific type. type Container = Array>; -var squareContainer: Container<\\"square\\"> = []; -var roundContainer: Container<\\"round\\"> = []; +var squareContainer: Container<"square"> = []; +var roundContainer: Container<"round"> = []; // This wrapper is unnecessary, of course, because array.push // already does this. Only for demonstration purposes. @@ -82,8 +82,8 @@ var circle: Circle = { width: 200, height: 500 }; putIntoContainer(squareContainer, square); putIntoContainer(roundContainer, circle); -putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<\\"round\\">'. -putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<\\"square\\">'. +putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<"round">'. +putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<"square">'. \`\`\` A lot more code, I know, and if you don't know how generics work that blob of code is utter nonsense. One of the main reasons I avoided Typescript for a long time. But look at the benefits: @@ -113,15 +113,15 @@ putIntoContainer(roundContainer, circle); This is the power of generics. More specifically, this is Typescript generics at a glance. -But this post was supposed to be about the \\"Secrets\\" of Typescript Generics, right? Well, let's get into that. +But this post was supposed to be about the "Secrets" of Typescript Generics, right? Well, let's get into that. -## 1\\\\. Type filters using ternary operators +## 1\\. Type filters using ternary operators -## 2\\\\. Deeply recursive types +## 2\\. Deeply recursive types -## 3\\\\. Type functions +## 3\\. Type functions -## 4\\\\. Type inference using interface properties +## 4\\. Type inference using interface properties " `; @@ -230,7 +230,7 @@ Nene - dasvsadv -- adsva\\\\\`sd +- adsva\\\`sd - vasd @@ -246,9 +246,9 @@ exports[`convert HTML to text with codeblock2 > html-to-txt-codeblock2.txt 1`] = exports[`convert HTML to text with codeblocks > html-to-txt-codeblocks.txt 1`] = ` "Typescript is one of those languages that appear to be very simple. It's often -described as \\"Javascript with types\\" and it fits that name very well. However, +described as "Javascript with types" and it fits that name very well. However, what many don't realize starting out with Typescript is that Typescript is a -language and like all other languages it has it's own \\"secrets\\", it's own set of +language and like all other languages it has it's own "secrets", it's own set of quirks. When I started out with Typescript a few years back, I absolutely hated it. It @@ -282,12 +282,12 @@ checks & conditions: var container = []; function putIntoContainer(item) { - if (item.type === \\"round\\") throw new Error(\\"Rounded items not supported.\\") + if (item.type === "round") throw new Error("Rounded items not supported.") container.push(item); } -var square = {type: \\"square\\"} -var circle = {type: \\"round\\"} +var square = {type: "square"} +var circle = {type: "round"} putIntoContainer(square) putIntoContainer(circle) // ERROR! Rounded items not supported! @@ -304,11 +304,11 @@ In Typescript, this will be solved much more succinctly: // than square or round. // This gives us nice auto completion and safety // against typos. -type ItemTypes = \\"square\\" | \\"round\\"; +type ItemTypes = "square" | "round"; // Define a generic item that can be of any type // defined in ItemTypes. -// i.e. Item<\\"triangle\\"> will give an error. +// i.e. Item<"triangle"> will give an error. type Item = { type?: TItemType; width: number; @@ -316,15 +316,15 @@ type Item = { }; // This is just syntax sugar to increase readability. -type Square = Item<\\"square\\">; -type Circle = Item<\\"round\\">; +type Square = Item<"square">; +type Circle = Item<"round">; // Our container is just a simple wrapper around an array // that accepts items of only a specific type. type Container = Array>; -var squareContainer: Container<\\"square\\"> = []; -var roundContainer: Container<\\"round\\"> = []; +var squareContainer: Container<"square"> = []; +var roundContainer: Container<"round"> = []; // This wrapper is unnecessary, of course, because array.push // already does this. Only for demonstration purposes. @@ -340,8 +340,8 @@ var circle: Circle = { width: 200, height: 500 }; putIntoContainer(squareContainer, square); putIntoContainer(roundContainer, circle); -putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<\\"round\\">'. -putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<\\"square\\">'. +putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<"round">'. +putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<"square">'. A lot more code, I know, and if you don't know how generics work that blob of @@ -372,7 +372,7 @@ putIntoContainer(roundContainer, circle); This is the power of generics. More specifically, this is Typescript generics at a glance. -But this post was supposed to be about the \\"Secrets\\" of Typescript Generics, +But this post was supposed to be about the "Secrets" of Typescript Generics, right? Well, let's get into that. diff --git a/packages/core/src/database/triggers.ts b/packages/core/src/database/triggers.ts index 1e7f984aa..284dc89b6 100644 --- a/packages/core/src/database/triggers.ts +++ b/packages/core/src/database/triggers.ts @@ -53,7 +53,6 @@ export async function createTriggers(db: Kysely) { c.insertInto("content_fts").values({ content_fts: sql.lit("delete"), id: sql.ref("old.id"), - rowid: sql.ref("old.rowid"), data: sql.ref("old.data"), noteId: sql.ref("old.noteId") }) @@ -70,7 +69,6 @@ export async function createTriggers(db: Kysely) { c.insertInto("content_fts").values({ content_fts: sql.lit("delete"), id: sql.ref("old.id"), - rowid: sql.ref("old.rowid"), data: sql.ref("old.data"), noteId: sql.ref("old.noteId") }) @@ -78,7 +76,7 @@ export async function createTriggers(db: Kysely) { .addQuery((c) => c.insertInto("content_fts").values({ id: sql`new.id`, - data: sql`IIF(new.locked == 1, "", new.data)`, + data: sql`IIF(new.locked == 1, '', new.data)`, noteId: sql`new.noteId` }) ) @@ -114,7 +112,6 @@ export async function createTriggers(db: Kysely) { c.insertInto("notes_fts").values({ notes_fts: sql.lit("delete"), id: sql.ref("old.id"), - rowid: sql.ref("old.rowid"), title: sql.ref("old.title") }) ) @@ -130,7 +127,6 @@ export async function createTriggers(db: Kysely) { c.insertInto("notes_fts").values({ notes_fts: sql.lit("delete"), id: sql.ref("old.id"), - rowid: sql.ref("old.rowid"), title: sql.ref("old.title") }) ) diff --git a/packages/core/src/utils/__tests__/virtualized-grouping.test.ts b/packages/core/src/utils/__tests__/virtualized-grouping.test.ts index a5228128c..1fc943f4a 100644 --- a/packages/core/src/utils/__tests__/virtualized-grouping.test.ts +++ b/packages/core/src/utils/__tests__/virtualized-grouping.test.ts @@ -28,6 +28,7 @@ function createMock() { Object.fromEntries(ids.map((id) => [id, id])) ); } +test.todo("renable virtualized grouping tests"); // test("fetch items in batch if not found in cache", async (t) => { // const mocked = createMock(); // const grouping = new VirtualizedGrouping(