@hachej/boring-core 0.1.41 → 0.1.42

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 CHANGED
@@ -7,314 +7,63 @@
7
7
 
8
8
  </div>
9
9
 
10
- The foundation package for boring-ui apps: Postgres/Drizzle database schema, email/password auth (better-auth), config loader, Fastify HTTP app factory, and React frontend shell. Every child app imports core first.
10
+ The foundation package for boring-ui v2 apps: Postgres/Drizzle database, better-auth
11
+ (email/password, verification, password reset, magic links, optional Google), TOML+env
12
+ config loader, Fastify HTTP app factory, and a React frontend shell with auth/workspace
13
+ gating. Every child app imports core first; domain logic, agent runtime, and workspace UI
14
+ come from the sibling `@hachej/boring-*` packages.
11
15
 
12
16
  ```bash
13
17
  pnpm add @hachej/boring-core
14
18
  ```
15
19
 
16
- ---
17
-
18
- ## TL;DR
19
-
20
- **The Problem**: Building a multi-user agent-powered app means re-implementing auth, sessions, workspaces, invites, email flows, and an app shell every single time. These are the same across every deployment.
21
-
22
- **The Solution**: `@hachej/boring-core` provides a complete app skeleton — Postgres DB, better-auth with email verification + password reset + magic links, workspace membership with roles, email transport (Resend/SMTP/console), and a `<BoringApp>` React shell with auth pages. You bring the domain logic.
23
-
24
- ### Why Use @hachej/boring-core?
25
-
26
- | Feature | What It Does |
27
- |---------|--------------|
28
- | **Full auth suite** | Email/password + email verification + password reset + magic links (better-auth) |
29
- | **Workspace management** | Create, update, delete workspaces; member roles (owner/editor/viewer); invites |
30
- | **Fastify app factory** | Pre-wired with helmet, CORS, rate limiting, secret redaction, graceful shutdown |
31
- | **Drizzle + Postgres** | Ready-to-run schema for users, workspaces, members, invites, settings |
32
- | **Email transport** | Resend (default), SMTP, or console — pluggable via URL scheme |
33
- | **<BoringApp> shell** | Client-rendered React shell with auth gate, theme toggle, workspace switcher |
34
- | **Config loader** | TOML + env vars merged, Zod-validated, redacted for frontend |
20
+ ## Usage essentials
35
21
 
36
- ---
37
-
38
- ## Quick Example
22
+ Most apps use the composed `app/*` surfaces, which fuse core + workspace + agent:
39
23
 
40
24
  ```ts
41
- // Server — 4 lines to a full app
42
- import { createCoreApp, loadConfig } from "@hachej/boring-core/server"
43
-
44
- const config = await loadConfig()
45
- const app = await createCoreApp(config) // Fastify + DB + auth + routes
25
+ // server entry
26
+ import { createCoreWorkspaceAgentServer } from "@hachej/boring-core/app/server"
46
27
 
28
+ const app = await createCoreWorkspaceAgentServer({ plugins })
47
29
  await app.listen({ port: 3000 })
48
30
  ```
49
31
 
50
32
  ```tsx
51
- // Frontend — mount auth gate + workspace routing
52
- <BoringApp>
53
- <Route path="/workspace/:id" element={<WorkspaceRoute />} />
54
- <Route path="/settings" element={<Settings />} />
55
- </BoringApp>
56
- ```
57
-
58
- ```tsx
59
- // In your components — typed auth + workspace access
60
- const user = useUser()
61
- const workspace = useCurrentWorkspace()
62
- const role = useWorkspaceRole() // 'owner' | 'editor' | 'viewer'
63
- ```
64
-
65
- ---
66
-
67
- ## Design Philosophy
68
-
69
- 1. **Core owns persistence and identity** — DB tables, auth, sessions, workspaces, invites. Everything else injects stores via interfaces.
70
- 2. **One config source** — `boring.app.toml` + environment variables merged, Zod-validated at boot. No scattered config.
71
- 3. **Email flows are real, not stubs** — password reset, email verification, magic links, workspace invites — all shipped with React Email templates.
72
- 4. **Swap seams, not rewrites** — `AuthProvider`, `UserStore`, `WorkspaceStore` are interfaces. The default impl is Postgres; swap via `createCoreApp({ authProvider })`.
73
- 5. **Fail closed on auth** — config fetch failure throws a `ConfigFetchError` with retries. Users see "Cannot reach server" not a blank page.
74
-
75
- ---
76
-
77
- ## Installation
78
-
79
- ```bash
80
- # pnpm
81
- pnpm add @hachej/boring-core @hachej/boring-workspace
82
-
83
- # npm
84
- npm install @hachej/boring-core @hachej/boring-workspace
85
-
86
- # from source
87
- git clone https://github.com/hachej/boring-ui.git
88
- cd boring-ui && pnpm install
89
- pnpm --filter @hachej/boring-core build
90
- ```
91
-
92
- ### Dependencies
93
-
94
- Postgres is required for production. For dev, set `CORE_STORES=local` and core runs in-memory (state resets on restart).
95
-
96
- ---
97
-
98
- ## Quick Start
99
-
100
- ### 1. Set Environment
101
-
102
- ```bash
103
- # .env
104
- DATABASE_URL=postgres://user:pass@localhost:5432/myapp
105
- BETTER_AUTH_SECRET=<32-byte random hex>
106
- BETTER_AUTH_URL=http://localhost:3000
107
- WORKSPACE_SETTINGS_ENCRYPTION_KEY=<32-byte hex>
108
- MAIL_FROM=noreply@myapp.dev
109
- MAIL_TRANSPORT_URL=resend://re_xxxxxxxxxxxxxxxx
110
- ```
111
-
112
- ### 2. Create Config File
113
-
114
- ```toml
115
- # boring.app.toml
116
- [app]
117
- id = "my-app"
118
-
119
- [frontend.branding]
120
- name = "My App"
121
- logo = "/logo.svg"
122
-
123
- [features]
124
- invites_enabled = true
125
- invite_ttl_days = 7
126
- ```
127
-
128
- ### 3. Run Migrations
129
-
130
- ```bash
131
- pnpm drizzle-kit generate --config node_modules/@hachej/boring-core/drizzle.config.ts
132
- pnpm drizzle-kit migrate --config node_modules/@hachej/boring-core/drizzle.config.ts
133
- ```
134
-
135
- ### 4. Server Entry
136
-
137
- ```ts
138
- import { createCoreApp, loadConfig } from "@hachej/boring-core/server"
139
-
140
- const config = await loadConfig()
141
- const app = await createCoreApp(config)
142
-
143
- // add child-app routes
144
- app.get("/api/v1/my-thing", async () => ({ ok: true }))
145
-
146
- await app.listen({ port: config.port })
147
- ```
148
-
149
- ### 5. Frontend Entry
150
-
151
- ```tsx
33
+ // frontend entry
152
34
  import { createRoot } from "react-dom/client"
153
- import { BoringApp } from "@hachej/boring-core/front"
154
- import { Route } from "react-router-dom"
155
- import "@hachej/boring-core/theme.css"
35
+ import { CoreWorkspaceAgentFront } from "@hachej/boring-core/app/front"
36
+ import "@hachej/boring-core/app/front/styles.css"
156
37
 
157
38
  createRoot(document.getElementById("root")!).render(
158
- <BoringApp>
159
- <Route path="/" element={<Dashboard />} />
160
- </BoringApp>,
39
+ <CoreWorkspaceAgentFront apiBaseUrl="" chatEntryMode="chat-first" plugins={plugins} />,
161
40
  )
162
41
  ```
163
42
 
164
- ---
165
-
166
- ## Package Surfaces
43
+ For a core-only app (no agent/workspace), use `createCoreApp(config)` from
44
+ `@hachej/boring-core/server` and `CoreFront` from `@hachej/boring-core/front`.
167
45
 
168
- | Import | Environment | What You Get |
169
- |--------|-------------|--------------|
170
- | `@hachej/boring-core/server` | Node | `createCoreApp`, `loadConfig`, auth, stores, routes |
171
- | `@hachej/boring-core/server/db` | Node | Drizzle schema, migrations, store interfaces |
172
- | `@hachej/boring-core/front` | Browser | `<BoringApp>`, hooks, auth pages, components |
173
- | `@hachej/boring-core/shared` | Any | `User`, `Workspace`, `HttpError`, `ErrorCode` types |
174
- | `@hachej/boring-core/theme.css` | Browser | CSS theme tokens for the frontend shell |
175
- | `@hachej/boring-core/app/front` | Browser | App composition helpers (`WorkspaceAgentFront`, etc.) |
176
- | `@hachej/boring-core/app/server` | Node | App composition helpers (`createWorkspaceAgentApp`) |
46
+ ### Required environment (production)
177
47
 
178
- ---
48
+ `DATABASE_URL`, `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`,
49
+ `WORKSPACE_SETTINGS_ENCRYPTION_KEY`, `MAIL_FROM`, `MAIL_TRANSPORT_URL`
50
+ (`resend://…`, `smtp://…`, or `console://`), `CORS_ORIGINS`. Config is also read from
51
+ `boring.app.toml`. For dev without Postgres, set `CORE_STORES=local` (in-memory, resets
52
+ on restart; not supported by `createCoreWorkspaceAgentServer`).
179
53
 
180
- ## Configuration
54
+ Migrations live in `drizzle/`; run them with `drizzle-kit migrate` against
55
+ `drizzle.config.ts`.
181
56
 
182
- ### Environment Variables
57
+ ## Documentation
183
58
 
184
- | Variable | Required | Description |
185
- |----------|----------|-------------|
186
- | `DATABASE_URL` | Yes (prod) | Postgres connection string |
187
- | `BETTER_AUTH_SECRET` | Yes | 32-byte hex — signs session cookies |
188
- | `BETTER_AUTH_URL` | Yes | Public URL for OAuth callbacks |
189
- | `WORKSPACE_SETTINGS_ENCRYPTION_KEY` | Yes (prod) | 32-byte hex — encrypts workspace settings |
190
- | `MAIL_FROM` | Yes (prod) | Sender address for auth emails |
191
- | `MAIL_TRANSPORT_URL` | Yes (prod) | `resend://key`, `smtp://host`, or `console://` |
192
- | `CORE_STORES` | No | `postgres` (default) or `local` (in-memory dev) |
193
- | `CORS_ORIGINS` | Yes (prod) | Comma-separated allowlist |
194
- | `SEND_WELCOME_EMAIL` | No | Default `true` — suppress with `false` |
195
- | `SESSION_TTL_SECONDS` | No | Default 2,592,000 (30 days) |
196
-
197
- ---
198
-
199
- ## Architecture
200
-
201
- ```
202
- ┌──────────────────────┐
203
- │ Browser Client │
204
- │ /auth/* + /me + │
205
- │ /workspaces/* │
206
- └──────────┬───────────┘
207
- │ HTTP (typed, cookie auth)
208
- ┌──────────▼───────────┐
209
- │ Fastify App │
210
- │ ├── authHook (req.user)
211
- │ ├── helmet + CORS │
212
- │ ├── rate limits │
213
- │ ├── secret redaction│
214
- │ └── graceful shutdown
215
- └──────────┬───────────┘
216
-
217
- ┌──────────▼───────────┐
218
- │ better-auth │
219
- │ (sessions, email, │
220
- │ password reset) │
221
- └──────────┬───────────┘
222
-
223
- ┌──────────▼───────────┐
224
- │ Drizzle + Postgres │
225
- │ users, sessions, │
226
- │ workspaces, members, │
227
- │ invites, settings │
228
- └──────────────────────┘
229
- ```
230
-
231
- ### Error Handling Contract
232
-
233
- All errors flow through a single `setErrorHandler`:
234
-
235
- | Condition | Status | Code |
236
- |-----------|--------|------|
237
- | No/expired session | 401 | `unauthorized` |
238
- | Insufficient role | 403 | `forbidden` / `not_member` |
239
- | Zod validation fail | 400 | `validation_failed` |
240
- | Rate limited | 429 | `rate_limited` + `Retry-After` |
241
- | DB ping fails | 503 | `db_unavailable` |
242
- | Everything else | 500 | `internal_error` |
243
-
244
- Every response includes `{ error, code, message, requestId }`. Client-side `apiFetch` parses this into `HttpError` instances.
245
-
246
- ---
247
-
248
- ## How @hachej/boring-core Compares
249
-
250
- | Feature | @hachej/boring-core | Supabase + custom | Firebase | Roll your own |
251
- |---------|---------------------|-------------------|----------|---------------|
252
- | Auth flows | ✅ email + reset + magic link | ✅ OAuth only | ✅ OAuth/phone | Weeks to build |
253
- | Workspaces + invites | ✅ owner/editor/viewer roles | ❌ Custom tables | ❌ Custom rules | ~1 week |
254
- | Email templates | ✅ 5 React Email templates | ❌ You write them | ❌ SendGrid setup | ~3 days |
255
- | App shell | ✅ `<BoringApp>` + hooks | ❌ DIY | ❌ DIY | ~1 week |
256
- | Rate limiting | ✅ pre-wired routes | ❌ Edge functions | ⚠️ Cloud rules | ~2 days |
257
- | Config validation | ✅ TOML + env + Zod | ❌ dotenv only | ⚠️ Remote config | Custom |
258
-
259
- **When to use @hachej/boring-core:**
260
- - Building a multi-user app around an AI agent
261
- - You need auth + workspaces + invites in days, not weeks
262
- - You're deploying to Fly.io, Render, Railway, or any Postgres-capable host
263
-
264
- **When it might not fit:**
265
- - You need server-side rendering (client-rendered only)
266
- - You want SQLite (Postgres-only with Drizzle)
267
- - You need Google/Apple/Discord OAuth (planned for v1.x)
268
- - You need billing/Stripe integration (future `@boring/cloud` package)
269
-
270
- ---
271
-
272
- ## Troubleshooting
273
-
274
- | Error | Cause | Fix |
275
- |-------|-------|-----|
276
- | `ConfigValidationError` at boot | Missing required env var | Check `.env` has all required vars |
277
- | `config_fetch_failed` in browser | API server not reachable | Verify `BETTER_AUTH_URL` matches |
278
- | `mail_disabled` warning at boot | `MAIL_FROM` not set | Set `MAIL_TRANSPORT_URL=console://` for dev |
279
- | `unauthorized` on `/api/v1/me` | No session cookie | Check `BETTER_AUTH_URL` and `CORS_ORIGINS` |
280
- | `db_unavailable` on `/health` | Postgres can't connect | Verify `DATABASE_URL` and network access |
281
-
282
- ---
283
-
284
- ## Limitations
285
-
286
- - **Postgres only** — No SQLite/libsql support in v1.
287
- - **Client-rendered only** — `<BoringApp>` mounts client-side. No SSR.
288
- - **GitHub OAuth deferred** — Planned for v1.x, bundled with agent's GitHub App install.
289
- - **No billing** — Stripe integration planned for `@boring/cloud` package.
290
- - **In-memory stores are dev-only** — `CORE_STORES=local` resets on restart. Not for production.
291
- - **Partial swap seams** — `AuthProvider` is swappable, but the React auth surfaces (`useSession`, sign-in pages) are better-auth-shaped.
292
-
293
- ---
294
-
295
- ## FAQ
296
-
297
- **Q: Can I use this without Postgres?**
298
- A: In dev, yes — set `CORE_STORES=local`. State is in-memory and resets on restart. For production, Postgres is required.
299
-
300
- **Q: How do I add Google/Discord OAuth?**
301
- A: better-auth supports these out of the box. Add the provider config to `createAuth()` in the core source. Official v1.x support planned.
302
-
303
- **Q: Can I swap better-auth for Clerk/Neon?**
304
- A: The `AuthProvider` interface is designed as a swap seam. You'll need to re-implement the React auth surfaces (`SignInPage`, `useSession`, etc.) and preserve the `users.id` continuity invariant.
305
-
306
- **Q: How do email templates work?**
307
- A: Five React Email components (`VerifyEmail`, `ResetPassword`, `MagicLink`, `WorkspaceInvite`, `Welcome`) rendered via `@react-email/render`. CSS is inlined. Swap them by providing your own mail transport.
308
-
309
- **Q: What's the difference between `@hachej/boring-core/server` and `@hachej/boring-core/server/db`?**
310
- A: `server` includes the full Fastify app, routes, auth, and stores. `server/db` is the Drizzle schema + connection + store interfaces only — useful for migration tooling and type-only imports.
59
+ See [`docs/README.md`](./docs/README.md) for the architecture overview, public API
60
+ surface, key abstractions, and links to the gating, plugin, and deployment docs. The
61
+ reference app is [`apps/full-app`](../../apps/full-app/).
311
62
 
312
63
  ---
313
64
 
314
65
  *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
315
66
 
316
- ---
317
-
318
67
  ## License
319
68
 
320
69
  MIT
@@ -8,7 +8,7 @@ import {
8
8
  useSignIn,
9
9
  useSignUp,
10
10
  useWorkspaceRouteStatus
11
- } from "../../chunk-WYTCJ5WL.js";
11
+ } from "../../chunk-MLTJKZL4.js";
12
12
  import "../../chunk-HYNKZSTF.js";
13
13
  import "../../chunk-H5KU6R6Y.js";
14
14
  import "../../chunk-MLKGABMK.js";
@@ -2180,7 +2180,7 @@ import {
2180
2180
  Label as Label7,
2181
2181
  useToast
2182
2182
  } from "@hachej/boring-ui-kit";
2183
- import { Check as Check2, ChevronsUpDown, LayoutGrid, Plus, Settings as Settings2 } from "lucide-react";
2183
+ import { ChevronsUpDown, LayoutGrid, Plus, Settings as Settings2 } from "lucide-react";
2184
2184
  import { useNavigate as useNavigate3 } from "react-router-dom";
2185
2185
  import { z as z6 } from "zod";
2186
2186
  import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -2219,6 +2219,13 @@ function hrefForWorkspace(prefix, workspaceId, suffix = "") {
2219
2219
  function workspaceInitial(name) {
2220
2220
  return (name.trim()[0] ?? "W").toUpperCase();
2221
2221
  }
2222
+ function OpenInNewTabIcon({ className }) {
2223
+ return /* @__PURE__ */ jsxs9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2224
+ /* @__PURE__ */ jsx17("path", { d: "M15 3h6v6" }),
2225
+ /* @__PURE__ */ jsx17("path", { d: "M10 14 21 3" }),
2226
+ /* @__PURE__ */ jsx17("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
2227
+ ] });
2228
+ }
2222
2229
  function WorkspaceSwitcher({
2223
2230
  appTitle = "Boring",
2224
2231
  workspacePathPrefix = "/workspace"
@@ -2293,6 +2300,9 @@ function WorkspaceSwitcher({
2293
2300
  }
2294
2301
  }
2295
2302
  const switcherLabel = currentWorkspace?.name ?? "Select workspace";
2303
+ function openWorkspaceInNewTab(workspaceId) {
2304
+ window.open(hrefForWorkspace(workspacePathPrefix, workspaceId), "_blank", "noopener,noreferrer");
2305
+ }
2296
2306
  return /* @__PURE__ */ jsxs9(Fragment4, { children: [
2297
2307
  workspaces.length === 0 ? /* @__PURE__ */ jsxs9(
2298
2308
  Button11,
@@ -2352,21 +2362,43 @@ function WorkspaceSwitcher({
2352
2362
  ] }) }),
2353
2363
  /* @__PURE__ */ jsx17("div", { className: "max-h-72 overflow-y-auto pr-1", children: workspaces.map((workspace) => {
2354
2364
  const isCurrent = currentWorkspace?.id === workspace.id;
2355
- return /* @__PURE__ */ jsxs9(
2356
- DropdownMenuItem2,
2357
- {
2358
- "aria-label": workspace.name,
2359
- "data-current": isCurrent ? "true" : "false",
2360
- onSelect: () => navigate(hrefForWorkspace(workspacePathPrefix, workspace.id)),
2361
- className: "gap-3 rounded-md py-2 text-[13px] focus:bg-foreground/[0.06] focus:text-foreground",
2362
- children: [
2363
- /* @__PURE__ */ jsx17("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-border/60 bg-background text-xs font-semibold text-muted-foreground", children: workspaceInitial(workspace.name) }),
2364
- /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm", children: workspace.name }),
2365
- isCurrent ? /* @__PURE__ */ jsx17(Check2, { className: "h-4 w-4 text-foreground", "aria-hidden": "true" }) : null
2366
- ]
2367
- },
2368
- workspace.id
2369
- );
2365
+ return /* @__PURE__ */ jsxs9("div", { className: "group relative w-full", children: [
2366
+ /* @__PURE__ */ jsxs9(
2367
+ DropdownMenuItem2,
2368
+ {
2369
+ "aria-label": workspace.name,
2370
+ "data-current": isCurrent ? "true" : "false",
2371
+ onSelect: () => navigate(hrefForWorkspace(workspacePathPrefix, workspace.id)),
2372
+ style: { paddingRight: 72 },
2373
+ className: "gap-3 rounded-md py-2 text-[13px] focus:bg-foreground/[0.06] focus:text-foreground",
2374
+ children: [
2375
+ /* @__PURE__ */ jsx17("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-border/60 bg-background text-xs font-semibold text-muted-foreground", children: workspaceInitial(workspace.name) }),
2376
+ /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm", children: workspace.name })
2377
+ ]
2378
+ }
2379
+ ),
2380
+ /* @__PURE__ */ jsx17(
2381
+ "button",
2382
+ {
2383
+ type: "button",
2384
+ tabIndex: -1,
2385
+ "aria-label": `Open ${workspace.name} in new tab`,
2386
+ title: "Open in new tab",
2387
+ onPointerDown: (event) => {
2388
+ event.preventDefault();
2389
+ event.stopPropagation();
2390
+ },
2391
+ onClick: (event) => {
2392
+ event.preventDefault();
2393
+ event.stopPropagation();
2394
+ openWorkspaceInNewTab(workspace.id);
2395
+ },
2396
+ style: { right: 4, top: "50%", transform: "translateY(-50%)" },
2397
+ className: "absolute z-10 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition hover:bg-foreground/10 hover:text-foreground focus:opacity-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100",
2398
+ children: /* @__PURE__ */ jsx17(OpenInNewTabIcon, { className: "h-3.5 w-3.5" })
2399
+ }
2400
+ )
2401
+ ] }, workspace.id);
2370
2402
  }) }),
2371
2403
  /* @__PURE__ */ jsx17(DropdownMenuSeparator2, { className: "-mx-2" }),
2372
2404
  /* @__PURE__ */ jsxs9(
@@ -58,7 +58,7 @@ import {
58
58
  useWorkspaceMembers,
59
59
  useWorkspaceRole,
60
60
  useWorkspaceRouteStatus
61
- } from "../chunk-WYTCJ5WL.js";
61
+ } from "../chunk-MLTJKZL4.js";
62
62
  import {
63
63
  TopBarSlotProvider,
64
64
  useTopBarSlot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-core",
3
- "version": "0.1.41",
3
+ "version": "0.1.42",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Foundation package for boring-ui-v2 apps: DB, auth, config, HTTP app factory, and frontend app shell.",
@@ -79,9 +79,9 @@
79
79
  "react-router-dom": "^7.14.2",
80
80
  "smol-toml": "^1.6.1",
81
81
  "zod": "^3.25.76",
82
- "@hachej/boring-agent": "0.1.41",
83
- "@hachej/boring-workspace": "0.1.41",
84
- "@hachej/boring-ui-kit": "0.1.41"
82
+ "@hachej/boring-agent": "0.1.42",
83
+ "@hachej/boring-ui-kit": "0.1.42",
84
+ "@hachej/boring-workspace": "0.1.42"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@testing-library/jest-dom": "^6.9.1",