@objectstack/plugin-hono-server 6.7.1 → 6.8.1

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/LICENSE CHANGED
@@ -1,202 +1,93 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction,
10
- and distribution as defined by Sections 1 through 9 of this document.
11
-
12
- "Licensor" shall mean the copyright owner or entity authorized by
13
- the copyright owner that is granting the License.
14
-
15
- "Legal Entity" shall mean the union of the acting entity and all
16
- other entities that control, are controlled by, or are under common
17
- control with that entity. For the purposes of this definition,
18
- "control" means (i) the power, direct or indirect, to cause the
19
- direction or management of such entity, whether by contract or
20
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
- outstanding shares, or (iii) beneficial ownership of such entity.
22
-
23
- "You" (or "Your") shall mean an individual or Legal Entity
24
- exercising permissions granted by this License.
25
-
26
- "Source" form shall mean the preferred form for making modifications,
27
- including but not limited to software source code, documentation
28
- source, and configuration files.
29
-
30
- "Object" form shall mean any form resulting from mechanical
31
- transformation or translation of a Source form, including but
32
- not limited to compiled object code, generated documentation,
33
- and conversions to other media types.
34
-
35
- "Work" shall mean the work of authorship, whether in Source or
36
- Object form, made available under the License, as indicated by a
37
- copyright notice that is included in or attached to the work
38
- (an example is provided in the Appendix below).
39
-
40
- "Derivative Works" shall mean any work, whether in Source or Object
41
- form, that is based on (or derived from) the Work and for which the
42
- editorial revisions, annotations, elaborations, or other modifications
43
- represent, as a whole, an original work of authorship. For the purposes
44
- of this License, Derivative Works shall not include works that remain
45
- separable from, or merely link (or bind by name) to the interfaces of,
46
- the Work and Derivative Works thereof.
47
-
48
- "Contribution" shall mean any work of authorship, including
49
- the original version of the Work and any modifications or additions
50
- to that Work or Derivative Works thereof, that is intentionally
51
- submitted to Licensor for inclusion in the Work by the copyright owner
52
- or by an individual or Legal Entity authorized to submit on behalf of
53
- the copyright owner. For the purposes of this definition, "submitted"
54
- means any form of electronic, verbal, or written communication sent
55
- to the Licensor or its representatives, including but not limited to
56
- communication on electronic mailing lists, source code control systems,
57
- and issue tracking systems that are managed by, or on behalf of, the
58
- Licensor for the purpose of discussing and improving the Work, but
59
- excluding communication that is conspicuously marked or otherwise
60
- designated in writing by the copyright owner as "Not a Contribution."
61
-
62
- "Contributor" shall mean Licensor and any individual or Legal Entity
63
- on behalf of whom a Contribution has been received by Licensor and
64
- subsequently incorporated within the Work.
65
-
66
- 2. Grant of Copyright License. Subject to the terms and conditions of
67
- this License, each Contributor hereby grants to You a perpetual,
68
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
- copyright license to reproduce, prepare Derivative Works of,
70
- publicly display, publicly perform, sublicense, and distribute the
71
- Work and such Derivative Works in Source or Object form.
72
-
73
- 3. Grant of Patent License. Subject to the terms and conditions of
74
- this License, each Contributor hereby grants to You a perpetual,
75
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
- (except as stated in this section) patent license to make, have made,
77
- use, offer to sell, sell, import, and otherwise transfer the Work,
78
- where such license applies only to those patent claims licensable
79
- by such Contributor that are necessarily infringed by their
80
- Contribution(s) alone or by combination of their Contribution(s)
81
- with the Work to which such Contribution(s) was submitted. If You
82
- institute patent litigation against any entity (including a
83
- cross-claim or counterclaim in a lawsuit) alleging that the Work
84
- or a Contribution incorporated within the Work constitutes direct
85
- or contributory patent infringement, then any patent licenses
86
- granted to You under this License for that Work shall terminate
87
- as of the date such litigation is filed.
88
-
89
- 4. Redistribution. You may reproduce and distribute copies of the
90
- Work or Derivative Works thereof in any medium, with or without
91
- modifications, and in Source or Object form, provided that You
92
- meet the following conditions:
93
-
94
- (a) You must give any other recipients of the Work or
95
- Derivative Works a copy of this License; and
96
-
97
- (b) You must cause any modified files to carry prominent notices
98
- stating that You changed the files; and
99
-
100
- (c) You must retain, in the Source form of any Derivative Works
101
- that You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
105
-
106
- (d) If the Work includes a "NOTICE" text file as part of its
107
- distribution, then any Derivative Works that You distribute
108
- must include a readable copy of the attribution notices
109
- contained within such NOTICE file, excluding those notices
110
- that do not pertain to any part of the Derivative Works,
111
- in at least one of the following places: within a NOTICE
112
- text file distributed as part of the Derivative Works; within
113
- the Source form or documentation, if provided along with
114
- the Derivative Works; or, within a display generated by the
115
- Derivative Works, if and wherever such third-party notices
116
- normally appear. The contents of the NOTICE file are for
117
- informational purposes only and do not modify the License.
118
- You may add Your own attribution notices within Derivative
119
- Works that You distribute, alongside or as an addendum to
120
- the NOTICE text from the Work, provided that such additional
121
- attribution notices cannot be construed as modifying the
122
- License.
123
-
124
- You may add Your own copyright statement to Your modifications and
125
- may provide additional or different license terms and conditions
126
- for use, reproduction, or distribution of Your modifications, or
127
- for any such Derivative Works as a whole, provided Your use,
128
- reproduction, and distribution of the Work otherwise complies with
129
- the conditions stated in this License.
130
-
131
- 5. Submission of Contributions. Unless You explicitly state otherwise,
132
- any Contribution intentionally submitted for inclusion in the Work
133
- by You to the Licensor shall be under the terms and conditions of
134
- this License, without any additional terms or conditions.
135
- Notwithstanding the above, nothing herein shall supersede or modify
136
- the terms of any separate license agreement you may have executed
137
- with Licensor regarding such Contributions.
138
-
139
- 6. Trademarks. This License does not grant permission to use the trade
140
- names, trademarks, service marks, or product names of the Licensor,
141
- except as required for reasonable and customary use in describing the
142
- origin of the Work and reproducing the content of the NOTICE file.
143
-
144
- 7. Disclaimer of Warranty. Unless required by applicable law or
145
- agreed to in writing, Licensor provides the Work (and each
146
- Contributor provides its Contributions) on an "AS IS" BASIS,
147
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
- implied, including, without limitation, any warranties or conditions
149
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
- PARTICULAR PURPOSE. You are solely responsible for determining the
151
- appropriateness of using or redistributing the Work and assume any
152
- risks associated with Your exercise of permissions under this License.
153
-
154
- 8. Limitation of Liability. In no event and under no legal theory,
155
- whether in tort (including negligence), contract, or otherwise,
156
- unless required by applicable law (such as deliberate and grossly
157
- negligent acts) or agreed to in writing, shall any Contributor be
158
- liable to You for damages, including any direct, indirect, special,
159
- incidental, or consequential damages of any character arising as a
160
- result of this License or out of the use or inability to use the
161
- Work (including but not limited to damages for loss of goodwill,
162
- work stoppage, computer failure or malfunction, or any and all
163
- other commercial damages or losses), even if such Contributor
164
- has been advised of the possibility of such damages.
165
-
166
- 9. Accepting Warranty or Additional Liability. While redistributing
167
- the Work or Derivative Works thereof, You may choose to offer,
168
- and charge a fee for, acceptance of support, warranty, indemnity,
169
- or other liability obligations and/or rights consistent with this
170
- License. However, in accepting such obligations, You may act only
171
- on Your own behalf and on Your sole responsibility, not on behalf
172
- of any other Contributor, and only if You agree to indemnify,
173
- defend, and hold each Contributor harmless for any liability
174
- incurred by, or claims asserted against, such Contributor by reason
175
- of your accepting any such warranty or additional liability.
176
-
177
- END OF TERMS AND CONDITIONS
178
-
179
- APPENDIX: How to apply the Apache License to your work.
180
-
181
- To apply the Apache License to your work, attach the following
182
- boilerplate notice, with the fields enclosed by brackets "[]"
183
- replaced with your own identifying information. (Don't include
184
- the brackets!) The text should be enclosed in the appropriate
185
- comment syntax for the file format. We also recommend that a
186
- file or class name and description of purpose be included on the
187
- same "printed page" as the copyright notice for easier
188
- identification within third-party archives.
189
-
190
- Copyright 2026 ObjectStack
191
-
192
- Licensed under the Apache License, Version 2.0 (the "License");
193
- you may not use this file except in compliance with the License.
194
- You may obtain a copy of the License at
195
-
196
- http://www.apache.org/licenses/LICENSE-2.0
197
-
198
- Unless required by applicable law or agreed to in writing, software
199
- distributed under the License is distributed on an "AS IS" BASIS,
200
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
- See the License for the specific language governing permissions and
202
- limitations under the License.
1
+ License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
2
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
3
+
4
+ Parameters
5
+
6
+ Licensor: ObjectStack AI LLC
7
+ Licensed Work: ObjectStack Runtime: the BSL-licensed packages
8
+ of the ObjectStack monorepo as listed in LICENSING.md.
9
+ Copyright (c) 2026 ObjectStack AI LLC.
10
+ Additional Use Grant: You may make production use of the Licensed Work, provided
11
+ Your use does not include offering the Licensed Work to third
12
+ parties on a hosted or embedded basis in order to compete with
13
+ ObjectStack AI LLC's paid version(s) of the Licensed Work. For purposes
14
+ of this license:
15
+
16
+ A "competitive offering" is a Product that is offered to third
17
+ parties on a paid basis, including through paid support
18
+ arrangements, that significantly overlaps with the capabilities
19
+ of ObjectStack AI LLC's paid version(s) of the Licensed Work. If Your
20
+ Product is not a competitive offering when You first make it
21
+ generally available, it will not become a competitive offering
22
+ later due to ObjectStack AI LLC releasing a new version of the Licensed
23
+ Work with additional capabilities. In addition, Products that
24
+ are not provided on a paid basis are not competitive.
25
+
26
+ "Product" means software that is offered to end users to manage
27
+ in their own environments or offered as a service on a hosted
28
+ basis.
29
+
30
+ "Embedded" means including the source code or executable code
31
+ from the Licensed Work in a competitive offering. "Embedded"
32
+ also means packaging the competitive offering in such a way
33
+ that the Licensed Work must be accessed or downloaded for the
34
+ competitive offering to operate.
35
+
36
+ Hosting or using the Licensed Work(s) for internal purposes
37
+ within an organization is not considered a competitive
38
+ offering. ObjectStack AI LLC considers your organization to include all
39
+ of your affiliates under common control.
40
+
41
+ For binding interpretive guidance on using ObjectStack AI LLC products
42
+ under the Business Source License, please visit our FAQ.
43
+ (see LICENSING.md in this repository)
44
+ Change Date: Four years from the date the Licensed Work is published.
45
+ Change License: Apache License, Version 2.0
46
+
47
+ For information about alternative licensing arrangements for the Licensed Work,
48
+ please contact licensing@objectstack.dev.
49
+
50
+ Notice
51
+
52
+ Business Source License 1.1
53
+
54
+ Terms
55
+
56
+ The Licensor hereby grants you the right to copy, modify, create derivative
57
+ works, redistribute, and make non-production use of the Licensed Work. The
58
+ Licensor may make an Additional Use Grant, above, permitting limited production use.
59
+
60
+ Effective on the Change Date, or the fourth anniversary of the first publicly
61
+ available distribution of a specific version of the Licensed Work under this
62
+ License, whichever comes first, the Licensor hereby grants you rights under
63
+ the terms of the Change License, and the rights granted in the paragraph
64
+ above terminate.
65
+
66
+ If your use of the Licensed Work does not comply with the requirements
67
+ currently in effect as described in this License, you must purchase a
68
+ commercial license from the Licensor, its affiliated entities, or authorized
69
+ resellers, or you must refrain from using the Licensed Work.
70
+
71
+ All copies of the original and modified Licensed Work, and derivative works
72
+ of the Licensed Work, are subject to this License. This License applies
73
+ separately for each version of the Licensed Work and the Change Date may vary
74
+ for each version of the Licensed Work released by Licensor.
75
+
76
+ You must conspicuously display this License on each original or modified copy
77
+ of the Licensed Work. If you receive the Licensed Work in original or
78
+ modified form from a third party, the terms and conditions set forth in this
79
+ License apply to your use of that work.
80
+
81
+ Any use of the Licensed Work in violation of this License will automatically
82
+ terminate your rights under this License for the current and all other
83
+ versions of the Licensed Work.
84
+
85
+ This License does not grant you any right in any trademark or logo of
86
+ Licensor or its affiliates (provided that you may use a trademark or logo of
87
+ Licensor as expressly required by this License).
88
+
89
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
90
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
91
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
92
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
93
+ TITLE.
package/README.md CHANGED
@@ -126,3 +126,6 @@ kernel.use(new HonoServerPlugin({
126
126
 
127
127
  This plugin wraps `@objectstack/hono` to provide a turnkey HTTP server solution for the Runtime. It binds the standard `HttpDispatcher` to a Hono application and starts listening on the configured port.
128
128
 
129
+ ## License
130
+
131
+ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).
package/dist/index.js CHANGED
@@ -735,6 +735,9 @@ var HonoServerPlugin = class {
735
735
  }
736
736
  const objects = {};
737
737
  const fields = {};
738
+ const systemPermissions = /* @__PURE__ */ new Set();
739
+ const tabRank = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };
740
+ const tabPermissions = {};
738
741
  for (const ps of resolved) {
739
742
  if (ps?.objects) {
740
743
  for (const [obj, perm] of Object.entries(ps.objects)) {
@@ -755,6 +758,20 @@ var HonoServerPlugin = class {
755
758
  fields[key] = acc;
756
759
  }
757
760
  }
761
+ if (Array.isArray(ps?.systemPermissions)) {
762
+ for (const sp of ps.systemPermissions) {
763
+ if (typeof sp === "string") systemPermissions.add(sp);
764
+ }
765
+ }
766
+ if (ps?.tabPermissions && typeof ps.tabPermissions === "object") {
767
+ for (const [app, val] of Object.entries(ps.tabPermissions)) {
768
+ if (typeof val !== "string" || !(val in tabRank)) continue;
769
+ const cur = tabPermissions[app];
770
+ if (!cur || tabRank[val] > tabRank[cur]) {
771
+ tabPermissions[app] = val;
772
+ }
773
+ }
774
+ }
758
775
  }
759
776
  return c.json({
760
777
  authenticated: true,
@@ -763,13 +780,38 @@ var HonoServerPlugin = class {
763
780
  roles: execCtx.roles ?? [],
764
781
  permissionSets: resolved.map((p) => p?.name).filter(Boolean),
765
782
  objects,
766
- fields
783
+ fields,
784
+ systemPermissions: Array.from(systemPermissions),
785
+ tabPermissions
767
786
  });
768
787
  } catch (err) {
769
788
  ctx.logger.warn("[hono] /auth/me/permissions failed", { err: err?.message });
770
789
  return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });
771
790
  }
772
791
  });
792
+ rawApp.get(`${prefix}/me/apps`, async (c) => {
793
+ const execCtx = await resolveCtx(c);
794
+ if (!execCtx?.userId) return c.json({ apps: [] });
795
+ try {
796
+ const metadata = ctx.getService("metadata");
797
+ if (!metadata?.list) return c.json({ apps: [] });
798
+ const all = await metadata.list("app") ?? [];
799
+ const sysPerms = new Set(execCtx.systemPermissions ?? []);
800
+ const tabs = execCtx.tabPermissions ?? {};
801
+ const failOpen = !ctx.getService("security.permissions");
802
+ const apps = all.filter((app) => {
803
+ if (!app?.name) return false;
804
+ if (tabs[app.name] === "hidden") return false;
805
+ if (failOpen) return true;
806
+ const req = Array.isArray(app.requiredPermissions) ? app.requiredPermissions : [];
807
+ return req.every((p) => sysPerms.has(p));
808
+ });
809
+ return c.json({ apps });
810
+ } catch (err) {
811
+ ctx.logger.warn("[hono] /me/apps failed", { err: err?.message });
812
+ return c.json({ apps: [] });
813
+ }
814
+ });
773
815
  ctx.logger.debug("Registered standard CRUD data endpoints", { prefix });
774
816
  }
775
817
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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 | Uint8Array | ArrayBuffer | Buffer) => {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer || (typeof Buffer !== 'undefined' && Buffer.isBuffer?.(data))) {\n const body = data instanceof ArrayBuffer ? data : (data as Uint8Array).buffer.slice((data as Uint8Array).byteOffset, (data as Uint8Array).byteOffset + (data as Uint8Array).byteLength);\n capturedResponse = c.body(body as ArrayBuffer);\n } else {\n capturedResponse = c.html(data as string);\n }\n },\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-Environment-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 // Register standard endpoints during kernel:ready so they're\n // wired up alongside other plugins' route registrations.\n if (this.options.registerStandardEndpoints) {\n ctx.hook('kernel:ready', async () => {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n });\n }\n\n // Open the listening socket on kernel:listening — this fires\n // STRICTLY AFTER every kernel:ready handler completes, so all\n // plugins have finished registering routes by the time the\n // server starts accepting requests.\n //\n // Why this matters: Hono seals the route matcher the first\n // time a request is matched. If we listen during kernel:ready\n // and a request arrives before sibling plugins (auth, i18n,\n // storage, …) finish registering their routes, those late\n // `app.get(...)` calls throw \"matcher is already built\" and\n // crash the process. Cloudflare Containers fronts traffic the\n // millisecond port 4000 opens, so the race fires on every\n // cold boot in production. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts for\n // the full rationale.\n ctx.hook('kernel:listening', async () => {\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 // Resolve fellow-org user IDs so identity-table RLS (sys_user\n // org-members policy) can scope @-mention pickers, owner\n // lookups and reviewer selectors to the active organization.\n // Mirrors the resolvers in `@objectstack/rest` and\n // `@objectstack/runtime` so all three REST entry-points\n // produce a consistent ExecutionContext shape.\n let orgUserIds: string[] = [userId];\n if (tenantId) {\n try {\n const ql = getObjectQL();\n const sysCtx = { context: { isSystem: true } };\n const memberRows = await ql?.find?.(\n 'sys_member',\n { where: { organization_id: tenantId }, limit: 1000, ...sysCtx } as any,\n ).catch(() => []);\n const ids = new Set<string>([userId]);\n for (const m of (memberRows ?? []) as any[]) {\n const uid = m.user_id ?? m.userId;\n if (typeof uid === 'string' && uid.length > 0) ids.add(uid);\n }\n orgUserIds = Array.from(ids);\n } catch {\n /* fall back to self-only */\n }\n }\n return {\n userId,\n tenantId,\n roles,\n permissions,\n isSystem: false,\n org_user_ids: orgUserIds,\n } as any;\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 // Effective permissions for the current user — single aggregation\n // endpoint that resolves session → roles → permission sets → merged\n // field/object permissions. Frontend Field-Level Security (FLS)\n // consumes this to gate form fields / list columns without having\n // to replicate the server's role+permission-set resolution and\n // most-permissive merge logic.\n //\n // Response shape (designed to mirror @object-ui/permissions\n // expectations — see `PermissionSet` in @objectstack/spec):\n // {\n // userId, tenantId, roles, permissionSets,\n // objects: Record<objectName, { allowCreate, allowRead, ... }>,\n // fields: Record<\"object.field\", { readable, editable }>,\n // }\n //\n // Returns `{authenticated:false}` (200) when no session is\n // present, so the frontend can distinguish anon from error.\n rawApp.get(`${prefix}/auth/me/permissions`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) {\n return c.json({ authenticated: false });\n }\n try {\n const metadata: any = ctx.getService('metadata');\n const evaluator: any = ctx.getService('security.permissions');\n const bootstrap: any[] = (() => {\n try { return ctx.getService<any[]>('security.bootstrapPermissionSets') ?? []; }\n catch { return []; }\n })();\n const fallbackName: string | null = (() => {\n try { return ctx.getService<string | null>('security.fallbackPermissionSet') ?? 'member_default'; }\n catch { return 'member_default'; }\n })();\n // DB loader: surfaces user-defined permission sets\n // (created via the admin UI as `sys_permission_set`\n // rows) that aren't in metadata or bootstrap.\n const ql: any = (() => {\n try { return ctx.getService('objectql'); } catch { return null; }\n })();\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n if (!evaluator || !metadata) {\n // Auth resolved but security plugin isn't wired — emit\n // an empty-but-authenticated body so the frontend can\n // fail-open with full access (matches server behaviour\n // when SecurityPlugin isn't registered).\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: execCtx.permissions ?? [],\n objects: {},\n fields: {},\n });\n }\n // Resolve the same way SecurityPlugin middleware does:\n // role names + explicit permission-set names, with a\n // fallback to `member_default` when authenticated users\n // resolve to zero permission sets (matches the\n // post-resolution fallback in security-plugin.ts).\n const requested = [\n ...(execCtx.roles ?? []),\n ...(execCtx.permissions ?? []),\n ];\n let resolved: any[] = await evaluator\n .resolvePermissionSets(requested, metadata, bootstrap, dbLoader)\n .catch(() => []);\n if (resolved.length === 0 && fallbackName) {\n resolved = await evaluator\n .resolvePermissionSets([fallbackName], metadata, bootstrap, dbLoader)\n .catch(() => []);\n }\n // Most-permissive merge of `objects` and `fields` across\n // all resolved permission sets — same semantics as\n // PermissionEvaluator.getFieldPermissions but for ALL\n // objects in a single pass.\n const objects: Record<string, any> = {};\n const fields: Record<string, { readable: boolean; editable: boolean }> = {};\n for (const ps of resolved) {\n if (ps?.objects) {\n for (const [obj, perm] of Object.entries(ps.objects)) {\n const acc = objects[obj] ?? {};\n for (const [k, v] of Object.entries(perm as any)) {\n if (v === true) acc[k] = true;\n else if (acc[k] === undefined) acc[k] = v;\n }\n objects[obj] = acc;\n }\n }\n if (ps?.fields) {\n for (const [key, perm] of Object.entries(ps.fields)) {\n const acc = fields[key] ?? { readable: false, editable: false };\n const p = perm as any;\n if (p.readable) acc.readable = true;\n if (p.editable) acc.editable = true;\n fields[key] = acc;\n }\n }\n }\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: resolved.map((p: any) => p?.name).filter(Boolean),\n objects,\n fields,\n });\n } catch (err: any) {\n ctx.logger.warn('[hono] /auth/me/permissions failed', { err: err?.message });\n return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });\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":["body","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 | Uint8Array | ArrayBuffer | Buffer) => {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer || (typeof Buffer !== 'undefined' && Buffer.isBuffer?.(data))) {\n const body = data instanceof ArrayBuffer ? data : (data as Uint8Array).buffer.slice((data as Uint8Array).byteOffset, (data as Uint8Array).byteOffset + (data as Uint8Array).byteLength);\n capturedResponse = c.body(body as ArrayBuffer);\n } else {\n capturedResponse = c.html(data as string);\n }\n },\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-Environment-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 // Register standard endpoints during kernel:ready so they're\n // wired up alongside other plugins' route registrations.\n if (this.options.registerStandardEndpoints) {\n ctx.hook('kernel:ready', async () => {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n });\n }\n\n // Open the listening socket on kernel:listening — this fires\n // STRICTLY AFTER every kernel:ready handler completes, so all\n // plugins have finished registering routes by the time the\n // server starts accepting requests.\n //\n // Why this matters: Hono seals the route matcher the first\n // time a request is matched. If we listen during kernel:ready\n // and a request arrives before sibling plugins (auth, i18n,\n // storage, …) finish registering their routes, those late\n // `app.get(...)` calls throw \"matcher is already built\" and\n // crash the process. Cloudflare Containers fronts traffic the\n // millisecond port 4000 opens, so the race fires on every\n // cold boot in production. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts for\n // the full rationale.\n ctx.hook('kernel:listening', async () => {\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 // Resolve fellow-org user IDs so identity-table RLS (sys_user\n // org-members policy) can scope @-mention pickers, owner\n // lookups and reviewer selectors to the active organization.\n // Mirrors the resolvers in `@objectstack/rest` and\n // `@objectstack/runtime` so all three REST entry-points\n // produce a consistent ExecutionContext shape.\n let orgUserIds: string[] = [userId];\n if (tenantId) {\n try {\n const ql = getObjectQL();\n const sysCtx = { context: { isSystem: true } };\n const memberRows = await ql?.find?.(\n 'sys_member',\n { where: { organization_id: tenantId }, limit: 1000, ...sysCtx } as any,\n ).catch(() => []);\n const ids = new Set<string>([userId]);\n for (const m of (memberRows ?? []) as any[]) {\n const uid = m.user_id ?? m.userId;\n if (typeof uid === 'string' && uid.length > 0) ids.add(uid);\n }\n orgUserIds = Array.from(ids);\n } catch {\n /* fall back to self-only */\n }\n }\n return {\n userId,\n tenantId,\n roles,\n permissions,\n isSystem: false,\n org_user_ids: orgUserIds,\n } as any;\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 // Effective permissions for the current user — single aggregation\n // endpoint that resolves session → roles → permission sets → merged\n // field/object permissions. Frontend Field-Level Security (FLS)\n // consumes this to gate form fields / list columns without having\n // to replicate the server's role+permission-set resolution and\n // most-permissive merge logic.\n //\n // Response shape (designed to mirror @object-ui/permissions\n // expectations — see `PermissionSet` in @objectstack/spec):\n // {\n // userId, tenantId, roles, permissionSets,\n // objects: Record<objectName, { allowCreate, allowRead, ... }>,\n // fields: Record<\"object.field\", { readable, editable }>,\n // }\n //\n // Returns `{authenticated:false}` (200) when no session is\n // present, so the frontend can distinguish anon from error.\n rawApp.get(`${prefix}/auth/me/permissions`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) {\n return c.json({ authenticated: false });\n }\n try {\n const metadata: any = ctx.getService('metadata');\n const evaluator: any = ctx.getService('security.permissions');\n const bootstrap: any[] = (() => {\n try { return ctx.getService<any[]>('security.bootstrapPermissionSets') ?? []; }\n catch { return []; }\n })();\n const fallbackName: string | null = (() => {\n try { return ctx.getService<string | null>('security.fallbackPermissionSet') ?? 'member_default'; }\n catch { return 'member_default'; }\n })();\n // DB loader: surfaces user-defined permission sets\n // (created via the admin UI as `sys_permission_set`\n // rows) that aren't in metadata or bootstrap.\n const ql: any = (() => {\n try { return ctx.getService('objectql'); } catch { return null; }\n })();\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n if (!evaluator || !metadata) {\n // Auth resolved but security plugin isn't wired — emit\n // an empty-but-authenticated body so the frontend can\n // fail-open with full access (matches server behaviour\n // when SecurityPlugin isn't registered).\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: execCtx.permissions ?? [],\n objects: {},\n fields: {},\n });\n }\n // Resolve the same way SecurityPlugin middleware does:\n // role names + explicit permission-set names, with a\n // fallback to `member_default` when authenticated users\n // resolve to zero permission sets (matches the\n // post-resolution fallback in security-plugin.ts).\n const requested = [\n ...(execCtx.roles ?? []),\n ...(execCtx.permissions ?? []),\n ];\n let resolved: any[] = await evaluator\n .resolvePermissionSets(requested, metadata, bootstrap, dbLoader)\n .catch(() => []);\n if (resolved.length === 0 && fallbackName) {\n resolved = await evaluator\n .resolvePermissionSets([fallbackName], metadata, bootstrap, dbLoader)\n .catch(() => []);\n }\n // Most-permissive merge of `objects` and `fields` across\n // all resolved permission sets — same semantics as\n // PermissionEvaluator.getFieldPermissions but for ALL\n // objects in a single pass.\n const objects: Record<string, any> = {};\n const fields: Record<string, { readable: boolean; editable: boolean }> = {};\n const systemPermissions = new Set<string>();\n const tabRank: Record<string, number> = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };\n const tabPermissions: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'> = {};\n for (const ps of resolved) {\n if (ps?.objects) {\n for (const [obj, perm] of Object.entries(ps.objects)) {\n const acc = objects[obj] ?? {};\n for (const [k, v] of Object.entries(perm as any)) {\n if (v === true) acc[k] = true;\n else if (acc[k] === undefined) acc[k] = v;\n }\n objects[obj] = acc;\n }\n }\n if (ps?.fields) {\n for (const [key, perm] of Object.entries(ps.fields)) {\n const acc = fields[key] ?? { readable: false, editable: false };\n const p = perm as any;\n if (p.readable) acc.readable = true;\n if (p.editable) acc.editable = true;\n fields[key] = acc;\n }\n }\n if (Array.isArray(ps?.systemPermissions)) {\n for (const sp of ps.systemPermissions) {\n if (typeof sp === 'string') systemPermissions.add(sp);\n }\n }\n if (ps?.tabPermissions && typeof ps.tabPermissions === 'object') {\n for (const [app, val] of Object.entries(ps.tabPermissions as Record<string, unknown>)) {\n if (typeof val !== 'string' || !(val in tabRank)) continue;\n const cur = tabPermissions[app];\n if (!cur || tabRank[val] > tabRank[cur]) {\n tabPermissions[app] = val as 'visible' | 'hidden' | 'default_on' | 'default_off';\n }\n }\n }\n }\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: resolved.map((p: any) => p?.name).filter(Boolean),\n objects,\n fields,\n systemPermissions: Array.from(systemPermissions),\n tabPermissions,\n });\n } catch (err: any) {\n ctx.logger.warn('[hono] /auth/me/permissions failed', { err: err?.message });\n return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });\n }\n });\n\n // GET /me/apps — list apps the current user is allowed to enter.\n // Filters `metadata.list('app')` by:\n // 1. AppSchema.requiredPermissions ⊆ ctx.systemPermissions\n // 2. ctx.tabPermissions[app.name] !== 'hidden'\n // Anonymous users get an empty array. When SecurityPlugin is absent\n // we fail-open and return every app (matches server behaviour).\n rawApp.get(`${prefix}/me/apps`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) return c.json({ apps: [] });\n try {\n const metadata: any = ctx.getService('metadata');\n if (!metadata?.list) return c.json({ apps: [] });\n const all: any[] = (await metadata.list('app')) ?? [];\n const sysPerms = new Set<string>(execCtx.systemPermissions ?? []);\n const tabs = (execCtx as any).tabPermissions ?? {};\n const failOpen = !ctx.getService('security.permissions');\n const apps = all.filter((app: any) => {\n if (!app?.name) return false;\n if (tabs[app.name] === 'hidden') return false;\n if (failOpen) return true;\n const req: string[] = Array.isArray(app.requiredPermissions) ? app.requiredPermissions : [];\n return req.every((p) => sysPerms.has(p));\n });\n return c.json({ apps });\n } catch (err: any) {\n ctx.logger.warn('[hono] /me/apps failed', { err: err?.message });\n return c.json({ apps: [] });\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":["body","resolve","path","import_serve_static"]}
package/dist/index.mjs CHANGED
@@ -723,6 +723,9 @@ var HonoServerPlugin = class {
723
723
  }
724
724
  const objects = {};
725
725
  const fields = {};
726
+ const systemPermissions = /* @__PURE__ */ new Set();
727
+ const tabRank = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };
728
+ const tabPermissions = {};
726
729
  for (const ps of resolved) {
727
730
  if (ps?.objects) {
728
731
  for (const [obj, perm] of Object.entries(ps.objects)) {
@@ -743,6 +746,20 @@ var HonoServerPlugin = class {
743
746
  fields[key] = acc;
744
747
  }
745
748
  }
749
+ if (Array.isArray(ps?.systemPermissions)) {
750
+ for (const sp of ps.systemPermissions) {
751
+ if (typeof sp === "string") systemPermissions.add(sp);
752
+ }
753
+ }
754
+ if (ps?.tabPermissions && typeof ps.tabPermissions === "object") {
755
+ for (const [app, val] of Object.entries(ps.tabPermissions)) {
756
+ if (typeof val !== "string" || !(val in tabRank)) continue;
757
+ const cur = tabPermissions[app];
758
+ if (!cur || tabRank[val] > tabRank[cur]) {
759
+ tabPermissions[app] = val;
760
+ }
761
+ }
762
+ }
746
763
  }
747
764
  return c.json({
748
765
  authenticated: true,
@@ -751,13 +768,38 @@ var HonoServerPlugin = class {
751
768
  roles: execCtx.roles ?? [],
752
769
  permissionSets: resolved.map((p) => p?.name).filter(Boolean),
753
770
  objects,
754
- fields
771
+ fields,
772
+ systemPermissions: Array.from(systemPermissions),
773
+ tabPermissions
755
774
  });
756
775
  } catch (err) {
757
776
  ctx.logger.warn("[hono] /auth/me/permissions failed", { err: err?.message });
758
777
  return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });
759
778
  }
760
779
  });
780
+ rawApp.get(`${prefix}/me/apps`, async (c) => {
781
+ const execCtx = await resolveCtx(c);
782
+ if (!execCtx?.userId) return c.json({ apps: [] });
783
+ try {
784
+ const metadata = ctx.getService("metadata");
785
+ if (!metadata?.list) return c.json({ apps: [] });
786
+ const all = await metadata.list("app") ?? [];
787
+ const sysPerms = new Set(execCtx.systemPermissions ?? []);
788
+ const tabs = execCtx.tabPermissions ?? {};
789
+ const failOpen = !ctx.getService("security.permissions");
790
+ const apps = all.filter((app) => {
791
+ if (!app?.name) return false;
792
+ if (tabs[app.name] === "hidden") return false;
793
+ if (failOpen) return true;
794
+ const req = Array.isArray(app.requiredPermissions) ? app.requiredPermissions : [];
795
+ return req.every((p) => sysPerms.has(p));
796
+ });
797
+ return c.json({ apps });
798
+ } catch (err) {
799
+ ctx.logger.warn("[hono] /me/apps failed", { err: err?.message });
800
+ return c.json({ apps: [] });
801
+ }
802
+ });
761
803
  ctx.logger.debug("Registered standard CRUD data endpoints", { prefix });
762
804
  }
763
805
  /**
@@ -1 +1 @@
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 | Uint8Array | ArrayBuffer | Buffer) => {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer || (typeof Buffer !== 'undefined' && Buffer.isBuffer?.(data))) {\n const body = data instanceof ArrayBuffer ? data : (data as Uint8Array).buffer.slice((data as Uint8Array).byteOffset, (data as Uint8Array).byteOffset + (data as Uint8Array).byteLength);\n capturedResponse = c.body(body as ArrayBuffer);\n } else {\n capturedResponse = c.html(data as string);\n }\n },\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-Environment-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 // Register standard endpoints during kernel:ready so they're\n // wired up alongside other plugins' route registrations.\n if (this.options.registerStandardEndpoints) {\n ctx.hook('kernel:ready', async () => {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n });\n }\n\n // Open the listening socket on kernel:listening — this fires\n // STRICTLY AFTER every kernel:ready handler completes, so all\n // plugins have finished registering routes by the time the\n // server starts accepting requests.\n //\n // Why this matters: Hono seals the route matcher the first\n // time a request is matched. If we listen during kernel:ready\n // and a request arrives before sibling plugins (auth, i18n,\n // storage, …) finish registering their routes, those late\n // `app.get(...)` calls throw \"matcher is already built\" and\n // crash the process. Cloudflare Containers fronts traffic the\n // millisecond port 4000 opens, so the race fires on every\n // cold boot in production. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts for\n // the full rationale.\n ctx.hook('kernel:listening', async () => {\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 // Resolve fellow-org user IDs so identity-table RLS (sys_user\n // org-members policy) can scope @-mention pickers, owner\n // lookups and reviewer selectors to the active organization.\n // Mirrors the resolvers in `@objectstack/rest` and\n // `@objectstack/runtime` so all three REST entry-points\n // produce a consistent ExecutionContext shape.\n let orgUserIds: string[] = [userId];\n if (tenantId) {\n try {\n const ql = getObjectQL();\n const sysCtx = { context: { isSystem: true } };\n const memberRows = await ql?.find?.(\n 'sys_member',\n { where: { organization_id: tenantId }, limit: 1000, ...sysCtx } as any,\n ).catch(() => []);\n const ids = new Set<string>([userId]);\n for (const m of (memberRows ?? []) as any[]) {\n const uid = m.user_id ?? m.userId;\n if (typeof uid === 'string' && uid.length > 0) ids.add(uid);\n }\n orgUserIds = Array.from(ids);\n } catch {\n /* fall back to self-only */\n }\n }\n return {\n userId,\n tenantId,\n roles,\n permissions,\n isSystem: false,\n org_user_ids: orgUserIds,\n } as any;\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 // Effective permissions for the current user — single aggregation\n // endpoint that resolves session → roles → permission sets → merged\n // field/object permissions. Frontend Field-Level Security (FLS)\n // consumes this to gate form fields / list columns without having\n // to replicate the server's role+permission-set resolution and\n // most-permissive merge logic.\n //\n // Response shape (designed to mirror @object-ui/permissions\n // expectations — see `PermissionSet` in @objectstack/spec):\n // {\n // userId, tenantId, roles, permissionSets,\n // objects: Record<objectName, { allowCreate, allowRead, ... }>,\n // fields: Record<\"object.field\", { readable, editable }>,\n // }\n //\n // Returns `{authenticated:false}` (200) when no session is\n // present, so the frontend can distinguish anon from error.\n rawApp.get(`${prefix}/auth/me/permissions`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) {\n return c.json({ authenticated: false });\n }\n try {\n const metadata: any = ctx.getService('metadata');\n const evaluator: any = ctx.getService('security.permissions');\n const bootstrap: any[] = (() => {\n try { return ctx.getService<any[]>('security.bootstrapPermissionSets') ?? []; }\n catch { return []; }\n })();\n const fallbackName: string | null = (() => {\n try { return ctx.getService<string | null>('security.fallbackPermissionSet') ?? 'member_default'; }\n catch { return 'member_default'; }\n })();\n // DB loader: surfaces user-defined permission sets\n // (created via the admin UI as `sys_permission_set`\n // rows) that aren't in metadata or bootstrap.\n const ql: any = (() => {\n try { return ctx.getService('objectql'); } catch { return null; }\n })();\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n if (!evaluator || !metadata) {\n // Auth resolved but security plugin isn't wired — emit\n // an empty-but-authenticated body so the frontend can\n // fail-open with full access (matches server behaviour\n // when SecurityPlugin isn't registered).\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: execCtx.permissions ?? [],\n objects: {},\n fields: {},\n });\n }\n // Resolve the same way SecurityPlugin middleware does:\n // role names + explicit permission-set names, with a\n // fallback to `member_default` when authenticated users\n // resolve to zero permission sets (matches the\n // post-resolution fallback in security-plugin.ts).\n const requested = [\n ...(execCtx.roles ?? []),\n ...(execCtx.permissions ?? []),\n ];\n let resolved: any[] = await evaluator\n .resolvePermissionSets(requested, metadata, bootstrap, dbLoader)\n .catch(() => []);\n if (resolved.length === 0 && fallbackName) {\n resolved = await evaluator\n .resolvePermissionSets([fallbackName], metadata, bootstrap, dbLoader)\n .catch(() => []);\n }\n // Most-permissive merge of `objects` and `fields` across\n // all resolved permission sets — same semantics as\n // PermissionEvaluator.getFieldPermissions but for ALL\n // objects in a single pass.\n const objects: Record<string, any> = {};\n const fields: Record<string, { readable: boolean; editable: boolean }> = {};\n for (const ps of resolved) {\n if (ps?.objects) {\n for (const [obj, perm] of Object.entries(ps.objects)) {\n const acc = objects[obj] ?? {};\n for (const [k, v] of Object.entries(perm as any)) {\n if (v === true) acc[k] = true;\n else if (acc[k] === undefined) acc[k] = v;\n }\n objects[obj] = acc;\n }\n }\n if (ps?.fields) {\n for (const [key, perm] of Object.entries(ps.fields)) {\n const acc = fields[key] ?? { readable: false, editable: false };\n const p = perm as any;\n if (p.readable) acc.readable = true;\n if (p.editable) acc.editable = true;\n fields[key] = acc;\n }\n }\n }\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: resolved.map((p: any) => p?.name).filter(Boolean),\n objects,\n fields,\n });\n } catch (err: any) {\n ctx.logger.warn('[hono] /auth/me/permissions failed', { err: err?.message });\n return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });\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":["body","resolve","path","serveStatic","serveStatic"]}
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 | Uint8Array | ArrayBuffer | Buffer) => {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer || (typeof Buffer !== 'undefined' && Buffer.isBuffer?.(data))) {\n const body = data instanceof ArrayBuffer ? data : (data as Uint8Array).buffer.slice((data as Uint8Array).byteOffset, (data as Uint8Array).byteOffset + (data as Uint8Array).byteLength);\n capturedResponse = c.body(body as ArrayBuffer);\n } else {\n capturedResponse = c.html(data as string);\n }\n },\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-Environment-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 // Register standard endpoints during kernel:ready so they're\n // wired up alongside other plugins' route registrations.\n if (this.options.registerStandardEndpoints) {\n ctx.hook('kernel:ready', async () => {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n });\n }\n\n // Open the listening socket on kernel:listening — this fires\n // STRICTLY AFTER every kernel:ready handler completes, so all\n // plugins have finished registering routes by the time the\n // server starts accepting requests.\n //\n // Why this matters: Hono seals the route matcher the first\n // time a request is matched. If we listen during kernel:ready\n // and a request arrives before sibling plugins (auth, i18n,\n // storage, …) finish registering their routes, those late\n // `app.get(...)` calls throw \"matcher is already built\" and\n // crash the process. Cloudflare Containers fronts traffic the\n // millisecond port 4000 opens, so the race fires on every\n // cold boot in production. See\n // packages/spec/src/contracts/plugin-lifecycle-events.ts for\n // the full rationale.\n ctx.hook('kernel:listening', async () => {\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 // Resolve fellow-org user IDs so identity-table RLS (sys_user\n // org-members policy) can scope @-mention pickers, owner\n // lookups and reviewer selectors to the active organization.\n // Mirrors the resolvers in `@objectstack/rest` and\n // `@objectstack/runtime` so all three REST entry-points\n // produce a consistent ExecutionContext shape.\n let orgUserIds: string[] = [userId];\n if (tenantId) {\n try {\n const ql = getObjectQL();\n const sysCtx = { context: { isSystem: true } };\n const memberRows = await ql?.find?.(\n 'sys_member',\n { where: { organization_id: tenantId }, limit: 1000, ...sysCtx } as any,\n ).catch(() => []);\n const ids = new Set<string>([userId]);\n for (const m of (memberRows ?? []) as any[]) {\n const uid = m.user_id ?? m.userId;\n if (typeof uid === 'string' && uid.length > 0) ids.add(uid);\n }\n orgUserIds = Array.from(ids);\n } catch {\n /* fall back to self-only */\n }\n }\n return {\n userId,\n tenantId,\n roles,\n permissions,\n isSystem: false,\n org_user_ids: orgUserIds,\n } as any;\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 // Effective permissions for the current user — single aggregation\n // endpoint that resolves session → roles → permission sets → merged\n // field/object permissions. Frontend Field-Level Security (FLS)\n // consumes this to gate form fields / list columns without having\n // to replicate the server's role+permission-set resolution and\n // most-permissive merge logic.\n //\n // Response shape (designed to mirror @object-ui/permissions\n // expectations — see `PermissionSet` in @objectstack/spec):\n // {\n // userId, tenantId, roles, permissionSets,\n // objects: Record<objectName, { allowCreate, allowRead, ... }>,\n // fields: Record<\"object.field\", { readable, editable }>,\n // }\n //\n // Returns `{authenticated:false}` (200) when no session is\n // present, so the frontend can distinguish anon from error.\n rawApp.get(`${prefix}/auth/me/permissions`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) {\n return c.json({ authenticated: false });\n }\n try {\n const metadata: any = ctx.getService('metadata');\n const evaluator: any = ctx.getService('security.permissions');\n const bootstrap: any[] = (() => {\n try { return ctx.getService<any[]>('security.bootstrapPermissionSets') ?? []; }\n catch { return []; }\n })();\n const fallbackName: string | null = (() => {\n try { return ctx.getService<string | null>('security.fallbackPermissionSet') ?? 'member_default'; }\n catch { return 'member_default'; }\n })();\n // DB loader: surfaces user-defined permission sets\n // (created via the admin UI as `sys_permission_set`\n // rows) that aren't in metadata or bootstrap.\n const ql: any = (() => {\n try { return ctx.getService('objectql'); } catch { return null; }\n })();\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n if (!evaluator || !metadata) {\n // Auth resolved but security plugin isn't wired — emit\n // an empty-but-authenticated body so the frontend can\n // fail-open with full access (matches server behaviour\n // when SecurityPlugin isn't registered).\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: execCtx.permissions ?? [],\n objects: {},\n fields: {},\n });\n }\n // Resolve the same way SecurityPlugin middleware does:\n // role names + explicit permission-set names, with a\n // fallback to `member_default` when authenticated users\n // resolve to zero permission sets (matches the\n // post-resolution fallback in security-plugin.ts).\n const requested = [\n ...(execCtx.roles ?? []),\n ...(execCtx.permissions ?? []),\n ];\n let resolved: any[] = await evaluator\n .resolvePermissionSets(requested, metadata, bootstrap, dbLoader)\n .catch(() => []);\n if (resolved.length === 0 && fallbackName) {\n resolved = await evaluator\n .resolvePermissionSets([fallbackName], metadata, bootstrap, dbLoader)\n .catch(() => []);\n }\n // Most-permissive merge of `objects` and `fields` across\n // all resolved permission sets — same semantics as\n // PermissionEvaluator.getFieldPermissions but for ALL\n // objects in a single pass.\n const objects: Record<string, any> = {};\n const fields: Record<string, { readable: boolean; editable: boolean }> = {};\n const systemPermissions = new Set<string>();\n const tabRank: Record<string, number> = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };\n const tabPermissions: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'> = {};\n for (const ps of resolved) {\n if (ps?.objects) {\n for (const [obj, perm] of Object.entries(ps.objects)) {\n const acc = objects[obj] ?? {};\n for (const [k, v] of Object.entries(perm as any)) {\n if (v === true) acc[k] = true;\n else if (acc[k] === undefined) acc[k] = v;\n }\n objects[obj] = acc;\n }\n }\n if (ps?.fields) {\n for (const [key, perm] of Object.entries(ps.fields)) {\n const acc = fields[key] ?? { readable: false, editable: false };\n const p = perm as any;\n if (p.readable) acc.readable = true;\n if (p.editable) acc.editable = true;\n fields[key] = acc;\n }\n }\n if (Array.isArray(ps?.systemPermissions)) {\n for (const sp of ps.systemPermissions) {\n if (typeof sp === 'string') systemPermissions.add(sp);\n }\n }\n if (ps?.tabPermissions && typeof ps.tabPermissions === 'object') {\n for (const [app, val] of Object.entries(ps.tabPermissions as Record<string, unknown>)) {\n if (typeof val !== 'string' || !(val in tabRank)) continue;\n const cur = tabPermissions[app];\n if (!cur || tabRank[val] > tabRank[cur]) {\n tabPermissions[app] = val as 'visible' | 'hidden' | 'default_on' | 'default_off';\n }\n }\n }\n }\n return c.json({\n authenticated: true,\n userId: execCtx.userId,\n tenantId: execCtx.tenantId ?? null,\n roles: execCtx.roles ?? [],\n permissionSets: resolved.map((p: any) => p?.name).filter(Boolean),\n objects,\n fields,\n systemPermissions: Array.from(systemPermissions),\n tabPermissions,\n });\n } catch (err: any) {\n ctx.logger.warn('[hono] /auth/me/permissions failed', { err: err?.message });\n return c.json({ authenticated: true, userId: execCtx.userId, objects: {}, fields: {} });\n }\n });\n\n // GET /me/apps — list apps the current user is allowed to enter.\n // Filters `metadata.list('app')` by:\n // 1. AppSchema.requiredPermissions ⊆ ctx.systemPermissions\n // 2. ctx.tabPermissions[app.name] !== 'hidden'\n // Anonymous users get an empty array. When SecurityPlugin is absent\n // we fail-open and return every app (matches server behaviour).\n rawApp.get(`${prefix}/me/apps`, async (c: any) => {\n const execCtx = await resolveCtx(c);\n if (!execCtx?.userId) return c.json({ apps: [] });\n try {\n const metadata: any = ctx.getService('metadata');\n if (!metadata?.list) return c.json({ apps: [] });\n const all: any[] = (await metadata.list('app')) ?? [];\n const sysPerms = new Set<string>(execCtx.systemPermissions ?? []);\n const tabs = (execCtx as any).tabPermissions ?? {};\n const failOpen = !ctx.getService('security.permissions');\n const apps = all.filter((app: any) => {\n if (!app?.name) return false;\n if (tabs[app.name] === 'hidden') return false;\n if (failOpen) return true;\n const req: string[] = Array.isArray(app.requiredPermissions) ? app.requiredPermissions : [];\n return req.every((p) => sysPerms.has(p));\n });\n return c.json({ apps });\n } catch (err: any) {\n ctx.logger.warn('[hono] /me/apps failed', { err: err?.message });\n return c.json({ apps: [] });\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":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAGA;AAAA,2BAAc;AAOd,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,mBAAmB;AA6BrB,IAAM,iBAAN,MAA4C;AAAA,EAK/C,YACY,OAAe,KACf,YACV;AAFU;AACA;AANZ,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AAMJ,SAAK,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGQ,KAAK,SAAuB;AAChC,WAAO,OAAO,MAAW;AACrB,UAAI,OAAY,CAAC;AAEjB,YAAM,cAAc,EAAE,IAAI,OAAO,cAAc,KAAK;AACpD,YAAM,gBAAgB,YAAY,SAAS,0BAA0B;AAGrE,UAAI,YAAY,SAAS,kBAAkB,GAAG;AAC1C,YAAI;AACA,iBAAO,MAAM,EAAE,IAAI,KAAK;AAAA,QAC5B,SAAQ,GAAG;AAEP,cAAI;AACA,mBAAO,MAAM,EAAE,IAAI,UAAU;AAAA,UACjC,SAAQ,IAAI;AAAA,UAAC;AAAA,QACjB;AAAA,MACJ,WAAW,CAAC,eAAe;AAIvB,YAAI;AACA,iBAAO,MAAM,EAAE,IAAI,UAAU;AAAA,QACjC,SAAQ,GAAG;AAAA,QAAC;AAAA,MAChB;AAEA,YAAM,aAAa,EAAE,IAAI,OAAO;AAKhC,UAAI,CAAC,WAAW,MAAM;AAClB,YAAI;AACA,gBAAM,IAAI,IAAI,IAAI,EAAE,IAAI,GAAG;AAC3B,cAAI,EAAE,KAAM,YAAW,OAAO,EAAE;AAAA,QACpC,QAAQ;AAAA,QAA6C;AAAA,MACzD;AAEA,YAAM,MAAM;AAAA,QACR,QAAQ,EAAE,IAAI,MAAM;AAAA,QACpB,OAAO,EAAE,IAAI,MAAM;AAAA,QACnB;AAAA,QACA,SAAS;AAAA,QACT,QAAQ,EAAE,IAAI;AAAA,QACd,MAAM,EAAE,IAAI;AAAA,QACZ,SAAS,YAAY;AACjB,gBAAM,KAAK,MAAM,EAAE,IAAI,YAAY;AACnC,iBAAO,OAAO,KAAK,EAAE;AAAA,QACzB;AAAA,MACJ;AAEA,UAAI;AACJ,UAAI,mBAA2D;AAC/D,UAAI,gBAAoC;AACxC,UAAI,gBAAwC,CAAC;AAC7C,UAAI,cAAc;AAElB,YAAM,MAAM;AAAA,QACR,MAAM,CAAC,SAAc;AAAE,6BAAmB,EAAE,KAAK,IAAI;AAAA,QAAG;AAAA,QACxD,MAAM,CAAC,SAAqD;AACxD,cAAI,gBAAgB,cAAc,gBAAgB,eAAgB,OAAO,WAAW,eAAe,OAAO,WAAW,IAAI,GAAI;AACzH,kBAAMA,QAAO,gBAAgB,cAAc,OAAQ,KAAoB,OAAO,MAAO,KAAoB,YAAa,KAAoB,aAAc,KAAoB,UAAU;AACtL,+BAAmB,EAAE,KAAKA,KAAmB;AAAA,UACjD,OAAO;AACH,+BAAmB,EAAE,KAAK,IAAc;AAAA,UAC5C;AAAA,QACJ;AAAA,QACA,QAAQ,CAAC,SAAiB;AAAE,YAAE,OAAO,IAAI;AAAG,iBAAO;AAAA,QAAK;AAAA,QACxD,QAAQ,CAAC,MAAc,UAAkB;AACrC,YAAE,OAAO,MAAM,KAAK;AACpB,wBAAc,IAAI,IAAI;AACtB,iBAAO;AAAA,QACX;AAAA,QACA,OAAO,CAAC,UAA+B;AACnC,wBAAc;AACd,cAAI,oBAAoB,eAAe;AACnC,kBAAM,OAAO,OAAO,UAAU,WAAW,cAAc,OAAO,KAAK,IAAI;AACvE,6BAAiB,QAAQ,IAAI;AAAA,UACjC;AAAA,QACJ;AAAA,QACA,KAAK,MAAM;AACP,cAAI,kBAAkB;AAClB,6BAAiB,MAAM;AAAA,UAC3B;AAAA,QACJ;AAAA,MACJ;AAIA,YAAM,gBAAgB,IAAI,QAAyB,CAACC,aAAY;AAC5D,cAAM,SAAS,IAAI,eAAe;AAAA,UAC9B,MAAM,YAAY;AACd,+BAAmB;AACnB,4BAAgB,IAAI,YAAY;AAAA,UACpC;AAAA,QACJ,CAAC;AAGD,cAAM,SAAS,QAAQ,KAAY,GAAU;AAC7C,cAAM,OAAO,kBAAkB,UAAU,SAAS,QAAQ,QAAQ,MAAM;AACxE,aAAK,KAAK,MAAM;AACZ,cAAI,aAAa;AACb,YAAAA,SAAQ,IAAI,SAAS,QAAQ;AAAA,cACzB,QAAQ;AAAA,cACR,SAAS;AAAA,YACb,CAAC,CAAC;AAAA,UACN,OAAO;AAEH,8BAAkB,MAAM;AACxB,YAAAA,SAAQ,IAAI;AAAA,UAChB;AAAA,QACJ,CAAC,EAAE,MAAM,CAAC,QAAQ;AACd,4BAAkB,MAAM;AACxB,UAAAA,SAAQ,IAAI;AAAA,QAChB,CAAC;AAAA,MACL,CAAC;AAED,YAAM,iBAAiB,MAAM;AAC7B,aAAO,kBAAkB,oBAAoB,EAAE,KAAK,EAAE,OAAO,2BAA2B,GAAG,GAAG;AAAA,IAClG;AAAA,EACJ;AAAA,EAEA,IAAIC,OAAc,SAAuB;AACrC,SAAK,IAAI,IAAIA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EACzC;AAAA,EACA,KAAKA,OAAc,SAAuB;AACtC,SAAK,IAAI,KAAKA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC1C;AAAA,EACA,IAAIA,OAAc,SAAuB;AACrC,SAAK,IAAI,IAAIA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EACzC;AAAA,EACA,OAAOA,OAAc,SAAuB;AACxC,SAAK,IAAI,OAAOA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC5C;AAAA,EACA,MAAMA,OAAc,SAAuB;AACvC,SAAK,IAAI,MAAMA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC3C;AAAA,EAEA,IAAI,eAAoC,SAAsB;AAC1D,QAAI,OAAO,kBAAkB,YAAY,SAAS;AAC7C,WAAK,IAAI,IAAI,eAAe,OAAO,GAAG,SAAS;AAC3C,YAAI,aAAa;AACjB,cAAM,cAAc,MAAM;AAAE,uBAAa;AAAM,iBAAO,KAAK;AAAA,QAAG;AAC9D,cAAM,QAAQ,CAAC,GAAU,CAAC,GAAU,WAAW;AAC/C,YAAI,CAAC,WAAY,OAAM,KAAK;AAAA,MAChC,CAAC;AAAA,IACN,WAAW,OAAO,kBAAkB,YAAY;AAC3C,WAAK,IAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AACjC,YAAI,aAAa;AACjB,cAAM,cAAc,MAAM;AAAE,uBAAa;AAAM,iBAAO,KAAK;AAAA,QAAG;AAC9D,cAAM,cAAc,CAAC,GAAU,CAAC,GAAU,WAAW;AACrD,YAAI,CAAC,WAAY,OAAM,KAAK;AAAA,MAChC,CAAC;AAAA,IACN;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAMA,OAAc,QAAc;AAC9B,SAAK,IAAI,MAAMA,OAAM,MAAM;AAAA,EAC/B;AAAA,EAGA,MAAM,OAAO,MAAc;AACvB,QAAI,KAAK,YAAY;AACjB,WAAK,IAAI,IAAI,MAAM,YAAY,EAAE,MAAM,KAAK,WAAW,CAAC,CAAC;AAAA,IAC7D;AAEA,UAAM,aAAa,QAAQ,KAAK;AAChC,UAAM,aAAa;AAEnB,aAAS,UAAU,GAAG,UAAU,YAAY,WAAW;AACnD,YAAM,UAAU,aAAa;AAC7B,UAAI;AACA,cAAM,KAAK,UAAU,OAAO;AAC5B;AAAA,MACJ,SAAS,KAAU;AACf,YAAI,IAAI,SAAS,gBAAgB,UAAU,aAAa,GAAG;AACvD,cAAI,KAAK,UAAU,OAAO,KAAK,OAAO,UAAU,YAAY;AACxD,iBAAK,OAAO,MAAM;AAAA,UACtB;AACA;AAAA,QACJ;AACA,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,UAAU,MAA6B;AAC3C,WAAO,IAAI,QAAc,CAACD,UAAS,WAAW;AAC1C,YAAM,SAAS,MAAM;AAAA,QACjB,OAAO,KAAK,IAAI;AAAA,QAChB;AAAA,MACJ,GAAG,CAAC,SAAS;AACT,aAAK,gBAAgB,KAAK;AAC1B,QAAAA,SAAQ;AAAA,MACZ,CAAC;AACD,WAAK,SAAS;AACd,aAAO,GAAG,SAAS,CAAC,QAAa;AAC7B,eAAO,GAAG;AAAA,MACd,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA,EAEA,UAAU;AACN,WAAO,KAAK,iBAAiB,KAAK;AAAA,EACtC;AAAA;AAAA,EAGA,YAAY;AACR,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,QAAQ;AACV,QAAI,CAAC,KAAK,OAAQ;AAElB,QAAI,OAAO,KAAK,OAAO,wBAAwB,YAAY;AACvD,WAAK,OAAO,oBAAoB;AAAA,IACpC;AACA,UAAM,IAAI,QAAc,CAACA,UAAS,WAAW;AACzC,WAAK,OAAO,MAAM,CAAC,QAAc,MAAM,OAAO,GAAG,IAAIA,SAAQ,CAAE;AAAA,IACnE,CAAC;AAAA,EACL;AACJ;;;AC/QA,SAAS,YAAY;AACrB,SAAS,eAAAE,oBAAmB;AAC5B,YAAY,QAAQ;AACpB,YAAY,UAAU;;;ACoBf,SAAS,kBAAkB,QAAyB;AACvD,SAAO,uDAAuD,KAAK,MAAM;AAC7E;AAaO,SAAS,mBAAmB,QAAgB,SAA0B;AAEzE,MAAI,kBAAkB,MAAM,EAAG,QAAO;AAEtC,MAAI,YAAY,IAAK,QAAO;AAC5B,MAAI,YAAY,OAAQ,QAAO;AAK/B,QAAM,eAAe,QAChB,QAAQ,sBAAsB,MAAM,EACpC,QAAQ,OAAO,IAAI;AAExB,QAAM,QAAQ,IAAI,OAAO,IAAI,YAAY,GAAG;AAC5C,SAAO,MAAM,KAAK,MAAM;AAC5B;AAMO,SAAS,wBAAwB,UAAuC;AAC3E,MAAI,MAAM,QAAQ,QAAQ,GAAG;AACzB,WAAO,SAAS,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,EACrD;AACA,SAAO,SAAS,SAAS,GAAG,IACtB,SAAS,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IACrD,CAAC,SAAS,KAAK,CAAC,EAAE,OAAO,OAAO;AAC1C;AAYO,SAAS,oBACZ,UACiC;AACjC,QAAM,cAAc,wBAAwB,QAAQ;AAEpD,SAAO,CAAC,kBAA0B;AAC9B,QAAI,CAAC,cAAe,QAAO;AAC3B,eAAW,WAAW,aAAa;AAC/B,UAAI,mBAAmB,eAAe,OAAO,GAAG;AAC5C,eAAO;AAAA,MACX;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACJ;AAKO,SAAS,mBAAmB,UAAsC;AACrE,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAC3D,SAAO,KAAK,KAAK,OAAK,EAAE,SAAS,GAAG,CAAC;AACzC;;;ADvCO,IAAM,mBAAN,MAAyC;AAAA,EAa5C,YAAY,UAA6B,CAAC,GAAG;AAZ7C,gCAAO;AACP,gCAAO;AACP,mCAAU;AAOV,wBAAQ;AACR,wBAAQ;AAiBR;AAAA;AAAA;AAAA,gCAAO,OAAO,QAAuB;AACjC,UAAI,OAAO,MAAM,mCAAmC;AAAA,QAChD,MAAM,KAAK,QAAQ;AAAA,QACnB,YAAY,KAAK,QAAQ;AAAA,MAC7B,CAAC;AAID,UAAI,gBAAgB,eAAe,KAAK,MAAM;AAE9C,UAAI,gBAAgB,eAAe,KAAK,MAAM;AAC9C,UAAI,OAAO,MAAM,kCAAkC,EAAE,aAAa,cAAc,CAAC;AAIjF,YAAM,oBAAoB,QAAQ,IAAI,iBAAiB;AACvD,UAAI,KAAK,QAAQ,SAAS,SAAS,CAAC,mBAAmB;AACnD,cAAM,WAAW,OAAO,KAAK,QAAQ,SAAS,WAAW,KAAK,QAAQ,OAAO,CAAC;AAC9E,cAAM,UAAU,SAAS,WAAW;AAEpC,YAAI,SAAS;AACT,cAAI;AACJ,cAAI,SAAS,SAAS;AAClB,+BAAmB,SAAS;AAAA,UAChC,WAAW,QAAQ,IAAI,aAAa;AAChC,kBAAM,YAAY,QAAQ,IAAI,YAAY,KAAK;AAC/C,+BAAmB,UAAU,SAAS,GAAG,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,IAAI;AAAA,UAC3F,OAAO;AACH,+BAAmB;AAAA,UACvB;AAEA,gBAAM,cAAc,SAAS,eAAgB,QAAQ,IAAI,qBAAqB;AAC9E,gBAAM,SAAS,SAAS,WAAW,QAAQ,IAAI,eAAe,SAAS,QAAQ,IAAI,cAAc,EAAE,IAAI;AAOvG,cAAI;AAKJ,cAAI,qBAAqB,OAAO,aAAa;AAEzC,qBAAS,CAAC,kBAA0B,iBAAiB;AAAA,UACzD,WAAW,mBAAmB,gBAAgB,GAAG;AAG7C,qBAAS,oBAAoB,gBAAgB;AAAA,UACjD,OAAO;AAGH,kBAAM,UAAU,oBAAoB,gBAAgB;AACpD,qBAAS,CAAC,kBAA0B,QAAQ,aAAa;AAAA,UAC7D;AAEA,gBAAM,SAAS,KAAK,OAAO,UAAU;AAKrC,gBAAM,sBAAsB,CAAC,gBAAgB,iBAAiB,oBAAoB,eAAe,kBAAkB;AACnH,gBAAM,uBAAuB,CAAC,gBAAgB;AAC9C,gBAAM,eAAe,SAAS,gBAAgB;AAC9C,gBAAM,gBAAgB,MAAM,KAAK,oBAAI,IAAI;AAAA,YACrC,GAAG;AAAA,YACH,GAAI,SAAS,iBAAiB,CAAC;AAAA,UACnC,CAAC,CAAC;AAEF,iBAAO,IAAI,KAAK,KAAK;AAAA,YACjB;AAAA,YACA,cAAc,SAAS,WAAW,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS,QAAQ,SAAS;AAAA,YAC7F;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACJ,CAAC,CAAC;AAEF,cAAI,OAAO,MAAM,2BAA2B,EAAE,QAAQ,kBAAkB,YAAY,CAAC;AAAA,QACzF;AAAA,MACJ;AAAA,IACJ;AAKA;AAAA;AAAA;AAAA,iCAAQ,OAAO,QAAuB;AAClC,UAAI,OAAO,MAAM,6BAA6B;AAG9C,YAAM,SAAwB,KAAK,QAAQ,gBAAgB,CAAC;AAG5D,UAAI;AACA,cAAM,YAAY,IAAI,UAAU;AAChC,YAAI,UAAU,SAAS;AACnB,gBAAM,gBAAgB,UAAU,mBAAmB,MAC7C,MAAM,KAAK,UAAU,QAAQ,OAAO,CAAC,IACrC,MAAM,QAAQ,UAAU,OAAO,IAAI,UAAU,UAAU,OAAO,OAAO,UAAU,OAAO;AAE5F,qBAAW,UAAW,eAAyB;AAG3C,iBAAK,OAAO,SAAS,QAAQ,OAAO,SAAS,gBAAgB,OAAO,YAAY;AAE5E,oBAAM,OAAO,OAAO,QAAQ,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI;AACvD,oBAAM,YAAY,IAAI,IAAI;AAE1B,kBAAI,OAAO,MAAM,4BAA4B,OAAO,IAAI,IAAI;AAAA,gBACxD,MAAM;AAAA,gBACN,MAAM,OAAO;AAAA,cACjB,CAAC;AAED,qBAAO,KAAK;AAAA,gBACR,MAAM,OAAO;AAAA,gBACb,MAAM;AAAA,gBACN,SAAS;AAAA;AAAA,gBACT,KAAK;AAAA,cACT,CAAC;AAGD,kBAAI,OAAO,WAAW,OAAO,WAAW;AACnC,sBAAM,SAAS,KAAK,OAAO,UAAU;AACrC,uBAAO,IAAI,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC;AAC5C,oBAAI,OAAO,MAAM,iCAAiC,SAAS,EAAE;AAAA,cAClE;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ,SAAS,KAAU;AACf,YAAI,OAAO,KAAK,sCAAsC,EAAE,OAAO,IAAI,WAAW,IAAI,CAAC;AAAA,MACvF;AAGA,UAAI,KAAK,QAAQ,YAAY;AACzB,eAAO,KAAK;AAAA,UACR,MAAM,KAAK,QAAQ;AAAA,UACnB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,KAAK,KAAK,QAAQ;AAAA,QACtB,CAAC;AAAA,MACL;AAEA,UAAI,OAAO,SAAS,GAAG;AACnB,cAAM,SAAS,KAAK,OAAO,UAAU;AAErC,mBAAW,SAAS,QAAQ;AACxB,gBAAM,YAAiB,aAAQ,QAAQ,IAAI,GAAG,MAAM,IAAI;AAExD,cAAI,CAAI,cAAW,SAAS,GAAG;AAC3B,gBAAI,OAAO,KAAK,gCAAgC,SAAS,aAAa;AACtE;AAAA,UACJ;AAEA,gBAAM,YAAY,MAAM,QAAQ;AAChC,gBAAM,iBAAiB,UAAU,WAAW,GAAG,IAAI,YAAY,IAAI,SAAS;AAC5E,gBAAM,eAAe,mBAAmB,MAAM,OAAO,GAAG,eAAe,QAAQ,OAAO,EAAE,CAAC;AAGzF,gBAAM,SAAS,mBAAmB,MAAM,CAAC,YAAY,IAAI,CAAC,gBAAgB,YAAY;AAEtF,cAAI,OAAO,MAAM,yBAAyB;AAAA,YACtC,IAAI;AAAA,YACJ,MAAM;AAAA,YACN,SAAS,MAAM;AAAA,YACf,KAAK,MAAM;AAAA,UACf,CAAC;AAED,iBAAO,QAAQ,WAAS;AAEpB,mBAAO;AAAA,cACH;AAAA,cACAC,aAAY;AAAA,gBACR,MAAM,MAAM;AAAA,gBACZ,oBAAoB,CAAC,YAAY;AAC7B,sBAAI,MAAM,WAAW,mBAAmB,KAAK;AAEzC,wBAAI,QAAQ,WAAW,cAAc,GAAG;AACpC,6BAAO,QAAQ,UAAU,eAAe,MAAM,KAAK;AAAA,oBACvD;AAAA,kBACJ;AACA,yBAAO;AAAA,gBACX;AAAA,cACJ,CAAC;AAAA,YACL;AAGA,gBAAI,MAAM,KAAK;AACX,qBAAO,IAAI,OAAO,OAAO,GAAG,SAAS;AAEjC,sBAAM,SAAS,KAAK,QAAQ,cAAc,CAAC;AAC3C,sBAAM,WAAW,OAAO,KAAK,YAAY;AAEzC,oBAAI,EAAE,IAAI,KAAK,WAAW,QAAQ,GAAG;AACjC,yBAAO,KAAK;AAAA,gBAChB;AAEA,uBAAOA,aAAY;AAAA,kBACf,MAAM,MAAM;AAAA,kBACZ,oBAAoB,MAAM;AAAA,gBAC9B,CAAC,EAAE,GAAG,IAAI;AAAA,cACd,CAAC;AAAA,YACL;AAAA,UACJ,CAAC;AAAA,QACL;AAAA,MACJ;AAIA,YAAM,oBAAoB,KAAK,OAAO,UAAU;AAChD,UAAI,OAAO,kBAAkB,aAAa,YAAY;AAClD,0BAAkB,SAAS,CAAC,MAAW,EAAE,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG,CAAC;AAAA,MAC9E;AAIA,UAAI,KAAK,QAAQ,2BAA2B;AACxC,YAAI,KAAK,gBAAgB,YAAY;AACjC,eAAK,kCAAkC,GAAG;AAAA,QAC9C,CAAC;AAAA,MACL;AAiBA,UAAI,KAAK,oBAAoB,YAAY;AACrC,cAAM,OAAO,KAAK,QAAQ,QAAQ;AAClC,YAAI,OAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC;AAEjD,cAAM,KAAK,OAAO,OAAO,IAAI;AAE7B,cAAM,aAAa,KAAK,OAAO,QAAQ;AACvC,YAAI,eAAe,MAAM;AACrB,cAAI,OAAO,KAAK,QAAQ,IAAI,0BAA0B,UAAU,UAAU;AAAA,QAC9E;AACA,YAAI,OAAO,KAAK,oCAAoC;AAAA,UAChD,MAAM;AAAA,UACN,KAAK,oBAAoB,UAAU;AAAA,QACvC,CAAC;AAAA,MACL,CAAC;AAAA,IACL;AA5QI,SAAK,UAAU;AAAA,MACX,MAAM;AAAA,MACN,2BAA2B;AAAA,MAC3B,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,GAAG;AAAA,IACP;AAEA,SAAK,SAAS,IAAI,eAAe,KAAK,QAAQ,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA,EAyQQ,kCAAkC,KAAoB;AAC1D,UAAM,SAAS,KAAK,OAAO,UAAU;AACrC,UAAM,SAAS;AAGf,UAAM,YAAY;AAAA,MACd,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,QACJ,MAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,MAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,WAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,YAAe,GAAG,MAAM;AAAA,QACxB,IAAe,GAAG,MAAM;AAAA,QACxB,eAAe,GAAG,MAAM;AAAA,QACxB,MAAe,GAAG,MAAM;AAAA,QACxB,SAAe,GAAG,MAAM;AAAA,QACxB,IAAe,GAAG,MAAM;AAAA,MAC5B;AAAA,IACJ;AAGA,WAAO,IAAI,4BAA4B,CAAC,MAAW,EAAE,SAAS,GAAG,MAAM,YAAY,CAAC;AACpF,WAAO,IAAI,GAAG,MAAM,cAAc,CAAC,MAAW,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,CAAC;AAEzE,QAAI,OAAO,KAAK,kCAAkC,EAAE,OAAO,CAAC;AAG5D,UAAM,cAAc,MAAM,IAAI,WAAwB,UAAU;AAShE,UAAM,aAAa,OAAO,MAAqC;AAC3D,UAAI;AACA,cAAM,cAAmB,IAAI,WAAW,MAAM;AAC9C,YAAI,CAAC,YAAa,QAAO;AACzB,YAAI,MAAW,YAAY;AAC3B,YAAI,CAAC,OAAO,OAAO,YAAY,WAAW,YAAY;AAClD,gBAAM,MAAM,YAAY,OAAO;AAAA,QACnC;AACA,YAAI,CAAC,KAAK,WAAY,QAAO;AAC7B,cAAM,UAAU,MAAM,IAAI,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,QAAQ,CAAC;AACnE,YAAI,CAAC,SAAS,MAAM,GAAI,QAAO;AAC/B,cAAM,SAAS,QAAQ,KAAK;AAC5B,cAAM,WAAW,QAAQ,SAAS,wBAAwB;AAC1D,cAAM,cAAwB,CAAC;AAC/B,cAAM,QAAkB,CAAC;AACzB,YAAI;AACA,gBAAM,KAAK,YAAY;AACvB,gBAAM,SAAS,EAAE,SAAS,EAAE,UAAU,KAAK,EAAE;AAE7C,gBAAM,aAAa,MAAM,IAAI;AAAA,YACzB;AAAA,YACA;AAAA,cACI,OAAO,WACD,EAAE,SAAS,QAAQ,iBAAiB,SAAS,IAC7C,EAAE,SAAS,OAAO;AAAA,cACxB,OAAO;AAAA,cACP,GAAG;AAAA,YACP;AAAA,UACJ,EAAE,MAAM,MAAM,CAAC,CAAC;AAChB,qBAAW,KAAM,cAAc,CAAC,GAAa;AACzC,gBAAI,OAAO,EAAE,SAAS,UAAU;AAC5B,yBAAW,KAAK,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,GAAG;AAC5E,oBAAI,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,cACxC;AAAA,YACJ;AAAA,UACJ;AAMA,gBAAM,UAAU,MAAM,IAAI;AAAA,YACtB;AAAA,YACA,EAAE,OAAO,EAAE,SAAS,OAAO,GAAG,OAAO,KAAK,GAAG,OAAO;AAAA,UACxD,EAAE,MAAM,MAAM,CAAC,CAAC;AAChB,gBAAM,QAAQ,oBAAI,IAAY;AAC9B,qBAAW,KAAM,WAAW,CAAC,GAAa;AACtC,kBAAM,WAAW,EAAE,mBAAmB;AACtC,gBAAI,CAAC,YAAa,YAAY,aAAa,UAAW;AAClD,oBAAM,MAAM,EAAE,qBAAqB,EAAE;AACrC,kBAAI,IAAK,OAAM,IAAI,GAAG;AAAA,YAC1B;AAAA,UACJ;AACA,cAAI,MAAM,OAAO,GAAG;AAChB,kBAAM,SAAS,MAAM,IAAI;AAAA,cACrB;AAAA,cACA,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,KAAK,KAAK,EAAE,EAAE,GAAG,OAAO,KAAK,GAAG,OAAO;AAAA,YACvE,EAAE,MAAM,MAAM,CAAC,CAAC;AAChB,uBAAW,MAAO,UAAU,CAAC,GAAa;AACtC,kBAAI,GAAG,QAAQ,CAAC,YAAY,SAAS,GAAG,IAAI,EAAG,aAAY,KAAK,GAAG,IAAI;AAAA,YAC3E;AAAA,UACJ;AAAA,QACJ,QAAQ;AAAA,QAER;AAOA,YAAI,aAAuB,CAAC,MAAM;AAClC,YAAI,UAAU;AACV,cAAI;AACA,kBAAM,KAAK,YAAY;AACvB,kBAAM,SAAS,EAAE,SAAS,EAAE,UAAU,KAAK,EAAE;AAC7C,kBAAM,aAAa,MAAM,IAAI;AAAA,cACzB;AAAA,cACA,EAAE,OAAO,EAAE,iBAAiB,SAAS,GAAG,OAAO,KAAM,GAAG,OAAO;AAAA,YACnE,EAAE,MAAM,MAAM,CAAC,CAAC;AAChB,kBAAM,MAAM,oBAAI,IAAY,CAAC,MAAM,CAAC;AACpC,uBAAW,KAAM,cAAc,CAAC,GAAa;AACzC,oBAAM,MAAM,EAAE,WAAW,EAAE;AAC3B,kBAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,KAAI,IAAI,GAAG;AAAA,YAC9D;AACA,yBAAa,MAAM,KAAK,GAAG;AAAA,UAC/B,QAAQ;AAAA,UAER;AAAA,QACJ;AACA,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,cAAc;AAAA,QAClB;AAAA,MACJ,QAAQ;AACJ,eAAO;AAAA,MACX;AAAA,IACJ;AAGA,WAAO,KAAK,GAAG,MAAM,iBAAiB,OAAO,MAAW;AACpD,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AACnE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,OAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAChD,YAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAI;AACA,cAAM,MAAM,MAAM,GAAG,OAAO,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAQ;AACrE,cAAM,SAAS,EAAE,GAAG,MAAM,GAAG,IAAI;AACjC,eAAO,EAAE,KAAK,EAAE,QAAQ,IAAI,OAAO,IAAI,OAAO,CAAC;AAAA,MACnD,SAAS,KAAU;AACf,YAAI,KAAK,SAAS,uBAAuB,KAAK,SAAS,yBAAyB;AAC5E,iBAAO,EAAE,KAAK,EAAE,OAAO,IAAI,WAAW,YAAY,GAAG,GAAG;AAAA,QAC5D;AACA,cAAM;AAAA,MACV;AAAA,IACJ,CAAC;AAGD,WAAO,IAAI,GAAG,MAAM,qBAAqB,OAAO,MAAW;AACvD,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AACnE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,YAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAI;AACA,YAAI,MAAM,MAAM,GAAG,KAAK,QAAQ,EAAE,SAAS,QAAQ,CAAQ;AAC3D,YAAI,CAAC,IAAK,OAAM,CAAC;AACjB,cAAM,QAAQ,IAAI,KAAK,CAAC,MAAW,EAAE,OAAO,EAAE;AAC9C,eAAO,QAAQ,EAAE,KAAK,EAAE,QAAQ,IAAI,QAAQ,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;AAAA,MAC7F,SAAS,KAAU;AACf,YAAI,KAAK,SAAS,uBAAuB,KAAK,SAAS,yBAAyB;AAC5E,iBAAO,EAAE,KAAK,EAAE,OAAO,IAAI,WAAW,YAAY,GAAG,GAAG;AAAA,QAC5D;AACA,cAAM;AAAA,MACV;AAAA,IACJ,CAAC;AAGD,WAAO,IAAI,GAAG,MAAM,iBAAiB,OAAO,MAAW;AACnD,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,GAAI,QAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AACnE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAI;AACA,YAAI,MAAM,MAAM,GAAG,KAAK,QAAQ,EAAE,SAAS,QAAQ,CAAQ;AAC3D,YAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,OAAQ,IAAY,MAAO,OAAO,IAAY;AACzE,YAAI,CAAC,IAAK,OAAM,CAAC;AACjB,eAAO,EAAE,KAAK,EAAE,QAAQ,SAAS,KAAK,OAAO,IAAI,OAAO,CAAC;AAAA,MAC7D,SAAS,KAAU;AACf,YAAI,KAAK,SAAS,uBAAuB,KAAK,SAAS,yBAAyB;AAC5E,iBAAO,EAAE,KAAK,EAAE,OAAO,IAAI,WAAW,YAAY,GAAG,GAAG;AAAA,QAC5D;AACA,cAAM;AAAA,MACV;AAAA,IACJ,CAAC;AAmBD,WAAO,IAAI,GAAG,MAAM,wBAAwB,OAAO,MAAW;AAC1D,YAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAI,CAAC,SAAS,QAAQ;AAClB,eAAO,EAAE,KAAK,EAAE,eAAe,MAAM,CAAC;AAAA,MAC1C;AACA,UAAI;AACA,cAAM,WAAgB,IAAI,WAAW,UAAU;AAC/C,cAAM,YAAiB,IAAI,WAAW,sBAAsB;AAC5D,cAAM,aAAoB,MAAM;AAC5B,cAAI;AAAE,mBAAO,IAAI,WAAkB,kCAAkC,KAAK,CAAC;AAAA,UAAG,QACxE;AAAE,mBAAO,CAAC;AAAA,UAAG;AAAA,QACvB,GAAG;AACH,cAAM,gBAA+B,MAAM;AACvC,cAAI;AAAE,mBAAO,IAAI,WAA0B,gCAAgC,KAAK;AAAA,UAAkB,QAC5F;AAAE,mBAAO;AAAA,UAAkB;AAAA,QACrC,GAAG;AAIH,cAAM,MAAW,MAAM;AACnB,cAAI;AAAE,mBAAO,IAAI,WAAW,UAAU;AAAA,UAAG,QAAQ;AAAE,mBAAO;AAAA,UAAM;AAAA,QACpE,GAAG;AACH,cAAM,WAAW,KACX,OAAO,UAAoB;AACzB,cAAI;AACJ,cAAI;AACA,mBAAO,MAAM,GAAG;AAAA,cACZ;AAAA,cACA,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,GAAG,OAAO,MAAM,OAAO;AAAA,cACvD,EAAE,SAAS,EAAE,UAAU,KAAK,EAAE;AAAA,YAClC;AAAA,UACJ,QAAQ;AACJ,mBAAO,CAAC;AAAA,UACZ;AACA,gBAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,MAAM,WAAW,CAAC;AAC5D,iBAAO,KAAK,IAAI,CAAC,OAAY;AAAA,YACzB,MAAM,EAAE;AAAA,YACR,OAAO,EAAE;AAAA,YACT,SAAS,OAAO,EAAE,uBAAuB,WACnC,KAAK,MAAM,EAAE,sBAAsB,IAAI,IACvC,EAAE,sBAAsB,CAAC;AAAA,YAC/B,QAAQ,OAAO,EAAE,sBAAsB,WACjC,KAAK,MAAM,EAAE,qBAAqB,IAAI,IACtC,EAAE,qBAAqB,CAAC;AAAA,UAClC,EAAE;AAAA,QACN,IACE;AACN,YAAI,CAAC,aAAa,CAAC,UAAU;AAKzB,iBAAO,EAAE,KAAK;AAAA,YACV,eAAe;AAAA,YACf,QAAQ,QAAQ;AAAA,YAChB,UAAU,QAAQ,YAAY;AAAA,YAC9B,OAAO,QAAQ,SAAS,CAAC;AAAA,YACzB,gBAAgB,QAAQ,eAAe,CAAC;AAAA,YACxC,SAAS,CAAC;AAAA,YACV,QAAQ,CAAC;AAAA,UACb,CAAC;AAAA,QACL;AAMA,cAAM,YAAY;AAAA,UACd,GAAI,QAAQ,SAAS,CAAC;AAAA,UACtB,GAAI,QAAQ,eAAe,CAAC;AAAA,QAChC;AACA,YAAI,WAAkB,MAAM,UACvB,sBAAsB,WAAW,UAAU,WAAW,QAAQ,EAC9D,MAAM,MAAM,CAAC,CAAC;AACnB,YAAI,SAAS,WAAW,KAAK,cAAc;AACvC,qBAAW,MAAM,UACZ,sBAAsB,CAAC,YAAY,GAAG,UAAU,WAAW,QAAQ,EACnE,MAAM,MAAM,CAAC,CAAC;AAAA,QACvB;AAKA,cAAM,UAA+B,CAAC;AACtC,cAAM,SAAmE,CAAC;AAC1E,cAAM,oBAAoB,oBAAI,IAAY;AAC1C,cAAM,UAAkC,EAAE,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,EAAE;AAC/F,cAAM,iBAAsF,CAAC;AAC7F,mBAAW,MAAM,UAAU;AACvB,cAAI,IAAI,SAAS;AACb,uBAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,GAAG,OAAO,GAAG;AAClD,oBAAM,MAAM,QAAQ,GAAG,KAAK,CAAC;AAC7B,yBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAW,GAAG;AAC9C,oBAAI,MAAM,KAAM,KAAI,CAAC,IAAI;AAAA,yBAChB,IAAI,CAAC,MAAM,OAAW,KAAI,CAAC,IAAI;AAAA,cAC5C;AACA,sBAAQ,GAAG,IAAI;AAAA,YACnB;AAAA,UACJ;AACA,cAAI,IAAI,QAAQ;AACZ,uBAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,GAAG,MAAM,GAAG;AACjD,oBAAM,MAAM,OAAO,GAAG,KAAK,EAAE,UAAU,OAAO,UAAU,MAAM;AAC9D,oBAAM,IAAI;AACV,kBAAI,EAAE,SAAU,KAAI,WAAW;AAC/B,kBAAI,EAAE,SAAU,KAAI,WAAW;AAC/B,qBAAO,GAAG,IAAI;AAAA,YAClB;AAAA,UACJ;AACA,cAAI,MAAM,QAAQ,IAAI,iBAAiB,GAAG;AACtC,uBAAW,MAAM,GAAG,mBAAmB;AACnC,kBAAI,OAAO,OAAO,SAAU,mBAAkB,IAAI,EAAE;AAAA,YACxD;AAAA,UACJ;AACA,cAAI,IAAI,kBAAkB,OAAO,GAAG,mBAAmB,UAAU;AAC7D,uBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,GAAG,cAAyC,GAAG;AACnF,kBAAI,OAAO,QAAQ,YAAY,EAAE,OAAO,SAAU;AAClD,oBAAM,MAAM,eAAe,GAAG;AAC9B,kBAAI,CAAC,OAAO,QAAQ,GAAG,IAAI,QAAQ,GAAG,GAAG;AACrC,+BAAe,GAAG,IAAI;AAAA,cAC1B;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AACA,eAAO,EAAE,KAAK;AAAA,UACV,eAAe;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,UAAU,QAAQ,YAAY;AAAA,UAC9B,OAAO,QAAQ,SAAS,CAAC;AAAA,UACzB,gBAAgB,SAAS,IAAI,CAAC,MAAW,GAAG,IAAI,EAAE,OAAO,OAAO;AAAA,UAChE;AAAA,UACA;AAAA,UACA,mBAAmB,MAAM,KAAK,iBAAiB;AAAA,UAC/C;AAAA,QACJ,CAAC;AAAA,MACL,SAAS,KAAU;AACf,YAAI,OAAO,KAAK,sCAAsC,EAAE,KAAK,KAAK,QAAQ,CAAC;AAC3E,eAAO,EAAE,KAAK,EAAE,eAAe,MAAM,QAAQ,QAAQ,QAAQ,SAAS,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;AAAA,MAC1F;AAAA,IACJ,CAAC;AAQD,WAAO,IAAI,GAAG,MAAM,YAAY,OAAO,MAAW;AAC9C,YAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAI,CAAC,SAAS,OAAQ,QAAO,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AAChD,UAAI;AACA,cAAM,WAAgB,IAAI,WAAW,UAAU;AAC/C,YAAI,CAAC,UAAU,KAAM,QAAO,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AAC/C,cAAM,MAAc,MAAM,SAAS,KAAK,KAAK,KAAM,CAAC;AACpD,cAAM,WAAW,IAAI,IAAY,QAAQ,qBAAqB,CAAC,CAAC;AAChE,cAAM,OAAQ,QAAgB,kBAAkB,CAAC;AACjD,cAAM,WAAW,CAAC,IAAI,WAAW,sBAAsB;AACvD,cAAM,OAAO,IAAI,OAAO,CAAC,QAAa;AAClC,cAAI,CAAC,KAAK,KAAM,QAAO;AACvB,cAAI,KAAK,IAAI,IAAI,MAAM,SAAU,QAAO;AACxC,cAAI,SAAU,QAAO;AACrB,gBAAM,MAAgB,MAAM,QAAQ,IAAI,mBAAmB,IAAI,IAAI,sBAAsB,CAAC;AAC1F,iBAAO,IAAI,MAAM,CAAC,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA,QAC3C,CAAC;AACD,eAAO,EAAE,KAAK,EAAE,KAAK,CAAC;AAAA,MAC1B,SAAS,KAAU;AACf,YAAI,OAAO,KAAK,0BAA0B,EAAE,KAAK,KAAK,QAAQ,CAAC;AAC/D,eAAO,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AAAA,MAC9B;AAAA,IACJ,CAAC;AAED,QAAI,OAAO,MAAM,2CAA2C,EAAE,OAAO,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU;AACZ,SAAK,OAAO,MAAM;AAElB,YAAQ,IAAI,mCAAmC;AAAA,EACnD;AACJ;AAAA;AA1qBI,cANS,kBAMe,6BAA4B;AACpD,cAPS,kBAOe,0BAAyB;AACjD,cARS,kBAQe,+BAA8B;;;AF1E1D,0BAAc;","names":["body","resolve","path","serveStatic","serveStatic"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/plugin-hono-server",
3
- "version": "6.7.1",
3
+ "version": "6.8.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Standard Hono Server Adapter for ObjectStack Runtime",
6
6
  "main": "dist/index.js",
@@ -8,8 +8,8 @@
8
8
  "dependencies": {
9
9
  "@hono/node-server": "^2.0.4",
10
10
  "hono": "^4.12.23",
11
- "@objectstack/core": "6.7.1",
12
- "@objectstack/spec": "6.7.1"
11
+ "@objectstack/core": "6.8.1",
12
+ "@objectstack/spec": "6.8.1"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^25.9.1",