- commit
- f7e59b6c3c2a167b518f88d0d6eb89d342ae9175
- parent
- fa038de1d01839b21d1573f2c1c3f9ff70476428
- author
- Dami <dami.etoile@gmail.com>
| M | apps/ | 9 | ++--- |
| M | apps/ | 1 | + |
| M | apps/ | 8 | +++- |
| M | apps/ | 4 | ++ |
| M | apps/ | 3 | ++ |
| M | tools/ | 55 | +++++++++++++++++++++++------- |
@@ -16,11 +16,6 @@ if (Boolean(cloudAppId) !== Boolean(cloudServerUrl)) {
1616 "Cloud mode requires both JAZZ_APP_ID and JAZZ_SERVER_URL; set the missing name or remove the partial pair.",1717 );1818 }19-if (cloudAppId && cloudServerUrl) {20- process.env.VITE_JAZZ_APP_ID = cloudAppId;21- process.env.VITE_JAZZ_SERVER_URL = cloudServerUrl;22-}23-2419 export default defineConfig({2520 output: "static",2621 integrations: [preact({ compat: true, devtools: true })],@@ -35,6 +30,10 @@ export default defineConfig({
3530 server: {3631 fs: { allow: [workspaceRoot] },3732 },33+ define: {34+ __LOFI_JAZZ_APP_ID__: JSON.stringify(cloudAppId ?? ""),35+ __LOFI_JAZZ_SERVER_URL__: JSON.stringify(cloudServerUrl ?? ""),36+ },3837 resolve: {3938 noExternal: ["@astrojs/preact"],4039 },@@ -111,6 +111,7 @@ export class ChecklistStore {
111111 112112 async #settle(mutation: MutationHandle): Promise<void> {113113 const generation = ++this.#writeGeneration;114+ this.#set({ durability: "none", error: null });114115 try {115116 await mutation.wait({ tier: "local" });116117 this.#diagnostics.localWaitCalls += 1;@@ -4,8 +4,12 @@ import type { DbConfig } from "jazz-tools";
44 import { referenceApp } from "../app.ts";55 66 const LOCAL_APP_ID = "00000000-0000-0000-0000-00000000f153";7-export const appId = import.meta.env?.VITE_JAZZ_APP_ID ?? LOCAL_APP_ID;8-export const serverUrl = import.meta.env?.VITE_JAZZ_SERVER_URL;7+const configuredAppId = typeof __LOFI_JAZZ_APP_ID__ === "string" ? __LOFI_JAZZ_APP_ID__ : "";8+const configuredServerUrl = typeof __LOFI_JAZZ_SERVER_URL__ === "string"9+ ? __LOFI_JAZZ_SERVER_URL__10+ : "";11+export const appId = configuredAppId || LOCAL_APP_ID;12+export const serverUrl = configuredServerUrl || undefined;913 1014 export function databaseConfig(secret: string): DbConfig {1115 return {@@ -12,6 +12,8 @@ export type ReferenceProbe = {
1212 update(id: string, text: string): Promise<void>;1313 setCompleted(id: string, completed: boolean): Promise<void>;1414 delete(id: string): Promise<void>;15+ disconnect(): Promise<void>;16+ reconnect(): Promise<void>;1517 recreate(): Promise<void>;1618 shutdown(): Promise<void>;1719 };@@ -30,6 +32,8 @@ if (typeof document !== "undefined") {
3032 setCompleted: async (id, completed) =>3133 await (await getRuntime()).checklist.setCompleted(id, completed),3234 delete: async (id) => await (await getRuntime()).checklist.delete(id),35+ disconnect: async () => await (await getRuntime()).db.disconnect(),36+ reconnect: async () => await (await getRuntime()).db.reconnect(),3337 recreate: async () => {3438 await recreateRuntime();3539 globalThis.dispatchEvent(new Event(runtimeRecreatedEvent));@@ -8,3 +8,6 @@ interface ImportMetaEnv {
88 interface ImportMeta {99 readonly env: ImportMetaEnv;1010 }11+12+declare const __LOFI_JAZZ_APP_ID__: string;13+declare const __LOFI_JAZZ_SERVER_URL__: string;@@ -63,6 +63,7 @@ const updatedBody = "Golden path retained item updated";
6363 const disposableBody = "Golden path disposable item";6464 const offlineDevelopmentBody = "Golden path development offline item";6565 const offlineProductionBody = "Golden path production offline item";66+const offlineProductionSyncedBody = "Golden path production offline item synced";6667 6768 function assert(condition: unknown, message: string): asserts condition {6869 if (!condition) throw new Error(message);@@ -85,14 +86,11 @@ async function waitForItem(
8586 const island = page.locator("[data-island]").filter({ hasText: label });8687 const item = island.getByRole("listitem").filter({ hasText: body });8788 if (index === 0) {88- await Promise.race([89- item.waitFor({ state: "visible" }),90- island.getByRole("status").filter({ hasText: "Write failed:" }).waitFor({91- state: "visible",92- }).then(async () => {93- throw new Error((await island.getByRole("status").textContent()) ?? "Write failed");94- }),95- ]);89+ const failure = island.getByRole("status").filter({ hasText: "Write failed:" });90+ await item.or(failure).waitFor({ state: "visible" });91+ if (await failure.isVisible()) {92+ throw new Error((await failure.textContent()) ?? "Write failed");93+ }9694 } else {9795 await item.waitFor({ state: "visible" });9896 }@@ -168,16 +166,35 @@ async function assertRuntimeCardinality(
168166 });169167 }170168 169+async function setReferenceConnection(page: Page, connected: boolean): Promise<void> {170+ await page.evaluate(async (shouldConnect) => {171+ const probe = (globalThis as typeof globalThis & {172+ __LOFI_REFERENCE__?: {173+ disconnect(): Promise<void>;174+ reconnect(): Promise<void>;175+ };176+ }).__LOFI_REFERENCE__;177+ if (!probe) throw new Error("development reference probe is unavailable");178+ if (shouldConnect) await probe.reconnect();179+ else await probe.disconnect();180+ }, connected);181+}182+171183 async function observeReconnectSettlement(172184 page: Page,173185 timeoutMs: number,174186 required: boolean,175187 ): Promise<JourneyAssertion> {176188 try {177- await page.getByRole("status").filter({ hasText: /last write global/ }).first().waitFor({189+ const global = page.getByRole("status").filter({ hasText: /last write global/ }).first();190+ const failure = page.getByRole("status").filter({ hasText: "Write failed:" }).first();191+ await global.or(failure).waitFor({178192 state: "visible",179193 timeout: Math.min(timeoutMs, 10_000),180194 });195+ if (await failure.isVisible()) {196+ throw new Error((await failure.textContent()) ?? "Write failed");197+ }181198 return {182199 name: "development reconnect settlement",183200 status: "passed",@@ -244,13 +261,14 @@ async function completeItem(page: Page, body: string): Promise<void> {
244261 const name = new RegExp(`^(?:Complete ${escaped(body)}|Mark ${escaped(body)} complete)$`);245262 const checkboxes = page.getByRole("checkbox", { name });246263 try {247- await checkboxes.first().check();264+ await checkboxes.first().waitFor({ state: "visible" });248265 } catch (error) {249266 throw new Error(250267 `reference UI needs checkbox "Complete ${body}" or "Mark ${body} complete"`,251268 { cause: error },252269 );253270 }271+ await checkboxes.first().click();254272 await page.waitForFunction((accessibleName) => {255273 const matches = [...document.querySelectorAll<HTMLInputElement>('input[type="checkbox"]')]256274 .filter((element) =>@@ -474,11 +492,13 @@ export async function runJourney(options: GoldenPathOptions): Promise<JourneyRep
474492 detail: "Create, update, complete, and delete worked through named author-facing controls.",475493 });476494 495+ if (environmentMode === "cloud-from-root") await setReferenceConnection(page, false);477496 await context.setOffline(true);478497 await addItem(page, islandLabels[1], offlineDevelopmentBody);479498 await waitForItem(page, islandLabels, offlineDevelopmentBody);480499 await waitForLocalDurability(page);481500 await context.setOffline(false);501+ if (environmentMode === "cloud-from-root") await setReferenceConnection(page, true);482502 assertions.push(483503 await observeReconnectSettlement(page, timeoutMs, environmentMode === "cloud-from-root"),484504 );@@ -543,8 +563,17 @@ export async function runJourney(options: GoldenPathOptions): Promise<JourneyRep
543563 await page.reload({ waitUntil: "domcontentloaded", timeout: timeoutMs });544564 await waitForItem(page, islandLabels, offlineProductionBody);545565 await context.setOffline(false);566+ if (environmentMode === "cloud-from-root") {567+ await updateItem(page, offlineProductionBody, offlineProductionSyncedBody);568+ await waitForItem(page, islandLabels, offlineProductionSyncedBody);569+ assertions.push(await observeReconnectSettlement(page, timeoutMs, true));570+ }546571 await page.reload({ waitUntil: "domcontentloaded", timeout: timeoutMs });547- await waitForItem(page, islandLabels, offlineProductionBody);572+ await waitForItem(573+ page,574+ islandLabels,575+ environmentMode === "cloud-from-root" ? offlineProductionSyncedBody : offlineProductionBody,576+ );548577 assertions.push({549578 name: "production offline cold start",550579 status: "passed",@@ -552,11 +581,11 @@ export async function runJourney(options: GoldenPathOptions): Promise<JourneyRep
552581 });553582 554583 if (environmentMode === "cloud-from-root") {555- for (const body of [updatedBody, offlineDevelopmentBody, offlineProductionBody]) {584+ for (const body of [updatedBody, offlineDevelopmentBody, offlineProductionSyncedBody]) {556585 await deleteItem(page, body);557586 await waitForItemAbsent(page, islandLabels, body);587+ assertions.push(await observeReconnectSettlement(page, timeoutMs, true));558588 }559- assertions.push(await observeReconnectSettlement(page, timeoutMs, true));560589 assertions.push({561590 name: "cloud fixture cleanup",562591 status: "passed",