diff --git a/README.md b/README.md index 42ff8fa..343b03f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,22 @@ pnpm test:e2e pnpm test:production-smoke ``` +Publish the current working tree to the permanent Cloudflare Pages development +surface with: + +```sh +pnpm dlx wrangler@4.114.0 login # first use only +pnpm deploy:dev +``` + +This builds a web-only manifest for +, connects it to the hosted staging +Connect service and the isolated connector on `127.0.0.1:28486`, deploys only +the Cloudflare `staging` branch, and verifies the live application. It does not +change the production `app.tasknotes.dev` deployment. Start the matching local +connector from the sibling `mdbase-connect` checkout with +`pnpm dev:desktop:staging`. + To update the native projects, run `pnpm cap:sync`. Android builds require Java 21. On macOS, open the iOS project with `pnpm exec cap open ios`. Contributor documentation: diff --git a/package.json b/package.json index cfa8e8a..acfed66 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "manifest:validate:local": "mdbase-connect-dev validate-manifest public/.well-known/mdbase-app.json --allow-local", "dev": "pnpm manifest:dev && vite", "dev:e2e": "pnpm manifest:dev && vite --mode e2e", + "deploy:dev": "node scripts/deploy-pages-dev.mjs", "build": "pnpm manifest && pnpm manifest:validate && tsc -b && vite build && node scripts/write-static-routes.mjs", "build:e2e": "pnpm manifest && pnpm manifest:validate:local && tsc -b && vite build --mode e2e && node scripts/write-static-routes.mjs", "preview": "vite preview", @@ -50,9 +51,9 @@ "@fullcalendar/list": "6.1.17", "@fullcalendar/react": "6.1.17", "@fullcalendar/timegrid": "6.1.17", - "@mdbase-dev/connect": "file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz", - "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz", - "@tasknotes/model": "file:vendor/tasknotes-model-0.3.0-rc.10.tgz", + "@mdbase-dev/connect": "file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz", + "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz", + "@tasknotes/model": "file:vendor/tasknotes-model-0.3.0-rc.11.tgz", "dexie": "4.4.4", "firebase": "12.16.0", "lucide-react": "1.25.0", @@ -68,13 +69,13 @@ "devDependencies": { "@axe-core/playwright": "^4.12.1", "@callumalpass/mdbase": "file:vendor/callumalpass-mdbase-0.3.0-rc.5-3c9c89b8000b.tgz", - "@mdbase-dev/connect-dev": "file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz", - "@mdbase-dev/connect-testing": "file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz", "@capacitor/android": "8.4.2", "@capacitor/assets": "3.0.5", "@capacitor/cli": "8.4.2", "@capacitor/ios": "8.4.2", "@eslint/js": "10.0.1", + "@mdbase-dev/connect-dev": "file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz", + "@mdbase-dev/connect-testing": "file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz", "@playwright/test": "1.61.1", "@testing-library/jest-dom": "7.0.0", "@testing-library/react": "16.3.2", @@ -98,8 +99,9 @@ }, "pnpm": { "overrides": { - "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz", - "@mdbase-dev/connect": "file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz" - } + "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz", + "@mdbase-dev/connect": "file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz" + }, + "patchedDependencies": {} } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deec8be..9421ebb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,8 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: - '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz - '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz + '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz + '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz importers: @@ -55,14 +55,14 @@ importers: specifier: 6.1.17 version: 6.1.17(@fullcalendar/core@6.1.17) '@mdbase-dev/connect': - specifier: file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz - version: file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz + specifier: file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz + version: file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz '@mdbase-dev/connect-protocol': - specifier: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz - version: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz + specifier: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz + version: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz '@tasknotes/model': - specifier: file:vendor/tasknotes-model-0.3.0-rc.10.tgz - version: file:vendor/tasknotes-model-0.3.0-rc.10.tgz + specifier: file:vendor/tasknotes-model-0.3.0-rc.11.tgz + version: file:vendor/tasknotes-model-0.3.0-rc.11.tgz dexie: specifier: 4.4.4 version: 4.4.4 @@ -119,11 +119,11 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.7.0) '@mdbase-dev/connect-dev': - specifier: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz - version: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz + specifier: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz + version: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz '@mdbase-dev/connect-testing': - specifier: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz - version: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz + specifier: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz + version: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz '@playwright/test': specifier: 1.61.1 version: 1.61.1 @@ -814,22 +814,22 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@mdbase-dev/connect-dev@file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz': - resolution: {integrity: sha512-RmbNbPyQz9goFGew4ooz26Juco2vfcfQCxYM+uvL5WX9yimMDXGG6UjG3H4YYqT8nje5VR7JytKO7D60HfYZcw==, tarball: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz} - version: 0.1.0-beta.45 + '@mdbase-dev/connect-dev@file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz': + resolution: {integrity: sha512-Jm5HtXsQnnXZDCUehqq4mNmXwlsTsE9JN45fupns5w1pp1T9XTk3byWU2NIytA93ccUhaETj6PHxVIstd/CPkA==, tarball: file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz} + version: 0.1.0-beta.56 hasBin: true - '@mdbase-dev/connect-protocol@file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz': - resolution: {integrity: sha512-AeDvKoYN1h2DCMD4E1xOJrbjWccM4K2ZTNHAczEiJRdeF3jRr4EWX878WYZ1yq4zqBL+fEih0ufQo4kDRuYeww==, tarball: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz} - version: 0.1.0-beta.45 + '@mdbase-dev/connect-protocol@file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz': + resolution: {integrity: sha512-nOsNfZvMPbaLrqd+GprUcujkSAc4TSiL00sY4KtAQAn+I/MIQCPihGJ6Hrzt4OqyvtY54mvRYfb1EukZaQUDcw==, tarball: file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz} + version: 0.1.0-beta.56 - '@mdbase-dev/connect-testing@file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz': - resolution: {integrity: sha512-kWr5TTWmeAlROB8YuvursiwwB5epYCa20ZeZQMmGsQX7fKDsbR6w+kvVFLZIGmTPETFCZZcdKTA8RlsaBJVk7Q==, tarball: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz} - version: 0.1.0-beta.45 + '@mdbase-dev/connect-testing@file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz': + resolution: {integrity: sha512-XkSVa97IFF9chR+5BRgIuYxFOvOuM0eImVnkwqSeQMSZRB7MO0FC70jB0atsnO3YwA5Ek9EOww5+vrFQ/DvSaQ==, tarball: file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz} + version: 0.1.0-beta.56 - '@mdbase-dev/connect@file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz': - resolution: {integrity: sha512-NY9rfZFWJShMWxXaf9pIcfyHAy7l3HWuYoHl2CENxY8gebNLxUouniOTFBTY1OcKnZvuXgHzP5H9uxFqnUu8fA==, tarball: file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz} - version: 0.1.0-beta.45 + '@mdbase-dev/connect@file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz': + resolution: {integrity: sha512-SuX0KINk3M8/YAuF30HQ6i3YcBXnWW6kFEz0Cdd0e92O0/ZiJz6QKAhstTlcG7sRNu+E1hUtVkqPTXcVRu+yyQ==, tarball: file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz} + version: 0.1.0-beta.56 '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} @@ -974,9 +974,9 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@tasknotes/model@file:vendor/tasknotes-model-0.3.0-rc.10.tgz': - resolution: {integrity: sha512-R/InZeouGfpRFMZlbd2+rD9tyLi/BB+q4Jjgg+GoEVXPEmIcHLo+zMxkYF0cY4M3TUlhYO5QBLeTUB1usa8xbA==, tarball: file:vendor/tasknotes-model-0.3.0-rc.10.tgz} - version: 0.3.0-rc.10 + '@tasknotes/model@file:vendor/tasknotes-model-0.3.0-rc.11.tgz': + resolution: {integrity: sha512-67Sb9vqmhfXf2wXo7956w4JijzUyMYNngQ8xjjpHl6m02hVnv8H2N9iICaHS4191Ym6pEARuOUO9LYa4NKcB8w==, tarball: file:vendor/tasknotes-model-0.3.0-rc.11.tgz} + version: 0.3.0-rc.11 '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -4564,26 +4564,26 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@mdbase-dev/connect-dev@file:vendor/mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz': + '@mdbase-dev/connect-dev@file:vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz': dependencies: - '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz - '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz + '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz + '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz ajv: 8.20.0 yaml: 2.9.0 - '@mdbase-dev/connect-protocol@file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz': + '@mdbase-dev/connect-protocol@file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) - '@mdbase-dev/connect-testing@file:vendor/mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz': + '@mdbase-dev/connect-testing@file:vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz': dependencies: - '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz - '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz + '@mdbase-dev/connect': file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz + '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz - '@mdbase-dev/connect@file:vendor/mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz': + '@mdbase-dev/connect@file:vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz': dependencies: - '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz + '@mdbase-dev/connect-protocol': file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: @@ -4679,7 +4679,7 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@tasknotes/model@file:vendor/tasknotes-model-0.3.0-rc.10.tgz': + '@tasknotes/model@file:vendor/tasknotes-model-0.3.0-rc.11.tgz': dependencies: rrule: 2.8.1 yaml: 2.9.0 diff --git a/scripts/deploy-pages-dev.mjs b/scripts/deploy-pages-dev.mjs new file mode 100644 index 0000000..e7354fc --- /dev/null +++ b/scripts/deploy-pages-dev.mjs @@ -0,0 +1,151 @@ +import { spawn } from "node:child_process"; +import { readFile, readdir, unlink, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +export const developmentDeployment = Object.freeze({ + appOrigin: "https://staging.tasknotes-app.pages.dev", + connectOrigin: "https://connect-staging.mdbase.dev", + loopbackOrigin: "http://127.0.0.1:28486", + project: "tasknotes-app", + branch: "staging", + wranglerVersion: "4.114.0", +}); + +const projectRoot = resolve(import.meta.dirname, ".."); +const manifestTargets = [ + resolve(projectRoot, "public", ".well-known", "mdbase-app.json"), + resolve(projectRoot, "src", "generated", "mdbase-app.json"), +]; +const pnpm = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; + +if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { + await deployDevelopmentTaskNotes(process.env); +} + +export function developmentDeploymentEnvironment(environment) { + return { + ...environment, + VITE_BASE_PATH: "/", + TASKNOTES_APP_URL: developmentDeployment.appOrigin, + TASKNOTES_WEB_ONLY: "1", + TASKNOTES_FIREBASE_PROJECT_ID: "", + VITE_MDBASE_CONNECT_URL: developmentDeployment.connectOrigin, + VITE_MDBASE_CONNECT_LOOPBACK_URL: developmentDeployment.loopbackOrigin, + }; +} + +export async function deployDevelopmentTaskNotes( + environment, + dependencies = { run: runCommand, verifyBuild: verifyDevelopmentBuild }, +) { + const deploymentEnvironment = developmentDeploymentEnvironment(environment); + const previousManifests = await Promise.all( + manifestTargets.map(async (target) => { + try { + return await readFile(target); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + }), + ); + + try { + await dependencies.run(pnpm, ["build"], deploymentEnvironment); + await dependencies.verifyBuild(); + } finally { + await Promise.all( + manifestTargets.map((target, index) => + previousManifests[index] === null + ? unlink(target).catch((error) => { + if (error.code !== "ENOENT") throw error; + }) + : writeFile(target, previousManifests[index]), + ), + ); + } + + await dependencies.run( + pnpm, + [ + "dlx", + `wrangler@${developmentDeployment.wranglerVersion}`, + "pages", + "deploy", + "dist", + `--project-name=${developmentDeployment.project}`, + `--branch=${developmentDeployment.branch}`, + "--commit-dirty=true", + ], + deploymentEnvironment, + ); + await dependencies.run("node", ["scripts/production-smoke.mjs"], { + ...deploymentEnvironment, + TASKNOTES_PRODUCTION_URL: developmentDeployment.appOrigin, + MDBASE_CONNECT_ORIGIN: developmentDeployment.connectOrigin, + }); + + console.log( + `Development TaskNotes deployed: ${developmentDeployment.appOrigin}/`, + ); +} + +async function verifyDevelopmentBuild() { + const manifestPath = resolve( + projectRoot, + "dist", + ".well-known", + "mdbase-app.json", + ); + const manifest = JSON.parse(await readFile(manifestPath, "utf8")); + const callback = `${developmentDeployment.appOrigin}/auth/mdbase/callback`; + if ( + manifest.homepage !== `${developmentDeployment.appOrigin}/` || + manifest.icon !== `${developmentDeployment.appOrigin}/icon.png` || + manifest.redirect_uris?.length !== 1 || + manifest.redirect_uris[0] !== callback + ) { + throw new Error( + `TaskNotes deployment manifest does not declare ${developmentDeployment.appOrigin}.`, + ); + } + + const assetsDirectory = resolve(projectRoot, "dist", "assets"); + const scripts = (await readdir(assetsDirectory)) + .filter((file) => file.endsWith(".js")) + .map((file) => resolve(assetsDirectory, file)); + const sources = await Promise.all( + scripts.map((script) => readFile(script, "utf8")), + ); + for (const expected of [ + developmentDeployment.connectOrigin, + developmentDeployment.loopbackOrigin, + ]) { + if (!sources.some((source) => source.includes(expected))) { + throw new Error( + `TaskNotes deployment bundle does not contain ${expected}.`, + ); + } + } +} + +async function runCommand(command, arguments_, environment) { + const child = spawn(command, arguments_, { + cwd: projectRoot, + env: environment, + stdio: "inherit", + }); + const exitCode = await new Promise((resolveExit, rejectExit) => { + child.once("error", rejectExit); + child.once("exit", (code, signal) => { + if (signal) rejectExit(new Error(`${command} was stopped by ${signal}.`)); + else resolveExit(code); + }); + }); + if (exitCode !== 0) { + throw new Error( + `${command} ${arguments_.join(" ")} exited with code ${exitCode}.`, + ); + } +} diff --git a/scripts/deploy-pages-dev.test.mjs b/scripts/deploy-pages-dev.test.mjs new file mode 100644 index 0000000..6db6fee --- /dev/null +++ b/scripts/deploy-pages-dev.test.mjs @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; + +import { + deployDevelopmentTaskNotes, + developmentDeployment, + developmentDeploymentEnvironment, +} from "./deploy-pages-dev.mjs"; + +describe("TaskNotes development deployment", () => { + it("builds the staging Pages origin against staging Connect and its daemon", () => { + const environment = developmentDeploymentEnvironment({ EXISTING: "kept" }); + + expect(environment).toMatchObject({ + EXISTING: "kept", + VITE_BASE_PATH: "/", + TASKNOTES_APP_URL: "https://staging.tasknotes-app.pages.dev", + TASKNOTES_WEB_ONLY: "1", + TASKNOTES_FIREBASE_PROJECT_ID: "", + VITE_MDBASE_CONNECT_URL: "https://connect-staging.mdbase.dev", + VITE_MDBASE_CONNECT_LOOPBACK_URL: "http://127.0.0.1:28486", + }); + }); + + it("deploys only the staging branch and smokes the same origins", async () => { + const run = vi.fn(async () => undefined); + const verifyBuild = vi.fn(async () => undefined); + + await deployDevelopmentTaskNotes({}, { run, verifyBuild }); + + expect(verifyBuild).toHaveBeenCalledOnce(); + expect(run).toHaveBeenCalledTimes(3); + expect(run.mock.calls[0][1]).toEqual(["build"]); + expect(run.mock.calls[1][1]).toEqual( + expect.arrayContaining([ + `wrangler@${developmentDeployment.wranglerVersion}`, + "pages", + "deploy", + "dist", + "--project-name=tasknotes-app", + "--branch=staging", + ]), + ); + expect(run.mock.calls[2]).toEqual([ + "node", + ["scripts/production-smoke.mjs"], + expect.objectContaining({ + TASKNOTES_PRODUCTION_URL: developmentDeployment.appOrigin, + MDBASE_CONNECT_ORIGIN: developmentDeployment.connectOrigin, + }), + ]); + }); +}); diff --git a/src/domain/tasknotes-model.test.ts b/src/domain/tasknotes-model.test.ts index 9393d16..742022f 100644 --- a/src/domain/tasknotes-model.test.ts +++ b/src/domain/tasknotes-model.test.ts @@ -53,6 +53,28 @@ describe("TaskNotes task model app boundary", () => { expect(updated.frontmatter.status).toBe("doing"); }); + it("removes the completion date when a completed task is reopened", () => { + const created = model.create( + { title: "Reopen me", status: "todo" }, + { id: "reopen", now: "2026-08-10T01:00:00.000Z" }, + ); + const completed = model.update( + created, + { status: "done" }, + { now: "2026-08-10T01:01:00.000Z" }, + ); + const reopened = model.update( + completed, + { status: "todo" }, + { now: "2026-08-10T01:02:00.000Z" }, + ); + + expect(completed.completedDate).toBe("2026-08-10"); + expect(completed.frontmatter.completedDate).toBe("2026-08-10"); + expect(reopened.completedDate).toBeUndefined(); + expect(reopened.frontmatter).not.toHaveProperty("completedDate"); + }); + it("applies custom defaults and removes cleared custom properties", () => { const created = model.create( { title: "Configured", customProperties: { client: "Acme" } }, diff --git a/vendor/README.md b/vendor/README.md index fbf8ec9..8f90af3 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -8,7 +8,7 @@ their source commit, byte size, and SHA-512 digest. Current snapshots were packed from sibling checkouts after their full test suites passed: -- `../tasknotes-model` (`0.3.0-rc.10`) +- `../tasknotes-model` (`0.3.0-rc.11`) - `../tasknotes-nlp-core` (`62a0d9d`, including wikilink-safe parsing) - `../tasknotes-spec` - `../mdbase` diff --git a/vendor/mdbase-connect-sdk.json b/vendor/mdbase-connect-sdk.json index 44a2408..c65ddf9 100644 --- a/vendor/mdbase-connect-sdk.json +++ b/vendor/mdbase-connect-sdk.json @@ -1,37 +1,37 @@ { "schema_version": 1, "repository": "https://github.com/mdbase-dev/mdbase-connect", - "revision": "66d87bc4bfafe148f7cfb510f6aac1376471045b", + "revision": "da0294c2ad311c56d8b90a37d44ce686b983d2eb", "artifacts": [ { "package": "connect", - "file": "mdbase-dev-connect-0.1.0-beta.45-66d87bc4bfaf.tgz", - "bytes": 149077, - "sha512": "NY9rfZFWJShMWxXaf9pIcfyHAy7l3HWuYoHl2CENxY8gebNLxUouniOTFBTY1OcKnZvuXgHzP5H9uxFqnUu8fA==" + "file": "mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz", + "bytes": 151111, + "sha512": "SuX0KINk3M8/YAuF30HQ6i3YcBXnWW6kFEz0Cdd0e92O0/ZiJz6QKAhstTlcG7sRNu+E1hUtVkqPTXcVRu+yyQ==" }, { "package": "devkit", - "file": "mdbase-dev-connect-dev-0.1.0-beta.45-66d87bc4bfaf.tgz", + "file": "mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz", "bytes": 9242, - "sha512": "RmbNbPyQz9goFGew4ooz26Juco2vfcfQCxYM+uvL5WX9yimMDXGG6UjG3H4YYqT8nje5VR7JytKO7D60HfYZcw==" + "sha512": "Jm5HtXsQnnXZDCUehqq4mNmXwlsTsE9JN45fupns5w1pp1T9XTk3byWU2NIytA93ccUhaETj6PHxVIstd/CPkA==" }, { "package": "protocol", - "file": "mdbase-dev-connect-protocol-0.1.0-beta.45-66d87bc4bfaf.tgz", - "bytes": 54673, - "sha512": "AeDvKoYN1h2DCMD4E1xOJrbjWccM4K2ZTNHAczEiJRdeF3jRr4EWX878WYZ1yq4zqBL+fEih0ufQo4kDRuYeww==" + "file": "mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz", + "bytes": 54825, + "sha512": "nOsNfZvMPbaLrqd+GprUcujkSAc4TSiL00sY4KtAQAn+I/MIQCPihGJ6Hrzt4OqyvtY54mvRYfb1EukZaQUDcw==" }, { "package": "sync", - "file": "mdbase-dev-connect-sync-0.1.0-beta.45-66d87bc4bfaf.tgz", - "bytes": 97204, - "sha512": "1NxvZ9UfoAE4QLpqPMufRU8MiO47Tr9Lpb9KTNPEA4sYIr3fgReXNnUPchW4qdltJ8ilGG32ex02NcC+X/rpPw==" + "file": "mdbase-dev-connect-sync-0.1.0-beta.56-da0294c2ad31.tgz", + "bytes": 97205, + "sha512": "tMZiUFSEWzNiP9Q9pogbC/13b9i6HhQJB1574qtGoQJhefF134M2dKHANvgqbuYGqrOWehBhJv+eORHEbBz4lg==" }, { "package": "testing", - "file": "mdbase-dev-connect-testing-0.1.0-beta.45-66d87bc4bfaf.tgz", - "bytes": 6801, - "sha512": "kWr5TTWmeAlROB8YuvursiwwB5epYCa20ZeZQMmGsQX7fKDsbR6w+kvVFLZIGmTPETFCZZcdKTA8RlsaBJVk7Q==" + "file": "mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz", + "bytes": 6800, + "sha512": "XkSVa97IFF9chR+5BRgIuYxFOvOuM0eImVnkwqSeQMSZRB7MO0FC70jB0atsnO3YwA5Ek9EOww5+vrFQ/DvSaQ==" } ] } diff --git a/vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz b/vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz new file mode 100644 index 0000000..45af22f Binary files /dev/null and b/vendor/mdbase-dev-connect-0.1.0-beta.56-da0294c2ad31.tgz differ diff --git a/vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz b/vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz new file mode 100644 index 0000000..0f8a054 Binary files /dev/null and b/vendor/mdbase-dev-connect-dev-0.1.0-beta.56-da0294c2ad31.tgz differ diff --git a/vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz b/vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz new file mode 100644 index 0000000..be4d03c Binary files /dev/null and b/vendor/mdbase-dev-connect-protocol-0.1.0-beta.56-da0294c2ad31.tgz differ diff --git a/vendor/mdbase-dev-connect-sync-0.1.0-beta.56-da0294c2ad31.tgz b/vendor/mdbase-dev-connect-sync-0.1.0-beta.56-da0294c2ad31.tgz new file mode 100644 index 0000000..65e58f5 Binary files /dev/null and b/vendor/mdbase-dev-connect-sync-0.1.0-beta.56-da0294c2ad31.tgz differ diff --git a/vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz b/vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz new file mode 100644 index 0000000..afb44e7 Binary files /dev/null and b/vendor/mdbase-dev-connect-testing-0.1.0-beta.56-da0294c2ad31.tgz differ diff --git a/vendor/tasknotes-model-0.3.0-rc.11.tgz b/vendor/tasknotes-model-0.3.0-rc.11.tgz new file mode 100644 index 0000000..696d6d7 Binary files /dev/null and b/vendor/tasknotes-model-0.3.0-rc.11.tgz differ