// Streaming subscription of the tenant's projects. Every change to `projects` // for the caller's tenant lands as a delta — the frontend upgrades its first // paint to live with `useSubscription('projects.list')`. The runtime // AND-merges the tenant predicate (the table carries `tenant()`), so this never // leaks across tenants — no manual `eq('tenantId', ...)` needed. import { defineQuery } from '@voltro/protocol' import { Schema } from 'effect' export const listProjects = defineQuery({ name: 'projects.list', // Members only. `tenant()` decides WHICH rows; this decides WHO may open the // subscription at all — and the guard is re-checked on EVERY delivery, so a // member whose access is revoked mid-session stops receiving rows instead of // keeping a live feed open on a grant that has expired. // // Satisfiable by this app as shipped: sign in → `voltroPasswordStrategy` // matches the cookie → `auth.resolveScopes` (authz.ts) grants // `projects:read`. Anonymous → no scopes → refused. guards: [{ scope: 'projects:read' }], input: Schema.Struct({}), output: Schema.Array( Schema.Struct({ id: Schema.String, name: Schema.String, tenantId: Schema.String, createdAt: Schema.Date, }), ), })