@objectstack/plugin-hono-server 4.0.4 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport {\n IHttpServer,\n RouteHandler,\n Middleware\n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\nexport interface HonoCorsOptions {\n enabled?: boolean;\n origins?: string | string[];\n methods?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n\n // Try to parse JSON body first if content-type is JSON\n if (c.req.header('content-type')?.includes('application/json')) {\n try {\n body = await c.req.json();\n } catch(e) {\n // If JSON parsing fails, try parseBody\n try {\n body = await c.req.parseBody();\n } catch(e2) {}\n }\n } else {\n // For non-JSON content types, use parseBody\n try {\n body = await c.req.parseBody();\n } catch(e) {}\n }\n\n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: c.req.header(),\n method: c.req.method,\n path: c.req.path\n };\n\n let capturedResponse: any;\n let streamController: ReadableStreamDefaultController | null = null;\n let streamEncoder: TextEncoder | null = null;\n let streamHeaders: Record<string, string> = {};\n let isStreaming = false;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => {\n c.header(name, value);\n streamHeaders[name] = value;\n return res;\n },\n write: (chunk: string | Uint8Array) => {\n isStreaming = true;\n if (streamController && streamEncoder) {\n const data = typeof chunk === 'string' ? streamEncoder.encode(chunk) : chunk;\n streamController.enqueue(data);\n }\n },\n end: () => {\n if (streamController) {\n streamController.close();\n }\n },\n };\n\n // Create a streaming response wrapper — if handler calls res.write(),\n // we return a ReadableStream; otherwise fall back to capturedResponse.\n const streamPromise = new Promise<Response | null>((resolve) => {\n const stream = new ReadableStream({\n start(controller) {\n streamController = controller;\n streamEncoder = new TextEncoder();\n },\n });\n\n // Run the handler; once it's done, check if streaming was used\n const result = handler(req as any, res as any);\n const done = result instanceof Promise ? result : Promise.resolve(result);\n done.then(() => {\n if (isStreaming) {\n resolve(new Response(stream, {\n status: 200,\n headers: streamHeaders,\n }));\n } else {\n // Not streaming — close the unused stream and return null\n streamController?.close();\n resolve(null);\n }\n }).catch(() => {\n streamController?.close();\n resolve(null);\n });\n });\n\n const streamResponse = await streamPromise;\n return streamResponse ?? capturedResponse;\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n\n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n // Path based middleware\n // Hono middleware signature is different (c, next) => ...\n this.app.use(pathOrHandler, async (c, next) => {\n // Simplistic conversion\n await handler({} as any, {} as any, next);\n });\n } else if (typeof pathOrHandler === 'function') {\n // Global middleware\n this.app.use('*', async (c, next) => {\n await pathOrHandler({} as any, {} as any, next);\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n\n const targetPort = port || this.port;\n const maxRetries = 20;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const tryPort = targetPort + attempt;\n try {\n await this.tryListen(tryPort);\n return;\n } catch (err: any) {\n if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n continue;\n }\n throw err;\n }\n }\n }\n\n private tryListen(port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = serve({\n fetch: this.app.fetch,\n port\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n this.server = server;\n server.on('error', (err: any) => {\n reject(err);\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer, IDataEngine } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer, HonoCorsOptions } from './adapter';\nimport { cors } from 'hono/cors';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n\n /**\n * CORS configuration. Set to `false` to disable entirely.\n * Enabled by default with origin '*'.\n * Can also be controlled via environment variables:\n * CORS_ENABLED, CORS_ORIGIN, CORS_CREDENTIALS, CORS_MAX_AGE\n */\n cors?: HonoCorsOptions | false;\n}\n\n/**\n * Hono Server Plugin\n *\n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n *\n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\n/**\n * Check if an origin matches a pattern with wildcards.\n * Supports patterns like:\n * - \"https://*.example.com\" - matches any subdomain\n * - \"http://localhost:*\" - matches any port\n * - \"https://*.objectui.org,https://*.objectstack.ai\" - comma-separated patterns\n *\n * @param origin The origin to check (e.g., \"https://app.example.com\")\n * @param pattern The pattern to match against (supports * wildcard)\n * @returns true if origin matches the pattern\n */\nfunction matchOriginPattern(origin: string, pattern: string): boolean {\n if (pattern === '*') return true;\n if (pattern === origin) return true;\n\n // Convert wildcard pattern to regex\n // Escape special regex characters except *\n const regexPattern = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special chars\n .replace(/\\*/g, '.*'); // Convert * to .*\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(origin);\n}\n\n/**\n * Create a CORS origin matcher function that supports wildcard patterns.\n *\n * @param patterns Single pattern, array of patterns, or comma-separated patterns\n * @returns Function that returns the origin if it matches, or null/undefined\n */\nfunction createOriginMatcher(\n patterns: string | string[]\n): (origin: string) => string | undefined | null {\n // Normalize to array\n let patternList: string[];\n if (typeof patterns === 'string') {\n // Handle comma-separated patterns\n patternList = patterns.includes(',')\n ? patterns.split(',').map(s => s.trim()).filter(Boolean)\n : [patterns];\n } else {\n patternList = patterns;\n }\n\n // Return matcher function\n return (requestOrigin: string) => {\n for (const pattern of patternList) {\n if (matchOriginPattern(requestOrigin, pattern)) {\n return requestOrigin;\n }\n }\n return null;\n };\n}\n\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n\n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n\n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = {\n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', {\n port: this.options.port,\n staticRoot: this.options.staticRoot\n });\n\n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n\n // ─── CORS Middleware ──────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables.\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (this.options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof this.options.cors === 'object' ? this.options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n let configuredOrigin: string | string[];\n if (corsOpts.origins) {\n configuredOrigin = corsOpts.origins;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // Determine origin handler based on configuration\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n\n // Check if patterns contain wildcards (*, subdomain patterns, port patterns)\n const hasWildcard = (patterns: string | string[]): boolean => {\n const list = Array.isArray(patterns) ? patterns : [patterns];\n return list.some(p => p.includes('*'));\n };\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // For wildcard patterns (like \"https://*.example.com\"), always use a matcher function.\n // For exact origins, we can pass them directly as string/array.\n if (configuredOrigin === '*' && credentials) {\n // Credentials mode with '*' - reflect the request origin\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else if (hasWildcard(configuredOrigin)) {\n // Wildcard patterns (including better-auth style patterns like \"https://*.objectui.org\")\n // Use pattern matcher to support subdomain and port wildcards\n origin = createOriginMatcher(configuredOrigin);\n } else {\n // Exact origin(s) - pass through as-is\n origin = configuredOrigin;\n }\n\n const rawApp = this.server.getRawApp();\n rawApp.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],\n exposeHeaders: [],\n credentials,\n maxAge,\n }));\n\n ctx.logger.debug('CORS middleware enabled', { origin: configuredOrigin, credentials });\n }\n }\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map\n ? Array.from(rawKernel.plugins.values())\n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n\n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, {\n path: baseRoute,\n root: plugin.staticPath\n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n\n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n\n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', {\n to: routes,\n from: mountRoot,\n rewrite: mount.rewrite,\n spa: mount.spa\n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route,\n serveStatic({\n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n\n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({\n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n\n await this.server.listen(port);\n\n const actualPort = this.server.getPort();\n if (actualPort !== port) {\n ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);\n }\n ctx.logger.info('HTTP server started successfully', {\n port: actualPort,\n url: `http://localhost:${actualPort}`\n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.redirect(`${prefix}/discovery`));\n rawApp.get(`${prefix}/discovery`, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to ObjectQL service directly\n const getObjectQL = () => ctx.getService<IDataEngine>('objectql');\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const res = await ql.insert(object, data);\n const record = { ...data, ...res };\n return c.json({ object, id: record.id, record });\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n const id = c.req.param('id');\n let all = await ql.find(object);\n if (!all) all = [];\n const match = all.find((i: any) => i.id === id);\n return match ? c.json({ object, id, record: match }) : c.json({ error: 'Not found' }, 404);\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n let all = await ql.find(object);\n if (!Array.isArray(all) && all && (all as any).value) all = (all as any).value;\n if (!all) all = [];\n return c.json({ object, records: all, total: all.length });\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n"],"mappings":"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","names":["resolve","path","import_serve_static"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts","../src/pattern-matcher.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\nexport * from './pattern-matcher';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport {\n IHttpServer,\n RouteHandler,\n Middleware\n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\nexport interface HonoCorsOptions {\n enabled?: boolean;\n origins?: string | string[];\n methods?: string[];\n /**\n * Request headers allowed on preflight (`Access-Control-Allow-Headers`).\n *\n * Defaults to `['Content-Type', 'Authorization', 'X-Requested-With']`,\n * which is sufficient for cookie and bearer-token auth.\n */\n allowHeaders?: string[];\n /**\n * Response headers exposed to JS (`Access-Control-Expose-Headers`).\n *\n * Defaults to `['set-auth-token']` so that better-auth's `bearer()` plugin\n * can hand rotated session tokens to cross-origin clients. User-supplied\n * values are merged with this default — `set-auth-token` is always\n * exposed unless CORS is disabled entirely.\n */\n exposeHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n\n const contentType = c.req.header('content-type') ?? '';\n const isOctetStream = contentType.includes('application/octet-stream');\n\n // Try to parse JSON body first if content-type is JSON\n if (contentType.includes('application/json')) {\n try {\n body = await c.req.json();\n } catch(e) {\n // If JSON parsing fails, try parseBody\n try {\n body = await c.req.parseBody();\n } catch(e2) {}\n }\n } else if (!isOctetStream) {\n // For non-JSON / non-binary content types, use parseBody\n // (Skipping for octet-stream so the raw stream stays consumable\n // via `req.rawBody()` for binary uploads.)\n try {\n body = await c.req.parseBody();\n } catch(e) {}\n }\n\n const rawHeaders = c.req.header();\n // Fetch API `Request` objects don't expose the `Host` header\n // (it's a forbidden header — derived from the URL by the\n // transport). Hostname-based routing in REST/dispatcher\n // depends on it, so we backfill from `c.req.url`.\n if (!rawHeaders.host) {\n try {\n const u = new URL(c.req.url);\n if (u.host) rawHeaders.host = u.host;\n } catch { /* non-URL request, leave headers as-is */ }\n }\n\n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: rawHeaders,\n method: c.req.method,\n path: c.req.path,\n rawBody: async () => {\n const ab = await c.req.arrayBuffer();\n return Buffer.from(ab);\n },\n };\n\n let capturedResponse: any;\n let streamController: ReadableStreamDefaultController | null = null;\n let streamEncoder: TextEncoder | null = null;\n let streamHeaders: Record<string, string> = {};\n let isStreaming = false;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => {\n c.header(name, value);\n streamHeaders[name] = value;\n return res;\n },\n write: (chunk: string | Uint8Array) => {\n isStreaming = true;\n if (streamController && streamEncoder) {\n const data = typeof chunk === 'string' ? streamEncoder.encode(chunk) : chunk;\n streamController.enqueue(data);\n }\n },\n end: () => {\n if (streamController) {\n streamController.close();\n }\n },\n };\n\n // Create a streaming response wrapper — if handler calls res.write(),\n // we return a ReadableStream; otherwise fall back to capturedResponse.\n const streamPromise = new Promise<Response | null>((resolve) => {\n const stream = new ReadableStream({\n start(controller) {\n streamController = controller;\n streamEncoder = new TextEncoder();\n },\n });\n\n // Run the handler; once it's done, check if streaming was used\n const result = handler(req as any, res as any);\n const done = result instanceof Promise ? result : Promise.resolve(result);\n done.then(() => {\n if (isStreaming) {\n resolve(new Response(stream, {\n status: 200,\n headers: streamHeaders,\n }));\n } else {\n // Not streaming — close the unused stream and return null\n streamController?.close();\n resolve(null);\n }\n }).catch((err) => {\n streamController?.close();\n resolve(null);\n });\n });\n\n const streamResponse = await streamPromise;\n return streamResponse ?? capturedResponse ?? c.json({ error: 'No response from handler' }, 500);\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n\n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n this.app.use(pathOrHandler, async (c, next) => {\n let nextCalled = false;\n const wrappedNext = () => { nextCalled = true; return next(); };\n await handler({} as any, {} as any, wrappedNext);\n if (!nextCalled) await next();\n });\n } else if (typeof pathOrHandler === 'function') {\n this.app.use('*', async (c, next) => {\n let nextCalled = false;\n const wrappedNext = () => { nextCalled = true; return next(); };\n await pathOrHandler({} as any, {} as any, wrappedNext);\n if (!nextCalled) await next();\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n\n const targetPort = port || this.port;\n const maxRetries = 20;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const tryPort = targetPort + attempt;\n try {\n await this.tryListen(tryPort);\n return;\n } catch (err: any) {\n if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n continue;\n }\n throw err;\n }\n }\n }\n\n private tryListen(port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = serve({\n fetch: this.app.fetch,\n port\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n this.server = server;\n server.on('error', (err: any) => {\n reject(err);\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (!this.server) return;\n // Destroy all keep-alive sockets so the server stops immediately\n if (typeof this.server.closeAllConnections === 'function') {\n this.server.closeAllConnections();\n }\n await new Promise<void>((resolve, reject) => {\n this.server.close((err: any) => (err ? reject(err) : resolve()));\n });\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer, IDataEngine } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer, HonoCorsOptions } from './adapter';\nimport { cors } from 'hono/cors';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { createOriginMatcher, hasWildcardPattern, isLocalhostOrigin } from './pattern-matcher';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n\n /**\n * CORS configuration. Set to `false` to disable entirely.\n * Enabled by default with origin '*'.\n * Can also be controlled via environment variables:\n * CORS_ENABLED, CORS_ORIGIN, CORS_CREDENTIALS, CORS_MAX_AGE\n */\n cors?: HonoCorsOptions | false;\n}\n\n/**\n * Hono Server Plugin\n *\n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n *\n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n\n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n\n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = {\n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', {\n port: this.options.port,\n staticRoot: this.options.staticRoot\n });\n\n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n\n // ─── CORS Middleware ──────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables.\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (this.options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof this.options.cors === 'object' ? this.options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n let configuredOrigin: string | string[];\n if (corsOpts.origins) {\n configuredOrigin = corsOpts.origins;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // Determine origin handler based on configuration.\n // Always use a function so that localhost origins are\n // automatically allowed regardless of the configured\n // pattern list (handled inside matchOriginPattern /\n // createOriginMatcher).\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // For wildcard patterns (like \"https://*.example.com\"), always use a matcher function.\n // For exact origins, we can pass them directly as string/array.\n if (configuredOrigin === '*' && credentials) {\n // Credentials mode with '*' - reflect the request origin\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else if (hasWildcardPattern(configuredOrigin)) {\n // Wildcard patterns (including better-auth style patterns like \"https://*.objectui.org\")\n // Use pattern matcher to support subdomain and port wildcards\n origin = createOriginMatcher(configuredOrigin);\n } else {\n // Exact origin(s) — wrap in a function so localhost is\n // still auto-allowed via the matcher.\n const matcher = createOriginMatcher(configuredOrigin);\n origin = (requestOrigin: string) => matcher(requestOrigin);\n }\n\n const rawApp = this.server.getRawApp();\n // Always include `set-auth-token` in exposed headers so that\n // the better-auth `bearer()` plugin can deliver rotated\n // session tokens to cross-origin clients (see plugin-auth).\n // User-supplied exposeHeaders are merged with this default.\n const defaultAllowHeaders = ['Content-Type', 'Authorization', 'X-Requested-With', 'X-Tenant-ID', 'X-Project-Id'];\n const defaultExposeHeaders = ['set-auth-token'];\n const allowHeaders = corsOpts.allowHeaders ?? defaultAllowHeaders;\n const exposeHeaders = Array.from(new Set([\n ...defaultExposeHeaders,\n ...(corsOpts.exposeHeaders ?? []),\n ]));\n\n rawApp.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders,\n exposeHeaders,\n credentials,\n maxAge,\n }));\n\n ctx.logger.debug('CORS middleware enabled', { origin: configuredOrigin, credentials });\n }\n }\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map\n ? Array.from(rawKernel.plugins.values())\n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n\n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, {\n path: baseRoute,\n root: plugin.staticPath\n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n\n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n\n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', {\n to: routes,\n from: mountRoot,\n rewrite: mount.rewrite,\n spa: mount.spa\n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route,\n serveStatic({\n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n\n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({\n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Catch-all: ensure unmatched requests always get a proper Response\n // (prevents Hono \"Context is not finalized\" error)\n const rawAppForNotFound = this.server.getRawApp();\n if (typeof rawAppForNotFound.notFound === 'function') {\n rawAppForNotFound.notFound((c: any) => c.json({ error: 'Not found' }, 404));\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n\n await this.server.listen(port);\n\n const actualPort = this.server.getPort();\n if (actualPort !== port) {\n ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);\n }\n ctx.logger.info('HTTP server started successfully', {\n port: actualPort,\n url: `http://localhost:${actualPort}`\n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.redirect(`${prefix}/discovery`));\n rawApp.get(`${prefix}/discovery`, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to ObjectQL service directly\n const getObjectQL = () => ctx.getService<IDataEngine>('objectql');\n\n // Helper: resolve ExecutionContext from request headers (cookie session\n // or API key). Mirrors the runtime's resolveExecutionContext but\n // self-contained to avoid a cross-package dep. We DO query the\n // `sys_user_permission_set` link tables because hardcoding a single\n // permission set name (e.g. `member_default`) would silently ignore\n // any explicit admin / role assignment — including the platform-admin\n // promotion seeded by `bootstrapPlatformAdmin`.\n const resolveCtx = async (c: any): Promise<any | undefined> => {\n try {\n const authService: any = ctx.getService('auth');\n if (!authService) return undefined;\n let api: any = authService.api;\n if (!api && typeof authService.getApi === 'function') {\n api = await authService.getApi();\n }\n if (!api?.getSession) return undefined;\n const session = await api.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) return undefined;\n const userId = session.user.id;\n const tenantId = session.session?.activeOrganizationId ?? undefined;\n const permissions: string[] = [];\n const roles: string[] = [];\n try {\n const ql = getObjectQL();\n const sysCtx = { context: { isSystem: true } };\n // Roles via sys_member (org-scoped if active org).\n const memberRows = await ql?.find?.(\n 'sys_member',\n {\n where: tenantId\n ? { user_id: userId, organization_id: tenantId }\n : { user_id: userId },\n limit: 50,\n ...sysCtx,\n } as any,\n ).catch(() => []);\n for (const m of (memberRows ?? []) as any[]) {\n if (typeof m.role === 'string') {\n for (const r of m.role.split(',').map((s: string) => s.trim()).filter(Boolean)) {\n if (!roles.includes(r)) roles.push(r);\n }\n }\n }\n // User-scoped permission sets — match BOTH (a) the active\n // org's link rows and (b) the cross-tenant rows\n // (organization_id IS NULL) so the platform-admin\n // promotion seeded by `bootstrapPlatformAdmin` applies\n // regardless of the user's active org.\n const upsRows = await ql?.find?.(\n 'sys_user_permission_set',\n { where: { user_id: userId }, limit: 100, ...sysCtx } as any,\n ).catch(() => []);\n const psIds = new Set<string>();\n for (const r of (upsRows ?? []) as any[]) {\n const orgScope = r.organization_id ?? null;\n if (!orgScope || (tenantId && orgScope === tenantId)) {\n const pid = r.permission_set_id ?? r.permissionSetId;\n if (pid) psIds.add(pid);\n }\n }\n if (psIds.size > 0) {\n const psRows = await ql?.find?.(\n 'sys_permission_set',\n { where: { id: { $in: Array.from(psIds) } }, limit: 500, ...sysCtx } as any,\n ).catch(() => []);\n for (const ps of (psRows ?? []) as any[]) {\n if (ps.name && !permissions.includes(ps.name)) permissions.push(ps.name);\n }\n }\n } catch {\n /* fall through with whatever we resolved so far */\n }\n return {\n userId,\n tenantId,\n roles,\n permissions,\n isSystem: false,\n };\n } catch {\n return undefined;\n }\n };\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const execCtx = await resolveCtx(c);\n try {\n const res = await ql.insert(object, data, { context: execCtx } as any);\n const record = { ...data, ...res };\n return c.json({ object, id: record.id, record });\n } catch (err: any) {\n if (err?.code === 'PERMISSION_DENIED' || err?.name === 'PermissionDeniedError') {\n return c.json({ error: err.message ?? 'Forbidden' }, 403);\n }\n throw err;\n }\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n const id = c.req.param('id');\n const execCtx = await resolveCtx(c);\n try {\n let all = await ql.find(object, { context: execCtx } as any);\n if (!all) all = [];\n const match = all.find((i: any) => i.id === id);\n return match ? c.json({ object, id, record: match }) : c.json({ error: 'Not found' }, 404);\n } catch (err: any) {\n if (err?.code === 'PERMISSION_DENIED' || err?.name === 'PermissionDeniedError') {\n return c.json({ error: err.message ?? 'Forbidden' }, 403);\n }\n throw err;\n }\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const ql = getObjectQL();\n if (!ql) return c.json({ error: 'Data service not available' }, 503);\n const object = c.req.param('object');\n const execCtx = await resolveCtx(c);\n try {\n let all = await ql.find(object, { context: execCtx } as any);\n if (!Array.isArray(all) && all && (all as any).value) all = (all as any).value;\n if (!all) all = [];\n return c.json({ object, records: all, total: all.length });\n } catch (err: any) {\n if (err?.code === 'PERMISSION_DENIED' || err?.name === 'PermissionDeniedError') {\n return c.json({ error: err.message ?? 'Forbidden' }, 403);\n }\n throw err;\n }\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * CORS origin pattern matching utilities.\n *\n * Supports the same wildcard syntax as better-auth's `trustedOrigins`:\n * - `*` → matches any origin\n * - `https://*.example.com` → matches any subdomain\n * - `http://localhost:*` → matches any port\n * - Comma-separated list of the above\n *\n * These helpers are shared between the Hono plugin's CORS middleware and\n * consumers that need to apply CORS headers outside the Hono request\n * pipeline (e.g., the Vercel serverless entrypoint's preflight\n * short-circuit in `apps/objectos`). Keeping a single implementation\n * ensures both paths stay consistent — divergence caused bug where\n * wildcard `CORS_ORIGIN` values worked locally but produced browser\n * CORS errors on Vercel.\n */\n\n/**\n * Returns true when the origin points to localhost (any port, http or https).\n *\n * Matches:\n * - `http://localhost`\n * - `http://localhost:3000`\n * - `https://localhost:8443`\n * - `http://127.0.0.1:5173`\n * - `http://[::1]:3000`\n */\nexport function isLocalhostOrigin(origin: string): boolean {\n return /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|\\[::1\\])(:\\d+)?$/.test(origin);\n}\n\n/**\n * Check if an origin matches a pattern with wildcards.\n *\n * Localhost origins (`http(s)://localhost:<any-port>`, `127.0.0.1`, `[::1]`)\n * are **always allowed** regardless of the pattern — this removes the need to\n * enumerate every development port in `CORS_ORIGIN`.\n *\n * @param origin The origin to check (e.g., `https://app.example.com`)\n * @param pattern The pattern to match against (supports `*` wildcard)\n * @returns true if origin matches the pattern\n */\nexport function matchOriginPattern(origin: string, pattern: string): boolean {\n // Always allow localhost for development convenience\n if (isLocalhostOrigin(origin)) return true;\n\n if (pattern === '*') return true;\n if (pattern === origin) return true;\n\n // Convert wildcard pattern to regex:\n // 1. Escape regex special chars EXCEPT `*`\n // 2. Replace `*` with `.*`\n const regexPattern = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(origin);\n}\n\n/**\n * Normalize a single string / comma-separated string / array into a\n * trimmed array of non-empty patterns.\n */\nexport function normalizeOriginPatterns(patterns: string | string[]): string[] {\n if (Array.isArray(patterns)) {\n return patterns.map(p => p.trim()).filter(Boolean);\n }\n return patterns.includes(',')\n ? patterns.split(',').map(s => s.trim()).filter(Boolean)\n : [patterns.trim()].filter(Boolean);\n}\n\n/**\n * Create a CORS origin matcher function that supports wildcard patterns.\n *\n * The returned function follows Hono's `cors({ origin })` contract:\n * given the request's `Origin` header, it returns the origin to echo\n * back in `Access-Control-Allow-Origin`, or `null` if the origin is not\n * allowed.\n *\n * @param patterns Single pattern, array of patterns, or comma-separated patterns\n */\nexport function createOriginMatcher(\n patterns: string | string[]\n): (origin: string) => string | null {\n const patternList = normalizeOriginPatterns(patterns);\n\n return (requestOrigin: string) => {\n if (!requestOrigin) return null;\n for (const pattern of patternList) {\n if (matchOriginPattern(requestOrigin, pattern)) {\n return requestOrigin;\n }\n }\n return null;\n };\n}\n\n/**\n * True if any pattern in the given list contains a `*` wildcard.\n */\nexport function hasWildcardPattern(patterns: string | string[]): boolean {\n const list = Array.isArray(patterns) ? patterns : [patterns];\n return list.some(p => p.includes('*'));\n}\n"],"mappings":"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","names":["resolve","path","import_serve_static"]}
package/dist/index.mjs CHANGED
@@ -22,7 +22,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
24
  HonoHttpServer: () => HonoHttpServer,
25
- HonoServerPlugin: () => HonoServerPlugin
25
+ HonoServerPlugin: () => HonoServerPlugin,
26
+ createOriginMatcher: () => createOriginMatcher,
27
+ hasWildcardPattern: () => hasWildcardPattern,
28
+ isLocalhostOrigin: () => isLocalhostOrigin,
29
+ matchOriginPattern: () => matchOriginPattern,
30
+ normalizeOriginPatterns: () => normalizeOriginPatterns
26
31
  });
27
32
 
28
33
  // src/adapter.ts
@@ -48,7 +53,9 @@ var HonoHttpServer = class {
48
53
  wrap(handler) {
49
54
  return async (c) => {
50
55
  let body = {};
51
- if (c.req.header("content-type")?.includes("application/json")) {
56
+ const contentType = c.req.header("content-type") ?? "";
57
+ const isOctetStream = contentType.includes("application/octet-stream");
58
+ if (contentType.includes("application/json")) {
52
59
  try {
53
60
  body = await c.req.json();
54
61
  } catch (e) {
@@ -57,19 +64,31 @@ var HonoHttpServer = class {
57
64
  } catch (e2) {
58
65
  }
59
66
  }
60
- } else {
67
+ } else if (!isOctetStream) {
61
68
  try {
62
69
  body = await c.req.parseBody();
63
70
  } catch (e) {
64
71
  }
65
72
  }
73
+ const rawHeaders = c.req.header();
74
+ if (!rawHeaders.host) {
75
+ try {
76
+ const u = new URL(c.req.url);
77
+ if (u.host) rawHeaders.host = u.host;
78
+ } catch {
79
+ }
80
+ }
66
81
  const req = {
67
82
  params: c.req.param(),
68
83
  query: c.req.query(),
69
84
  body,
70
- headers: c.req.header(),
85
+ headers: rawHeaders,
71
86
  method: c.req.method,
72
- path: c.req.path
87
+ path: c.req.path,
88
+ rawBody: async () => {
89
+ const ab = await c.req.arrayBuffer();
90
+ return Buffer.from(ab);
91
+ }
73
92
  };
74
93
  let capturedResponse;
75
94
  let streamController = null;
@@ -124,13 +143,13 @@ var HonoHttpServer = class {
124
143
  streamController?.close();
125
144
  resolve2(null);
126
145
  }
127
- }).catch(() => {
146
+ }).catch((err) => {
128
147
  streamController?.close();
129
148
  resolve2(null);
130
149
  });
131
150
  });
132
151
  const streamResponse = await streamPromise;
133
- return streamResponse ?? capturedResponse;
152
+ return streamResponse ?? capturedResponse ?? c.json({ error: "No response from handler" }, 500);
134
153
  };
135
154
  }
136
155
  get(path2, handler) {
@@ -151,11 +170,23 @@ var HonoHttpServer = class {
151
170
  use(pathOrHandler, handler) {
152
171
  if (typeof pathOrHandler === "string" && handler) {
153
172
  this.app.use(pathOrHandler, async (c, next) => {
154
- await handler({}, {}, next);
173
+ let nextCalled = false;
174
+ const wrappedNext = () => {
175
+ nextCalled = true;
176
+ return next();
177
+ };
178
+ await handler({}, {}, wrappedNext);
179
+ if (!nextCalled) await next();
155
180
  });
156
181
  } else if (typeof pathOrHandler === "function") {
157
182
  this.app.use("*", async (c, next) => {
158
- await pathOrHandler({}, {}, next);
183
+ let nextCalled = false;
184
+ const wrappedNext = () => {
185
+ nextCalled = true;
186
+ return next();
187
+ };
188
+ await pathOrHandler({}, {}, wrappedNext);
189
+ if (!nextCalled) await next();
159
190
  });
160
191
  }
161
192
  }
@@ -210,9 +241,13 @@ var HonoHttpServer = class {
210
241
  return this.app;
211
242
  }
212
243
  async close() {
213
- if (this.server && typeof this.server.close === "function") {
214
- this.server.close();
244
+ if (!this.server) return;
245
+ if (typeof this.server.closeAllConnections === "function") {
246
+ this.server.closeAllConnections();
215
247
  }
248
+ await new Promise((resolve2, reject) => {
249
+ this.server.close((err) => err ? reject(err) : resolve2());
250
+ });
216
251
  }
217
252
  };
218
253
 
@@ -221,21 +256,29 @@ import { cors } from "hono/cors";
221
256
  import { serveStatic as serveStatic2 } from "@hono/node-server/serve-static";
222
257
  import * as fs from "fs";
223
258
  import * as path from "path";
259
+
260
+ // src/pattern-matcher.ts
261
+ function isLocalhostOrigin(origin) {
262
+ return /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(origin);
263
+ }
224
264
  function matchOriginPattern(origin, pattern) {
265
+ if (isLocalhostOrigin(origin)) return true;
225
266
  if (pattern === "*") return true;
226
267
  if (pattern === origin) return true;
227
268
  const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
228
269
  const regex = new RegExp(`^${regexPattern}$`);
229
270
  return regex.test(origin);
230
271
  }
231
- function createOriginMatcher(patterns) {
232
- let patternList;
233
- if (typeof patterns === "string") {
234
- patternList = patterns.includes(",") ? patterns.split(",").map((s) => s.trim()).filter(Boolean) : [patterns];
235
- } else {
236
- patternList = patterns;
272
+ function normalizeOriginPatterns(patterns) {
273
+ if (Array.isArray(patterns)) {
274
+ return patterns.map((p) => p.trim()).filter(Boolean);
237
275
  }
276
+ return patterns.includes(",") ? patterns.split(",").map((s) => s.trim()).filter(Boolean) : [patterns.trim()].filter(Boolean);
277
+ }
278
+ function createOriginMatcher(patterns) {
279
+ const patternList = normalizeOriginPatterns(patterns);
238
280
  return (requestOrigin) => {
281
+ if (!requestOrigin) return null;
239
282
  for (const pattern of patternList) {
240
283
  if (matchOriginPattern(requestOrigin, pattern)) {
241
284
  return requestOrigin;
@@ -244,6 +287,12 @@ function createOriginMatcher(patterns) {
244
287
  return null;
245
288
  };
246
289
  }
290
+ function hasWildcardPattern(patterns) {
291
+ const list = Array.isArray(patterns) ? patterns : [patterns];
292
+ return list.some((p) => p.includes("*"));
293
+ }
294
+
295
+ // src/hono-plugin.ts
247
296
  var HonoServerPlugin = class {
248
297
  constructor(options = {}) {
249
298
  __publicField(this, "name", "com.objectstack.server.hono");
@@ -279,23 +328,27 @@ var HonoServerPlugin = class {
279
328
  const credentials = corsOpts.credentials ?? process.env.CORS_CREDENTIALS !== "false";
280
329
  const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);
281
330
  let origin;
282
- const hasWildcard = (patterns) => {
283
- const list = Array.isArray(patterns) ? patterns : [patterns];
284
- return list.some((p) => p.includes("*"));
285
- };
286
331
  if (configuredOrigin === "*" && credentials) {
287
332
  origin = (requestOrigin) => requestOrigin || "*";
288
- } else if (hasWildcard(configuredOrigin)) {
333
+ } else if (hasWildcardPattern(configuredOrigin)) {
289
334
  origin = createOriginMatcher(configuredOrigin);
290
335
  } else {
291
- origin = configuredOrigin;
336
+ const matcher = createOriginMatcher(configuredOrigin);
337
+ origin = (requestOrigin) => matcher(requestOrigin);
292
338
  }
293
339
  const rawApp = this.server.getRawApp();
340
+ const defaultAllowHeaders = ["Content-Type", "Authorization", "X-Requested-With", "X-Tenant-ID", "X-Project-Id"];
341
+ const defaultExposeHeaders = ["set-auth-token"];
342
+ const allowHeaders = corsOpts.allowHeaders ?? defaultAllowHeaders;
343
+ const exposeHeaders = Array.from(/* @__PURE__ */ new Set([
344
+ ...defaultExposeHeaders,
345
+ ...corsOpts.exposeHeaders ?? []
346
+ ]));
294
347
  rawApp.use("*", cors({
295
348
  origin,
296
349
  allowMethods: corsOpts.methods || ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
297
- allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
298
- exposeHeaders: [],
350
+ allowHeaders,
351
+ exposeHeaders,
299
352
  credentials,
300
353
  maxAge
301
354
  }));
@@ -396,6 +449,10 @@ var HonoServerPlugin = class {
396
449
  });
397
450
  }
398
451
  }
452
+ const rawAppForNotFound = this.server.getRawApp();
453
+ if (typeof rawAppForNotFound.notFound === "function") {
454
+ rawAppForNotFound.notFound((c) => c.json({ error: "Not found" }, 404));
455
+ }
399
456
  ctx.hook("kernel:ready", async () => {
400
457
  if (this.options.registerStandardEndpoints) {
401
458
  this.registerDiscoveryAndCrudEndpoints(ctx);
@@ -452,33 +509,124 @@ var HonoServerPlugin = class {
452
509
  rawApp.get(`${prefix}/discovery`, (c) => c.json({ data: discovery }));
453
510
  ctx.logger.info("Registered discovery endpoints", { prefix });
454
511
  const getObjectQL = () => ctx.getService("objectql");
512
+ const resolveCtx = async (c) => {
513
+ try {
514
+ const authService = ctx.getService("auth");
515
+ if (!authService) return void 0;
516
+ let api = authService.api;
517
+ if (!api && typeof authService.getApi === "function") {
518
+ api = await authService.getApi();
519
+ }
520
+ if (!api?.getSession) return void 0;
521
+ const session = await api.getSession({ headers: c.req.raw.headers });
522
+ if (!session?.user?.id) return void 0;
523
+ const userId = session.user.id;
524
+ const tenantId = session.session?.activeOrganizationId ?? void 0;
525
+ const permissions = [];
526
+ const roles = [];
527
+ try {
528
+ const ql = getObjectQL();
529
+ const sysCtx = { context: { isSystem: true } };
530
+ const memberRows = await ql?.find?.(
531
+ "sys_member",
532
+ {
533
+ where: tenantId ? { user_id: userId, organization_id: tenantId } : { user_id: userId },
534
+ limit: 50,
535
+ ...sysCtx
536
+ }
537
+ ).catch(() => []);
538
+ for (const m of memberRows ?? []) {
539
+ if (typeof m.role === "string") {
540
+ for (const r of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
541
+ if (!roles.includes(r)) roles.push(r);
542
+ }
543
+ }
544
+ }
545
+ const upsRows = await ql?.find?.(
546
+ "sys_user_permission_set",
547
+ { where: { user_id: userId }, limit: 100, ...sysCtx }
548
+ ).catch(() => []);
549
+ const psIds = /* @__PURE__ */ new Set();
550
+ for (const r of upsRows ?? []) {
551
+ const orgScope = r.organization_id ?? null;
552
+ if (!orgScope || tenantId && orgScope === tenantId) {
553
+ const pid = r.permission_set_id ?? r.permissionSetId;
554
+ if (pid) psIds.add(pid);
555
+ }
556
+ }
557
+ if (psIds.size > 0) {
558
+ const psRows = await ql?.find?.(
559
+ "sys_permission_set",
560
+ { where: { id: { $in: Array.from(psIds) } }, limit: 500, ...sysCtx }
561
+ ).catch(() => []);
562
+ for (const ps of psRows ?? []) {
563
+ if (ps.name && !permissions.includes(ps.name)) permissions.push(ps.name);
564
+ }
565
+ }
566
+ } catch {
567
+ }
568
+ return {
569
+ userId,
570
+ tenantId,
571
+ roles,
572
+ permissions,
573
+ isSystem: false
574
+ };
575
+ } catch {
576
+ return void 0;
577
+ }
578
+ };
455
579
  rawApp.post(`${prefix}/data/:object`, async (c) => {
456
580
  const ql = getObjectQL();
457
581
  if (!ql) return c.json({ error: "Data service not available" }, 503);
458
582
  const object = c.req.param("object");
459
583
  const data = await c.req.json().catch(() => ({}));
460
- const res = await ql.insert(object, data);
461
- const record = { ...data, ...res };
462
- return c.json({ object, id: record.id, record });
584
+ const execCtx = await resolveCtx(c);
585
+ try {
586
+ const res = await ql.insert(object, data, { context: execCtx });
587
+ const record = { ...data, ...res };
588
+ return c.json({ object, id: record.id, record });
589
+ } catch (err) {
590
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
591
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
592
+ }
593
+ throw err;
594
+ }
463
595
  });
464
596
  rawApp.get(`${prefix}/data/:object/:id`, async (c) => {
465
597
  const ql = getObjectQL();
466
598
  if (!ql) return c.json({ error: "Data service not available" }, 503);
467
599
  const object = c.req.param("object");
468
600
  const id = c.req.param("id");
469
- let all = await ql.find(object);
470
- if (!all) all = [];
471
- const match = all.find((i) => i.id === id);
472
- return match ? c.json({ object, id, record: match }) : c.json({ error: "Not found" }, 404);
601
+ const execCtx = await resolveCtx(c);
602
+ try {
603
+ let all = await ql.find(object, { context: execCtx });
604
+ if (!all) all = [];
605
+ const match = all.find((i) => i.id === id);
606
+ return match ? c.json({ object, id, record: match }) : c.json({ error: "Not found" }, 404);
607
+ } catch (err) {
608
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
609
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
610
+ }
611
+ throw err;
612
+ }
473
613
  });
474
614
  rawApp.get(`${prefix}/data/:object`, async (c) => {
475
615
  const ql = getObjectQL();
476
616
  if (!ql) return c.json({ error: "Data service not available" }, 503);
477
617
  const object = c.req.param("object");
478
- let all = await ql.find(object);
479
- if (!Array.isArray(all) && all && all.value) all = all.value;
480
- if (!all) all = [];
481
- return c.json({ object, records: all, total: all.length });
618
+ const execCtx = await resolveCtx(c);
619
+ try {
620
+ let all = await ql.find(object, { context: execCtx });
621
+ if (!Array.isArray(all) && all && all.value) all = all.value;
622
+ if (!all) all = [];
623
+ return c.json({ object, records: all, total: all.length });
624
+ } catch (err) {
625
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
626
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
627
+ }
628
+ throw err;
629
+ }
482
630
  });
483
631
  ctx.logger.debug("Registered standard CRUD data endpoints", { prefix });
484
632
  }
@@ -499,6 +647,11 @@ __publicField(HonoServerPlugin, "DISCOVERY_ENDPOINT_PRIORITY", 900);
499
647
  __reExport(index_exports, adapter_exports);
500
648
  export {
501
649
  HonoHttpServer,
502
- HonoServerPlugin
650
+ HonoServerPlugin,
651
+ createOriginMatcher,
652
+ hasWildcardPattern,
653
+ isLocalhostOrigin,
654
+ matchOriginPattern,
655
+ normalizeOriginPatterns
503
656
  };
504
657
  //# sourceMappingURL=index.mjs.map