From 0cf13ca559d6f1a693dcadc182e459174017aac2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 02:27:09 +0000 Subject: [PATCH 1/7] fix(deps): update dependency mind-elixir to v4.3.6 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 78f8e3bef..734d7a998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,7 @@ "marked": "15.0.6", "mermaid": "11.4.1", "mime-types": "2.1.35", - "mind-elixir": "4.3.5", + "mind-elixir": "4.3.6", "multer": "1.4.5-lts.1", "normalize-strings": "1.1.1", "normalize.css": "8.0.1", @@ -12866,9 +12866,9 @@ } }, "node_modules/mind-elixir": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.5.tgz", - "integrity": "sha512-I1Mxc/jCwHEDMecDjQVpc+WShmzrEnIv6+MnWPauJ0LAiOXMBQB/wpKqlF4bTp+kCqzOHMYryAvIWm0jvloZ8Q==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.6.tgz", + "integrity": "sha512-6E9DT5vOYJ7DMDFXJlAnKU3Q6ekwBkR48Tjo6PchEcxJjPURJsiIASxtIeZCfvp8V39N4WyIa3Yt7Q/SFQkVfw==", "license": "MIT" }, "node_modules/minimalistic-assert": { diff --git a/package.json b/package.json index 2f056ed91..d078620b9 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "marked": "15.0.6", "mermaid": "11.4.1", "mime-types": "2.1.35", - "mind-elixir": "4.3.5", + "mind-elixir": "4.3.6", "multer": "1.4.5-lts.1", "normalize-strings": "1.1.1", "normalize.css": "8.0.1", From 353156e62577b54a15ef46de393f9709314e5772 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 18:47:42 +0200 Subject: [PATCH 2/7] fix(mindmap): not working due to dependency change --- src/public/app/services/library_loader.ts | 5 ----- src/public/app/widgets/type_widgets/mind_map.js | 9 +++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/public/app/services/library_loader.ts b/src/public/app/services/library_loader.ts index a67bb934a..697b20a65 100644 --- a/src/public/app/services/library_loader.ts +++ b/src/public/app/services/library_loader.ts @@ -96,10 +96,6 @@ const I18NEXT: Library = { js: ["node_modules/i18next/i18next.min.js", "node_modules/i18next-http-backend/i18nextHttpBackend.min.js"] }; -const MIND_ELIXIR: Library = { - js: ["node_modules/mind-elixir/dist/MindElixir.iife.js", "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs"] -}; - const HIGHLIGHT_JS: Library = { js: () => { const mimeTypes = mimeTypesService.getMimeTypes(); @@ -219,6 +215,5 @@ export default { EXCALIDRAW, MARKJS, I18NEXT, - MIND_ELIXIR, HIGHLIGHT_JS }; diff --git a/src/public/app/widgets/type_widgets/mind_map.js b/src/public/app/widgets/type_widgets/mind_map.js index 551a108d6..fafe99b18 100644 --- a/src/public/app/widgets/type_widgets/mind_map.js +++ b/src/public/app/widgets/type_widgets/mind_map.js @@ -1,6 +1,7 @@ -import libraryLoader from "../../services/library_loader.js"; import TypeWidget from "./type_widget.js"; import utils from "../../services/utils.js"; +import MindElixir from "mind-elixir"; +import nodeMenu from "@mind-elixir/node-menu"; const TPL = `
@@ -169,10 +170,6 @@ export default class MindMapWidget extends TypeWidget { return; } - if (!window.MindElixir) { - await libraryLoader.requireLibrary(libraryLoader.MIND_ELIXIR); - } - this.#initLibrary(); await this.#loadData(note); } @@ -194,7 +191,7 @@ export default class MindMapWidget extends TypeWidget { el: this.$content[0], direction: MindElixir.LEFT }); - mind.install(window["@mind-elixir/node-menu"]); + mind.install(nodeMenu); this.mind = mind; mind.init(MindElixir.new()); From e16f4a1a715204ea99ac01c958708b2043cb00cf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 19:12:29 +0200 Subject: [PATCH 3/7] chore(client/ts): port type_widget --- src/public/app/components/app_context.ts | 2 +- src/public/app/components/component.ts | 8 +++---- .../{type_widget.js => type_widget.ts} | 21 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) rename src/public/app/widgets/type_widgets/{type_widget.js => type_widget.ts} (64%) diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index 15e9b8910..54041b3d0 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -282,7 +282,7 @@ type CommandAndEventMappings = CommandMappings & EventMappings; * This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}. */ export type CommandNames = keyof CommandMappings; -type EventNames = keyof EventMappings; +export type EventNames = keyof EventMappings; type FilterByValueType = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T]; diff --git a/src/public/app/components/component.ts b/src/public/app/components/component.ts index 569b5d433..64a9597dc 100644 --- a/src/public/app/components/component.ts +++ b/src/public/app/components/component.ts @@ -1,5 +1,5 @@ import utils from "../services/utils.js"; -import type { CommandMappings, CommandNames } from "./app_context.js"; +import type { CommandMappings, CommandNames, EventData, EventNames } from "./app_context.js"; /** * Abstract class for all components in the Trilium's frontend. @@ -65,11 +65,11 @@ export class TypedComponent> { return this.parent?.triggerEvent(name, data); } - handleEventInChildren(name: string, data: unknown = {}) { - const promises = []; + handleEventInChildren(name: T, data: EventData): Promise | null { + const promises: Promise[] = []; for (const child of this.children) { - const ret = child.handleEvent(name, data); + const ret = child.handleEvent(name, data) as Promise; if (ret) { promises.push(ret); diff --git a/src/public/app/widgets/type_widgets/type_widget.js b/src/public/app/widgets/type_widgets/type_widget.ts similarity index 64% rename from src/public/app/widgets/type_widgets/type_widget.js rename to src/public/app/widgets/type_widgets/type_widget.ts index 3591c7821..50afb3c61 100644 --- a/src/public/app/widgets/type_widgets/type_widget.js +++ b/src/public/app/widgets/type_widgets/type_widget.ts @@ -1,7 +1,9 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js"; -import appContext from "../../components/app_context.js"; +import appContext, { type EventData, type EventNames } from "../../components/app_context.js"; +import type FNote from "../../entities/fnote.js"; +import type NoteDetailWidget from "../note_detail.js"; -export default class TypeWidget extends NoteContextAwareWidget { +export default abstract class TypeWidget extends NoteContextAwareWidget { // for overriding static getType() {} @@ -11,12 +13,11 @@ export default class TypeWidget extends NoteContextAwareWidget { return super.doRender(); } - /** @param {FNote} note */ - async doRefresh(note) {} + abstract doRefresh(note: FNote | null | undefined): Promise; async refresh() { - const thisWidgetType = this.constructor.getType(); - const noteWidgetType = await this.parent.getWidgetType(); + const thisWidgetType = (this.constructor as any).getType(); + const noteWidgetType = await (this.parent as NoteDetailWidget).getWidgetType(); if (thisWidgetType !== noteWidgetType) { this.toggleInt(false); @@ -27,7 +28,7 @@ export default class TypeWidget extends NoteContextAwareWidget { await this.doRefresh(this.note); - this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext.ntxId }); + this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext?.ntxId }); } } @@ -40,7 +41,7 @@ export default class TypeWidget extends NoteContextAwareWidget { focus() {} - async readOnlyTemporarilyDisabledEvent({ noteContext }) { + async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) { if (this.isNoteContext(noteContext.ntxId)) { await this.refresh(); @@ -49,10 +50,10 @@ export default class TypeWidget extends NoteContextAwareWidget { } // events should be propagated manually to the children widgets - handleEventInChildren(name, data) { + handleEventInChildren(name: T, data: EventData) { if (["activeContextChanged", "setNoteContext"].includes(name)) { // won't trigger .refresh(); - return super.handleEventInChildren("setNoteContext", data); + return super.handleEventInChildren("setNoteContext", data as EventData<"activeContextChanged">); } else if (name === "entitiesReloaded") { return super.handleEventInChildren(name, data); } else { From 580bebb4a34a78be802629747b026620321ad8cd Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 19:18:44 +0200 Subject: [PATCH 4/7] chore(client/ts): port mind_map --- src/public/app/components/app_context.ts | 3 ++ src/public/app/entities/fnote.ts | 2 +- src/public/app/types-lib.d.ts | 4 +++ .../type_widgets/{mind_map.js => mind_map.ts} | 36 ++++++++++++------- .../app/widgets/type_widgets/type_widget.ts | 4 +++ 5 files changed, 36 insertions(+), 13 deletions(-) rename src/public/app/widgets/type_widgets/{mind_map.js => mind_map.ts} (85%) diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index 54041b3d0..ac48070d0 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -262,6 +262,9 @@ type EventMappings = { }; noteContextRemovedEvent: { ntxIds: string[]; + }; + exportSvg: { + ntxId: string; } }; diff --git a/src/public/app/entities/fnote.ts b/src/public/app/entities/fnote.ts index 9759f8b07..9dea5071e 100644 --- a/src/public/app/entities/fnote.ts +++ b/src/public/app/entities/fnote.ts @@ -35,7 +35,7 @@ const NOTE_TYPE_ICONS = { * end user. Those types should be used only for checking against, they are * not for direct use. */ -type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code"; +type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap"; interface NotePathRecord { isArchived: boolean; diff --git a/src/public/app/types-lib.d.ts b/src/public/app/types-lib.d.ts index f91e4d553..d84101b80 100644 --- a/src/public/app/types-lib.d.ts +++ b/src/public/app/types-lib.d.ts @@ -20,3 +20,7 @@ declare module "draggabilly" { destroy(); } } + +declare module '@mind-elixir/node-menu' { + export default mindmap; +} diff --git a/src/public/app/widgets/type_widgets/mind_map.js b/src/public/app/widgets/type_widgets/mind_map.ts similarity index 85% rename from src/public/app/widgets/type_widgets/mind_map.js rename to src/public/app/widgets/type_widgets/mind_map.ts index fafe99b18..3bd72836a 100644 --- a/src/public/app/widgets/type_widgets/mind_map.js +++ b/src/public/app/widgets/type_widgets/mind_map.ts @@ -1,7 +1,11 @@ import TypeWidget from "./type_widget.js"; import utils from "../../services/utils.js"; -import MindElixir from "mind-elixir"; +import MindElixir, { type MindElixirCtor } from "mind-elixir"; import nodeMenu from "@mind-elixir/node-menu"; +import type FNote from "../../entities/fnote.js"; +import type { EventData } from "../../components/app_context.js"; + +const NEW_TOPIC_NAME = ""; const TPL = `
@@ -138,6 +142,11 @@ const TPL = ` `; export default class MindMapWidget extends TypeWidget { + + private $content!: JQuery; + private triggeredByUserOperation?: boolean; + private mind?: ReturnType; + static getType() { return "mindMap"; } @@ -164,7 +173,7 @@ export default class MindMapWidget extends TypeWidget { super.doRender(); } - async doRefresh(note) { + async doRefresh(note: FNote) { if (this.triggeredByUserOperation) { this.triggeredByUserOperation = false; return; @@ -178,12 +187,14 @@ export default class MindMapWidget extends TypeWidget { this.triggeredByUserOperation = false; } - async #loadData(note) { + async #loadData(note: FNote) { const blob = await note.getBlob(); - const content = blob.getJsonContent() || MindElixir.new(); + const content = blob?.getJsonContent() || MindElixir.new(NEW_TOPIC_NAME); - this.mind.refresh(content); - this.mind.toCenter(); + if (this.mind) { + this.mind.refresh(content); + this.mind.toCenter(); + } } #initLibrary() { @@ -194,8 +205,9 @@ export default class MindMapWidget extends TypeWidget { mind.install(nodeMenu); this.mind = mind; - mind.init(MindElixir.new()); - mind.bus.addListener("operation", (operation) => { + mind.init(MindElixir.new(NEW_TOPIC_NAME)); + // TODO: See why the typeof mindmap is not correct. + mind.bus.addListener("operation", (operation: { name: string }) => { this.triggeredByUserOperation = true; if (operation.name !== "beginEdit") { this.spacedUpdate.scheduleUpdate(); @@ -234,14 +246,14 @@ export default class MindMapWidget extends TypeWidget { return await this.mind.exportSvg().text(); } - async entitiesReloadedEvent({ loadResults }) { - if (loadResults.isNoteReloaded(this.noteId)) { + async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded"> ) { + if (this.noteId && loadResults.isNoteReloaded(this.noteId)) { this.refresh(); } } - async exportSvgEvent({ ntxId }) { - if (!this.isNoteContext(ntxId) || this.note.type !== "mindMap") { + async exportSvgEvent({ ntxId }: EventData<"exportSvg">) { + if (!this.isNoteContext(ntxId) || this.note?.type !== "mindMap") { return; } diff --git a/src/public/app/widgets/type_widgets/type_widget.ts b/src/public/app/widgets/type_widgets/type_widget.ts index 50afb3c61..eab836b2b 100644 --- a/src/public/app/widgets/type_widgets/type_widget.ts +++ b/src/public/app/widgets/type_widgets/type_widget.ts @@ -2,8 +2,12 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js"; import appContext, { type EventData, type EventNames } from "../../components/app_context.js"; import type FNote from "../../entities/fnote.js"; import type NoteDetailWidget from "../note_detail.js"; +import type SpacedUpdate from "../../services/spaced_update.js"; export default abstract class TypeWidget extends NoteContextAwareWidget { + + protected spacedUpdate!: SpacedUpdate; + // for overriding static getType() {} From 331b2252f20cd8ba15bef2f681769556315f7464 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 19:19:46 +0200 Subject: [PATCH 5/7] chore(e2e): rename incorrect test suite --- e2e/note_types/{mermaid.spec.ts => mindmap.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename e2e/note_types/{mermaid.spec.ts => mindmap.ts} (100%) diff --git a/e2e/note_types/mermaid.spec.ts b/e2e/note_types/mindmap.ts similarity index 100% rename from e2e/note_types/mermaid.spec.ts rename to e2e/note_types/mindmap.ts From 0221039ebecac1fe5b6c063870c4565008b50903 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 20:08:57 +0200 Subject: [PATCH 6/7] fix(client/ts): fix build errors & define command to event bridge --- src/public/app/components/app_context.ts | 13 ++++++------- src/public/app/components/component.ts | 2 +- src/public/app/services/frontend_script_api.ts | 6 +++--- src/public/app/services/protected_session.ts | 4 ++-- .../app/widgets/containers/launcher_container.ts | 2 +- .../app/widgets/containers/right_pane_container.ts | 3 ++- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index ac48070d0..ae416cdd9 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -92,7 +92,7 @@ export type CommandMappings = { filePath: string; }; focusAndSelectTitle: CommandData & { - isNewNote: boolean; + isNewNote?: boolean; }; showPromptDialog: PromptDialogOptions; showInfoDialog: ConfirmWithMessageOptions; @@ -277,15 +277,16 @@ export type CommandListener = { }; export type CommandListenerData = CommandMappings[T]; -export type EventData = EventMappings[T]; type CommandAndEventMappings = CommandMappings & EventMappings; +type EventOnlyNames = keyof EventMappings; +export type EventNames = CommandNames | EventOnlyNames; +export type EventData = CommandAndEventMappings[T]; /** * This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}. */ export type CommandNames = keyof CommandMappings; -export type EventNames = keyof EventMappings; type FilterByValueType = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T]; @@ -378,12 +379,10 @@ class AppContext extends Component { this.child(rootWidget); - this.triggerEvent("initialRenderComplete"); + this.triggerEvent("initialRenderComplete", {}); } - // TODO: Remove ignore once all commands are mapped out. - //@ts-ignore - triggerEvent(name: K, data: CommandAndEventMappings[K] = {}) { + triggerEvent(name: K, data: EventData) { return this.handleEvent(name, data); } diff --git a/src/public/app/components/component.ts b/src/public/app/components/component.ts index 64a9597dc..2db9f96a4 100644 --- a/src/public/app/components/component.ts +++ b/src/public/app/components/component.ts @@ -46,7 +46,7 @@ export class TypedComponent> { return this; } - handleEvent(name: string, data: unknown): Promise | null { + handleEvent(name: T, data: EventData): Promise | null { try { const callMethodPromise = this.initialized ? this.initialized.then(() => this.callMethod((this as any)[`${name}Event`], data)) : this.callMethod((this as any)[`${name}Event`], data); diff --git a/src/public/app/services/frontend_script_api.ts b/src/public/app/services/frontend_script_api.ts index a8160bbff..178b0aa59 100644 --- a/src/public/app/services/frontend_script_api.ts +++ b/src/public/app/services/frontend_script_api.ts @@ -463,7 +463,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig await ws.waitForMaxKnownEntityChangeId(); await appContext.tabManager.getActiveContext().setNote(notePath); - await appContext.triggerEvent("focusAndSelectTitle"); + await appContext.triggerEvent("focusAndSelectTitle", {}); }; this.openTabWithNote = async (notePath, activate) => { @@ -472,7 +472,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig await appContext.tabManager.openTabWithNoteWithHoisting(notePath, { activate }); if (activate) { - await appContext.triggerEvent("focusAndSelectTitle"); + await appContext.triggerEvent("focusAndSelectTitle", {}); } }; @@ -485,7 +485,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath }); if (activate) { - await appContext.triggerEvent("focusAndSelectTitle"); + await appContext.triggerEvent("focusAndSelectTitle", {}); } }; diff --git a/src/public/app/services/protected_session.ts b/src/public/app/services/protected_session.ts index 1bf995d93..fc34a805f 100644 --- a/src/public/app/services/protected_session.ts +++ b/src/public/app/services/protected_session.ts @@ -74,9 +74,9 @@ ws.subscribeToMessages(async (message) => { if (message.type === "protectedSessionLogin") { await reloadData(); - await appContext.triggerEvent("frocaReloaded"); + await appContext.triggerEvent("frocaReloaded", {}); - appContext.triggerEvent("protectedSessionStarted"); + appContext.triggerEvent("protectedSessionStarted", {}); appContext.triggerCommand("closeProtectedSessionPasswordDialog"); diff --git a/src/public/app/widgets/containers/launcher_container.ts b/src/public/app/widgets/containers/launcher_container.ts index 9c5a6eec7..7fa833182 100644 --- a/src/public/app/widgets/containers/launcher_container.ts +++ b/src/public/app/widgets/containers/launcher_container.ts @@ -51,7 +51,7 @@ export default class LauncherContainer extends FlexContainer { this.$widget.empty(); this.renderChildren(); - await this.handleEventInChildren("initialRenderComplete"); + await this.handleEventInChildren("initialRenderComplete", {}); const activeContext = appContext.tabManager.getActiveContext(); diff --git a/src/public/app/widgets/containers/right_pane_container.ts b/src/public/app/widgets/containers/right_pane_container.ts index c7632769b..764fad85d 100644 --- a/src/public/app/widgets/containers/right_pane_container.ts +++ b/src/public/app/widgets/containers/right_pane_container.ts @@ -1,6 +1,7 @@ import FlexContainer from "./flex_container.js"; import splitService from "../../services/resizer.js"; import type RightPanelWidget from "../right_panel_widget.js"; +import type { EventData, EventNames } from "../../components/app_context.js"; export default class RightPaneContainer extends FlexContainer { private rightPaneHidden: boolean; @@ -19,7 +20,7 @@ export default class RightPaneContainer extends FlexContainer return super.isEnabled() && !this.rightPaneHidden && this.children.length > 0 && !!this.children.find((ch) => ch.isEnabled() && ch.canBeShown()); } - handleEventInChildren(name: string, data: unknown) { + handleEventInChildren(name: T, data: EventData): Promise | null { const promise = super.handleEventInChildren(name, data); if (["activeContextChanged", "noteSwitchedAndActivated", "noteSwitched"].includes(name)) { From f4789857617be327ca9ad50d800a60def597f162 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Jan 2025 20:09:16 +0200 Subject: [PATCH 7/7] chore(ci): define relations between dev jobs --- .github/workflows/dev.yml | 40 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 295a259ce..3afb9811c 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -16,9 +16,28 @@ env: TEST_TAG: ${{ github.repository_owner }}/notes:test jobs: + test_dev: + name: Test development + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + + - run: npm ci + + - name: Run the TypeScript build + run: npx tsc build_docker: name: Build Docker image runs-on: ubuntu-latest + needs: + - test_dev steps: - uses: actions/checkout@v4 - name: Set up node & dependencies @@ -36,27 +55,12 @@ jobs: with: context: . cache-from: type=gha - cache-to: type=gha,mode=max - test_dev: - name: Test development - runs-on: ubuntu-latest - steps: - - name: Checkout the repository - uses: actions/checkout@v4 - - - name: Set up node & dependencies - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "npm" - - - run: npm ci - - - name: Run the TypeScript build - run: npx tsc + cache-to: type=gha,mode=max test_docker: name: Check Docker build runs-on: ubuntu-latest + needs: + - build_docker strategy: matrix: include: