@yuhere/js-jsonrpc2 2.1.9 → 2.1.10
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/README.md +10 -2
- package/SKILL.md +21 -3
- package/htdoc/assets/{index-BAZmvr5x.css → index-Ck-39Igh.css} +221 -0
- package/htdoc/assets/{index-sVQlhxjI.js → index-Cr0Jnrgb.js} +716 -35
- package/htdoc/assets/index-Cr0Jnrgb.js.map +1 -0
- package/htdoc/index.html +2 -2
- package/package.json +3 -3
- package/webui/src/App.tsx +101 -20
- package/webui/src/api/glob.ts +90 -0
- package/webui/src/api/metrics.ts +135 -0
- package/webui/src/api/paramForm.ts +4 -3
- package/webui/src/components/MetricsPanel.md +126 -0
- package/webui/src/components/MetricsPanel.tsx +290 -0
- package/webui/src/components/SchemaForm.tsx +29 -27
- package/webui/src/components/SchemaView.tsx +3 -3
- package/webui/src/hooks/useMetrics.ts +91 -0
- package/webui/src/styles/geek.css +221 -0
- package/htdoc/assets/index-sVQlhxjI.js.map +0 -1
package/README.md
CHANGED
|
@@ -105,7 +105,8 @@ s.string() // string
|
|
|
105
105
|
s.string().min(1).max(64) // length-constrained string
|
|
106
106
|
s.number() // finite number
|
|
107
107
|
s.boolean() // boolean
|
|
108
|
-
s.literal('2.0') // exact literal
|
|
108
|
+
s.literal('2.0') // exact literal (string | number | boolean only)
|
|
109
|
+
s.null() // exactly null
|
|
109
110
|
s.array(s.string()) // string[]
|
|
110
111
|
s.object({ id: s.number() }) // open object
|
|
111
112
|
s.object({ id: s.number() }).strict() // all props required, no extras
|
|
@@ -113,7 +114,7 @@ s.record(s.number()) // Record<string, number>
|
|
|
113
114
|
s.union([s.string(), s.number()])
|
|
114
115
|
s.tuple([s.string(), s.number()])
|
|
115
116
|
s.enum(['pending', 'success', 'failed'])
|
|
116
|
-
s.unknown() // any JSON value
|
|
117
|
+
s.unknown() // any JSON value (already includes null)
|
|
117
118
|
s.void() // RPC-only: no return value
|
|
118
119
|
s.injectable('request') // RPC-only: server-injected parameter
|
|
119
120
|
```
|
|
@@ -123,6 +124,13 @@ Chainable modifiers: `.min()`, `.max()`, `.nullable()`, `.strict()`,
|
|
|
123
124
|
`SchemaValidationError`) and `.safeParse(input)` (returns a result object).
|
|
124
125
|
Introspection: `.toJSON()` returns a `SchemaDescription`.
|
|
125
126
|
|
|
127
|
+
Nullability notes:
|
|
128
|
+
|
|
129
|
+
- `.nullable()` / `.nullable(true)` allow `null`; `.nullable(false)` clears it. All calls are immutable.
|
|
130
|
+
- `s.unknown()` and `s.null()` do not expose `.nullable()`; they already accept / are `null`.
|
|
131
|
+
- `s.literal(v)` accepts `string | number | boolean`. Use `s.null()` for exact null, or `.nullable()` for "literal or null".
|
|
132
|
+
- `s.union([...])` strips member-level nullability; only the union's own `.nullable()` decides whether `null` is accepted.
|
|
133
|
+
|
|
126
134
|
## Transports
|
|
127
135
|
|
|
128
136
|
### Express
|
package/SKILL.md
CHANGED
|
@@ -27,10 +27,16 @@ Hard constraints (by design, do not work around):
|
|
|
27
27
|
5. Write `handler` so its positional args match the non-injectable slots in order;
|
|
28
28
|
injectable slots are filled at their declared positions.
|
|
29
29
|
6. Use `s.*` factories for schemas: `s.string()`, `s.number()`, `s.boolean()`,
|
|
30
|
-
`s.literal(v)
|
|
31
|
-
`s.
|
|
32
|
-
|
|
30
|
+
`s.literal(v)` (`v` is `string | number | boolean` only), `s.null()`,
|
|
31
|
+
`s.array(item)`, `s.object(shape)`, `s.record(v)`, `s.union([...])`,
|
|
32
|
+
`s.tuple([...])`, `s.enum([...])`, `s.unknown()`.
|
|
33
|
+
7. Chain modifiers before registering: `.min()`, `.max()`, `.nullable(bool)`,
|
|
33
34
|
`.strict()`, `.describe(text)`.
|
|
35
|
+
8. Nullability rules:
|
|
36
|
+
- `.nullable()` / `.nullable(true)` allow `null`; `.nullable(false)` clears it. Calls are immutable.
|
|
37
|
+
- `s.unknown()` and `s.null()` already handle `null`; they do not expose `.nullable()`.
|
|
38
|
+
- `s.literal(v)` rejects `null`; use `s.null()` for exact null, `.nullable()` for "literal or null".
|
|
39
|
+
- `s.union([...])` strips member-level nullability at construction; use `s.union([...]).nullable()` to accept `null`.
|
|
34
40
|
|
|
35
41
|
```ts
|
|
36
42
|
import { createRepository, s } from '@yuhere/js-jsonrpc2';
|
|
@@ -235,3 +241,15 @@ so `system.methodHelp` returns useful text.
|
|
|
235
241
|
- **Treating a terminal `error` frame as a broken connection.** A mid-stream
|
|
236
242
|
throw is delivered as `{ done: true, error }`; the HTTP/WS/stdio response
|
|
237
243
|
itself ends cleanly. Only transport-level failures abort the connection.
|
|
244
|
+
- **Calling `.nullable()` on `s.unknown()` or `s.null()`.** Those schemas do not
|
|
245
|
+
extend `NullableSchema`; the call is a TypeScript error. Use `.nullable()` on
|
|
246
|
+
the concrete schema instead (e.g. `s.string().nullable()`), or `s.null()`
|
|
247
|
+
when the value must be exactly `null`.
|
|
248
|
+
- **Expecting member-level `.nullable()` inside a union to matter.**
|
|
249
|
+
`s.union([s.string().nullable(), s.number()])` is equivalent to
|
|
250
|
+
`s.union([s.string(), s.number()])` — members are cloned and set to
|
|
251
|
+
`nullable(false)` at construction. Put `.nullable()` on the union itself:
|
|
252
|
+
`s.union([s.string(), s.number()]).nullable()`.
|
|
253
|
+
- **Calling `s.literal(null)`.** `literal` only accepts `string | number |
|
|
254
|
+
boolean`; construction throws `TypeError`. Use `s.null()` for exact null, or
|
|
255
|
+
`.nullable()` for "literal or null".
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
--syn-container: #7ee787;
|
|
26
26
|
--syn-object: #79c0ff;
|
|
27
27
|
--syn-union: #ffa657;
|
|
28
|
+
--syn-nullable: #ff79c6;
|
|
28
29
|
--syn-muted: #8b949e;
|
|
29
30
|
|
|
30
31
|
--mono: "JetBrains Mono", "Fira Code", Menlo, Consolas, "Courier New", monospace;
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
--syn-container: #116329;
|
|
53
54
|
--syn-object: #0969da;
|
|
54
55
|
--syn-union: #bc4c00;
|
|
56
|
+
--syn-nullable: #a626a4;
|
|
55
57
|
--syn-muted: #57606a;
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -391,6 +393,15 @@ textarea:focus {
|
|
|
391
393
|
color: var(--fg-dim);
|
|
392
394
|
}
|
|
393
395
|
|
|
396
|
+
.schema-nullable-badge {
|
|
397
|
+
color: var(--syn-nullable);
|
|
398
|
+
font-size: 90%;
|
|
399
|
+
font-weight: bold;
|
|
400
|
+
letter-spacing: .5px;
|
|
401
|
+
text-transform: lowercase;
|
|
402
|
+
opacity: .9;
|
|
403
|
+
}
|
|
404
|
+
|
|
394
405
|
.schema-constraints {
|
|
395
406
|
color: var(--syn-muted);
|
|
396
407
|
font-size: 90%;
|
|
@@ -458,6 +469,15 @@ textarea:focus {
|
|
|
458
469
|
color: var(--fg-dim);
|
|
459
470
|
}
|
|
460
471
|
|
|
472
|
+
.sf-nullable-badge {
|
|
473
|
+
color: var(--syn-nullable);
|
|
474
|
+
font-size: 90%;
|
|
475
|
+
font-weight: bold;
|
|
476
|
+
letter-spacing: .5px;
|
|
477
|
+
text-transform: lowercase;
|
|
478
|
+
opacity: .9;
|
|
479
|
+
}
|
|
480
|
+
|
|
461
481
|
.sf-desc {
|
|
462
482
|
color: var(--fg-dim);
|
|
463
483
|
font-style: italic;
|
|
@@ -739,4 +759,205 @@ textarea:focus {
|
|
|
739
759
|
|
|
740
760
|
.elapsed-badge .hint {
|
|
741
761
|
font-size: 11px;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
/* ================================================================
|
|
767
|
+
* Metrics 面板
|
|
768
|
+
* ================================================================ */
|
|
769
|
+
.metrics {
|
|
770
|
+
display: flex;
|
|
771
|
+
flex-direction: column;
|
|
772
|
+
gap: 16px;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.metrics-header {
|
|
776
|
+
display: flex;
|
|
777
|
+
align-items: center;
|
|
778
|
+
justify-content: space-between;
|
|
779
|
+
gap: 12px;
|
|
780
|
+
flex-wrap: wrap;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.metrics-header h2 {
|
|
784
|
+
margin: 0;
|
|
785
|
+
color: var(--accent);
|
|
786
|
+
font-size: 16px;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.metrics-controls {
|
|
790
|
+
display: flex;
|
|
791
|
+
align-items: center;
|
|
792
|
+
gap: 10px;
|
|
793
|
+
flex-wrap: wrap;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.metrics-auto {
|
|
797
|
+
display: inline-flex;
|
|
798
|
+
align-items: center;
|
|
799
|
+
gap: 4px;
|
|
800
|
+
font-size: 12px;
|
|
801
|
+
color: var(--fg-dim);
|
|
802
|
+
cursor: pointer;
|
|
803
|
+
user-select: none;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.metrics-cards {
|
|
807
|
+
display: grid;
|
|
808
|
+
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
|
809
|
+
gap: 10px;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.metric-card {
|
|
813
|
+
background: var(--bg-elev);
|
|
814
|
+
border: 1px solid var(--border);
|
|
815
|
+
border-radius: 4px;
|
|
816
|
+
padding: 10px 12px;
|
|
817
|
+
display: flex;
|
|
818
|
+
flex-direction: column;
|
|
819
|
+
gap: 4px;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.metric-card-label {
|
|
823
|
+
font-size: 11px;
|
|
824
|
+
color: var(--fg-dim);
|
|
825
|
+
text-transform: uppercase;
|
|
826
|
+
letter-spacing: 1px;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.metric-card-value {
|
|
830
|
+
font-size: 22px;
|
|
831
|
+
font-weight: bold;
|
|
832
|
+
color: var(--fg);
|
|
833
|
+
font-variant-numeric: tabular-nums;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.metric-card-danger .metric-card-value {
|
|
837
|
+
color: var(--danger);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.metric-card-warn .metric-card-value {
|
|
841
|
+
color: var(--syn-number);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.metrics-table {
|
|
845
|
+
width: 100%;
|
|
846
|
+
border-collapse: collapse;
|
|
847
|
+
font-size: 12px;
|
|
848
|
+
font-variant-numeric: tabular-nums;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.metrics-table th,
|
|
852
|
+
.metrics-table td {
|
|
853
|
+
padding: 6px 8px;
|
|
854
|
+
border-bottom: 1px solid var(--border);
|
|
855
|
+
text-align: left;
|
|
856
|
+
white-space: nowrap;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.metrics-table th {
|
|
860
|
+
color: var(--fg-dim);
|
|
861
|
+
font-weight: normal;
|
|
862
|
+
text-transform: uppercase;
|
|
863
|
+
letter-spacing: .5px;
|
|
864
|
+
font-size: 11px;
|
|
865
|
+
position: sticky;
|
|
866
|
+
top: 0;
|
|
867
|
+
background: var(--bg);
|
|
868
|
+
z-index: 1;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.metrics-table td.num,
|
|
872
|
+
.metrics-table th.num {
|
|
873
|
+
text-align: right;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.metrics-table tr:hover td {
|
|
877
|
+
background: var(--hover-bg);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.metrics-table .method-name {
|
|
881
|
+
color: var(--accent);
|
|
882
|
+
font-family: var(--mono);
|
|
883
|
+
max-width: 260px;
|
|
884
|
+
overflow: hidden;
|
|
885
|
+
text-overflow: ellipsis;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.metrics-table .error {
|
|
889
|
+
color: var(--danger);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.metrics-table .warn {
|
|
893
|
+
color: var(--syn-number);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.metrics-table .rate-hot {
|
|
897
|
+
color: var(--syn-boolean);
|
|
898
|
+
font-weight: bold;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.metrics-table .rate-warm {
|
|
902
|
+
color: var(--syn-number);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.metrics-table .rate-cold {
|
|
906
|
+
color: var(--fg-dim);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.metrics-table.compact th,
|
|
910
|
+
.metrics-table.compact td {
|
|
911
|
+
padding: 3px 6px;
|
|
912
|
+
font-size: 11px;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.view-switch {
|
|
916
|
+
margin-bottom: 8px;
|
|
917
|
+
width: 100%;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.view-switch button {
|
|
921
|
+
flex: 1;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/* ---- Metrics 侧边栏 ---- */
|
|
925
|
+
.metrics-sidebar {
|
|
926
|
+
display: flex;
|
|
927
|
+
flex-direction: column;
|
|
928
|
+
gap: 8px;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.metrics-filter {
|
|
932
|
+
display: flex;
|
|
933
|
+
flex-direction: column;
|
|
934
|
+
gap: 4px;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.metrics-filter-label {
|
|
938
|
+
font-size: 11px;
|
|
939
|
+
color: var(--fg-dim);
|
|
940
|
+
text-transform: uppercase;
|
|
941
|
+
letter-spacing: 1px;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.metrics-filter-help {
|
|
945
|
+
font-size: 11px;
|
|
946
|
+
line-height: 1.5;
|
|
947
|
+
margin: 0;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.metrics-filter-help code {
|
|
951
|
+
background: var(--bg-deep);
|
|
952
|
+
border: 1px solid var(--border);
|
|
953
|
+
border-radius: 3px;
|
|
954
|
+
padding: 0 4px;
|
|
955
|
+
font-size: 11px;
|
|
956
|
+
color: var(--syn-literal);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.metrics-filter-stat {
|
|
960
|
+
font-size: 11px;
|
|
961
|
+
margin: 4px 0 0;
|
|
962
|
+
color: var(--accent);
|
|
742
963
|
}
|