@feasibleone/blong-gogo 1.25.1 → 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,49 @@
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
+
11
+ ## [1.26.0](https://github.com/feasibleone/blong/compare/blong-gogo-v1.25.1...blong-gogo-v1.26.0) (2026-07-14)
12
+
13
+
14
+ ### Features
15
+
16
+ * authorization ([cf1a7ff](https://github.com/feasibleone/blong/commit/cf1a7ff605de0a6078fc859a31532443386808b2))
17
+ * blong-mock, blong-server ([756caca](https://github.com/feasibleone/blong/commit/756cacab712db6792bf1b93830fc1d833b396f8d))
18
+ * blong-party ([602a3b4](https://github.com/feasibleone/blong/commit/602a3b422e1f76c27a96b1cd5d5a8cf0edbc268f))
19
+ * check access credentials ([e6af405](https://github.com/feasibleone/blong/commit/e6af405c43921cb9c553db9c4986b7fe56154724))
20
+ * core.resource merging ([371d3e2](https://github.com/feasibleone/blong/commit/371d3e2b3f519dfd59ca4fd480bc33317992afcd))
21
+ * implement subjectObjectSchema ([8ca3282](https://github.com/feasibleone/blong/commit/8ca32829277b9816eb5e43ce708d2f1d0cc3ef28))
22
+ * improve playwright ([c88dca8](https://github.com/feasibleone/blong/commit/c88dca832548a878ce81aab7320395c3c6a81551))
23
+ * manifest concept ([ed3c038](https://github.com/feasibleone/blong/commit/ed3c038352644d2d8f828b3cb69cbea718e7084b))
24
+ * object schema ([688d5d2](https://github.com/feasibleone/blong/commit/688d5d2a4a895c8feba7b898da03c46b3a3f3292))
25
+ * playwright coverage ([616cf61](https://github.com/feasibleone/blong/commit/616cf61f8616dc7fa6fac8f75fcd5a8577e70448))
26
+ * schema helpers and constraints ([8409257](https://github.com/feasibleone/blong/commit/84092571f6b327c1166ef142f70cb7d91b013eaf))
27
+ * schema sync ([ae63684](https://github.com/feasibleone/blong/commit/ae63684ea85d2b0f7adc8e8a380e51e0564d54c4))
28
+ * seed ([5c76603](https://github.com/feasibleone/blong/commit/5c7660392bc1197638810e864bfd47ce1218b2e5))
29
+ * support expressions in the configuration ([843bb58](https://github.com/feasibleone/blong/commit/843bb58c97456d85eac573874c90755bdb68bc95))
30
+ * vitest coverage ([4c6d9cb](https://github.com/feasibleone/blong/commit/4c6d9cb941957b0291cbe091e06884589b1e8f06))
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * authorization flow test ([f816dc2](https://github.com/feasibleone/blong/commit/f816dc2fb0d1556dff04d42901cfe5c36cca0717))
36
+ * build ([de22713](https://github.com/feasibleone/blong/commit/de2271393350945b8d793ca0ec89670465bba085))
37
+ * core.resource merging ([f191055](https://github.com/feasibleone/blong/commit/f1910558ce0f369f556911c4c0f6397b8003fd33))
38
+ * full stack alignment for CRUD ([8795e08](https://github.com/feasibleone/blong/commit/8795e087182402ac28c3ea03c6bee27319069625))
39
+ * handle existing PK ([fb0041b](https://github.com/feasibleone/blong/commit/fb0041b8f6ac7ba2f0dc0a752ab19dd49c09d79f))
40
+ * hanging playwright install ([13bf7a4](https://github.com/feasibleone/blong/commit/13bf7a4031f1f94160e0cb9d8b4f83a1732e70af))
41
+ * keep running the server in CI for the playwright intent ([5d01f3b](https://github.com/feasibleone/blong/commit/5d01f3b044f4506c2f6fab3c2c048b116b59accb))
42
+ * login ([4def73d](https://github.com/feasibleone/blong/commit/4def73d33473d2b9183e91fa1ab030b2ccb85ad5))
43
+ * Playwright, Storybook and realm DRY ([4302512](https://github.com/feasibleone/blong/commit/43025123e613ef43e0ac5b83ab00fbc0cabfccf2))
44
+ * Playwright, Storybook and realm DRY ([7bbfad8](https://github.com/feasibleone/blong/commit/7bbfad8160da0ead5600ad56b543e935bfd01954))
45
+ * portal loading in the browser ([5c2bc76](https://github.com/feasibleone/blong/commit/5c2bc762dea53d7d2caa0022ea3d7e3f106fdddb))
46
+
3
47
  ## [1.25.1](https://github.com/feasibleone/blong/compare/blong-gogo-v1.25.0...blong-gogo-v1.25.1) (2026-05-25)
4
48
 
5
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feasibleone/blong-gogo",
3
- "version": "1.25.1",
3
+ "version": "1.27.0",
4
4
  "repository": {
5
5
  "url": "git+https://github.com/feasibleone/blong.git"
6
6
  },
@@ -24,12 +24,18 @@
24
24
  "@fastify/bearer-auth": "^10.1.2",
25
25
  "@fastify/cookie": "^11.0.2",
26
26
  "@fastify/cors": "^11.2.0",
27
+ "@fastify/deepmerge": "^3.2.1",
28
+ "@fastify/helmet": "^13.1.0",
29
+ "@fastify/static": "^10.1.0",
27
30
  "@fastify/swagger": "^9.7.0",
28
31
  "@fastify/swagger-ui": "^5.2.5",
32
+ "@fastify/type-provider-typebox": "^6.1.0",
29
33
  "@feasibleone/blong": "^1.0.0",
30
34
  "@feasibleone/blong-chain": "^1.0.0",
31
35
  "@feasibleone/blong-config": "^1.0.0",
32
36
  "@feasibleone/blong-kopi": "^1.0.0",
37
+ "@feasibleone/blong-lib": "^1.0.0",
38
+ "@feasibleone/blong-mock": "^1.0.0",
33
39
  "@keycloak/keycloak-admin-client": "^26.6.2",
34
40
  "@kubernetes/client-node": "^1.4.0",
35
41
  "@octokit/rest": "^22.0.1",
@@ -43,7 +49,7 @@
43
49
  "fastify-plugin": "^5.1.0",
44
50
  "got": "^14.6.6",
45
51
  "jose": "^6.2.1",
46
- "knex": "^3.1.0",
52
+ "knex": "^3.2.10",
47
53
  "ky": "^1.14.2",
48
54
  "lru-cache": "^11.2.4",
49
55
  "minimist": "^1.2.8",
@@ -64,10 +70,12 @@
64
70
  "undici": "^8.0.1",
65
71
  "ut-bitsyntax": "^6.2.7",
66
72
  "ut-dns-discovery": "^6.2.3",
73
+ "ut-function.cbc": "^1.1.6",
67
74
  "ut-function.interpolate": "1.1.3",
68
75
  "ut-function.merge": "^1.5.6",
69
76
  "uuid": "^13.0.0",
70
- "yaml": "^2.8.3"
77
+ "yaml": "^2.8.3",
78
+ "@feasibleone/blong-template": "1.0.0"
71
79
  },
72
80
  "devDependencies": {
73
81
  "@rushstack/heft": "^1.2.6",
@@ -75,10 +83,10 @@
75
83
  "@rushstack/heft-typescript-plugin": "^1.3.1",
76
84
  "@types/cacache": "^20.0.1",
77
85
  "@types/node": "^24",
78
- "playwright": "^1.58.2",
86
+ "playwright": "^1.60.0",
79
87
  "tap": "^21.6.3",
80
88
  "typescript": "^6.0.3",
81
- "@feasibleone/blong-dev": "1.0.2"
89
+ "@feasibleone/blong-dev": "1.1.1"
82
90
  },
83
91
  "scripts": {
84
92
  "browser-check": "node scripts/browser-compat-check.mjs",
@@ -11,7 +11,6 @@ import type {
11
11
  } from '@feasibleone/blong/types';
12
12
  import type net from 'node:net';
13
13
  import PQueue from 'p-queue';
14
- import merge from 'ut-function.merge';
15
14
 
16
15
  import ConfigRuntime from './ConfigRuntime.ts';
17
16
  import {isExpectedError} from './lib.ts';
@@ -87,6 +86,7 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
87
86
  configBase: string;
88
87
  log: unknown = null;
89
88
  importedMap?: Map<string, Record<string, (...args: unknown[]) => unknown>>;
89
+ platform: IApi['platform'];
90
90
 
91
91
  // These are prefixed with _ rather than using # private class fields.
92
92
  // The adapter uses Object.setPrototypeOf(current, base) to set the base as
@@ -103,7 +103,7 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
103
103
  // api.attachHandlers = ... only *after* the port factory returns. Storing
104
104
  // the api object and reading api.attachHandlers lazily (at call time)
105
105
  // ensures we always see the real function rather than the initial undefined.
106
- _api: Pick<IApi, 'attachHandlers'>;
106
+ _api: Pick<IApi, 'attachHandlers' | 'render' | 'attach'>;
107
107
  _createLog: IApi['createLog'];
108
108
  _attachCheckpoint: IApi['attachCheckpoint'];
109
109
  _activationNames: string[];
@@ -123,6 +123,9 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
123
123
  | 'attachHandlers'
124
124
  | 'createLog'
125
125
  | 'attachCheckpoint'
126
+ | 'render'
127
+ | 'platform'
128
+ | 'attach'
126
129
  >,
127
130
  configBase: string,
128
131
  activationNames: string[] = [],
@@ -133,6 +136,7 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
133
136
  this._dispatch = api.dispatch;
134
137
  this._methodId = api.methodId;
135
138
  this._getPath = api.getPath;
139
+ this.platform = api.platform;
136
140
  this._api = api;
137
141
  this._createLog = api.createLog;
138
142
  this._attachCheckpoint = api.attachCheckpoint;
@@ -150,7 +154,7 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
150
154
  }
151
155
 
152
156
  async init(...configs: object[]): Promise<void> {
153
- this.config = merge(this.activeConfig(), ...configs) as Config<T, C>;
157
+ this.config = this._api.render([this.activeConfig(), ...configs]) as Config<T, C>;
154
158
  this.log = this._createLog?.(this.config.logLevel || 'info', {
155
159
  ...this.config.log,
156
160
  name: this.config.id,
@@ -275,7 +279,7 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
275
279
  async drain(): Promise<void> {}
276
280
 
277
281
  findHandler(methodName: string): unknown {
278
- methodName = this._methodId(methodName);
282
+ methodName = this.methodPath(this._methodId(methodName));
279
283
  return this.imported[methodName];
280
284
  }
281
285
 
@@ -323,12 +327,12 @@ export class AdapterBase<T, C extends IContext> implements AdapterHandlerContext
323
327
  return this.event('start', {configBase: this.configBase, config});
324
328
  }
325
329
 
326
- async link(
330
+ async attach(
327
331
  patterns: (string | RegExp)[] | string | RegExp,
328
- target: AdapterHandlerContext = {} as unknown as AdapterHandlerContext,
332
+ target: object = {},
329
333
  ): Promise<object> {
330
- await this._api.attachHandlers(target, patterns);
331
- return target.imported;
334
+ await this._api.attach(target, patterns);
335
+ return target;
332
336
  }
333
337
 
334
338
  async handle(...params: unknown[]): Promise<unknown> {
@@ -371,17 +375,27 @@ export default async function adapter<T, C extends IContext>(
371
375
  configBase: string,
372
376
  activationNames: string[] = [],
373
377
  ): Promise<Adapter<T, C>> {
374
- const {adapter: adapterFactory, utError, handlers, remote, rpc, local, registry, type} = api;
378
+ const {
379
+ adapter: adapterFactory,
380
+ utError,
381
+ handlers,
382
+ remote,
383
+ rpc,
384
+ local,
385
+ registry,
386
+ type,
387
+ schema,
388
+ } = api;
375
389
  _errors ||= utError.register(errorMap);
376
390
 
377
391
  const base = new AdapterBase<T, C>(api, configBase, activationNames);
378
392
 
379
- const result = handlers!({utError, remote, type});
393
+ const result = handlers!({utError, remote, type, schema: registry.objectSchema, manifest: api.manifest});
380
394
  let current = result;
381
395
  while (current.extends) {
382
396
  const parent = await (typeof current.extends === 'string'
383
- ? adapterFactory(current.extends)!({utError, remote, rpc, local, registry})
384
- : current.extends({utError, remote, rpc, local, registry}));
397
+ ? adapterFactory(current.extends)!({utError, remote, rpc, local, registry, schema, manifest: api.manifest})
398
+ : current.extends({utError, remote, rpc, local, registry, schema, manifest: api.manifest}));
385
399
  Object.setPrototypeOf(current, parent);
386
400
  current = parent;
387
401
  }
@@ -133,7 +133,12 @@ export function createConfigProxy<T extends object>(
133
133
  get(_target, prop) {
134
134
  const container = getNode(path);
135
135
  const val = container == null ? undefined : Reflect.get(container as object, prop);
136
- if (val === undefined || val === null || typeof val !== 'object') {
136
+ if (
137
+ val === undefined ||
138
+ val === null ||
139
+ typeof val !== 'object' ||
140
+ val.constructor !== Object
141
+ ) {
137
142
  if (
138
143
  _factoryPhase.active &&
139
144
  val !== undefined &&
@@ -252,7 +257,9 @@ export function deepDiff(prev: unknown, next: unknown, path = ''): ConfigDiff {
252
257
  // ---------------------------------------------------------------------------
253
258
 
254
259
  export default class ConfigRuntime implements IConfigRuntime {
255
- readonly #loadParams: object;
260
+ readonly #baseConfig: object;
261
+ readonly #configs: object[];
262
+ readonly #suite: string;
256
263
  readonly #subscribers: Set<ConfigSubscriber> = new Set();
257
264
 
258
265
  #rawSnapshot: object = {};
@@ -262,8 +269,10 @@ export default class ConfigRuntime implements IConfigRuntime {
262
269
  readonly enterConfig: (mode?: FactoryPhaseMode) => void;
263
270
  readonly exitConfig: () => Error[];
264
271
 
265
- public constructor(loadParams: object = {}) {
266
- this.#loadParams = loadParams;
272
+ public constructor(baseConfig: object, configs: (object | boolean)[], suite: string) {
273
+ this.#baseConfig = baseConfig;
274
+ this.#configs = configs.filter(Boolean) as object[];
275
+ this.#suite = suite;
267
276
  const {proxy, update, enterConfig, exitConfig} = createConfigProxy(this.#rawSnapshot);
268
277
  this.#proxy = proxy;
269
278
  this.#updateProxy = update;
@@ -283,13 +292,18 @@ export default class ConfigRuntime implements IConfigRuntime {
283
292
  return this.#rawSnapshot;
284
293
  }
285
294
 
295
+ private mergeConfigs(blongConfig: object): object {
296
+ const loaded = loadBlong(blongConfig);
297
+ return merge({}, this.#baseConfig, ...this.#configs, loaded);
298
+ }
299
+
286
300
  /**
287
301
  * Load configuration from all sources using blong-config.
288
302
  * Merges the result into the proxy backing store so existing references
289
303
  * automatically reflect the new values.
290
304
  */
291
305
  public async load(params: object = {}): Promise<object> {
292
- const loaded = loadBlong({...this.#loadParams, ...params});
306
+ const loaded = this.mergeConfigs({config: {suite: this.#suite}, ...params});
293
307
  this.#rawSnapshot = loaded;
294
308
  this.#updateProxy(loaded);
295
309
  return this.#proxy;
@@ -304,7 +318,7 @@ export default class ConfigRuntime implements IConfigRuntime {
304
318
  */
305
319
  public async reload(): Promise<ConfigDiff> {
306
320
  const prev = this.#rawSnapshot;
307
- const next = loadBlong(this.#loadParams);
321
+ const next = this.mergeConfigs({config: {suite: this.#suite}});
308
322
  const diff = this.diff(prev, next);
309
323
 
310
324
  // Only do work when something actually changed
@@ -390,17 +404,4 @@ export default class ConfigRuntime implements IConfigRuntime {
390
404
  .flat(),
391
405
  ]);
392
406
  }
393
-
394
- /**
395
- * Merge module configs from multiple sources in the standard order.
396
- *
397
- * Previously done in `loadRealm()`:
398
- * `merge(mergedConfig, ...loadedConfigs.filter(Boolean))`
399
- *
400
- * @param base - the base config object (mutated in place)
401
- * @param sources - config objects to merge, in priority order
402
- */
403
- static mergeModuleConfig(base: object, ...sources: unknown[]): object {
404
- return merge(base, ...sources.filter(Boolean));
405
- }
406
407
  }
package/src/Gateway.ts CHANGED
@@ -7,6 +7,7 @@ import type {
7
7
  IGateway,
8
8
  ILocal,
9
9
  ILog,
10
+ IManifest,
10
11
  IMeta,
11
12
  IPlatformApi,
12
13
  } from '@feasibleone/blong/types';
@@ -15,12 +16,14 @@ import fastify, {type FastifyReply, type FastifyRequest, type RouteOptions} from
15
16
  import os from 'os';
16
17
  import type {LevelWithSilent} from 'pino';
17
18
  import {Type, type TSchema} from 'typebox';
19
+ import Value from 'typebox/value';
18
20
  import {v4} from 'uuid';
19
21
 
22
+ import {type TypeBoxTypeProvider} from '@fastify/type-provider-typebox';
20
23
  import type {IResolution} from './Resolution.ts';
21
24
  import type {IRpcClient} from './RpcClient.ts';
22
25
  import jwt from './jwt.ts';
23
- import {isExpectedError, methodParts, snakeToCamel} from './lib.ts';
26
+ import {isExpectedError, methodId, methodParts, snakeToCamel} from './lib.ts';
24
27
  import type {IConfig as IConfigMLE} from './mle.ts';
25
28
  import swagger from './swagger.ts';
26
29
 
@@ -68,11 +71,21 @@ interface IConfig extends IConfigMLE {
68
71
  * production to prevent callers from suppressing audit-level error logs.
69
72
  */
70
73
  expectedErrors?: boolean;
74
+ /**
75
+ * Optional handler name (semantic triple) that returns the list of allowed
76
+ * actions for the authenticated user. When set, the gateway calls this
77
+ * handler during authentication and stores the result in credentials,
78
+ * then enforces that every request's methodId is in the allowed list.
79
+ */
80
+ authorize?: string;
71
81
  errorFields: unknown[];
72
82
  jwt: {
73
83
  cache: object;
74
84
  audience: string;
75
85
  };
86
+ static?: {
87
+ root: string;
88
+ };
76
89
  }
77
90
 
78
91
  function operationParams(
@@ -146,7 +159,7 @@ export default class Gateway extends Internal implements IGateway {
146
159
  #config: IConfig = {
147
160
  host: '0.0.0.0',
148
161
  port: 8080,
149
- logLevel: 'trace',
162
+ logLevel: 'warn',
150
163
  cors: undefined,
151
164
  sign: undefined as IConfig['sign'] | undefined,
152
165
  encrypt: undefined as IConfig['encrypt'] | undefined,
@@ -156,6 +169,7 @@ export default class Gateway extends Internal implements IGateway {
156
169
  },
157
170
  debug: false,
158
171
  expectedErrors: false,
172
+ authorize: undefined,
159
173
  errorFields: [],
160
174
  jwt: {
161
175
  cache: {},
@@ -170,6 +184,7 @@ export default class Gateway extends Internal implements IGateway {
170
184
  #errorFields: [string, unknown][] = [];
171
185
  #plugins: {plugin: unknown; options: unknown}[] = [];
172
186
  #platform: IPlatformApi;
187
+ #manifest: IManifest | undefined;
173
188
 
174
189
  public constructor(
175
190
  config: IConfig,
@@ -180,6 +195,7 @@ export default class Gateway extends Internal implements IGateway {
180
195
  resolution,
181
196
  local,
182
197
  platform,
198
+ manifest,
183
199
  }: {
184
200
  log?: ILog;
185
201
  error?: IErrorFactory;
@@ -187,6 +203,7 @@ export default class Gateway extends Internal implements IGateway {
187
203
  local?: ILocal;
188
204
  resolution?: IResolution;
189
205
  platform?: IPlatformApi;
206
+ manifest?: IManifest;
190
207
  },
191
208
  ) {
192
209
  super({log});
@@ -194,6 +211,16 @@ export default class Gateway extends Internal implements IGateway {
194
211
  this.#platform = platform!;
195
212
 
196
213
  this.merge(this.#config, config);
214
+ this.#config.sign ||= (
215
+ process.env.GATEWAY_SIGN_KEY
216
+ ? {env: 'GATEWAY_SIGN_KEY'}
217
+ : {generate: {alg: 'ES384', crv: 'P-384', use: 'sig'}}
218
+ ) as IConfig['sign'];
219
+ this.#config.encrypt ||= (
220
+ process.env.GATEWAY_ENCRYPT_KEY
221
+ ? {env: 'GATEWAY_ENCRYPT_KEY'}
222
+ : {generate: {alg: 'ECDH-ES+A256KW', crv: 'P-384', use: 'enc'}}
223
+ ) as IConfig['encrypt'];
197
224
  this.#errorFields = Object.entries({
198
225
  type: true,
199
226
  message: true,
@@ -210,6 +237,7 @@ export default class Gateway extends Internal implements IGateway {
210
237
  this.#resolution = resolution!;
211
238
  this.#rpcClient = remote!;
212
239
  this.#local = local!;
240
+ this.#manifest = manifest;
213
241
  }
214
242
 
215
243
  protected config(): object {
@@ -336,6 +364,7 @@ export default class Gateway extends Internal implements IGateway {
336
364
  : `/${value.basePath ?? 'rpc'}/${method.split('.').join('/')}`,
337
365
  config: {
338
366
  auth: value.auth ?? 'jwt',
367
+ methodName: method,
339
368
  },
340
369
  schema: Type && {
341
370
  ...('body' in value
@@ -539,11 +568,34 @@ export default class Gateway extends Internal implements IGateway {
539
568
  strict: false,
540
569
  },
541
570
  },
571
+ }).withTypeProvider<TypeBoxTypeProvider>();
572
+
573
+ this.#server.setValidatorCompiler(({schema}: {schema: TSchema}) => {
574
+ return (data: unknown) => {
575
+ // 1. Runtime validation check
576
+ if (!Value.Check(schema, data)) {
577
+ const errors = [...Value.Errors(schema, data)];
578
+ return {
579
+ error: new Error(
580
+ `Validation failed: ${errors.map(e => e.message).join(', ')}`,
581
+ ),
582
+ };
583
+ }
584
+
585
+ // 2. Transform/Decode the validated string into a Date object
586
+ try {
587
+ const decoded = Value.Decode(schema, data);
588
+ return {value: decoded};
589
+ } catch (error) {
590
+ return {error};
591
+ }
592
+ };
542
593
  });
594
+
543
595
  this.#server.setErrorHandler(
544
596
  (error: Record<string, unknown>, request: FastifyRequest, reply: FastifyReply) => {
545
597
  request.log.error({err: error}, 'gateway unhandled error');
546
- return reply.status(500).send({
598
+ return reply.status((error as {statusCode?: number}).statusCode || 500).send({
547
599
  jsonrpc: '2.0',
548
600
  id: (request.body as {id?: unknown})?.id,
549
601
  error: this._formatError(error),
@@ -556,7 +608,16 @@ export default class Gateway extends Internal implements IGateway {
556
608
  this.#rpcClient.verify(token, options),
557
609
  errors: this.#errors,
558
610
  audience: this.#config.jwt.audience,
611
+ authorize: this.#config.authorize,
612
+ local: this.#local,
613
+ methodId: methodId,
614
+ methodParts: methodParts,
559
615
  });
616
+ if (this.#config.static)
617
+ await this.#server.register(
618
+ (await import('./static.ts')).default,
619
+ this.#config.static,
620
+ );
560
621
  if (this.#config.cors)
561
622
  await this.#server.register((await import('./cors.ts')).default, this.#config.cors);
562
623
  if (this.#config.sign || this.#config.encrypt) {
@@ -584,6 +645,13 @@ export default class Gateway extends Internal implements IGateway {
584
645
  port: this.#config.port,
585
646
  host: this.#config.host,
586
647
  });
648
+ // Publish the effective port to the manifest so other platforms
649
+ // (e.g. browser backend adapter) can discover it.
650
+ // The manifest proxy automatically resolves any pending deferred.
651
+ const address = this.#server.server.address();
652
+ if (this.#manifest && address && typeof address === 'object') {
653
+ this.#manifest.gatewayPort = address.port;
654
+ }
587
655
  return this;
588
656
  }
589
657