create-rindle 0.3.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
-- declared BOOLEAN/BOOL → boolean(), JSON → json(). Column ORDER matters — the IVM engine reads it
|
|
13
13
|
-- back with PRAGMA table_info. `IF NOT EXISTS` keeps re-runs safe.
|
|
14
14
|
|
|
15
|
-
CREATE TABLE IF NOT EXISTS room (id TEXT, name TEXT, createdAt REAL, PRIMARY KEY (id));
|
|
15
|
+
CREATE TABLE IF NOT EXISTS room (id TEXT NOT NULL, name TEXT NOT NULL, createdAt REAL NOT NULL, PRIMARY KEY (id));
|
|
16
16
|
-- The room list orders newest-first.
|
|
17
17
|
CREATE INDEX IF NOT EXISTS room_created ON room (createdAt DESC, id);
|
|
18
18
|
|
|
19
|
-
CREATE TABLE IF NOT EXISTS message (id TEXT, roomId TEXT, author TEXT, body TEXT, createdAt REAL, PRIMARY KEY (id));
|
|
19
|
+
CREATE TABLE IF NOT EXISTS message (id TEXT NOT NULL, roomId TEXT NOT NULL, author TEXT NOT NULL, body TEXT NOT NULL, createdAt REAL NOT NULL, PRIMARY KEY (id));
|
|
20
20
|
-- Forward (a room's messages, oldest-first) AND the reverse the live count uses when a message changes.
|
|
21
21
|
CREATE INDEX IF NOT EXISTS message_room ON message (roomId, createdAt, id);
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// Column kinds come from your declared SQL types: TEXT maps to string(), INTEGER/REAL to
|
|
5
5
|
// number(), and a column declared BOOLEAN/BOOL or JSON to boolean()/json(). The only thing the
|
|
6
6
|
// SQL can't carry is a refinement *within* a kind — the element type of json<T>(), or a
|
|
7
|
-
// string/number literal union —
|
|
7
|
+
// string/number literal union — declare those ONCE in a hand-written module with
|
|
8
|
+
// refineTable(...) + refineSchema(...); they survive every regen of this file.
|
|
8
9
|
|
|
9
10
|
import { createSchema, number, string, table } from "@rindle/client";
|
|
10
11
|
|
|
@@ -1,47 +1,19 @@
|
|
|
1
|
-
// The SSR→SPA store handoff.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// `useQuery` reads its seed, so the server and the client's first render produce identical markup
|
|
6
|
-
// with NO engine on either side.
|
|
7
|
-
// - After hydration: the wasm IVM engine boots (client-only dynamic import, rindle-client.ts), its
|
|
8
|
-
// views are seeded from the same snapshot (so the swap shows the SSR rows with no flash), and the
|
|
9
|
-
// live `subscribe` reconciles — the page is now a live SPA.
|
|
1
|
+
// The SSR→SPA store handoff. The handoff itself — seed the render from the SSR snapshot, boot the
|
|
2
|
+
// wasm engine after hydration, swap the store under `useQuery` with no flash — lives in `<RindleSSR>`
|
|
3
|
+
// (@rindle/react). This file just binds THIS app's `schema` + `bootClient` into it, so the route
|
|
4
|
+
// tree keeps rendering `<RindleApp ssrState={…}>` unchanged.
|
|
10
5
|
|
|
11
|
-
import { useEffect, useRef, useState } from "react";
|
|
12
6
|
import type { ReactNode } from "react";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
7
|
+
import { RindleSSR } from "@rindle/react";
|
|
8
|
+
import type { DehydratedState } from "@rindle/client";
|
|
15
9
|
|
|
16
10
|
import { schema } from "../shared/app-def.ts";
|
|
17
11
|
import { bootClient } from "./rindle-client.ts";
|
|
18
12
|
|
|
19
|
-
type RindleClient = Awaited<ReturnType<typeof bootClient>>;
|
|
20
|
-
|
|
21
13
|
export function RindleApp({ ssrState, children }: { ssrState: DehydratedState; children: ReactNode }) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return store;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// The live wasm store — booted only in the browser, only after hydration.
|
|
31
|
-
const [liveStore, setLiveStore] = useState<RindleClient["store"] | null>(null);
|
|
32
|
-
const ssrStateRef = useRef(ssrState);
|
|
33
|
-
ssrStateRef.current = ssrState;
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
let alive = true;
|
|
36
|
-
void bootClient().then((app) => {
|
|
37
|
-
if (!alive) return;
|
|
38
|
-
app.store.hydrate(ssrStateRef.current); // seed the live views so the swap doesn't flash empty
|
|
39
|
-
setLiveStore(app.store);
|
|
40
|
-
});
|
|
41
|
-
return () => {
|
|
42
|
-
alive = false;
|
|
43
|
-
};
|
|
44
|
-
}, []);
|
|
45
|
-
|
|
46
|
-
return <Rindle store={liveStore ?? seedStore}>{children}</Rindle>;
|
|
14
|
+
return (
|
|
15
|
+
<RindleSSR schema={schema} ssrState={ssrState} boot={bootClient}>
|
|
16
|
+
{children}
|
|
17
|
+
</RindleSSR>
|
|
18
|
+
);
|
|
47
19
|
}
|
|
@@ -36,16 +36,11 @@ const readInProcess: OneShotQueryFn = async ({ name, args }): Promise<OneShotRes
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
/** Preload the given NAMED queries through the authority and return the dehydrated first-paint cache to
|
|
39
|
-
* embed in the HTML. Call from a route loader (server only).
|
|
40
|
-
* that query (the live engine fills it in after hydration) rather than breaking the whole page. */
|
|
39
|
+
* embed in the HTML. Call from a route loader (server only). `preloadAll` degrades a failed read to no
|
|
40
|
+
* seed for that query (the live engine fills it in after hydration) rather than breaking the whole page. */
|
|
41
41
|
export async function preloadRindle(queries: Array<Query<any, any, any>>): Promise<DehydratedState> {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.error("[ssr] preload failed; rendering this query without its first-paint seed:", err instanceof Error ? err.message : err);
|
|
47
|
-
}),
|
|
48
|
-
),
|
|
49
|
-
);
|
|
50
|
-
return server.dehydrate();
|
|
42
|
+
return createServerStore(schema, { query: readInProcess }).preloadAll(queries, {
|
|
43
|
+
onError: (_query, err) =>
|
|
44
|
+
console.error("[ssr] preload failed; rendering this query without its first-paint seed:", err instanceof Error ? err.message : err),
|
|
45
|
+
});
|
|
51
46
|
}
|