@feasibleone/blong-gogo 1.26.0 → 1.27.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.27.0](https://github.com/feasibleone/blong/compare/blong-gogo-v1.26.0...blong-gogo-v1.27.0) (2026-07-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * compress static assets ([a0a376d](https://github.com/feasibleone/blong/commit/a0a376dc141a8b3041ad0c83895bd3de5675c2e2))
9
+ * static routes ([b544ca3](https://github.com/feasibleone/blong/commit/b544ca3b4c3ed858457c93f21b8523147c30bf59))
10
+
3
11
  ## [1.26.0](https://github.com/feasibleone/blong/compare/blong-gogo-v1.25.1...blong-gogo-v1.26.0) (2026-07-14)
4
12
 
5
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feasibleone/blong-gogo",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "repository": {
5
5
  "url": "git+https://github.com/feasibleone/blong.git"
6
6
  },
@@ -25,6 +25,8 @@
25
25
  "@fastify/cookie": "^11.0.2",
26
26
  "@fastify/cors": "^11.2.0",
27
27
  "@fastify/deepmerge": "^3.2.1",
28
+ "@fastify/helmet": "^13.1.0",
29
+ "@fastify/static": "^10.1.0",
28
30
  "@fastify/swagger": "^9.7.0",
29
31
  "@fastify/swagger-ui": "^5.2.5",
30
32
  "@fastify/type-provider-typebox": "^6.1.0",
@@ -84,7 +86,7 @@
84
86
  "playwright": "^1.60.0",
85
87
  "tap": "^21.6.3",
86
88
  "typescript": "^6.0.3",
87
- "@feasibleone/blong-dev": "1.1.0"
89
+ "@feasibleone/blong-dev": "1.1.1"
88
90
  },
89
91
  "scripts": {
90
92
  "browser-check": "node scripts/browser-compat-check.mjs",
package/src/Gateway.ts CHANGED
@@ -83,6 +83,9 @@ interface IConfig extends IConfigMLE {
83
83
  cache: object;
84
84
  audience: string;
85
85
  };
86
+ static?: {
87
+ root: string;
88
+ };
86
89
  }
87
90
 
88
91
  function operationParams(
@@ -610,6 +613,11 @@ export default class Gateway extends Internal implements IGateway {
610
613
  methodId: methodId,
611
614
  methodParts: methodParts,
612
615
  });
616
+ if (this.#config.static)
617
+ await this.#server.register(
618
+ (await import('./static.ts')).default,
619
+ this.#config.static,
620
+ );
613
621
  if (this.#config.cors)
614
622
  await this.#server.register((await import('./cors.ts')).default, this.#config.cors);
615
623
  if (this.#config.sign || this.#config.encrypt) {
package/src/Watch.ts CHANGED
@@ -195,7 +195,8 @@ export default class Watch extends Internal implements IWatch {
195
195
  },
196
196
  [[] as string[], [] as string[]],
197
197
  );
198
- const {Formatter, TypeScriptToTypeBox} = await import('@sinclair/typebox-codegen');
198
+ const codeGen = '@sinclair/typebox-codegen';
199
+ const {Formatter, TypeScriptToTypeBox} = await import(/* @vite-ignore */ codeGen);
199
200
  if (schema.length)
200
201
  this.#platform.writeFileSync(
201
202
  this.#platform.join(dir, '~.schema.ts'),
Binary file
package/src/jwt.ts CHANGED
@@ -5,6 +5,7 @@ import {type Errors, type ILocal} from '@feasibleone/blong/types';
5
5
  import type {FastifyInstance, FastifyPluginOptions, FastifyReply, FastifyRequest} from 'fastify';
6
6
  import fp from 'fastify-plugin';
7
7
  import {LRUCache} from 'lru-cache';
8
+ import isPublic from './public.ts';
8
9
 
9
10
  import {type IGatewayCodec} from './GatewayCodec.ts';
10
11
 
@@ -65,7 +66,7 @@ export default fp<{
65
66
  'preValidation',
66
67
  function (request: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void) {
67
68
  const auth = request.routeOptions.config.auth;
68
- if (auth !== false && !request.originalUrl.startsWith('/documentation')) {
69
+ if (auth !== false && !isPublic(request.originalUrl)) {
69
70
  if (auth === 'login') {
70
71
  request.auth = {credentials: {mlek: 'header', mlsk: 'header'}};
71
72
  done();
package/src/load.ts CHANGED
@@ -25,6 +25,7 @@ import {methodParts} from './lib.ts';
25
25
  import layerProxy from './layerProxy.ts';
26
26
  import RealmImpl, {type IRealm} from './Realm.ts';
27
27
  import type {IWatch} from './Watch.ts';
28
+ const extension = '.ts';
28
29
 
29
30
  const LAYER_FILE = 'layer' as const;
30
31
 
@@ -444,6 +445,7 @@ export default async function loadRealm<T extends TSchema>(
444
445
  },
445
446
  gateway: {
446
447
  port: 0,
448
+ static: {},
447
449
  // Static development keys, so sessions survive server hot-reloads
448
450
  /* cSpell:disable */
449
451
  sign: {
@@ -487,7 +489,7 @@ export default async function loadRealm<T extends TSchema>(
487
489
  load: () =>
488
490
  rootKind === 'browser' && globalThis.window
489
491
  ? import('./BrowserLog.ts')
490
- : import('./Log.ts'),
492
+ : import(/* vite-ignore */ './Log' + extension),
491
493
  },
492
494
  {
493
495
  name: 'apiSchema',
@@ -551,27 +553,27 @@ export default async function loadRealm<T extends TSchema>(
551
553
  {
552
554
  name: 'remote',
553
555
  deps: ['log', 'local'],
554
- load: () => import('./RpcClient.ts'),
556
+ load: () => import(/* @vite-ignore */ './RpcClient' + extension),
555
557
  },
556
558
  {
557
559
  name: 'rpcServer',
558
560
  deps: ['log'],
559
- load: () => import('./RpcServer.ts'),
561
+ load: () => import(/* @vite-ignore */ './RpcServer' + extension),
560
562
  },
561
563
  {
562
564
  name: 'gateway',
563
565
  deps: ['log'],
564
- load: () => import('./Gateway.ts'),
566
+ load: () => import(/* @vite-ignore */ './Gateway' + extension),
565
567
  },
566
568
  {
567
569
  name: 'restFs',
568
570
  deps: ['log', 'gateway'],
569
- load: () => import('./RestFs.ts'),
571
+ load: () => import(/* @vite-ignore */ './RestFs' + extension),
570
572
  },
571
573
  {
572
574
  name: 'systemDebug',
573
575
  deps: ['log', 'gateway', 'registry', 'rpcServer'],
574
- load: () => import('./SystemDebug.ts'),
576
+ load: () => import(/* @vite-ignore */ './SystemDebug' + extension),
575
577
  },
576
578
  {
577
579
  name: 'registry',
@@ -585,22 +587,22 @@ export default async function loadRealm<T extends TSchema>(
585
587
  'watch',
586
588
  'apiSchema',
587
589
  ],
588
- load: () => import('./Registry.ts'),
590
+ load: () => import(/* @vite-ignore */ './Registry' + extension),
589
591
  },
590
592
  {
591
593
  name: 'codec',
592
594
  deps: ['log'],
593
- load: () => import('./codec/server.ts'),
595
+ load: () => import(/* @vite-ignore */ './codec/server' + extension),
594
596
  },
595
597
  {
596
598
  name: 'orchestrator',
597
599
  deps: ['log'],
598
- load: () => import('./orchestrator/index.ts'),
600
+ load: () => import(/* @vite-ignore */ './orchestrator/index' + extension),
599
601
  },
600
602
  {
601
603
  name: 'adapter',
602
604
  deps: ['log'],
603
- load: () => import('./adapter/server.ts'),
605
+ load: () => import(/* @vite-ignore */ './adapter/server' + extension),
604
606
  },
605
607
  ]),
606
608
  ]).map(({name, load}) => {
@@ -777,7 +779,9 @@ export default async function loadRealm<T extends TSchema>(
777
779
  platformApi.join(destUrl, 'package.json'),
778
780
  )
779
781
  ) {
780
- const {createRealm} = await import('./kopi.ts');
782
+ const {createRealm} = await import(
783
+ /* @vite-ignore */ './kopi' + extension
784
+ );
781
785
  await createRealm(destUrl, logger);
782
786
  const mod = await import(/* @vite-ignore */ fileName);
783
787
  return mod.default ?? mod;
package/src/mle.ts CHANGED
@@ -2,6 +2,7 @@ import type {FastifyInstance, FastifyRequest} from 'fastify';
2
2
  import fp from 'fastify-plugin';
3
3
 
4
4
  import jose from './jose.ts';
5
+ import isPublic from './public.ts';
5
6
 
6
7
  export type IConfig = Parameters<typeof jose>[0] & {public: {sign: unknown; encrypt: unknown}};
7
8
 
@@ -14,6 +15,7 @@ export default fp<IConfig>(async function mlePlugin(fastify: FastifyInstance, co
14
15
  fastify.addHook(
15
16
  'preValidation',
16
17
  async (request: FastifyRequest<{Body: {jsonrpc?: string}}>, reply) => {
18
+ if (isPublic(request.originalUrl)) return;
17
19
  if (
18
20
  request.routeOptions.config.auth &&
19
21
  request.headers['content-type'] === 'application/json'
@@ -95,6 +97,7 @@ export default fp<IConfig>(async function mlePlugin(fastify: FastifyInstance, co
95
97
  checkpoints?: unknown;
96
98
  },
97
99
  ) => {
100
+ if (isPublic(request.originalUrl)) return payload;
98
101
  if (payload instanceof Error) return payload;
99
102
  const mlek =
100
103
  request.auth?.credentials?.mlek ||
@@ -108,10 +111,7 @@ export default fp<IConfig>(async function mlePlugin(fastify: FastifyInstance, co
108
111
  const encrypt: (message: object) => unknown = message =>
109
112
  request.routeOptions.config.mle === false
110
113
  ? message
111
- : mle.signEncrypt(
112
- message,
113
- mlek as {type: string},
114
- );
114
+ : mle.signEncrypt(message, mlek as {type: string});
115
115
  const where = payload.jsonrpc
116
116
  ? payload
117
117
  : {
package/src/public.ts ADDED
@@ -0,0 +1,9 @@
1
+ export default function isPublic(path: string): boolean {
2
+ return (
3
+ path.startsWith('/documentation/') ||
4
+ path.startsWith('/s/') ||
5
+ path === '/favicon.ico' ||
6
+ path === '/s' ||
7
+ path === '/documentation'
8
+ );
9
+ }
package/src/static.ts ADDED
@@ -0,0 +1,35 @@
1
+ import helmet from '@fastify/helmet';
2
+ import fastifyStatic from '@fastify/static';
3
+ import type {FastifyInstance} from 'fastify';
4
+ import fp from 'fastify-plugin';
5
+ import path from 'path';
6
+
7
+ export type IConfig = {
8
+ root: string;
9
+ };
10
+
11
+ export default fp<IConfig>(async function staticPlugin(fastify: FastifyInstance, config: IConfig) {
12
+ await fastify.register(helmet, {
13
+ contentSecurityPolicy: false, // Prevents helmet from wasting bytes on images/scripts/JSON
14
+ });
15
+ fastify.get('/favicon.ico', async (_request, reply) => {
16
+ return reply.sendFile('favicon.ico', import.meta.dirname);
17
+ });
18
+ fastify.register(fastifyStatic, {
19
+ root: config.root ?? path.join(process.cwd(), 'dist'),
20
+ prefix: '/s',
21
+ redirect: true,
22
+ preCompressed: true,
23
+ cacheControl: true,
24
+ maxAge: '1y',
25
+ immutable: true,
26
+ setHeaders: (res, filePath) => {
27
+ if (filePath.endsWith('.html')) {
28
+ res.header(
29
+ 'Content-Security-Policy',
30
+ "default-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline'",
31
+ );
32
+ }
33
+ },
34
+ });
35
+ });