@enkaku/server 0.13.3 → 0.14.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.
@@ -1,3 +1,4 @@
1
+ import { type DelegationChainOptions } from '@enkaku/capability';
1
2
  import type { SignedToken } from '@enkaku/token';
2
3
  export type EncryptionPolicy = 'required' | 'optional' | 'none';
3
4
  export type ProcedureAccessConfig = {
@@ -14,6 +15,6 @@ export type ProcedureAccessPayload = {
14
15
  prc?: string;
15
16
  exp?: number;
16
17
  };
17
- export declare function checkProcedureAccess(serverID: string, record: ProcedureAccessRecord, token: SignedToken<ProcedureAccessPayload>, atTime?: number): Promise<void>;
18
- export declare function checkClientToken(serverID: string, record: ProcedureAccessRecord, token: SignedToken, atTime?: number): Promise<void>;
18
+ export declare function checkProcedureAccess(serverID: string, record: ProcedureAccessRecord, token: SignedToken<ProcedureAccessPayload>, options?: DelegationChainOptions): Promise<void>;
19
+ export declare function checkClientToken(serverID: string, record: ProcedureAccessRecord, token: SignedToken, options?: DelegationChainOptions): Promise<void>;
19
20
  //# sourceMappingURL=access-control.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../src/access-control.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAE/D,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,qBAAqB,CAAA;AAElF,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;AAgBxE,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,qBAAqB,GAAG,SAAS,EACzC,YAAY,EAAE,gBAAgB,GAC7B,gBAAgB,CAYlB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,CAAC,sBAAsB,CAAC,EAC1C,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
1
+ {"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../src/access-control.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,sBAAsB,EAE5B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAE/D,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,qBAAqB,CAAA;AAElF,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;AAgBxE,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,qBAAqB,GAAG,SAAS,EACzC,YAAY,EAAE,gBAAgB,GAC7B,gBAAgB,CAYlB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,CAAC,sBAAsB,CAAC,EAC1C,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAiCf"}
@@ -24,7 +24,7 @@ export function resolveEncryptionPolicy(procedure, record, globalPolicy) {
24
24
  }
25
25
  return globalPolicy;
26
26
  }
27
- export async function checkProcedureAccess(serverID, record, token, atTime) {
27
+ export async function checkProcedureAccess(serverID, record, token, options) {
28
28
  const payload = token.payload;
29
29
  if (payload.prc == null) {
30
30
  throw new Error('No procedure to check');
@@ -51,14 +51,19 @@ export async function checkProcedureAccess(serverID, record, token, atTime) {
51
51
  await checkCapability({
52
52
  act: payload.prc,
53
53
  res: serverID
54
- }, payload, atTime);
54
+ }, payload, options);
55
55
  return;
56
- } catch {}
56
+ } catch (err) {
57
+ const message = err instanceof Error ? err.message : '';
58
+ if (!message.startsWith('Invalid capability') && !message.startsWith('Invalid payload') && !message.startsWith('Invalid token')) {
59
+ throw err;
60
+ }
61
+ }
57
62
  }
58
63
  }
59
64
  throw new Error('Access denied');
60
65
  }
61
- export async function checkClientToken(serverID, record, token, atTime) {
66
+ export async function checkClientToken(serverID, record, token, options) {
62
67
  const payload = token.payload;
63
68
  const procedure = payload.prc;
64
69
  if (procedure == null) {
@@ -70,7 +75,7 @@ export async function checkClientToken(serverID, record, token, atTime) {
70
75
  throw new Error('Invalid audience');
71
76
  }
72
77
  if (payload.exp != null) {
73
- assertNonExpired(payload, atTime);
78
+ assertNonExpired(payload, options?.atTime);
74
79
  }
75
80
  return;
76
81
  }
@@ -79,11 +84,11 @@ export async function checkClientToken(serverID, record, token, atTime) {
79
84
  await checkCapability({
80
85
  act: procedure,
81
86
  res: serverID
82
- }, payload, atTime);
87
+ }, payload, options);
83
88
  return;
84
89
  }
85
90
  if (payload.aud !== serverID) {
86
91
  throw new Error('Invalid audience');
87
92
  }
88
- await checkProcedureAccess(serverID, record, token, atTime);
93
+ await checkProcedureAccess(serverID, record, token, options);
89
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/handlers/channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAGV,cAAc,EAGf,MAAM,aAAa,CAAA;AAGpB,MAAM,MAAM,gBAAgB,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAEnE,wBAAgB,aAAa,CAC3B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EAEzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC7B,GAAG,EAAE,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,GACzC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFvB"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/handlers/channel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAGV,cAAc,EAGf,MAAM,aAAa,CAAA;AAGpB,MAAM,MAAM,gBAAgB,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAEnE,wBAAgB,aAAa,CAC3B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EAEzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC7B,GAAG,EAAE,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,GACzC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAwFvB"}
@@ -1,3 +1,4 @@
1
+ import { AttributeKeys, getActiveSpan } from '@enkaku/otel';
1
2
  import { createPipe, tap, writeTo } from '@enkaku/stream';
2
3
  import { executeHandler } from '../utils.js';
3
4
  export function handleChannel(ctx, msg) {
@@ -18,6 +19,7 @@ export function handleChannel(ctx, msg) {
18
19
  param
19
20
  });
20
21
  }
22
+ const activeSpan = getActiveSpan();
21
23
  const sendStream = createPipe();
22
24
  const issuer = msg.payload.iss;
23
25
  const controller = Object.assign(new AbortController(), {
@@ -29,10 +31,15 @@ export function handleChannel(ctx, msg) {
29
31
  });
30
32
  ctx.controllers[msg.payload.rid] = controller;
31
33
  const receiveStream = createPipe();
32
- receiveStream.readable.pipeTo(writeTo(async (val)=>{
34
+ const pipePromise = receiveStream.readable.pipeTo(writeTo(async (val)=>{
33
35
  if (controller.signal.aborted) {
34
36
  return;
35
37
  }
38
+ if (activeSpan != null) {
39
+ activeSpan.addEvent('channel.message.sent', {
40
+ [AttributeKeys.MESSAGE_DIRECTION]: 'send'
41
+ });
42
+ }
36
43
  ctx.logger.trace('send value to channel {procedure} with ID {rid}: {val}', {
37
44
  procedure: msg.payload.prc,
38
45
  rid: msg.payload.rid,
@@ -44,7 +51,13 @@ export function handleChannel(ctx, msg) {
44
51
  val
45
52
  });
46
53
  }));
54
+ // @ts-expect-error type instantiation too deep
47
55
  const readable = sendStream.readable.pipeThrough(tap((value)=>{
56
+ if (activeSpan != null) {
57
+ activeSpan.addEvent('channel.message.received', {
58
+ [AttributeKeys.MESSAGE_DIRECTION]: 'receive'
59
+ });
60
+ }
48
61
  ctx.logger.trace('received value from channel {procedure} with ID {rid}: {value}', {
49
62
  procedure: msg.payload.prc,
50
63
  rid: msg.payload.rid,
@@ -58,17 +71,11 @@ export function handleChannel(ctx, msg) {
58
71
  signal: controller.signal,
59
72
  writable: receiveStream.writable
60
73
  };
61
- // Wrap execution to ensure stream cleanup on handler crash
62
- return (async ()=>{
63
- try {
64
- // @ts-expect-error context and handler types
65
- await executeHandler(ctx, msg.payload, ()=>handler(handlerContext));
66
- } finally{
67
- try {
68
- await receiveStream.writable.close();
69
- } catch {
70
- // Stream may already be closed
71
- }
72
- }
73
- })();
74
+ return executeHandler({
75
+ context: ctx,
76
+ payload: msg.payload,
77
+ // @ts-expect-error handler context types
78
+ execute: ()=>handler(handlerContext),
79
+ beforeEnd: ()=>receiveStream.drain(pipePromise)
80
+ });
74
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/handlers/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,aAAa,CAAA;AAGjE,MAAM,MAAM,gBAAgB,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAEnE,wBAAgB,aAAa,CAC3B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EAEzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC7B,GAAG,EAAE,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,GACzC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BvB"}
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/handlers/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,aAAa,CAAA;AAGjE,MAAM,MAAM,gBAAgB,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAEnE,wBAAgB,aAAa,CAC3B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EAEzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC7B,GAAG,EAAE,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,GACzC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCvB"}
@@ -24,6 +24,10 @@ export function handleRequest(ctx, msg) {
24
24
  param: msg.payload.prm,
25
25
  signal: controller.signal
26
26
  };
27
- // @ts-expect-error context and handler types
28
- return executeHandler(ctx, msg.payload, ()=>handler(handlerContext));
27
+ return executeHandler({
28
+ context: ctx,
29
+ payload: msg.payload,
30
+ // @ts-expect-error handler context types
31
+ execute: ()=>handler(handlerContext)
32
+ });
29
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/handlers/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EACb,kBAAkB,EAClB,eAAe,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAA;AAG7E,MAAM,MAAM,eAAe,CACzB,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAElE,wBAAgB,YAAY,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EACzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA8DjG"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/handlers/stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EACb,kBAAkB,EAClB,eAAe,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAA;AAG7E,MAAM,MAAM,eAAe,CACzB,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,MAAM,IACjE,aAAa,CAAC,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAElE,wBAAgB,YAAY,CAC1B,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EACzC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DjG"}
@@ -1,3 +1,4 @@
1
+ import { AttributeKeys, getActiveSpan } from '@enkaku/otel';
1
2
  import { createPipe, writeTo } from '@enkaku/stream';
2
3
  import { executeHandler } from '../utils.js';
3
4
  export function handleStream(ctx, msg) {
@@ -18,13 +19,19 @@ export function handleStream(ctx, msg) {
18
19
  param
19
20
  });
20
21
  }
22
+ const activeSpan = getActiveSpan();
21
23
  const controller = new AbortController();
22
24
  ctx.controllers[msg.payload.rid] = controller;
23
25
  const receiveStream = createPipe();
24
- receiveStream.readable.pipeTo(writeTo(async (val)=>{
26
+ const pipePromise = receiveStream.readable.pipeTo(writeTo(async (val)=>{
25
27
  if (controller.signal.aborted) {
26
28
  return;
27
29
  }
30
+ if (activeSpan != null) {
31
+ activeSpan.addEvent('stream.message.sent', {
32
+ [AttributeKeys.MESSAGE_DIRECTION]: 'send'
33
+ });
34
+ }
28
35
  ctx.logger.trace('send value to stream {procedure} with ID {rid}: {val}', {
29
36
  procedure: msg.payload.prc,
30
37
  rid: msg.payload.rid,
@@ -42,17 +49,11 @@ export function handleStream(ctx, msg) {
42
49
  signal: controller.signal,
43
50
  writable: receiveStream.writable
44
51
  };
45
- // Wrap execution to ensure stream cleanup on handler crash
46
- return (async ()=>{
47
- try {
48
- // @ts-expect-error context and handler types
49
- await executeHandler(ctx, msg.payload, ()=>handler(handlerContext));
50
- } finally{
51
- try {
52
- await receiveStream.writable.close();
53
- } catch {
54
- // Stream may already be closed
55
- }
56
- }
57
- })();
52
+ return executeHandler({
53
+ context: ctx,
54
+ payload: msg.payload,
55
+ // @ts-expect-error handler context types
56
+ execute: ()=>handler(handlerContext),
57
+ beforeEnd: ()=>receiveStream.drain(pipePromise)
58
+ });
58
59
  }
package/lib/server.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Disposer } from '@enkaku/async';
2
+ import type { VerifyTokenHook } from '@enkaku/capability';
2
3
  import { type Logger } from '@enkaku/log';
4
+ import { type Tracer } from '@enkaku/otel';
3
5
  import { type AnyClientMessageOf, type ProtocolDefinition, type ServerTransportOf } from '@enkaku/protocol';
4
6
  import { type Validator } from '@enkaku/schema';
5
7
  import { type Identity } from '@enkaku/token';
@@ -7,15 +9,16 @@ import { type EncryptionPolicy, type ProcedureAccessRecord } from './access-cont
7
9
  import { type ResourceLimiter, type ResourceLimits } from './limits.js';
8
10
  import type { ProcedureHandlers, ServerEmitter } from './types.js';
9
11
  export type AccessControlParams = ({
10
- public: true;
12
+ requireAuth: false;
11
13
  serverID?: string;
12
- access?: ProcedureAccessRecord;
14
+ access: ProcedureAccessRecord;
13
15
  } | {
14
- public: false;
16
+ requireAuth: true;
15
17
  serverID: string;
16
18
  access: ProcedureAccessRecord;
17
19
  }) & {
18
20
  encryptionPolicy?: EncryptionPolicy;
21
+ verifyToken?: VerifyTokenHook;
19
22
  };
20
23
  export type HandleMessagesParams<Protocol extends ProtocolDefinition> = AccessControlParams & {
21
24
  events: ServerEmitter;
@@ -23,11 +26,12 @@ export type HandleMessagesParams<Protocol extends ProtocolDefinition> = AccessCo
23
26
  limiter: ResourceLimiter;
24
27
  logger: Logger;
25
28
  signal: AbortSignal;
29
+ tracer: Tracer;
26
30
  transport: ServerTransportOf<Protocol>;
27
31
  validator?: Validator<AnyClientMessageOf<Protocol>>;
28
32
  };
29
33
  export type ServerParams<Protocol extends ProtocolDefinition> = {
30
- access?: ProcedureAccessRecord;
34
+ accessControl?: false | true | ProcedureAccessRecord;
31
35
  encryptionPolicy?: EncryptionPolicy;
32
36
  getRandomID?: () => string;
33
37
  handlers: ProcedureHandlers<Protocol>;
@@ -35,14 +39,15 @@ export type ServerParams<Protocol extends ProtocolDefinition> = {
35
39
  limits?: Partial<ResourceLimits>;
36
40
  logger?: Logger;
37
41
  protocol?: Protocol;
38
- public?: boolean;
42
+ tracer?: Tracer;
39
43
  signal?: AbortSignal;
40
44
  transports?: Array<ServerTransportOf<Protocol>>;
45
+ verifyToken?: VerifyTokenHook;
41
46
  };
42
47
  export type HandleOptions = {
43
- access?: ProcedureAccessRecord;
48
+ accessControl?: false | true | ProcedureAccessRecord;
44
49
  logger?: Logger;
45
- public?: boolean;
50
+ verifyToken?: VerifyTokenHook;
46
51
  };
47
52
  export declare class Server<Protocol extends ProtocolDefinition> extends Disposer {
48
53
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/lib/server.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { DisposeInterruption, Disposer } from '@enkaku/async';
2
2
  import { EventEmitter } from '@enkaku/event';
3
3
  import { getEnkakuLogger } from '@enkaku/log';
4
+ import { AttributeKeys, createTracer, extractTraceContext, SpanNames, SpanStatusCode, setSpanOnContext, TraceFlags, withActiveContext } from '@enkaku/otel';
4
5
  import { createClientMessageSchema } from '@enkaku/protocol';
5
6
  import { createValidator, ValidationError } from '@enkaku/schema';
6
7
  import { createUnsignedToken, isSignedToken } from '@enkaku/token';
@@ -11,6 +12,7 @@ import { handleEvent } from './handlers/event.js';
11
12
  import { handleRequest } from './handlers/request.js';
12
13
  import { handleStream } from './handlers/stream.js';
13
14
  import { createResourceLimiter } from './limits.js';
15
+ const defaultTracer = createTracer('server');
14
16
  function defaultRandomID() {
15
17
  return globalThis.crypto.randomUUID();
16
18
  }
@@ -70,6 +72,20 @@ async function handleMessages(params) {
70
72
  const processMessage = validator ? (message)=>{
71
73
  const result = validator(message);
72
74
  if (result instanceof ValidationError) {
75
+ const validationSpan = params.tracer.startSpan(SpanNames.SERVER_HANDLE, {
76
+ attributes: {
77
+ [AttributeKeys.RPC_SYSTEM]: 'enkaku'
78
+ }
79
+ });
80
+ validationSpan.addEvent('enkaku.validation', {
81
+ [AttributeKeys.VALIDATION_SUCCESS]: false,
82
+ [AttributeKeys.VALIDATION_ERROR]: result.message
83
+ });
84
+ validationSpan.setStatus({
85
+ code: SpanStatusCode.ERROR,
86
+ message: 'Validation failed'
87
+ });
88
+ validationSpan.end();
73
89
  logger.debug('received invalid message', {
74
90
  error: result
75
91
  });
@@ -167,26 +183,174 @@ async function handleMessages(params) {
167
183
  context.send(error.toPayload(message.payload.rid));
168
184
  }
169
185
  }
170
- const process = params.public ? (message, handle)=>{
186
+ function getParentContext(message) {
187
+ const header = message.header;
188
+ return extractTraceContext(header);
189
+ }
190
+ function createHandleSpan(message) {
191
+ const parentCtx = getParentContext(message);
192
+ const procedure = message.payload.prc;
193
+ const rid = 'rid' in message.payload ? message.payload.rid : undefined;
194
+ // Build span links from client trace context
195
+ const header = message.header;
196
+ const links = [];
197
+ if (typeof header.tid === 'string' && typeof header.sid === 'string') {
198
+ links.push({
199
+ context: {
200
+ traceId: header.tid,
201
+ spanId: header.sid,
202
+ traceFlags: TraceFlags.SAMPLED,
203
+ isRemote: true
204
+ }
205
+ });
206
+ }
207
+ return params.tracer.startSpan(SpanNames.SERVER_HANDLE, {
208
+ attributes: {
209
+ [AttributeKeys.RPC_SYSTEM]: 'enkaku',
210
+ ...procedure != null ? {
211
+ [AttributeKeys.RPC_PROCEDURE]: procedure
212
+ } : {},
213
+ ...rid != null ? {
214
+ [AttributeKeys.RPC_REQUEST_ID]: rid
215
+ } : {}
216
+ },
217
+ links
218
+ }, parentCtx);
219
+ }
220
+ function wrapHandle(span, handle) {
221
+ return ()=>{
222
+ const spanCtx = setSpanOnContext(undefined, span);
223
+ const result = withActiveContext(spanCtx, ()=>{
224
+ const handlerSpan = params.tracer.startSpan(SpanNames.SERVER_HANDLER);
225
+ const handlerResult = handle();
226
+ if (handlerResult instanceof Error) {
227
+ if ('code' in handlerResult) {
228
+ handlerSpan.setAttribute(AttributeKeys.ERROR_CODE, handlerResult.code);
229
+ }
230
+ handlerSpan.setAttribute(AttributeKeys.ERROR_MESSAGE, handlerResult.message);
231
+ handlerSpan.setStatus({
232
+ code: SpanStatusCode.ERROR,
233
+ message: handlerResult.message
234
+ });
235
+ handlerSpan.recordException(handlerResult);
236
+ handlerSpan.end();
237
+ return handlerResult;
238
+ }
239
+ handlerResult.then(()=>{
240
+ handlerSpan.setStatus({
241
+ code: SpanStatusCode.OK
242
+ });
243
+ handlerSpan.end();
244
+ }).catch((err)=>{
245
+ if ('code' in err) {
246
+ handlerSpan.setAttribute(AttributeKeys.ERROR_CODE, err.code);
247
+ }
248
+ handlerSpan.setAttribute(AttributeKeys.ERROR_MESSAGE, err.message);
249
+ handlerSpan.setStatus({
250
+ code: SpanStatusCode.ERROR,
251
+ message: err.message
252
+ });
253
+ handlerSpan.recordException(err);
254
+ handlerSpan.end();
255
+ });
256
+ return handlerResult;
257
+ });
258
+ if (result instanceof Error) {
259
+ if ('code' in result) {
260
+ span.setAttribute(AttributeKeys.ERROR_CODE, result.code);
261
+ }
262
+ span.setAttribute(AttributeKeys.ERROR_MESSAGE, result.message);
263
+ span.setStatus({
264
+ code: SpanStatusCode.ERROR,
265
+ message: result.message
266
+ });
267
+ span.recordException(result);
268
+ span.end();
269
+ return result;
270
+ }
271
+ result.then(()=>{
272
+ span.setStatus({
273
+ code: SpanStatusCode.OK
274
+ });
275
+ span.end();
276
+ }).catch((err)=>{
277
+ if ('code' in err) {
278
+ span.setAttribute(AttributeKeys.ERROR_CODE, err.code);
279
+ }
280
+ span.setAttribute(AttributeKeys.ERROR_MESSAGE, err.message);
281
+ span.setStatus({
282
+ code: SpanStatusCode.ERROR,
283
+ message: err.message
284
+ });
285
+ span.recordException(err);
286
+ span.end();
287
+ });
288
+ return result;
289
+ };
290
+ }
291
+ const process = !params.requireAuth ? (message, handle)=>{
292
+ const span = createHandleSpan(message);
293
+ if (validator != null) {
294
+ span.addEvent('enkaku.validation', {
295
+ [AttributeKeys.VALIDATION_SUCCESS]: true
296
+ });
297
+ }
171
298
  if (!checkMessageEncryption(message)) {
299
+ span.setAttribute(AttributeKeys.AUTH_REASON, 'encryption_required');
300
+ span.setAttribute(AttributeKeys.ERROR_CODE, 'EK_ENCRYPTION');
301
+ span.setAttribute(AttributeKeys.ERROR_MESSAGE, 'Encryption required');
302
+ span.setStatus({
303
+ code: SpanStatusCode.ERROR,
304
+ message: 'Encryption required'
305
+ });
306
+ span.end();
172
307
  handleEncryptionViolation(message);
173
308
  return;
174
309
  }
175
- processHandler(message, handle);
310
+ processHandler(message, wrapHandle(span, handle));
176
311
  } : async (message, handle)=>{
312
+ const span = createHandleSpan(message);
313
+ if (validator != null) {
314
+ span.addEvent('enkaku.validation', {
315
+ [AttributeKeys.VALIDATION_SUCCESS]: true
316
+ });
317
+ }
177
318
  try {
178
- if (!params.public) {
179
- if (!isSignedToken(message)) {
180
- throw new Error('Message is not signed');
181
- }
182
- await checkClientToken(params.serverID, params.access, message);
319
+ if (!isSignedToken(message)) {
320
+ span.setAttribute(AttributeKeys.AUTH_REASON, 'unsigned_message');
321
+ span.setAttribute(AttributeKeys.AUTH_ALLOWED, false);
322
+ throw new Error('Message is not signed');
183
323
  }
324
+ await checkClientToken(params.serverID, params.access, message, params.verifyToken != null ? {
325
+ verifyToken: params.verifyToken
326
+ } : undefined);
327
+ const did = message.payload.iss;
328
+ if (did != null) {
329
+ span.setAttribute(AttributeKeys.AUTH_DID, did);
330
+ }
331
+ span.setAttribute(AttributeKeys.AUTH_ALLOWED, true);
184
332
  } catch (cause) {
333
+ const did = isSignedToken(message) ? message.payload.iss : undefined;
334
+ if (did != null) {
335
+ span.setAttribute(AttributeKeys.AUTH_DID, did);
336
+ }
337
+ span.setAttribute(AttributeKeys.AUTH_ALLOWED, false);
338
+ if (!cause.message?.includes('unsigned')) {
339
+ span.setAttribute(AttributeKeys.AUTH_REASON, cause.message);
340
+ }
185
341
  const error = new HandlerError({
186
342
  cause,
187
343
  code: 'EK02',
188
344
  message: cause.message ?? 'Access denied'
189
345
  });
346
+ span.setAttribute(AttributeKeys.ERROR_CODE, error.code);
347
+ span.setAttribute(AttributeKeys.ERROR_MESSAGE, error.message);
348
+ span.setStatus({
349
+ code: SpanStatusCode.ERROR,
350
+ message: error.message
351
+ });
352
+ span.recordException(error);
353
+ span.end();
190
354
  if (message.payload.typ === 'event') {
191
355
  events.emit('eventAuthError', {
192
356
  error,
@@ -202,10 +366,18 @@ async function handleMessages(params) {
202
366
  return;
203
367
  }
204
368
  if (!checkMessageEncryption(message)) {
369
+ span.setAttribute(AttributeKeys.AUTH_REASON, 'encryption_required');
370
+ span.setAttribute(AttributeKeys.ERROR_CODE, 'EK_ENCRYPTION');
371
+ span.setAttribute(AttributeKeys.ERROR_MESSAGE, 'Encryption required');
372
+ span.setStatus({
373
+ code: SpanStatusCode.ERROR,
374
+ message: 'Encryption required'
375
+ });
376
+ span.end();
205
377
  handleEncryptionViolation(message);
206
378
  return;
207
379
  }
208
- processHandler(message, handle);
380
+ processHandler(message, wrapHandle(span, handle));
209
381
  };
210
382
  async function handleNext() {
211
383
  const next = await transport.read();
@@ -262,8 +434,8 @@ async function handleMessages(params) {
262
434
  });
263
435
  break;
264
436
  }
265
- // In non-public mode, validate send messages against the channel owner
266
- if (!params.public) {
437
+ // In authenticated mode, validate send messages against the channel owner
438
+ if (params.requireAuth) {
267
439
  if (!isSignedToken(msg)) {
268
440
  const error = new HandlerError({
269
441
  code: 'EK02',
@@ -307,6 +479,7 @@ export class Server extends Disposer {
307
479
  #handling = [];
308
480
  #limiter;
309
481
  #logger;
482
+ #tracer;
310
483
  #validator;
311
484
  constructor(params){
312
485
  super({
@@ -348,22 +521,37 @@ export class Server extends Disposer {
348
521
  this.#logger = params.logger ?? getEnkakuLogger('server', {
349
522
  serverID: serverID ?? this.#getRandomID()
350
523
  });
524
+ this.#tracer = params.tracer ?? defaultTracer;
525
+ const accessControl = params.accessControl;
351
526
  if (serverID == null) {
352
- if (params.public) {
353
- this.#accessControl = {
354
- public: true,
355
- access: params.access,
356
- encryptionPolicy: params.encryptionPolicy
357
- };
358
- } else {
359
- throw new Error('Invalid server parameters: either the server "identity" must be provided or the "public" parameter must be set to true');
527
+ // No identity: accessControl must be explicitly false
528
+ if (accessControl !== false) {
529
+ throw new Error('Invalid server parameters: either "identity" must be provided or "accessControl" must be set to false');
360
530
  }
531
+ this.#accessControl = {
532
+ requireAuth: false,
533
+ access: {},
534
+ encryptionPolicy: params.encryptionPolicy,
535
+ verifyToken: params.verifyToken
536
+ };
537
+ } else if (accessControl === false) {
538
+ // Has identity but public access
539
+ this.#accessControl = {
540
+ requireAuth: false,
541
+ serverID,
542
+ access: {},
543
+ encryptionPolicy: params.encryptionPolicy,
544
+ verifyToken: params.verifyToken
545
+ };
361
546
  } else {
547
+ // Has identity with access control (true = server-only, record = granular)
548
+ const access = accessControl === true || accessControl == null ? {} : accessControl;
362
549
  this.#accessControl = {
363
- public: !!params.public,
550
+ requireAuth: true,
364
551
  serverID,
365
- access: params.access ?? {},
366
- encryptionPolicy: params.encryptionPolicy
552
+ access,
553
+ encryptionPolicy: params.encryptionPolicy,
554
+ verifyToken: params.verifyToken
367
555
  };
368
556
  }
369
557
  this.#limiter = createResourceLimiter(params.limits);
@@ -380,29 +568,38 @@ export class Server extends Disposer {
380
568
  return this.#events;
381
569
  }
382
570
  handle(transport, options = {}) {
383
- const publicAccess = options.public ?? this.#accessControl.public;
384
- const access = options.access ?? this.#accessControl.access ?? {};
571
+ const accessControlOverride = options.accessControl;
385
572
  const logger = options.logger ?? this.#logger.getChild('handler').with({
386
573
  transportID: this.#getRandomID()
387
574
  });
388
575
  const encryptionPolicy = this.#accessControl.encryptionPolicy;
389
576
  let accessControl;
390
- if (publicAccess) {
577
+ if (accessControlOverride === false) {
391
578
  accessControl = {
392
- public: true,
393
- access,
394
- encryptionPolicy
579
+ requireAuth: false,
580
+ access: this.#accessControl.access ?? {},
581
+ encryptionPolicy,
582
+ verifyToken: options.verifyToken ?? this.#accessControl.verifyToken
395
583
  };
396
- } else {
584
+ } else if (accessControlOverride != null) {
585
+ // Override with true or ProcedureAccessRecord
397
586
  const serverID = this.#accessControl.serverID;
398
587
  if (serverID == null) {
399
- return Promise.reject(new Error('Server ID is required to enable access control'));
588
+ return Promise.reject(new Error('Server identity is required to enable access control on transport'));
400
589
  }
590
+ const access = accessControlOverride === true ? {} : accessControlOverride;
401
591
  accessControl = {
402
- public: false,
592
+ requireAuth: true,
403
593
  serverID,
404
594
  access,
405
- encryptionPolicy
595
+ encryptionPolicy,
596
+ verifyToken: options.verifyToken ?? this.#accessControl.verifyToken
597
+ };
598
+ } else {
599
+ // Use server-level defaults
600
+ accessControl = {
601
+ ...this.#accessControl,
602
+ verifyToken: options.verifyToken ?? this.#accessControl.verifyToken
406
603
  };
407
604
  }
408
605
  const done = handleMessages({
@@ -411,6 +608,7 @@ export class Server extends Disposer {
411
608
  limiter: this.#limiter,
412
609
  logger,
413
610
  signal: this.#abortController.signal,
611
+ tracer: this.#tracer,
414
612
  transport,
415
613
  validator: this.#validator,
416
614
  ...accessControl
package/lib/utils.d.ts CHANGED
@@ -1,4 +1,14 @@
1
- import type { ProtocolDefinition, RequestPayloadOf } from '@enkaku/protocol';
2
- import type { HandlerContext, ResultType } from './types.js';
3
- export declare function executeHandler<Protocol extends ProtocolDefinition, Procedure extends keyof Protocol & string, Result extends ResultType<Protocol, Procedure> = ResultType<Protocol, Procedure>>(context: HandlerContext<Protocol>, payload: RequestPayloadOf<Procedure, Protocol[Procedure]>, execute: () => Result | Promise<Result>): Promise<void>;
1
+ import type { ProtocolDefinition } from '@enkaku/protocol';
2
+ import type { HandlerContext } from './types.js';
3
+ export type ExecuteHandlerParams<Protocol extends ProtocolDefinition> = {
4
+ context: HandlerContext<Protocol>;
5
+ payload: {
6
+ typ: string;
7
+ prc: string;
8
+ rid: string;
9
+ };
10
+ execute: () => unknown;
11
+ beforeEnd?: () => Promise<void>;
12
+ };
13
+ export declare function executeHandler<Protocol extends ProtocolDefinition>(params: ExecuteHandlerParams<Protocol>): Promise<void>;
4
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGhG,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAO5D,wBAAsB,cAAc,CAClC,QAAQ,SAAS,kBAAkB,EACnC,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,EACzC,MAAM,SAAS,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,EAEhF,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,EACjC,OAAO,EAAE,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,EACzD,OAAO,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAG9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAMhD,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,kBAAkB,IAAI;IACtE,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IACjC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,OAAO,EAAE,MAAM,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAChC,CAAA;AAED,wBAAsB,cAAc,CAAC,QAAQ,SAAS,kBAAkB,EACtE,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAoDf"}
package/lib/utils.js CHANGED
@@ -3,11 +3,14 @@ import { HandlerError } from './error.js';
3
3
  function canSend(signal) {
4
4
  return !signal.aborted || signal.reason === 'Close';
5
5
  }
6
- // @ts-expect-error type instantiation too deep
7
- export async function executeHandler(context, payload, execute) {
6
+ export async function executeHandler(params) {
7
+ const { context, payload, execute, beforeEnd } = params;
8
8
  const controller = context.controllers[payload.rid];
9
9
  try {
10
10
  const val = await toPromise(execute);
11
+ if (beforeEnd != null) {
12
+ await beforeEnd();
13
+ }
11
14
  if (canSend(controller.signal)) {
12
15
  context.logger.trace('send result to {type} {procedure} with ID {rid}: {result}', {
13
16
  type: payload.typ,
@@ -22,6 +25,9 @@ export async function executeHandler(context, payload, execute) {
22
25
  });
23
26
  }
24
27
  } catch (cause) {
28
+ if (beforeEnd != null) {
29
+ await beforeEnd();
30
+ }
25
31
  const error = HandlerError.from(cause, {
26
32
  code: 'EK01',
27
33
  message: 'Handler execution failed'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enkaku/server",
3
- "version": "0.13.3",
3
+ "version": "0.14.1",
4
4
  "license": "MIT",
5
5
  "homepage": "https://enkaku.dev",
6
6
  "description": "Server logic for Enkaku RPC",
@@ -25,17 +25,18 @@
25
25
  ],
26
26
  "sideEffects": false,
27
27
  "dependencies": {
28
- "@enkaku/async": "^0.13.0",
29
- "@enkaku/event": "^0.13.0",
30
- "@enkaku/capability": "^0.13.0",
31
- "@enkaku/log": "^0.13.1",
32
- "@enkaku/stream": "^0.13.0",
33
- "@enkaku/protocol": "^0.13.0",
34
- "@enkaku/token": "^0.13.0",
35
- "@enkaku/schema": "^0.13.0"
28
+ "@enkaku/capability": "^0.14.0",
29
+ "@enkaku/async": "^0.14.0",
30
+ "@enkaku/log": "^0.14.0",
31
+ "@enkaku/otel": "^0.14.0",
32
+ "@enkaku/protocol": "^0.14.0",
33
+ "@enkaku/token": "^0.14.0",
34
+ "@enkaku/stream": "^0.14.1",
35
+ "@enkaku/event": "^0.14.1",
36
+ "@enkaku/schema": "^0.14.0"
36
37
  },
37
38
  "devDependencies": {
38
- "@enkaku/transport": "^0.13.1"
39
+ "@enkaku/transport": "^0.14.0"
39
40
  },
40
41
  "scripts": {
41
42
  "build:clean": "del lib",