Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,207 changes: 1,094 additions & 1,113 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions test/fixtures/_shared/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ ZERO_LOG_LEVEL="debug"
ZERO_ADMIN_PASSWORD="password"

ZERO_MUTATE_FORWARD_COOKIES="true"
# ZERO_MUTATE_URL="http://localhost:3000/api/zero/mutate"
ZERO_MUTATE_URL="http://localhost:3000/api/zero/mutate"

ZERO_QUERY_FORWARD_COOKIES="true"
# ZERO_QUERY_URL="http://localhost:3000/api/zero/query"

# ZERO_AUTH_SECRET="secretkey"
ZERO_QUERY_URL="http://localhost:3000/api/zero/query"

ZERO_REPLICA_FILE="/tmp/zstart_replicas.db"
103 changes: 103 additions & 0 deletions test/fixtures/_shared/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import {
boolean,
createBuilder,
createSchema,
defineMutator,
defineMutators,
defineQueries,
defineQuery,
escapeLike,
number,
relationships,
string,
table,
} from '@rocicorp/zero'
import { z } from 'zod'

const user = table('user')
.columns({
Expand Down Expand Up @@ -73,3 +79,100 @@ declare module '@rocicorp/zero' {
}

export const zql = createBuilder(schema)

export interface ZeroContext {
userID: string | undefined
}

declare module '@rocicorp/zero' {
interface DefaultTypes {
context: ZeroContext
}
}

export const mutators = defineMutators({
message: {
insert: defineMutator(
z.object({
mediumID: z.string(),
body: z.string(),
id: z.string(),
timestamp: z.number(),
}),
async ({ tx, ctx: { userID }, args: { mediumID, body, id, timestamp } }) => {
if (!userID) {
throw new Error('You must be logged in to add messages')
}

return tx.mutate.message.insert({
senderID: userID,
mediumID,
body,
id,
timestamp,
})
},
),
update: defineMutator(
z.object({ id: z.string(), body: z.string() }),
async ({ tx, ctx: { userID }, args: { id, body } }) => {
const messageToEdit = await tx.run(
zql.message.where('id', id).one(),
)
if (!messageToEdit) {
throw new Error(`Message with id ${id} not found`)
}

if (messageToEdit.senderID !== userID) {
throw new Error(`You aren't allowed to edit this message`)
}

return tx.mutate.message.update({ id, body })
},
),
delete: defineMutator(
z.object({ id: z.string() }),
async ({ tx, ctx: { userID }, args: { id } }) => {
if (!userID) {
throw new Error('You must be logged in to delete')
}

return tx.mutate.message.delete({ id })
},
),
},
})

export const queries = defineQueries({
messages: {
all: defineQuery(() => zql.message),
filtered: defineQuery(
z.object({
filterUser: z.string().optional(),
filterText: z.string().optional(),
}),
({ args: { filterUser, filterText } }) => {
let filtered = zql.message
.related('medium', medium => medium.one())
.related('sender', sender => sender.one())
.orderBy('timestamp', 'desc')

if (filterUser) {
filtered = filtered.where('senderID', filterUser)
}

if (filterText) {
filtered = filtered.where('body', 'LIKE', `%${escapeLike(filterText)}%`)
}

return filtered
},
),
},
users: {
all: defineQuery(() => zql.user),
},
mediums: {
all: defineQuery(() => zql.medium),
},
})
4 changes: 3 additions & 1 deletion test/fixtures/_shared/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
name: zero-vue

services:
zstart_postgres:
image: postgres:18.3-alpine
Expand All @@ -23,7 +25,7 @@ services:
-c hot_standby=on
-c hot_standby_feedback=on
volumes:
- zstart_pgdata:/var/lib/postgresql/data
- zstart_pgdata:/var/lib/postgresql
- ./:/docker-entrypoint-initdb.d

volumes:
Expand Down
7 changes: 5 additions & 2 deletions test/fixtures/nuxt/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# NUXT_ZERO_AUTH_SECRET="secretkey"
# NUXT_PUBLIC_ZERO_CACHE_URL="http://localhost:4848"
NUXT_AUTH_SECRET="secretKey"
NUXT_PUBLIC_ZERO_CACHE_URL="http://localhost:4848"
NUXT_PUBLIC_ZERO_QUERY_URL="http://localhost:3000/api/zero/query"
NUXT_PUBLIC_ZERO_MUTATE_URL="http://localhost:3000/api/zero/mutate"
ZERO_UPSTREAM_DB="postgresql://user:password@127.0.0.1:5430/zstart"
18 changes: 12 additions & 6 deletions test/fixtures/nuxt/app/app.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script setup lang="ts">
import { useInterval } from '#fx/composables/use-interval'
import { randomMessage } from '#fx/db/data/test-data'
import { mutators, queries } from '#fx/db/schema'
import { formatDate } from '#fx/utils/date'
import { randInt } from '#fx/utils/rand'
import { mutators, queries } from '~/composables/zero'

const zero = useZero()
const { data: users } = useQuery(() => queries.users.all())
Expand All @@ -20,6 +20,7 @@ const { data: filteredMessages } = useQuery(() => queries.messages.filtered({
}))

const hasFilters = computed(() => filterUser.value || filterText.value)
const canAddMessages = computed(() => !!zero.value.userID && users.value.length > 0 && mediums.value.length > 0)

function deleteRandomMessage() {
if (allMessages.value.length === 0) {
Expand All @@ -32,6 +33,10 @@ function deleteRandomMessage() {
}

function addRandomMessage() {
if (!canAddMessages.value) {
return false
}

zero.value.mutate(mutators.message.insert(
randomMessage(users.value, mediums.value),
))
Expand Down Expand Up @@ -68,7 +73,7 @@ function handleAddAction() {
}

function handleRemoveAction(e: MouseEvent | TouchEvent) {
if (zero.value.userID === 'anon' && 'shiftKey' in e && !e.shiftKey) {
if (!zero.value.userID && 'shiftKey' in e && !e.shiftKey) {
// eslint-disable-next-line no-alert
alert('You must be logged in to delete. Hold shift to try anyway.')
return
Expand Down Expand Up @@ -106,7 +111,7 @@ function editMessage(e: MouseEvent, id: string, senderID: string, prev: string)

const jwt = useCookie('jwt')
async function toggleLogin() {
if (zero.value.userID === 'anon') {
if (!zero.value.userID) {
await $fetch('/api/login')
}
else {
Expand All @@ -115,14 +120,15 @@ async function toggleLogin() {
location.reload()
}

const user = computed(() => users.value.find(user => user.id === zero.value.userID)?.name ?? 'anon')
const user = computed(() => users.value.find(user => user.id === zero.value.userID)?.name)
</script>

<template>
<div>
<div class="controls">
<div>
<button
:disabled="!canAddMessages"
@mousedown="handleAddAction"
@mouseup="stopAction"
@mouseleave="stopAction"
Expand All @@ -143,9 +149,9 @@ const user = computed(() => users.value.find(user => user.id === zero.value.user
<em>(hold down buttons to repeat)</em>
</div>
<div :style="{ justifyContent: 'end' }">
{{ user === 'anon' ? '' : `Logged in as ${user}` }}
{{ user ? `Logged in as ${user}` : '' }}
<button @mousedown="toggleLogin">
{{ user === 'anon' ? 'Login' : 'Logout' }}
{{ user ? 'Logout' : 'Login' }}
</button>
</div>
</div>
Expand Down
110 changes: 5 additions & 105 deletions test/fixtures/nuxt/app/composables/zero.ts
Original file line number Diff line number Diff line change
@@ -1,121 +1,21 @@
import {
defineMutator,
defineMutators,
defineQueries,
defineQuery,
escapeLike,
} from '@rocicorp/zero'
import { decodeJwt } from 'jose'
import { createZeroComposables } from 'zero-vue'
import z from 'zod'

import { schema, zql } from '#fx/db/schema'

export interface ZeroContext {
userID: string
}

declare module '@rocicorp/zero' {
interface DefaultTypes {
context: ZeroContext
}
}

export const mutators = defineMutators({
message: {
insert: defineMutator(
z.object({
mediumID: z.string(),
body: z.string(),
id: z.string(),
timestamp: z.number(),
}),
async ({ tx, ctx: { userID }, args: { mediumID, body, id, timestamp } }) => {
return tx.mutate.message.insert({
senderID: userID,
mediumID,
body,
id,
timestamp,
})
},
),
update: defineMutator(
z.object({ id: z.string(), body: z.string() }),
async ({ tx, ctx: { userID }, args: { id, body } }) => {
const messageToEdit = await tx.run(
zql.message.where('id', id).one(),
)
if (!messageToEdit) {
throw new Error(`Message with id ${id} not found`)
}

if (messageToEdit.senderID !== userID) {
throw new Error(`You aren't allowed to edit this message`)
}

return tx.mutate.message.update({ id, body })
},
),
delete: defineMutator(
z.object({ id: z.string() }),
async ({ tx, ctx: { userID }, args: { id } }) => {
if (!userID) {
throw new Error('You must be logged in to delete')
}

return tx.mutate.message.delete({ id })
},
),
},
})

export const queries = defineQueries({
messages: {
all: defineQuery(() => zql.message),
filtered: defineQuery(
z.object({
filterUser: z.string().optional(),
filterText: z.string().optional(),
}),
({ args: { filterUser, filterText } }) => {
let filtered = zql.message
.related('medium', medium => medium.one())
.related('sender', sender => sender.one())
.orderBy('timestamp', 'desc')

if (filterUser) {
filtered = filtered.where('senderID', filterUser)
}

if (filterText) {
filtered = filtered.where('body', 'LIKE', `%${escapeLike(filterText)}%`)
}

return filtered
},
),
},
users: {
all: defineQuery(() => zql.user),
},
mediums: {
all: defineQuery(() => zql.medium),
},
})
import { mutators, schema } from '#fx/db/schema'

function createComposables() {
return createZeroComposables(() => {
const jwt = useCookie('jwt')
const decoded = jwt.value ? decodeJwt(jwt.value) : undefined
const userID = typeof decoded?.sub === 'string' ? decoded.sub : 'anon'
const userID = typeof decoded?.sub === 'string' ? decoded.sub : undefined
const config = useRuntimeConfig()

return {
userID,
auth: jwt.value || undefined,
context: { userID },
server: import.meta.client ? config.public.zero.cacheURL : undefined,
cacheURL: import.meta.client ? config.public.zero.cacheURL : undefined,
queryURL: config.public.zero.queryURL,
mutateURL: config.public.zero.mutateURL,
schema,
mutators,
kvStore: 'mem' as const,
Expand Down
13 changes: 9 additions & 4 deletions test/fixtures/nuxt/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export default defineNuxtConfig({
},
css: ['~/assets/index.css'],
runtimeConfig: {
zero: {
authSecret: '',
},
authSecret: '',
public: {
zero: {
cacheURL: '',
queryURL: '',
mutateURL: '',
},
},
},
Expand All @@ -28,7 +28,12 @@ export default defineNuxtConfig({
esbuildOptions: {
target: 'es2022',
},
include: ['@rocicorp/zero'],
include: [
'@rocicorp/zero',
'@rocicorp/zero/bindings',
'jose',
'zod',
],
},
},
})
Loading