@swimmesberger/elarion-jsonrpc-client-generator 0.2.3-preview.61.1 → 0.2.3-preview.62.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/cli.js +14 -1
- package/dist/generate.js +19 -0
- package/dist/schema.d.ts +29 -0
- package/dist/session-client-source.d.ts +22 -0
- package/dist/session-client-source.js +232 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -42,6 +42,11 @@ function parseArgs(argv) {
|
|
|
42
42
|
index += 1;
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
+
if (arg === '--session-client') {
|
|
46
|
+
options.sessionClientFileName = next;
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
45
50
|
if (arg === '--source-label') {
|
|
46
51
|
options.sourceLabel = next;
|
|
47
52
|
index += 1;
|
|
@@ -60,6 +65,8 @@ Options:
|
|
|
60
65
|
--types <file> TypeScript types filename (default: rpc-types.ts)
|
|
61
66
|
--schemas <file> Zod schemas filename (default: rpc-schemas.ts)
|
|
62
67
|
--client <file> Fetch client filename (default: rpc-client.ts)
|
|
68
|
+
--session-client <file> Client-capability snapshot client + OpenFeature provider (default: session-client.ts;
|
|
69
|
+
emitted only when the schema exposes the elarion.session operation)
|
|
63
70
|
--source-label <text> Source label written into generated file headers
|
|
64
71
|
`);
|
|
65
72
|
}
|
|
@@ -74,11 +81,17 @@ function main() {
|
|
|
74
81
|
typesFileName: options.typesFileName,
|
|
75
82
|
schemasFileName: options.schemasFileName,
|
|
76
83
|
clientFileName: options.clientFileName,
|
|
84
|
+
sessionClientFileName: options.sessionClientFileName,
|
|
77
85
|
});
|
|
78
86
|
mkdirSync(outDir, { recursive: true });
|
|
79
87
|
writeFileSync(resolve(outDir, generated.typesFileName), generated.typesSource, 'utf-8');
|
|
80
88
|
writeFileSync(resolve(outDir, generated.schemasFileName), generated.schemasSource, 'utf-8');
|
|
81
89
|
writeFileSync(resolve(outDir, generated.clientFileName), generated.clientSource, 'utf-8');
|
|
82
|
-
|
|
90
|
+
let sessionNote = '';
|
|
91
|
+
if (generated.sessionClientFileName !== undefined && generated.sessionClientSource !== undefined) {
|
|
92
|
+
writeFileSync(resolve(outDir, generated.sessionClientFileName), generated.sessionClientSource, 'utf-8');
|
|
93
|
+
sessionNote = ` (+ ${generated.sessionClientFileName})`;
|
|
94
|
+
}
|
|
95
|
+
console.log(`[jsonrpc-client-generator] Generated ${generated.methodCount} RPC method types and schemas${sessionNote} -> ${outDir}`);
|
|
83
96
|
}
|
|
84
97
|
main();
|
package/dist/generate.js
CHANGED
|
@@ -2,11 +2,14 @@ import { jsonSchemaToTypeScript } from './json-schema-to-ts.js';
|
|
|
2
2
|
import { jsonSchemaToZod } from './json-schema-to-zod.js';
|
|
3
3
|
import { stripNullable } from './json-schema.js';
|
|
4
4
|
import { generateRpcClientSource } from './rpc-client-source.js';
|
|
5
|
+
import { generateSessionClientSource } from './session-client-source.js';
|
|
5
6
|
const DEFAULT_GENERATED_BY = 'elarion-jsonrpc-client-generator';
|
|
6
7
|
const DEFAULT_SOURCE_LABEL = 'rpc-schema.json';
|
|
7
8
|
const DEFAULT_TYPES_FILE = 'rpc-types.ts';
|
|
8
9
|
const DEFAULT_SCHEMAS_FILE = 'rpc-schemas.ts';
|
|
9
10
|
const DEFAULT_CLIENT_FILE = 'rpc-client.ts';
|
|
11
|
+
const DEFAULT_SESSION_CLIENT_FILE = 'session-client.ts';
|
|
12
|
+
const DEFAULT_SESSION_OPERATION = 'elarion.session';
|
|
10
13
|
export { UnsupportedJsonSchemaError } from './json-schema.js';
|
|
11
14
|
export function generateRpcClientFiles(schema, options = {}) {
|
|
12
15
|
const generatedBy = options.generatedBy ?? DEFAULT_GENERATED_BY;
|
|
@@ -68,6 +71,20 @@ export function generateRpcClientFiles(schema, options = {}) {
|
|
|
68
71
|
methods,
|
|
69
72
|
idempotentMethods,
|
|
70
73
|
});
|
|
74
|
+
// The client-capability snapshot client + OpenFeature provider (ADR-0030) is emitted only when the host exposes
|
|
75
|
+
// the session operation, so a schema without it produces byte-identical output. The schema's optional
|
|
76
|
+
// `capabilities` vocabulary (ADR-0032) turns the emitted module/flag/permission/role names into typed constants.
|
|
77
|
+
const sessionOperationName = options.sessionOperationName ?? DEFAULT_SESSION_OPERATION;
|
|
78
|
+
const sessionClientFileName = options.sessionClientFileName ?? DEFAULT_SESSION_CLIENT_FILE;
|
|
79
|
+
const hasSession = Object.prototype.hasOwnProperty.call(schema.methods, sessionOperationName);
|
|
80
|
+
const sessionClientSource = hasSession
|
|
81
|
+
? generateSessionClientSource({
|
|
82
|
+
generatedBy,
|
|
83
|
+
sourceLabel,
|
|
84
|
+
operationName: sessionOperationName,
|
|
85
|
+
capabilities: schema.capabilities,
|
|
86
|
+
})
|
|
87
|
+
: undefined;
|
|
71
88
|
return {
|
|
72
89
|
methodCount: methods.length,
|
|
73
90
|
typesFileName,
|
|
@@ -76,6 +93,8 @@ export function generateRpcClientFiles(schema, options = {}) {
|
|
|
76
93
|
typesSource: typesLines.join('\n'),
|
|
77
94
|
schemasSource: schemasLines.join('\n'),
|
|
78
95
|
clientSource,
|
|
96
|
+
sessionClientFileName: sessionClientSource === undefined ? undefined : sessionClientFileName,
|
|
97
|
+
sessionClientSource,
|
|
79
98
|
};
|
|
80
99
|
}
|
|
81
100
|
function createContext(root, path) {
|
package/dist/schema.d.ts
CHANGED
|
@@ -25,8 +25,27 @@ export interface RpcMethodSchema {
|
|
|
25
25
|
result: JsonSchema;
|
|
26
26
|
idempotent?: boolean;
|
|
27
27
|
}
|
|
28
|
+
/** One structured permission from the schema's capability vocabulary ({resource}.{verb} plus its parts). */
|
|
29
|
+
export interface RpcCapabilityPermission {
|
|
30
|
+
permission: string;
|
|
31
|
+
resource: string;
|
|
32
|
+
verb: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The capability vocabulary block emitted by the server exporter (ADR-0032): module names with the
|
|
36
|
+
* `[ClientFeatures]` each exposes, the structured permission catalog, and role names. All optional — older
|
|
37
|
+
* schemas (or hosts without the session/authorization registrations) simply omit it.
|
|
38
|
+
*/
|
|
39
|
+
export interface RpcSchemaCapabilities {
|
|
40
|
+
modules?: Record<string, {
|
|
41
|
+
features?: string[];
|
|
42
|
+
}>;
|
|
43
|
+
permissions?: RpcCapabilityPermission[];
|
|
44
|
+
roles?: string[];
|
|
45
|
+
}
|
|
28
46
|
export interface RpcSchema {
|
|
29
47
|
methods: Record<string, RpcMethodSchema>;
|
|
48
|
+
capabilities?: RpcSchemaCapabilities;
|
|
30
49
|
}
|
|
31
50
|
export interface GenerateRpcClientOptions {
|
|
32
51
|
generatedBy?: string;
|
|
@@ -34,6 +53,10 @@ export interface GenerateRpcClientOptions {
|
|
|
34
53
|
typesFileName?: string;
|
|
35
54
|
schemasFileName?: string;
|
|
36
55
|
clientFileName?: string;
|
|
56
|
+
/** Output filename for the client-capability snapshot client + OpenFeature provider (default `session-client.ts`). */
|
|
57
|
+
sessionClientFileName?: string;
|
|
58
|
+
/** The operation name the client-capability snapshot is served under (default `elarion.session`). */
|
|
59
|
+
sessionOperationName?: string;
|
|
37
60
|
}
|
|
38
61
|
export interface GeneratedRpcClientFiles {
|
|
39
62
|
methodCount: number;
|
|
@@ -43,4 +66,10 @@ export interface GeneratedRpcClientFiles {
|
|
|
43
66
|
typesSource: string;
|
|
44
67
|
schemasSource: string;
|
|
45
68
|
clientSource: string;
|
|
69
|
+
/**
|
|
70
|
+
* The client-capability snapshot client + OpenFeature provider (ADR-0020). Present only when the schema exposes
|
|
71
|
+
* the session operation (default `elarion.session`); otherwise both fields are `undefined`.
|
|
72
|
+
*/
|
|
73
|
+
sessionClientFileName?: string;
|
|
74
|
+
sessionClientSource?: string;
|
|
46
75
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RpcSchemaCapabilities } from './schema.js';
|
|
2
|
+
export interface GenerateSessionClientSourceOptions {
|
|
3
|
+
generatedBy: string;
|
|
4
|
+
sourceLabel: string;
|
|
5
|
+
/** The bus/REST operation name the session snapshot is served under (default `elarion.session`). */
|
|
6
|
+
operationName: string;
|
|
7
|
+
/** The schema's capability vocabulary (ADR-0032); absent on older schemas — types then fall back to `string`. */
|
|
8
|
+
capabilities?: RpcSchemaCapabilities;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Emits a self-contained client for the Elarion client-capability snapshot (ADR-0030): a typed `ClientSnapshot`,
|
|
12
|
+
* synchronous capability accessors, and an `@openfeature/web-sdk`-shaped provider that answers every key from the
|
|
13
|
+
* cached snapshot via the reserved `module.` / `permission.` / `role.` namespaces (a bare key is a flag/variant).
|
|
14
|
+
* When the schema carries the ADR-0032 capability vocabulary, the module/flag/permission/role names become typed
|
|
15
|
+
* constants and literal unions, so a typo in a capability check is a compile error; accessor parameters keep an
|
|
16
|
+
* escape hatch (`| (string & {})`) for names outside the exported vocabulary.
|
|
17
|
+
*
|
|
18
|
+
* The module has **no imports** — it neither depends on the RPC schema's generated dictionary typing nor pins
|
|
19
|
+
* `@openfeature/web-sdk`, so it type-checks and runs in any browser or Node target. The consumer fetches the
|
|
20
|
+
* snapshot through the generated RPC client (`rpc.{operation}(...)`) and hands it to the factory.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateSessionClientSource(options: GenerateSessionClientSourceOptions): string;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const q = (value) => JSON.stringify(value);
|
|
2
|
+
/**
|
|
3
|
+
* Emits the typed vocabulary section: `Modules`/`Flags`/`Permissions`/`Roles` const objects (only those with
|
|
4
|
+
* entries) plus the `ModuleName`/`FlagName`/`PermissionName`/`RoleName` aliases the accessors reference. With no
|
|
5
|
+
* vocabulary every alias is `string`, so the emitted accessors keep their pre-vocabulary signatures.
|
|
6
|
+
*/
|
|
7
|
+
function vocabularySection(capabilities) {
|
|
8
|
+
const moduleNames = Object.keys(capabilities?.modules ?? {}).sort();
|
|
9
|
+
const flagNames = [
|
|
10
|
+
...new Set(Object.values(capabilities?.modules ?? {}).flatMap((m) => m.features ?? [])),
|
|
11
|
+
].sort();
|
|
12
|
+
const permissions = [...(capabilities?.permissions ?? [])].sort((a, b) => a.permission < b.permission ? -1 : a.permission > b.permission ? 1 : 0);
|
|
13
|
+
const roles = [...(capabilities?.roles ?? [])].sort();
|
|
14
|
+
const lines = [];
|
|
15
|
+
if (moduleNames.length > 0) {
|
|
16
|
+
lines.push('/** Module names declared by the backend (enabled at schema-export time). */');
|
|
17
|
+
lines.push('export const Modules = {');
|
|
18
|
+
for (const name of moduleNames)
|
|
19
|
+
lines.push(` ${q(name)}: ${q(name)},`);
|
|
20
|
+
lines.push('} as const');
|
|
21
|
+
lines.push('export type ModuleName = (typeof Modules)[keyof typeof Modules]');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
lines.push('/** No module vocabulary in the schema — any string is accepted. */');
|
|
25
|
+
lines.push('export type ModuleName = string');
|
|
26
|
+
}
|
|
27
|
+
lines.push('');
|
|
28
|
+
if (flagNames.length > 0) {
|
|
29
|
+
lines.push('/** Flag/variant names the backend exposes to the client (the union of every [ClientFeatures] list). */');
|
|
30
|
+
lines.push('export const Flags = {');
|
|
31
|
+
for (const name of flagNames)
|
|
32
|
+
lines.push(` ${q(name)}: ${q(name)},`);
|
|
33
|
+
lines.push('} as const');
|
|
34
|
+
lines.push('export type FlagName = (typeof Flags)[keyof typeof Flags]');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
lines.push('/** No flag vocabulary in the schema — any string is accepted. */');
|
|
38
|
+
lines.push('export type FlagName = string');
|
|
39
|
+
}
|
|
40
|
+
lines.push('');
|
|
41
|
+
if (permissions.length > 0) {
|
|
42
|
+
const byResource = new Map();
|
|
43
|
+
for (const entry of permissions) {
|
|
44
|
+
const list = byResource.get(entry.resource) ?? [];
|
|
45
|
+
list.push({ verb: entry.verb, permission: entry.permission });
|
|
46
|
+
byResource.set(entry.resource, list);
|
|
47
|
+
}
|
|
48
|
+
lines.push('/** The declared permission catalog, nested by resource → verb (values are the composed claim strings). */');
|
|
49
|
+
lines.push('export const Permissions = {');
|
|
50
|
+
for (const [resource, entries] of [...byResource.entries()].sort(([a], [b]) => (a < b ? -1 : 1))) {
|
|
51
|
+
lines.push(` ${q(resource)}: {`);
|
|
52
|
+
for (const { verb, permission } of entries)
|
|
53
|
+
lines.push(` ${q(verb)}: ${q(permission)},`);
|
|
54
|
+
lines.push(' },');
|
|
55
|
+
}
|
|
56
|
+
lines.push('} as const');
|
|
57
|
+
lines.push(`export type PermissionName = ${permissions.map((p) => q(p.permission)).join(' | ')}`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push('/** No permission vocabulary in the schema — any string is accepted. */');
|
|
61
|
+
lines.push('export type PermissionName = string');
|
|
62
|
+
}
|
|
63
|
+
lines.push('');
|
|
64
|
+
if (roles.length > 0) {
|
|
65
|
+
lines.push('/** Role names declared by the backend ([RequireRole] across enabled modules). */');
|
|
66
|
+
lines.push('export const Roles = {');
|
|
67
|
+
for (const name of roles)
|
|
68
|
+
lines.push(` ${q(name)}: ${q(name)},`);
|
|
69
|
+
lines.push('} as const');
|
|
70
|
+
lines.push('export type RoleName = (typeof Roles)[keyof typeof Roles]');
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
lines.push('/** No role vocabulary in the schema — any string is accepted. */');
|
|
74
|
+
lines.push('export type RoleName = string');
|
|
75
|
+
}
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Emits a self-contained client for the Elarion client-capability snapshot (ADR-0030): a typed `ClientSnapshot`,
|
|
80
|
+
* synchronous capability accessors, and an `@openfeature/web-sdk`-shaped provider that answers every key from the
|
|
81
|
+
* cached snapshot via the reserved `module.` / `permission.` / `role.` namespaces (a bare key is a flag/variant).
|
|
82
|
+
* When the schema carries the ADR-0032 capability vocabulary, the module/flag/permission/role names become typed
|
|
83
|
+
* constants and literal unions, so a typo in a capability check is a compile error; accessor parameters keep an
|
|
84
|
+
* escape hatch (`| (string & {})`) for names outside the exported vocabulary.
|
|
85
|
+
*
|
|
86
|
+
* The module has **no imports** — it neither depends on the RPC schema's generated dictionary typing nor pins
|
|
87
|
+
* `@openfeature/web-sdk`, so it type-checks and runs in any browser or Node target. The consumer fetches the
|
|
88
|
+
* snapshot through the generated RPC client (`rpc.{operation}(...)`) and hands it to the factory.
|
|
89
|
+
*/
|
|
90
|
+
export function generateSessionClientSource(options) {
|
|
91
|
+
const { generatedBy, sourceLabel, operationName, capabilities } = options;
|
|
92
|
+
return `// Auto-generated by ${generatedBy} — DO NOT EDIT
|
|
93
|
+
// Source: ${sourceLabel}
|
|
94
|
+
//
|
|
95
|
+
// Client-capability snapshot (operation: ${JSON.stringify(operationName)}). Fetch it with the generated RPC client
|
|
96
|
+
// and pass it to createSessionCapabilities / createElarionOpenFeatureProvider.
|
|
97
|
+
|
|
98
|
+
/** The current user's identity and raw grants, as projected into the snapshot. */
|
|
99
|
+
export interface ClientSnapshotUser {
|
|
100
|
+
readonly id: string
|
|
101
|
+
readonly email?: string | null
|
|
102
|
+
readonly isAuthenticated: boolean
|
|
103
|
+
readonly roles: readonly string[]
|
|
104
|
+
readonly permissions: readonly string[]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The client-capability snapshot returned by the ${JSON.stringify(operationName)} operation. */
|
|
108
|
+
export interface ClientSnapshot {
|
|
109
|
+
readonly user: ClientSnapshotUser
|
|
110
|
+
readonly modules: Readonly<Record<string, boolean>>
|
|
111
|
+
readonly flags: Readonly<Record<string, boolean>>
|
|
112
|
+
readonly variants: Readonly<Record<string, string>>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
${vocabularySection(capabilities)}
|
|
116
|
+
|
|
117
|
+
/** The reserved OpenFeature key namespaces and their typed builders. */
|
|
118
|
+
export const Keys = {
|
|
119
|
+
module: (name: ModuleName | (string & {})): string => \`module.\${name}\`,
|
|
120
|
+
permission: (permission: PermissionName | (string & {})): string => \`permission.\${permission}\`,
|
|
121
|
+
role: (role: RoleName | (string & {})): string => \`role.\${role}\`,
|
|
122
|
+
} as const
|
|
123
|
+
|
|
124
|
+
/** Synchronous, typed accessors over a cached snapshot. */
|
|
125
|
+
export class SessionCapabilities {
|
|
126
|
+
// An explicit field, not a constructor parameter property — the emitted code must stay valid under
|
|
127
|
+
// 'erasableSyntaxOnly' (parameter properties are non-erasable TypeScript).
|
|
128
|
+
private readonly snapshot: ClientSnapshot
|
|
129
|
+
|
|
130
|
+
constructor(snapshot: ClientSnapshot) {
|
|
131
|
+
this.snapshot = snapshot
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get user(): ClientSnapshotUser {
|
|
135
|
+
return this.snapshot.user
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
isModuleEnabled(name: ModuleName | (string & {})): boolean {
|
|
139
|
+
return this.snapshot.modules[name] ?? false
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
hasRole(role: RoleName | (string & {})): boolean {
|
|
143
|
+
return this.snapshot.user.roles.includes(role)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
hasPermission(permission: PermissionName | (string & {})): boolean {
|
|
147
|
+
return this.snapshot.user.permissions.includes(permission)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
isFlagEnabled(name: FlagName | (string & {})): boolean {
|
|
151
|
+
return this.snapshot.flags[name] ?? false
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getVariant(name: FlagName | (string & {})): string | undefined {
|
|
155
|
+
return this.snapshot.variants[name]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Resolves a reserved-namespace boolean key (the OpenFeature boolean read). */
|
|
159
|
+
resolveBoolean(key: string): boolean {
|
|
160
|
+
if (key.startsWith('module.')) return this.isModuleEnabled(key.slice('module.'.length))
|
|
161
|
+
if (key.startsWith('permission.')) return this.hasPermission(key.slice('permission.'.length))
|
|
162
|
+
if (key.startsWith('role.')) return this.hasRole(key.slice('role.'.length))
|
|
163
|
+
return this.isFlagEnabled(key)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function createSessionCapabilities(snapshot: ClientSnapshot): SessionCapabilities {
|
|
168
|
+
return new SessionCapabilities(snapshot)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A single flag resolution result (structurally compatible with OpenFeature's ResolutionDetails). */
|
|
172
|
+
export interface ResolutionDetails<T> {
|
|
173
|
+
readonly value: T
|
|
174
|
+
readonly variant?: string
|
|
175
|
+
readonly reason: string
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The subset of the \`@openfeature/web-sdk\` Provider surface this snapshot provider implements. Register it with
|
|
180
|
+
* \`OpenFeature.setProvider(provider as unknown as Provider)\` — the static-context (web) paradigm reads
|
|
181
|
+
* synchronously, so module enablement (deployment-scoped, context-invariant) rides the same snapshot.
|
|
182
|
+
*/
|
|
183
|
+
export interface ElarionWebProvider {
|
|
184
|
+
readonly metadata: { readonly name: string }
|
|
185
|
+
readonly runsOn: 'client'
|
|
186
|
+
resolveBooleanEvaluation(flagKey: string, defaultValue: boolean): ResolutionDetails<boolean>
|
|
187
|
+
resolveStringEvaluation(flagKey: string, defaultValue: string): ResolutionDetails<string>
|
|
188
|
+
resolveNumberEvaluation(flagKey: string, defaultValue: number): ResolutionDetails<number>
|
|
189
|
+
resolveObjectEvaluation<T>(flagKey: string, defaultValue: T): ResolutionDetails<T>
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const STATIC = 'STATIC'
|
|
193
|
+
const DEFAULT = 'DEFAULT'
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Builds an OpenFeature web-SDK provider hydrated from one session snapshot. Every key resolves from the cache:
|
|
197
|
+
* \`module.{name}\` / \`permission.{p}\` / \`role.{r}\` are booleans, a bare key is a flag (boolean) or a variant
|
|
198
|
+
* (string), and anything else falls back to the default. No network calls — refresh by building a new provider
|
|
199
|
+
* from a freshly fetched snapshot (e.g. on \`onContextChange\`).
|
|
200
|
+
*/
|
|
201
|
+
export function createElarionOpenFeatureProvider(snapshot: ClientSnapshot): ElarionWebProvider {
|
|
202
|
+
const capabilities = new SessionCapabilities(snapshot)
|
|
203
|
+
return {
|
|
204
|
+
metadata: { name: 'elarion-session' },
|
|
205
|
+
runsOn: 'client',
|
|
206
|
+
resolveBooleanEvaluation(flagKey, defaultValue) {
|
|
207
|
+
if (
|
|
208
|
+
flagKey.startsWith('module.') ||
|
|
209
|
+
flagKey.startsWith('permission.') ||
|
|
210
|
+
flagKey.startsWith('role.')
|
|
211
|
+
) {
|
|
212
|
+
return { value: capabilities.resolveBoolean(flagKey), reason: STATIC }
|
|
213
|
+
}
|
|
214
|
+
const flag = snapshot.flags[flagKey]
|
|
215
|
+
return flag === undefined ? { value: defaultValue, reason: DEFAULT } : { value: flag, reason: STATIC }
|
|
216
|
+
},
|
|
217
|
+
resolveStringEvaluation(flagKey, defaultValue) {
|
|
218
|
+
const variant = capabilities.getVariant(flagKey)
|
|
219
|
+
return variant === undefined
|
|
220
|
+
? { value: defaultValue, reason: DEFAULT }
|
|
221
|
+
: { value: variant, variant, reason: STATIC }
|
|
222
|
+
},
|
|
223
|
+
resolveNumberEvaluation(_flagKey, defaultValue) {
|
|
224
|
+
return { value: defaultValue, reason: DEFAULT }
|
|
225
|
+
},
|
|
226
|
+
resolveObjectEvaluation(_flagKey, defaultValue) {
|
|
227
|
+
return { value: defaultValue, reason: DEFAULT }
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
`;
|
|
232
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swimmesberger/elarion-jsonrpc-client-generator",
|
|
3
|
-
"version": "0.2.3-preview.
|
|
3
|
+
"version": "0.2.3-preview.62.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",
|