@arcote.tech/arc-host 0.1.5 → 0.1.7

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/host.ts CHANGED
@@ -102,7 +102,7 @@ class RTCHost implements RealTimeCommunicationAdapter {
102
102
  */
103
103
  private getDefaultAuthContext(ipAddress?: string): AuthContext {
104
104
  return {
105
- userId: "anonymous",
105
+ userId: undefined as any,
106
106
  roles: [],
107
107
  ipAddress,
108
108
  };
@@ -282,6 +282,70 @@ class RTCHost implements RealTimeCommunicationAdapter {
282
282
  }
283
283
  }
284
284
 
285
+ private async handleRoute(req: Request) {
286
+ const url = new URL(req.url);
287
+ const method = req.method;
288
+
289
+ // Find matching route
290
+ let matchedRoute: any = null;
291
+ let routeParams: Record<string, string> = {};
292
+
293
+ for (const element of this.context.elements) {
294
+ // Check if element has matchesRoutePath method (ArcRoute)
295
+ if (typeof (element as any).matchesRoutePath === "function") {
296
+ const { matches, params } = (element as any).matchesRoutePath(
297
+ url.pathname,
298
+ );
299
+ if (matches) {
300
+ matchedRoute = element;
301
+ routeParams = params || {};
302
+ break;
303
+ }
304
+ }
305
+ }
306
+
307
+ if (!matchedRoute) {
308
+ return new Response("Route not found", { status: 404 });
309
+ }
310
+
311
+ const handler = matchedRoute.getHandler(method);
312
+ if (!handler) {
313
+ return new Response(`Method ${method} not allowed`, { status: 405 });
314
+ }
315
+
316
+ try {
317
+ // Extract token from Authorization header
318
+ const authHeader = req.headers.get("Authorization");
319
+ const token = authHeader?.replace("Bearer ", "");
320
+
321
+ const clientIp = this.getClientIpAddress(req);
322
+ let authContext: AuthContext;
323
+
324
+ if (token && !matchedRoute.isPublic) {
325
+ const payload = await this.verifyAuthToken(token);
326
+ if (!payload) {
327
+ return new Response("Invalid or expired token", { status: 401 });
328
+ }
329
+ authContext = this.tokenToAuthContext(payload, clientIp);
330
+ } else {
331
+ authContext = this.getDefaultAuthContext(clientIp);
332
+ }
333
+
334
+ // Use the model's routes method to properly handle event publishing
335
+ const routes = this.model.routes(authContext);
336
+ const response = await routes[matchedRoute.name](
337
+ method,
338
+ req,
339
+ routeParams,
340
+ url,
341
+ );
342
+ return response;
343
+ } catch (error) {
344
+ console.error(`Error executing route ${matchedRoute.name}:`, error);
345
+ return new Response("Internal Server Error", { status: 500 });
346
+ }
347
+ }
348
+
285
349
  private setupServer() {
286
350
  this.server = Bun.serve({
287
351
  fetch: async (req, server) => {
@@ -328,6 +392,12 @@ class RTCHost implements RealTimeCommunicationAdapter {
328
392
  return await this.handleQuery(req);
329
393
  }
330
394
 
395
+ // Try to handle as a route
396
+ const routeResponse = await this.handleRoute(req);
397
+ if (routeResponse.status !== 404) {
398
+ return routeResponse;
399
+ }
400
+
331
401
  return new Response("Not Found", { status: 404 });
332
402
  },
333
403
  websocket: {
@@ -344,25 +414,31 @@ class RTCHost implements RealTimeCommunicationAdapter {
344
414
  }
345
415
 
346
416
  private isPublicEndpoint(pathname: string): boolean {
347
- // Extract command name from pathname
348
- if (!pathname.startsWith("/command/")) {
349
- return false;
350
- }
417
+ // Iterate through all context elements and check if any match and are public
418
+ for (const element of this.context.elements) {
419
+ // Check if element has matchesCommandPath method (ArcCommand)
420
+ if (typeof (element as any).matchesCommandPath === "function") {
421
+ const { matches, isPublic } = (element as any).matchesCommandPath(
422
+ pathname,
423
+ );
424
+ if (matches) {
425
+ return isPublic;
426
+ }
427
+ }
351
428
 
352
- const commandName = pathname.split("/command/")[1];
353
- if (!commandName) {
354
- return false;
429
+ // Check if element has matchesRoutePath method (ArcRoute)
430
+ if (typeof (element as any).matchesRoutePath === "function") {
431
+ const { matches, isPublic } = (element as any).matchesRoutePath(
432
+ pathname,
433
+ );
434
+ if (matches) {
435
+ return isPublic;
436
+ }
437
+ }
355
438
  }
356
439
 
357
- // Get the command from the context and check if it's marked as public
358
- const contextElement = this.context.elements.find(
359
- (element: any) => element.name === commandName,
360
- );
361
-
362
- // Check if it's an ArcCommand instance and if it's marked as public
363
- return contextElement && "isPublic" in contextElement
364
- ? contextElement.isPublic === true
365
- : false;
440
+ // Default to non-public if no matching element found
441
+ return false;
366
442
  }
367
443
 
368
444
  private async handleSync(lastDate: string | null) {
package/index.ts CHANGED
@@ -3,11 +3,9 @@ import { rtcHostFactory } from "./host";
3
3
  import { sqliteAdapterFactory } from "./sqliteAdapter";
4
4
 
5
5
  export function hostLiveModel<C extends ArcContextAny>(
6
- contextPromise: Promise<C>,
6
+ context: C,
7
7
  options: { db: string; version: number },
8
8
  ) {
9
9
  const dbAdapterFactory = sqliteAdapterFactory(options.db);
10
- contextPromise.then((context) => {
11
- rtcHostFactory(context, dbAdapterFactory(context))();
12
- });
10
+ rtcHostFactory(context, dbAdapterFactory(context))();
13
11
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.5",
7
+ "version": "0.1.7",
8
8
  "private": false,
9
9
  "author": "Przemysław Krasiński [arcote.tech]",
10
10
  "dependencies": {