From d5a2bf3efbd1771be8097651b4501ec052046314 Mon Sep 17 00:00:00 2001 From: Richard Ward Date: Tue, 30 Dec 2025 20:59:38 +0000 Subject: [PATCH] config: storybook,json,config (4 files) --- dbal/development/src/adapters/acl-adapter.ts | 2 +- storybook/src/discovery/package-discovery.ts | 187 +++++++++++++++++++ storybook/storybook-config.schema.json | 98 ++++++++++ storybook/storybook.config.json | 74 ++++++++ 4 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 storybook/src/discovery/package-discovery.ts create mode 100644 storybook/storybook-config.schema.json create mode 100644 storybook/storybook.config.json diff --git a/dbal/development/src/adapters/acl-adapter.ts b/dbal/development/src/adapters/acl-adapter.ts index 9fbcd7d83..f8f0d269a 100644 --- a/dbal/development/src/adapters/acl-adapter.ts +++ b/dbal/development/src/adapters/acl-adapter.ts @@ -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' diff --git a/storybook/src/discovery/package-discovery.ts b/storybook/src/discovery/package-discovery.ts new file mode 100644 index 000000000..0aad3df1c --- /dev/null +++ b/storybook/src/discovery/package-discovery.ts @@ -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 + }> + } + scripts: { + renderFunctions: string[] + ignoredScripts: string[] + } + manualOverrides: Record + }> +} + +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 { + 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 { + const response = await fetch('/packages/index.json') + return await response.json() +} + +/** + * Load script manifest for a package + */ +export async function loadScriptManifest(packageId: string): Promise { + 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 { + 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> { + 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 { + const config = await loadConfig() + return config.rendering.defaultContext +} + +/** + * Clear caches (useful for hot reloading) + */ +export function clearCaches(): void { + configCache = null + packagesCache = null +} diff --git a/storybook/storybook-config.schema.json b/storybook/storybook-config.schema.json new file mode 100644 index 000000000..74f8cc419 --- /dev/null +++ b/storybook/storybook-config.schema.json @@ -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" } + } + } + } + } + } + } + } +} diff --git a/storybook/storybook.config.json b/storybook/storybook.config.json new file mode 100644 index 000000000..f20b5e283 --- /dev/null +++ b/storybook/storybook.config.json @@ -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" } + } + } + } +}