@fuzdev/fuz_app 0.42.0 → 0.43.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.
@@ -1 +1 @@
1
- {"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE9F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAGN,KAAK,gBAAgB,EAGrB,MAAM,oBAAoB,CAAC;AAW5B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,8DAA8D;IAC9D,EAAE,EAAE,EAAE,CAAC;IACP,oFAAoF;IACpF,aAAa,EAAE,EAAE,CAAC;IAClB,2EAA2E;IAC3E,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CACxD,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,yBAAyB,EACjE,MAAM,KAAK,EACX,SAAS,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KACvE,SAGD,CAAC;AAEH,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACxC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACZ;AA4DD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,wBAAwB,KAAG,KAAK,CAAC,SAAS,CA6PtF,CAAC"}
1
+ {"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAAgC,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE9F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAGN,KAAK,gBAAgB,EAGrB,MAAM,oBAAoB,CAAC;AAW5B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,8DAA8D;IAC9D,EAAE,EAAE,EAAE,CAAC;IACP,oFAAoF;IACpF,aAAa,EAAE,EAAE,CAAC;IAClB,2EAA2E;IAC3E,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CACxD,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,yBAAyB,EACjE,MAAM,KAAK,EACX,SAAS,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KACvE,SAGD,CAAC;AAEH,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACxC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACZ;AA4DD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,wBAAwB,KAAG,KAAK,CAAC,SAAS,CAsQtF,CAAC"}
@@ -17,7 +17,7 @@ import {} from '../http/route_spec.js';
17
17
  import { get_client_ip } from '../http/proxy.js';
18
18
  import { get_request_context, has_role } from '../auth/request_context.js';
19
19
  import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
20
- import { is_null_schema } from '../http/schema_helpers.js';
20
+ import { is_null_schema, is_void_schema } from '../http/schema_helpers.js';
21
21
  import { JSONRPC_VERSION, JsonrpcRequest, } from '../http/jsonrpc.js';
22
22
  import { jsonrpc_error_messages, jsonrpc_error_code_to_http_status, JSONRPC_ERROR_CODES, } from '../http/jsonrpc_errors.js';
23
23
  import { ERROR_INSUFFICIENT_PERMISSIONS, ERROR_KEEPER_REQUIRES_DAEMON_TOKEN, } from '../http/error_schemas.js';
@@ -127,6 +127,9 @@ export const create_rpc_endpoint = (options) => {
127
127
  if (action_map.has(action.spec.method)) {
128
128
  throw new Error(`Duplicate RPC action method: ${action.spec.method}`);
129
129
  }
130
+ if (is_null_schema(action.spec.input)) {
131
+ throw new Error(`RPC action "${action.spec.method}" uses z.null() for input — JSON-RPC 2.0 §4.2 forbids "params": null on the wire (must be omitted or be a Structured value). Use z.void() for parameterless methods.`);
132
+ }
130
133
  action_map.set(action.spec.method, action);
131
134
  }
132
135
  /**
@@ -163,12 +166,16 @@ export const create_rpc_endpoint = (options) => {
163
166
  return c.json(error, jsonrpc_error_code_to_http_status(auth_error.code));
164
167
  }
165
168
  // step 4: validate params
166
- // Missing `params` on the envelope maps to `null` for `z.null()` input
167
- // schemas and `{}` for object inputs matches HTTP's "empty body = empty
168
- // object" convention so callers of all-optional-object RPC methods can
169
- // omit `params` on the wire (JSON-RPC envelope still serializes without
170
- // a `params` field; no protocol-level change).
171
- const params = raw_params ?? (is_null_schema(action.spec.input) ? null : {});
169
+ // Missing `params` on the envelope maps to `undefined` for `z.void()`
170
+ // input schemas and `{}` for object inputs (matches HTTP's "empty
171
+ // body = empty object" convention so callers of all-optional-object
172
+ // RPC methods can omit `params` on the wire). JSON-RPC 2.0 §4.2
173
+ // forbids `params: null`, so `z.void()` is the spec-correct schema
174
+ // for parameterless methods registration above rejects `z.null()`
175
+ // inputs to keep this branch from having to consider that legacy
176
+ // shape. When `raw_params` is present it flows through unchanged so
177
+ // contract-violating shapes still fail validation.
178
+ const params = is_void_schema(action.spec.input) ? raw_params : (raw_params ?? {});
172
179
  const parse_result = action.spec.input.safeParse(params);
173
180
  if (!parse_result.success) {
174
181
  const error = jsonrpc_error_response(id, jsonrpc_error_messages.invalid_params('invalid params', {
@@ -845,16 +845,16 @@ auth per-spec, so mixed-auth endpoints compose cleanly.
845
845
 
846
846
  | Spec | Side effects | Input | Output |
847
847
  | -------------------------------------- | ------------ | --------------------------------------------------------- | ----------------------------- |
848
- | `admin_account_list_action_spec` | false | `z.null()` | `{accounts, grantable_roles}` |
849
- | `admin_session_list_action_spec` | false | `z.null()` | `{sessions}` |
848
+ | `admin_account_list_action_spec` | false | `z.void()` | `{accounts, grantable_roles}` |
849
+ | `admin_session_list_action_spec` | false | `z.void()` | `{sessions}` |
850
850
  | `admin_session_revoke_all_action_spec` | true | `{account_id}` | `{ok, count}` |
851
851
  | `admin_token_revoke_all_action_spec` | true | `{account_id}` | `{ok, count}` |
852
852
  | `audit_log_list_action_spec` | false | `{event_type?, account_id?, limit?, offset?, since_seq?}` | `{events}` |
853
853
  | `audit_log_permit_history_action_spec` | false | `{limit?, offset?}` | `{events}` |
854
854
  | `invite_create_action_spec` | true | `{email?, username?}` | `{ok, invite}` |
855
- | `invite_list_action_spec` | false | `z.null()` | `{invites}` |
855
+ | `invite_list_action_spec` | false | `z.void()` | `{invites}` |
856
856
  | `invite_delete_action_spec` | true | `{invite_id}` | `{ok}` |
857
- | `app_settings_get_action_spec` | false | `z.null()` | `{settings}` |
857
+ | `app_settings_get_action_spec` | false | `z.void()` | `{settings}` |
858
858
  | `app_settings_update_action_spec` | true | `{open_signup}` | `{ok, settings}` |
859
859
 
860
860
  `AUDIT_LOG_LIST_LIMIT_MAX = 200` — page size clamp (mirrors the former REST
@@ -1058,12 +1058,12 @@ exists.
1058
1058
 
1059
1059
  | Spec | Side effects | Input | Output |
1060
1060
  | ---------------------------------------- | ------------ | -------------- | ----------------------- |
1061
- | `account_verify_action_spec` | false | `z.null()` | `SessionAccountJson` |
1062
- | `account_session_list_action_spec` | false | `z.null()` | `{sessions}` |
1061
+ | `account_verify_action_spec` | false | `z.void()` | `SessionAccountJson` |
1062
+ | `account_session_list_action_spec` | false | `z.void()` | `{sessions}` |
1063
1063
  | `account_session_revoke_action_spec` | true | `{session_id}` | `{ok, revoked}` |
1064
- | `account_session_revoke_all_action_spec` | true | `z.null()` | `{ok, count}` |
1064
+ | `account_session_revoke_all_action_spec` | true | `z.void()` | `{ok, count}` |
1065
1065
  | `account_token_create_action_spec` | true | `{name?}` | `{ok, token, id, name}` |
1066
- | `account_token_list_action_spec` | false | `z.null()` | `{tokens}` |
1066
+ | `account_token_list_action_spec` | false | `z.void()` | `{tokens}` |
1067
1067
  | `account_token_revoke_action_spec` | true | `{token_id}` | `{ok, revoked}` |
1068
1068
 
1069
1069
  `session_id` validates as `Blake3Hash`; `token_id` validates as
@@ -10,10 +10,10 @@
10
10
  import { z } from 'zod';
11
11
  import type { RequestResponseActionSpec } from '../actions/action_spec.js';
12
12
  /** Input for `account_verify`. No parameters — the caller is the subject. */
13
- export declare const VerifyInput: z.ZodNull;
13
+ export declare const VerifyInput: z.ZodVoid;
14
14
  export type VerifyInput = z.infer<typeof VerifyInput>;
15
15
  /** Input for `account_session_list`. No parameters. */
16
- export declare const SessionListInput: z.ZodNull;
16
+ export declare const SessionListInput: z.ZodVoid;
17
17
  export type SessionListInput = z.infer<typeof SessionListInput>;
18
18
  /** Output for `account_session_list`. */
19
19
  export declare const SessionListOutput: z.ZodObject<{
@@ -38,7 +38,7 @@ export declare const SessionRevokeOutput: z.ZodObject<{
38
38
  }, z.core.$strict>;
39
39
  export type SessionRevokeOutput = z.infer<typeof SessionRevokeOutput>;
40
40
  /** Input for `account_session_revoke_all`. No parameters. */
41
- export declare const SessionRevokeAllInput: z.ZodNull;
41
+ export declare const SessionRevokeAllInput: z.ZodVoid;
42
42
  export type SessionRevokeAllInput = z.infer<typeof SessionRevokeAllInput>;
43
43
  /** Output for `account_session_revoke_all`. */
44
44
  export declare const SessionRevokeAllOutput: z.ZodObject<{
@@ -60,7 +60,7 @@ export declare const TokenCreateOutput: z.ZodObject<{
60
60
  }, z.core.$strict>;
61
61
  export type TokenCreateOutput = z.infer<typeof TokenCreateOutput>;
62
62
  /** Input for `account_token_list`. No parameters. */
63
- export declare const TokenListInput: z.ZodNull;
63
+ export declare const TokenListInput: z.ZodVoid;
64
64
  export type TokenListInput = z.infer<typeof TokenListInput>;
65
65
  /** Output for `account_token_list`. Hashes are excluded. */
66
66
  export declare const TokenListOutput: z.ZodObject<{
@@ -92,7 +92,7 @@ export declare const account_verify_action_spec: {
92
92
  initiator: "frontend";
93
93
  auth: "authenticated";
94
94
  side_effects: false;
95
- input: z.ZodNull;
95
+ input: z.ZodVoid;
96
96
  output: z.ZodObject<{
97
97
  id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
98
98
  username: z.ZodString;
@@ -109,7 +109,7 @@ export declare const account_session_list_action_spec: {
109
109
  initiator: "frontend";
110
110
  auth: "authenticated";
111
111
  side_effects: false;
112
- input: z.ZodNull;
112
+ input: z.ZodVoid;
113
113
  output: z.ZodObject<{
114
114
  sessions: z.ZodArray<z.ZodObject<{
115
115
  id: z.ZodString;
@@ -144,7 +144,7 @@ export declare const account_session_revoke_all_action_spec: {
144
144
  initiator: "frontend";
145
145
  auth: "authenticated";
146
146
  side_effects: true;
147
- input: z.ZodNull;
147
+ input: z.ZodVoid;
148
148
  output: z.ZodObject<{
149
149
  ok: z.ZodLiteral<true>;
150
150
  count: z.ZodNumber;
@@ -176,7 +176,7 @@ export declare const account_token_list_action_spec: {
176
176
  initiator: "frontend";
177
177
  auth: "authenticated";
178
178
  side_effects: false;
179
- input: z.ZodNull;
179
+ input: z.ZodVoid;
180
180
  output: z.ZodObject<{
181
181
  tokens: z.ZodArray<z.ZodObject<{
182
182
  id: z.ZodString;
@@ -13,9 +13,9 @@ import { AuthSessionJson, ClientApiTokenJson, SessionAccountJson } from './accou
13
13
  import { ApiTokenId } from './api_token.js';
14
14
  // -- Input/output schemas ---------------------------------------------------
15
15
  /** Input for `account_verify`. No parameters — the caller is the subject. */
16
- export const VerifyInput = z.null();
16
+ export const VerifyInput = z.void();
17
17
  /** Input for `account_session_list`. No parameters. */
18
- export const SessionListInput = z.null();
18
+ export const SessionListInput = z.void();
19
19
  /** Output for `account_session_list`. */
20
20
  export const SessionListOutput = z.strictObject({
21
21
  sessions: z.array(AuthSessionJson),
@@ -30,7 +30,7 @@ export const SessionRevokeOutput = z.strictObject({
30
30
  revoked: z.boolean(),
31
31
  });
32
32
  /** Input for `account_session_revoke_all`. No parameters. */
33
- export const SessionRevokeAllInput = z.null();
33
+ export const SessionRevokeAllInput = z.void();
34
34
  /** Output for `account_session_revoke_all`. */
35
35
  export const SessionRevokeAllOutput = z.strictObject({
36
36
  ok: z.literal(true),
@@ -51,7 +51,7 @@ export const TokenCreateOutput = z.strictObject({
51
51
  name: z.string(),
52
52
  });
53
53
  /** Input for `account_token_list`. No parameters. */
54
- export const TokenListInput = z.null();
54
+ export const TokenListInput = z.void();
55
55
  /** Output for `account_token_list`. Hashes are excluded. */
56
56
  export const TokenListOutput = z.strictObject({
57
57
  tokens: z.array(ClientApiTokenJson),
@@ -20,7 +20,7 @@ import type { RequestResponseActionSpec } from '../actions/action_spec.js';
20
20
  /** Max audit-log page size. Mirrors the former REST route's clamp. */
21
21
  export declare const AUDIT_LOG_LIST_LIMIT_MAX = 200;
22
22
  /** Input for `admin_account_list`. No parameters — the caller is the subject. */
23
- export declare const AdminAccountListInput: z.ZodNull;
23
+ export declare const AdminAccountListInput: z.ZodVoid;
24
24
  export type AdminAccountListInput = z.infer<typeof AdminAccountListInput>;
25
25
  /** Output for `admin_account_list`. */
26
26
  export declare const AdminAccountListOutput: z.ZodObject<{
@@ -60,7 +60,7 @@ export declare const AdminAccountListOutput: z.ZodObject<{
60
60
  }, z.core.$strict>;
61
61
  export type AdminAccountListOutput = z.infer<typeof AdminAccountListOutput>;
62
62
  /** Input for `admin_session_list`. No parameters — reads every active session. */
63
- export declare const AdminSessionListInput: z.ZodNull;
63
+ export declare const AdminSessionListInput: z.ZodVoid;
64
64
  export type AdminSessionListInput = z.infer<typeof AdminSessionListInput>;
65
65
  /** Output for `admin_session_list`. Cross-account listing; fan-out already scoped by role auth. */
66
66
  export declare const AdminSessionListOutput: z.ZodObject<{
@@ -183,7 +183,7 @@ export declare const InviteCreateOutput: z.ZodObject<{
183
183
  }, z.core.$strict>;
184
184
  export type InviteCreateOutput = z.infer<typeof InviteCreateOutput>;
185
185
  /** Input for `invite_list`. */
186
- export declare const InviteListInput: z.ZodNull;
186
+ export declare const InviteListInput: z.ZodVoid;
187
187
  export type InviteListInput = z.infer<typeof InviteListInput>;
188
188
  /** Output for `invite_list`. Uses the enriched row including creator/claimer usernames. */
189
189
  export declare const InviteListOutput: z.ZodObject<{
@@ -211,7 +211,7 @@ export declare const InviteDeleteOutput: z.ZodObject<{
211
211
  }, z.core.$strict>;
212
212
  export type InviteDeleteOutput = z.infer<typeof InviteDeleteOutput>;
213
213
  /** Input for `app_settings_get`. No parameters. */
214
- export declare const AppSettingsGetInput: z.ZodNull;
214
+ export declare const AppSettingsGetInput: z.ZodVoid;
215
215
  export type AppSettingsGetInput = z.infer<typeof AppSettingsGetInput>;
216
216
  /** Output for `app_settings_get`. */
217
217
  export declare const AppSettingsGetOutput: z.ZodObject<{
@@ -247,7 +247,7 @@ export declare const admin_account_list_action_spec: {
247
247
  role: string;
248
248
  };
249
249
  side_effects: false;
250
- input: z.ZodNull;
250
+ input: z.ZodVoid;
251
251
  output: z.ZodObject<{
252
252
  accounts: z.ZodArray<z.ZodObject<{
253
253
  account: z.ZodObject<{
@@ -294,7 +294,7 @@ export declare const admin_session_list_action_spec: {
294
294
  role: string;
295
295
  };
296
296
  side_effects: false;
297
- input: z.ZodNull;
297
+ input: z.ZodVoid;
298
298
  output: z.ZodObject<{
299
299
  sessions: z.ZodArray<z.ZodObject<{
300
300
  id: z.ZodString;
@@ -454,7 +454,7 @@ export declare const invite_list_action_spec: {
454
454
  role: string;
455
455
  };
456
456
  side_effects: false;
457
- input: z.ZodNull;
457
+ input: z.ZodVoid;
458
458
  output: z.ZodObject<{
459
459
  invites: z.ZodArray<z.ZodObject<{
460
460
  id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
@@ -496,7 +496,7 @@ export declare const app_settings_get_action_spec: {
496
496
  role: string;
497
497
  };
498
498
  side_effects: false;
499
- input: z.ZodNull;
499
+ input: z.ZodVoid;
500
500
  output: z.ZodObject<{
501
501
  settings: z.ZodObject<{
502
502
  open_signup: z.ZodBoolean;
@@ -26,14 +26,14 @@ import { AppSettingsWithUsernameJson } from './app_settings_schema.js';
26
26
  export const AUDIT_LOG_LIST_LIMIT_MAX = 200;
27
27
  // -- Input/output schemas ---------------------------------------------------
28
28
  /** Input for `admin_account_list`. No parameters — the caller is the subject. */
29
- export const AdminAccountListInput = z.null();
29
+ export const AdminAccountListInput = z.void();
30
30
  /** Output for `admin_account_list`. */
31
31
  export const AdminAccountListOutput = z.strictObject({
32
32
  accounts: z.array(AdminAccountEntryJson),
33
33
  grantable_roles: z.array(RoleName),
34
34
  });
35
35
  /** Input for `admin_session_list`. No parameters — reads every active session. */
36
- export const AdminSessionListInput = z.null();
36
+ export const AdminSessionListInput = z.void();
37
37
  /** Output for `admin_session_list`. Cross-account listing; fan-out already scoped by role auth. */
38
38
  export const AdminSessionListOutput = z.strictObject({
39
39
  sessions: z.array(AdminSessionJson),
@@ -116,7 +116,7 @@ export const InviteCreateOutput = z.strictObject({
116
116
  invite: InviteJson,
117
117
  });
118
118
  /** Input for `invite_list`. */
119
- export const InviteListInput = z.null();
119
+ export const InviteListInput = z.void();
120
120
  /** Output for `invite_list`. Uses the enriched row including creator/claimer usernames. */
121
121
  export const InviteListOutput = z.strictObject({
122
122
  invites: z.array(InviteWithUsernamesJson),
@@ -130,7 +130,7 @@ export const InviteDeleteOutput = z.strictObject({
130
130
  ok: z.literal(true),
131
131
  });
132
132
  /** Input for `app_settings_get`. No parameters. */
133
- export const AppSettingsGetInput = z.null();
133
+ export const AppSettingsGetInput = z.void();
134
134
  /** Output for `app_settings_get`. */
135
135
  export const AppSettingsGetOutput = z.strictObject({
136
136
  settings: AppSettingsWithUsernameJson,
@@ -18,6 +18,15 @@ import { type RateLimitKey, type RouteErrorSchemas } from './error_schemas.js';
18
18
  * but also accept other values.
19
19
  */
20
20
  export declare const is_null_schema: (schema: z.ZodType) => boolean;
21
+ /**
22
+ * Check if a schema is exactly `z.void()`.
23
+ *
24
+ * RPC action specs use `z.void()` to declare a parameterless method —
25
+ * JSON-RPC 2.0 forbids `params: null` (params must be omitted or be a
26
+ * Structured value), so `z.void()` is the correct schema for "no params"
27
+ * and the dispatcher maps absent params to `undefined` for these specs.
28
+ */
29
+ export declare const is_void_schema: (schema: z.ZodType) => boolean;
21
30
  /**
22
31
  * Check if a schema is a strict object (`z.strictObject()`).
23
32
  *
@@ -1 +1 @@
1
- {"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,KAC1C,iBAAiB,GAAG,IAUtB,CAAC"}
1
+ {"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,KAC1C,iBAAiB,GAAG,IAUtB,CAAC"}
@@ -17,6 +17,15 @@ import { derive_error_schemas } from './error_schemas.js';
17
17
  * but also accept other values.
18
18
  */
19
19
  export const is_null_schema = (schema) => schema instanceof z.ZodNull;
20
+ /**
21
+ * Check if a schema is exactly `z.void()`.
22
+ *
23
+ * RPC action specs use `z.void()` to declare a parameterless method —
24
+ * JSON-RPC 2.0 forbids `params: null` (params must be omitted or be a
25
+ * Structured value), so `z.void()` is the correct schema for "no params"
26
+ * and the dispatcher maps absent params to `undefined` for these specs.
27
+ */
28
+ export const is_void_schema = (schema) => schema instanceof z.ZodVoid;
20
29
  /**
21
30
  * Check if a schema is a strict object (`z.strictObject()`).
22
31
  *
@@ -163,7 +163,7 @@ export const describe_standard_admin_integration_tests = (options) => {
163
163
  app: test_app.app,
164
164
  path: rpc_path,
165
165
  spec: admin_account_list_action_spec,
166
- params: null,
166
+ params: undefined,
167
167
  headers: test_app.create_session_headers(),
168
168
  });
169
169
  assert.ok(res.ok, `admin_account_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -181,7 +181,7 @@ export const describe_standard_admin_integration_tests = (options) => {
181
181
  app: test_app.app,
182
182
  path: rpc_path,
183
183
  spec: admin_account_list_action_spec,
184
- params: null,
184
+ params: undefined,
185
185
  headers: test_app.create_session_headers(),
186
186
  });
187
187
  assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
@@ -205,7 +205,7 @@ export const describe_standard_admin_integration_tests = (options) => {
205
205
  app: test_app.app,
206
206
  path: rpc_path,
207
207
  spec: admin_session_list_action_spec,
208
- params: null,
208
+ params: undefined,
209
209
  headers: test_app.create_session_headers(),
210
210
  });
211
211
  assert.ok(res.ok, `admin_session_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -220,7 +220,7 @@ export const describe_standard_admin_integration_tests = (options) => {
220
220
  app: test_app.app,
221
221
  path: rpc_path,
222
222
  spec: account_verify_action_spec,
223
- params: null,
223
+ params: undefined,
224
224
  headers: create_headers(user_two.session_cookie),
225
225
  });
226
226
  assert.strictEqual(before.status, 200);
@@ -240,7 +240,7 @@ export const describe_standard_admin_integration_tests = (options) => {
240
240
  app: test_app.app,
241
241
  path: rpc_path,
242
242
  spec: account_verify_action_spec,
243
- params: null,
243
+ params: undefined,
244
244
  headers: create_headers(user_two.session_cookie),
245
245
  });
246
246
  assert.strictEqual(after.status, 401);
@@ -263,7 +263,7 @@ export const describe_standard_admin_integration_tests = (options) => {
263
263
  app: test_app.app,
264
264
  path: rpc_path,
265
265
  spec: account_verify_action_spec,
266
- params: null,
266
+ params: undefined,
267
267
  headers: test_app.create_session_headers(),
268
268
  });
269
269
  assert.strictEqual(after.status, 401);
@@ -279,7 +279,7 @@ export const describe_standard_admin_integration_tests = (options) => {
279
279
  app: test_app.app,
280
280
  path: rpc_path,
281
281
  spec: account_verify_action_spec,
282
- params: null,
282
+ params: undefined,
283
283
  headers: { authorization: `Bearer ${user_two.api_token}` },
284
284
  suppress_default_origin: true,
285
285
  });
@@ -300,7 +300,7 @@ export const describe_standard_admin_integration_tests = (options) => {
300
300
  app: test_app.app,
301
301
  path: rpc_path,
302
302
  spec: account_verify_action_spec,
303
- params: null,
303
+ params: undefined,
304
304
  headers: { authorization: `Bearer ${user_two.api_token}` },
305
305
  suppress_default_origin: true,
306
306
  });
@@ -725,7 +725,7 @@ export const describe_standard_admin_integration_tests = (options) => {
725
725
  app: test_app.app,
726
726
  path: rpc_path,
727
727
  spec: admin_account_list_action_spec,
728
- params: null,
728
+ params: undefined,
729
729
  headers: create_headers(regular_user.session_cookie),
730
730
  });
731
731
  assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
@@ -144,7 +144,7 @@ export const describe_audit_completeness_tests = (options) => {
144
144
  app: test_app.app,
145
145
  path: rpc_path,
146
146
  spec: account_token_list_action_spec,
147
- params: null,
147
+ params: undefined,
148
148
  headers: test_app.create_session_headers(),
149
149
  });
150
150
  assert.ok(list_res.ok, 'account_token_list should succeed');
@@ -179,7 +179,7 @@ export const describe_audit_completeness_tests = (options) => {
179
179
  app: test_app.app,
180
180
  path: rpc_path,
181
181
  spec: account_session_list_action_spec,
182
- params: null,
182
+ params: undefined,
183
183
  headers: test_app.create_session_headers(),
184
184
  });
185
185
  assert.ok(list_res.ok, 'account_session_list should succeed');
@@ -203,7 +203,7 @@ export const describe_audit_completeness_tests = (options) => {
203
203
  app: test_app.app,
204
204
  path: rpc_path,
205
205
  spec: account_session_revoke_all_action_spec,
206
- params: null,
206
+ params: undefined,
207
207
  headers: test_app.create_session_headers(),
208
208
  });
209
209
  assert.ok(res.ok, `account_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -225,7 +225,7 @@ export const describe_standard_integration_tests = (options) => {
225
225
  app: test_app.app,
226
226
  path: rpc_path,
227
227
  spec: account_verify_action_spec,
228
- params: null,
228
+ params: undefined,
229
229
  headers: create_headers(),
230
230
  });
231
231
  assert.strictEqual(verify_res.status, 200);
@@ -243,7 +243,7 @@ export const describe_standard_integration_tests = (options) => {
243
243
  app: test_app.app,
244
244
  path: rpc_path,
245
245
  spec: account_verify_action_spec,
246
- params: null,
246
+ params: undefined,
247
247
  headers: create_headers(),
248
248
  });
249
249
  assert.strictEqual(verify_after.status, 401);
@@ -315,7 +315,7 @@ export const describe_standard_integration_tests = (options) => {
315
315
  app: test_app.app,
316
316
  path: rpc_path,
317
317
  spec: account_verify_action_spec,
318
- params: null,
318
+ params: undefined,
319
319
  headers: { host: 'localhost' },
320
320
  });
321
321
  assert.strictEqual(res.status, 401);
@@ -326,7 +326,7 @@ export const describe_standard_integration_tests = (options) => {
326
326
  app: test_app.app,
327
327
  path: rpc_path,
328
328
  spec: account_verify_action_spec,
329
- params: null,
329
+ params: undefined,
330
330
  headers: { cookie: `${cookie_name}=random_garbage_value` },
331
331
  });
332
332
  assert.strictEqual(res.status, 401);
@@ -338,7 +338,7 @@ export const describe_standard_integration_tests = (options) => {
338
338
  app: test_app.app,
339
339
  path: rpc_path,
340
340
  spec: account_verify_action_spec,
341
- params: null,
341
+ params: undefined,
342
342
  headers: { cookie: `${cookie_name}=${expired_cookie}` },
343
343
  });
344
344
  assert.strictEqual(res.status, 401);
@@ -354,7 +354,7 @@ export const describe_standard_integration_tests = (options) => {
354
354
  app: test_app.app,
355
355
  path: rpc_path,
356
356
  spec: account_session_list_action_spec,
357
- params: null,
357
+ params: undefined,
358
358
  headers,
359
359
  });
360
360
  assert.ok(list_res.ok, 'account_session_list should succeed');
@@ -376,7 +376,7 @@ export const describe_standard_integration_tests = (options) => {
376
376
  app: test_app.app,
377
377
  path: rpc_path,
378
378
  spec: account_verify_action_spec,
379
- params: null,
379
+ params: undefined,
380
380
  headers,
381
381
  });
382
382
  assert.strictEqual(after.status, 401);
@@ -389,7 +389,7 @@ export const describe_standard_integration_tests = (options) => {
389
389
  app: test_app.app,
390
390
  path: rpc_path,
391
391
  spec: account_verify_action_spec,
392
- params: null,
392
+ params: undefined,
393
393
  headers,
394
394
  });
395
395
  assert.strictEqual(before.status, 200);
@@ -398,7 +398,7 @@ export const describe_standard_integration_tests = (options) => {
398
398
  app: test_app.app,
399
399
  path: rpc_path,
400
400
  spec: account_session_revoke_all_action_spec,
401
- params: null,
401
+ params: undefined,
402
402
  headers,
403
403
  });
404
404
  assert.ok(revoke_res.ok, 'account_session_revoke_all should succeed');
@@ -407,7 +407,7 @@ export const describe_standard_integration_tests = (options) => {
407
407
  app: test_app.app,
408
408
  path: rpc_path,
409
409
  spec: account_verify_action_spec,
410
- params: null,
410
+ params: undefined,
411
411
  headers,
412
412
  });
413
413
  assert.strictEqual(after.status, 401);
@@ -443,7 +443,7 @@ export const describe_standard_integration_tests = (options) => {
443
443
  app: test_app.app,
444
444
  path: rpc_path,
445
445
  spec: account_verify_action_spec,
446
- params: null,
446
+ params: undefined,
447
447
  headers: test_app.create_session_headers(),
448
448
  });
449
449
  assert.strictEqual(verify_after.status, 401);
@@ -485,7 +485,7 @@ export const describe_standard_integration_tests = (options) => {
485
485
  app: test_app.app,
486
486
  path: rpc_path,
487
487
  spec: account_verify_action_spec,
488
- params: null,
488
+ params: undefined,
489
489
  headers: test_app.create_session_headers(),
490
490
  });
491
491
  assert.strictEqual(verify_res.status, 200);
@@ -521,7 +521,7 @@ export const describe_standard_integration_tests = (options) => {
521
521
  app: test_app.app,
522
522
  path: rpc_path,
523
523
  spec: account_verify_action_spec,
524
- params: null,
524
+ params: undefined,
525
525
  headers: test_app.create_session_headers(),
526
526
  });
527
527
  assert.strictEqual(res.status, 200);
@@ -534,7 +534,7 @@ export const describe_standard_integration_tests = (options) => {
534
534
  app: test_app.app,
535
535
  path: rpc_path,
536
536
  spec: account_verify_action_spec,
537
- params: null,
537
+ params: undefined,
538
538
  headers: { cookie: `${cookie_name}=${test_app.backend.session_cookie}` },
539
539
  suppress_default_origin: true,
540
540
  });
@@ -549,7 +549,7 @@ export const describe_standard_integration_tests = (options) => {
549
549
  app: test_app.app,
550
550
  path: rpc_path,
551
551
  spec: account_verify_action_spec,
552
- params: null,
552
+ params: undefined,
553
553
  headers: test_app.create_bearer_headers(),
554
554
  suppress_default_origin: true,
555
555
  });
@@ -561,7 +561,7 @@ export const describe_standard_integration_tests = (options) => {
561
561
  app: test_app.app,
562
562
  path: rpc_path,
563
563
  spec: account_verify_action_spec,
564
- params: null,
564
+ params: undefined,
565
565
  headers: { authorization: 'Bearer secret_fuz_token_invalid' },
566
566
  suppress_default_origin: true,
567
567
  });
@@ -575,7 +575,7 @@ export const describe_standard_integration_tests = (options) => {
575
575
  app: test_app.app,
576
576
  path: rpc_path,
577
577
  spec: account_verify_action_spec,
578
- params: null,
578
+ params: undefined,
579
579
  headers: bearer_headers,
580
580
  suppress_default_origin: true,
581
581
  });
@@ -585,7 +585,7 @@ export const describe_standard_integration_tests = (options) => {
585
585
  app: test_app.app,
586
586
  path: rpc_path,
587
587
  spec: account_verify_action_spec,
588
- params: null,
588
+ params: undefined,
589
589
  headers: { ...bearer_headers, origin: 'http://localhost:5173' },
590
590
  });
591
591
  assert.strictEqual(res.status, 401);
@@ -610,7 +610,7 @@ export const describe_standard_integration_tests = (options) => {
610
610
  app: test_app.app,
611
611
  path: rpc_path,
612
612
  spec: account_verify_action_spec,
613
- params: null,
613
+ params: undefined,
614
614
  headers: { authorization: `Bearer ${token}` },
615
615
  suppress_default_origin: true,
616
616
  });
@@ -629,7 +629,7 @@ export const describe_standard_integration_tests = (options) => {
629
629
  app: test_app.app,
630
630
  path: rpc_path,
631
631
  spec: account_verify_action_spec,
632
- params: null,
632
+ params: undefined,
633
633
  headers: { authorization: `Bearer ${token}` },
634
634
  suppress_default_origin: true,
635
635
  });
@@ -662,7 +662,7 @@ export const describe_standard_integration_tests = (options) => {
662
662
  app: test_app.app,
663
663
  path: rpc_path,
664
664
  spec: account_session_revoke_all_action_spec,
665
- params: null,
665
+ params: undefined,
666
666
  headers: test_app.create_session_headers(),
667
667
  });
668
668
  assert.ok(revoke_res.ok, 'account_session_revoke_all should succeed');
@@ -671,7 +671,7 @@ export const describe_standard_integration_tests = (options) => {
671
671
  app: test_app.app,
672
672
  path: rpc_path,
673
673
  spec: account_verify_action_spec,
674
- params: null,
674
+ params: undefined,
675
675
  headers: { cookie: `${cookie_name}=${user_b.session_cookie}` },
676
676
  });
677
677
  assert.strictEqual(verify_b.status, 200);
@@ -685,7 +685,7 @@ export const describe_standard_integration_tests = (options) => {
685
685
  app: test_app.app,
686
686
  path: rpc_path,
687
687
  spec: account_session_list_action_spec,
688
- params: null,
688
+ params: undefined,
689
689
  headers: user_b_headers,
690
690
  });
691
691
  assert.ok(list_res.ok, 'account_session_list should succeed');
@@ -706,7 +706,7 @@ export const describe_standard_integration_tests = (options) => {
706
706
  app: test_app.app,
707
707
  path: rpc_path,
708
708
  spec: account_verify_action_spec,
709
- params: null,
709
+ params: undefined,
710
710
  headers: user_b_headers,
711
711
  });
712
712
  assert.strictEqual(verify_b.status, 200);
@@ -720,7 +720,7 @@ export const describe_standard_integration_tests = (options) => {
720
720
  app: test_app.app,
721
721
  path: rpc_path,
722
722
  spec: account_token_list_action_spec,
723
- params: null,
723
+ params: undefined,
724
724
  headers: user_b_headers,
725
725
  });
726
726
  assert.ok(list_res.ok, 'account_token_list should succeed');
@@ -741,7 +741,7 @@ export const describe_standard_integration_tests = (options) => {
741
741
  app: test_app.app,
742
742
  path: rpc_path,
743
743
  spec: account_verify_action_spec,
744
- params: null,
744
+ params: undefined,
745
745
  headers: { authorization: `Bearer ${user_b.api_token}` },
746
746
  suppress_default_origin: true,
747
747
  });
@@ -755,7 +755,7 @@ export const describe_standard_integration_tests = (options) => {
755
755
  app: test_app.app,
756
756
  path: rpc_path,
757
757
  spec: account_session_list_action_spec,
758
- params: null,
758
+ params: undefined,
759
759
  headers: test_app.create_session_headers(),
760
760
  });
761
761
  assert.ok(res.ok, 'account_session_list should succeed');
@@ -772,7 +772,7 @@ export const describe_standard_integration_tests = (options) => {
772
772
  app: test_app.app,
773
773
  path: rpc_path,
774
774
  spec: account_token_list_action_spec,
775
- params: null,
775
+ params: undefined,
776
776
  headers: test_app.create_session_headers(),
777
777
  });
778
778
  assert.ok(res.ok, 'account_token_list should succeed');
@@ -898,7 +898,7 @@ export const describe_standard_integration_tests = (options) => {
898
898
  app: test_app.app,
899
899
  path: rpc_path,
900
900
  spec: account_session_list_action_spec,
901
- params: null,
901
+ params: undefined,
902
902
  headers: { host: 'localhost' },
903
903
  });
904
904
  assert.strictEqual(session_list.status, 401);
@@ -907,7 +907,7 @@ export const describe_standard_integration_tests = (options) => {
907
907
  app: test_app.app,
908
908
  path: rpc_path,
909
909
  spec: account_session_revoke_all_action_spec,
910
- params: null,
910
+ params: undefined,
911
911
  headers: { host: 'localhost' },
912
912
  });
913
913
  assert.strictEqual(session_revoke_all.status, 401);
@@ -916,7 +916,7 @@ export const describe_standard_integration_tests = (options) => {
916
916
  app: test_app.app,
917
917
  path: rpc_path,
918
918
  spec: account_token_list_action_spec,
919
- params: null,
919
+ params: undefined,
920
920
  headers: { host: 'localhost' },
921
921
  });
922
922
  assert.strictEqual(token_list.status, 401);
@@ -934,7 +934,7 @@ export const describe_standard_integration_tests = (options) => {
934
934
  app: test_app.app,
935
935
  path: rpc_path,
936
936
  spec: account_verify_action_spec,
937
- params: null,
937
+ params: undefined,
938
938
  headers: { host: 'localhost' },
939
939
  });
940
940
  assert.strictEqual(verify.status, 401);
@@ -960,7 +960,7 @@ export const describe_standard_integration_tests = (options) => {
960
960
  app: test_app.app,
961
961
  path: rpc_path,
962
962
  spec: account_verify_action_spec,
963
- params: null,
963
+ params: undefined,
964
964
  headers: { host: 'localhost' },
965
965
  });
966
966
  assert.strictEqual(res.status, 401);
@@ -980,7 +980,7 @@ export const describe_standard_integration_tests = (options) => {
980
980
  app: test_app.app,
981
981
  path: rpc_path,
982
982
  spec: account_verify_action_spec,
983
- params: null,
983
+ params: undefined,
984
984
  headers: { cookie: `${cookie_name}=${expired_cookie}` },
985
985
  });
986
986
  assert.strictEqual(res.status, 401, 'Expired session cookie should be rejected');
@@ -1054,7 +1054,7 @@ export const describe_standard_integration_tests = (options) => {
1054
1054
  app: test_app.app,
1055
1055
  path: rpc_path,
1056
1056
  spec: account_verify_action_spec,
1057
- params: null,
1057
+ params: undefined,
1058
1058
  headers: { authorization: `Bearer ${raw_token}` },
1059
1059
  suppress_default_origin: true,
1060
1060
  });
@@ -1077,7 +1077,7 @@ export const describe_standard_integration_tests = (options) => {
1077
1077
  app: test_app.app,
1078
1078
  path: rpc_path,
1079
1079
  spec: account_verify_action_spec,
1080
- params: null,
1080
+ params: undefined,
1081
1081
  headers: { authorization: `Bearer ${raw_token}` },
1082
1082
  suppress_default_origin: true,
1083
1083
  });
@@ -40,10 +40,13 @@ export declare const resolve_rpc_endpoints_for_setup: (rpc_endpoints: RpcEndpoin
40
40
  * Create a `RequestInit` for a JSON-RPC POST request.
41
41
  *
42
42
  * @param method - JSON-RPC method name
43
- * @param params - params object (omit or pass `null` for null-input methods;
44
- * both are serialized without a `params` field so the envelope
45
- * schema accepts the request — `"params":null` is not a valid
46
- * JSON-RPC value)
43
+ * @param params - params (omit for parameterless methods; `null` is also
44
+ * stripped for ergonomic call sites JSON-RPC 2.0 §4.2
45
+ * forbids `"params": null` on the wire, and `create_rpc_endpoint`
46
+ * rejects `z.null()` action input schemas at registration).
47
+ * Tests that need to construct a literal `"params": null`
48
+ * envelope (e.g. asserting envelope-level rejection) should
49
+ * build the body inline rather than route through this helper.
47
50
  * @param id - request id (default `'test'`)
48
51
  * @returns a `RequestInit` with the JSON-RPC envelope as body
49
52
  */
@@ -53,7 +56,7 @@ export declare const create_rpc_post_init: (method: string, params?: unknown, id
53
56
  *
54
57
  * @param endpoint_path - the RPC endpoint path (e.g., `/api/rpc`)
55
58
  * @param method - JSON-RPC method name
56
- * @param params - params object (omit for null-input methods)
59
+ * @param params - params (omit for parameterless methods)
57
60
  * @param id - request id (default `'test'`)
58
61
  * @returns the full URL with query string
59
62
  */
@@ -113,7 +116,12 @@ export interface RpcCallArgs {
113
116
  path: string;
114
117
  /** JSON-RPC method name. */
115
118
  method: string;
116
- /** Params object. Omit or pass `null` for null-input methods. */
119
+ /**
120
+ * Params for the call. Omit (or pass `undefined`) for parameterless
121
+ * (`z.void()`) methods — the helper drops `params` from the envelope
122
+ * either way. See `create_rpc_post_init` for the null-stripping
123
+ * affordance and JSON-RPC 2.0 §4.2's prohibition on `params: null`.
124
+ */
117
125
  params?: unknown;
118
126
  /** Extra request headers (session cookie, bearer, etc.). Overrides defaults. */
119
127
  headers?: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAIN,KAAK,gBAAgB,EACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAC,qBAAqB,EAAE,mBAAmB,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAC;AACpG,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG9D;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAChC,KAAK,CAAC,eAAe,CAAC,GACtB,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,+BAA+B,GAC3C,eAAe,uBAAuB,EACtC,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,KAAK,CAAC,eAAe,CAuBvB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,GAChC,QAAQ,MAAM,EACd,SAAS,OAAO,EAChB,KAAI,MAAM,GAAG,MAAe,KAC1B,WAQF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,eAAe,MAAM,EACrB,QAAQ,MAAM,EACd,SAAS,OAAO,EAChB,KAAI,MAAM,GAAG,MAAe,KAC1B,MAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,OAAO,EACb,gBAAgB,gBAAgB,KAC9B,IAUF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAAI,MAAM,OAAO,EAAE,gBAAgB,CAAC,CAAC,OAAO,KAAG,IAU1F,CAAC;AAIF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAErF,2DAA2D;AAC3D,eAAO,MAAM,cAAc,GACzB,KAAK;IACL,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;CAC5E,KAAG,gBAEmB,CAAC;AAEzB,yEAAyE;AACzE,MAAM,MAAM,aAAa,GACtB;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAC3C;IACA,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC;CACtD,CAAC;AAEL,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC3B,mEAAmE;IACnE,GAAG,EAAE;QAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAA;KAAC,CAAC;IACnF,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wCAAwC;IACxC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAcD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GAAU,MAAM,WAAW,KAAG,OAAO,CAAC,aAAa,CA0DvE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,IAAI,CAAC,WAAW,EAAE,yBAAyB,CAAC,KAChD,OAAO,CAAC,aAAa,CAAuD,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAAC,KAAK,SAAS,yBAAyB,IACrE;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;CAAC,GAC5D;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAC,CAAA;CAAC,CAAC;AAEvF,mFAAmF;AACnF,MAAM,MAAM,kBAAkB,CAAC,KAAK,SAAS,yBAAyB,IAAI,IAAI,CAC7E,WAAW,EACX,QAAQ,GAAG,QAAQ,CACnB,GAAG;IACH,2GAA2G;IAC3G,IAAI,EAAE,KAAK,CAAC;IACZ,0CAA0C;IAC1C,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAU,KAAK,SAAS,yBAAyB,EAC9E,MAAM,kBAAkB,CAAC,KAAK,CAAC,KAC7B,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAarC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,EACrC,MAAM,WAAW,EACjB,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KACzB,OAAO,CAAC,CAAC,CAcX,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC3B,eAAe,aAAa,CAAC,eAAe,CAAC,EAC7C,QAAQ,MAAM,KACZ;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAC,GAAG,SAOtC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC3B,eAAe,aAAa,CAAC,qBAAqB,CAAC,EACnD,QAAQ,MAAM,KACZ;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAC,GAAG,SAOrD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACrC,eAAe,aAAa,CAAC,eAAe,CAAC,KAC3C,MAYF,CAAC"}
@@ -55,10 +55,13 @@ export const resolve_rpc_endpoints_for_setup = (rpc_endpoints, session_options)
55
55
  * Create a `RequestInit` for a JSON-RPC POST request.
56
56
  *
57
57
  * @param method - JSON-RPC method name
58
- * @param params - params object (omit or pass `null` for null-input methods;
59
- * both are serialized without a `params` field so the envelope
60
- * schema accepts the request — `"params":null` is not a valid
61
- * JSON-RPC value)
58
+ * @param params - params (omit for parameterless methods; `null` is also
59
+ * stripped for ergonomic call sites JSON-RPC 2.0 §4.2
60
+ * forbids `"params": null` on the wire, and `create_rpc_endpoint`
61
+ * rejects `z.null()` action input schemas at registration).
62
+ * Tests that need to construct a literal `"params": null`
63
+ * envelope (e.g. asserting envelope-level rejection) should
64
+ * build the body inline rather than route through this helper.
62
65
  * @param id - request id (default `'test'`)
63
66
  * @returns a `RequestInit` with the JSON-RPC envelope as body
64
67
  */
@@ -77,7 +80,7 @@ export const create_rpc_post_init = (method, params, id = 'test') => {
77
80
  *
78
81
  * @param endpoint_path - the RPC endpoint path (e.g., `/api/rpc`)
79
82
  * @param method - JSON-RPC method name
80
- * @param params - params object (omit for null-input methods)
83
+ * @param params - params (omit for parameterless methods)
81
84
  * @param id - request id (default `'test'`)
82
85
  * @returns the full URL with query string
83
86
  */
@@ -68,8 +68,8 @@ import { format_scope_context } from './format_scope.js';
68
68
  */
69
69
  export const create_admin_rpc_adapters = (rpc_call) => ({
70
70
  admin_accounts: {
71
- list_accounts: () => rpc_call('admin_account_list', null),
72
- list_sessions: () => rpc_call('admin_session_list', null),
71
+ list_accounts: () => rpc_call('admin_account_list'),
72
+ list_sessions: () => rpc_call('admin_session_list'),
73
73
  grant_permit: (params) => rpc_call('permit_offer_create', params),
74
74
  revoke_permit: (params) => rpc_call('permit_revoke', params),
75
75
  retract_offer: (offer_id) => rpc_call('permit_offer_retract', { offer_id }),
@@ -77,7 +77,7 @@ export const create_admin_rpc_adapters = (rpc_call) => ({
77
77
  token_revoke_all: (params) => rpc_call('admin_token_revoke_all', params),
78
78
  },
79
79
  admin_invites: {
80
- list: () => rpc_call('invite_list', null),
80
+ list: () => rpc_call('invite_list'),
81
81
  create: (params) => rpc_call('invite_create', params),
82
82
  delete: (params) => rpc_call('invite_delete', params),
83
83
  },
@@ -86,7 +86,7 @@ export const create_admin_rpc_adapters = (rpc_call) => ({
86
86
  permit_history: (params) => rpc_call('audit_log_permit_history', params ?? {}),
87
87
  },
88
88
  app_settings: {
89
- get: () => rpc_call('app_settings_get', null),
89
+ get: () => rpc_call('app_settings_get'),
90
90
  update: (params) => rpc_call('app_settings_update', params),
91
91
  },
92
92
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",