@objectstack/plugin-hono-server 4.0.4 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,6 +100,28 @@ kernel.use(new HonoServerPlugin({
100
100
  }));
101
101
  ```
102
102
 
103
+ ### Bearer token auth (`set-auth-token`)
104
+
105
+ The CORS middleware always includes `set-auth-token` in `Access-Control-Expose-Headers`
106
+ so that `@objectstack/plugin-auth`'s `bearer()` plugin can deliver rotated session
107
+ tokens to cross-origin and mobile clients. `Authorization` is included in
108
+ `Access-Control-Allow-Headers` by default so `Authorization: Bearer <token>`
109
+ requests succeed preflight.
110
+
111
+ You can contribute additional exposed / allowed headers — `exposeHeaders`
112
+ entries you supply are **merged** with the `set-auth-token` default:
113
+
114
+ ```typescript
115
+ kernel.use(new HonoServerPlugin({
116
+ cors: {
117
+ origins: ['https://app.example.com'],
118
+ credentials: true,
119
+ exposeHeaders: ['X-Request-Id'], // in addition to set-auth-token
120
+ allowHeaders: ['Content-Type', 'Authorization', 'X-Tenant-Id'],
121
+ },
122
+ }));
123
+ ```
124
+
103
125
  ## Architecture
104
126
 
105
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.
package/dist/index.d.mts CHANGED
@@ -8,6 +8,22 @@ interface HonoCorsOptions {
8
8
  enabled?: boolean;
9
9
  origins?: string | string[];
10
10
  methods?: string[];
11
+ /**
12
+ * Request headers allowed on preflight (`Access-Control-Allow-Headers`).
13
+ *
14
+ * Defaults to `['Content-Type', 'Authorization', 'X-Requested-With']`,
15
+ * which is sufficient for cookie and bearer-token auth.
16
+ */
17
+ allowHeaders?: string[];
18
+ /**
19
+ * Response headers exposed to JS (`Access-Control-Expose-Headers`).
20
+ *
21
+ * Defaults to `['set-auth-token']` so that better-auth's `bearer()` plugin
22
+ * can hand rotated session tokens to cross-origin clients. User-supplied
23
+ * values are merged with this default — `set-auth-token` is always
24
+ * exposed unless CORS is disabled entirely.
25
+ */
26
+ exposeHeaders?: string[];
11
27
  credentials?: boolean;
12
28
  maxAge?: number;
13
29
  }
@@ -81,6 +97,16 @@ interface HonoPluginOptions {
81
97
  */
82
98
  cors?: HonoCorsOptions | false;
83
99
  }
100
+ /**
101
+ * Hono Server Plugin
102
+ *
103
+ * Provides HTTP server capabilities using Hono framework.
104
+ * Registers the IHttpServer service so other plugins can register routes.
105
+ *
106
+ * Route registration is handled by plugins:
107
+ * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch
108
+ * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.
109
+ */
84
110
  declare class HonoServerPlugin implements Plugin {
85
111
  name: string;
86
112
  type: string;
@@ -110,4 +136,65 @@ declare class HonoServerPlugin implements Plugin {
110
136
  destroy(): Promise<void>;
111
137
  }
112
138
 
113
- export { type HonoCorsOptions, HonoHttpServer, type HonoPluginOptions, HonoServerPlugin, type StaticMount };
139
+ /**
140
+ * CORS origin pattern matching utilities.
141
+ *
142
+ * Supports the same wildcard syntax as better-auth's `trustedOrigins`:
143
+ * - `*` → matches any origin
144
+ * - `https://*.example.com` → matches any subdomain
145
+ * - `http://localhost:*` → matches any port
146
+ * - Comma-separated list of the above
147
+ *
148
+ * These helpers are shared between the Hono plugin's CORS middleware and
149
+ * consumers that need to apply CORS headers outside the Hono request
150
+ * pipeline (e.g., the Vercel serverless entrypoint's preflight
151
+ * short-circuit in `apps/objectos`). Keeping a single implementation
152
+ * ensures both paths stay consistent — divergence caused bug where
153
+ * wildcard `CORS_ORIGIN` values worked locally but produced browser
154
+ * CORS errors on Vercel.
155
+ */
156
+ /**
157
+ * Returns true when the origin points to localhost (any port, http or https).
158
+ *
159
+ * Matches:
160
+ * - `http://localhost`
161
+ * - `http://localhost:3000`
162
+ * - `https://localhost:8443`
163
+ * - `http://127.0.0.1:5173`
164
+ * - `http://[::1]:3000`
165
+ */
166
+ declare function isLocalhostOrigin(origin: string): boolean;
167
+ /**
168
+ * Check if an origin matches a pattern with wildcards.
169
+ *
170
+ * Localhost origins (`http(s)://localhost:<any-port>`, `127.0.0.1`, `[::1]`)
171
+ * are **always allowed** regardless of the pattern — this removes the need to
172
+ * enumerate every development port in `CORS_ORIGIN`.
173
+ *
174
+ * @param origin The origin to check (e.g., `https://app.example.com`)
175
+ * @param pattern The pattern to match against (supports `*` wildcard)
176
+ * @returns true if origin matches the pattern
177
+ */
178
+ declare function matchOriginPattern(origin: string, pattern: string): boolean;
179
+ /**
180
+ * Normalize a single string / comma-separated string / array into a
181
+ * trimmed array of non-empty patterns.
182
+ */
183
+ declare function normalizeOriginPatterns(patterns: string | string[]): string[];
184
+ /**
185
+ * Create a CORS origin matcher function that supports wildcard patterns.
186
+ *
187
+ * The returned function follows Hono's `cors({ origin })` contract:
188
+ * given the request's `Origin` header, it returns the origin to echo
189
+ * back in `Access-Control-Allow-Origin`, or `null` if the origin is not
190
+ * allowed.
191
+ *
192
+ * @param patterns Single pattern, array of patterns, or comma-separated patterns
193
+ */
194
+ declare function createOriginMatcher(patterns: string | string[]): (origin: string) => string | null;
195
+ /**
196
+ * True if any pattern in the given list contains a `*` wildcard.
197
+ */
198
+ declare function hasWildcardPattern(patterns: string | string[]): boolean;
199
+
200
+ export { type HonoCorsOptions, HonoHttpServer, type HonoPluginOptions, HonoServerPlugin, type StaticMount, createOriginMatcher, hasWildcardPattern, isLocalhostOrigin, matchOriginPattern, normalizeOriginPatterns };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,22 @@ interface HonoCorsOptions {
8
8
  enabled?: boolean;
9
9
  origins?: string | string[];
10
10
  methods?: string[];
11
+ /**
12
+ * Request headers allowed on preflight (`Access-Control-Allow-Headers`).
13
+ *
14
+ * Defaults to `['Content-Type', 'Authorization', 'X-Requested-With']`,
15
+ * which is sufficient for cookie and bearer-token auth.
16
+ */
17
+ allowHeaders?: string[];
18
+ /**
19
+ * Response headers exposed to JS (`Access-Control-Expose-Headers`).
20
+ *
21
+ * Defaults to `['set-auth-token']` so that better-auth's `bearer()` plugin
22
+ * can hand rotated session tokens to cross-origin clients. User-supplied
23
+ * values are merged with this default — `set-auth-token` is always
24
+ * exposed unless CORS is disabled entirely.
25
+ */
26
+ exposeHeaders?: string[];
11
27
  credentials?: boolean;
12
28
  maxAge?: number;
13
29
  }
@@ -81,6 +97,16 @@ interface HonoPluginOptions {
81
97
  */
82
98
  cors?: HonoCorsOptions | false;
83
99
  }
100
+ /**
101
+ * Hono Server Plugin
102
+ *
103
+ * Provides HTTP server capabilities using Hono framework.
104
+ * Registers the IHttpServer service so other plugins can register routes.
105
+ *
106
+ * Route registration is handled by plugins:
107
+ * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch
108
+ * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.
109
+ */
84
110
  declare class HonoServerPlugin implements Plugin {
85
111
  name: string;
86
112
  type: string;
@@ -110,4 +136,65 @@ declare class HonoServerPlugin implements Plugin {
110
136
  destroy(): Promise<void>;
111
137
  }
112
138
 
113
- export { type HonoCorsOptions, HonoHttpServer, type HonoPluginOptions, HonoServerPlugin, type StaticMount };
139
+ /**
140
+ * CORS origin pattern matching utilities.
141
+ *
142
+ * Supports the same wildcard syntax as better-auth's `trustedOrigins`:
143
+ * - `*` → matches any origin
144
+ * - `https://*.example.com` → matches any subdomain
145
+ * - `http://localhost:*` → matches any port
146
+ * - Comma-separated list of the above
147
+ *
148
+ * These helpers are shared between the Hono plugin's CORS middleware and
149
+ * consumers that need to apply CORS headers outside the Hono request
150
+ * pipeline (e.g., the Vercel serverless entrypoint's preflight
151
+ * short-circuit in `apps/objectos`). Keeping a single implementation
152
+ * ensures both paths stay consistent — divergence caused bug where
153
+ * wildcard `CORS_ORIGIN` values worked locally but produced browser
154
+ * CORS errors on Vercel.
155
+ */
156
+ /**
157
+ * Returns true when the origin points to localhost (any port, http or https).
158
+ *
159
+ * Matches:
160
+ * - `http://localhost`
161
+ * - `http://localhost:3000`
162
+ * - `https://localhost:8443`
163
+ * - `http://127.0.0.1:5173`
164
+ * - `http://[::1]:3000`
165
+ */
166
+ declare function isLocalhostOrigin(origin: string): boolean;
167
+ /**
168
+ * Check if an origin matches a pattern with wildcards.
169
+ *
170
+ * Localhost origins (`http(s)://localhost:<any-port>`, `127.0.0.1`, `[::1]`)
171
+ * are **always allowed** regardless of the pattern — this removes the need to
172
+ * enumerate every development port in `CORS_ORIGIN`.
173
+ *
174
+ * @param origin The origin to check (e.g., `https://app.example.com`)
175
+ * @param pattern The pattern to match against (supports `*` wildcard)
176
+ * @returns true if origin matches the pattern
177
+ */
178
+ declare function matchOriginPattern(origin: string, pattern: string): boolean;
179
+ /**
180
+ * Normalize a single string / comma-separated string / array into a
181
+ * trimmed array of non-empty patterns.
182
+ */
183
+ declare function normalizeOriginPatterns(patterns: string | string[]): string[];
184
+ /**
185
+ * Create a CORS origin matcher function that supports wildcard patterns.
186
+ *
187
+ * The returned function follows Hono's `cors({ origin })` contract:
188
+ * given the request's `Origin` header, it returns the origin to echo
189
+ * back in `Access-Control-Allow-Origin`, or `null` if the origin is not
190
+ * allowed.
191
+ *
192
+ * @param patterns Single pattern, array of patterns, or comma-separated patterns
193
+ */
194
+ declare function createOriginMatcher(patterns: string | string[]): (origin: string) => string | null;
195
+ /**
196
+ * True if any pattern in the given list contains a `*` wildcard.
197
+ */
198
+ declare function hasWildcardPattern(patterns: string | string[]): boolean;
199
+
200
+ export { type HonoCorsOptions, HonoHttpServer, type HonoPluginOptions, HonoServerPlugin, type StaticMount, createOriginMatcher, hasWildcardPattern, isLocalhostOrigin, matchOriginPattern, normalizeOriginPatterns };
package/dist/index.js CHANGED
@@ -34,7 +34,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
34
34
  var index_exports = {};
35
35
  __export(index_exports, {
36
36
  HonoHttpServer: () => HonoHttpServer,
37
- HonoServerPlugin: () => HonoServerPlugin
37
+ HonoServerPlugin: () => HonoServerPlugin,
38
+ createOriginMatcher: () => createOriginMatcher,
39
+ hasWildcardPattern: () => hasWildcardPattern,
40
+ isLocalhostOrigin: () => isLocalhostOrigin,
41
+ matchOriginPattern: () => matchOriginPattern,
42
+ normalizeOriginPatterns: () => normalizeOriginPatterns
38
43
  });
39
44
  module.exports = __toCommonJS(index_exports);
40
45
 
@@ -60,7 +65,9 @@ var HonoHttpServer = class {
60
65
  wrap(handler) {
61
66
  return async (c) => {
62
67
  let body = {};
63
- if (c.req.header("content-type")?.includes("application/json")) {
68
+ const contentType = c.req.header("content-type") ?? "";
69
+ const isOctetStream = contentType.includes("application/octet-stream");
70
+ if (contentType.includes("application/json")) {
64
71
  try {
65
72
  body = await c.req.json();
66
73
  } catch (e) {
@@ -69,19 +76,31 @@ var HonoHttpServer = class {
69
76
  } catch (e2) {
70
77
  }
71
78
  }
72
- } else {
79
+ } else if (!isOctetStream) {
73
80
  try {
74
81
  body = await c.req.parseBody();
75
82
  } catch (e) {
76
83
  }
77
84
  }
85
+ const rawHeaders = c.req.header();
86
+ if (!rawHeaders.host) {
87
+ try {
88
+ const u = new URL(c.req.url);
89
+ if (u.host) rawHeaders.host = u.host;
90
+ } catch {
91
+ }
92
+ }
78
93
  const req = {
79
94
  params: c.req.param(),
80
95
  query: c.req.query(),
81
96
  body,
82
- headers: c.req.header(),
97
+ headers: rawHeaders,
83
98
  method: c.req.method,
84
- path: c.req.path
99
+ path: c.req.path,
100
+ rawBody: async () => {
101
+ const ab = await c.req.arrayBuffer();
102
+ return Buffer.from(ab);
103
+ }
85
104
  };
86
105
  let capturedResponse;
87
106
  let streamController = null;
@@ -136,13 +155,13 @@ var HonoHttpServer = class {
136
155
  streamController?.close();
137
156
  resolve2(null);
138
157
  }
139
- }).catch(() => {
158
+ }).catch((err) => {
140
159
  streamController?.close();
141
160
  resolve2(null);
142
161
  });
143
162
  });
144
163
  const streamResponse = await streamPromise;
145
- return streamResponse ?? capturedResponse;
164
+ return streamResponse ?? capturedResponse ?? c.json({ error: "No response from handler" }, 500);
146
165
  };
147
166
  }
148
167
  get(path2, handler) {
@@ -163,11 +182,23 @@ var HonoHttpServer = class {
163
182
  use(pathOrHandler, handler) {
164
183
  if (typeof pathOrHandler === "string" && handler) {
165
184
  this.app.use(pathOrHandler, async (c, next) => {
166
- await handler({}, {}, next);
185
+ let nextCalled = false;
186
+ const wrappedNext = () => {
187
+ nextCalled = true;
188
+ return next();
189
+ };
190
+ await handler({}, {}, wrappedNext);
191
+ if (!nextCalled) await next();
167
192
  });
168
193
  } else if (typeof pathOrHandler === "function") {
169
194
  this.app.use("*", async (c, next) => {
170
- await pathOrHandler({}, {}, next);
195
+ let nextCalled = false;
196
+ const wrappedNext = () => {
197
+ nextCalled = true;
198
+ return next();
199
+ };
200
+ await pathOrHandler({}, {}, wrappedNext);
201
+ if (!nextCalled) await next();
171
202
  });
172
203
  }
173
204
  }
@@ -222,9 +253,13 @@ var HonoHttpServer = class {
222
253
  return this.app;
223
254
  }
224
255
  async close() {
225
- if (this.server && typeof this.server.close === "function") {
226
- this.server.close();
256
+ if (!this.server) return;
257
+ if (typeof this.server.closeAllConnections === "function") {
258
+ this.server.closeAllConnections();
227
259
  }
260
+ await new Promise((resolve2, reject) => {
261
+ this.server.close((err) => err ? reject(err) : resolve2());
262
+ });
228
263
  }
229
264
  };
230
265
 
@@ -233,21 +268,29 @@ var import_cors = require("hono/cors");
233
268
  var import_serve_static2 = require("@hono/node-server/serve-static");
234
269
  var fs = __toESM(require("fs"));
235
270
  var path = __toESM(require("path"));
271
+
272
+ // src/pattern-matcher.ts
273
+ function isLocalhostOrigin(origin) {
274
+ return /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(origin);
275
+ }
236
276
  function matchOriginPattern(origin, pattern) {
277
+ if (isLocalhostOrigin(origin)) return true;
237
278
  if (pattern === "*") return true;
238
279
  if (pattern === origin) return true;
239
280
  const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
240
281
  const regex = new RegExp(`^${regexPattern}$`);
241
282
  return regex.test(origin);
242
283
  }
243
- function createOriginMatcher(patterns) {
244
- let patternList;
245
- if (typeof patterns === "string") {
246
- patternList = patterns.includes(",") ? patterns.split(",").map((s) => s.trim()).filter(Boolean) : [patterns];
247
- } else {
248
- patternList = patterns;
284
+ function normalizeOriginPatterns(patterns) {
285
+ if (Array.isArray(patterns)) {
286
+ return patterns.map((p) => p.trim()).filter(Boolean);
249
287
  }
288
+ return patterns.includes(",") ? patterns.split(",").map((s) => s.trim()).filter(Boolean) : [patterns.trim()].filter(Boolean);
289
+ }
290
+ function createOriginMatcher(patterns) {
291
+ const patternList = normalizeOriginPatterns(patterns);
250
292
  return (requestOrigin) => {
293
+ if (!requestOrigin) return null;
251
294
  for (const pattern of patternList) {
252
295
  if (matchOriginPattern(requestOrigin, pattern)) {
253
296
  return requestOrigin;
@@ -256,6 +299,12 @@ function createOriginMatcher(patterns) {
256
299
  return null;
257
300
  };
258
301
  }
302
+ function hasWildcardPattern(patterns) {
303
+ const list = Array.isArray(patterns) ? patterns : [patterns];
304
+ return list.some((p) => p.includes("*"));
305
+ }
306
+
307
+ // src/hono-plugin.ts
259
308
  var HonoServerPlugin = class {
260
309
  constructor(options = {}) {
261
310
  __publicField(this, "name", "com.objectstack.server.hono");
@@ -291,23 +340,27 @@ var HonoServerPlugin = class {
291
340
  const credentials = corsOpts.credentials ?? process.env.CORS_CREDENTIALS !== "false";
292
341
  const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);
293
342
  let origin;
294
- const hasWildcard = (patterns) => {
295
- const list = Array.isArray(patterns) ? patterns : [patterns];
296
- return list.some((p) => p.includes("*"));
297
- };
298
343
  if (configuredOrigin === "*" && credentials) {
299
344
  origin = (requestOrigin) => requestOrigin || "*";
300
- } else if (hasWildcard(configuredOrigin)) {
345
+ } else if (hasWildcardPattern(configuredOrigin)) {
301
346
  origin = createOriginMatcher(configuredOrigin);
302
347
  } else {
303
- origin = configuredOrigin;
348
+ const matcher = createOriginMatcher(configuredOrigin);
349
+ origin = (requestOrigin) => matcher(requestOrigin);
304
350
  }
305
351
  const rawApp = this.server.getRawApp();
352
+ const defaultAllowHeaders = ["Content-Type", "Authorization", "X-Requested-With", "X-Tenant-ID", "X-Project-Id"];
353
+ const defaultExposeHeaders = ["set-auth-token"];
354
+ const allowHeaders = corsOpts.allowHeaders ?? defaultAllowHeaders;
355
+ const exposeHeaders = Array.from(/* @__PURE__ */ new Set([
356
+ ...defaultExposeHeaders,
357
+ ...corsOpts.exposeHeaders ?? []
358
+ ]));
306
359
  rawApp.use("*", (0, import_cors.cors)({
307
360
  origin,
308
361
  allowMethods: corsOpts.methods || ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
309
- allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
310
- exposeHeaders: [],
362
+ allowHeaders,
363
+ exposeHeaders,
311
364
  credentials,
312
365
  maxAge
313
366
  }));
@@ -408,6 +461,10 @@ var HonoServerPlugin = class {
408
461
  });
409
462
  }
410
463
  }
464
+ const rawAppForNotFound = this.server.getRawApp();
465
+ if (typeof rawAppForNotFound.notFound === "function") {
466
+ rawAppForNotFound.notFound((c) => c.json({ error: "Not found" }, 404));
467
+ }
411
468
  ctx.hook("kernel:ready", async () => {
412
469
  if (this.options.registerStandardEndpoints) {
413
470
  this.registerDiscoveryAndCrudEndpoints(ctx);
@@ -464,33 +521,124 @@ var HonoServerPlugin = class {
464
521
  rawApp.get(`${prefix}/discovery`, (c) => c.json({ data: discovery }));
465
522
  ctx.logger.info("Registered discovery endpoints", { prefix });
466
523
  const getObjectQL = () => ctx.getService("objectql");
524
+ const resolveCtx = async (c) => {
525
+ try {
526
+ const authService = ctx.getService("auth");
527
+ if (!authService) return void 0;
528
+ let api = authService.api;
529
+ if (!api && typeof authService.getApi === "function") {
530
+ api = await authService.getApi();
531
+ }
532
+ if (!api?.getSession) return void 0;
533
+ const session = await api.getSession({ headers: c.req.raw.headers });
534
+ if (!session?.user?.id) return void 0;
535
+ const userId = session.user.id;
536
+ const tenantId = session.session?.activeOrganizationId ?? void 0;
537
+ const permissions = [];
538
+ const roles = [];
539
+ try {
540
+ const ql = getObjectQL();
541
+ const sysCtx = { context: { isSystem: true } };
542
+ const memberRows = await ql?.find?.(
543
+ "sys_member",
544
+ {
545
+ where: tenantId ? { user_id: userId, organization_id: tenantId } : { user_id: userId },
546
+ limit: 50,
547
+ ...sysCtx
548
+ }
549
+ ).catch(() => []);
550
+ for (const m of memberRows ?? []) {
551
+ if (typeof m.role === "string") {
552
+ for (const r of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
553
+ if (!roles.includes(r)) roles.push(r);
554
+ }
555
+ }
556
+ }
557
+ const upsRows = await ql?.find?.(
558
+ "sys_user_permission_set",
559
+ { where: { user_id: userId }, limit: 100, ...sysCtx }
560
+ ).catch(() => []);
561
+ const psIds = /* @__PURE__ */ new Set();
562
+ for (const r of upsRows ?? []) {
563
+ const orgScope = r.organization_id ?? null;
564
+ if (!orgScope || tenantId && orgScope === tenantId) {
565
+ const pid = r.permission_set_id ?? r.permissionSetId;
566
+ if (pid) psIds.add(pid);
567
+ }
568
+ }
569
+ if (psIds.size > 0) {
570
+ const psRows = await ql?.find?.(
571
+ "sys_permission_set",
572
+ { where: { id: { $in: Array.from(psIds) } }, limit: 500, ...sysCtx }
573
+ ).catch(() => []);
574
+ for (const ps of psRows ?? []) {
575
+ if (ps.name && !permissions.includes(ps.name)) permissions.push(ps.name);
576
+ }
577
+ }
578
+ } catch {
579
+ }
580
+ return {
581
+ userId,
582
+ tenantId,
583
+ roles,
584
+ permissions,
585
+ isSystem: false
586
+ };
587
+ } catch {
588
+ return void 0;
589
+ }
590
+ };
467
591
  rawApp.post(`${prefix}/data/:object`, async (c) => {
468
592
  const ql = getObjectQL();
469
593
  if (!ql) return c.json({ error: "Data service not available" }, 503);
470
594
  const object = c.req.param("object");
471
595
  const data = await c.req.json().catch(() => ({}));
472
- const res = await ql.insert(object, data);
473
- const record = { ...data, ...res };
474
- return c.json({ object, id: record.id, record });
596
+ const execCtx = await resolveCtx(c);
597
+ try {
598
+ const res = await ql.insert(object, data, { context: execCtx });
599
+ const record = { ...data, ...res };
600
+ return c.json({ object, id: record.id, record });
601
+ } catch (err) {
602
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
603
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
604
+ }
605
+ throw err;
606
+ }
475
607
  });
476
608
  rawApp.get(`${prefix}/data/:object/:id`, async (c) => {
477
609
  const ql = getObjectQL();
478
610
  if (!ql) return c.json({ error: "Data service not available" }, 503);
479
611
  const object = c.req.param("object");
480
612
  const id = c.req.param("id");
481
- let all = await ql.find(object);
482
- if (!all) all = [];
483
- const match = all.find((i) => i.id === id);
484
- return match ? c.json({ object, id, record: match }) : c.json({ error: "Not found" }, 404);
613
+ const execCtx = await resolveCtx(c);
614
+ try {
615
+ let all = await ql.find(object, { context: execCtx });
616
+ if (!all) all = [];
617
+ const match = all.find((i) => i.id === id);
618
+ return match ? c.json({ object, id, record: match }) : c.json({ error: "Not found" }, 404);
619
+ } catch (err) {
620
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
621
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
622
+ }
623
+ throw err;
624
+ }
485
625
  });
486
626
  rawApp.get(`${prefix}/data/:object`, async (c) => {
487
627
  const ql = getObjectQL();
488
628
  if (!ql) return c.json({ error: "Data service not available" }, 503);
489
629
  const object = c.req.param("object");
490
- let all = await ql.find(object);
491
- if (!Array.isArray(all) && all && all.value) all = all.value;
492
- if (!all) all = [];
493
- return c.json({ object, records: all, total: all.length });
630
+ const execCtx = await resolveCtx(c);
631
+ try {
632
+ let all = await ql.find(object, { context: execCtx });
633
+ if (!Array.isArray(all) && all && all.value) all = all.value;
634
+ if (!all) all = [];
635
+ return c.json({ object, records: all, total: all.length });
636
+ } catch (err) {
637
+ if (err?.code === "PERMISSION_DENIED" || err?.name === "PermissionDeniedError") {
638
+ return c.json({ error: err.message ?? "Forbidden" }, 403);
639
+ }
640
+ throw err;
641
+ }
494
642
  });
495
643
  ctx.logger.debug("Registered standard CRUD data endpoints", { prefix });
496
644
  }
@@ -512,6 +660,11 @@ __reExport(index_exports, adapter_exports, module.exports);
512
660
  // Annotate the CommonJS export names for ESM import in node:
513
661
  0 && (module.exports = {
514
662
  HonoHttpServer,
515
- HonoServerPlugin
663
+ HonoServerPlugin,
664
+ createOriginMatcher,
665
+ hasWildcardPattern,
666
+ isLocalhostOrigin,
667
+ matchOriginPattern,
668
+ normalizeOriginPatterns
516
669
  });
517
670
  //# sourceMappingURL=index.js.map