@fuzdev/fuz_app 0.8.0 → 0.10.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/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +1 -6
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +1 -1
- package/dist/http/jsonrpc.d.ts +197 -13
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +90 -10
- package/dist/http/jsonrpc_errors.d.ts +46 -34
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +57 -33
- package/dist/http/jsonrpc_helpers.d.ts +56 -0
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -0
- package/dist/http/jsonrpc_helpers.js +138 -0
- package/dist/testing/rpc_helpers.d.ts +1 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +1 -1
- package/dist/ui/AdminOverview.svelte +33 -33
- package/dist/ui/ColumnLayout.svelte +6 -6
- package/dist/ui/Datatable.svelte +14 -14
- package/dist/ui/OpenSignupToggle.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +1 -5
- package/dist/ui/SurfaceExplorer.svelte.d.ts +3 -3
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.d.ts +9 -9
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +5 -5
- package/dist/ui/sidebar_state.svelte.d.ts +5 -5
- package/dist/ui/sidebar_state.svelte.d.ts.map +1 -1
- package/dist/ui/sidebar_state.svelte.js +1 -1
- package/package.json +2 -2
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides error types, named constructors, and HTTP status mapping
|
|
5
5
|
* for the throw/catch error pattern used by `apply_route_specs`.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Core error codes (5 standard + 8 general application). Domain-specific
|
|
7
|
+
* codes stay in consumers — add by casting `as JsonrpcErrorCode`.
|
|
8
|
+
*
|
|
9
|
+
* `JsonrpcErrorCode` and `JsonrpcErrorObject` types are Zod-inferred
|
|
10
|
+
* from `jsonrpc.ts` — this module re-uses those as the single source
|
|
11
|
+
* of truth.
|
|
10
12
|
*
|
|
11
13
|
* Complementary to `error_schemas.ts`: that module is declarative
|
|
12
14
|
* (Zod schemas for surface introspection), this one is runtime
|
|
@@ -14,6 +16,7 @@
|
|
|
14
16
|
*
|
|
15
17
|
* @module
|
|
16
18
|
*/
|
|
19
|
+
import { JSONRPC_PARSE_ERROR, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_INVALID_PARAMS, JSONRPC_INTERNAL_ERROR, } from './jsonrpc.js';
|
|
17
20
|
/**
|
|
18
21
|
* Standard JSON-RPC error codes (5) plus general application codes (8).
|
|
19
22
|
*
|
|
@@ -22,17 +25,12 @@
|
|
|
22
25
|
* -32099 range reserved by the JSON-RPC spec.
|
|
23
26
|
*/
|
|
24
27
|
export const JSONRPC_ERROR_CODES = {
|
|
25
|
-
// Standard JSON-RPC errors —
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
method_not_found: -32601,
|
|
32
|
-
/** -32602 */
|
|
33
|
-
invalid_params: -32602,
|
|
34
|
-
/** -32603 */
|
|
35
|
-
internal_error: -32603,
|
|
28
|
+
// Standard JSON-RPC errors — values from jsonrpc.ts
|
|
29
|
+
parse_error: JSONRPC_PARSE_ERROR,
|
|
30
|
+
invalid_request: JSONRPC_INVALID_REQUEST,
|
|
31
|
+
method_not_found: JSONRPC_METHOD_NOT_FOUND,
|
|
32
|
+
invalid_params: JSONRPC_INVALID_PARAMS,
|
|
33
|
+
internal_error: JSONRPC_INTERNAL_ERROR,
|
|
36
34
|
// General application errors (-32000 to -32099)
|
|
37
35
|
/**
|
|
38
36
|
* Same as HTTP 401 "unauthorized", but correctly named.
|
|
@@ -57,9 +55,9 @@ export const JSONRPC_ERROR_CODES = {
|
|
|
57
55
|
timeout: -32008,
|
|
58
56
|
};
|
|
59
57
|
/**
|
|
60
|
-
* Named constructors for `
|
|
58
|
+
* Named constructors for `JsonrpcErrorObject` values.
|
|
61
59
|
*
|
|
62
|
-
* Each function creates a JSON-RPC error
|
|
60
|
+
* Each function creates a JSON-RPC error object with the correct
|
|
63
61
|
* code and a sensible default message. Used by the catch layer in
|
|
64
62
|
* `apply_route_specs` to build response bodies.
|
|
65
63
|
*/
|
|
@@ -170,21 +168,38 @@ export const jsonrpc_errors = {
|
|
|
170
168
|
timeout: create_error_thrower(jsonrpc_error_messages.timeout),
|
|
171
169
|
};
|
|
172
170
|
// --- HTTP status mapping ---
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
[-
|
|
181
|
-
[-
|
|
182
|
-
[-
|
|
183
|
-
[-
|
|
184
|
-
[-
|
|
185
|
-
[-
|
|
186
|
-
[-
|
|
187
|
-
]
|
|
171
|
+
/**
|
|
172
|
+
* Maps JSON-RPC error codes to HTTP status codes.
|
|
173
|
+
*
|
|
174
|
+
* Extensible — consumers with domain-specific error codes can spread
|
|
175
|
+
* this into their own mapping object.
|
|
176
|
+
*/
|
|
177
|
+
export const JSONRPC_ERROR_CODE_TO_HTTP_STATUS = {
|
|
178
|
+
[-32700]: 400, // parse_error
|
|
179
|
+
[-32600]: 400, // invalid_request
|
|
180
|
+
[-32601]: 404, // method_not_found
|
|
181
|
+
[-32602]: 400, // invalid_params
|
|
182
|
+
[-32603]: 500, // internal_error
|
|
183
|
+
[-32001]: 401, // unauthenticated
|
|
184
|
+
[-32002]: 403, // forbidden
|
|
185
|
+
[-32003]: 404, // not_found
|
|
186
|
+
[-32004]: 409, // conflict
|
|
187
|
+
[-32005]: 422, // validation_error
|
|
188
|
+
[-32006]: 429, // rate_limited
|
|
189
|
+
[-32007]: 503, // service_unavailable
|
|
190
|
+
[-32008]: 504, // timeout
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Maps HTTP status codes to JSON-RPC error codes (reverse mapping).
|
|
194
|
+
*
|
|
195
|
+
* When multiple error codes map to the same HTTP status (e.g. parse_error
|
|
196
|
+
* and invalid_request both map to 400), the last one wins. Use for
|
|
197
|
+
* best-effort HTTP → JSON-RPC translation.
|
|
198
|
+
*/
|
|
199
|
+
export const HTTP_STATUS_TO_JSONRPC_ERROR_CODE = Object.fromEntries(Object.entries(JSONRPC_ERROR_CODE_TO_HTTP_STATUS).map(([code, status]) => [
|
|
200
|
+
status,
|
|
201
|
+
Number(code),
|
|
202
|
+
]));
|
|
188
203
|
/**
|
|
189
204
|
* Map a JSON-RPC error code to an HTTP status code.
|
|
190
205
|
*
|
|
@@ -194,4 +209,13 @@ const JSONRPC_ERROR_CODE_HTTP_STATUS = new Map([
|
|
|
194
209
|
* @param code - the JSON-RPC error code
|
|
195
210
|
* @returns the corresponding HTTP status code
|
|
196
211
|
*/
|
|
197
|
-
export const jsonrpc_error_code_to_http_status = (code) =>
|
|
212
|
+
export const jsonrpc_error_code_to_http_status = (code) => JSONRPC_ERROR_CODE_TO_HTTP_STATUS[code] ?? 500;
|
|
213
|
+
/**
|
|
214
|
+
* Map an HTTP status code to a JSON-RPC error code.
|
|
215
|
+
*
|
|
216
|
+
* Returns `internal_error` (-32603) for unrecognized status codes.
|
|
217
|
+
*
|
|
218
|
+
* @param status - the HTTP status code
|
|
219
|
+
* @returns the corresponding JSON-RPC error code
|
|
220
|
+
*/
|
|
221
|
+
export const http_status_to_jsonrpc_error_code = (status) => HTTP_STATUS_TO_JSONRPC_ERROR_CODE[status] ?? JSONRPC_ERROR_CODES.internal_error;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-RPC message builders, type guards, and converters.
|
|
3
|
+
*
|
|
4
|
+
* Used by the SAES runtime (ActionEvent, ActionPeer, transports) and
|
|
5
|
+
* the RPC endpoint dispatcher. Complements `jsonrpc.ts` (schemas) and
|
|
6
|
+
* `jsonrpc_errors.ts` (error infrastructure).
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { type JsonrpcErrorResponse, type JsonrpcMethod, type JsonrpcNotification, type JsonrpcNotificationParams, type JsonrpcRequest, type JsonrpcRequestId, type JsonrpcRequestParams, type JsonrpcResponse, type JsonrpcResult, type JsonrpcMessage, JSONRPC_VERSION } from './jsonrpc.js';
|
|
11
|
+
/** Creates a JSON-RPC request message. */
|
|
12
|
+
export declare const create_jsonrpc_request: (method: JsonrpcMethod, params: JsonrpcRequestParams | undefined, id: JsonrpcRequestId) => JsonrpcRequest;
|
|
13
|
+
/** Creates a JSON-RPC success response message. */
|
|
14
|
+
export declare const create_jsonrpc_response: (id: JsonrpcRequestId, result: JsonrpcResult) => JsonrpcResponse;
|
|
15
|
+
/** Creates a JSON-RPC notification message (no id, no response expected). */
|
|
16
|
+
export declare const create_jsonrpc_notification: (method: JsonrpcMethod, params: JsonrpcNotificationParams | undefined) => JsonrpcNotification;
|
|
17
|
+
/** Creates a JSON-RPC error response message. */
|
|
18
|
+
export declare const create_jsonrpc_error_response: (id: JsonrpcErrorResponse["id"], error: JsonrpcErrorResponse["error"]) => JsonrpcErrorResponse;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a JSON-RPC error response from any error.
|
|
21
|
+
* Handles `ThrownJsonrpcError` (preserves code/message/data) and
|
|
22
|
+
* regular `Error` objects (maps to internal_error, includes stack in DEV).
|
|
23
|
+
*/
|
|
24
|
+
export declare const create_jsonrpc_error_response_from_thrown: (id: JsonrpcRequestId | null, error: unknown) => JsonrpcErrorResponse;
|
|
25
|
+
/** Checks if a value is a valid JSON-RPC request id (string or finite number). */
|
|
26
|
+
export declare const is_jsonrpc_request_id: (id: unknown) => id is JsonrpcRequestId;
|
|
27
|
+
/** Checks if a value is a JSON-RPC object (has `jsonrpc: '2.0'`). */
|
|
28
|
+
export declare const is_jsonrpc_object: (message: unknown) => message is {
|
|
29
|
+
jsonrpc: typeof JSONRPC_VERSION;
|
|
30
|
+
};
|
|
31
|
+
/** Checks if a value is any valid JSON-RPC message or batch array. */
|
|
32
|
+
export declare const is_jsonrpc_message: (message: unknown) => message is JsonrpcMessage | Array<JsonrpcMessage>;
|
|
33
|
+
/** Checks if a value is a JSON-RPC request (has method + id). */
|
|
34
|
+
export declare const is_jsonrpc_request: (message: unknown) => message is JsonrpcRequest;
|
|
35
|
+
/** Checks if a value is a JSON-RPC notification (has method, no id). */
|
|
36
|
+
export declare const is_jsonrpc_notification: (message: unknown) => message is JsonrpcNotification;
|
|
37
|
+
/** Checks if a value is a JSON-RPC success response (has result + id). */
|
|
38
|
+
export declare const is_jsonrpc_response: (message: unknown) => message is JsonrpcResponse;
|
|
39
|
+
/** Checks if a value is a JSON-RPC error response (has error + id). */
|
|
40
|
+
export declare const is_jsonrpc_error_response: (message: unknown) => message is JsonrpcErrorResponse;
|
|
41
|
+
/**
|
|
42
|
+
* Extracts a JSON-RPC request id from a message or raw value.
|
|
43
|
+
* Returns `null` if no valid id can be extracted.
|
|
44
|
+
*/
|
|
45
|
+
export declare const to_jsonrpc_message_id: (message_or_id: unknown) => JsonrpcRequestId | null;
|
|
46
|
+
/**
|
|
47
|
+
* Normalizes input to JSON-RPC params format.
|
|
48
|
+
* Returns `undefined` for null/undefined, wraps primitives in `{value}`.
|
|
49
|
+
*/
|
|
50
|
+
export declare const to_jsonrpc_params: (input: unknown) => Record<string, any> | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Normalizes output to JSON-RPC result format.
|
|
53
|
+
* Returns empty object for null/undefined, wraps primitives in `{value}`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const to_jsonrpc_result: (output: unknown) => Record<string, any>;
|
|
56
|
+
//# sourceMappingURL=jsonrpc_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI;IAAC,OAAO,EAAE,OAAO,eAAe,CAAA;CAInD,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAQrE,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-RPC message builders, type guards, and converters.
|
|
3
|
+
*
|
|
4
|
+
* Used by the SAES runtime (ActionEvent, ActionPeer, transports) and
|
|
5
|
+
* the RPC endpoint dispatcher. Complements `jsonrpc.ts` (schemas) and
|
|
6
|
+
* `jsonrpc_errors.ts` (error infrastructure).
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { DEV } from 'esm-env';
|
|
11
|
+
import { JSONRPC_VERSION, } from './jsonrpc.js';
|
|
12
|
+
import { ThrownJsonrpcError, JSONRPC_ERROR_CODES } from './jsonrpc_errors.js';
|
|
13
|
+
// --- Message builders ---
|
|
14
|
+
/** Creates a JSON-RPC request message. */
|
|
15
|
+
export const create_jsonrpc_request = (method, params, id) => {
|
|
16
|
+
const message = {
|
|
17
|
+
jsonrpc: JSONRPC_VERSION,
|
|
18
|
+
id,
|
|
19
|
+
method,
|
|
20
|
+
};
|
|
21
|
+
if (params !== undefined) {
|
|
22
|
+
message.params = params;
|
|
23
|
+
}
|
|
24
|
+
return message;
|
|
25
|
+
};
|
|
26
|
+
/** Creates a JSON-RPC success response message. */
|
|
27
|
+
export const create_jsonrpc_response = (id, result) => ({
|
|
28
|
+
jsonrpc: JSONRPC_VERSION,
|
|
29
|
+
id,
|
|
30
|
+
result,
|
|
31
|
+
});
|
|
32
|
+
/** Creates a JSON-RPC notification message (no id, no response expected). */
|
|
33
|
+
export const create_jsonrpc_notification = (method, params) => {
|
|
34
|
+
const message = {
|
|
35
|
+
jsonrpc: JSONRPC_VERSION,
|
|
36
|
+
method,
|
|
37
|
+
};
|
|
38
|
+
if (params !== undefined) {
|
|
39
|
+
message.params = params;
|
|
40
|
+
}
|
|
41
|
+
return message;
|
|
42
|
+
};
|
|
43
|
+
/** Creates a JSON-RPC error response message. */
|
|
44
|
+
export const create_jsonrpc_error_response = (id, error) => ({
|
|
45
|
+
jsonrpc: JSONRPC_VERSION,
|
|
46
|
+
id,
|
|
47
|
+
error,
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Creates a JSON-RPC error response from any error.
|
|
51
|
+
* Handles `ThrownJsonrpcError` (preserves code/message/data) and
|
|
52
|
+
* regular `Error` objects (maps to internal_error, includes stack in DEV).
|
|
53
|
+
*/
|
|
54
|
+
export const create_jsonrpc_error_response_from_thrown = (id, error) => {
|
|
55
|
+
let code = JSONRPC_ERROR_CODES.internal_error;
|
|
56
|
+
let message = 'internal server error';
|
|
57
|
+
let data = undefined;
|
|
58
|
+
if (error instanceof ThrownJsonrpcError) {
|
|
59
|
+
code = error.code;
|
|
60
|
+
message = error.message;
|
|
61
|
+
data = error.data;
|
|
62
|
+
}
|
|
63
|
+
else if (error instanceof Error) {
|
|
64
|
+
if (DEV) {
|
|
65
|
+
message = error.message;
|
|
66
|
+
data = { stack: error.stack };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
jsonrpc: JSONRPC_VERSION,
|
|
71
|
+
id,
|
|
72
|
+
error: {
|
|
73
|
+
code,
|
|
74
|
+
message,
|
|
75
|
+
data,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
// --- Type guards ---
|
|
80
|
+
/** Checks if a value is a valid JSON-RPC request id (string or finite number). */
|
|
81
|
+
export const is_jsonrpc_request_id = (id) => {
|
|
82
|
+
const type = typeof id;
|
|
83
|
+
return type === 'string' || (type === 'number' && !Number.isNaN(id) && Number.isFinite(id));
|
|
84
|
+
};
|
|
85
|
+
/** Checks if a value is a JSON-RPC object (has `jsonrpc: '2.0'`). */
|
|
86
|
+
export const is_jsonrpc_object = (message) => typeof message === 'object' &&
|
|
87
|
+
message !== null &&
|
|
88
|
+
!Array.isArray(message) &&
|
|
89
|
+
message.jsonrpc === JSONRPC_VERSION;
|
|
90
|
+
/** Checks if a value is any valid JSON-RPC message or batch array. */
|
|
91
|
+
export const is_jsonrpc_message = (message) => Array.isArray(message)
|
|
92
|
+
? message.length > 0 && message.every((m) => is_jsonrpc_object(m))
|
|
93
|
+
: is_jsonrpc_object(message);
|
|
94
|
+
/** Checks if a value is a JSON-RPC request (has method + id). */
|
|
95
|
+
export const is_jsonrpc_request = (message) => is_jsonrpc_object(message) && 'method' in message && 'id' in message;
|
|
96
|
+
/** Checks if a value is a JSON-RPC notification (has method, no id). */
|
|
97
|
+
export const is_jsonrpc_notification = (message) => is_jsonrpc_object(message) && 'method' in message && !('id' in message);
|
|
98
|
+
/** Checks if a value is a JSON-RPC success response (has result + id). */
|
|
99
|
+
export const is_jsonrpc_response = (message) => is_jsonrpc_object(message) && 'result' in message && 'id' in message;
|
|
100
|
+
/** Checks if a value is a JSON-RPC error response (has error + id). */
|
|
101
|
+
export const is_jsonrpc_error_response = (message) => is_jsonrpc_object(message) && 'error' in message && 'id' in message;
|
|
102
|
+
// --- Converters ---
|
|
103
|
+
/**
|
|
104
|
+
* Extracts a JSON-RPC request id from a message or raw value.
|
|
105
|
+
* Returns `null` if no valid id can be extracted.
|
|
106
|
+
*/
|
|
107
|
+
export const to_jsonrpc_message_id = (message_or_id) => {
|
|
108
|
+
if (message_or_id == null)
|
|
109
|
+
return null;
|
|
110
|
+
const maybe_id = typeof message_or_id === 'object' ? message_or_id.id : message_or_id;
|
|
111
|
+
return is_jsonrpc_request_id(maybe_id) ? maybe_id : null;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Normalizes input to JSON-RPC params format.
|
|
115
|
+
* Returns `undefined` for null/undefined, wraps primitives in `{value}`.
|
|
116
|
+
*/
|
|
117
|
+
export const to_jsonrpc_params = (input) => {
|
|
118
|
+
if (input === undefined || input === null) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
if (typeof input === 'object' && !Array.isArray(input)) {
|
|
122
|
+
return input;
|
|
123
|
+
}
|
|
124
|
+
return { value: input };
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Normalizes output to JSON-RPC result format.
|
|
128
|
+
* Returns empty object for null/undefined, wraps primitives in `{value}`.
|
|
129
|
+
*/
|
|
130
|
+
export const to_jsonrpc_result = (output) => {
|
|
131
|
+
if (output === null || output === undefined) {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
if (typeof output === 'object' && !Array.isArray(output)) {
|
|
135
|
+
return output;
|
|
136
|
+
}
|
|
137
|
+
return { value: output };
|
|
138
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAW7B,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAW7B,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAIN,KAAK,gBAAgB,EACrB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,QAAQ,MAAM,EACd,SAAS,OAAO,EAChB,KAAI,MAAM,GAAG,MAAe,KAC1B,WAID,CAAC;AAEH;;;;;;;;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"}
|
|
@@ -8,7 +8,7 @@ import './assert_dev_env.js';
|
|
|
8
8
|
*/
|
|
9
9
|
import { assert } from 'vitest';
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
-
import { JSONRPC_VERSION, JsonrpcErrorResponse, JsonrpcResponse } from '../http/jsonrpc.js';
|
|
11
|
+
import { JSONRPC_VERSION, JsonrpcErrorResponse, JsonrpcResponse, } from '../http/jsonrpc.js';
|
|
12
12
|
/**
|
|
13
13
|
* Create a `RequestInit` for a JSON-RPC POST request.
|
|
14
14
|
*
|
|
@@ -67,20 +67,20 @@
|
|
|
67
67
|
<!-- TODO: panels will be user-draggable/rearrangeable, hardcoded order for now -->
|
|
68
68
|
<div class="overview">
|
|
69
69
|
<section>
|
|
70
|
-
<div class="
|
|
70
|
+
<div class="panel-header">
|
|
71
71
|
<h3>accounts</h3>
|
|
72
72
|
<a href={resolve('/admin/accounts' as any)} class="text_50 font_size_sm">view all →</a>
|
|
73
73
|
</div>
|
|
74
74
|
{#if accounts.loading}
|
|
75
75
|
<p class="text_50">loading...</p>
|
|
76
76
|
{:else if accounts.error}
|
|
77
|
-
<p class="
|
|
77
|
+
<p class="color_c_50">{accounts.error}</p>
|
|
78
78
|
{:else}
|
|
79
|
-
<div class="
|
|
79
|
+
<div class="baseline-row gap_xs">
|
|
80
80
|
<strong class="font_size_lg">{accounts.account_count}</strong>
|
|
81
81
|
<span class="text_50">accounts</span>
|
|
82
82
|
</div>
|
|
83
|
-
<div class="
|
|
83
|
+
<div class="baseline-row gap_xs flex-wrap:wrap font_size_sm mt_xs">
|
|
84
84
|
{#each role_counts as [role, count] (role)}
|
|
85
85
|
<span>{count} {role}</span>
|
|
86
86
|
<span class="text_50">·</span>
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
<span>{unroled_count} unroled</span>
|
|
89
89
|
</div>
|
|
90
90
|
{#if accounts.accounts.length > 0}
|
|
91
|
-
<ul class="
|
|
91
|
+
<ul class="compact-list">
|
|
92
92
|
{#each accounts.accounts.slice(0, 6) as entry (entry)}
|
|
93
93
|
<li>
|
|
94
94
|
<strong>{entry.account.username}</strong>
|
|
@@ -109,25 +109,25 @@
|
|
|
109
109
|
</section>
|
|
110
110
|
|
|
111
111
|
<section>
|
|
112
|
-
<div class="
|
|
112
|
+
<div class="panel-header">
|
|
113
113
|
<h3>sessions</h3>
|
|
114
114
|
<a href={resolve('/admin/sessions' as any)} class="text_50 font_size_sm">view all →</a>
|
|
115
115
|
</div>
|
|
116
116
|
{#if sessions.loading}
|
|
117
117
|
<p class="text_50">loading...</p>
|
|
118
118
|
{:else if sessions.error}
|
|
119
|
-
<p class="
|
|
119
|
+
<p class="color_c_50">{sessions.error}</p>
|
|
120
120
|
{:else}
|
|
121
|
-
<div class="
|
|
121
|
+
<div class="baseline-row gap_xs">
|
|
122
122
|
<strong class="font_size_lg">{sessions.active_count}</strong>
|
|
123
123
|
<span class="text_50">active</span>
|
|
124
124
|
</div>
|
|
125
|
-
<div class="
|
|
125
|
+
<div class="baseline-row gap_xs">
|
|
126
126
|
<strong class="font_size_lg">{unique_users}</strong>
|
|
127
127
|
<span class="text_50">unique users</span>
|
|
128
128
|
</div>
|
|
129
129
|
{#if most_recent}
|
|
130
|
-
<div class="
|
|
130
|
+
<div class="baseline-row gap_xs font_size_sm mt_sm">
|
|
131
131
|
<span class="text_50">last active:</span>
|
|
132
132
|
<strong>{most_recent.username}</strong>
|
|
133
133
|
<span class="text_50" title={format_datetime_local(most_recent.last_seen_at)}
|
|
@@ -139,16 +139,16 @@
|
|
|
139
139
|
</section>
|
|
140
140
|
|
|
141
141
|
<section>
|
|
142
|
-
<div class="
|
|
142
|
+
<div class="panel-header">
|
|
143
143
|
<h3>invites</h3>
|
|
144
144
|
<a href={resolve('/admin/invites' as any)} class="text_50 font_size_sm">view all →</a>
|
|
145
145
|
</div>
|
|
146
146
|
{#if invites.loading}
|
|
147
147
|
<p class="text_50">loading...</p>
|
|
148
148
|
{:else if invites.error}
|
|
149
|
-
<p class="
|
|
149
|
+
<p class="color_c_50">{invites.error}</p>
|
|
150
150
|
{:else}
|
|
151
|
-
<div class="
|
|
151
|
+
<div class="baseline-row gap_sm">
|
|
152
152
|
<span class="text_50">public signup</span>
|
|
153
153
|
{#if app_settings.settings?.open_signup}
|
|
154
154
|
<span class="chip color_b">open</span>
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
<span class="chip">closed</span>
|
|
157
157
|
{/if}
|
|
158
158
|
</div>
|
|
159
|
-
<div class="
|
|
159
|
+
<div class="baseline-row gap_xs">
|
|
160
160
|
<strong class="font_size_lg">{invites.unclaimed_count}</strong>
|
|
161
161
|
<span class="text_50">unclaimed</span>
|
|
162
162
|
<span class="text_50">/</span>
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
<span class="text_50">total</span>
|
|
165
165
|
</div>
|
|
166
166
|
{#if invites.invites.length > 0}
|
|
167
|
-
<ul class="
|
|
167
|
+
<ul class="compact-list">
|
|
168
168
|
{#each invites.invites.slice(0, 4) as invite (invite.id)}
|
|
169
169
|
<li>
|
|
170
170
|
<span>{invite.email || invite.username || '—'}</span>
|
|
@@ -184,18 +184,18 @@
|
|
|
184
184
|
</section>
|
|
185
185
|
|
|
186
186
|
<section>
|
|
187
|
-
<div class="
|
|
187
|
+
<div class="panel-header">
|
|
188
188
|
<h3>recent activity</h3>
|
|
189
189
|
<a href={resolve('/admin/audit-log' as any)} class="text_50 font_size_sm">view all →</a>
|
|
190
190
|
</div>
|
|
191
191
|
{#if audit_log.loading}
|
|
192
192
|
<p class="text_50">loading...</p>
|
|
193
193
|
{:else if audit_log.error}
|
|
194
|
-
<p class="
|
|
194
|
+
<p class="color_c_50">{audit_log.error}</p>
|
|
195
195
|
{:else if recent_events.length === 0}
|
|
196
196
|
<p class="text_50">no events</p>
|
|
197
197
|
{:else}
|
|
198
|
-
<ul class="
|
|
198
|
+
<ul class="compact-list">
|
|
199
199
|
{#each recent_events as event (event.id)}
|
|
200
200
|
<li>
|
|
201
201
|
<span class="text_50 font_size_sm" title={format_datetime_local(event.created_at)}
|
|
@@ -212,27 +212,27 @@
|
|
|
212
212
|
</section>
|
|
213
213
|
|
|
214
214
|
<section>
|
|
215
|
-
<div class="
|
|
215
|
+
<div class="panel-header">
|
|
216
216
|
<h3>security</h3>
|
|
217
217
|
<a href={resolve('/admin/audit-log' as any)} class="text_50 font_size_sm">audit log →</a>
|
|
218
218
|
</div>
|
|
219
219
|
{#if audit_log.loading}
|
|
220
220
|
<p class="text_50">loading...</p>
|
|
221
221
|
{:else if audit_log.error}
|
|
222
|
-
<p class="
|
|
222
|
+
<p class="color_c_50">{audit_log.error}</p>
|
|
223
223
|
{:else}
|
|
224
|
-
<div class="
|
|
225
|
-
<strong class="font_size_lg" class:
|
|
224
|
+
<div class="baseline-row gap_xs">
|
|
225
|
+
<strong class="font_size_lg" class:color_c_50={failed_logins.length > 0}>
|
|
226
226
|
{failed_logins.length}
|
|
227
227
|
</strong>
|
|
228
228
|
<span class="text_50">failed logins</span>
|
|
229
229
|
</div>
|
|
230
|
-
<div class="
|
|
230
|
+
<div class="baseline-row gap_xs">
|
|
231
231
|
<strong class="font_size_lg">{permit_changes.length}</strong>
|
|
232
232
|
<span class="text_50">permit changes</span>
|
|
233
233
|
</div>
|
|
234
234
|
{#if permit_changes.length > 0}
|
|
235
|
-
<ul class="
|
|
235
|
+
<ul class="compact-list">
|
|
236
236
|
{#each permit_changes.slice(0, 4) as event (event.id)}
|
|
237
237
|
<li class="font_size_sm">
|
|
238
238
|
<span class="text_50" title={format_datetime_local(event.created_at)}
|
|
@@ -250,15 +250,15 @@
|
|
|
250
250
|
</section>
|
|
251
251
|
|
|
252
252
|
<section>
|
|
253
|
-
<div class="
|
|
253
|
+
<div class="panel-header">
|
|
254
254
|
<h3>system</h3>
|
|
255
255
|
</div>
|
|
256
256
|
{#if app_settings.loading}
|
|
257
257
|
<p class="text_50">loading...</p>
|
|
258
258
|
{:else if app_settings.error}
|
|
259
|
-
<p class="
|
|
259
|
+
<p class="color_c_50">{app_settings.error}</p>
|
|
260
260
|
{:else}
|
|
261
|
-
<div class="
|
|
261
|
+
<div class="baseline-row gap_sm">
|
|
262
262
|
<span class="text_50">public signup</span>
|
|
263
263
|
{#if app_settings.settings?.open_signup}
|
|
264
264
|
<span class="chip color_b">open</span>
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
{/if}
|
|
268
268
|
</div>
|
|
269
269
|
{#if app_settings.settings?.updated_at}
|
|
270
|
-
<div class="
|
|
270
|
+
<div class="baseline-row gap_xs font_size_sm mt_xs">
|
|
271
271
|
<span class="text_50">last changed:</span>
|
|
272
272
|
<span title={format_datetime_local(app_settings.settings.updated_at)}>
|
|
273
273
|
{format_relative_time(app_settings.settings.updated_at)}
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
{/if}
|
|
281
281
|
{/if}
|
|
282
282
|
{#if auth_state.account}
|
|
283
|
-
<div class="
|
|
283
|
+
<div class="baseline-row gap_sm">
|
|
284
284
|
<span class="text_50">logged in as</span>
|
|
285
285
|
<strong>{auth_state.account.username}</strong>
|
|
286
286
|
</div>
|
|
@@ -310,25 +310,25 @@
|
|
|
310
310
|
gap: var(--space_lg);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
.
|
|
313
|
+
.panel-header {
|
|
314
314
|
display: flex;
|
|
315
315
|
justify-content: space-between;
|
|
316
316
|
align-items: baseline;
|
|
317
317
|
margin-bottom: var(--space_md);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
.
|
|
320
|
+
.baseline-row {
|
|
321
321
|
display: flex;
|
|
322
322
|
align-items: baseline;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
.
|
|
325
|
+
.compact-list {
|
|
326
326
|
list-style: none;
|
|
327
327
|
padding: 0;
|
|
328
328
|
margin: var(--space_sm) 0 0;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
.
|
|
331
|
+
.compact-list li {
|
|
332
332
|
display: flex;
|
|
333
333
|
align-items: baseline;
|
|
334
334
|
gap: var(--space_xs);
|
|
@@ -15,29 +15,29 @@
|
|
|
15
15
|
} = $props();
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
|
-
<div class="
|
|
19
|
-
<aside class="
|
|
18
|
+
<div class="column-layout {class_name}" style:--column_width={column_width} {...rest}>
|
|
19
|
+
<aside class="column-fixed unstyled">
|
|
20
20
|
{@render aside()}
|
|
21
21
|
</aside>
|
|
22
|
-
<div class="
|
|
22
|
+
<div class="column-fluid">
|
|
23
23
|
{@render children()}
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
27
|
<style>
|
|
28
|
-
.
|
|
28
|
+
.column-layout {
|
|
29
29
|
display: flex;
|
|
30
30
|
height: 100%;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
.
|
|
33
|
+
.column-fixed {
|
|
34
34
|
width: var(--column_width, 280px);
|
|
35
35
|
min-width: var(--column_width, 280px);
|
|
36
36
|
height: 100%;
|
|
37
37
|
overflow: auto;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
.
|
|
40
|
+
.column-fluid {
|
|
41
41
|
flex: 1;
|
|
42
42
|
height: 100%;
|
|
43
43
|
min-width: 0;
|