snapback2 0.0.1
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/README.md +127 -0
- package/bin/snapback2.mjs +36 -0
- package/dist/api.d.ts +20 -0
- package/dist/assets.d.ts +34 -0
- package/dist/client-assets.d.ts +26 -0
- package/dist/client-offline.d.ts +94 -0
- package/dist/client-outbox.d.ts +58 -0
- package/dist/client-upload.d.ts +41 -0
- package/dist/client-wire.d.ts +41 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.mjs +5 -0
- package/dist/client.mjs.map +7 -0
- package/dist/compiler-core.mjs +60 -0
- package/dist/compiler-core.mjs.map +7 -0
- package/dist/compiler-lib.d.ts +71 -0
- package/dist/compiler.mjs +5 -0
- package/dist/compiler.mjs.map +7 -0
- package/dist/drain.d.ts +33 -0
- package/dist/expo/files.d.ts +3 -0
- package/dist/expo/index.d.ts +16 -0
- package/dist/expo/token-store.d.ts +7 -0
- package/dist/expo/witness.d.ts +28 -0
- package/dist/expo/witness.mjs +2 -0
- package/dist/expo/witness.mjs.map +7 -0
- package/dist/expo.d.ts +4 -0
- package/dist/expo.mjs +9 -0
- package/dist/expo.mjs.map +7 -0
- package/dist/guide/auth.md +89 -0
- package/dist/guide/effects.md +52 -0
- package/dist/guide/families.json +446 -0
- package/dist/guide/grammar.md +52 -0
- package/dist/guide/live-query.md +27 -0
- package/dist/guide/offline.md +75 -0
- package/dist/guide/personas.md +15 -0
- package/dist/guide/quarry.md +297 -0
- package/dist/guide/testing.md +57 -0
- package/dist/index.d.ts +362 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +7 -0
- package/dist/offline-protocol.d.ts +180 -0
- package/dist/offline-schema.d.ts +1 -0
- package/dist/offline.d.ts +177 -0
- package/dist/react-core.d.ts +91 -0
- package/dist/react-core.mjs +2 -0
- package/dist/react-core.mjs.map +7 -0
- package/dist/react-native/components.d.ts +23 -0
- package/dist/react-native/files.d.ts +43 -0
- package/dist/react-native/source.d.ts +16 -0
- package/dist/react-native.d.ts +4 -0
- package/dist/react-native.mjs +2 -0
- package/dist/react-native.mjs.map +7 -0
- package/dist/react.d.ts +19 -0
- package/dist/react.mjs +2 -0
- package/dist/react.mjs.map +7 -0
- package/dist/sqlite-test.mjs +1720 -0
- package/dist/sqlite-test.mjs.map +7 -0
- package/dist/sse.d.ts +44 -0
- package/dist/store/byte-cache.d.ts +162 -0
- package/dist/store/canonical.d.ts +2 -0
- package/dist/store/indexeddb.d.ts +23 -0
- package/dist/store/locks.d.ts +11 -0
- package/dist/store/outbox.d.ts +113 -0
- package/dist/store/overlay-retirement.d.ts +17 -0
- package/dist/store/projection.d.ts +11 -0
- package/dist/store/range-store.d.ts +388 -0
- package/dist/store/sqlite-driver.conformance.d.ts +7 -0
- package/dist/store/sqlite-driver.d.ts +16 -0
- package/dist/store/sqlite-expo.d.ts +3 -0
- package/dist/store/sqlite.d.ts +31 -0
- package/dist/templates/chat/expo/App.tsx +29 -0
- package/dist/templates/chat/expo/app.json +11 -0
- package/dist/templates/chat/expo/index.js +3 -0
- package/dist/templates/chat/expo/shared/log.js +92 -0
- package/dist/templates/chat/expo/shared/log.ts +130 -0
- package/dist/templates/chat/expo/shared/offline.js +8 -0
- package/dist/templates/chat/expo/shared/offline.ts +9 -0
- package/dist/templates/chat/expo/src/Chat.tsx +23 -0
- package/dist/templates/chat/expo/src/Composer.tsx +23 -0
- package/dist/templates/chat/expo/src/MediaView.tsx +29 -0
- package/dist/templates/chat/expo/src/Witness.tsx +131 -0
- package/dist/templates/chat/expo/src/screens/Inbox.tsx +9 -0
- package/dist/templates/chat/expo/src/screens/Search.tsx +10 -0
- package/dist/templates/chat/expo/src/screens/Thread.tsx +22 -0
- package/dist/templates/chat/expo/src/witness-state.ts +68 -0
- package/dist/templates/chat/expo/tsconfig.json +5 -0
- package/dist/templates/chat/react/index.html +5 -0
- package/dist/templates/chat/react/shared/log.js +92 -0
- package/dist/templates/chat/react/shared/log.ts +130 -0
- package/dist/templates/chat/react/shared/offline.js +8 -0
- package/dist/templates/chat/react/shared/offline.ts +9 -0
- package/dist/templates/chat/react/src/App.tsx +36 -0
- package/dist/templates/chat/react/src/Composer.tsx +45 -0
- package/dist/templates/chat/react/src/Inbox.tsx +24 -0
- package/dist/templates/chat/react/src/Search.tsx +26 -0
- package/dist/templates/chat/react/src/Thread.tsx +126 -0
- package/dist/templates/chat/react/src/env.d.ts +1 -0
- package/dist/templates/chat/react/src/main.tsx +20 -0
- package/dist/templates/chat/shared/log.js +92 -0
- package/dist/templates/chat/shared/log.ts +130 -0
- package/dist/templates/chat/shared/offline.js +8 -0
- package/dist/templates/chat/shared/offline.ts +9 -0
- package/dist/templates/chat/snapback/deliveries.q +33 -0
- package/dist/templates/chat/snapback/delivery.ts +30 -0
- package/dist/templates/chat/snapback/feeds.q +14 -0
- package/dist/templates/chat/snapback/follows.q +11 -0
- package/dist/templates/chat/snapback/groups.q +40 -0
- package/dist/templates/chat/snapback/messages.q +32 -0
- package/dist/templates/chat/snapback/notifications.q +7 -0
- package/dist/templates/chat/snapback/posts.q +4 -0
- package/dist/templates/chat/snapback/profiles.q +17 -0
- package/dist/templates/chat/snapback/schema.q +136 -0
- package/dist/templates/chat/snapback/seed.ts +45 -0
- package/dist/templates/chat/snapback/tests/chat.test.ts +759 -0
- package/dist/templates/react/index.html +5 -0
- package/dist/templates/react/src/App.tsx +5 -0
- package/dist/templates/react/src/main.tsx +18 -0
- package/dist/templates/todos/snapback/schema.q +11 -0
- package/dist/templates/todos/snapback/seed.ts +10 -0
- package/dist/templates/todos/snapback/tests/todos.test.ts +13 -0
- package/dist/templates/todos/snapback/todos.q +6 -0
- package/dist/test-runner.mjs +8541 -0
- package/dist/test-runner.mjs.map +7 -0
- package/dist/test.d.ts +28 -0
- package/dist/test.mjs +8541 -0
- package/dist/test.mjs.map +7 -0
- package/dist/token-store.d.ts +3 -0
- package/dist/twin-hydrate.d.ts +43 -0
- package/dist/twin.d.ts +70 -0
- package/dist/types.d.ts +410 -0
- package/dist/witness-test.mjs +2 -0
- package/dist/witness-test.mjs.map +7 -0
- package/package.json +117 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../templates/chat/expo/src/MediaView.tsx", "../templates/chat/expo/src/witness-state.ts", "../templates/chat/shared/log.ts", "../templates/chat/expo/src/screens/Inbox.tsx", "../templates/chat/expo/src/Composer.tsx", "../templates/chat/expo/src/screens/Thread.tsx", "../templates/chat/expo/src/screens/Search.tsx", "../templates/chat/expo/src/Chat.tsx", "../templates/chat/shared/offline.ts", "../templates/chat/expo/src/Witness.tsx", "../tests/witness-exports.ts", "../templates/chat/expo/App.tsx"],
|
|
4
|
+
"sourcesContent": ["import React, { useCallback } from \"react\";\nimport type { AssetRecord } from \"snapback2/client\";\nimport { AssetImage, AssetVideo } from \"snapback2/react-native\";\n\nexport interface Media {\n asset: AssetRecord; nonce: number; once: boolean; hidden?: boolean;\n loaded: boolean; uri?: string; resolve(value: unknown): void;\n}\n/** The witness observes the source actually passed to the native renderer. */\nexport function MediaView({ media }: { media: Media }) {\n const source = useCallback((uri: string | undefined, settled: boolean) => {\n media.uri = uri;\n if (media.hidden && settled) media.resolve({ loaded: media.loaded, uri });\n }, [media]);\n const loaded = useCallback((loadedUri?: string) => {\n const uri = media.uri;\n if (!uri) { media.resolve({ loaded: false }); return; }\n if (loadedUri && loadedUri !== uri) return; // a late event from the prior memory source\n media.loaded = true;\n if (media.once ? uri.startsWith(\"data:\") : uri.startsWith(\"file://\"))\n media.resolve({ loaded: true, uri, ...( \"duration\" in media.asset ? { playing: true } : {}) });\n }, [media]);\n const refusal = useCallback((error: { envelope: unknown }) => media.resolve({ loaded: false, refusal: error.envelope }), [media]);\n return \"duration\" in media.asset\n ? <AssetVideo asset={media.asset} width={64} autoPlay={!media.hidden} testID=\"witness-media\"\n onSourceChange={source} onPlaybackStart={loaded} onRefusal={refusal} />\n : <AssetImage asset={media.asset} width={64} testID=\"witness-media\"\n onSourceChange={source} onLoad={(event) => loaded(event?.nativeEvent?.source?.uri)} onRefusal={refusal} />;\n}\n", "import { Directory, File, Paths } from \"expo-file-system\";\nimport { expoSqliteDriver } from \"snapback2/expo\";\nimport type { AssetRecord, OutboxEntry, RangeStore, StoreSwap } from \"snapback2/client\";\n\nexport const DATABASE = \"snapback2-witness.db\";\nexport interface Configuration { offline: boolean; fault: \"carrier\" | \"none\"; name: string; once: string[]; assets: AssetRecord[] }\nconst configDirectory = () => new Directory(Paths.document, \"snapback2-witness-config\");\nconst defaultConfiguration = (): Configuration => ({ offline: false, fault: \"none\", name: \"alice\", once: [], assets: [] });\nfunction versions(): File[] {\n const directory = configDirectory();\n return directory.exists ? directory.list().filter((file): file is File => file instanceof File && /\\/\\d{16}\\.json$/.test(file.uri)).sort((a, b) => a.uri.localeCompare(b.uri)) : [];\n}\nexport async function configuration(): Promise<Configuration> {\n const file = versions().at(-1);\n return file ? JSON.parse(await file.text()) : defaultConfiguration();\n}\nlet persistTail: Promise<unknown> = Promise.resolve();\nexport function persist(patch: Partial<Configuration>): Promise<void> {\n const write = persistTail.then(async () => {\n const directory = configDirectory(); directory.create({ intermediates: true, idempotent: true });\n const previous = versions();\n const last = previous.at(-1);\n const sequence = last ? Number(last.uri.match(/(\\d{16})\\.json$/)![1]) + 1 : 1;\n const temp = new File(directory, \"next.tmp\");\n temp.write(JSON.stringify({ ...await configuration(), ...patch }));\n // Expo File.move cannot replace an existing destination atomically. Rename\n // into a fresh numbered leaf; readers choose the latest committed leaf.\n temp.move(new File(directory, `${String(sequence).padStart(16, \"0\")}.json`));\n for (const old of previous) old.delete();\n });\n persistTail = write.catch(() => undefined);\n return write;\n}\nexport function listFiles(directory = new Directory(Paths.cache, \"snapback2\")): string[] {\n if (!directory.exists) return [];\n return directory.list().flatMap((item) => item instanceof Directory ? listFiles(item) : [item.uri]);\n}\nexport async function databaseState(): Promise<Record<string, unknown>> {\n const driver = expoSqliteDriver(DATABASE);\n const result: Record<string, unknown> = {};\n try {\n for (const table of [\"state\", \"rows\", \"answers\", \"outbox\", \"bytes\"]) {\n const rows = await driver.all<Record<string, unknown>>(`SELECT * FROM ${table} ORDER BY partition${table === \"state\" ? \"\" : \", key\"}`);\n result[table] = rows.map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) =>\n [key, value instanceof Uint8Array ? Array.from(value) : value])));\n }\n return result;\n } finally { await driver.close(); }\n}\nconst markerId = \"s2id-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-0000000000000001\";\n/** A dev-only swap touching every persisted projection with distinct pre/post markers. */\nexport async function markerSwap(store: RangeStore, phase: \"pre\" | \"post\", entries: OutboxEntry[]): Promise<void> {\n if (!entries[0]) throw new Error(\"enqueue both messages before the atomicity marker\");\n const byte = phase === \"pre\" ? 11 : 22;\n const swap: StoreSwap = {\n // The markers are schema-valid rows of real tables: the twin hydrates every held row\n // through the schema and treats a row of an unknown table as corruption (`hydrating`),\n // which is the fail-closed answer the spec wants \u2014 so a witness marker must look like\n // the app's own data. A messages row in a conversation no member ever opens.\n deliveries: [{ op: \"deliveries.thread\", args: { conversationId: \"witness-marker\", c: null }, seq: phase === \"pre\" ? 1 : 2, state: \"complete\", next: null,\n data: { $row: { table: \"messages\", id: \"witness-marker\" } },\n rows: [{ table: \"messages\", id: \"witness-marker\", row: { id: \"witness-marker\", conversationId: \"witness-marker\", authorId: \"witness\", body: phase, photo: { id: markerId, width: 1, height: 1 }, at: 1 } }],\n receipt: { ranges: [], absences: [], verdicts: [] } }],\n outbox: { put: [{ ...entries[0], intent: \"__witness\", state: \"committed\", op: \"deliveries.post\", args: { conversationId: \"witness-marker\", body: phase }, predicted: false }] },\n bytes: { put: [{ assetId: markerId, width: null, type: `witness/${phase}`, bytes: new Uint8Array([byte]) }] },\n };\n await store.commit(swap);\n}\n", "// The delivery log is the app's source of truth on the device (LLP 1012 \u00A73):\n// every screen is a pure function over held log rows plus the durable outbox.\nimport type { Api, OutboxEntry } from \"snapback2/client\";\n\ntype ResultOf<T> = T extends { readonly __result?: infer Result } ? NonNullable<Result> : never;\nexport type LogRow = ResultOf<Api[\"deliveries\"][\"inbox\"]>[number];\nexport type MessageRow = NonNullable<LogRow[\"message\"]>;\n\nexport interface ConversationSummary {\n conversationId: string;\n latestAt: number;\n latestBody: string | undefined;\n messages: number;\n}\n\n/** One line per conversation, newest activity first, from the held log. */\nexport function conversations(rows: readonly LogRow[]): ConversationSummary[] {\n const byConversation = new Map<string, ConversationSummary>();\n for (const row of rows) {\n const current = byConversation.get(row.conversationId) ?? {\n conversationId: row.conversationId,\n latestAt: row.at,\n latestBody: undefined,\n messages: 0,\n };\n if (row.kind === \"message\") current.messages += 1;\n if (row.at >= current.latestAt) {\n current.latestAt = row.at;\n current.latestBody = row.message?.body ?? current.latestBody;\n }\n byConversation.set(row.conversationId, current);\n }\n return [...byConversation.values()].sort((a, b) => b.latestAt - a.latestAt);\n}\n\nexport type PostArgs = { conversationId: string; body: string };\nexport type EditArgs = { messageId: string; body: string; baseBody: string };\n\nexport function postArgs(entry: OutboxEntry): PostArgs | undefined {\n const args = entry.args as Partial<PostArgs> | null;\n return entry.op === \"deliveries.post\" && args && typeof args.conversationId === \"string\" && typeof args.body === \"string\"\n ? { conversationId: args.conversationId, body: args.body }\n : undefined;\n}\n\nexport function editArgs(entry: OutboxEntry): EditArgs | undefined {\n const args = entry.args as Partial<EditArgs> | null;\n return entry.op === \"deliveries.edit\" && args && typeof args.messageId === \"string\" && typeof args.body === \"string\"\n ? { messageId: args.messageId, body: args.body, baseBody: args.baseBody ?? \"\" }\n : undefined;\n}\n\n/** The label a settled or queued intent shows; never a client clock. */\nexport function intentLabel(entry: OutboxEntry): string {\n switch (entry.state) {\n case \"queued\": return entry.reason === \"fence\" ? \"Will be checked by the server\" : \"Sending\u2026\";\n case \"committed\": return \"Sent\";\n case \"expired\": return \"Expired\";\n case \"rejected\": {\n const response = entry.response as { reason?: string; code?: string; rewrite?: string; message?: string } | undefined;\n // @ref LLP 1012#7-writes-offline \u2014 a trace conflict, or the recipe's own\n // compare-and-set (`require msg.body = baseBody else CONFLICT`), reads the same.\n if (response?.reason === \"conflict\" || response?.code === \"CONFLICT\") return \"Not sent \u00B7 Message changed\";\n return `Not sent \u00B7 ${response?.rewrite ?? response?.message ?? response?.code ?? \"refused\"}`;\n }\n }\n}\n\nexport type Bubble =\n | { key: string; kind: \"row\"; message: MessageRow; at: number; edit?: OutboxEntry }\n | { key: string; kind: \"pending\"; entry: OutboxEntry; body: string };\n\n/**\n * The thread as bubbles: one per message id from the log (tombstoned ids\n * hidden), then the outbox's intents for this conversation. A pending post is\n * keyed by its first pinned id, the id the server row will carry, so the same\n * bubble becomes the row on `committed` and nothing jumps.\n */\nexport function threadBubbles(rows: readonly LogRow[], entries: readonly OutboxEntry[], conversationId: string): Bubble[] {\n const hidden = new Set(rows.filter((row) => row.kind === \"removed\" || row.kind === \"expired\").map((row) => row.messageId));\n const edits = new Map(entries.map((entry) => [editArgs(entry)?.messageId, entry] as const).filter(([id]) => id !== undefined));\n const seen = new Set<string>();\n const bubbles: Bubble[] = [];\n // @ref LLP 1012#7-writes-offline \u2014 a queued send the twin predicted is inside\n // the log's rows under its pinned id; it is still the pending bubble, with\n // `args.body` and \"Sending\u2026\" and no time, until the server's row settles it.\n const pending = new Map(entries\n .filter((entry) => entry.state === \"queued\" && postArgs(entry) !== undefined)\n .map((entry) => [entry.ids[0] ?? entry.intent, entry] as const));\n // The log's thread page is newest-first (`last 50`); a thread reads oldest-first.\n const chronological = [...rows].sort((a, b) => a.at - b.at || (a.messageId < b.messageId ? -1 : a.messageId > b.messageId ? 1 : 0));\n for (const row of chronological) {\n if (row.kind !== \"message\" || !row.message || hidden.has(row.messageId) || seen.has(row.messageId)) continue;\n seen.add(row.messageId);\n const predicted = pending.get(row.messageId);\n if (predicted) {\n bubbles.push({ key: row.messageId, kind: \"pending\", entry: predicted, body: postArgs(predicted)!.body });\n continue;\n }\n const edit = edits.get(row.messageId);\n bubbles.push({ key: row.messageId, kind: \"row\", message: row.message, at: row.at, ...(edit ? { edit } : {}) });\n }\n for (const entry of entries) {\n const args = postArgs(entry);\n if (!args || args.conversationId !== conversationId) continue;\n const key = entry.ids[0] ?? entry.intent;\n if (seen.has(key)) continue;\n bubbles.push({ key, kind: \"pending\", entry, body: args.body });\n }\n return bubbles;\n}\n\n/** Committed posts whose server row is now held can leave the outbox. */\nexport function settledByRows(rows: readonly LogRow[], entries: readonly OutboxEntry[]): OutboxEntry[] {\n const held = new Set(rows.map((row) => row.messageId));\n return entries.filter((entry) => entry.state === \"committed\" && postArgs(entry) !== undefined && held.has(entry.ids[0] ?? \"\"));\n}\n\n/** Offline search is a filter over the downloaded log (LLP 1012 \u00A76); it finds nothing evicted. */\nexport function searchHeld(rows: readonly LogRow[], term: string): LogRow[] {\n const needle = term.trim().toLowerCase();\n if (!needle) return [];\n const seen = new Set<string>();\n return rows.filter((row) => {\n const body = row.message?.body;\n if (row.kind !== \"message\" || !body || seen.has(row.messageId) || !body.toLowerCase().includes(needle)) return false;\n seen.add(row.messageId);\n return true;\n });\n}\n", "import React from \"react\";\nimport { Pressable, Text, View } from \"react-native\";\nimport { conversations, type LogRow } from \"../../shared/log.js\";\nexport function Inbox({ rows, onSelect }: { rows: readonly LogRow[]; onSelect(id: string): void }) {\n return <View accessibilityLabel=\"Inbox\">{conversations(rows).map((item) =>\n <Pressable key={item.conversationId} onPress={() => onSelect(item.conversationId)} style={{ paddingVertical: 16 }}>\n <Text style={{ fontWeight: \"600\" }}>{item.conversationId}</Text><Text>{item.latestBody ?? `${item.messages} messages`}</Text>\n </Pressable>)}{rows.length === 0 ? <Text>No downloaded messages yet.</Text> : null}</View>;\n}\n", "import React, { useState } from \"react\";\nimport { Button, Text, TextInput, View } from \"react-native\";\nimport { api, isSnapbackRefusal } from \"snapback2/client\";\nimport { useMutation, useOutbox } from \"snapback2/react-native\";\nexport function Composer({ conversationId }: { conversationId: string }) {\n const post = useMutation(api.deliveries.post);\n const { fence } = useOutbox();\n const [body, setBody] = useState(\"\");\n const [note, setNote] = useState(\"\");\n const send = async () => {\n if (!body.trim() || !post.ready) return;\n try {\n const result = await post.run({ conversationId, body: body.trim() });\n if (result.state === \"rejected\") setNote(result.rewrite ?? result.message);\n else { setBody(\"\"); setNote(result.state === \"queued\" ? \"Queued until the next sync.\" : \"\"); }\n } catch (error) { setNote(isSnapbackRefusal(error) ? error.envelope.rewrite ?? error.message : String(error)); }\n };\n return <View style={{ padding: 16 }}><TextInput accessibilityLabel=\"Message\" placeholder=\"Message\" value={body} onChangeText={setBody} multiline />\n <Button title=\"Send\" disabled={!post.ready || post.inFlight || !body.trim()} onPress={() => { void send(); }} />\n {!post.ready ? <Text>Sending opens once the log has downloaded.</Text> : fence === \"past\" ? <Text>Queued until the next sync.</Text> : null}\n {note ? <Text accessibilityRole=\"alert\">{note}</Text> : null}\n </View>;\n}\n", "import React from \"react\";\nimport { ScrollView, Text, View } from \"react-native\";\nimport { api } from \"snapback2/client\";\nimport { AssetImage, AssetVideo, useOutbox, useQuery } from \"snapback2/react-native\";\nimport { intentLabel, threadBubbles, type LogRow } from \"../../shared/log.js\";\nimport { Composer } from \"../Composer\";\nexport function Thread({ conversationId }: { conversationId: string }) {\n const result = useQuery(api.deliveries.thread, { conversationId, c: null });\n const { entries } = useOutbox();\n const rows: LogRow[] = \"data\" in result && Array.isArray(result.data) ? result.data : [];\n return <View style={{ flex: 1 }}><ScrollView contentContainerStyle={{ padding: 16 }}>\n {result.state === \"partial\" ? <Text>Some messages are not downloaded.</Text> : null}\n {result.state === \"refused\" ? <Text>{result.refusal.rewrite ?? result.refusal.message}</Text> : null}\n {threadBubbles(rows, entries, conversationId).map((bubble) => <View key={bubble.key} testID={`message-${bubble.key}`} style={{ padding: 12, marginVertical: 4, backgroundColor: \"#e7edff\", borderRadius: 12 }}>\n {bubble.kind === \"pending\" ? <><Text>{bubble.body}</Text><Text>{intentLabel(bubble.entry)}</Text></>\n : <><Text>{bubble.message.body}</Text>\n {bubble.message.photo ? <AssetImage asset={bubble.message.photo} width={240} /> : null}\n {bubble.message.clip ? <AssetVideo asset={bubble.message.clip} width={240} /> : null}\n <Text>{new Date(bubble.at).toLocaleTimeString()}</Text></>}\n </View>)}\n </ScrollView><Composer conversationId={conversationId} /></View>;\n}\n", "import React, { useState } from \"react\";\nimport { Pressable, Text, TextInput, View } from \"react-native\";\nimport { searchHeld, type LogRow } from \"../../shared/log.js\";\nexport function Search({ rows, onOpen }: { rows: readonly LogRow[]; onOpen(id: string): void }) {\n const [term, setTerm] = useState(\"\");\n return <View><TextInput accessibilityLabel=\"Search downloaded messages\" placeholder=\"Search\" value={term} onChangeText={setTerm} />\n {term ? <Text>in downloaded messages</Text> : null}\n {searchHeld(rows, term).map((row) => <Pressable key={row.messageId} onPress={() => onOpen(row.conversationId)}><Text>{row.message?.body}</Text></Pressable>)}\n </View>;\n}\n", "import React, { useState } from \"react\";\nimport { SafeAreaView, ScrollView, Text, Button } from \"react-native\";\nimport { api } from \"snapback2/client\";\nimport { useConnection, useQuery } from \"snapback2/react-native\";\nimport type { LogRow } from \"../shared/log.js\";\nimport { Inbox } from \"./screens/Inbox\";\nimport { Thread } from \"./screens/Thread\";\nimport { Search } from \"./screens/Search\";\n\nexport function Chat() {\n const connection = useConnection();\n const inbox = useQuery(api.deliveries.inbox, { c: null }, { drain: true });\n const rows: LogRow[] = \"data\" in inbox && Array.isArray(inbox.data) ? inbox.data : [];\n const [selected, select] = useState<string>();\n return <SafeAreaView style={{ flex: 1, backgroundColor: \"#f7f7fb\" }}>\n <Text style={{ fontSize: 24, padding: 16 }}>Snapback Chat \u00B7 {connection}</Text>\n {selected ? <><Button title=\"Inbox\" onPress={() => select(undefined)} /><Thread conversationId={selected} /></>\n : <ScrollView contentContainerStyle={{ padding: 16 }}>\n <Search rows={rows} onOpen={select} /><Inbox rows={rows} onSelect={select} />\n {inbox.state === \"refused\" ? <Text>{inbox.refusal.rewrite ?? inbox.refusal.message}</Text> : null}\n </ScrollView>}\n </SafeAreaView>;\n}\n", "type StoredRow = { table: string; row: Record<string, unknown> };\n\nexport const chatOffline = {\n onTombstone: (item: StoredRow) =>\n item.table === \"deliveries\" && (item.row.kind === \"removed\" || item.row.kind === \"expired\")\n && typeof item.row.messageId === \"string\" ? [{ table: \"messages\", id: item.row.messageId }] : [],\n // Optional on the recipe's message row; absent/null is ordinary media.\n viewOnce: (table: string, row: Record<string, unknown>) => table === \"messages\" && row.viewOnce === true,\n};\n", "import { twinLastDecline } from \"snapback2/expo/witness\";\nimport React, { useEffect, useState } from \"react\";\nimport { Text, View } from \"react-native\";\nimport { api, SnapbackRefusal, type AssetRecord, type ImageAsset, type RangeStore, type SnapbackClient, type Subscription } from \"snapback2/client\";\nimport { createExpoClient, expoSqliteStore, expoSqliteDriver, SnapbackProvider } from \"snapback2/expo\";\nimport { MediaView, type Media } from \"./MediaView\";\nimport { witnessControl, runSqliteDriverConformance, type WitnessHooks } from \"snapback2/expo/witness\";\nimport { chatOffline } from \"../shared/offline.js\";\nimport { searchHeld, threadBubbles } from \"../shared/log.js\";\nimport { configuration, persist, databaseState, markerSwap, listFiles, DATABASE } from \"./witness-state\";\nimport { Chat } from \"./Chat\";\n\nexport default function Witness({ url, controlUrl }: { url: string; controlUrl: string }) {\n const [client, setClient] = useState<SnapbackClient>();\n const [media, setMedia] = useState<Media>();\n const [error, setError] = useState<string>();\n useEffect(() => {\n let live: SnapbackClient | undefined, store: RangeStore | undefined, selected = \"conversation-1\";\n let subscriptions: Subscription<unknown>[] = [];\n let currentMedia: Media | undefined, viewNonce = 0;\n const dispose = async () => { for (const sub of subscriptions) sub.close(); subscriptions = []; await live?.close(); live = undefined; setClient(undefined); };\n const construct = async (hooks: WitnessHooks, args: Record<string, unknown>) => {\n await dispose();\n if (args.fresh === true) {\n const initial = expoSqliteStore(DATABASE)();\n try { await initial.wipe(\"dev:alice\"); } finally { await initial.close?.(); }\n await persist({ name: \"alice\", offline: false, fault: \"none\" });\n }\n if (typeof args.name === \"string\") await persist({ name: args.name });\n if (Array.isArray(args.once) || Array.isArray(args.assets)) await persist({\n ...(Array.isArray(args.once) ? { once: args.once as string[] } : {}),\n ...(Array.isArray(args.assets) ? { assets: args.assets as AssetRecord[] } : {}),\n });\n const config = await configuration();\n store = expoSqliteStore(DATABASE, { beforeCommit: hooks.beforeCommit })();\n if (config.fault === \"carrier\") store.open = async () => { throw new SnapbackRefusal({\n code: \"E_OFFLINE_CARRIER\", family: \"offline\", site: \"witness construct\", message: \"persisted witness carrier fault\",\n rewrite: \"send fault(none), then construct\", guide: \"offline\",\n }); };\n live = createExpoClient({ url, as: config.name, startOffline: config.offline,\n offline: { ...chatOffline, store: () => store!, viewOnce: (table, row) => chatOffline.viewOnce(table, row) || (table === \"messages\" && config.once.includes(String(row.id))) } });\n try { await live.ready; setClient(live); setError(undefined); return live; }\n catch (error) { setError(String(error)); throw error; }\n };\n const control = witnessControl({ construct, persist,\n async command(client, verb, args) {\n // The carrier can be read before any client constructs: the harness inspects a\n // reopened database before the app mounts anything that writes to it.\n if (verb === \"stores\") return { stores: await databaseState(), files: listFiles() };\n if (!client || !store) throw new Error(\"construct first\");\n if (verb === \"drain\") {\n const sub = client.subscribe(api.deliveries.inbox, { c: null }, () => {}, { drain: true });\n subscriptions.push(sub);\n return sub.until((value) => \"caughtUp\" in value && value.caughtUp === true);\n }\n if (verb === \"open\") {\n selected = String(args.conversationId);\n const sub = client.subscribe(api.deliveries.thread, { conversationId: selected, c: null }, () => {});\n subscriptions.push(sub);\n await sub.until((value) => value.state === \"complete\");\n // Media mutations predate the delivery recipe; drain this named read to\n // hold their row leases too (messages.sendPhoto has no delivery fanout).\n await client.query(api.messages.thread, { conversationId: selected, c: null });\n return client.query(api.deliveries.thread, { conversationId: selected, c: null });\n }\n if (verb === \"send\") return client.mutation(api.deliveries.post, {\n conversationId: typeof args.conversationId === \"string\" ? args.conversationId : selected, body: String(args.body),\n });\n if (verb === \"view\") {\n const config = await configuration();\n const asset = config.assets.find((asset) => asset.id === args.assetId) as AssetRecord | undefined;\n if (!asset) throw new Error(\"unknown witness asset\");\n const once = Boolean(await client.assetCache?.viewOnce(asset.id) || await client.assetCache?.viewed(asset.id));\n const result = await new Promise<Record<string, unknown>>((resolve) => {\n currentMedia = { asset, nonce: ++viewNonce, once, hidden: args.hidden === true,\n loaded: false, resolve: (value) => resolve(value as Record<string, unknown>) };\n setMedia(currentMedia);\n });\n if (!once && result.loaded === true) {\n // The ordinary source may first load from memory; acknowledge only\n // the materialized file, after the bridge's directory reports it.\n // Compare paths, not spellings: the bridge's URI percent-encodes the partition\n // (`dev%3Aalice`) while the directory listing may not.\n const path = (uri: string) => decodeURIComponent(uri).replace(/\\/+$/, \"\");\n const served = path(String(result.uri));\n const deadline = Date.now() + 10_000;\n while (!listFiles().some((file) => path(file) === served)) {\n if (Date.now() >= deadline) throw new Error(`ordinary media did not materialize a file: served ${result.uri}; listed ${JSON.stringify(listFiles().slice(0, 6))}`);\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n return { ...result, viewed: await client.assetCache?.viewed(asset.id),\n files: listFiles().filter((file) => file.includes(asset.id)) };\n }\n\n if (verb === \"swap\") {\n if (args.phase !== \"pre\" && args.phase !== \"post\") throw new Error(\"swap needs pre or post\");\n await markerSwap(store, args.phase, await client.outbox.entries());\n return { phase: args.phase };\n }\n if (verb === \"conformance\") return runSqliteDriverConformance(() => expoSqliteDriver(\"snapback2-conformance.db\"));\n if (verb === \"settled\") {\n // A retryable refusal stops the replay and keeps the intent queued; the supported\n // retry is an explicit drain of the same envelope, then settled again (LLP 1012 \u00A77).\n await client.outbox.settled();\n if ((await client.outbox.entries()).some((entry) => entry.state === \"queued\")) {\n await client.outbox.drain();\n await client.outbox.settled();\n }\n const entries = await client.outbox.entries();\n if (entries.some((entry) => entry.state === \"queued\")) throw new Error(\"outbox did not settle\");\n return entries;\n }\n if (verb === \"state\") {\n const answer = typeof args.conversationId === \"string\"\n ? await client.query(api.deliveries.thread, { conversationId: args.conversationId, c: null }) : undefined;\n const entries = await client.outbox.entries();\n const rows = answer && \"data\" in answer && Array.isArray(answer.data) ? answer.data : [];\n return { snapshot: client.snapshot, coverage: await store.coverage(), outbox: entries,\n bubbles: typeof args.conversationId === \"string\" ? threadBubbles(rows, entries, args.conversationId) : [],\n search: typeof args.search === \"string\" ? searchHeld(rows, args.search) : [],\n answer, twin: twinLastDecline(), stores: await databaseState(), files: listFiles(), media: currentMedia ? { loaded: currentMedia.loaded, uri: currentMedia.uri } : null };\n }\n throw new Error(`unknown witness command ${verb}`);\n },\n }, controlUrl);\n return () => { control.close(); void dispose(); };\n }, [url, controlUrl]);\n return client ? <SnapbackProvider client={client}><View style={{ flex: 1 }}><Chat />{media ? <MediaView key={media.nonce} media={media} /> : null}</View></SnapbackProvider>\n : <Text>{error ?? \"Witness connected; waiting for construct.\"}</Text>;\n}\n", "export { MediaView } from \"../templates/chat/expo/src/MediaView.js\";\nexport { configuration, persist } from \"../templates/chat/expo/src/witness-state.js\";\nexport { NormalApp } from \"../templates/chat/expo/App.js\";\n", "import React, { lazy, Suspense, useEffect, useState } from \"react\";\nimport { createExpoClient, SnapbackProvider } from \"snapback2/expo\";\nimport type { SnapbackClient } from \"snapback2/client\";\nimport { Text } from \"react-native\";\nimport { Chat } from \"./src/Chat\";\nimport { chatOffline } from \"./shared/offline.js\";\n\nconst url = process.env.EXPO_PUBLIC_SNAPBACK_URL ?? \"http://127.0.0.1:3210\";\n// Metro inlines this in app source, never at simctl launch or inside node_modules.\nconst witnessUrl = process.env.EXPO_PUBLIC_SNAPBACK_WITNESS;\n\nconst Witness = witnessUrl ? lazy(() => import(\"./src/Witness\")) : undefined;\n\nexport function NormalApp() {\n const [client, setClient] = useState<SnapbackClient>();\n const [error, setError] = useState<string>();\n useEffect(() => {\n const client = createExpoClient({ url, as: \"alice\", offline: chatOffline });\n setClient(client);\n void client.ready.catch((error) => setError(String(error)));\n return () => { void client.close(); };\n }, []);\n if (!client || error) return <Text accessibilityLiveRegion=\"polite\">{error ?? \"Connecting\u2026\"}</Text>;\n return <SnapbackProvider client={client}><Chat /></SnapbackProvider>;\n}\n\nexport default function App() {\n return witnessUrl && Witness ? <Suspense fallback={<Text>Loading witness\u2026</Text>}><Witness url={url} controlUrl={witnessUrl} /></Suspense> : <NormalApp />;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["React", "useCallback", "AssetImage", "AssetVideo", "MediaView", "media", "source", "uri", "settled", "loaded", "loadedUri", "refusal", "error", "event", "init_MediaView", "__esmMin", "Directory", "File", "Paths", "expoSqliteDriver", "versions", "directory", "configDirectory", "file", "a", "b", "configuration", "defaultConfiguration", "persist", "patch", "write", "persistTail", "previous", "last", "sequence", "temp", "old", "listFiles", "item", "databaseState", "driver", "DATABASE", "result", "table", "rows", "row", "key", "value", "markerSwap", "store", "phase", "entries", "byte", "swap", "markerId", "init_witness_state", "__esmMin", "conversations", "rows", "byConversation", "row", "current", "a", "b", "postArgs", "entry", "args", "editArgs", "intentLabel", "response", "threadBubbles", "entries", "conversationId", "hidden", "edits", "id", "seen", "bubbles", "pending", "chronological", "predicted", "edit", "key", "searchHeld", "term", "needle", "body", "init_log", "__esmMin", "React", "Pressable", "Text", "View", "Inbox", "rows", "onSelect", "conversations", "item", "init_Inbox", "__esmMin", "init_log", "React", "useState", "Button", "Text", "TextInput", "View", "api", "isSnapbackRefusal", "useMutation", "useOutbox", "Composer", "conversationId", "post", "fence", "body", "setBody", "note", "setNote", "send", "result", "error", "init_Composer", "__esmMin", "React", "ScrollView", "Text", "View", "api", "AssetImage", "AssetVideo", "useOutbox", "useQuery", "Thread", "conversationId", "result", "entries", "rows", "threadBubbles", "bubble", "intentLabel", "Composer", "init_Thread", "__esmMin", "init_log", "init_Composer", "React", "useState", "Pressable", "Text", "TextInput", "View", "Search", "rows", "onOpen", "term", "setTerm", "searchHeld", "row", "init_Search", "__esmMin", "init_log", "React", "useState", "SafeAreaView", "ScrollView", "Text", "Button", "api", "useConnection", "useQuery", "Chat", "connection", "inbox", "rows", "selected", "select", "Thread", "Search", "Inbox", "init_Chat", "__esmMin", "init_Inbox", "init_Thread", "init_Search", "chatOffline", "init_offline", "__esmMin", "item", "table", "row", "Witness_exports", "__export", "Witness", "twinLastDecline", "React", "useEffect", "useState", "Text", "View", "api", "SnapbackRefusal", "createExpoClient", "expoSqliteStore", "expoSqliteDriver", "SnapbackProvider", "witnessControl", "runSqliteDriverConformance", "url", "controlUrl", "client", "setClient", "media", "setMedia", "error", "setError", "live", "store", "selected", "subscriptions", "currentMedia", "viewNonce", "dispose", "sub", "control", "hooks", "args", "initial", "DATABASE", "persist", "config", "configuration", "chatOffline", "table", "row", "verb", "databaseState", "listFiles", "value", "asset", "once", "result", "resolve", "path", "uri", "served", "deadline", "file", "markerSwap", "entry", "entries", "answer", "rows", "threadBubbles", "searchHeld", "Chat", "MediaView", "init_Witness", "__esmMin", "init_MediaView", "init_offline", "init_log", "init_witness_state", "init_Chat", "init_MediaView", "init_witness_state", "init_Chat", "init_offline", "React", "lazy", "Suspense", "useEffect", "useState", "createExpoClient", "SnapbackProvider", "Text", "url", "witnessUrl", "Witness", "NormalApp", "client", "setClient", "error", "setError", "chatOffline", "Chat"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "snapback2",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Snapback 2 CLI (the platform binary ships in snapback2-<os>-<arch>).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./dist/client.d.ts",
|
|
15
|
+
"import": "./dist/client.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./react": {
|
|
18
|
+
"react-native": {
|
|
19
|
+
"types": "./dist/react-native.d.ts",
|
|
20
|
+
"import": "./dist/react-native.mjs"
|
|
21
|
+
},
|
|
22
|
+
"types": "./dist/react.d.ts",
|
|
23
|
+
"import": "./dist/react.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./test": {
|
|
26
|
+
"types": "./dist/test.d.ts",
|
|
27
|
+
"import": "./dist/test.mjs"
|
|
28
|
+
},
|
|
29
|
+
"./react-core": {
|
|
30
|
+
"types": "./dist/react-core.d.ts",
|
|
31
|
+
"import": "./dist/react-core.mjs"
|
|
32
|
+
},
|
|
33
|
+
"./react-native": {
|
|
34
|
+
"types": "./dist/react-native.d.ts",
|
|
35
|
+
"import": "./dist/react-native.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./expo": {
|
|
38
|
+
"types": "./dist/expo.d.ts",
|
|
39
|
+
"import": "./dist/expo.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./expo/witness": {
|
|
42
|
+
"types": "./dist/expo/witness.d.ts",
|
|
43
|
+
"import": "./dist/expo/witness.mjs"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"bin": {
|
|
47
|
+
"snapback2": "bin/snapback2.mjs"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"bin",
|
|
51
|
+
"dist"
|
|
52
|
+
],
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "node build.mjs",
|
|
55
|
+
"prepack": "node build.mjs",
|
|
56
|
+
"test": "node build.mjs && env -u SNAPBACK_URL node --test --test-timeout=120000 tests/"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"typescript": "5.9.2"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"expo": ">=54",
|
|
63
|
+
"expo-file-system": ">=19",
|
|
64
|
+
"expo-video": ">=2",
|
|
65
|
+
"react-native": ">=0.75",
|
|
66
|
+
"react": ">=18",
|
|
67
|
+
"expo-sqlite": "^16",
|
|
68
|
+
"expo-secure-store": "^15"
|
|
69
|
+
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"expo": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"expo-file-system": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"expo-video": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"react-native": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"expo-sqlite": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"expo-secure-store": {
|
|
87
|
+
"optional": true
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@types/node": "^24",
|
|
92
|
+
"@types/react": "19.1.12",
|
|
93
|
+
"@types/react-dom": "19.1.9",
|
|
94
|
+
"esbuild": "0.25.9",
|
|
95
|
+
"expo": "54.0.37",
|
|
96
|
+
"expo-file-system": "19.0.24",
|
|
97
|
+
"expo-secure-store": "15.0.8",
|
|
98
|
+
"expo-sqlite": "16.0.10",
|
|
99
|
+
"expo-video": "3.0.16",
|
|
100
|
+
"jsdom": "^26.1.0",
|
|
101
|
+
"playwright-core": "1.55.1",
|
|
102
|
+
"react": "19.1.0",
|
|
103
|
+
"react-dom": "19.1.0",
|
|
104
|
+
"react-native": "0.81.5",
|
|
105
|
+
"react-test-renderer": "19.1.0",
|
|
106
|
+
"ws": "8.18.3"
|
|
107
|
+
},
|
|
108
|
+
"optionalDependencies": {
|
|
109
|
+
"snapback2-darwin-arm64": "0.0.1",
|
|
110
|
+
"snapback2-linux-x64": "0.0.1"
|
|
111
|
+
},
|
|
112
|
+
"repository": {
|
|
113
|
+
"type": "git",
|
|
114
|
+
"url": "git+https://github.com/expo/snapback.git",
|
|
115
|
+
"directory": "snapback2/packages/snapback2"
|
|
116
|
+
}
|
|
117
|
+
}
|