robodev 0.12.0 → 0.13.0

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.
Files changed (155) hide show
  1. package/package.json +1 -1
  2. package/src/emit-starter.test.ts +14 -0
  3. package/src/index.ts +12 -4
  4. package/templates/auth-chat/.config/local.spa.template.yml +27 -0
  5. package/templates/auth-chat/.oxlint-base.json +29 -0
  6. package/templates/auth-chat/.oxlintrc.json +78 -0
  7. package/templates/auth-chat/.rulesync/rules/frontend-api-boundary.md +36 -0
  8. package/templates/auth-chat/.rulesync/rules/frontend-component-structure.md +38 -0
  9. package/templates/auth-chat/.rulesync/rules/frontend-forms.md +32 -0
  10. package/templates/auth-chat/.rulesync/rules/frontend-notifications.md +24 -0
  11. package/templates/auth-chat/.rulesync/rules/frontend-povio-components.md +42 -0
  12. package/templates/auth-chat/.rulesync/rules/frontend-povio-ui.md +45 -0
  13. package/templates/auth-chat/.rulesync/rules/frontend-query-autocomplete.md +29 -0
  14. package/templates/auth-chat/.rulesync/rules/frontend-tables-and-lists.md +34 -0
  15. package/templates/auth-chat/.rulesync/rules/frontend-translations.md +26 -0
  16. package/templates/auth-chat/.rulesync/rules/project-overview.md +32 -0
  17. package/templates/auth-chat/.rulesync/rules/robodev-api.md +30 -0
  18. package/templates/auth-chat/.rulesync/rules/robodev-auth.md +40 -0
  19. package/templates/auth-chat/.rulesync/rules/robodev-database.md +22 -0
  20. package/templates/auth-chat/.rulesync/rules/role-based-app-structure.md +36 -0
  21. package/templates/auth-chat/.rulesync/skills/media-feature/SKILL.md +21 -0
  22. package/templates/auth-chat/.rulesync/skills/povio-ui-styling/SKILL.md +208 -0
  23. package/templates/auth-chat/.rulesync/skills/povio-ui-styling/agents/openai.yaml +4 -0
  24. package/templates/auth-chat/.rulesync/skills/robodev-api-route/SKILL.md +13 -0
  25. package/templates/auth-chat/.rulesync/skills/robodev-table/SKILL.md +12 -0
  26. package/templates/auth-chat/README.md +26 -7
  27. package/templates/auth-chat/api/invite.ts +43 -0
  28. package/templates/auth-chat/apps/fe/index.html +24 -0
  29. package/templates/auth-chat/apps/fe/openapi-codegen.config.ts +37 -0
  30. package/templates/auth-chat/apps/fe/package.json +82 -0
  31. package/templates/auth-chat/apps/fe/public/apple-touch-icon.png +0 -0
  32. package/templates/auth-chat/apps/fe/public/favicon-96x96.png +0 -0
  33. package/templates/auth-chat/apps/fe/public/favicon.ico +0 -0
  34. package/templates/auth-chat/apps/fe/public/favicon.svg +3 -0
  35. package/templates/auth-chat/apps/fe/public/site.webmanifest +21 -0
  36. package/templates/auth-chat/apps/fe/public/web-app-manifest-192x192.png +0 -0
  37. package/templates/auth-chat/apps/fe/public/web-app-manifest-512x512.png +0 -0
  38. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Bold.otf +0 -0
  39. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Medium.otf +0 -0
  40. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Regular.otf +0 -0
  41. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Semibold.otf +0 -0
  42. package/templates/auth-chat/apps/fe/src/assets/locales/en/translation.json +202 -0
  43. package/templates/auth-chat/apps/fe/src/assets/locales/sl/translation.json +202 -0
  44. package/templates/auth-chat/apps/fe/src/clients/app-rest-client.ts +15 -0
  45. package/templates/auth-chat/apps/fe/src/clients/auth-token-store.ts +101 -0
  46. package/templates/auth-chat/apps/fe/src/clients/rest/app-error-handler.ts +31 -0
  47. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/authorization-header.interceptor.ts +15 -0
  48. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/refresh-token.interceptor.ts +37 -0
  49. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/response.interceptor.ts +17 -0
  50. package/templates/auth-chat/apps/fe/src/components/404.tsx +18 -0
  51. package/templates/auth-chat/apps/fe/src/components/features/auth/AuthBrandPanel.tsx +60 -0
  52. package/templates/auth-chat/apps/fe/src/components/features/auth/AuthLayout.tsx +23 -0
  53. package/templates/auth-chat/apps/fe/src/components/features/auth/LoginPage.tsx +111 -0
  54. package/templates/auth-chat/apps/fe/src/components/features/auth/RegisterPage.tsx +143 -0
  55. package/templates/auth-chat/apps/fe/src/components/features/chat/ChatPage.tsx +105 -0
  56. package/templates/auth-chat/apps/fe/src/components/features/chat/InviteForm.tsx +84 -0
  57. package/templates/auth-chat/apps/fe/src/components/features/profile/ProfilePage.tsx +96 -0
  58. package/templates/auth-chat/apps/fe/src/components/googleAnalytics/GoogleAnalytics.tsx +35 -0
  59. package/templates/auth-chat/apps/fe/src/components/layout/AppLayout.tsx +19 -0
  60. package/templates/auth-chat/apps/fe/src/components/layout/app-header/AppHeader.tsx +91 -0
  61. package/templates/auth-chat/apps/fe/src/components/layout/app-header/MobileNavigation.tsx +51 -0
  62. package/templates/auth-chat/apps/fe/src/components/layout/app-header/NavLink.tsx +29 -0
  63. package/templates/auth-chat/apps/fe/src/components/shared/branding/BrandLogo.tsx +13 -0
  64. package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorFallback.tsx +43 -0
  65. package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorText.tsx +20 -0
  66. package/templates/auth-chat/apps/fe/src/components/shared/error/NotFound.tsx +36 -0
  67. package/templates/auth-chat/apps/fe/src/components/shared/forms/RequiredLabel.tsx +21 -0
  68. package/templates/auth-chat/apps/fe/src/components/shared/forms/RowInputWrapper.tsx +50 -0
  69. package/templates/auth-chat/apps/fe/src/components/shared/head/AppHead.tsx +32 -0
  70. package/templates/auth-chat/apps/fe/src/components/shared/head/DefaultAppHead.tsx +34 -0
  71. package/templates/auth-chat/apps/fe/src/components/shared/layout/LoadingState.tsx +9 -0
  72. package/templates/auth-chat/apps/fe/src/components/shared/layout/ThinPageWrapper.tsx +5 -0
  73. package/templates/auth-chat/apps/fe/src/components/shared/page/PageHeader.tsx +69 -0
  74. package/templates/auth-chat/apps/fe/src/components/shared/ui/Card.tsx +60 -0
  75. package/templates/auth-chat/apps/fe/src/components/shared/ui/GoogleLoginButton.tsx +19 -0
  76. package/templates/auth-chat/apps/fe/src/components/shared/ui/RequiredLabel.tsx +22 -0
  77. package/templates/auth-chat/apps/fe/src/components/shared/ui/TableActions.tsx +22 -0
  78. package/templates/auth-chat/apps/fe/src/config/app.config.ts +35 -0
  79. package/templates/auth-chat/apps/fe/src/config/i18n.ts +43 -0
  80. package/templates/auth-chat/apps/fe/src/config/inits/a11y.ts +14 -0
  81. package/templates/auth-chat/apps/fe/src/config/inits/logger.ts +7 -0
  82. package/templates/auth-chat/apps/fe/src/config/inits/sentry.ts +21 -0
  83. package/templates/auth-chat/apps/fe/src/config/jwt.config.ts +2 -0
  84. package/templates/auth-chat/apps/fe/src/config/query.config.ts +20 -0
  85. package/templates/auth-chat/apps/fe/src/hooks/useAuth.ts +5 -0
  86. package/templates/auth-chat/apps/fe/src/main.tsx +52 -0
  87. package/templates/auth-chat/apps/fe/src/pages/(guest)/login.tsx +23 -0
  88. package/templates/auth-chat/apps/fe/src/pages/(guest)/register.tsx +23 -0
  89. package/templates/auth-chat/apps/fe/src/pages/(guest)/route.tsx +18 -0
  90. package/templates/auth-chat/apps/fe/src/pages/(private)/index.tsx +23 -0
  91. package/templates/auth-chat/apps/fe/src/pages/(private)/profile.tsx +23 -0
  92. package/templates/auth-chat/apps/fe/src/pages/(private)/route.tsx +18 -0
  93. package/templates/auth-chat/apps/fe/src/pages/(public)/auth.tsx +37 -0
  94. package/templates/auth-chat/apps/fe/src/pages/(public)/route.tsx +9 -0
  95. package/templates/auth-chat/apps/fe/src/pages/__root.tsx +164 -0
  96. package/templates/auth-chat/apps/fe/src/providers/AppErrorBoundary.tsx +10 -0
  97. package/templates/auth-chat/apps/fe/src/providers/OpenApiRuntimeProvider.tsx +31 -0
  98. package/templates/auth-chat/apps/fe/src/providers/index.tsx +44 -0
  99. package/templates/auth-chat/apps/fe/src/providers/jwt.provider.tsx +95 -0
  100. package/templates/auth-chat/apps/fe/src/routeTree.gen.ts +225 -0
  101. package/templates/auth-chat/apps/fe/src/styles/base.css +103 -0
  102. package/templates/auth-chat/apps/fe/src/styles/fonts/fonts.tsx +10 -0
  103. package/templates/auth-chat/apps/fe/src/styles/fonts/general-sans.css +31 -0
  104. package/templates/auth-chat/apps/fe/src/styles/globals.css +28 -0
  105. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/button.override.ts +536 -0
  106. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/checkbox.override.ts +71 -0
  107. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/input.override.ts +252 -0
  108. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/label.override.ts +91 -0
  109. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/modal.override.ts +57 -0
  110. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/radio.override.ts +46 -0
  111. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/table.override.ts +104 -0
  112. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/tag.override.ts +66 -0
  113. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/typography.override.ts +115 -0
  114. package/templates/auth-chat/apps/fe/src/styles/overrides/outline.clsx.ts +10 -0
  115. package/templates/auth-chat/apps/fe/src/styles/overrides/uiOverrides.override.ts +60 -0
  116. package/templates/auth-chat/apps/fe/src/styles/theme.css +2177 -0
  117. package/templates/auth-chat/apps/fe/src/types/i18next.d.ts +12 -0
  118. package/templates/auth-chat/apps/fe/src/types/table.d.ts +17 -0
  119. package/templates/auth-chat/apps/fe/src/types/ui.d.ts +26 -0
  120. package/templates/auth-chat/apps/fe/src/types/vite-env.d.ts +20 -0
  121. package/templates/auth-chat/apps/fe/src/utils/date.utils.ts +17 -0
  122. package/templates/auth-chat/apps/fe/src/utils/number.utils.ts +12 -0
  123. package/templates/auth-chat/apps/fe/src/utils/string.utils.ts +5 -0
  124. package/templates/auth-chat/apps/fe/src/vite-env.d.ts +1 -0
  125. package/templates/auth-chat/apps/fe/tsconfig.app.json +32 -0
  126. package/templates/auth-chat/apps/fe/tsconfig.json +9 -0
  127. package/templates/auth-chat/apps/fe/tsconfig.node.json +31 -0
  128. package/templates/auth-chat/apps/fe/vite.config.ts +94 -0
  129. package/templates/auth-chat/openapi.json +1016 -0
  130. package/templates/auth-chat/oxfmt.config.js +23 -0
  131. package/templates/auth-chat/package.json +6 -13
  132. package/templates/auth-chat/rulesync.jsonc +18 -0
  133. package/templates/auth-chat/tsconfig.json +2 -4
  134. package/templates/backend/README.md +2 -2
  135. package/templates/backend/api/_lib.ts +359 -0
  136. package/templates/backend/api/aliens/labels.ts +19 -0
  137. package/templates/backend/api/files/presigned-url.ts +48 -0
  138. package/templates/backend/api/files/upload.ts +29 -0
  139. package/templates/backend/api/planets/[id]/like.ts +36 -0
  140. package/templates/backend/api/planets/[id].ts +56 -13
  141. package/templates/backend/api/planets/paginate.ts +36 -0
  142. package/templates/backend/api/planets.ts +52 -44
  143. package/templates/backend/database.ts +33 -13
  144. package/templates/backend/package.json +1 -1
  145. package/templates/catalog.json +2 -2
  146. package/templates/empty/package.json +1 -1
  147. package/templates/space/package.json +1 -1
  148. package/templates/auth-chat/index.html +0 -201
  149. package/templates/auth-chat/src/main.tsx +0 -282
  150. package/templates/auth-chat/vite.config.ts +0 -7
  151. package/templates/backend/api/health.ts +0 -7
  152. package/templates/backend/api/launch.ts +0 -29
  153. package/templates/backend/api/me.ts +0 -14
  154. package/templates/backend/api/motto.ts +0 -9
  155. package/templates/backend/api/rockets.ts +0 -43
@@ -1,282 +0,0 @@
1
- import { StrictMode, useEffect, useRef, useState, type FormEvent } from "react";
2
- import { createRoot } from "react-dom/client";
3
- import { createClient, type AuthUser } from "@robodev-ai/client";
4
-
5
- type Message = {
6
- id: string;
7
- userId: string;
8
- authorName: string | null;
9
- authorEmail: string;
10
- body: string;
11
- createdAt: string | null;
12
- };
13
-
14
- function projectApiUrl(): string {
15
- const fromEnv = import.meta.env.VITE_API_URL;
16
- if (typeof fromEnv === "string" && fromEnv.trim()) return fromEnv.replace(/\/+$/, "");
17
- if (typeof window !== "undefined") return window.location.origin;
18
- return "";
19
- }
20
-
21
- const apiUrl = projectApiUrl();
22
- const api = createClient({ url: apiUrl });
23
-
24
- function authorLabel(user: { name?: string | null; email: string }) {
25
- return user.name?.trim() || user.email;
26
- }
27
-
28
- function App() {
29
- const [mode, setMode] = useState<"signin" | "signup">("signin");
30
- const [user, setUser] = useState<AuthUser | null>(null);
31
- const [ready, setReady] = useState(false);
32
- const [error, setError] = useState<string | null>(null);
33
- const [pending, setPending] = useState(false);
34
- const [email, setEmail] = useState("");
35
- const [password, setPassword] = useState("");
36
- const [name, setName] = useState("");
37
- const [messages, setMessages] = useState<Message[]>([]);
38
- const [draft, setDraft] = useState("");
39
- const [unauthStatus, setUnauthStatus] = useState<string | null>(null);
40
- const listRef = useRef<HTMLDivElement>(null);
41
-
42
- async function refreshMessages() {
43
- const res = await api.fetch("/api/messages");
44
- if (!res.ok) throw new Error(await res.text());
45
- setMessages((await res.json()) as Message[]);
46
- }
47
-
48
- useEffect(() => {
49
- api.consumeTokenFromUrl();
50
- void api.auth
51
- .getUser()
52
- .then(({ user: next }) => setUser(next))
53
- .catch(() => setUser(null))
54
- .finally(() => setReady(true));
55
- }, []);
56
-
57
- useEffect(() => {
58
- if (!user) return;
59
- let cancelled = false;
60
- const load = () =>
61
- refreshMessages().catch((err: unknown) => {
62
- if (!cancelled) setError(err instanceof Error ? err.message : "Failed to load messages");
63
- });
64
- void load();
65
- const timer = window.setInterval(() => void load(), 2500);
66
- return () => {
67
- cancelled = true;
68
- window.clearInterval(timer);
69
- };
70
- }, [user]);
71
-
72
- useEffect(() => {
73
- listRef.current?.scrollTo({ top: listRef.current.scrollHeight });
74
- }, [messages]);
75
-
76
- useEffect(() => {
77
- void fetch(`${apiUrl}/api/messages`).then((res) => {
78
- setUnauthStatus(
79
- `${res.status} ${res.status === 401 ? "unauthorized (expected)" : res.statusText}`,
80
- );
81
- });
82
- }, []);
83
-
84
- async function onEmailAuth(event: FormEvent) {
85
- event.preventDefault();
86
- setPending(true);
87
- setError(null);
88
- try {
89
- if (mode === "signup") {
90
- await api.auth.signUp({ email, password, name: name.trim() || undefined });
91
- } else {
92
- await api.auth.signIn.email({ email, password });
93
- }
94
- const { user: next } = await api.auth.getUser();
95
- setUser(next);
96
- setPassword("");
97
- } catch (err) {
98
- setError(err instanceof Error ? err.message : "Auth failed");
99
- } finally {
100
- setPending(false);
101
- }
102
- }
103
-
104
- async function onGoogle() {
105
- setPending(true);
106
- setError(null);
107
- try {
108
- const start = new URL(`${api.url}/api/user/auth/google`);
109
- start.searchParams.set("redirect_uri", window.location.href);
110
- const probe = await fetch(start.toString(), { redirect: "manual" });
111
- if (probe.status === 503) {
112
- const body = (await probe.json().catch(() => ({}))) as { error?: string; message?: string };
113
- throw new Error(
114
- body.message ||
115
- `Google sign-in is not configured on Starbase (${body.error ?? "503"}). Set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and PUBLIC_URL.`,
116
- );
117
- }
118
- api.auth.signIn.google();
119
- } catch (err) {
120
- setError(err instanceof Error ? err.message : "Google sign-in failed");
121
- setPending(false);
122
- }
123
- }
124
-
125
- async function onSignOut() {
126
- await api.auth.signOut();
127
- setUser(null);
128
- setMessages([]);
129
- }
130
-
131
- async function onSend(event: FormEvent) {
132
- event.preventDefault();
133
- if (!draft.trim()) return;
134
- setPending(true);
135
- setError(null);
136
- try {
137
- const res = await api.fetch("/api/messages", {
138
- method: "POST",
139
- body: JSON.stringify({ body: draft.trim() }),
140
- });
141
- if (!res.ok) throw new Error(await res.text());
142
- setDraft("");
143
- await refreshMessages();
144
- } catch (err) {
145
- setError(err instanceof Error ? err.message : "Could not send");
146
- } finally {
147
- setPending(false);
148
- }
149
- }
150
-
151
- if (!ready) {
152
- return (
153
- <main>
154
- <p className="lede">Loading session…</p>
155
- </main>
156
- );
157
- }
158
-
159
- if (!user) {
160
- return (
161
- <main className="narrow">
162
- <p className="eyebrow">Robodev Auth</p>
163
- <h1>Auth chat</h1>
164
- <p className="lede">
165
- Sign in with email or Google. Protected APIs live at {apiUrl}. Unauthenticated GET
166
- /api/messages: {unauthStatus ?? "checking…"}.
167
- </p>
168
- {error ? <p className="error">{error}</p> : null}
169
-
170
- <div className="tabs">
171
- <button
172
- type="button"
173
- className={mode === "signin" ? "tab on" : "tab"}
174
- onClick={() => setMode("signin")}
175
- >
176
- Sign in
177
- </button>
178
- <button
179
- type="button"
180
- className={mode === "signup" ? "tab on" : "tab"}
181
- onClick={() => setMode("signup")}
182
- >
183
- Sign up
184
- </button>
185
- </div>
186
-
187
- <form onSubmit={onEmailAuth}>
188
- {mode === "signup" ? (
189
- <label>
190
- Name
191
- <input
192
- value={name}
193
- onChange={(event) => setName(event.target.value)}
194
- placeholder="Ada"
195
- />
196
- </label>
197
- ) : null}
198
- <label>
199
- Email
200
- <input
201
- type="email"
202
- required
203
- autoComplete="email"
204
- value={email}
205
- onChange={(event) => setEmail(event.target.value)}
206
- />
207
- </label>
208
- <label>
209
- Password
210
- <input
211
- type="password"
212
- required
213
- minLength={12}
214
- autoComplete={mode === "signup" ? "new-password" : "current-password"}
215
- value={password}
216
- onChange={(event) => setPassword(event.target.value)}
217
- />
218
- </label>
219
- <button type="submit" disabled={pending}>
220
- {mode === "signup" ? "Create account" : "Sign in"}
221
- </button>
222
- </form>
223
-
224
- <div className="or">or</div>
225
- <button className="google" type="button" onClick={onGoogle} disabled={pending}>
226
- Continue with Google
227
- </button>
228
- </main>
229
- );
230
- }
231
-
232
- return (
233
- <main className="chat">
234
- <header>
235
- <div>
236
- <p className="eyebrow">Signed in</p>
237
- <h1>{authorLabel(user)}</h1>
238
- <p className="lede">{user.email}</p>
239
- </div>
240
- <button className="ghost" type="button" onClick={() => void onSignOut()}>
241
- Sign out
242
- </button>
243
- </header>
244
- {error ? <p className="error">{error}</p> : null}
245
- <div className="transcript" ref={listRef}>
246
- {messages.length === 0 ? <p className="empty">No messages yet. Say hello.</p> : null}
247
- {messages.map((message) => {
248
- const mine = message.userId === user.id;
249
- return (
250
- <article key={message.id} className={mine ? "bubble mine" : "bubble"}>
251
- <div className="meta">
252
- {authorLabel({ name: message.authorName, email: message.authorEmail })}
253
- {message.createdAt
254
- ? ` · ${new Date(message.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`
255
- : ""}
256
- </div>
257
- <p>{message.body}</p>
258
- </article>
259
- );
260
- })}
261
- </div>
262
- <form className="composer" onSubmit={onSend}>
263
- <input
264
- required
265
- maxLength={2000}
266
- placeholder="Message the room…"
267
- value={draft}
268
- onChange={(event) => setDraft(event.target.value)}
269
- />
270
- <button type="submit" disabled={pending || !draft.trim()}>
271
- Send
272
- </button>
273
- </form>
274
- </main>
275
- );
276
- }
277
-
278
- createRoot(document.getElementById("root")!).render(
279
- <StrictMode>
280
- <App />
281
- </StrictMode>,
282
- );
@@ -1,7 +0,0 @@
1
- import react from "@vitejs/plugin-react";
2
- import { defineConfig } from "vite";
3
-
4
- export default defineConfig({
5
- plugins: [react()],
6
- server: { port: 5173 },
7
- });
@@ -1,7 +0,0 @@
1
- /** GET /api/health — liveness check for the deployed project host. */
2
- import { defineApi, z } from "@robodev-ai/sdk";
3
-
4
- export const get = defineApi({
5
- response: z.object({ ok: z.literal(true) }),
6
- handler: async () => ({ ok: true as const }),
7
- });
@@ -1,29 +0,0 @@
1
- /** POST /api/launch — marks a rocket as launched. */
2
- import { rockets } from "../database";
3
- import { defineApi, eq, z } from "@robodev-ai/sdk";
4
-
5
- const Rocket = z.object({
6
- id: z.string(),
7
- name: z.string(),
8
- destinationId: z.string(),
9
- launched: z.boolean(),
10
- });
11
-
12
- export const post = defineApi({
13
- body: z.object({
14
- id: z.string().uuid(),
15
- }),
16
- response: Rocket,
17
- handler: async ({ db, body }) => {
18
- const rows = await db
19
- .update(rockets)
20
- .set({ launched: true })
21
- .where(eq(rockets.id, body.id))
22
- .returning();
23
- const row = rows[0];
24
- if (!row) {
25
- throw new Error("Rocket not found");
26
- }
27
- return Rocket.parse(row);
28
- },
29
- });
@@ -1,14 +0,0 @@
1
- /** GET /api/me — current project user (Robodev Auth required). */
2
- import { defineApi, z } from "@robodev-ai/sdk";
3
-
4
- export const get = defineApi({
5
- auth: "required",
6
- response: z.object({
7
- user: z.object({
8
- id: z.string(),
9
- email: z.string(),
10
- name: z.string().nullable().optional(),
11
- }),
12
- }),
13
- handler: async ({ user }) => ({ user }),
14
- });
@@ -1,9 +0,0 @@
1
- /** GET /api/motto — plain-text envelope response. */
2
- import { defineApi } from "@robodev-ai/sdk";
3
-
4
- export const get = defineApi({
5
- handler: async () => ({
6
- contentType: "text/plain; charset=utf-8",
7
- body: "Ad astra",
8
- }),
9
- });
@@ -1,43 +0,0 @@
1
- /** GET/POST /api/rockets — list (optional `?destinationId=`) and create rockets. */
2
- import { rockets } from "../database";
3
- import { defineApi, eq, z } from "@robodev-ai/sdk";
4
-
5
- const Rocket = z.object({
6
- id: z.string(),
7
- name: z.string(),
8
- destinationId: z.string(),
9
- launched: z.boolean(),
10
- });
11
-
12
- export const get = defineApi({
13
- query: z.object({
14
- destinationId: z.string().optional(),
15
- }),
16
- response: z.array(Rocket),
17
- handler: async ({ db, query }) => {
18
- const rows = query.destinationId
19
- ? await db.select().from(rockets).where(eq(rockets.destinationId, query.destinationId))
20
- : await db.select().from(rockets);
21
- return Rocket.array().parse(rows);
22
- },
23
- });
24
-
25
- export const post = defineApi({
26
- body: z.object({
27
- name: z.string().min(1),
28
- destinationId: z.string().uuid(),
29
- launched: z.boolean().optional(),
30
- }),
31
- response: Rocket,
32
- handler: async ({ db, body }) => {
33
- const [row] = await db
34
- .insert(rockets)
35
- .values({
36
- name: body.name,
37
- destinationId: body.destinationId,
38
- launched: body.launched ?? false,
39
- })
40
- .returning();
41
- return Rocket.parse(row);
42
- },
43
- });