@swimmesberger/elarion-jsonrpc-client-generator 0.2.21 → 0.2.22-preview.190.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/generate.js CHANGED
@@ -126,6 +126,24 @@ export function generateRpcClientFiles(schema, options = {}) {
126
126
  }
127
127
  }
128
128
  const idempotentMethods = methods.filter((method) => schema.methods[method].idempotent === true);
129
+ // Every method's client-visible facts, normalized so a consumer never has to tell "absent" from "false".
130
+ const methodInfo = {};
131
+ for (const method of methods) {
132
+ const definition = schema.methods[method];
133
+ const requires = definition.requires ?? {};
134
+ methodInfo[method] = {
135
+ readOnly: definition.readOnly === true,
136
+ idempotent: definition.idempotent === true,
137
+ requires: {
138
+ anonymous: requires.anonymous === true,
139
+ authenticated: requires.authenticated === true,
140
+ permissions: [...(requires.permissions ?? [])].sort(),
141
+ roles: [...(requires.roles ?? [])].sort(),
142
+ serverChecks: requires.serverChecks === true,
143
+ },
144
+ tags: Object.fromEntries(Object.entries(definition.tags ?? {}).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),
145
+ };
146
+ }
129
147
  // Where each method carries file payloads (x-elarion-file nodes), so the client can convert between
130
148
  // native File values and the base64 wire envelope. Methods without files are omitted entirely.
131
149
  const paramsFilePaths = {};
@@ -148,6 +166,7 @@ export function generateRpcClientFiles(schema, options = {}) {
148
166
  schemasFileName,
149
167
  methods,
150
168
  idempotentMethods,
169
+ methodInfo,
151
170
  errorCodesByMethod,
152
171
  paramsFilePaths,
153
172
  resultFilePaths,
@@ -1,3 +1,16 @@
1
+ /** The normalized client-visible facts of one method, emitted verbatim into `rpcMethodInfo`. */
2
+ export interface RpcMethodInfoLiteral {
3
+ readOnly: boolean;
4
+ idempotent: boolean;
5
+ requires: {
6
+ anonymous: boolean;
7
+ authenticated: boolean;
8
+ permissions: string[];
9
+ roles: string[];
10
+ serverChecks: boolean;
11
+ };
12
+ tags: Record<string, string>;
13
+ }
1
14
  interface GenerateRpcClientSourceOptions {
2
15
  generatedBy: string;
3
16
  sourceLabel: string;
@@ -5,6 +18,8 @@ interface GenerateRpcClientSourceOptions {
5
18
  schemasFileName: string;
6
19
  methods: readonly string[];
7
20
  idempotentMethods: readonly string[];
21
+ /** Every method's normalized requirements, read-only flag and tags. */
22
+ methodInfo: Readonly<Record<string, RpcMethodInfoLiteral>>;
8
23
  /** The declared error codes per method (ADR-0080); methods without declared errors are omitted. */
9
24
  errorCodesByMethod: Readonly<Record<string, readonly string[]>>;
10
25
  /** Property paths of file payloads per method ('*' = array items, [] = the value itself); omitted methods carry none. */
@@ -120,6 +120,39 @@ export function generateRpcClientSource(options) {
120
120
  ...generateMethodNameLines(options.methods),
121
121
  `const rpcIdempotentMethods: ReadonlySet<RpcMethod> = new Set(${JSON.stringify(options.idempotentMethods)})`,
122
122
  '',
123
+ '/**',
124
+ ' * What a caller needs, as far as a client can tell: `anonymous` (any caller), or `authenticated` with every',
125
+ ' * permission and role listed. `serverChecks` marks requirements only the server evaluates (a policy, claim or',
126
+ ' * resource check), so an allowed-looking call may still be refused.',
127
+ ' */',
128
+ 'export interface RpcMethodRequirements {',
129
+ ' readonly anonymous: boolean',
130
+ ' readonly authenticated: boolean',
131
+ ' readonly permissions: readonly string[]',
132
+ ' readonly roles: readonly string[]',
133
+ ' readonly serverChecks: boolean',
134
+ '}',
135
+ '',
136
+ '/** What the schema says about a method before it is called. */',
137
+ 'export interface RpcMethodInfo {',
138
+ ' /** The request is a query: the operation only reads. */',
139
+ ' readonly readOnly: boolean',
140
+ ' /** The operation is [Idempotent]: the client attaches an idempotency key by default. */',
141
+ ' readonly idempotent: boolean',
142
+ ' readonly requires: RpcMethodRequirements',
143
+ ' /** The handler\'s [ClientTag] hints (for example a timeout) by key. */',
144
+ ' readonly tags: Readonly<Record<string, string>>',
145
+ '}',
146
+ '',
147
+ '/**',
148
+ ' * Every method\'s requirements, read-only flag and tags, from the server\'s handler declarations — the input of',
149
+ ' * UI gating (for example canCall from @swimmesberger/elarion-contributions). A UX projection, never security:',
150
+ ' * the server authorizes every call.',
151
+ ' */',
152
+ 'export const rpcMethodInfo: { readonly [M in RpcMethod]: RpcMethodInfo } = {',
153
+ ...options.methods.map((method) => ` ${JSON.stringify(method)}: ${JSON.stringify(options.methodInfo[method])},`),
154
+ '}',
155
+ '',
123
156
  `const rpcErrorCodes: Partial<Record<RpcMethod, readonly string[]>> = ${JSON.stringify(options.errorCodesByMethod)}`,
124
157
  '',
125
158
  '/** True when `error` is an RpcError carrying one of the error codes `method` declares. */',
@@ -180,6 +213,11 @@ export function generateRpcClientSource(options) {
180
213
  ' readonly data: TData',
181
214
  ' /** The numeric JSON-RPC error code (the coarse error kind, see ElarionErrorCodes). */',
182
215
  ' readonly rpcCode: number',
216
+ ' /**',
217
+ ' * The support reference of an unexpected server error (code "internal"): show it next to the message so a',
218
+ ' * user can quote it. Undefined for every other error.',
219
+ ' */',
220
+ ' readonly reference: string | undefined',
183
221
  '',
184
222
  ' constructor(rpcCode: number, message: string, wireData?: unknown) {',
185
223
  ' super(message)',
@@ -188,6 +226,7 @@ export function generateRpcClientSource(options) {
188
226
  ' const envelope = readErrorData(wireData)',
189
227
  ' this.code = (envelope?.code ?? defaultErrorCode(rpcCode)) as TCode',
190
228
  ' this.data = envelope?.data as TData',
229
+ ' this.reference = envelope?.reference',
191
230
  ' }',
192
231
  '',
193
232
  ' get isParseError() {',
@@ -677,10 +716,11 @@ export function generateRpcClientSource(options) {
677
716
  ' return rpcError',
678
717
  '}',
679
718
  '',
680
- '// The wire shape of error.data is always { code, data? } (ADR-0080).',
681
- 'function readErrorData(value: unknown): { code: string; data?: unknown } | undefined {',
719
+ '// The wire shape of error.data is always { code, data?, reference? } (ADR-0080).',
720
+ 'function readErrorData(value: unknown): { code: string; data?: unknown; reference?: string | undefined } | undefined {',
682
721
  ' if (!isRecord(value) || typeof value.code !== ' + "'string'" + ') return undefined',
683
- ' return { code: value.code, data: value.data }',
722
+ " const reference = typeof value.reference === 'string' ? value.reference : undefined",
723
+ ' return { code: value.code, data: value.data, reference }',
684
724
  '}',
685
725
  '',
686
726
  '// Fallback for a server that sends no error.data: the default code of the numeric kind.',
package/dist/schema.d.ts CHANGED
@@ -47,6 +47,24 @@ export interface RpcMethodSchema {
47
47
  /** The declared error contract, keyed by stable error code; omitted when the method declares none. */
48
48
  errors?: Record<string, RpcErrorSchema>;
49
49
  idempotent?: boolean;
50
+ /** What a caller needs, as far as a client can tell; omitted when the handler declares nothing. */
51
+ requires?: RpcMethodRequirementsSchema;
52
+ /** Set when the request is an IQuery: the operation only reads. */
53
+ readOnly?: boolean;
54
+ /** The handler's [ClientTag] hints by key; omitted when it declares none. */
55
+ tags?: Record<string, string>;
56
+ }
57
+ /**
58
+ * The per-method `requires` block of the server exporter: `anonymous` alone, or the requirements a client can
59
+ * evaluate against its session (`authenticated`, `permissions`, `roles`, all of them) plus `serverChecks` for
60
+ * requirements only the server can evaluate (a policy, claim or resource check).
61
+ */
62
+ export interface RpcMethodRequirementsSchema {
63
+ anonymous?: boolean;
64
+ authenticated?: boolean;
65
+ permissions?: string[];
66
+ roles?: string[];
67
+ serverChecks?: boolean;
50
68
  }
51
69
  /** One structured permission from the schema's capability vocabulary ({resource}.{verb} plus its parts). */
52
70
  export interface RpcCapabilityPermission {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swimmesberger/elarion-jsonrpc-client-generator",
3
- "version": "0.2.21",
3
+ "version": "0.2.22-preview.190.1",
4
4
  "description": "Generate TypeScript RPC contracts, Zod schemas, and a fetch client from Elarion JSON-RPC schema exports.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",