@yottagraph-app/aether-instructions 1.1.15 → 1.1.17
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 +1 -1
- package/rules/pref.mdc +17 -8
- package/rules/server.mdc +72 -24
package/package.json
CHANGED
package/rules/pref.mdc
CHANGED
|
@@ -46,19 +46,23 @@ The backing store auto-initializes on first use — no need to call
|
|
|
46
46
|
|
|
47
47
|
## Local Development
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`undefined` for reads and silently skip writes.
|
|
52
|
-
|
|
49
|
+
KV credentials are only available in deployed builds (Vercel auto-injects
|
|
50
|
+
them at runtime). In local dev, `getRedis()` returns `null` and KV routes
|
|
51
|
+
return `undefined` for reads and silently skip writes. `Pref<T>` still
|
|
52
|
+
works with its default value but won't persist across page refreshes.
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
This is expected — push to `main` and test persistence on the deployed build.
|
|
55
|
+
|
|
56
|
+
**Auth dependency:** All `/api/kv/*` routes call `unsealCookie(event)` to
|
|
57
|
+
identify the user. In dev mode (no `NUXT_PUBLIC_AUTH0_CLIENT_SECRET` set),
|
|
58
|
+
this is bypassed automatically using `NUXT_PUBLIC_USER_NAME`. If you set an
|
|
59
|
+
Auth0 client secret without proper cookie setup, KV writes will silently
|
|
60
|
+
no-op.
|
|
57
61
|
|
|
58
62
|
## Direct API Alternative
|
|
59
63
|
|
|
60
64
|
If you prefer not to use the `Pref<T>` class, you can call the KV
|
|
61
|
-
routes directly:
|
|
65
|
+
routes directly from client-side code:
|
|
62
66
|
|
|
63
67
|
```typescript
|
|
64
68
|
// Read
|
|
@@ -73,6 +77,11 @@ await $fetch('/api/kv/write', {
|
|
|
73
77
|
});
|
|
74
78
|
```
|
|
75
79
|
|
|
80
|
+
These routes require the browser's auth cookie — they work from client-side
|
|
81
|
+
`$fetch` calls but not from server routes or external scripts. For
|
|
82
|
+
server-to-server KV access, use `getRedis()` from `server/utils/redis.ts`
|
|
83
|
+
directly.
|
|
84
|
+
|
|
76
85
|
## Feature-Scoped Preferences
|
|
77
86
|
|
|
78
87
|
Features should namespace preferences under the app's prefix:
|
package/rules/server.mdc
CHANGED
|
@@ -19,7 +19,8 @@ server/
|
|
|
19
19
|
│ ├── kv/ # KV (Upstash Redis) CRUD — read, write, delete, documents, status
|
|
20
20
|
│ └── avatar/[url].ts # Avatar image proxy
|
|
21
21
|
└── utils/
|
|
22
|
-
├── redis.ts # Upstash Redis client init
|
|
22
|
+
├── redis.ts # Upstash Redis client (lazy-init from KV_REST_API_URL)
|
|
23
|
+
├── neon.ts # Neon Postgres client (lazy-init from DATABASE_URL) — present if Neon provisioned
|
|
23
24
|
└── cookies.ts # Cookie handling (@hapi/iron)
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -67,35 +68,33 @@ if (redis) {
|
|
|
67
68
|
|
|
68
69
|
Returns `null` if KV is not configured (env vars missing). Always check.
|
|
69
70
|
|
|
71
|
+
For client-side preferences, use `usePrefsStore()` and `Pref<T>` instead of
|
|
72
|
+
calling KV routes directly — see the `pref` rule.
|
|
73
|
+
|
|
70
74
|
## Neon Postgres
|
|
71
75
|
|
|
72
|
-
If
|
|
73
|
-
`
|
|
74
|
-
|
|
75
|
-
init. Check `.env` for `DATABASE_URL` — if present, Postgres is ready to use.
|
|
76
|
+
If `DATABASE_URL` is in `.env`, Postgres is ready to use. The project init
|
|
77
|
+
scaffolds `server/utils/neon.ts` and installs `@neondatabase/serverless`
|
|
78
|
+
automatically when a Neon database is detected.
|
|
76
79
|
|
|
77
|
-
###
|
|
80
|
+
### How to check
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
`
|
|
82
|
+
- `DATABASE_URL` present in `.env` → Postgres is connected
|
|
83
|
+
- `server/utils/neon.ts` exists → utility is scaffolded
|
|
84
|
+
- If either is missing, the project wasn't provisioned with Neon (add it
|
|
85
|
+
from the Broadchurch Portal, then re-run `node init-project.js`)
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
**Local dev:** `DATABASE_URL` is not yet available for local development.
|
|
88
|
+
`getDb()` returns `null` when the credential is missing or invalid. Write
|
|
89
|
+
your server routes to handle this gracefully (return a "database not
|
|
90
|
+
configured" error or empty state). Push to `main` to test with a real
|
|
91
|
+
database on the deployed build, where credentials are auto-injected.
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
### Usage
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!url) return null;
|
|
91
|
-
_sql = neon(url);
|
|
92
|
-
return _sql;
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Install the driver: `npm install @neondatabase/serverless`
|
|
97
|
-
|
|
98
|
-
### Usage in server routes
|
|
95
|
+
`server/utils/neon.ts` exports `getDb()` (same lazy-init pattern as
|
|
96
|
+
`getRedis()` in `redis.ts`): returns a query function or `null` if
|
|
97
|
+
`DATABASE_URL` is not set.
|
|
99
98
|
|
|
100
99
|
```typescript
|
|
101
100
|
import { getDb } from '~/server/utils/neon';
|
|
@@ -109,13 +108,62 @@ export default defineEventHandler(async () => {
|
|
|
109
108
|
});
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
The driver uses tagged template literals for automatic SQL injection
|
|
111
|
+
The Neon driver uses tagged template literals for automatic SQL injection
|
|
113
112
|
protection — `await sql\`SELECT * FROM notes WHERE id = ${id}\`` is safe.
|
|
114
113
|
No ORM, no query builder, no connection pool setup needed.
|
|
115
114
|
|
|
115
|
+
### Creating tables
|
|
116
|
+
|
|
117
|
+
There is no migrations framework. Use `CREATE TABLE IF NOT EXISTS` directly
|
|
118
|
+
in a setup route or at the top of a route that needs the table:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const sql = getDb()!;
|
|
122
|
+
await sql`CREATE TABLE IF NOT EXISTS notes (
|
|
123
|
+
id SERIAL PRIMARY KEY,
|
|
124
|
+
content TEXT NOT NULL,
|
|
125
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
126
|
+
)`;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
For simple apps, putting the `CREATE TABLE IF NOT EXISTS` in each route that
|
|
130
|
+
uses the table is fine — it's a no-op after the first call. For more complex
|
|
131
|
+
schemas, create a `server/api/db/setup.post.ts` route that initializes all
|
|
132
|
+
tables.
|
|
133
|
+
|
|
134
|
+
### If `server/utils/neon.ts` doesn't exist
|
|
135
|
+
|
|
136
|
+
Create it manually (or re-run init):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm install @neondatabase/serverless
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// server/utils/neon.ts
|
|
144
|
+
import { neon, type NeonQueryFunction } from '@neondatabase/serverless';
|
|
145
|
+
|
|
146
|
+
let _sql: NeonQueryFunction | null = null;
|
|
147
|
+
|
|
148
|
+
export function isDbConfigured(): boolean {
|
|
149
|
+
return Boolean(process.env.DATABASE_URL);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function getDb(): NeonQueryFunction | null {
|
|
153
|
+
if (_sql) return _sql;
|
|
154
|
+
const url = process.env.DATABASE_URL;
|
|
155
|
+
if (!url) return null;
|
|
156
|
+
_sql = neon(url);
|
|
157
|
+
return _sql;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
116
161
|
## Key Differences from Client-Side Code
|
|
117
162
|
|
|
118
163
|
- Server routes run on the server (Node.js), not in the browser
|
|
119
164
|
- They have access to Redis, Neon Postgres, secrets, and server-only APIs
|
|
120
165
|
- They do NOT have access to Vue composables, Vuetify, or any client-side code
|
|
121
166
|
- Use `defineEventHandler`, not Vue component patterns
|
|
167
|
+
|
|
168
|
+
See `architecture` rule for the full data architecture overview, `pref` rule
|
|
169
|
+
for client-side KV preferences.
|