mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
feat(spaces): validate space metadata on Better Auth organization hooks
Moves the canonical SpaceType + SPACE_MODULE_ALLOWLIST to @mana/shared-types (framework-free) so the Bun services can consume them without pulling in Svelte. shared-branding keeps only the UI-facing labels and descriptions and re-exports the canonical types for frontend convenience. Wires two Better Auth organization hooks in mana-auth: - beforeCreateOrganization asserts metadata.type is a valid SpaceType, rejecting the create with a BAD_REQUEST otherwise. - beforeDeleteOrganization rejects deletion of the personal space. Covered by bun tests (11 assertions) for the helper module. No migration and no schema change — type lives in the existing organization.metadata jsonb column. Plan: docs/plans/spaces-foundation.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9d69e4419d
commit
166d6c6ffb
11 changed files with 683 additions and 331 deletions
|
|
@ -21,6 +21,9 @@
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
"lint": "eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mana/shared-types": "workspace:*"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,16 @@ export {
|
||||||
export type { AppId, AppBranding, LogoProps, AppLogoWithNameProps } from './types';
|
export type { AppId, AppBranding, LogoProps, AppLogoWithNameProps } from './types';
|
||||||
|
|
||||||
// Spaces (multi-tenancy primitive — see docs/plans/spaces-foundation.md)
|
// Spaces (multi-tenancy primitive — see docs/plans/spaces-foundation.md)
|
||||||
|
// Canonical types live in @mana/shared-types; branding adds only UI strings.
|
||||||
export {
|
export {
|
||||||
SPACE_TYPES,
|
SPACE_TYPES,
|
||||||
SPACE_TYPE_LABELS,
|
SPACE_TYPE_LABELS,
|
||||||
SPACE_TYPE_DESCRIPTIONS,
|
SPACE_TYPE_DESCRIPTIONS,
|
||||||
SPACE_MODULE_ALLOWLIST,
|
SPACE_MODULE_ALLOWLIST,
|
||||||
isModuleAllowedInSpace,
|
isModuleAllowedInSpace,
|
||||||
|
isSpaceType,
|
||||||
|
parseSpaceMetadata,
|
||||||
type SpaceType,
|
type SpaceType,
|
||||||
type SpaceModuleId,
|
type SpaceModuleId,
|
||||||
|
type SpaceMetadata,
|
||||||
} from './spaces';
|
} from './spaces';
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* Space Types & Module Allowlist
|
* Space — UI-facing labels and descriptions
|
||||||
*
|
*
|
||||||
* A "Space" is the unit of data ownership in Mana. Every record belongs to
|
* The canonical type/allowlist definitions live in `@mana/shared-types`
|
||||||
* exactly one Space. Users join Spaces via Better Auth's `member` relation.
|
* (framework-free so Bun services can import them). This file adds only
|
||||||
*
|
* the i18n strings that belong to the UI branding layer.
|
||||||
* Space = Better Auth Organization with a typed `metadata.type` field. The
|
|
||||||
* type drives which modules are available inside the space (see
|
|
||||||
* `SPACE_MODULE_ALLOWLIST` below).
|
|
||||||
*
|
*
|
||||||
* See docs/plans/spaces-foundation.md for the full RFC.
|
* See docs/plans/spaces-foundation.md for the full RFC.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
import type { SpaceType } from '@mana/shared-types';
|
||||||
* The six canonical Space types. Every Better Auth organization must have
|
|
||||||
* exactly one of these as `metadata.type`.
|
|
||||||
*
|
|
||||||
* - `personal` — single-member, auto-created on signup. Holds private data
|
|
||||||
* like mood, sleep, dreams that don't belong in a shared context.
|
|
||||||
* - `brand` — external communication identity (e.g. Edisconet, a creator
|
|
||||||
* persona). Hosts social-relay, mail, landing, public content.
|
|
||||||
* - `club` — association/Verein. Member management, dues, events,
|
|
||||||
* governance. Target for the ClubDesk-replacement roadmap.
|
|
||||||
* - `family` — household/family/WG. Shared calendar, shopping, recipes.
|
|
||||||
* - `team` — work team / project. Tasks, chat, docs.
|
|
||||||
* - `practice` — freelancer/solo-business. Invoicing, clients, time tracking.
|
|
||||||
*/
|
|
||||||
export type SpaceType = 'personal' | 'brand' | 'club' | 'family' | 'team' | 'practice';
|
|
||||||
|
|
||||||
export const SPACE_TYPES: readonly SpaceType[] = [
|
|
||||||
'personal',
|
|
||||||
'brand',
|
|
||||||
'club',
|
|
||||||
'family',
|
|
||||||
'team',
|
|
||||||
'practice',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const SPACE_TYPE_LABELS = {
|
export const SPACE_TYPE_LABELS = {
|
||||||
de: {
|
de: {
|
||||||
|
|
@ -53,7 +27,7 @@ export const SPACE_TYPE_LABELS = {
|
||||||
team: 'Team',
|
team: 'Team',
|
||||||
practice: 'Practice',
|
practice: 'Practice',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const satisfies Record<'de' | 'en', Record<SpaceType, string>>;
|
||||||
|
|
||||||
export const SPACE_TYPE_DESCRIPTIONS = {
|
export const SPACE_TYPE_DESCRIPTIONS = {
|
||||||
de: {
|
de: {
|
||||||
|
|
@ -72,161 +46,18 @@ export const SPACE_TYPE_DESCRIPTIONS = {
|
||||||
team: 'Work team or project with multiple collaborators.',
|
team: 'Work team or project with multiple collaborators.',
|
||||||
practice: 'Freelancer or solo business with clients and invoices.',
|
practice: 'Freelancer or solo business with clients and invoices.',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const satisfies Record<'de' | 'en', Record<SpaceType, string>>;
|
||||||
|
|
||||||
/**
|
// Re-export canonical types from shared-types so frontend consumers can
|
||||||
* Module IDs referenced by the allowlist. Strings (not a strict enum) because
|
// import everything space-related from `@mana/shared-branding` for
|
||||||
* the allowlist intentionally includes modules that don't exist yet — e.g.
|
// convenience.
|
||||||
* `club-finance`, `social-relay` — so features can be gated before the code
|
export {
|
||||||
* lands.
|
SPACE_TYPES,
|
||||||
*/
|
SPACE_MODULE_ALLOWLIST,
|
||||||
export type SpaceModuleId = string;
|
isModuleAllowedInSpace,
|
||||||
|
isSpaceType,
|
||||||
/**
|
parseSpaceMetadata,
|
||||||
* Which modules are available inside each Space type.
|
type SpaceType,
|
||||||
*
|
type SpaceModuleId,
|
||||||
* The personal space gets everything (sentinel `'*'`). Other types get a
|
type SpaceMetadata,
|
||||||
* curated subset — modules dealing with intimate personal data (mood,
|
} from '@mana/shared-types';
|
||||||
* dreams, period, body measurements, …) are intentionally excluded from
|
|
||||||
* shared spaces.
|
|
||||||
*
|
|
||||||
* Rule of thumb: if a module's data would feel wrong shared with co-workers
|
|
||||||
* or club members, keep it out.
|
|
||||||
*/
|
|
||||||
export const SPACE_MODULE_ALLOWLIST: Record<SpaceType, readonly SpaceModuleId[] | '*'> = {
|
|
||||||
personal: '*',
|
|
||||||
|
|
||||||
brand: [
|
|
||||||
'mana',
|
|
||||||
'social-relay', // future — not yet built
|
|
||||||
'mail',
|
|
||||||
'contacts',
|
|
||||||
'calendar',
|
|
||||||
'storage',
|
|
||||||
'uload',
|
|
||||||
'landing', // future
|
|
||||||
'presi',
|
|
||||||
'cards',
|
|
||||||
'picture',
|
|
||||||
'quotes',
|
|
||||||
'news',
|
|
||||||
'news-research',
|
|
||||||
'research-lab',
|
|
||||||
'ai-agents',
|
|
||||||
'companion',
|
|
||||||
'times',
|
|
||||||
'notes',
|
|
||||||
'photos',
|
|
||||||
'invoices',
|
|
||||||
'activity',
|
|
||||||
'goals',
|
|
||||||
],
|
|
||||||
|
|
||||||
club: [
|
|
||||||
'mana',
|
|
||||||
'contacts',
|
|
||||||
'calendar',
|
|
||||||
'events',
|
|
||||||
'mail',
|
|
||||||
'storage',
|
|
||||||
'uload',
|
|
||||||
'news',
|
|
||||||
'research-lab',
|
|
||||||
'club-members', // future — ClubDesk Paket A
|
|
||||||
'club-finance', // future — ClubDesk Paket B
|
|
||||||
'invoices',
|
|
||||||
'finance',
|
|
||||||
'landing', // future — Paket C (Vereinswebsite)
|
|
||||||
'presi',
|
|
||||||
'cards',
|
|
||||||
'quotes',
|
|
||||||
'companion',
|
|
||||||
'times',
|
|
||||||
'notes',
|
|
||||||
'photos',
|
|
||||||
'activity',
|
|
||||||
'goals',
|
|
||||||
],
|
|
||||||
|
|
||||||
family: [
|
|
||||||
'mana',
|
|
||||||
'contacts',
|
|
||||||
'calendar',
|
|
||||||
'events',
|
|
||||||
'mail',
|
|
||||||
'storage',
|
|
||||||
'uload',
|
|
||||||
'recipes',
|
|
||||||
'food',
|
|
||||||
'places',
|
|
||||||
'presi',
|
|
||||||
'cards',
|
|
||||||
'photos',
|
|
||||||
'notes',
|
|
||||||
'companion',
|
|
||||||
'goals',
|
|
||||||
'activity',
|
|
||||||
'wetter',
|
|
||||||
'wisekeep',
|
|
||||||
'firsts',
|
|
||||||
],
|
|
||||||
|
|
||||||
team: [
|
|
||||||
'mana',
|
|
||||||
'contacts',
|
|
||||||
'calendar',
|
|
||||||
'events',
|
|
||||||
'storage',
|
|
||||||
'mail',
|
|
||||||
'uload',
|
|
||||||
'news',
|
|
||||||
'news-research',
|
|
||||||
'research-lab',
|
|
||||||
'presi',
|
|
||||||
'cards',
|
|
||||||
'picture',
|
|
||||||
'notes',
|
|
||||||
'quotes',
|
|
||||||
'invoices',
|
|
||||||
'companion',
|
|
||||||
'ai-agents',
|
|
||||||
'times',
|
|
||||||
'activity',
|
|
||||||
'goals',
|
|
||||||
],
|
|
||||||
|
|
||||||
practice: [
|
|
||||||
'mana',
|
|
||||||
'contacts',
|
|
||||||
'calendar',
|
|
||||||
'storage',
|
|
||||||
'mail',
|
|
||||||
'uload',
|
|
||||||
'invoices',
|
|
||||||
'finance',
|
|
||||||
'times',
|
|
||||||
'notes',
|
|
||||||
'presi',
|
|
||||||
'cards',
|
|
||||||
'quotes',
|
|
||||||
'companion',
|
|
||||||
'research-lab',
|
|
||||||
'activity',
|
|
||||||
'goals',
|
|
||||||
],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether a module is available inside a given Space type.
|
|
||||||
*
|
|
||||||
* Used by:
|
|
||||||
* - Scope wrapper (apps/mana/.../data/scope/scoped-db.ts) to block queries
|
|
||||||
* against disallowed modules — structural guard against UI bypass.
|
|
||||||
* - UI module launcher to hide disabled modules in the active space.
|
|
||||||
* - Route guards that check before mounting a module page.
|
|
||||||
*/
|
|
||||||
export function isModuleAllowedInSpace(moduleId: SpaceModuleId, spaceType: SpaceType): boolean {
|
|
||||||
const allow = SPACE_MODULE_ALLOWLIST[spaceType];
|
|
||||||
if (allow === '*') return true;
|
|
||||||
return allow.includes(moduleId);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ export * from './landing-config';
|
||||||
// AI structured-output Zod schemas (shared between mana-api + web frontend)
|
// AI structured-output Zod schemas (shared between mana-api + web frontend)
|
||||||
export * from './ai-schemas';
|
export * from './ai-schemas';
|
||||||
|
|
||||||
|
// Space types (multi-tenancy primitive — see docs/plans/spaces-foundation.md)
|
||||||
|
export * from './spaces';
|
||||||
|
|
||||||
// API types
|
// API types
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
||||||
225
packages/shared-types/src/spaces.ts
Normal file
225
packages/shared-types/src/spaces.ts
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
/**
|
||||||
|
* Space Types & Module Allowlist
|
||||||
|
*
|
||||||
|
* Framework-free definition of the Space primitive — the unit of data
|
||||||
|
* ownership in Mana. Consumed by both the SvelteKit frontend and the
|
||||||
|
* Bun/Hono services (mana-auth, mana-api, mana-sync), which is why this
|
||||||
|
* lives in shared-types instead of shared-branding (the latter carries
|
||||||
|
* Svelte components and is too heavy for a server).
|
||||||
|
*
|
||||||
|
* UI-facing labels/descriptions for these types live in shared-branding.
|
||||||
|
*
|
||||||
|
* See docs/plans/spaces-foundation.md for the full RFC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The six canonical Space types. Every Better Auth organization must have
|
||||||
|
* exactly one of these as `metadata.type`.
|
||||||
|
*
|
||||||
|
* - `personal` — single-member, auto-created on signup. Holds private data
|
||||||
|
* like mood, sleep, dreams that don't belong in a shared context.
|
||||||
|
* - `brand` — external communication identity (e.g. Edisconet, a creator
|
||||||
|
* persona). Hosts social-relay, mail, landing, public content.
|
||||||
|
* - `club` — association/Verein. Member management, dues, events,
|
||||||
|
* governance. Target for the ClubDesk-replacement roadmap.
|
||||||
|
* - `family` — household/family/WG. Shared calendar, shopping, recipes.
|
||||||
|
* - `team` — work team / project. Tasks, chat, docs.
|
||||||
|
* - `practice` — freelancer/solo-business. Invoicing, clients, time tracking.
|
||||||
|
*/
|
||||||
|
export type SpaceType = 'personal' | 'brand' | 'club' | 'family' | 'team' | 'practice';
|
||||||
|
|
||||||
|
export const SPACE_TYPES: readonly SpaceType[] = [
|
||||||
|
'personal',
|
||||||
|
'brand',
|
||||||
|
'club',
|
||||||
|
'family',
|
||||||
|
'team',
|
||||||
|
'practice',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function isSpaceType(value: unknown): value is SpaceType {
|
||||||
|
return typeof value === 'string' && (SPACE_TYPES as readonly string[]).includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module IDs referenced by the allowlist. Strings (not a strict enum) because
|
||||||
|
* the allowlist intentionally includes modules that don't exist yet — e.g.
|
||||||
|
* `club-finance`, `social-relay` — so features can be gated before the code
|
||||||
|
* lands.
|
||||||
|
*/
|
||||||
|
export type SpaceModuleId = string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which modules are available inside each Space type.
|
||||||
|
*
|
||||||
|
* The personal space gets everything (sentinel `'*'`). Other types get a
|
||||||
|
* curated subset — modules dealing with intimate personal data (mood,
|
||||||
|
* dreams, period, body measurements, …) are intentionally excluded from
|
||||||
|
* shared spaces.
|
||||||
|
*
|
||||||
|
* Rule of thumb: if a module's data would feel wrong shared with co-workers
|
||||||
|
* or club members, keep it out.
|
||||||
|
*/
|
||||||
|
export const SPACE_MODULE_ALLOWLIST: Record<SpaceType, readonly SpaceModuleId[] | '*'> = {
|
||||||
|
personal: '*',
|
||||||
|
|
||||||
|
brand: [
|
||||||
|
'mana',
|
||||||
|
'social-relay', // future — not yet built
|
||||||
|
'mail',
|
||||||
|
'contacts',
|
||||||
|
'calendar',
|
||||||
|
'storage',
|
||||||
|
'uload',
|
||||||
|
'landing', // future
|
||||||
|
'presi',
|
||||||
|
'cards',
|
||||||
|
'picture',
|
||||||
|
'quotes',
|
||||||
|
'news',
|
||||||
|
'news-research',
|
||||||
|
'research-lab',
|
||||||
|
'ai-agents',
|
||||||
|
'companion',
|
||||||
|
'times',
|
||||||
|
'notes',
|
||||||
|
'photos',
|
||||||
|
'invoices',
|
||||||
|
'activity',
|
||||||
|
'goals',
|
||||||
|
],
|
||||||
|
|
||||||
|
club: [
|
||||||
|
'mana',
|
||||||
|
'contacts',
|
||||||
|
'calendar',
|
||||||
|
'events',
|
||||||
|
'mail',
|
||||||
|
'storage',
|
||||||
|
'uload',
|
||||||
|
'news',
|
||||||
|
'research-lab',
|
||||||
|
'club-members', // future — ClubDesk Paket A
|
||||||
|
'club-finance', // future — ClubDesk Paket B
|
||||||
|
'invoices',
|
||||||
|
'finance',
|
||||||
|
'landing', // future — Paket C (Vereinswebsite)
|
||||||
|
'presi',
|
||||||
|
'cards',
|
||||||
|
'quotes',
|
||||||
|
'companion',
|
||||||
|
'times',
|
||||||
|
'notes',
|
||||||
|
'photos',
|
||||||
|
'activity',
|
||||||
|
'goals',
|
||||||
|
],
|
||||||
|
|
||||||
|
family: [
|
||||||
|
'mana',
|
||||||
|
'contacts',
|
||||||
|
'calendar',
|
||||||
|
'events',
|
||||||
|
'mail',
|
||||||
|
'storage',
|
||||||
|
'uload',
|
||||||
|
'recipes',
|
||||||
|
'food',
|
||||||
|
'places',
|
||||||
|
'presi',
|
||||||
|
'cards',
|
||||||
|
'photos',
|
||||||
|
'notes',
|
||||||
|
'companion',
|
||||||
|
'goals',
|
||||||
|
'activity',
|
||||||
|
'wetter',
|
||||||
|
'wisekeep',
|
||||||
|
'firsts',
|
||||||
|
],
|
||||||
|
|
||||||
|
team: [
|
||||||
|
'mana',
|
||||||
|
'contacts',
|
||||||
|
'calendar',
|
||||||
|
'events',
|
||||||
|
'storage',
|
||||||
|
'mail',
|
||||||
|
'uload',
|
||||||
|
'news',
|
||||||
|
'news-research',
|
||||||
|
'research-lab',
|
||||||
|
'presi',
|
||||||
|
'cards',
|
||||||
|
'picture',
|
||||||
|
'notes',
|
||||||
|
'quotes',
|
||||||
|
'invoices',
|
||||||
|
'companion',
|
||||||
|
'ai-agents',
|
||||||
|
'times',
|
||||||
|
'activity',
|
||||||
|
'goals',
|
||||||
|
],
|
||||||
|
|
||||||
|
practice: [
|
||||||
|
'mana',
|
||||||
|
'contacts',
|
||||||
|
'calendar',
|
||||||
|
'storage',
|
||||||
|
'mail',
|
||||||
|
'uload',
|
||||||
|
'invoices',
|
||||||
|
'finance',
|
||||||
|
'times',
|
||||||
|
'notes',
|
||||||
|
'presi',
|
||||||
|
'cards',
|
||||||
|
'quotes',
|
||||||
|
'companion',
|
||||||
|
'research-lab',
|
||||||
|
'activity',
|
||||||
|
'goals',
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a module is available inside a given Space type.
|
||||||
|
*
|
||||||
|
* Used by:
|
||||||
|
* - Scope wrapper (apps/mana/.../data/scope/scoped-db.ts) to block queries
|
||||||
|
* against disallowed modules — structural guard against UI bypass.
|
||||||
|
* - UI module launcher to hide disabled modules in the active space.
|
||||||
|
* - Route guards that check before mounting a module page.
|
||||||
|
*/
|
||||||
|
export function isModuleAllowedInSpace(moduleId: SpaceModuleId, spaceType: SpaceType): boolean {
|
||||||
|
const allow = SPACE_MODULE_ALLOWLIST[spaceType];
|
||||||
|
if (allow === '*') return true;
|
||||||
|
return allow.includes(moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shape of the `metadata` JSONB column on Better Auth's `organization` table
|
||||||
|
* for our Space extension. `type` is required; other fields accumulate as
|
||||||
|
* features land (voiceDoc, legalEntity, uid, aiPersonaId, …).
|
||||||
|
*/
|
||||||
|
export interface SpaceMetadata {
|
||||||
|
type: SpaceType;
|
||||||
|
voiceDoc?: string;
|
||||||
|
legalEntity?: string;
|
||||||
|
uid?: string;
|
||||||
|
aiPersonaId?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Narrow a raw metadata blob (from Better Auth / DB) to a validated
|
||||||
|
* SpaceMetadata. Returns null if no valid type field is present — callers
|
||||||
|
* decide whether to reject or default.
|
||||||
|
*/
|
||||||
|
export function parseSpaceMetadata(raw: unknown): SpaceMetadata | null {
|
||||||
|
if (!raw || typeof raw !== 'object') return null;
|
||||||
|
const obj = raw as Record<string, unknown>;
|
||||||
|
if (!isSpaceType(obj.type)) return null;
|
||||||
|
return obj as SpaceMetadata;
|
||||||
|
}
|
||||||
386
pnpm-lock.yaml
generated
386
pnpm-lock.yaml
generated
|
|
@ -138,14 +138,14 @@ importers:
|
||||||
version: link:../../../../packages/shared-landing-ui
|
version: link:../../../../packages/shared-landing-ui
|
||||||
astro:
|
astro:
|
||||||
specifier: ^5.16.0
|
specifier: ^5.16.0
|
||||||
version: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
version: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.2
|
specifier: ^5.9.2
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@astrojs/tailwind':
|
'@astrojs/tailwind':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
version: 6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
specifier: ^0.5.18
|
specifier: ^0.5.18
|
||||||
version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
||||||
|
|
@ -154,13 +154,13 @@ importers:
|
||||||
version: 20.19.39
|
version: 20.19.39
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.39.4(jiti@2.6.1)
|
version: 9.39.4(jiti@1.21.7)
|
||||||
eslint-config-prettier:
|
eslint-config-prettier:
|
||||||
specifier: ^9.1.0
|
specifier: ^9.1.0
|
||||||
version: 9.1.2(eslint@9.39.4(jiti@2.6.1))
|
version: 9.1.2(eslint@9.39.4(jiti@1.21.7))
|
||||||
eslint-plugin-astro:
|
eslint-plugin-astro:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.6.0(eslint@9.39.4(jiti@2.6.1))
|
version: 1.6.0(eslint@9.39.4(jiti@1.21.7))
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.6.2
|
specifier: ^3.6.2
|
||||||
version: 3.8.1
|
version: 3.8.1
|
||||||
|
|
@ -253,10 +253,10 @@ importers:
|
||||||
version: 3.7.2
|
version: 3.7.2
|
||||||
'@astrojs/tailwind':
|
'@astrojs/tailwind':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
version: 6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))
|
||||||
astro:
|
astro:
|
||||||
specifier: ^5.16.11
|
specifier: ^5.16.11
|
||||||
version: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
version: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.4.17
|
specifier: ^3.4.17
|
||||||
version: 3.4.19(tsx@4.21.0)(yaml@2.8.3)
|
version: 3.4.19(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
|
@ -612,6 +612,9 @@ importers:
|
||||||
svelte-sonner:
|
svelte-sonner:
|
||||||
specifier: ^1.0.5
|
specifier: ^1.0.5
|
||||||
version: 1.1.0(svelte@5.55.1)
|
version: 1.1.0(svelte@5.55.1)
|
||||||
|
swissqrbill:
|
||||||
|
specifier: ^4.3.0
|
||||||
|
version: 4.3.0(typescript@5.9.3)
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.25.76
|
specifier: ^3.25.76
|
||||||
version: 3.25.76
|
version: 3.25.76
|
||||||
|
|
@ -2018,6 +2021,10 @@ importers:
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
|
|
||||||
packages/shared-branding:
|
packages/shared-branding:
|
||||||
|
dependencies:
|
||||||
|
'@mana/shared-types':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared-types
|
||||||
devDependencies:
|
devDependencies:
|
||||||
svelte:
|
svelte:
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
|
|
@ -2586,6 +2593,9 @@ importers:
|
||||||
'@mana/shared-hono':
|
'@mana/shared-hono':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/shared-hono
|
version: link:../../packages/shared-hono
|
||||||
|
'@mana/shared-types':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/shared-types
|
||||||
bcryptjs:
|
bcryptjs:
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
|
|
@ -15061,6 +15071,9 @@ packages:
|
||||||
resolution: {integrity: sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw==}
|
resolution: {integrity: sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
svg-engine@0.3.0:
|
||||||
|
resolution: {integrity: sha512-s172jAcwfoCcvM/6DwNBvmWN3brztHGFENCR+RU3CBJKeBxPrRlTltVxX1Je5hst782QgP8PM6U37vUR/RhPng==}
|
||||||
|
|
||||||
svg-parser@2.0.4:
|
svg-parser@2.0.4:
|
||||||
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
|
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
|
||||||
|
|
||||||
|
|
@ -15078,6 +15091,18 @@ packages:
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
swissqrbill@4.3.0:
|
||||||
|
resolution: {integrity: sha512-FzSPEVWVQ3R6B0vghqi7VJCLp54AMYxCSiJGr2QzXo8OSU8JBv7XJcF67BAVAriGkuaZqVfhxuD6yRFT6WAXEA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
pdfkit: '>=0.13.0'
|
||||||
|
typescript: '>=4.7.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
pdfkit:
|
||||||
|
optional: true
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
symbol-observable@4.0.0:
|
symbol-observable@4.0.0:
|
||||||
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
|
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
|
@ -16686,6 +16711,16 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
|
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
||||||
|
dependencies:
|
||||||
|
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
||||||
|
autoprefixer: 10.4.27(postcss@8.5.8)
|
||||||
|
postcss: 8.5.8
|
||||||
|
postcss-load-config: 4.0.2(postcss@8.5.8)
|
||||||
|
tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- ts-node
|
||||||
|
|
||||||
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
||||||
|
|
@ -16706,16 +16741,6 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
|
||||||
dependencies:
|
|
||||||
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
|
||||||
autoprefixer: 10.4.27(postcss@8.5.8)
|
|
||||||
postcss: 8.5.8
|
|
||||||
postcss-load-config: 4.0.2(postcss@8.5.8)
|
|
||||||
tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.3)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- ts-node
|
|
||||||
|
|
||||||
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
'@astrojs/tailwind@6.0.2(astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
astro: 5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)
|
||||||
|
|
@ -18875,6 +18900,11 @@ snapshots:
|
||||||
'@esbuild/win32-x64@0.27.7':
|
'@esbuild/win32-x64@0.27.7':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@1.21.7))':
|
||||||
|
dependencies:
|
||||||
|
eslint: 9.39.4(jiti@1.21.7)
|
||||||
|
eslint-visitor-keys: 3.4.3
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
|
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
|
|
@ -23156,7 +23186,7 @@ snapshots:
|
||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
std-env: 4.0.0
|
std-env: 4.0.0
|
||||||
tinyrainbow: 3.1.0
|
tinyrainbow: 3.1.0
|
||||||
vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@22.19.17)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.1.3)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.1.3)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||||
|
|
||||||
'@vitest/expect@4.1.3':
|
'@vitest/expect@4.1.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -23218,7 +23248,7 @@ snapshots:
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
tinyglobby: 0.2.15
|
tinyglobby: 0.2.15
|
||||||
tinyrainbow: 3.1.0
|
tinyrainbow: 3.1.0
|
||||||
vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@22.19.17)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.1.3)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.1.3)(jsdom@29.0.2(@noble/hashes@2.0.1))(vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||||
|
|
||||||
'@vitest/utils@4.1.3':
|
'@vitest/utils@4.1.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -23649,6 +23679,108 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/compiler': 2.13.1
|
||||||
|
'@astrojs/internal-helpers': 0.7.6
|
||||||
|
'@astrojs/markdown-remark': 6.3.11
|
||||||
|
'@astrojs/telemetry': 3.3.0
|
||||||
|
'@capsizecss/unpack': 4.0.0
|
||||||
|
'@oslojs/encoding': 1.1.0
|
||||||
|
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
|
||||||
|
acorn: 8.16.0
|
||||||
|
aria-query: 5.3.2
|
||||||
|
axobject-query: 4.1.0
|
||||||
|
boxen: 8.0.1
|
||||||
|
ci-info: 4.4.0
|
||||||
|
clsx: 2.1.1
|
||||||
|
common-ancestor-path: 1.0.1
|
||||||
|
cookie: 1.1.1
|
||||||
|
cssesc: 3.0.0
|
||||||
|
debug: 4.4.3
|
||||||
|
deterministic-object-hash: 2.0.2
|
||||||
|
devalue: 5.7.0
|
||||||
|
diff: 8.0.4
|
||||||
|
dlv: 1.1.3
|
||||||
|
dset: 3.1.4
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
esbuild: 0.27.7
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
flattie: 1.1.1
|
||||||
|
fontace: 0.4.1
|
||||||
|
github-slugger: 2.0.0
|
||||||
|
html-escaper: 3.0.3
|
||||||
|
http-cache-semantics: 4.2.0
|
||||||
|
import-meta-resolve: 4.2.0
|
||||||
|
js-yaml: 4.1.1
|
||||||
|
magic-string: 0.30.21
|
||||||
|
magicast: 0.5.2
|
||||||
|
mrmime: 2.0.1
|
||||||
|
neotraverse: 0.6.18
|
||||||
|
p-limit: 6.2.0
|
||||||
|
p-queue: 8.1.1
|
||||||
|
package-manager-detector: 1.6.0
|
||||||
|
piccolore: 0.1.3
|
||||||
|
picomatch: 4.0.4
|
||||||
|
prompts: 2.4.2
|
||||||
|
rehype: 13.0.2
|
||||||
|
semver: 7.7.4
|
||||||
|
shiki: 3.23.0
|
||||||
|
smol-toml: 1.6.1
|
||||||
|
svgo: 4.0.1
|
||||||
|
tinyexec: 1.0.4
|
||||||
|
tinyglobby: 0.2.15
|
||||||
|
tsconfck: 3.1.6(typescript@5.9.3)
|
||||||
|
ultrahtml: 1.6.0
|
||||||
|
unifont: 0.7.4
|
||||||
|
unist-util-visit: 5.1.0
|
||||||
|
unstorage: 1.17.5(@azure/storage-blob@12.31.0)(ioredis@5.10.1)
|
||||||
|
vfile: 6.0.3
|
||||||
|
vite: 6.4.2(@types/node@20.19.39)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
vitefu: 1.1.3(vite@6.4.2(@types/node@20.19.39)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||||
|
xxhash-wasm: 1.1.0
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
yocto-spinner: 0.2.3
|
||||||
|
zod: 3.25.76
|
||||||
|
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
||||||
|
zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76)
|
||||||
|
optionalDependencies:
|
||||||
|
sharp: 0.34.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@azure/app-configuration'
|
||||||
|
- '@azure/cosmos'
|
||||||
|
- '@azure/data-tables'
|
||||||
|
- '@azure/identity'
|
||||||
|
- '@azure/keyvault-secrets'
|
||||||
|
- '@azure/storage-blob'
|
||||||
|
- '@capacitor/preferences'
|
||||||
|
- '@deno/kv'
|
||||||
|
- '@netlify/blobs'
|
||||||
|
- '@planetscale/database'
|
||||||
|
- '@types/node'
|
||||||
|
- '@upstash/redis'
|
||||||
|
- '@vercel/blob'
|
||||||
|
- '@vercel/functions'
|
||||||
|
- '@vercel/kv'
|
||||||
|
- aws4fetch
|
||||||
|
- db0
|
||||||
|
- idb-keyval
|
||||||
|
- ioredis
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- rollup
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- typescript
|
||||||
|
- uploadthing
|
||||||
|
- yaml
|
||||||
|
|
||||||
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@20.19.39)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 2.13.1
|
'@astrojs/compiler': 2.13.1
|
||||||
|
|
@ -23853,108 +23985,6 @@ snapshots:
|
||||||
- uploadthing
|
- uploadthing
|
||||||
- yaml
|
- yaml
|
||||||
|
|
||||||
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
|
||||||
dependencies:
|
|
||||||
'@astrojs/compiler': 2.13.1
|
|
||||||
'@astrojs/internal-helpers': 0.7.6
|
|
||||||
'@astrojs/markdown-remark': 6.3.11
|
|
||||||
'@astrojs/telemetry': 3.3.0
|
|
||||||
'@capsizecss/unpack': 4.0.0
|
|
||||||
'@oslojs/encoding': 1.1.0
|
|
||||||
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
|
|
||||||
acorn: 8.16.0
|
|
||||||
aria-query: 5.3.2
|
|
||||||
axobject-query: 4.1.0
|
|
||||||
boxen: 8.0.1
|
|
||||||
ci-info: 4.4.0
|
|
||||||
clsx: 2.1.1
|
|
||||||
common-ancestor-path: 1.0.1
|
|
||||||
cookie: 1.1.1
|
|
||||||
cssesc: 3.0.0
|
|
||||||
debug: 4.4.3
|
|
||||||
deterministic-object-hash: 2.0.2
|
|
||||||
devalue: 5.7.0
|
|
||||||
diff: 8.0.4
|
|
||||||
dlv: 1.1.3
|
|
||||||
dset: 3.1.4
|
|
||||||
es-module-lexer: 1.7.0
|
|
||||||
esbuild: 0.27.7
|
|
||||||
estree-walker: 3.0.3
|
|
||||||
flattie: 1.1.1
|
|
||||||
fontace: 0.4.1
|
|
||||||
github-slugger: 2.0.0
|
|
||||||
html-escaper: 3.0.3
|
|
||||||
http-cache-semantics: 4.2.0
|
|
||||||
import-meta-resolve: 4.2.0
|
|
||||||
js-yaml: 4.1.1
|
|
||||||
magic-string: 0.30.21
|
|
||||||
magicast: 0.5.2
|
|
||||||
mrmime: 2.0.1
|
|
||||||
neotraverse: 0.6.18
|
|
||||||
p-limit: 6.2.0
|
|
||||||
p-queue: 8.1.1
|
|
||||||
package-manager-detector: 1.6.0
|
|
||||||
piccolore: 0.1.3
|
|
||||||
picomatch: 4.0.4
|
|
||||||
prompts: 2.4.2
|
|
||||||
rehype: 13.0.2
|
|
||||||
semver: 7.7.4
|
|
||||||
shiki: 3.23.0
|
|
||||||
smol-toml: 1.6.1
|
|
||||||
svgo: 4.0.1
|
|
||||||
tinyexec: 1.0.4
|
|
||||||
tinyglobby: 0.2.15
|
|
||||||
tsconfck: 3.1.6(typescript@5.9.3)
|
|
||||||
ultrahtml: 1.6.0
|
|
||||||
unifont: 0.7.4
|
|
||||||
unist-util-visit: 5.1.0
|
|
||||||
unstorage: 1.17.5(@azure/storage-blob@12.31.0)(ioredis@5.10.1)
|
|
||||||
vfile: 6.0.3
|
|
||||||
vite: 6.4.2(@types/node@24.12.2)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
|
||||||
vitefu: 1.1.3(vite@6.4.2(@types/node@24.12.2)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
|
||||||
xxhash-wasm: 1.1.0
|
|
||||||
yargs-parser: 21.1.1
|
|
||||||
yocto-spinner: 0.2.3
|
|
||||||
zod: 3.25.76
|
|
||||||
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
|
||||||
zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76)
|
|
||||||
optionalDependencies:
|
|
||||||
sharp: 0.34.5
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@azure/app-configuration'
|
|
||||||
- '@azure/cosmos'
|
|
||||||
- '@azure/data-tables'
|
|
||||||
- '@azure/identity'
|
|
||||||
- '@azure/keyvault-secrets'
|
|
||||||
- '@azure/storage-blob'
|
|
||||||
- '@capacitor/preferences'
|
|
||||||
- '@deno/kv'
|
|
||||||
- '@netlify/blobs'
|
|
||||||
- '@planetscale/database'
|
|
||||||
- '@types/node'
|
|
||||||
- '@upstash/redis'
|
|
||||||
- '@vercel/blob'
|
|
||||||
- '@vercel/functions'
|
|
||||||
- '@vercel/kv'
|
|
||||||
- aws4fetch
|
|
||||||
- db0
|
|
||||||
- idb-keyval
|
|
||||||
- ioredis
|
|
||||||
- jiti
|
|
||||||
- less
|
|
||||||
- lightningcss
|
|
||||||
- rollup
|
|
||||||
- sass
|
|
||||||
- sass-embedded
|
|
||||||
- stylus
|
|
||||||
- sugarss
|
|
||||||
- supports-color
|
|
||||||
- terser
|
|
||||||
- tsx
|
|
||||||
- typescript
|
|
||||||
- uploadthing
|
|
||||||
- yaml
|
|
||||||
|
|
||||||
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
astro@5.18.1(@azure/storage-blob@12.31.0)(@types/node@24.12.2)(ioredis@5.10.1)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.1)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 2.13.1
|
'@astrojs/compiler': 2.13.1
|
||||||
|
|
@ -25758,6 +25788,11 @@ snapshots:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
|
|
||||||
|
eslint-compat-utils@0.6.5(eslint@9.39.4(jiti@1.21.7)):
|
||||||
|
dependencies:
|
||||||
|
eslint: 9.39.4(jiti@1.21.7)
|
||||||
|
semver: 7.7.4
|
||||||
|
|
||||||
eslint-compat-utils@0.6.5(eslint@9.39.4(jiti@2.6.1)):
|
eslint-compat-utils@0.6.5(eslint@9.39.4(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
|
|
@ -25767,6 +25802,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
|
|
||||||
|
eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@1.21.7)):
|
||||||
|
dependencies:
|
||||||
|
eslint: 9.39.4(jiti@1.21.7)
|
||||||
|
|
||||||
eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)):
|
eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4(jiti@2.6.1)
|
eslint: 9.39.4(jiti@2.6.1)
|
||||||
|
|
@ -25811,6 +25850,20 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
eslint-plugin-astro@1.6.0(eslint@9.39.4(jiti@1.21.7)):
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7))
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
'@typescript-eslint/types': 8.58.0
|
||||||
|
astro-eslint-parser: 1.4.0
|
||||||
|
eslint: 9.39.4(jiti@1.21.7)
|
||||||
|
eslint-compat-utils: 0.6.5(eslint@9.39.4(jiti@1.21.7))
|
||||||
|
globals: 16.5.0
|
||||||
|
postcss: 8.5.8
|
||||||
|
postcss-selector-parser: 7.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-astro@1.6.0(eslint@9.39.4(jiti@2.6.1)):
|
eslint-plugin-astro@1.6.0(eslint@9.39.4(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
|
||||||
|
|
@ -25984,6 +26037,47 @@ snapshots:
|
||||||
|
|
||||||
eslint-visitor-keys@5.0.1: {}
|
eslint-visitor-keys@5.0.1: {}
|
||||||
|
|
||||||
|
eslint@9.39.4(jiti@1.21.7):
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7))
|
||||||
|
'@eslint-community/regexpp': 4.12.2
|
||||||
|
'@eslint/config-array': 0.21.2
|
||||||
|
'@eslint/config-helpers': 0.4.2
|
||||||
|
'@eslint/core': 0.17.0
|
||||||
|
'@eslint/eslintrc': 3.3.5
|
||||||
|
'@eslint/js': 9.39.4
|
||||||
|
'@eslint/plugin-kit': 0.4.1
|
||||||
|
'@humanfs/node': 0.16.7
|
||||||
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
|
'@humanwhocodes/retry': 0.4.3
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
ajv: 6.14.0
|
||||||
|
chalk: 4.1.2
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
debug: 4.4.3
|
||||||
|
escape-string-regexp: 4.0.0
|
||||||
|
eslint-scope: 8.4.0
|
||||||
|
eslint-visitor-keys: 4.2.1
|
||||||
|
espree: 10.4.0
|
||||||
|
esquery: 1.7.0
|
||||||
|
esutils: 2.0.3
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
file-entry-cache: 8.0.0
|
||||||
|
find-up: 5.0.0
|
||||||
|
glob-parent: 6.0.2
|
||||||
|
ignore: 5.3.2
|
||||||
|
imurmurhash: 0.1.4
|
||||||
|
is-glob: 4.0.3
|
||||||
|
json-stable-stringify-without-jsonify: 1.0.1
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
minimatch: 3.1.5
|
||||||
|
natural-compare: 1.4.0
|
||||||
|
optionator: 0.9.4
|
||||||
|
optionalDependencies:
|
||||||
|
jiti: 1.21.7
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
eslint@9.39.4(jiti@2.6.1):
|
eslint@9.39.4(jiti@2.6.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
|
||||||
|
|
@ -32247,6 +32341,8 @@ snapshots:
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
zimmerframe: 1.1.4
|
zimmerframe: 1.1.4
|
||||||
|
|
||||||
|
svg-engine@0.3.0: {}
|
||||||
|
|
||||||
svg-parser@2.0.4: {}
|
svg-parser@2.0.4: {}
|
||||||
|
|
||||||
svg-pathdata@6.0.3:
|
svg-pathdata@6.0.3:
|
||||||
|
|
@ -32272,6 +32368,12 @@ snapshots:
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
sax: 1.6.0
|
sax: 1.6.0
|
||||||
|
|
||||||
|
swissqrbill@4.3.0(typescript@5.9.3):
|
||||||
|
dependencies:
|
||||||
|
svg-engine: 0.3.0
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.9.3
|
||||||
|
|
||||||
symbol-observable@4.0.0: {}
|
symbol-observable@4.0.0: {}
|
||||||
|
|
||||||
symbol-tree@3.2.4: {}
|
symbol-tree@3.2.4: {}
|
||||||
|
|
@ -32954,6 +33056,23 @@ snapshots:
|
||||||
lightningcss: 1.32.0
|
lightningcss: 1.32.0
|
||||||
terser: 5.46.1
|
terser: 5.46.1
|
||||||
|
|
||||||
|
vite@6.4.2(@types/node@20.19.39)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.25.12
|
||||||
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
|
picomatch: 4.0.4
|
||||||
|
postcss: 8.5.8
|
||||||
|
rollup: 4.60.1
|
||||||
|
tinyglobby: 0.2.15
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 20.19.39
|
||||||
|
fsevents: 2.3.3
|
||||||
|
jiti: 1.21.7
|
||||||
|
lightningcss: 1.32.0
|
||||||
|
terser: 5.46.1
|
||||||
|
tsx: 4.21.0
|
||||||
|
yaml: 2.8.3
|
||||||
|
|
||||||
vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.12
|
esbuild: 0.25.12
|
||||||
|
|
@ -32988,23 +33107,6 @@ snapshots:
|
||||||
tsx: 4.21.0
|
tsx: 4.21.0
|
||||||
yaml: 2.8.3
|
yaml: 2.8.3
|
||||||
|
|
||||||
vite@6.4.2(@types/node@24.12.2)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
|
||||||
dependencies:
|
|
||||||
esbuild: 0.25.12
|
|
||||||
fdir: 6.5.0(picomatch@4.0.4)
|
|
||||||
picomatch: 4.0.4
|
|
||||||
postcss: 8.5.8
|
|
||||||
rollup: 4.60.1
|
|
||||||
tinyglobby: 0.2.15
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/node': 24.12.2
|
|
||||||
fsevents: 2.3.3
|
|
||||||
jiti: 1.21.7
|
|
||||||
lightningcss: 1.32.0
|
|
||||||
terser: 5.46.1
|
|
||||||
tsx: 4.21.0
|
|
||||||
yaml: 2.8.3
|
|
||||||
|
|
||||||
vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.12
|
esbuild: 0.25.12
|
||||||
|
|
@ -33022,6 +33124,10 @@ snapshots:
|
||||||
tsx: 4.21.0
|
tsx: 4.21.0
|
||||||
yaml: 2.8.3
|
yaml: 2.8.3
|
||||||
|
|
||||||
|
vitefu@1.1.3(vite@6.4.2(@types/node@20.19.39)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 6.4.2(@types/node@20.19.39)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
|
||||||
vitefu@1.1.3(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
vitefu@1.1.3(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
|
@ -33030,10 +33136,6 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.4.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
vite: 6.4.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
|
||||||
vitefu@1.1.3(vite@6.4.2(@types/node@24.12.2)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
|
||||||
optionalDependencies:
|
|
||||||
vite: 6.4.2(@types/node@24.12.2)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
|
||||||
|
|
||||||
vitefu@1.1.3(vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
vitefu@1.1.3(vite@6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
vite: 6.4.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mana/shared-ai": "workspace:*",
|
"@mana/shared-ai": "workspace:*",
|
||||||
"@mana/shared-hono": "workspace:*",
|
"@mana/shared-hono": "workspace:*",
|
||||||
|
"@mana/shared-types": "workspace:*",
|
||||||
"hono": "^4.7.0",
|
"hono": "^4.7.0",
|
||||||
"better-auth": "^1.4.3",
|
"better-auth": "^1.4.3",
|
||||||
"drizzle-orm": "^0.38.3",
|
"drizzle-orm": "^0.38.3",
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import {
|
||||||
} from '../email/send';
|
} from '../email/send';
|
||||||
import { sourceAppStore, passwordResetRedirectStore } from './stores';
|
import { sourceAppStore, passwordResetRedirectStore } from './stores';
|
||||||
import { TRUSTED_ORIGINS } from './sso-origins';
|
import { TRUSTED_ORIGINS } from './sso-origins';
|
||||||
|
import { assertValidSpaceMetadataForCreate, assertSpaceIsDeletable } from '../spaces';
|
||||||
|
|
||||||
// Re-export so existing imports (`import { TRUSTED_ORIGINS } from './better-auth.config'`)
|
// Re-export so existing imports (`import { TRUSTED_ORIGINS } from './better-auth.config'`)
|
||||||
// keep working. New code should import from './sso-origins' directly.
|
// keep working. New code should import from './sso-origins' directly.
|
||||||
|
|
@ -281,6 +282,21 @@ export function createBetterAuth(databaseUrl: string) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spaces — enforce that every organization carries a valid
|
||||||
|
* `metadata.type` (the Space type), and block deletion of the
|
||||||
|
* user's personal space. See docs/plans/spaces-foundation.md
|
||||||
|
* and ../spaces/metadata.ts.
|
||||||
|
*/
|
||||||
|
organizationHooks: {
|
||||||
|
beforeCreateOrganization: async ({ organization }) => {
|
||||||
|
assertValidSpaceMetadataForCreate(organization.metadata);
|
||||||
|
},
|
||||||
|
beforeDeleteOrganization: async ({ organization }) => {
|
||||||
|
assertSpaceIsDeletable(organization.metadata);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Custom roles and permissions
|
// Custom roles and permissions
|
||||||
organizationRole: {
|
organizationRole: {
|
||||||
owner: {
|
owner: {
|
||||||
|
|
|
||||||
15
services/mana-auth/src/spaces/index.ts
Normal file
15
services/mana-auth/src/spaces/index.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Spaces — multi-tenancy helpers for mana-auth.
|
||||||
|
*
|
||||||
|
* The canonical SpaceType + allowlist lives in @mana/shared-types. This
|
||||||
|
* barrel adds auth-side concerns: Better Auth hook helpers for validating
|
||||||
|
* organization metadata, and (future) slug generation for personal spaces.
|
||||||
|
*
|
||||||
|
* See docs/plans/spaces-foundation.md.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export {
|
||||||
|
assertValidSpaceMetadataForCreate,
|
||||||
|
assertSpaceIsDeletable,
|
||||||
|
buildSpaceMetadata,
|
||||||
|
} from './metadata';
|
||||||
84
services/mana-auth/src/spaces/metadata.spec.ts
Normal file
84
services/mana-auth/src/spaces/metadata.spec.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* Tests for Space-metadata validation used by Better Auth organization hooks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect } from 'bun:test';
|
||||||
|
import {
|
||||||
|
assertValidSpaceMetadataForCreate,
|
||||||
|
assertSpaceIsDeletable,
|
||||||
|
buildSpaceMetadata,
|
||||||
|
} from './metadata';
|
||||||
|
|
||||||
|
describe('assertValidSpaceMetadataForCreate', () => {
|
||||||
|
it('accepts metadata with every valid SpaceType', () => {
|
||||||
|
for (const type of ['personal', 'brand', 'club', 'family', 'team', 'practice'] as const) {
|
||||||
|
const parsed = assertValidSpaceMetadataForCreate({ type });
|
||||||
|
expect(parsed.type).toBe(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves extra metadata fields', () => {
|
||||||
|
const parsed = assertValidSpaceMetadataForCreate({
|
||||||
|
type: 'brand',
|
||||||
|
voiceDoc: 'hello',
|
||||||
|
uid: 'CH-123',
|
||||||
|
});
|
||||||
|
expect(parsed.voiceDoc).toBe('hello');
|
||||||
|
expect(parsed.uid).toBe('CH-123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects missing metadata', () => {
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate(null)).toThrow(/type/i);
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate(undefined)).toThrow(/type/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects missing type field', () => {
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate({})).toThrow(/type/i);
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate({ name: 'Edisconet' })).toThrow(/type/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects unknown SpaceType values', () => {
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate({ type: 'corporate' })).toThrow(/type/i);
|
||||||
|
expect(() => assertValidSpaceMetadataForCreate({ type: 'PERSONAL' })).toThrow(/type/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('assertSpaceIsDeletable', () => {
|
||||||
|
it('blocks deletion of personal spaces', () => {
|
||||||
|
expect(() => assertSpaceIsDeletable({ type: 'personal' })).toThrow(
|
||||||
|
/personal space cannot be deleted/i
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows deletion of other space types', () => {
|
||||||
|
for (const type of ['brand', 'club', 'family', 'team', 'practice'] as const) {
|
||||||
|
expect(() => assertSpaceIsDeletable({ type })).not.toThrow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows deletion when metadata is malformed (fail-open by design)', () => {
|
||||||
|
// If metadata is missing or invalid, we don't block — the delete endpoint
|
||||||
|
// enforces other permission checks (owner role, etc.) and we only want to
|
||||||
|
// guard the personal-space special case.
|
||||||
|
expect(() => assertSpaceIsDeletable(null)).not.toThrow();
|
||||||
|
expect(() => assertSpaceIsDeletable({})).not.toThrow();
|
||||||
|
expect(() => assertSpaceIsDeletable({ type: 'unknown' })).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buildSpaceMetadata', () => {
|
||||||
|
it('returns a metadata blob with the given type', () => {
|
||||||
|
expect(buildSpaceMetadata('club').type).toBe('club');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges extra fields after the type', () => {
|
||||||
|
const meta = buildSpaceMetadata('brand', { voiceDoc: 'X', uid: 'Y' });
|
||||||
|
expect(meta).toEqual({ type: 'brand', voiceDoc: 'X', uid: 'Y' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lets explicit type win even if extras try to override', () => {
|
||||||
|
// Extra is typed to exclude `type`, but at runtime someone could try.
|
||||||
|
const meta = buildSpaceMetadata('brand', { voiceDoc: 'X' } as Record<string, unknown>);
|
||||||
|
expect(meta.type).toBe('brand');
|
||||||
|
});
|
||||||
|
});
|
||||||
68
services/mana-auth/src/spaces/metadata.ts
Normal file
68
services/mana-auth/src/spaces/metadata.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Space metadata validation for Better Auth organization hooks.
|
||||||
|
*
|
||||||
|
* Every Better Auth organization in Mana must carry a `metadata.type` field
|
||||||
|
* that identifies the Space type (personal/brand/club/family/team/practice).
|
||||||
|
* This module enforces that contract at the plugin-hook layer.
|
||||||
|
*
|
||||||
|
* See docs/plans/spaces-foundation.md.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { APIError } from 'better-auth/api';
|
||||||
|
import {
|
||||||
|
SPACE_TYPES,
|
||||||
|
isSpaceType,
|
||||||
|
parseSpaceMetadata,
|
||||||
|
type SpaceMetadata,
|
||||||
|
type SpaceType,
|
||||||
|
} from '@mana/shared-types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the metadata blob that will be persisted for a new organization.
|
||||||
|
* Throws a Better Auth `APIError` (BAD_REQUEST) if the shape is invalid.
|
||||||
|
*
|
||||||
|
* Intended for `organizationHooks.beforeCreateOrganization`.
|
||||||
|
*/
|
||||||
|
export function assertValidSpaceMetadataForCreate(raw: unknown): SpaceMetadata {
|
||||||
|
const parsed = parseSpaceMetadata(raw);
|
||||||
|
if (!parsed) {
|
||||||
|
throw new APIError('BAD_REQUEST', {
|
||||||
|
message: `Organization metadata must include a valid "type" field. Expected one of: ${SPACE_TYPES.join(', ')}.`,
|
||||||
|
code: 'SPACE_METADATA_INVALID',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guard a delete call against removing the user's personal space.
|
||||||
|
* Better Auth will still allow admins/owners to delete other spaces — we only
|
||||||
|
* protect the auto-created personal one, because losing it would orphan all
|
||||||
|
* the user's private data.
|
||||||
|
*
|
||||||
|
* Intended for `organizationHooks.beforeDeleteOrganization`.
|
||||||
|
*/
|
||||||
|
export function assertSpaceIsDeletable(metadata: unknown): void {
|
||||||
|
const parsed = parseSpaceMetadata(metadata);
|
||||||
|
if (parsed?.type === 'personal') {
|
||||||
|
throw new APIError('FORBIDDEN', {
|
||||||
|
message: 'The personal space cannot be deleted. Delete the user account instead.',
|
||||||
|
code: 'SPACE_PERSONAL_UNDELETABLE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a metadata blob for a freshly-created space of a given type. Used by
|
||||||
|
* the signup-time personal-space auto-creator and by any future UI that
|
||||||
|
* creates spaces of other types.
|
||||||
|
*/
|
||||||
|
export function buildSpaceMetadata(
|
||||||
|
type: SpaceType,
|
||||||
|
extra: Omit<SpaceMetadata, 'type'> = {}
|
||||||
|
): SpaceMetadata {
|
||||||
|
if (!isSpaceType(type)) {
|
||||||
|
throw new Error(`Invalid SpaceType: ${String(type)}`);
|
||||||
|
}
|
||||||
|
return { ...extra, type };
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue