@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 +93 -202
- package/README.md +3 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/LICENSE
CHANGED
|
@@ -1,202 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
/**
|
package/dist/index.mjs.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":";;;;;;;;;;;;;;;;;;;;;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,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;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,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;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;AAznBI,cANS,kBAMe,6BAA4B;AACpD,cAPS,kBAOe,0BAAyB;AACjD,cARS,kBAQe,+BAA8B;;;AF1E1D,0BAAc;","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":"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","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.
|
|
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.
|
|
12
|
-
"@objectstack/spec": "6.
|
|
11
|
+
"@objectstack/core": "6.8.1",
|
|
12
|
+
"@objectstack/spec": "6.8.1"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/node": "^25.9.1",
|