config: storybook,json,config (4 files)

This commit is contained in:
Richard Ward
2025-12-30 20:59:38 +00:00
parent 0be3dc9a3b
commit d5a2bf3efb
4 changed files with 360 additions and 1 deletions

View File

@@ -1,3 +1,3 @@
export { ACLAdapter } from './acl-adapter'
export { ACLAdapter } from './acl-adapter/acl-adapter'
export type { ACLAdapterOptions, ACLContext, ACLRule, User } from './acl-adapter/types'
export { defaultACLRules } from './acl/default-rules'

View File

@@ -0,0 +1,187 @@
/**
* Package Discovery
*
* Reads package metadata from packages/index.json and script manifests
* to auto-populate Storybook with all available Lua packages.
*/
import type { LuaPackageMetadata, LuaRenderContext } from '../types/lua-types'
export interface StorybookConfig {
packagesPath: string
packagesIndex: string
discovery: {
enabled: boolean
scanManifests: boolean
includedCategories: string[]
excludedPackages: string[]
minLevel: number
maxLevel: number
}
rendering: {
defaultContext: LuaRenderContext
contextVariants: Array<{
name: string
context: Partial<LuaRenderContext>
}>
}
scripts: {
renderFunctions: string[]
ignoredScripts: string[]
}
manualOverrides: Record<string, {
featured?: boolean
renders?: Record<string, { description?: string }>
}>
}
export interface DiscoveredPackage {
metadata: LuaPackageMetadata
scripts: DiscoveredScript[]
featured: boolean
}
export interface DiscoveredScript {
file: string
name: string
category?: string
description?: string
hasRenderFunction: boolean
}
export interface PackageIndex {
generatedAt: string
packages: LuaPackageMetadata[]
}
// Cache for loaded data
let configCache: StorybookConfig | null = null
let packagesCache: DiscoveredPackage[] | null = null
/**
* Load the storybook config
*/
export async function loadConfig(): Promise<StorybookConfig> {
if (configCache) return configCache
const response = await fetch('/storybook.config.json')
configCache = await response.json()
return configCache!
}
/**
* Load the packages index
*/
export async function loadPackagesIndex(): Promise<PackageIndex> {
const response = await fetch('/packages/index.json')
return await response.json()
}
/**
* Load script manifest for a package
*/
export async function loadScriptManifest(packageId: string): Promise<DiscoveredScript[]> {
try {
const response = await fetch(`/packages/${packageId}/seed/scripts/manifest.json`)
if (!response.ok) return []
const manifest = await response.json()
return manifest.scripts || []
} catch {
return []
}
}
/**
* Discover all packages based on config
*/
export async function discoverPackages(): Promise<DiscoveredPackage[]> {
if (packagesCache) return packagesCache
const config = await loadConfig()
const index = await loadPackagesIndex()
const discovered: DiscoveredPackage[] = []
for (const pkg of index.packages) {
// Apply filters
if (config.discovery.excludedPackages.includes(pkg.packageId)) continue
if (pkg.minLevel < config.discovery.minLevel) continue
if (pkg.minLevel > config.discovery.maxLevel) continue
if (!config.discovery.includedCategories.includes(pkg.category)) continue
// Load script manifest if enabled
let scripts: DiscoveredScript[] = []
if (config.discovery.scanManifests) {
scripts = await loadScriptManifest(pkg.packageId)
// Filter out ignored scripts
scripts = scripts.filter(s =>
!config.scripts.ignoredScripts.some(ignored =>
s.name.includes(ignored) || s.file.includes(ignored)
)
)
// Mark scripts that likely have render functions
scripts = scripts.map(s => ({
...s,
hasRenderFunction: config.scripts.renderFunctions.some(fn =>
s.name === fn || s.file.includes(fn)
)
}))
}
// Check for manual overrides
const override = config.manualOverrides[pkg.packageId]
discovered.push({
metadata: pkg,
scripts,
featured: override?.featured || false,
})
}
// Sort: featured first, then by name
discovered.sort((a, b) => {
if (a.featured && !b.featured) return -1
if (!a.featured && b.featured) return 1
return a.metadata.name.localeCompare(b.metadata.name)
})
packagesCache = discovered
return discovered
}
/**
* Get all context variants from config
*/
export async function getContextVariants(): Promise<Array<{ name: string; context: LuaRenderContext }>> {
const config = await loadConfig()
return config.rendering.contextVariants.map(variant => ({
name: variant.name,
context: {
...config.rendering.defaultContext,
...variant.context,
user: {
...config.rendering.defaultContext.user,
...variant.context.user,
},
} as LuaRenderContext,
}))
}
/**
* Get default context
*/
export async function getDefaultContext(): Promise<LuaRenderContext> {
const config = await loadConfig()
return config.rendering.defaultContext
}
/**
* Clear caches (useful for hot reloading)
*/
export function clearCaches(): void {
configCache = null
packagesCache = null
}

View File

@@ -0,0 +1,98 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Storybook Config",
"description": "Configuration for auto-discovering and rendering Lua packages in Storybook",
"type": "object",
"properties": {
"$schema": { "type": "string" },
"description": { "type": "string" },
"packagesPath": {
"type": "string",
"description": "Relative path to packages directory"
},
"packagesIndex": {
"type": "string",
"description": "Relative path to packages/index.json"
},
"discovery": {
"type": "object",
"properties": {
"enabled": { "type": "boolean" },
"scanManifests": { "type": "boolean" },
"includedCategories": {
"type": "array",
"items": { "type": "string" }
},
"excludedPackages": {
"type": "array",
"items": { "type": "string" }
},
"minLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
"maxLevel": { "type": "integer", "minimum": 1, "maximum": 6 }
}
},
"rendering": {
"type": "object",
"properties": {
"defaultContext": {
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": { "type": "string" },
"username": { "type": "string" },
"level": { "type": "integer" },
"email": { "type": "string" }
}
},
"nerdMode": { "type": "boolean" },
"theme": { "type": "string", "enum": ["light", "dark"] }
}
},
"contextVariants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"context": { "type": "object" }
},
"required": ["name", "context"]
}
}
}
},
"scripts": {
"type": "object",
"properties": {
"renderFunctions": {
"type": "array",
"items": { "type": "string" }
},
"ignoredScripts": {
"type": "array",
"items": { "type": "string" }
}
}
},
"manualOverrides": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"featured": { "type": "boolean" },
"renders": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"description": { "type": "string" }
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
{
"$schema": "./storybook-config.schema.json",
"description": "Auto-discovery configuration for Lua package Storybook",
"packagesPath": "../packages",
"packagesIndex": "../packages/index.json",
"discovery": {
"enabled": true,
"scanManifests": true,
"includedCategories": ["ui", "admin", "gaming", "social", "editors"],
"excludedPackages": ["shared", "lua_test"],
"minLevel": 1,
"maxLevel": 6
},
"rendering": {
"defaultContext": {
"user": {
"id": "storybook-user",
"username": "demo_user",
"level": 4,
"email": "demo@example.com"
},
"nerdMode": false,
"theme": "light"
},
"contextVariants": [
{
"name": "Guest",
"context": { "user": { "username": "guest", "level": 1 } }
},
{
"name": "Admin",
"context": { "user": { "username": "admin", "level": 4 }, "nerdMode": false }
},
{
"name": "Admin (Nerd Mode)",
"context": { "user": { "username": "admin", "level": 4 }, "nerdMode": true }
},
{
"name": "Supergod",
"context": { "user": { "username": "supergod", "level": 6 } }
}
]
},
"scripts": {
"renderFunctions": ["render", "main", "layout", "init"],
"ignoredScripts": ["test", "types", "utils"]
},
"manualOverrides": {
"dashboard": {
"featured": true,
"renders": {
"stats.card": { "description": "Single stat card with trend indicator" },
"stats.grid": { "description": "Grid of stat cards" },
"layout.standard": { "description": "Standard dashboard layout" }
}
},
"ui_level4": {
"featured": true,
"renders": {
"layout.render": { "description": "Admin panel main layout" }
}
},
"user_manager": {
"featured": true,
"renders": {
"render_users": { "description": "User management table" }
}
}
}
}