@hachej/boring-core 0.1.41 → 0.1.43

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.
@@ -27,7 +27,11 @@ var ERROR_CODES = {
27
27
  RATE_LIMITED: "rate_limited",
28
28
  MAIL_DISABLED: "mail_disabled",
29
29
  DB_UNAVAILABLE: "db_unavailable",
30
- INTERNAL_ERROR: "internal_error"
30
+ INTERNAL_ERROR: "internal_error",
31
+ // Credits + purchases
32
+ PAYMENT_REQUIRED: "payment_required",
33
+ INVALID_PACK: "invalid_pack",
34
+ CHECKOUT_FAILED: "checkout_failed"
31
35
  };
32
36
  var HttpError = class extends Error {
33
37
  status;
@@ -1,6 +1,10 @@
1
1
  import {
2
+ creditGrants,
3
+ creditPurchases,
2
4
  idempotencyKeys,
3
5
  schema_exports,
6
+ usageLedger,
7
+ usageReservations,
4
8
  users,
5
9
  verification_tokens,
6
10
  workspaceInvites,
@@ -8,12 +12,12 @@ import {
8
12
  workspaceRuntimes,
9
13
  workspaceSettings,
10
14
  workspaces
11
- } from "./chunk-C3YMOITB.js";
15
+ } from "./chunk-I56OTSPB.js";
12
16
  import {
13
17
  ConfigValidationError,
14
18
  ERROR_CODES,
15
19
  HttpError
16
- } from "./chunk-H5KU6R6Y.js";
20
+ } from "./chunk-LIBHVT7V.js";
17
21
 
18
22
  // src/server/config/schema.ts
19
23
  import { z } from "zod";
@@ -700,6 +704,7 @@ async function createCoreApp(config, options) {
700
704
  styleSrcAttr: ["'unsafe-inline'"],
701
705
  imgSrc: ["'self'", "data:", "blob:"],
702
706
  connectSrc: ["'self'"],
707
+ frameSrc: ["'self'", "https://calendly.com"],
703
708
  fontSrc: ["'self'", "https://fonts.gstatic.com", "data:"],
704
709
  objectSrc: ["'none'"],
705
710
  frameAncestors: ["'none'"],
@@ -837,6 +842,10 @@ async function deleteUserCompletely(userId, deps) {
837
842
  if (userRow?.email) {
838
843
  await tx.delete(verification_tokens).where(eq(verification_tokens.identifier, userRow.email));
839
844
  }
845
+ await tx.delete(usageReservations).where(eq(usageReservations.userId, userId));
846
+ await tx.delete(usageLedger).where(eq(usageLedger.userId, userId));
847
+ await tx.delete(creditGrants).where(eq(creditGrants.userId, userId));
848
+ await tx.delete(creditPurchases).where(eq(creditPurchases.userId, userId));
840
849
  await tx.delete(users).where(eq(users.id, userId));
841
850
  });
842
851
  return;
@@ -5,7 +5,7 @@ import {
5
5
  ConfigFetchError,
6
6
  ERROR_CODES,
7
7
  HttpError
8
- } from "./chunk-H5KU6R6Y.js";
8
+ } from "./chunk-LIBHVT7V.js";
9
9
 
10
10
  // src/front/AppErrorBoundary.tsx
11
11
  import { Component } from "react";
@@ -1410,10 +1410,10 @@ function SettingsTopBar() {
1410
1410
  {
1411
1411
  "aria-hidden": "true",
1412
1412
  className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-foreground text-[12px] font-semibold text-background",
1413
- children: "B"
1413
+ children: "S"
1414
1414
  }
1415
1415
  ),
1416
- /* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: "Boring" }),
1416
+ /* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: "Sovereign Workspace" }),
1417
1417
  /* @__PURE__ */ jsx13("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
1418
1418
  /* @__PURE__ */ jsx13("span", { className: "truncate text-[13px] text-muted-foreground", children: "Account settings" })
1419
1419
  ] })
@@ -1446,12 +1446,12 @@ function SettingsPageHeader({
1446
1446
  ] })
1447
1447
  ] });
1448
1448
  }
1449
- var ACCOUNT_NAV_ITEMS = [
1450
- { href: "#profile", label: "Profile", description: "Identity and email" },
1449
+ var PROFILE_NAV_ITEM = { href: "#profile", label: "Profile", description: "Identity and email" };
1450
+ var ACCOUNT_TAIL_NAV_ITEMS = [
1451
1451
  { href: "#password", label: "Password", description: "Sign-in security" },
1452
1452
  { href: "#danger-zone", label: "Deletion", description: "Permanent actions" }
1453
1453
  ];
1454
- function UserSettingsPage({ topBar } = {}) {
1454
+ function UserSettingsPage({ topBar, extraSections = [] } = {}) {
1455
1455
  const session = useSession();
1456
1456
  const identity = useUser();
1457
1457
  const signOut = useSignOut();
@@ -1535,7 +1535,17 @@ function UserSettingsPage({ topBar } = {}) {
1535
1535
  return /* @__PURE__ */ jsxs6("main", { className: "boring-settings-shell", children: [
1536
1536
  topBarNode,
1537
1537
  /* @__PURE__ */ jsx13("div", { className: "boring-settings-scroll", children: /* @__PURE__ */ jsxs6("div", { className: "boring-settings-layout", children: [
1538
- /* @__PURE__ */ jsx13("aside", { className: "boring-settings-sidebar", children: /* @__PURE__ */ jsx13(UiSettingsNav, { label: "Account settings", items: ACCOUNT_NAV_ITEMS }) }),
1538
+ /* @__PURE__ */ jsx13("aside", { className: "boring-settings-sidebar", children: /* @__PURE__ */ jsx13(
1539
+ UiSettingsNav,
1540
+ {
1541
+ label: "Account settings",
1542
+ items: [
1543
+ PROFILE_NAV_ITEM,
1544
+ ...extraSections.map((s) => ({ href: `#${s.id}`, label: s.navLabel, description: s.navDescription })),
1545
+ ...ACCOUNT_TAIL_NAV_ITEMS
1546
+ ]
1547
+ }
1548
+ ) }),
1539
1549
  /* @__PURE__ */ jsxs6("div", { className: "boring-settings-content space-y-4", children: [
1540
1550
  /* @__PURE__ */ jsx13(
1541
1551
  SettingsPageHeader,
@@ -1588,6 +1598,7 @@ function UserSettingsPage({ topBar } = {}) {
1588
1598
  ] })
1589
1599
  }
1590
1600
  ),
1601
+ extraSections.map((section) => /* @__PURE__ */ jsx13("div", { children: section.content }, section.id)),
1591
1602
  /* @__PURE__ */ jsx13("form", { onSubmit: handleSubmit(onChangePassword), noValidate: true, children: /* @__PURE__ */ jsx13(
1592
1603
  UiSettingsPanel,
1593
1604
  {
@@ -2180,7 +2191,7 @@ import {
2180
2191
  Label as Label7,
2181
2192
  useToast
2182
2193
  } from "@hachej/boring-ui-kit";
2183
- import { Check as Check2, ChevronsUpDown, LayoutGrid, Plus, Settings as Settings2 } from "lucide-react";
2194
+ import { ChevronsUpDown, LayoutGrid, Plus, Settings as Settings2 } from "lucide-react";
2184
2195
  import { useNavigate as useNavigate3 } from "react-router-dom";
2185
2196
  import { z as z6 } from "zod";
2186
2197
  import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -2219,8 +2230,15 @@ function hrefForWorkspace(prefix, workspaceId, suffix = "") {
2219
2230
  function workspaceInitial(name) {
2220
2231
  return (name.trim()[0] ?? "W").toUpperCase();
2221
2232
  }
2233
+ function OpenInNewTabIcon({ className }) {
2234
+ return /* @__PURE__ */ jsxs9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2235
+ /* @__PURE__ */ jsx17("path", { d: "M15 3h6v6" }),
2236
+ /* @__PURE__ */ jsx17("path", { d: "M10 14 21 3" }),
2237
+ /* @__PURE__ */ jsx17("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
2238
+ ] });
2239
+ }
2222
2240
  function WorkspaceSwitcher({
2223
- appTitle = "Boring",
2241
+ appTitle = "Sovereign Workspace",
2224
2242
  workspacePathPrefix = "/workspace"
2225
2243
  }) {
2226
2244
  const navigate = useNavigate3();
@@ -2293,6 +2311,9 @@ function WorkspaceSwitcher({
2293
2311
  }
2294
2312
  }
2295
2313
  const switcherLabel = currentWorkspace?.name ?? "Select workspace";
2314
+ function openWorkspaceInNewTab(workspaceId) {
2315
+ window.open(hrefForWorkspace(workspacePathPrefix, workspaceId), "_blank", "noopener,noreferrer");
2316
+ }
2296
2317
  return /* @__PURE__ */ jsxs9(Fragment4, { children: [
2297
2318
  workspaces.length === 0 ? /* @__PURE__ */ jsxs9(
2298
2319
  Button11,
@@ -2352,21 +2373,43 @@ function WorkspaceSwitcher({
2352
2373
  ] }) }),
2353
2374
  /* @__PURE__ */ jsx17("div", { className: "max-h-72 overflow-y-auto pr-1", children: workspaces.map((workspace) => {
2354
2375
  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
- );
2376
+ return /* @__PURE__ */ jsxs9("div", { className: "group relative w-full", children: [
2377
+ /* @__PURE__ */ jsxs9(
2378
+ DropdownMenuItem2,
2379
+ {
2380
+ "aria-label": workspace.name,
2381
+ "data-current": isCurrent ? "true" : "false",
2382
+ onSelect: () => navigate(hrefForWorkspace(workspacePathPrefix, workspace.id)),
2383
+ style: { paddingRight: 72 },
2384
+ className: "gap-3 rounded-md py-2 text-[13px] focus:bg-foreground/[0.06] focus:text-foreground",
2385
+ children: [
2386
+ /* @__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) }),
2387
+ /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm", children: workspace.name })
2388
+ ]
2389
+ }
2390
+ ),
2391
+ /* @__PURE__ */ jsx17(
2392
+ "button",
2393
+ {
2394
+ type: "button",
2395
+ tabIndex: -1,
2396
+ "aria-label": `Open ${workspace.name} in new tab`,
2397
+ title: "Open in new tab",
2398
+ onPointerDown: (event) => {
2399
+ event.preventDefault();
2400
+ event.stopPropagation();
2401
+ },
2402
+ onClick: (event) => {
2403
+ event.preventDefault();
2404
+ event.stopPropagation();
2405
+ openWorkspaceInNewTab(workspace.id);
2406
+ },
2407
+ style: { right: 4, top: "50%", transform: "translateY(-50%)" },
2408
+ 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",
2409
+ children: /* @__PURE__ */ jsx17(OpenInNewTabIcon, { className: "h-3.5 w-3.5" })
2410
+ }
2411
+ )
2412
+ ] }, workspace.id);
2370
2413
  }) }),
2371
2414
  /* @__PURE__ */ jsx17(DropdownMenuSeparator2, { className: "-mx-2" }),
2372
2415
  /* @__PURE__ */ jsxs9(
@@ -2995,10 +3038,10 @@ function SettingsTopBar2({ workspaceId, workspaceName }) {
2995
3038
  title: "Back to workspace",
2996
3039
  onClick: () => navigate(workspaceHref),
2997
3040
  className: "shrink-0 bg-foreground text-[12px] font-semibold text-background hover:bg-foreground/90",
2998
- children: "B"
3041
+ children: "S"
2999
3042
  }
3000
3043
  ),
3001
- /* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: "Boring" }),
3044
+ /* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] font-medium tracking-tight text-foreground", children: "Sovereign Workspace" }),
3002
3045
  /* @__PURE__ */ jsx21("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
3003
3046
  /* @__PURE__ */ jsx21("span", { className: "truncate text-[13px] text-muted-foreground", children: workspaceName }),
3004
3047
  /* @__PURE__ */ jsx21("span", { "aria-hidden": "true", className: "text-muted-foreground/30", children: "/" }),
@@ -1,4 +1,4 @@
1
- import { C as CoreConfig, W as Workspace, E as ERROR_CODES, M as MemberRole, a as WorkspaceMember, U as User, b as WorkspaceInvite, c as WorkspaceRuntime, d as WorkspaceRuntimeResourceSelector, e as WorkspaceRuntimeResource, f as WorkspaceRuntimeResourceInput, g as CapabilitiesResponse } from './types-CbMOXLBf.js';
1
+ import { C as CoreConfig, W as Workspace, E as ERROR_CODES, M as MemberRole, a as WorkspaceMember, U as User, b as WorkspaceInvite, c as WorkspaceRuntime, d as WorkspaceRuntimeResourceSelector, e as WorkspaceRuntimeResource, f as WorkspaceRuntimeResourceInput, g as CapabilitiesResponse } from './types-CWtJ4kgd.js';
2
2
  import { drizzle } from 'drizzle-orm/postgres-js';
3
3
  import postgres from 'postgres';
4
4
 
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { Component, ReactNode, ErrorInfo } from 'react';
4
- import { R as RuntimeConfig, g as CapabilitiesResponse, a as WorkspaceMember, U as User, W as Workspace, M as MemberRole, S as SessionState } from '../types-CbMOXLBf.js';
4
+ import { R as RuntimeConfig, g as CapabilitiesResponse, a as WorkspaceMember, U as User, W as Workspace, M as MemberRole, S as SessionState } from '../types-CWtJ4kgd.js';
5
5
  import * as _tanstack_react_query from '@tanstack/react-query';
6
6
  import * as better_auth_react from 'better-auth/react';
7
7
  import { createAuthClient } from 'better-auth/react';
@@ -396,10 +396,23 @@ declare function ResetPasswordPage(): react_jsx_runtime.JSX.Element;
396
396
 
397
397
  declare function VerifyEmailPage(): react_jsx_runtime.JSX.Element;
398
398
 
399
+ /** A host-provided settings section inserted after the profile panel, with its own
400
+ * nav entry. Lets a host add feature sections (e.g. billing) WITHOUT this page —
401
+ * which lives in the feature-agnostic `@hachej/boring-core/front` — knowing what
402
+ * they are. `id` must match the rendered content's anchor id for nav scroll. */
403
+ interface UserSettingsSection {
404
+ id: string;
405
+ navLabel: string;
406
+ navDescription?: string;
407
+ content: ReactNode;
408
+ }
399
409
  interface UserSettingsPageProps {
400
410
  topBar?: ReactNode;
411
+ /** Extra settings sections (each with its own nav entry) inserted after the
412
+ * profile panel. Omitted entirely when not provided. */
413
+ extraSections?: UserSettingsSection[];
401
414
  }
402
- declare function UserSettingsPage({ topBar }?: UserSettingsPageProps): react_jsx_runtime.JSX.Element;
415
+ declare function UserSettingsPage({ topBar, extraSections }?: UserSettingsPageProps): react_jsx_runtime.JSX.Element;
403
416
 
404
417
  declare function InviteAcceptPage(): react_jsx_runtime.JSX.Element | null;
405
418
 
@@ -58,12 +58,12 @@ import {
58
58
  useWorkspaceMembers,
59
59
  useWorkspaceRole,
60
60
  useWorkspaceRouteStatus
61
- } from "../chunk-WYTCJ5WL.js";
61
+ } from "../chunk-VYXEXOCO.js";
62
62
  import {
63
63
  TopBarSlotProvider,
64
64
  useTopBarSlot
65
65
  } from "../chunk-HYNKZSTF.js";
66
- import "../chunk-H5KU6R6Y.js";
66
+ import "../chunk-LIBHVT7V.js";
67
67
  import "../chunk-MLKGABMK.js";
68
68
  export {
69
69
  AppErrorBoundary,
@@ -1,7 +1,7 @@
1
- import { U as UserStore, W as WorkspaceStore } from '../../connection-AL8KSENV.js';
2
- export { D as Database, c as createDatabase } from '../../connection-AL8KSENV.js';
3
- export { R as RunMigrationsOptions, r as runMigrations } from '../../migrate-B4dwdtGP.js';
4
- import { U as User, W as Workspace, E as ERROR_CODES, M as MemberRole, a as WorkspaceMember, b as WorkspaceInvite, c as WorkspaceRuntime, d as WorkspaceRuntimeResourceSelector, e as WorkspaceRuntimeResource, f as WorkspaceRuntimeResourceInput } from '../../types-CbMOXLBf.js';
1
+ import { U as UserStore, W as WorkspaceStore } from '../../connection-C5SiqoNc.js';
2
+ export { D as Database, c as createDatabase } from '../../connection-C5SiqoNc.js';
3
+ export { F as FinishReservationInput, G as GrantOnceInput, I as InsufficientCreditError, M as MeteringBalance, P as PostgresMeteringStore, R as RecordUsageInput, a as RecordUsageResult, b as ReservationFinalStatus, c as ReserveInput, d as ReserveResult, e as RunMigrationsOptions, r as runMigrations } from '../../PostgresMeteringStore-CzNv6xil.js';
4
+ import { U as User, W as Workspace, E as ERROR_CODES, M as MemberRole, a as WorkspaceMember, b as WorkspaceInvite, c as WorkspaceRuntime, d as WorkspaceRuntimeResourceSelector, e as WorkspaceRuntimeResource, f as WorkspaceRuntimeResourceInput } from '../../types-CWtJ4kgd.js';
5
5
  import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
6
6
  import 'postgres';
7
7
 
@@ -1,16 +1,20 @@
1
1
  import {
2
+ InsufficientCreditError,
2
3
  LocalUserStore,
3
4
  LocalWorkspaceStore,
5
+ PostgresMeteringStore,
4
6
  PostgresUserStore,
5
7
  PostgresWorkspaceStore,
6
8
  createDatabase,
7
9
  runMigrations
8
- } from "../../chunk-C3YMOITB.js";
9
- import "../../chunk-H5KU6R6Y.js";
10
+ } from "../../chunk-I56OTSPB.js";
11
+ import "../../chunk-LIBHVT7V.js";
10
12
  import "../../chunk-MLKGABMK.js";
11
13
  export {
14
+ InsufficientCreditError,
12
15
  LocalUserStore,
13
16
  LocalWorkspaceStore,
17
+ PostgresMeteringStore,
14
18
  PostgresUserStore,
15
19
  PostgresWorkspaceStore,
16
20
  createDatabase,